1.Hvac

Hvac:供暖通风与空气调节(Heating Ventilation and Air Conditioning)

在Android Automotive中,Hvac是作为控制汽车供暖通风与空气调节的系统应用,如下为Hvac源码目录结构及相关说明:

│ BootCompleteReceiver.java 用于开机启动HvacUiService

│ DataStore.java 存储管理Hvac属性数据

│ HvacController.java Hvac属性控制类

│ HvacPolicy.java Hvac属性规则类

│ HvacUiService.java Hvac界面服务

│ LocalHvacPropertyService.java Demo模式使用的模拟服务

├─controllers

│ FanDirectionButtonsController.java 风扇方向控制

│ FanSpeedBarController.java 风扇速度控制

│ HvacPanelController.java Hvac界面布局及相关逻辑

│ SeatWarmerController.java 座椅温度控制

│ TemperatureController.java 温度控制

└─ui

FanDirectionButtons.java

FanSpeedBar.java

FanSpeedBarSegment.java

HvacPanelRow.java

PressAndHoldTouchListener.java

SeatWarmerButton.java

TemperatureBarOverlay.java

ToggleButton.java

2.Hvac Manifest文件

在Hvac的Manifest文件中,一共注册了三个组件

服务HvacController:用于设置及获取Hvac相关属性

服务HvacUiService:用于显示Hvac的界面

广播接收器BootCompleteReceiver:接收开机启动广播并启动HvacUiService服务

3.Hvac启动

应用在Manifest文件中注册了开机广播,所以在开机完成后会收到开机完成的广播,然后再广播接收器里面会启动HvacUiService。

BootCompleteReceiver.java

@Override

public void onReceive(Context context, Intent intent) {

Intent hvacUiService = new Intent(context, HvacUiService.class);

context.startService(hvacUiService);

}

在HvacUiService启动的时候实例化HvacPanelController并绑定HvacController服务。

HvacUiService服务通过WindowManager将View添加Window中,并且给HvacPanelController传入了布局信息,然后在HvacPanelController中进行view的layout操作。并且在HvacPanelController包含了controller包下的FanSpeedBarController,SeatWarmerController等几个类,使得HvacPanelController可以作为Hvac的界面统一控制类。

HvacUiService.java

mHvacPanelController = new HvacPanelController(this /* context */, mContainer,

mWindowManager, mDriverTemperatureBar, mPassengerTemperatureBar,

mDriverTemperatureBarCollapsed, mPassengerTemperatureBarCollapsed

);

Intent bindIntent = new Intent(this /* context */, HvacController.class);

if (!bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE)) {

Log.e(TAG, "Failed to connect to HvacController.");

}

当HvacUiService成功绑定HvacController时,HvacPanelController会通过HvacController获取Hvac属性进行界面更新,并且HvacController也会从系统中读取各属性值,并存取在DateStore中。

HvacUiService.java

final Runnable r = () -> {

// Once the hvac controller has refreshed its values from the vehicle,

// bind all the values.

mHvacPanelController.updateHvacController(mHvacController);// 更新界面

};

if (mHvacController != null) {

mHvacController.requestRefresh(r, new Handler(context.getMainLooper()));// 存储数据

}

4.Hvac属性存储

DataStore用于存储HvacController从系统中获取的属性值。

我们可以从两个位置进行更新Hvac相关属性,即用户界面和硬件按钮。这两种不同的更新方式都是从不同的线程更新到当前状态。此外,在某些情况下,暖通空调系统可能会发送虚假的更新,因此这个类将所有内容更新管理合并,从而确保在用户看来应用程序的界面是正常的。

如风扇速度,DateStore会在收到系统事件是通过shouldPropagateFanSpeedUpdate方法判断是否需要更新,判断是否要更新的规则也很简单,即最近两秒内是否更新过该属性值,如果更新过,那么从系统回调的属性值将不会被Hvac应用处理。

DataStore.java

public int getFanSpeed() {

synchronized (mFanSpeed) {

return mFanSpeed;

}

}

public void setFanSpeed(int speed) {

synchronized (mFanSpeed) {

mFanSpeed = speed;

mLastFanSpeedSet = SystemClock.uptimeMillis();

}

}

public boolean shouldPropagateFanSpeedUpdate(int zone, int speed) {

// TODO: We ignore fan speed zones for now because we dont have a multi zone car.

synchronized (mFanSpeed) {

if (SystemClock.uptimeMillis() - mLastFanSpeedSet < COALESCE_TIME_MS) {

return false;

}

mFanSpeed = speed;

}

return true;

}

比如用户通过界面设置风扇速度

此时记录了最近设置风扇速度的时间mLastFanSpeedSet,而如果Hvac在两秒内收到系统的回调,可能是系统的错误信息也可能是通过风扇实体按钮改变的回调信息,那么这样的信息是不会显示在界面上的(这点有待验证)。

HvacController.java

public void setFanSpeed(final int fanSpeed) {

mDataStore.setFanSpeed(fanSpeed);

final AsyncTask task = new AsyncTask() {

int newFanSpeed;

protected Void doInBackground(Void... unused) {

if (mHvacManager != null) {

int zone = SEAT_ALL; // Car specific workaround.

try {

if (Log.isLoggable(TAG, Log.DEBUG)) {

Log.d(TAG, "Setting fanspeed to: " + fanSpeed);

}

mHvacManager.setIntProperty(

CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone, fanSpeed);

newFanSpeed = mHvacManager.getIntProperty(

CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone);

} catch (android.car.CarNotConnectedException e) {

Log.e(TAG, "Car not connected in setFanSpeed");

}

}

return null;

}

@Override

protected void onPostExecute(final Void result) {

Log.e(TAG, "postExecute new fanSpeed: " + newFanSpeed);

}

};

task.execute();

}

5.HvacController

HvacController作为Hvac应用与系统的信息传输控制器。

在Hvac中的设置及获取操作都是通过HvacController进行的,在HvacController启动时会获取一个Car实例,并通过connect方法连接CarService。

HvacController.java

mCarApiClient = Car.createCar(this, mCarConnectionCallback);

mCarApiClient.connect();

当成功连接CarService时,会进行初始化CarHvacManager并通过CarHvacManager获取车辆支持的属性列表,然后通知HvacPanelController已经成连接CarService并更新相关属性存储到DataStore中。

HvacController.java

@Override

public void onConnected(Car car) {

synchronized (mHvacManagerReady) {

try {

initHvacManager((CarHvacManager) mCarApiClient.getCarManager(

android.car.Car.HVAC_SERVICE));

mHvacManagerReady.notifyAll();

} catch (CarNotConnectedException e) {

Log.e(TAG, "Car not connected in onServiceConnected");

}

}

}

private void initHvacManager(CarHvacManager carHvacManager) {

mHvacManager = carHvacManager;

List properties = null;

try {

properties = mHvacManager.getPropertyList();

mPolicy = new HvacPolicy(HvacController.this, properties);

mHvacManager.registerCallback(mHardwareCallback);

} catch (android.car.CarNotConnectedException e) {

Log.e(TAG, "Car not connected in HVAC");

}

}

在HvacController中有两个重要的对象,Car和CarHvacManager,HvacController正是通过这两个对象与CarService进行通信。

(1)Car

Car作为汽车平台最高等级的API为外界提供汽车所有服务和数据的访问。

通过createCar方法可以新建一个Car实例,通过connect方法连接CarService。当成功连接时可以通过getCarManager方法获取一个一个相关的manager,比如Hvac通过getCarManager方法获取了一个CarHvacManager,当获取到manager后就可以进行相关操作了。

(2)CarHvacManager

CarHvacManager作为控制汽车Hvac系统的API为外界提供数据访问。

CarHvacManager实现了CarManagerBase接口,并且只要是作为Car*Manager, 都需要实现CarManagerBase接口,如CarCabinManager,CarSensorManager等都实现了该接口。

CarHvacManager的控制操作是通过CarPropertyManager来完成的,CarPropertyManager统一控制汽车属性相关的操作。CarHvacManager只是控制与Hvac相关的操作,在汽车中还有很多属性控制的Manager,如传感器,座舱等属性的控制,他们都是通过CarPropertyManager进行属性操作,通过在操作时传入的属性ID,属性区域以及属性值,在CarPropertyManager中会将这些参数转化为一个CarPropertyValue对象继续往下层传递。

CarHvacManager控制的属性有:

// 全局属性,只有一个

ID_MIRROR_DEFROSTER_ON //视镜除雾

ID_STEERING_WHEEL_HEAT //方向盘温度

ID_OUTSIDE_AIR_TEMP //室外温度

ID_TEMPERATURE_DISPLAY_UNITS //在使用的温度

// 区域属性,可在不同区域设置

ID_ZONED_TEMP_SETPOINT //用户设置的温度

ID_ZONED_TEMP_ACTUAL //区域实际温度

ID_ZONED_HVAC_POWER_ON //HVAC系统电源开关

ID_ZONED_FAN_SPEED_SETPOINT //风扇设置的速度

ID_ZONED_FAN_SPEED_RPM //风扇实际的速度

ID_ZONED_FAN_DIRECTION_AVAILABLE //风扇可设置的方向

ID_ZONED_FAN_DIRECTION //现在风扇设置的方向

ID_ZONED_SEAT_TEMP //座椅温度

ID_ZONED_AC_ON //空调开关

ID_ZONED_AUTOMATIC_MODE_ON //HVAC自动模式开关

ID_ZONED_AIR_RECIRCULATION_ON //空气循环开关

ID_ZONED_MAX_AC_ON //空调最大速度开关

ID_ZONED_DUAL_ZONE_ON //双区模式开关

ID_ZONED_MAX_DEFROST_ON //最大除雾开关

ID_ZONED_HVAC_AUTO_RECIRC_ON //自动循环模式开关

ID_WINDOW_DEFROSTER_ON //除雾模式开关

6.一次设置过程

现在我们跟踪一次风扇速度的设置过程。

点击Hvac界面中的风扇速度最大按钮时,通过在HvacPanelController中实例化时传入的mHvacController对象来设置最大速度。

FanSpeedBarController.java

private static final int MAX_FAN_SPEED = 6;

private FanSpeedBar.FanSpeedButtonClickListener mClickListener

= new FanSpeedBar.FanSpeedButtonClickListener() {

@Override

public void onMaxButtonClicked() {

mHvacController.setFanSpeed(MAX_FAN_SPEED);

}

……

};

HvacController收到传入的为值为MAX_FAN_SPEED = 6;

首先会将这个值存储到DataStore中,然后通过CarHvacManager的setInProperty方法设置

传入属性ID为ID_ZONED_FAN_SPEED_SETPOINT,属性区域为SEAT_ALL,属性值为要设置的风扇速度。

HvacController.java

// Hardware specific value for the front seats

public static final int SEAT_ALL = VehicleAreaSeat.SEAT_ROW_1_LEFT |

VehicleAreaSeat.SEAT_ROW_1_RIGHT | VehicleAreaSeat.SEAT_ROW_2_LEFT |

VehicleAreaSeat.SEAT_ROW_2_CENTER | VehicleAreaSeat.SEAT_ROW_2_RIGHT;

public void setFanSpeed(final int fanSpeed) {

mDataStore.setFanSpeed(fanSpeed);

final AsyncTask task = new AsyncTask() {

int newFanSpeed;

protected Void doInBackground(Void... unused) {

if (mHvacManager != null) {

int zone = SEAT_ALL; // Car specific workaround.

try {

if (Log.isLoggable(TAG, Log.DEBUG)) {

Log.d(TAG, "Setting fanspeed to: " + fanSpeed);

}

mHvacManager.setIntProperty(

CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone, fanSpeed);

newFanSpeed = mHvacManager.getIntProperty(

CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone);

} catch (android.car.CarNotConnectedException e) {

Log.e(TAG, "Car not connected in setFanSpeed");

}

}

return null;

}

@Override

protected void onPostExecute(final Void result) {

Log.e(TAG, "postExecute new fanSpeed: " + newFanSpeed);

}

};

task.execute();

}

在CarHvacManager中进行了属性ID合法性的检查,判断这个属性是否为Hvac相关的属性。然后通过CarPropertyManager进行设置。

CarHvacManager.java

public void setIntProperty(@PropertyId int propertyId, int area, int val)

throws CarNotConnectedException {

if (mHvacPropertyIds.contains(propertyId)) {

mCarPropertyMgr.setIntProperty(propertyId, area, val);

}

}

在CarPropertyManager中继续进行属性设置,最后是通过CarPropertyService进行操作的,

而CarPropertyService是Car调用getCarManager时通过AIDL绑定的Service,所以可能会出现RemoteException,而出现这个异常时,意味着CarService不可用,需要抛出去让用户知道。

CarPropertyManager.java

/** Set int value of property*/

public void setIntProperty(int prop, int area, int val) throws CarNotConnectedException {

setProperty(Integer.class, prop, area, val);

}

/** Set CarPropertyValue */

public void setProperty(Class clazz, int propId, int area, E val)

throws CarNotConnectedException {

if (mDbg) {

Log.d(mTag, "setProperty, propId: 0x" + toHexString(propId)

+ ", area: 0x" + toHexString(area) + ", class: " + clazz + ", val: " + val);

}

try {

mService.setProperty(new CarPropertyValue<>(propId, area, val));

} catch (RemoteException e) {

Log.e(mTag, "setProperty failed with " + e.toString(), e);

throw new CarNotConnectedException(e);

}

}

在CarPropertyService中,会再次对属性ID合法性进行判断,并判断要修改该属性的应用是否有权限,最后通过PropertyHalService进行设置。

CarPropertyService.java

@Override

public void setProperty(CarPropertyValue prop) {

int propId = prop.getPropertyId();

if (mConfigs.get(propId) == null) {

// Do not attempt to register an invalid propId

Log.e(TAG, "setProperty: propId is not in config list:0x" + toHexString(propId));

return;

}

ICarImpl.assertPermission(mContext, mHal.getWritePermission(propId));

mHal.setProperty(prop);

}

在PropertyHalService中,首先将Car属性值对象转换为Vehicle的Hal属性值对象,并判断此车机是否支持该属性,然后通过VehicleHal进行设置,VehiclePropValue是在Hal层定义的一个数据类型。

PropertyHalService.java

public void setProperty(CarPropertyValue prop) {

int halPropId = managerToHalPropId(prop.getPropertyId());

if (halPropId == NOT_SUPPORTED_PROPERTY) {

throw new IllegalArgumentException("Invalid property Id : 0x"

+ toHexString(prop.getPropertyId()));

}

VehiclePropValue halProp = toVehiclePropValue(prop, halPropId);

try {

mVehicleHal.set(halProp);

} catch (PropertyTimeoutException e) {

Log.e(CarLog.TAG_PROPERTY, "set, property not ready 0x" + toHexString(halPropId), e);

throw new RuntimeException(e);

}

}

types.hal

/**

* Encapsulates the property name and the associated value. It

* is used across various API calls to set values, get values or to register for

* events.

*/

struct VehiclePropValue {

/** Time is elapsed nanoseconds since boot */

int64_t timestamp;

/**

* Area type(s) for non-global property it must be one of the value from

* VehicleArea* enums or 0 for global properties.

*/

int32_t areaId;

/** Property identifier */

int32_t prop;

/** Status of the property */

VehiclePropertyStatus status;

/**

* Contains value for a single property. Depending on property data type of

* this property (VehiclePropetyType) one field of this structure must be filled in.

*/

struct RawValue {

/**

* This is used for properties of types VehiclePropertyType#INT

* and VehiclePropertyType#INT_VEC

*/

vec int32Values;

/**

* This is used for properties of types VehiclePropertyType#FLOAT

* and VehiclePropertyType#FLOAT_VEC

*/

vec floatValues;

/** This is used for properties of type VehiclePropertyType#INT64 */

vec int64Values;

/** This is used for properties of type VehiclePropertyType#BYTES */

vec bytes;

/** This is used for properties of type VehiclePropertyType#STRING */

string stringValue;

};

RawValue value;

};

VehicleHal为车辆HAL的抽象。此类处理与本机HAL的接口信息,并对接收到的数据进行基本分析(类型检查)。

在这里的set方法中,使用的是HalClient的setValue方法。

VehicleHal.java

void set(VehiclePropValue propValue) throws PropertyTimeoutException {

mHalClient.setValue(propValue);

}

HalClient为直接与车辆HAL接口交互的类。

到此时,已经是与Hal层通过HIDL进行交互了,在HalClient实例化时,会传入一个Ivehicle对象,这个对象是在CarService创建时通过HIDL获取的对象,与Hal层进行数据传输。

HalClient.java

public void setValue(VehiclePropValue propValue) throws PropertyTimeoutException {

int status = invokeRetriable(() -> {

try {

return mVehicle.set(propValue);

} catch (RemoteException e) {

Log.e(CarLog.TAG_HAL, "Failed to set value", e);

return StatusCode.TRY_AGAIN;

}

}, WAIT_CAP_FOR_RETRIABLE_RESULT_MS, SLEEP_BETWEEN_RETRIABLE_INVOKES_MS);

……

}

如下方法为CarService通过HIDL的方式获取Hal层服务对象,获取的为Hal层VehicleService注册的服务。

CarService.java

@Nullable

private static IVehicle getVehicle() {

try {

return android.hardware.automotive.vehicle.V2_0.IVehicle.getService();

} catch (RemoteException e) {

Log.e(CarLog.TAG_SERVICE, "Failed to get IVehicle service", e);

} catch (NoSuchElementException e) {

Log.e(CarLog.TAG_SERVICE, "IVehicle service not registered yet");

}

return null;

}

VehicleService.cpp

ALOGI("Registering as service...");

status_t status = service->registerAsService();

在HalClient中调用了IVehicle的set方法,这个set方法的真正实现在EmulatedVehicleHal.cpp中

IVehicle.hal

/**

* Set a vehicle property value.

*

* Timestamp of data must be ignored for set operation.

*

* Setting some properties require having initial state available. If initial

* data is not available yet this call must return StatusCode::TRY_AGAIN.

* For a property with separate power control this call must return

* StatusCode::NOT_AVAILABLE error if property is not powered on.

*/

set(VehiclePropValue propValue) generates (StatusCode status);

EmulatedVehicleHal.cpp

StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {

static constexpr bool shouldUpdateStatus = false;

if (propValue.prop == kGenerateFakeDataControllingProperty) {

StatusCode status = handleGenerateFakeDataRequest(propValue);

if (status != StatusCode::OK) {

return status;

}

} else if (mHvacPowerProps.count(propValue.prop)) {

auto hvacPowerOn = mPropStore->readValueOrNull(

toInt(VehicleProperty::HVAC_POWER_ON),

(VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |

VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |

VehicleAreaSeat::ROW_2_RIGHT));

if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1

&& hvacPowerOn->value.int32Values[0] == 0) {

return StatusCode::NOT_AVAILABLE;

}

} else {

// Handle property specific code

switch (propValue.prop) {

case OBD2_FREEZE_FRAME_CLEAR:

return clearObd2FreezeFrames(propValue);

case VEHICLE_MAP_SERVICE:

// Placeholder for future implementation of VMS property in the default hal. For

// now, just returns OK; otherwise, hal clients crash with property not supported.

return StatusCode::OK;

case AP_POWER_STATE_REPORT:

// This property has different behavior between get/set. When it is set, the value

// goes to the vehicle but is NOT updated in the property store back to Android.

// Commented out for now, because it may mess up automated testing that use the

// emulator interface.

// getEmulatorOrDie()->doSetValueFromClient(propValue);

return StatusCode::OK;

}

}

if (propValue.status != VehiclePropertyStatus::AVAILABLE) {

// Android side cannot set property status - this value is the

// purview of the HAL implementation to reflect the state of

// its underlying hardware

return StatusCode::INVALID_ARG;

}

auto currentPropValue = mPropStore->readValueOrNull(propValue);

if (currentPropValue == nullptr) {

return StatusCode::INVALID_ARG;

}

if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {

// do not allow Android side to set() a disabled/error property

return StatusCode::NOT_AVAILABLE;

}

if (!mPropStore->writeValue(propValue, shouldUpdateStatus)) {

return StatusCode::INVALID_ARG;

}

getEmulatorOrDie()->doSetValueFromClient(propValue);

return StatusCode::OK;

}

7.Hvac注册及监听属性信息

在hardware的vehicle目录下,总共定义了3个hal文件:Ivehicle.hal, IvehicleCallback.hal和types.hal文件。在上面的一次设置过程中,我们可以看到在最后与hal层进行交互时调用的正是Ivehicle.hal中的set方法,而我们仅仅是通过CarService中以HIDL方式获取的Ivehicle服务来调用他,其中复杂的调用过程都由HIDL来完成,最终会调用到服务端实现该接口的方法中去,即EmulatedVehicleHal.cpp中的set方法。

这三个文件对应的功能为:

IVehicle.hal: 定义了服务端给客户端调用的的接口

IVehicleCallback.hal: 定义了服务端回调客户端的接口

types.hal: 定义了需要使用的数据结构

在Hvac应用中,通过HvacController注册了CarHvacManager.CarHvacEventCallback,我们用这个回调函数来接收Hvac相关的属性改变。

在CarHvacManager的registerCallback中,首先会通过传入的属性ID获取当前支持的属性列表(如果不支持,那么就在返回列表中就不会存在该属性),传输的ID列表mHvacPropertyIds为Hvac相关的属性ID,然后通过一个for循环,将可支持的属性ID都进行注册监听,并通过CarPropertyEventListenerToBase来接收属性变化。

CarHvacManager.java

public synchronized void registerCallback(CarHvacEventCallback callback)

throws CarNotConnectedException {

if (mCallbacks.isEmpty()) {

mListenerToBase = new CarPropertyEventListenerToBase(this);

}

List configs = getPropertyList();

for (CarPropertyConfig c : configs) {

// Register each individual propertyId

mCarPropertyMgr.registerListener(mListenerToBase, c.getPropertyId(), 0);

}

mCallbacks.add(callback);

}

public List getPropertyList() throws CarNotConnectedException {

return mCarPropertyMgr.getPropertyList(mHvacPropertyIds);

}

在CarPropertyManager中进行简单的判断后调用CarPropertyService中的registerListener方法;

CarPropertyManager.java

public boolean registerListener(CarPropertyEventListener listener, int propertyId, float rate)

throws CarNotConnectedException {

synchronized (mActivePropertyListener) {

if (mCarPropertyEventToService == null) {

mCarPropertyEventToService = new CarPropertyEventListenerToService(this);

}

boolean needsServerUpdate = false;

CarPropertyListeners listeners;

listeners = mActivePropertyListener.get(propertyId);

if (listeners == null) {

listeners = new CarPropertyListeners(rate);

mActivePropertyListener.put(propertyId, listeners);

needsServerUpdate = true;

}

if (listeners.addAndUpdateRate(listener, rate)) {

needsServerUpdate = true;

}

if (needsServerUpdate) {

if (!registerOrUpdatePropertyListener(propertyId, rate)) {

return false;

}

}

}

return true;

}

private boolean registerOrUpdatePropertyListener(int propertyId, float rate)

throws CarNotConnectedException {

try {

mService.registerListener(propertyId, rate, mCarPropertyEventToService);

} catch (IllegalStateException e) {

CarApiUtil.checkCarNotConnectedExceptionFromCarService(e);

} catch (RemoteException e) {

throw new CarNotConnectedException(e);

}

return true;

}

在CarPropertyService的registerListener方法中,会判断当前要监听的属性是否已经监听,如果没有监听则会调用PropertyHalService中的subscribeProperty方法来监听该属性的变化;

CarPropertyService.java

@Override

public void registerListener(int propId, float rate, ICarPropertyEventListener listener) {

……

IBinder listenerBinder = listener.asBinder();

synchronized (mLock) {

// Get the client for this listener

Client client = mClientMap.get(listenerBinder);

if (client == null) {

client = new Client(listener);

}

client.addProperty(propId, rate);

// Insert the client into the propId --> clients map

List clients = mPropIdClientMap.get(propId);

if (clients == null) {

clients = new CopyOnWriteArrayList();

mPropIdClientMap.put(propId, clients);

}

if (!clients.contains(client)) {

clients.add(client);

}

// Set the HAL listener if necessary

if (!mListenerIsSet) {

mHal.setListener(this);

}

// Set the new rate

if (rate > mHal.getSampleRate(propId)) {

mHal.subscribeProperty(propId, rate);

}

}

……

}

在PropertyHalService中,进行简单处理进入到VehicleHal的subscribePropert的方法;

PropertyHalService.java

public void subscribeProperty(int propId, float rate) {

……

synchronized (mSubscribedPropIds) {

mSubscribedPropIds.add(halPropId);

}

mVehicleHal.subscribeProperty(this, halPropId, rate);

}

在VehicleHal的subscribeProperty方法中,判断当前要注册监听的属性是否是可注册的(即该属性是否可读或者是可更改的),然后进入HalClient的subscribe方法进行注册;

VehicleHal.java

public void subscribeProperty(HalServiceBase service, int property,

float samplingRateHz, int flags) throws IllegalArgumentException {

……

if (config == null) {

throw new IllegalArgumentException("subscribe error: config is null for property 0x" +

toHexString(property));

} else if (isPropertySubscribable(config)) {

SubscribeOptions opts = new SubscribeOptions();

opts.propId = property;

opts.sampleRate = samplingRateHz;

opts.flags = flags;

synchronized (this) {

assertServiceOwnerLocked(service, property);

mSubscribedProperties.put(property, opts);

}

try {

mHalClient.subscribe(opts);

} catch (RemoteException e) {

Log.e(CarLog.TAG_HAL, "Failed to subscribe to property: 0x" + property, e);

}

} else {

Log.e(CarLog.TAG_HAL, "Cannot subscribe to property: " + property);

}

}

在HalClient中,属性合法性问题都在之前进行了处理,这里直接通过Ivehicle获得的服务端方法进行注册,之后会通过HIDL调用到EmulatedVehicleHal的subscribe方法,在该方法中除了传入需要监听的属性之外,还传入了一个回调对象VehicleCallback。VehicleCallback继承自IVehicleCallback.Stub并实现了该类中的相关方法,当hal服务端属性信息变化时,会通过该回调把信息传回来,客户端会做出相应的处理。

HalClient.java

public void subscribe(SubscribeOptions... options) throws RemoteException {

mVehicle.subscribe(mInternalCallback, new ArrayList<>(Arrays.asList(options)));

}

至此,Car的属性注册及监听过程结束。

8.Car获取CarHvacManager

Car获取Car相关的manager都是通过传入要获取manager的名称,类似于getSystemService的方法,然后会返回相关的manager

获取manager是通过CarServiceLoaderEmbedded中的getCarManager方法

Car.java

public Object getCarManager(String serviceName)

throws CarNotConnectedException {

Object manager = null;

synchronized (mCarManagerLock) {

manager = mServiceMap.get(serviceName);

if (manager == null) {

manager = mCarServiceLoader.getCarManager(serviceName);

}

// do not store if it is not CarManagerBase. This can happen when system version

// is retrieved from this call.

if (manager != null && manager instanceof CarManagerBase) {

mServiceMap.put(serviceName, (CarManagerBase) manager);

}

}

return manager;

}

这里的mEmbeddedCar为包android.car下面的Car,而我们之前使用的Car为android.support.car下面的Car;

CarServiceLoaderEmbedded.java

public Object getCarManager(String serviceName) throws CarNotConnectedException {

Object manager;

try {

manager = mEmbeddedCar.getCarManager(serviceName);

} catch (android.car.CarNotConnectedException e) {

throw new CarNotConnectedException(e);

}

……

default:

return manager;

}

}

通过传入的manager名称来获取服务,然后为该服务创建一个manager并返回给请求者

其中ICar的getCarService的实现在ICarImpl中;

Car.java

public Object getCarManager(String serviceName) throws CarNotConnectedException {

CarManagerBase manager;

ICar service = getICarOrThrow();

synchronized (mCarManagerLock) {

manager = mServiceMap.get(serviceName);

if (manager == null) {

try {

IBinder binder = service.getCarService(serviceName);

……

manager = createCarManager(serviceName, binder);

……

mServiceMap.put(serviceName, manager);

} catch (RemoteException e) {

handleRemoteException(e);

}

}

}

return manager;

}

通过获取的服务新建manager实例,然后返回;

Car.java

private CarManagerBase createCarManager(String serviceName, IBinder binder)

throws CarNotConnectedException {

CarManagerBase manager = null;

switch (serviceName) {

……

case HVAC_SERVICE:

manager = new CarHvacManager(binder, mContext, mEventHandler);

break;

……

default:

break;

}

return manager;

}

可以看到,在请求舱室,暖通空调,信息,属性,传感器以及三方拓展服务是返回的都是CarPropertyService服务

IcarImpl.java

@Override

public IBinder getCarService(String serviceName) {

switch (serviceName) {

case Car.AUDIO_SERVICE:

return mCarAudioService;

……

case Car.CABIN_SERVICE:

case Car.HVAC_SERVICE:

case Car.INFO_SERVICE:

case Car.PROPERTY_SERVICE:

case Car.SENSOR_SERVICE:

case Car.VENDOR_EXTENSION_SERVICE:

return mCarPropertyService;

……

default:

Log.w(CarLog.TAG_SERVICE, "getCarService for unknown service:" + serviceName);

return null;

}

}

至此,获取manager的过程结束。

9.Car获取CarServie

这里的Car为android.car包下的Car

在CarService中定义了action

Car.java

public static final String CAR_SERVICE_INTERFACE_NAME = "android.car.ICar";

private void startCarService() {

Intent intent = new Intent();

intent.setPackage(CAR_SERVICE_PACKAGE);

intent.setAction(Car.CAR_SERVICE_INTERFACE_NAME);

boolean bound = mContext.bindServiceAsUser(intent, mServiceConnectionListener,

Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF);

……

}

AndroidManifest.xml

android:singleUser="true">

10.与Hal交互的数据类型

在Hal层定义了一系列的属性值,都放在了types.hal文件中。

如座椅属性与Hvac中传入的VehicleAreaSeat.java中的座椅属性,两者的值是相同的

types.hal

/**

* Various Seats in the car.

*/

enum VehicleAreaSeat : int32_t {

ROW_1_LEFT = 0x0001,

ROW_1_CENTER = 0x0002,

ROW_1_RIGHT = 0x0004,

ROW_2_LEFT = 0x0010,

ROW_2_CENTER = 0x0020,

ROW_2_RIGHT = 0x0040,

ROW_3_LEFT = 0x0100,

ROW_3_CENTER = 0x0200,

ROW_3_RIGHT = 0x0400

};

VehicleAreaSeat.java

public static final int SEAT_ROW_1_LEFT = 0x0001;

public static final int SEAT_ROW_1_CENTER = 0x0002;

public static final int SEAT_ROW_1_RIGHT = 0x0004;

public static final int SEAT_ROW_2_LEFT = 0x0010;

public static final int SEAT_ROW_2_CENTER = 0x0020;

public static final int SEAT_ROW_2_RIGHT = 0x0040;

public static final int SEAT_ROW_3_LEFT = 0x0100;

public static final int SEAT_ROW_3_CENTER = 0x0200;

public static final int SEAT_ROW_3_RIGHT = 0x0400;

11.车机属性与权限

在设置Hvac时,用到了暖通空调相关的属性。

在车机中,与PropertyHalService有关的属性可分为4种舱室属性(车门,车镜,座椅等),HVAC属性(空调,风扇等),信息属性(制造商,型号,ID等)以及传感器属性(车速,油量等),与修改这些属性需要的相关权限都被定义在PropertyHalServiceIds文件中。

PropertyHalServiceIds.java

// Index (key is propertyId, and the value is readPermission, writePermission

private final SparseArray> mProps;

// HVAC properties

mProps.put(VehicleProperty.HVAC_FAN_SPEED, new Pair<>(

Car.PERMISSION_CONTROL_CAR_CLIMATE,

Car.PERMISSION_CONTROL_CAR_CLIMATE));

mProps.put(VehicleProperty.HVAC_FAN_DIRECTION, new Pair<>(

Car.PERMISSION_CONTROL_CAR_CLIMATE,

Car.PERMISSION_CONTROL_CAR_CLIMATE));

mProps.put(VehicleProperty.HVAC_TEMPERATURE_CURRENT, new Pair<>(

Car.PERMISSION_CONTROL_CAR_CLIMATE,

Car.PERMISSION_CONTROL_CAR_CLIMATE));

mProps.put(VehicleProperty.HVAC_TEMPERATURE_SET, new Pair<>(

Car.PERMISSION_CONTROL_CAR_CLIMATE,

Car.PERMISSION_CONTROL_CAR_CLIMATE));

mProps.put(VehicleProperty.HVAC_DEFROSTER, new Pair<>(

Car.PERMISSION_CONTROL_CAR_CLIMATE,

Car.PERMISSION_CONTROL_CAR_CLIMATE));

12.CarInfoManager

用于获取车辆固定属性信息的工具,这些信息都是不可变的。

获取这些信息都是通过CarPropertyService获得的

信息有:

BASIC_INFO_KEY_MANUFACTURER //制造商

BASIC_INFO_KEY_MODEL //型号

BASIC_INFO_KEY_MODEL_YEAR //

BASIC_INFO_KEY_VEHICLE_ID //车机ID

INFO_KEY_PRODUCT_CONFIGURATION //配置相关

BASIC_INFO_FUEL_CAPACITY //油箱容量

BASIC_INFO_FUEL_TYPES //燃油类型

BASIC_INFO_EV_BATTERY_CAPACITY //电源容量

BASIC_INFO_EV_CONNECTOR_TYPES //电源连接类型

13.CarPropertyService

CarPropertyService 实现了IcarProperty.aidl接口,控制各manager对车机属性的注册,监听以及读写。当属性发生变化时,回调给注册监听了该属性的manager,然后继续向上传递,使得各属性manager方便的处理车机相关属性。该类对车机属性的处理是通过PropertyHalService进行的,而在PropertyHalService中的操作为VehicleHal。

14.PropertyHalService

PropertyHalService是汽车HAL用来传输车辆属性的服务。PropertyHalService是继承自HalServiceBase,继承HalServiceBase的还有InputHalService, SensorHalService,PowerHalService以及VmsHalService。

PropertyHalService为CarPropertyService提供对各属性的操作。

15.HIDL

16.HAL

17.Treble & HIDL

automotive 安卓开发_Android Automotive相关推荐

  1. automotive 安卓开发_谷歌首次提及「Android Automotive」,是安卓车机系统亮相前奏?...

    谷歌近日放出了Android 6.0 Marshmallow版本的兼容性测试纲要(ACDD,Android Compatibility Definition Document),如果细细阅读,你会意外 ...

  2. java基础学安卓开发_Android开发学习路线之Java基础学习

    原标题:Android开发学习路线之Java基础学习 很多Android学习开发者刚入手Android开发技术时,稍微有点迫切.任何的开发技术都有其基础语言,Android应用程序开发是以Java语言 ...

  3. form字体和颜色java安卓开发_Android 修改App中默认TextView的字体和颜色

    一.别人怎么做 来源 http://stackoverflow.com/questions/3078081/setting-global-styles-for-views-in-android Act ...

  4. android 橡皮擦功能吗,android,安卓开发_Android 图片涂鸦橡皮擦功能,android,安卓开发 - phpStudy...

    Android 图片涂鸦橡皮擦功能 最近在做一个画板功能,大致的不同颜色画笔.不同粗细已经实现. 参照的是该教程:android-drawing-app 现在要做的功能是,从相册或者相机导入图像,然后 ...

  5. 从0开始安卓开发之路_Android Studio安装包

    Android Studio原本是基于JAVA IDEA下的一个安卓开发插件,后被谷歌从插件中移了出来,成为独立安卓开发IDE,但语言是基于JAVA! 但是如果想要下载此IDE就要到谷歌官方的应用商店 ...

  6. java安卓开发工具_Android开发必备那些工具

    工欲善其事,必先利其器,在Android项目的开发中,借助工具能使开发效率大幅提升,下面分享我经常使用的工具,欢迎各位同学补充. 1.Android Studio Android程序员的吃饭工具,可以 ...

  7. 安卓开发学习日记第一天(笑)_Android Studio3.6安装_莫韵乐的快乐笔记

    安卓开发学习第一天 Android Studio3.6安装 没想到终于要写出自己的第一个博客了 反正是第一篇,有没有人看都无所谓(理智:129/129) 万事开头难,做下去就容易很多了 言归正传 工欲 ...

  8. 安卓开发学习日记第四天番外篇_用Kotlin炒冷饭——越炒越小_莫韵乐的欢乐笔记

    安卓开发学习日记第四天番外篇--用Kotlin炒冷饭--越炒越小 前情提要 安卓开发学习日记第一天_Android Studio3.6安装 安卓开发学习日记第二天_破坏陷阱卡之sync的坑 安卓开发学 ...

  9. 安卓开发学习日记第五天——奇怪的bug出现了(VT-x说没就没)_莫韵乐的欢乐日记

    安卓开发学习日记第五天--奇怪的bug出现了(VT-x说没就没) 前情提要: 安卓开发学习日记第一天_Android Studio3.6安装 安卓开发学习日记第二天_破坏陷阱卡之sync的坑 安卓开发 ...

最新文章

  1. 手把手教你 Socket 通信(TCP/IP)
  2. 第四代测序(纳米孔测序)有望全面代替边合成边测序吗?
  3. DB2 创建数据库、缓冲池和表空间
  4. Java_Notes_基础排序总结与对比
  5. Web前端开发笔记——第四章 JavaScript程序设计 第二节 变量
  6. javascript中索引_如何在JavaScript中找到数字在数组中所属的索引
  7. 机器学习算法中的F值(F-Measure)、准确率(Precision)、召回率(Recall)
  8. 190615每日一句;每个学生都不能错过的9个人生忠告; 什么样心态的人,才能取得最终的成功
  9. 中控考勤机忘记密码处理
  10. css html5布局方式_创建新HTML5&CSS3单页布局– Rock门户
  11. iPhone/iPad的IAP防破解之第三方服务器二次验证
  12. 第一篇,从0开始安装Ubuntu
  13. Python - 寻找数组的子集
  14. 互联网晚报 | 10月20日 星期三 | 小米汽车预计2024年量产;阿里发布自研CPU芯片倚天710;《长津湖》票房破50亿元...
  15. 全球与中国葡萄酒保鲜工具市场现状及未来发展趋势
  16. php不支持png图片裁剪,thinkphp5.1 图片处理类think-image的png 缩略,裁剪和添加水印透明度丢失的问题...
  17. 为什么要进行个人知识管理(PKM)
  18. 五十分钟带你看遍C语言初阶语法(总纲)
  19. 初级商业数字营销师钻展题库
  20. 【陈工笔记】# ubuntu系统登陆密码忘记,如何修改 #

热门文章

  1. Echarts升级2:柱状图头部显示百分比、同时内容在弹框显示
  2. 再php用if语句实现成绩,php中if语句怎么编写学生成绩等级
  3. 【2022团体程序设计天梯赛】GPLT2022,L1~L2部分(PTA,L1-081~L1-088,L2-041~L2-044)题解代码复盘
  4. 高速HDMI接口PCB板布线layout指南
  5. 多路YUV_RGB文件播放器
  6. 在线普通法测试软件,法宣在线官网登录平台app
  7. 上海二本计算机学校有哪些,上海有哪些二本大学 附所有二本大学名单
  8. 我的网络通信相关文章
  9. CentOS下安装及使用宝塔面板
  10. php获取拼音首字母排序,PHP获取汉字拼音首字母的方法