重要类的源码文件名及位置:
- CarPropertyManager.java
packages/services/Car/car-lib/src/android/car/hardware/property/
- CarPropertyService.java
packages/services/Car/service/src/com/android/car/
类的介绍:
CarPropertyManager:是CarPropertyService在客户端的代理,通过 CarPropertyManager中 提供的 API,可以设置和获取车辆各个属性的状态。但在实际使用时,CarPropertyManager 却未必是开发者使用最频繁的对象。尤其是在 Android9平台上开发时,当开发者想控制 空调相关的功能的时候,也许会使用 CarHvacManager;当想获取车辆信息的时候,也许会 使用 CarInfoManager;当想获取车辆传感器数据的时候,会使用 CarSensorManager。但其 实无论是 CarHvacManager还是 CarInfoManager或是 CarSensorManager,它们最后都会 通过 CarPropertyManager来获取设置属性,在服务端对应的都是 CarPropertyService。通 过ICarImpl中getCarService方法也可以很清楚地发现这一点,在Android 10中,谷歌官方直接推荐使用CarPropertyManager;
CarPropertyService:绝大部分与车辆硬件相关联的属性,如空调、车舱功能、车辆传感器等都是通过CarPropertyService来读取或者设置的。
类的使用:
CarPropertyManager:在安卓9中CarPropertyManger还是隐藏(hide)接口,所以不会在公开的SDK中出现,但是它十分重要。而在Android 10中,CarPropertyManger变成了车辆属性的主要API,并允许任何运行在Android Automotive OS上的应用进行调用。初看CarPropertyManger会觉得很熟悉,它的方法包括(但不限于)以下这些:
boolean registerListener(CarPropertyEventCallback callback, int prop, float rate)
boolean isPropertyAvailable(int propId, int area)
boolean getBooleanProperty(int prop, int area)
float getFloatProperty(int prop, int area)
int getIntProperty(int prop, int area)
int[] getIntArrayProperty(int prop, int area)
<E> CarPropertyValue <E> getProperty(Class <E> clazz, int propId, int area)
<E> CarPropertyValue <E> getProperty(int prop, int area)
<E> void setProperty()(Class <E> clazz, int propId, int area,E val)
void setBooleanProperty(int prop, int area, boolean val)
void setFloatProperty (int prop int area, float val)
void setIntProperty (int prop, int area, int val)
看到这些方法,就会发现和 CarHvacManager、CarVendorExtensionManager等服务中 的方法定 义 很 类 似。在 使 用 方 法 上 和 之 前 提 到 的 几 个 服 务 也 是 一 样 的。其 实,无 论 是 CarInfoManager,还是 CarSensorManager或 CarHvacManager,它们的功能都可以直接通 过 CarPropertyManager来完成。
1.CarPropertyManager的用法
熟悉了 CarHvacManager、CarVendorExtensionManager等几个相关服务的用法之后, 在 CarPropertyManager的使用上,相信读者对相关方法已经很了解了。这里再做一些简单 的补充。 关于属 性 的 获 取,在 CarPropertyManager 中 除 了 getProperty 方 法 之 外,还 有 像 getBooleanProperty、getIntProperty这样明确属性类型的获取方法。其实这些方法只是对 于getProperty方法的封装,以getIntProperty为例,它的实现是这样的:
看上去在明确知道属性类型的情况下,getBooleanProperty、getIntProperty等方法在 使用上更加简洁。但是在这里,依然推荐开发者们使用getProperty来获取相应的属性值, 因为getProperty方法返回的是 CarPropertyValue对象,其不仅包含属性值,还包含属性的 状态,而getIntProperty等方法在属性不可用的情况下,返回的是默认值,这在有的时候会 导致读取的数据不正确。 下面以 NIGHT_MODE(昼夜模式)属性为例,说明使用getProperty方法的好处。
从这段源码中,可以很直观地看到使用getProperty方法,与getBooleanProperty方法 获取昼夜状态相比,可以更准确地判断当前属性的状态,并在属性不支持或不可用时,使用 更合理的处理逻辑。因此,虽然 getProperty方法会增加源码的数量,但在大部分情况下, 依然推荐大家使用该方式获取属性。
在注册 监 听 属 性 变 化 方 面,CarPropertyManager 提 供 更 细 颗 粒 度 的 监 听 方 法, registerListener① 方法可以监听单个属性值的变化,开发者可以通过在注册监听器时传入 属性ID指定监听器所对应的属性。同时,可以指定数据上报的频率,与5.2节介绍的一样, 该频率与属性类型和其他监听器的频率有关,并不能保证数据会以传入的期望频率进行上 报。监听属性的方式,可参考以下源码:
CarPropertyManager.CarPropertyEventListener mCarPropertyEventListener = new CarPropertyManager.CarPropertyEventListener() {
@Override
public void onChangeEvent(CarPropertyValue value) {
}
@Override
public void onErrorEvent(int propId, int zone) {
}
};
mCarPropertyManager.registerListener(mCarPropertyEventListener,VehiclePropertyIds.PERF_VEHICLE_SPEED,/ * rate=* / 5);
2.CarPropertyManager的相关类
除了和 CarPropertyManager相关的这几个 Manager之外,在之前的例子中,还出现了 如 VehiclePropertyIds、CarPropertyValue、CarPropertyConfig等相关的辅助类,由于种类 繁多,有必要在这里梳理一下各个辅助类的作用。
(1)VehiclePropertyIds,CarPropertyManager都是通过属性ID来对应具体的功能的, 不同功能对应不同的ID,VehiclePropertyIds中列出了所有在 VehicleHAL 中定义的功能 属性,是AndroidAutomotiveOS官方定义的属性集合。
(2)VehicleAreaDoor,许多功能点都分多个区域,在设置、获取相应属性时,需要传入区域 参数,VehicleAreaDoor定义了与车门相关的区域值,在使用与车门相关的属性时配套使用。
(3)VehicleAreaMirror,与 VehicleAreaDoor类似,多区域定义,后视镜区域值。
(4)VehicleAreaSeat,多区域定义,座位区域值。
(5)VehicleAreaWheel,多区域定义,车胎区域值。
(6)VehicleAreaWindow,多区域定义,车窗区域值。
(7)VehicleAreaType,区域类型是用以区分一个属性所对应的位置的。对于非多区域 属性,往往使用 VEHICLE_AREA_TYPE_GLOBAL 作为其区域ID。每个区域属性都必 须使用预定义的区域类型,即车门、车窗、座椅、轮胎、后视镜中的一个。每种区域类型都有 一组在区域类型的枚举中定义的位标记,也就是前文中使用的像SEAT_ROW_1_LEFT 这 53 第 5 章 CarPropertyService———车辆属性服务 样具体的区域值。
(8)VehicleLightState,灯光状态,开、关、日间。
(9)VehicleLightSwitch,灯光切换,开、关、日间、自动。
(10)VehicleOilLevel,油量状态。
以上这些辅助类中,都定义了相关的静态变量,同时,这些值都与 VehicleHAL 的相关 定义一一对应,在 CarAPI中将其再次定义是为了方便上层应用使用。
除了以上 几 个 定 义 静 态 变 量 的 辅 助 类 以 外,还 会 经 常 用 到 CarPropertyConfig 和 CarPropertyValue这两个模板类。CarPropertyConfig和 CarPropertyValue非常有用,通 过前者能获取到一个属性的静态参数,如取值范围、类型、支持的区域等;通过后者能获取 一个属性的值和状态。
这里举两个简单的例子。
(1)通过 CarPropertyManager获取当前车辆支持的属性(注意,需要拥有对应属性的 权限才能获取)。
CarPropertyConfig对象的成员变量如表5-4所示。
开发者可以通过以上成员变量对应的 get方法获取具体的值。对于区域属性来说, CarPropertyConfig中还封装了额外的方法方便开发者获取特定区域的取值,AreaConfig类 的getMinValue、getMaxValue方法可以返回某一属性特定区域的取值范围。
(2)通过 CarPropertyManager获取当前的车速。
CarPropertyValue对象的成员变量如表5-5所示。
虽然看上去很简单,但实际使用过程中会涉及较多的判断,开发者可以进一步对属性进 行封装管理,并总结一些有用的实践。CarPropertyConfig和 CarPropertyValue这两个类 同样 和 VehicleHAL 中 的 定 义 的 结 构 体 相 关 联,CarService 会 将 从 HAL 层 获 取 的 VehiclePropConfig和 VehiclePropValue① 分别转换为CarPropertyConfig和CarPropertyValue。
3.进一步了解 CarPropertyManager
通过前文的介绍,读者应该已经了解了 CarPropertyManager的重要性。因此有必要进 一步了解 CarPropertyManager背后的原理,更全面地掌握它。 熟悉 Android的读者应该知道,在 Android中往往一个 Manager会对应一个Service,例如 ActivityManager对应着 ActivityManagerService;PackageManager对应着PackageManagerService。 它 们 运 行 在 不 同 的 进 程 中,通 过 Binder 这 一 IPC 机 制 进 行 通 信。 同 样 地,与 CarPropertyManager相对应的是 CarPropertyService这一服务。 同时,读者如果对车辆电子电器的架构有一定了解,那么应该知道,各个具体的功能往 往有对应的电子控制单元(ElectronicControlUnit,ECU)进行控制。例如,控制座椅位置 的命令,最终需要发送到负责控制座椅的 ECU 中才能使座椅移动。 因此,一次控制命令的调用过程大致如图5-1所示。
上层的应用通过 CarAPI提供的接口进行设置,最终通过车辆总线将命令发送给对应 的 ECU,ECU 返 回 结 果 给 CarService,并 通 过 回 调 通 知 相 关 应 用。 这 一 过 程 中 的 ① VehiclePropConfig和 VehiclePropValue定义在/hardware/interfaces/automotive/vehicle/2.0/types.hal文件中。 55 第 5 章 CarPropertyService———车辆属性服务 VehicleHAL非 常 重 要,它 指 的 是 制 造 商 实 现 的 硬 件 抽 象 层 服 务,实 现 了 Android AutomotiveOS定义的相关硬件抽象层接口。由于不同汽车制造商与ECU 的通信方式、标 准、数据格式都不尽相同,所以需要对其进行抽象,统一接口,而具体逻辑则由汽车制造商自 己实现。关于 VehicleHAL的具体内容,会在后面的章节再展开。 下面通过源码,进一步了解 CarPropertyManager中的具体实现,建议读者在阅读的同 时打开源码文件进行查看。 以下是 CarPropertyManager的构造函数实现:
在 CarPropertyManager的构造函数中,获得了ICarProperty的远程对象,通过该远程 对象就可以调用 CarPropertyService。关于 Binder机制的具体实现及 AIDL 的调用过程, 在此就不做展开了①。 再来看看 CarPropertyManager的setProperty的调用过程:
通过 mService对象,调用 CarPropertyService中对应的方法:
这里出现了一个新的对象 mHal,它是 PropertyHalService对象的实例。
接着调用 VehicleHal的set方法,虽然对象命名叫 VehicleHal,但该 VehicleHal对象 依然是在CarService进程中定义并创建的对象。还没有看到对 HAL层的Binder调用。接着往下:
VehicleHal中的set方法只是进一步调用了 HalClient对象的setValue方法。
在 HalClient的setValue方法中,终于发现这个 mVehicle对象是一个 HIDL调用的远 程对象,通过它,实际上调用的是抽象层 VehicleHal的实现。这里涉及了 Android8.0引入 的 HIDL机制,不详细展开了①。随着 HIDL 机制的引入,VehicleHal运行在独立的进程 中,由设备制造商或汽车制造商进行实现。
以上就是一次完整的设置属性值的调用过程,可以看到设置命令最终将发送给制造商 实现的 VehicleHal进程,并由 VehicleHal最终完成该次调用。
再来追踪 VehicleHal中的事件的传递过程。
通过设置的流程,可以发现发起 HIDL调用的远程对象是被 HalClient对象所持有的, 与 VehicleHal的 直 接 交 互 是 在 HalClient 中 完 成 的。 因 此 事 件 的 向 上 传 递 也 是 从 HalClient开始的。在收到上报的事件之前,上层应用首先要注册相应的监听方法。
当应用调用 CarPropertyManager的registerListener方法时,其会调用 CarPropertyService 的registerListener方法。
上述源码进一步 调 用 PropertyHalService的 subscribeProperty 方 法,中 间 会 再 经 过 VehicleHal.java的调用,最后调用 HalClient中的subscribe方法,调用的路径和设置的流 程是一样的。在此省略一些中间过程,直接来看 HalClient的subscribe方法的实现:
这里将 mInternalCallback对象传递给了 Hal层。mInternalCallback对象的实例是继 承了IVehicleCallback.Stub的 HIDL桩对象,实现如下:
通过IVehicleCallback,制造商实现的 VehicleHal进程就可以将事件传递给 CarService 进程了。有兴趣的读者可以进一步追踪当IVehicleCallback的onPropertyEvent方法被调 用后,事件又是如何传递给应用注册的监听器的。
上文出现了一些新的对象,为了便于理解,整理上述类之间的关系,见图5-2。
在 CarService中虽然有 VehicleHal类,但该类并不直接调用 HIDL 方法,而是进一步 调 用 HalClient 封 装 的 方 法,HalClient 才 是 真 正 与 HAL 层 打 交 道 的 类。 同 时, PropertyHalService处理与 CarPropertyService相关的业务逻辑,在后面的内容中,还会接触 InputHalService、PowerHalService,VehicleHal会将不同的事件分发给对应的类进行处理。 总体来说,通过 CarPropertyService的层层调用,最后通过IVehicle与实现了车辆硬件 抽象层的进程通信。VehicleHal进程由各个厂家进行实现,再进一步将消息发送给关联的 ECU,实现控制功能。