本文主要记录property信号添加的操作流程。
以下操作基于HIDL,安卓13:
信号配置
1.types.hal中更新信号
首先,VHAL需要根据信号矩阵表,更新types.hal中的信号。
文件路径LINUX\android\hardware\interfaces\automotive\vehicle\2.0\types.hal
信号id是8位的16进制数,可分为4个部分,它们通过或运算累加起来。
下面以原生信号为例,进行说明。
INFO_EV_BATTERY_CAPACITY = (
0x0106
| VehiclePropertyGroup:SYSTEM
| VehiclePropertyType:FLOAT
| VehicleArea:GLOBAL),
后4位0x0106是信号的id,由信号矩阵表定义,一般是唯一的。
VehiclePropertyGroup是最高位,系统原生的为SYSTEM(0x10000000);一般来说,供应商添加使用VENDOR(0x20000000)。
VehiclePropertyType是第三第四位,定义如下
enum VehiclePropertyType : int32_t {
STRING = 0x00100000,
BOOLEAN = 0x00200000,
INT32 = 0x00400000,
INT32_VEC = 0x00410000,
INT64 = 0x00500000,
INT64_VEC = 0x00510000,
FLOAT = 0x00600000,
FLOAT_VEC = 0x00610000,
BYTES = 0x00700000,
MIXED = 0x00e00000,
MASK = 0x00ff0000
};
VehicleArea是第二位,一般大部分信号的area是GLOBAL。
enum VehicleArea : int32_t {
/**
* A global property is a property that applies to the entire vehicle and is not associated with
* a specific area. For example, FUEL_LEVEL, HVAC_STEERING_WHEEL_HEAT are global properties.
*/
GLOBAL = 0x01000000,
/** WINDOW maps to enum VehicleAreaWindow */
WINDOW = 0x03000000,
/** MIRROR maps to enum VehicleAreaMirror */
MIRROR = 0x04000000,
/** SEAT maps to enum VehicleAreaSeat */
SEAT = 0x05000000,
/** DOOR maps to enum VehicleAreaDoor */
DOOR = 0x06000000,
/** WHEEL maps to enum VehicleAreaWheel */
WHEEL = 0x07000000,
MASK = 0x0f000000,
};
在编译CarService时,会自动先编译VHAL。所以修改types.hal之后,可以不立即编译VHAL。可以等CarService代码完成后,直接编译CarService即可。
2.PropertyHalServiceIds中配置信号的读写权限
PropertyHalServiceIds文件路径LINUX\android\packages\services\Car\service\src\com\android\car\hal\PropertyHalServiceIds.java
参考原生代码添加权限
mProps.put(VehicleProperty.INFO_EV_BATTERY_CAPACITY, new Pair<>(
Car.PERMISSION_CAR_INFO,
null));
new Pair<>(,)中,先后添加读权限和写权限。
PropertyHalServiceIds中会使用到types.hal生成的VehicleProperty类,这也是CarService的编译依赖VHAL的原因。
3.VehiclePropertyIds中添加信号id
VehiclePropertyIds在car-lib目录下,是需要打包成jar以后,提供给APP使用的。
VehiclePropertyIds文件路径LINUX\android\packages\services\Car\car-lib\src\android\car\VehiclePropertyIds.java
car-lib目录下的接口文件修改后,要执行make update-api命令。这个命令会更新以下两个current.txt文件:
- LINUX\android\packages\services\Car\car-lib\api\current.txt
- LINUX\android\packages\services\Car\car-lib-module\api\current.txt
其实这2个文件的内容是完全一样的,文件内容就是按照字母表顺序,列出了APP可用的公共接口方法和常量。
这2个文件很重要,进行完整编译时,会校验其中的内容。假如VehiclePropertyIds中新增的信号在current.txt中没有,就会报错并终止编译。如果要手动修改current.txt(例如手动同步代码),一定要注意代码写入的位置是否正确,如果没有按照字母表顺序排列,也会报错并导致无法编译image。
4.编译获取产物和使用
添加property的基本步骤就是以上的3个。完成后即可编译。
- source build/envsetup.sh
- lunch 编译选项
- make CarServiceUpdatableNonModule 或者 在Car的对应目录下mm
这里不能make CarService,因为CarService.apk和CarServiceUpdatableNonModule.apk是不同的,具体区别参考这篇博客《CarService的构成和初始化分析》。
编译完成后可获取jar包供APP使用。建议APP引用jar包时使用compileOnly,以避免一些库的冲突。对于CarPropertyService来说,APP只要使用接口和常量,所以compileOnly参与编译即可,不需要用implementation参与打包。
提供给APP的jar包路径为/out/soong/.intermediates/packages/services/Car/car-lib/android.car/android_common/javac/android.car.jar
也可以使用/out/target/common/obj/JAVA_LIBRARIES/android.car_intermediates/classes.jar
用MD5计算可得知,上面两个路径的文件,MD5值是完全一样的。
如果是framework使用编译产物进行调试,需要使用到的产物为android.car.jar(包含Car/car-lib目录下的修改)和CarServiceUpdatableNonModule.apk(包含Car/service目录下的修改)
注意这里的 android.car.jar和提供的APP的jar包是不同的东西,其路径为out/target/product/qssi/system/framework/android.car.jar。具体的区别可以参考这篇博客《CarService的构成和初始化分析》。
将android.car.jar文件push进system/framework/之后,重启即可生效。
代码提交
综上,更新一个信号需要CarService修改4个文件
- LINUX\android\packages\services\Car\car-lib\api\current.txt
- LINUX\android\packages\services\Car\car-lib-module\api\current.txt
- LINUX\android\packages\services\Car\car-lib\src\android\car\VehiclePropertyIds.java
- LINUX\android\packages\services\Car\service\src\com\android\car\hal\PropertyHalServiceIds.java
站在CarService提交代码的角度来说,VHAL只需要保证修改types.hal并比CarService先合入即可。DefaultConfig.h是VHAL自己的默认值设置,对于CarService编译无影响。
(非必需)添加枚举值
有些进阶需求还会在car-lib中添加枚举类,来限制信号的具体值。比如,在枚举类中定义常量ON和OFF,这样APP编写代码时使用常量名,至于ON和OFF代表的具体值,APP并不关注。这样当信号值变化时,更新枚举类和生成的jar包即可,APP代码无需变动,对于平台类项目很合适。
添加枚举值时,可以在types.hal中添加enum,参考原生的代码:
enum EvsServiceState : int32_t {
OFF = 0,
ON = 1,
};
之后编译VHAL,可得到产物/out/soong/.intermediates/hardware/interfaces/automotive/vehicle/2.0/android.hardware.automotive.vehicle-V2.0-java_gen_java/gen/srcs/android/hardware/automotive/vehicle/V2_0/EvsServiceState.java
可以把这个java枚举类放入car-lib目录下,例如在car-lib/src/android/car中新建一个文件夹专门放置枚举类。然后打包进jar供APP使用。但java文件中的包名需要修改,还需要添加构造方法,否则会编译报错。枚举类较多时,可以用脚本进行批量修改。
注意在car-lib中修改枚举类之后,要更新current.txt文件。
调试
CarService的调试,涉及上层APP和下层VHAL。
APP侧调试
APP侧,可以写一个测试APP引入android.car.jar,调用set/get/register三种接口。APP应当有系统权限,否则无法set。如果一些信号有在PropertyHalServiceIds里设置读写权限,APP也应当加上。
初始化代码
private Car mCarApi;
private CarPropertyManager mCarPropertyManager;
if (mCarApi != null && mCarApi.isConnected()) {
mCarApi.disconnect();
mCarApi = null;
}
mCarApi = Car.createCar(context, null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT, (car, ready) -> {
if (ready) {
mCarPropertyManager = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
} else {
// CarService发生异常或断开时,需要client端处理
}
});
注册/注销监听
private final CarPropertyManager.CarPropertyEventCallback mCarPropertyEventListener = new CarPropertyManager.CarPropertyEventCallback() {
@Override
public void onChangeEvent(CarPropertyValue carPropertyValue) {
}
@Override
public void onErrorEvent(int var1, int var2) {
}
};
mCarPropertyManager.registerCallback(mCarPropertyEventListener, propertyId, CarPropertyManager.SENSOR_RATE_ONCHANGE);
mCarPropertyManager.unregisterCallback(mCarPropertyEventListener, propertyId);
set接口。areaId一般使用0(GLOBAL),不是GLOBAL的信号应使用对应的areaId。
public void setBooleanProperty(int prop, int areaId, boolean val)
public void setFloatProperty(int prop, int areaId, float val)
public void setIntProperty(int prop, int areaId, int val)
public <E> void setProperty(@NonNull Class<E> clazz, int propId, int areaId, @NonNull E val)
get接口。areaId一般使用0(GLOBAL),不是GLOBAL的信号应使用对应的areaId。
public boolean getBooleanProperty(int prop, int area)
public float getFloatProperty(int prop, int area)
public int getIntProperty(int prop, int area)
public int[] getIntArrayProperty(int prop, int area)
public <E> CarPropertyValue<E> getProperty(@NonNull Class<E> clazz, int propId, int areaId)
VHAL侧调试
原生提供了命令进行调试。
adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle --set 信号id -i 信号值
信号id可以直接写十进制的id,也可以使用0x的十六进制id。-i表示int型,还可以使用f(float),b(byte数组)
byte数组的格式比较特殊
adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle --set 信号id -b 0x0102
0x之后,每两位占一个字节。
get命令
adb shell lshal debug android.hardware.automotive.vehicle@2.0::IVehicle --get 信号id