1.Hvac
Hvac:供暖通風(fēng)與空氣調(diào)節(jié)(Heating Ventilation and Air Conditioning)
在Android Automotive中评肆,Hvac是作為控制汽車供暖通風(fēng)與空氣調(diào)節(jié)的系統(tǒng)應(yīng)用债查,如下為Hvac源碼目錄結(jié)構(gòu)及相關(guān)說(shuō)明:
│ BootCompleteReceiver.java 用于開(kāi)機(jī)啟動(dòng)HvacUiService
│ DataStore.java 存儲(chǔ)管理Hvac屬性數(shù)據(jù)
│ HvacController.java Hvac屬性控制類
│ HvacPolicy.java Hvac屬性規(guī)則類
│ HvacUiService.java Hvac界面服務(wù)
│ LocalHvacPropertyService.java Demo模式使用的模擬服務(wù)
├─controllers
│ FanDirectionButtonsController.java 風(fēng)扇方向控制
│ FanSpeedBarController.java 風(fēng)扇速度控制
│ HvacPanelController.java Hvac界面布局及相關(guān)邏輯
│ 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文件中,一共注冊(cè)了三個(gè)組件
服務(wù)HvacController:用于設(shè)置及獲取Hvac相關(guān)屬性
服務(wù)HvacUiService:用于顯示Hvac的界面
廣播接收器BootCompleteReceiver:接收開(kāi)機(jī)啟動(dòng)廣播并啟動(dòng)HvacUiService服務(wù)
3.Hvac啟動(dòng)
應(yīng)用在Manifest文件中注冊(cè)了開(kāi)機(jī)廣播瓜挽,所以在開(kāi)機(jī)完成后會(huì)收到開(kāi)機(jī)完成的廣播盹廷,然后再?gòu)V播接收器里面會(huì)啟動(dòng)HvacUiService。
BootCompleteReceiver.java
@Override
public void onReceive(Context context, Intent intent) {
Intent hvacUiService = new Intent(context, HvacUiService.class);
context.startService(hvacUiService);
}
在HvacUiService啟動(dòng)的時(shí)候?qū)嵗疕vacPanelController并綁定HvacController服務(wù)久橙。
HvacUiService服務(wù)通過(guò)WindowManager將View添加Window中俄占,并且給HvacPanelController傳入了布局信息,然后在HvacPanelController中進(jìn)行view的layout操作剥汤。并且在HvacPanelController包含了controller包下的FanSpeedBarController,SeatWarmerController等幾個(gè)類排惨,使得HvacPanelController可以作為Hvac的界面統(tǒng)一控制類吭敢。
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.");
}
當(dāng)HvacUiService成功綁定HvacController時(shí),HvacPanelController會(huì)通過(guò)HvacController獲取Hvac屬性進(jìn)行界面更新暮芭,并且HvacController也會(huì)從系統(tǒng)中讀取各屬性值鹿驼,并存取在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()));// 存儲(chǔ)數(shù)據(jù)
}
4.Hvac屬性存儲(chǔ)
DataStore用于存儲(chǔ)HvacController從系統(tǒng)中獲取的屬性值辕宏。
我們可以從兩個(gè)位置進(jìn)行更新Hvac相關(guān)屬性畜晰,即用戶界面和硬件按鈕。這兩種不同的更新方式都是從不同的線程更新到當(dāng)前狀態(tài)瑞筐。此外凄鼻,在某些情況下,暖通空調(diào)系統(tǒng)可能會(huì)發(fā)送虛假的更新聚假,因此這個(gè)類將所有內(nèi)容更新管理合并块蚌,從而確保在用戶看來(lái)應(yīng)用程序的界面是正常的。
如風(fēng)扇速度膘格,DateStore會(huì)在收到系統(tǒng)事件是通過(guò)shouldPropagateFanSpeedUpdate方法判斷是否需要更新峭范,判斷是否要更新的規(guī)則也很簡(jiǎn)單,即最近兩秒內(nèi)是否更新過(guò)該屬性值瘪贱,如果更新過(guò)纱控,那么從系統(tǒng)回調(diào)的屬性值將不會(huì)被Hvac應(yīng)用處理辆毡。
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;
}
比如用戶通過(guò)界面設(shè)置風(fēng)扇速度
此時(shí)記錄了最近設(shè)置風(fēng)扇速度的時(shí)間mLastFanSpeedSet,而如果Hvac在兩秒內(nèi)收到系統(tǒng)的回調(diào)甜害,可能是系統(tǒng)的錯(cuò)誤信息也可能是通過(guò)風(fēng)扇實(shí)體按鈕改變的回調(diào)信息舶掖,那么這樣的信息是不會(huì)顯示在界面上的(這點(diǎn)有待驗(yàn)證)。
HvacController.java
public void setFanSpeed(final int fanSpeed) {
mDataStore.setFanSpeed(fanSpeed);
final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
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應(yīng)用與系統(tǒng)的信息傳輸控制器唾那。
在Hvac中的設(shè)置及獲取操作都是通過(guò)HvacController進(jìn)行的访锻,在HvacController啟動(dòng)時(shí)會(huì)獲取一個(gè)Car實(shí)例脑题,并通過(guò)connect方法連接CarService仇味。
HvacController.java
mCarApiClient = Car.createCar(this, mCarConnectionCallback);
mCarApiClient.connect();
當(dāng)成功連接CarService時(shí),會(huì)進(jìn)行初始化CarHvacManager并通過(guò)CarHvacManager獲取車輛支持的屬性列表财岔,然后通知HvacPanelController已經(jīng)成連接CarService并更新相關(guān)屬性存儲(chǔ)到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<CarPropertyConfig> 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中有兩個(gè)重要的對(duì)象龟虎,Car和CarHvacManager,HvacController正是通過(guò)這兩個(gè)對(duì)象與CarService進(jìn)行通信沙庐。
(1)Car
Car作為汽車平臺(tái)最高等級(jí)的API為外界提供汽車所有服務(wù)和數(shù)據(jù)的訪問(wèn)鲤妥。
通過(guò)createCar方法可以新建一個(gè)Car實(shí)例,通過(guò)connect方法連接CarService拱雏。當(dāng)成功連接時(shí)可以通過(guò)getCarManager方法獲取一個(gè)一個(gè)相關(guān)的manager棉安,比如Hvac通過(guò)getCarManager方法獲取了一個(gè)CarHvacManager,當(dāng)獲取到manager后就可以進(jìn)行相關(guān)操作了铸抑。
(2)CarHvacManager
CarHvacManager作為控制汽車Hvac系統(tǒng)的API為外界提供數(shù)據(jù)訪問(wèn)贡耽。
CarHvacManager實(shí)現(xiàn)了CarManagerBase接口,并且只要是作為Car*Manager, 都需要實(shí)現(xiàn)CarManagerBase接口,如CarCabinManager鹊汛,CarSensorManager等都實(shí)現(xiàn)了該接口蒲赂。
CarHvacManager的控制操作是通過(guò)CarPropertyManager來(lái)完成的,CarPropertyManager統(tǒng)一控制汽車屬性相關(guān)的操作刁憋。CarHvacManager只是控制與Hvac相關(guān)的操作滥嘴,在汽車中還有很多屬性控制的Manager,如傳感器至耻,座艙等屬性的控制若皱,他們都是通過(guò)CarPropertyManager進(jìn)行屬性操作,通過(guò)在操作時(shí)傳入的屬性ID尘颓,屬性區(qū)域以及屬性值是尖,在CarPropertyManager中會(huì)將這些參數(shù)轉(zhuǎn)化為一個(gè)CarPropertyValue對(duì)象繼續(xù)往下層傳遞。
CarHvacManager控制的屬性有:
// 全局屬性泥耀,只有一個(gè)
ID_MIRROR_DEFROSTER_ON //視鏡除霧
ID_STEERING_WHEEL_HEAT //方向盤溫度
ID_OUTSIDE_AIR_TEMP //室外溫度
ID_TEMPERATURE_DISPLAY_UNITS //在使用的溫度
// 區(qū)域?qū)傩越刃冢稍诓煌瑓^(qū)域設(shè)置
ID_ZONED_TEMP_SETPOINT //用戶設(shè)置的溫度
ID_ZONED_TEMP_ACTUAL //區(qū)域?qū)嶋H溫度
ID_ZONED_HVAC_POWER_ON //HVAC系統(tǒng)電源開(kāi)關(guān)
ID_ZONED_FAN_SPEED_SETPOINT //風(fēng)扇設(shè)置的速度
ID_ZONED_FAN_SPEED_RPM //風(fēng)扇實(shí)際的速度
ID_ZONED_FAN_DIRECTION_AVAILABLE //風(fēng)扇可設(shè)置的方向
ID_ZONED_FAN_DIRECTION //現(xiàn)在風(fēng)扇設(shè)置的方向
ID_ZONED_SEAT_TEMP //座椅溫度
ID_ZONED_AC_ON //空調(diào)開(kāi)關(guān)
ID_ZONED_AUTOMATIC_MODE_ON //HVAC自動(dòng)模式開(kāi)關(guān)
ID_ZONED_AIR_RECIRCULATION_ON //空氣循環(huán)開(kāi)關(guān)
ID_ZONED_MAX_AC_ON //空調(diào)最大速度開(kāi)關(guān)
ID_ZONED_DUAL_ZONE_ON //雙區(qū)模式開(kāi)關(guān)
ID_ZONED_MAX_DEFROST_ON //最大除霧開(kāi)關(guān)
ID_ZONED_HVAC_AUTO_RECIRC_ON //自動(dòng)循環(huán)模式開(kāi)關(guān)
ID_WINDOW_DEFROSTER_ON //除霧模式開(kāi)關(guān)
6.一次設(shè)置過(guò)程
現(xiàn)在我們跟蹤一次風(fēng)扇速度的設(shè)置過(guò)程。
點(diǎn)擊Hvac界面中的風(fēng)扇速度最大按鈕時(shí)痰催,通過(guò)在HvacPanelController中實(shí)例化時(shí)傳入的mHvacController對(duì)象來(lái)設(shè)置最大速度兜辞。
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迎瞧;
首先會(huì)將這個(gè)值存儲(chǔ)到DataStore中,然后通過(guò)CarHvacManager的setInProperty方法設(shè)置
傳入屬性ID為ID_ZONED_FAN_SPEED_SETPOINT逸吵,屬性區(qū)域?yàn)镾EAT_ALL凶硅,屬性值為要設(shè)置的風(fēng)扇速度。
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<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
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中進(jìn)行了屬性ID合法性的檢查扫皱,判斷這個(gè)屬性是否為Hvac相關(guān)的屬性足绅。然后通過(guò)CarPropertyManager進(jìn)行設(shè)置。
CarHvacManager.java
public void setIntProperty(@PropertyId int propertyId, int area, int val)
throws CarNotConnectedException {
if (mHvacPropertyIds.contains(propertyId)) {
mCarPropertyMgr.setIntProperty(propertyId, area, val);
}
}
在CarPropertyManager中繼續(xù)進(jìn)行屬性設(shè)置韩脑,最后是通過(guò)CarPropertyService進(jìn)行操作的氢妈,
而CarPropertyService是Car調(diào)用getCarManager時(shí)通過(guò)AIDL綁定的Service,所以可能會(huì)出現(xiàn)RemoteException段多,而出現(xiàn)這個(gè)異常時(shí)首量,意味著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 <E> void setProperty(Class<E> 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中加缘,會(huì)再次對(duì)屬性ID合法性進(jìn)行判斷,并判斷要修改該屬性的應(yīng)用是否有權(quán)限觉啊,最后通過(guò)PropertyHalService進(jìn)行設(shè)置拣宏。
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屬性值對(duì)象轉(zhuǎn)換為Vehicle的Hal屬性值對(duì)象杠人,并判斷此車機(jī)是否支持該屬性勋乾,然后通過(guò)VehicleHal進(jìn)行設(shè)置,VehiclePropValue是在Hal層定義的一個(gè)數(shù)據(jù)類型搜吧。
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<int32_t> int32Values;
/**
* This is used for properties of types VehiclePropertyType#FLOAT
* and VehiclePropertyType#FLOAT_VEC
*/
vec<float> floatValues;
/** This is used for properties of type VehiclePropertyType#INT64 */
vec<int64_t> int64Values;
/** This is used for properties of type VehiclePropertyType#BYTES */
vec<uint8_t> bytes;
/** This is used for properties of type VehiclePropertyType#STRING */
string stringValue;
};
RawValue value;
};
VehicleHal為車輛HAL的抽象市俊。此類處理與本機(jī)HAL的接口信息杨凑,并對(duì)接收到的數(shù)據(jù)進(jìn)行基本分析(類型檢查)滤奈。
在這里的set方法中,使用的是HalClient的setValue方法撩满。
VehicleHal.java
void set(VehiclePropValue propValue) throws PropertyTimeoutException {
mHalClient.setValue(propValue);
}
HalClient為直接與車輛HAL接口交互的類蜒程。
到此時(shí),已經(jīng)是與Hal層通過(guò)HIDL進(jìn)行交互了伺帘,在HalClient實(shí)例化時(shí)昭躺,會(huì)傳入一個(gè)Ivehicle對(duì)象,這個(gè)對(duì)象是在CarService創(chuàng)建時(shí)通過(guò)HIDL獲取的對(duì)象伪嫁,與Hal層進(jìn)行數(shù)據(jù)傳輸领炫。
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通過(guò)HIDL的方式獲取Hal層服務(wù)對(duì)象,獲取的為Hal層VehicleService注冊(cè)的服務(wù)张咳。
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中調(diào)用了IVehicle的set方法帝洪,這個(gè)set方法的真正實(shí)現(xiàn)在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注冊(cè)及監(jiān)聽(tīng)屬性信息
在hardware的vehicle目錄下似舵,總共定義了3個(gè)hal文件:Ivehicle.hal, IvehicleCallback.hal和types.hal文件。在上面的一次設(shè)置過(guò)程中葱峡,我們可以看到在最后與hal層進(jìn)行交互時(shí)調(diào)用的正是Ivehicle.hal中的set方法砚哗,而我們僅僅是通過(guò)CarService中以HIDL方式獲取的Ivehicle服務(wù)來(lái)調(diào)用他,其中復(fù)雜的調(diào)用過(guò)程都由HIDL來(lái)完成砰奕,最終會(huì)調(diào)用到服務(wù)端實(shí)現(xiàn)該接口的方法中去蛛芥,即EmulatedVehicleHal.cpp中的set方法。
這三個(gè)文件對(duì)應(yīng)的功能為:
IVehicle.hal: 定義了服務(wù)端給客戶端調(diào)用的的接口
IVehicleCallback.hal: 定義了服務(wù)端回調(diào)客戶端的接口
types.hal: 定義了需要使用的數(shù)據(jù)結(jié)構(gòu)
在Hvac應(yīng)用中军援,通過(guò)HvacController注冊(cè)了CarHvacManager.CarHvacEventCallback仅淑,我們用這個(gè)回調(diào)函數(shù)來(lái)接收Hvac相關(guān)的屬性改變。
在CarHvacManager的registerCallback中盖溺,首先會(huì)通過(guò)傳入的屬性ID獲取當(dāng)前支持的屬性列表(如果不支持漓糙,那么就在返回列表中就不會(huì)存在該屬性),傳輸?shù)腎D列表mHvacPropertyIds為Hvac相關(guān)的屬性ID烘嘱,然后通過(guò)一個(gè)for循環(huán)昆禽,將可支持的屬性ID都進(jìn)行注冊(cè)監(jiān)聽(tīng),并通過(guò)CarPropertyEventListenerToBase來(lái)接收屬性變化蝇庭。
CarHvacManager.java
public synchronized void registerCallback(CarHvacEventCallback callback)
throws CarNotConnectedException {
if (mCallbacks.isEmpty()) {
mListenerToBase = new CarPropertyEventListenerToBase(this);
}
List<CarPropertyConfig> configs = getPropertyList();
for (CarPropertyConfig c : configs) {
// Register each individual propertyId
mCarPropertyMgr.registerListener(mListenerToBase, c.getPropertyId(), 0);
}
mCallbacks.add(callback);
}
public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
return mCarPropertyMgr.getPropertyList(mHvacPropertyIds);
}
在CarPropertyManager中進(jìn)行簡(jiǎn)單的判斷后調(diào)用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方法中,會(huì)判斷當(dāng)前要監(jiān)聽(tīng)的屬性是否已經(jīng)監(jiān)聽(tīng)哮内,如果沒(méi)有監(jiān)聽(tīng)則會(huì)調(diào)用PropertyHalService中的subscribeProperty方法來(lái)監(jiān)聽(tīng)該屬性的變化盗棵;
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<Client> clients = mPropIdClientMap.get(propId);
if (clients == null) {
clients = new CopyOnWriteArrayList<Client>();
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中,進(jìn)行簡(jiǎn)單處理進(jìn)入到VehicleHal的subscribePropert的方法北发;
PropertyHalService.java
public void subscribeProperty(int propId, float rate) {
……
synchronized (mSubscribedPropIds) {
mSubscribedPropIds.add(halPropId);
}
mVehicleHal.subscribeProperty(this, halPropId, rate);
}
在VehicleHal的subscribeProperty方法中纹因,判斷當(dāng)前要注冊(cè)監(jiān)聽(tīng)的屬性是否是可注冊(cè)的(即該屬性是否可讀或者是可更改的),然后進(jìn)入HalClient的subscribe方法進(jìn)行注冊(cè)琳拨;
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中瞭恰,屬性合法性問(wèn)題都在之前進(jìn)行了處理,這里直接通過(guò)Ivehicle獲得的服務(wù)端方法進(jìn)行注冊(cè)狱庇,之后會(huì)通過(guò)HIDL調(diào)用到EmulatedVehicleHal的subscribe方法惊畏,在該方法中除了傳入需要監(jiān)聽(tīng)的屬性之外,還傳入了一個(gè)回調(diào)對(duì)象VehicleCallback密任。VehicleCallback繼承自IVehicleCallback.Stub并實(shí)現(xiàn)了該類中的相關(guān)方法颜启,當(dāng)hal服務(wù)端屬性信息變化時(shí),會(huì)通過(guò)該回調(diào)把信息傳回來(lái)浪讳,客戶端會(huì)做出相應(yīng)的處理缰盏。
HalClient.java
public void subscribe(SubscribeOptions... options) throws RemoteException {
mVehicle.subscribe(mInternalCallback, new ArrayList<>(Arrays.asList(options)));
}
至此,Car的屬性注冊(cè)及監(jiān)聽(tīng)過(guò)程結(jié)束。
8.Car獲取CarHvacManager
Car獲取Car相關(guān)的manager都是通過(guò)傳入要獲取manager的名稱口猜,類似于getSystemService的方法形葬,然后會(huì)返回相關(guān)的manager
獲取manager是通過(guò)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;
}
}
通過(guò)傳入的manager名稱來(lái)獲取服務(wù)笙以,然后為該服務(wù)創(chuàng)建一個(gè)manager并返回給請(qǐng)求者
其中ICar的getCarService的實(shí)現(xiàn)在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;
}
通過(guò)獲取的服務(wù)新建manager實(shí)例冻辩,然后返回猖腕;
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;
}
可以看到,在請(qǐng)求艙室恨闪,暖通空調(diào)倘感,信息,屬性咙咽,傳感器以及三方拓展服務(wù)是返回的都是CarPropertyService服務(wù)
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的過(guò)程結(jié)束。
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
<service android:name=".CarService"
android:singleUser="true">
<intent-filter>
<action android:name="android.car.ICar" />
</intent-filter>
</service>
10.與Hal交互的數(shù)據(jù)類型
在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.車機(jī)屬性與權(quán)限
在設(shè)置Hvac時(shí)溉苛,用到了暖通空調(diào)相關(guān)的屬性镜廉。
在車機(jī)中,與PropertyHalService有關(guān)的屬性可分為4種艙室屬性(車門愚战,車鏡娇唯,座椅等),HVAC屬性(空調(diào)寂玲,風(fēng)扇等)塔插,信息屬性(制造商,型號(hào)拓哟,ID等)以及傳感器屬性(車速想许,油量等),與修改這些屬性需要的相關(guān)權(quán)限都被定義在PropertyHalServiceIds文件中彰檬。
PropertyHalServiceIds.java
// Index (key is propertyId, and the value is readPermission, writePermission
private final SparseArray<Pair<String, String>> 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
用于獲取車輛固定屬性信息的工具伸刃,這些信息都是不可變的谎砾。
獲取這些信息都是通過(guò)CarPropertyService獲得的
信息有:
BASIC_INFO_KEY_MANUFACTURER //制造商
BASIC_INFO_KEY_MODEL //型號(hào)
BASIC_INFO_KEY_MODEL_YEAR //
BASIC_INFO_KEY_VEHICLE_ID //車機(jī)ID
INFO_KEY_PRODUCT_CONFIGURATION //配置相關(guān)
BASIC_INFO_FUEL_CAPACITY //油箱容量
BASIC_INFO_FUEL_TYPES //燃油類型
BASIC_INFO_EV_BATTERY_CAPACITY //電源容量
BASIC_INFO_EV_CONNECTOR_TYPES //電源連接類型
13.CarPropertyService
CarPropertyService 實(shí)現(xiàn)了IcarProperty.aidl接口逢倍,控制各manager對(duì)車機(jī)屬性的注冊(cè),監(jiān)聽(tīng)以及讀寫景图。當(dāng)屬性發(fā)生變化時(shí)较雕,回調(diào)給注冊(cè)監(jiān)聽(tīng)了該屬性的manager,然后繼續(xù)向上傳遞,使得各屬性manager方便的處理車機(jī)相關(guān)屬性亮蒋。該類對(duì)車機(jī)屬性的處理是通過(guò)PropertyHalService進(jìn)行的扣典,而在PropertyHalService中的操作為VehicleHal。
14.PropertyHalService
PropertyHalService是汽車HAL用來(lái)傳輸車輛屬性的服務(wù)慎玖。PropertyHalService是繼承自HalServiceBase贮尖,繼承HalServiceBase的還有InputHalService, SensorHalService,PowerHalService以及VmsHalService。
PropertyHalService為CarPropertyService提供對(duì)各屬性的操作趁怔。
15.HIDL
一次使用HIDL實(shí)現(xiàn)簡(jiǎn)單的HAL通信過(guò)程