Android汽车服务篇(三) CarPropertyService下篇

一. 简介

        上篇文章介绍了Android汽车服务篇(二) CarPropertyService上篇, 我们继续看一看看CarPropertyService, 这个服务也是很重要的, 绝大部分与车辆硬件功能相关联的属性,如空调, 车舱功能, 车辆传感器等都是通过CarPropertyService来读取或者设置的.

         CarPropertyManager 是CarPropertyService在客户端的代理, 通过CarPropertyManager提供的API,可以设置和获取车辆各个属性的状态.

二. 座舱服务 CarCabinManager

        CarCabinManager提供的是座舱内相关功能的API, 包括座椅,安全带,车窗等. 它在用法上和CarHvacManager类似, 同样的CarCabinManager也是系统级别的,只有拥有系统权限的应用才可以使用.

        CarCabinManager的属性

CarCabinManager中的属性都和座舱内的硬件设备相关,如车门,后视镜,座椅等. 与这些设备相关的属性又根据其特点进行了细分,对于可以移动,调节的设备而言,会有不同方向之分.

下表中的列出了CarCabinManager中所包含的属性, 以及属性所对应的主要设备和功能

属性 类型 功能
ID_DOOR_POS int 车门
ID_DOOR_MOVE int
ID_DOOR_LOCK bool
ID_MIRROR_Z_POS int 后视镜
ID_MIRROR_Z_MOVE int
ID_MIRROR_Y_POS int
ID_MIRROR_Y_MOVE int
ID_MIRROR_LOCK

bool

ID_MIRROR_FOLD bool
ID_SEAT_MEMORY_SELECT

int

座椅记忆
ID_SEAT_MEMORY_SET

int

ID_SEAT_BELT_BUCKLED

bool

安全带
ID_SEAT_BELT_HEIGHT_POS int
ID_SEAT_BELT_HEIGHT_MOVE int
ID_SEAT_FORE_AFT_POS int 座椅前后位置
ID_SEAT_FORE_AFT_MOVE int
ID_SEAT_BACKREST_ANGLE_1_POS int 座椅靠背
ID_SEAT_BACKREST_ANGLE_1_MOVE int
ID_SEAT_BACKREST_ANGLE_2_POS int
ID_SEAT_BACKREST_ANGLE_2_MOVE int
ID_SEAT_HEIGHT_POS int 座椅高度
ID_SEAT_HEIGHT_MOVE int
ID_SEAT_DEPTH_POS int 座椅深度
ID_SEAT_DEPTH_MOVE int

                                                                               表一

属性 类型 功能
ID_SEAT_TILT_POS int 座椅倾角
ID_SEAT_TILT_MOVE int
ID_SEAT_LUMBAR_FORE_AFT_POS int 腰托
ID_SEAT_LUMBAR_FORE_AFT_MOVE int
ID_SEAT_LUMBAR_SIDE_SUPPORT_POS int
ID_SEAT_LUMBAR_SIDE_SUPPORT_MOVE int
ID_SEAT_HEADREST_HEIGHT_POS int 头枕
ID_SEAT_HEADREST_HEIGHT_MOVE int
ID_SEAT_HEADREST_ANGLE_POS int
ID_SEAT_HEADREST_ANGLE_MOVE int
ID_SEAT_HEADREST_FORE_AFT_POS int
ID_SEAT_HEADREST_FORE_AFT_MOVE int
ID_WINDOW_POS int 车窗
 
ID_WINDOW_MOVE int
ID_WINDOW_LOCK bool

                                                                                 表二

        可以看到CarCabinManager中最主要的是和座椅相关的属性,同时还有车窗,后视镜相关的功能. 大部分功能同时会有位置(position)和移动(move)两个属性.其中位置属性主要是设置具体的位置值而移动则是该设备的移动方向.

        

        CarCabinManager丰富了车内设施的控制功能,通过它提供的API,开发者可以为驾驶者提供很多个性化的功能,如座椅调节, 车窗调节

        在用法上, CarCabinManager和CarHvacManager非常相似,同样可以获取或设置属性的值, 并对属性变化进行监听. API也没有什么两样. 

        相关的权限主要有以下三个:

  <uses-permission android:name="android.car.permission.CONTROL_CAR_WINDOWS"/>

  <uses-permission android:name="android.car.permission.CONTROL_CAR_SEATS"/>

  <uses-permission android:name="android.car.permission.CONTROL_CAR_MIRRORS"/>

三. 制造商扩展服务 CarVendorExtensionManager

        市场上的汽车品牌种类繁多,每款车型的功能又不相同.汽车制造商们也在不断推陈出新,推出一些属于品牌特有的功能来吸引消费者的目光. 要想将说有的功能都定义为标准的属性肯定非常困难.

        对此 AAOS的做法是, 除了定义目前市场上绝大多数车型都适用的属性外,同样允许制造商根据自己所拥有的其他功能进行扩展, 这就是本节CarVendorExtensionManager的主要作用. 它让制造商可以扩展VehicleHAL中已经定义的属性,加入额外的功能.

 3.1 CarVendorExtensionManager用法

通过以下方式获取CarVendorExtensionManager对象的实例

        Car car = Car.createCar(this);
        CarVendorExtensionManager vendorManager = (CarVendorExtensionManager)car.getCarManager(Car.VENDOR_EXTENSION_SERVICE);

要使用CarVendorExtensionManager需要申请如下权限:

<uses-permission android:name="android.car.permission.CAR_VENDOR_EXTENSION"/>

该权限同时是系统级别的,普通的第三方应用无法使用.

3.2 获取和设置属性

        在属性的设置或获取上, CarVendorExtensionManager和CarHvacManager. CarCabinManager的使用方法区别并不大, 但是由于是扩展的属性, 属性的类型是不确定的,所以在调用setProperty和getProperty时需要传入属性的类型. 

        举个例子, 自定义了一个 CUSTOM_FLOAT_AREA 属性,且值为float型,如果该区域是多区域的,还需要传入区域值, 获取和设置该属性的方法如下:

        //设置属性值
        vendorManager.setProperty(Float.class, CUSTOM_FLOAT_AREA, VehicleAreaSeat.SEAT_ROW_1_CENTER, value);
        //获取实际值
        float actualVaule = vendorManager.getProperty(Float.class, CUSTOM_FLOAT_AREA, VehicleAreaSeat.SEAT_ROW_1_CENTER);

我们可以看看packages/services/Car/car-lib/src/android/car/hardware/CarVendorExtensionManager.java 的 这两个方法

 public <E> void setProperty(Class<E> propertyClass, int propId, int area, E value) {
        mPropertyManager.setProperty(propertyClass, propId, area, value);
}


 public <E> E getProperty(Class<E> propertyClass, int propId, int area) {
        return mPropertyManager.getProperty(propertyClass, propId, area).getValue();
    }

3.3 监听属性变化

CarVendorExtensionManager同样可以通过注册回调接口的方式来监听属性值发生的变化. 方式十分简单.实现CarVendorExtensionCallback就可以了.方式如下:

         //创建一个监听器对象
        CarVendorExtensionListener listener = new CarVendorExtensionListener();

        //注册监听器
        vendorManager.registerCallback(listener);

        //移除监听器
        vendorManager.unregisterCallback(listener);


class CarVendorExtensionListener implements CarVendorExtensionManager.CarVendorExtensionCallback{
    @Override
    public void onChangeEvent(CarPropertyValue carPropertyValue) {

    }

    @Override
    public void onErrorEvent(int i, int i1) {

    }
}

需要注意的是, 尽管注册的是CarVendorExtensionCallback,但是该回调方法不仅会收到扩展属性相关的变化事件,对于其他属性的变化事件(如空调,传感器)也有可能被传递过里. 因此在onChangeEvent方法中需要做好相关的判断,确保该次事件是所需要的.

有了CarVendorExtensionManager, CarService一下子有了拥有扩展属性的能力,让原来看上去有限的功能,变得可以丰富无比,当然实际上还是得依赖制造商的实现.  

对于需要使用自定义属性的应用, 只需要知道确切的ID和类型.

四.  车辆属性API  CarPropertyManager

本节介绍最重要的一个Manager :  CarPropertyManager. 在Android10中,它变成了车辆属性的主要API, 并允许任何运行在 AAOS上的应用进行调用. 

4.1 CarPropertyManager用法

        熟悉了上面介绍的5个服务的相关用法后,在CarPropertyManager的使用上, 对相关的用法有所了解, 接下来这里再做一点补充.

        关于属性的获取, 在CarPropertyManager.java中除了有getProperty方法之外, 还有getBooleanProperty. getIntProperty这样明确属性类型的获取方法. 这些方法的最后实现还是对getProperty方法的封装.

    /**
     * Returns value of a integer property
     *
     * @param prop Property ID to get
     * @param area Zone of the property to get
     */
    public int getIntProperty(int prop, int area) {
        //最后还是调用的getProperty方法
        CarPropertyValue<Integer> carProp = getProperty(Integer.class, prop, area);
        return carProp != null ? carProp.getValue() : 0;
    }




public <E> CarPropertyValue<E> getProperty(@NonNull Class<E> clazz, int propId, int areaId) {
        if (DBG) {
            Log.d(TAG, "getProperty, propId: 0x" + toHexString(propId)
                    + ", areaId: 0x" + toHexString(areaId) + ", class: " + clazz);
        }
        try {
            CarPropertyValue<E> propVal = mService.getProperty(propId, areaId);
            if (propVal != null && propVal.getValue() != null) {
                Class<?> actualClass = propVal.getValue().getClass();
                if (actualClass != clazz) {
                    throw new IllegalArgumentException("Invalid property type. " + "Expected: "
                            + clazz + ", but was: " + actualClass);
                }
            }
            return propVal;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

注意事项:  官方推荐开发者使用getProperty来获取相应的属性值, 因为getProperty方法返回的是CarPropertyValue对象,其不仅仅包含属性值,还包括属性的状态,而getIntProperty等方法在属性不可用的情况下,返回的是默认值,所以会造成有些时候读取的数据不准确.

下面已NIGHT_MODE(昼夜模式)属性为例子, 说明使用getProperty的好处.

private void testCarPropertyManager() {
        Car car = Car.createCar(this);
        CarPropertyManager carProperty = (CarPropertyManager)car.getCarManager(Car.PROPERTY_SERVICE);

        CarPropertyValue<Boolean> val = carProperty.getProperty(Boolean.class, NIGHT_MODE, 0);
        if(val == null || val.getStatus() != CarPropertyValue.STATUS_AVAILABLE) {
            // 该属性不支持或不可用, 使用当前时间判断昼夜情况
        } else if (val.getValue()) {
            //黑夜模式
        } else {
            //白昼模式
        }
    }

虽然getProperty 方法会增加源码的数量,  但还是推荐大家使用该方式获取属性.

在设置属性方面, CarPropertyManager同样提供了setProperty以及明确类型的setIntProperty, setBooleanProperty等方法. 当然这些方法最后还是调用的setProperty.

  public void setBooleanProperty(int prop, int areaId, boolean val) {
        setProperty(Boolean.class, prop, areaId, val);
    }

   public void setFloatProperty(int prop, int areaId, float val) {
        setProperty(Float.class, prop, areaId, val);
    }

    //最终调用的还是这个方法
    public <E> void setProperty(@NonNull Class<E> clazz, int propId, int areaId, @NonNull E val) {
        if (DBG) {
            Log.d(TAG, "setProperty, propId: 0x" + toHexString(propId)
                    + ", areaId: 0x" + toHexString(areaId) + ", class: " + clazz + ", val: " + val);
        }
        try {
            mService.setProperty(new CarPropertyValue<>(propId, areaId, val));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

4.2 注册监听器

在注册监听属性变化方面, CarPropertyManager提供更细的监听方法, 它可以监听到单个属性值的变化, 开发者可以通过在注册监听器时传入属性ID指定监听器所对应的属性. 同时指定数据上报的频率.

        //注册监听器
        CarPropertyListener carPropertyListener = new CarPropertyListener();
        carProperty.registerCallback(carPropertyListener, VehiclePropertyIds.PERF_VEHICLE_SPEED, 6);

        //移除监听器
        carProperty.unregisterCallback(carPropertyListener);



class CarPropertyListener implements  CarPropertyManager.CarPropertyEventCallback{

    @Override
    public void onChangeEvent(CarPropertyValue carPropertyValue) {

    }

    @Override
    public void onErrorEvent(int i, int i1) {

    }
}

4.3 CarPropertyManager相关类

前文已经提到过CarInfoManager, CarHvacManager, CarSensorManager 都是通过CarPropertyManager实现其功能的. 在其他几个Manager初始化的时候,都会创建属于自己的CarPropertyManager对象. 这几个Manager拥有了CarPropertyManager以后用来做什么呢?

/packages/services/Car/car-lib/src/android/car/CarInfoManager.java

    public float getFuelCapacity() {
        return mCarPropertyMgr.getFloatProperty(BASIC_INFO_FUEL_CAPACITY, 0);
    }


    /** @hide */
    CarInfoManager(IBinder service) {
        ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
        //在构造方法中创建CarPropertyManager对象
        mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, null);
    }

packages/services/Car/car-lib/src/android/car/hardware/hvac/CarHvacManager.java

    public boolean getBooleanProperty(@PropertyId int propertyId, int area) {
        return mCarPropertyMgr.getBooleanProperty(propertyId, area);
    }


    public CarHvacManager(IBinder service, Context context, Handler handler) {
        ICarProperty mCarPropertyService = ICarProperty.Stub.asInterface(service);
        //构造方法中 创建CarPropertyManager对象
        mCarPropertyMgr = new CarPropertyManager(mCarPropertyService, handler);
    }

原来这些Manager就是对CarPropertyManager的调用,其他几个Manager并没有做什么具体的事情,只是把任务交给了CarPropertyManager再去执行.

在前面文章中出现了如VehiclePropertyIds,  CarPropertyValue, CarPropertyConfig等相关的辅助类. 我们在这里梳理一下各个辅助类的作用

辅助类名称 功能作用
VehiclePropertyIds

CarPropertyManager都是通过属性ID来对应具体的功能,不同的功能对应不同的ID

VehiclePropertyIds中列出了所有在VehicleHAL中定义的功能属性

VehicleAreaDoor 许多功能点都分为多个区域,在设置,获取相应属性时,需要传入区域参数,VehicleAreaDoor定义了与车门相关的区域值,在使用和车门相关的属性时配套使用
VehicleAreaMirror 与VehicleAreaDoor类似, 多区域定义, 后视镜区域值
VehicleAreaSet 多区域定义, 座位区域值
VehicleAreaWheel 多区域定义, 车胎区域值
VehicleAreaWindow 多区域定义,车窗区域值
VehicleAreaType

区域类型是用来区分一个属性所对应的位置, 源码类中有6个值:VEHICLE_AREA_TYPE_GLOBAL 对应于非多区域

VEHICLE_AREA_TYPE_WINDOW(车窗)

VEHICLE_AREA_TYPE_SEAT(座椅)

VEHICLE_AREA_TYPE_DOOR(车门)

VEHICLE_AREA_TYPE_MIRROR(后视镜)

VEHICLE_AREA_TYPE_WHEEL(轮胎)

VehicleLightState 灯光状态  开    关    日间
VehicleLightSwitch 灯光切换, 开   关     日间      自动
VehicleOilLevel 油量状态

以上这些辅助类中,都定义了相关的静态变量,同时这些值都是与VehicleHAL的相关定义是一一对应的.在Car API中将其再次定义是为了方便上层应用使用.

我们也还经常用到CarPropertyConfig 和 CarPropertyValue这两个模板类.

辅助类 功能
CarPropertyConfig 获取一个属性的静态参数, 如取值范围,类型,支持的区域等
CarPropertyValue 获取一个属性的值和状态

我们先看看CarPropertyConfig它的成员变量列表

类型 变量名 说明
int mAccess 该属性是否可读可写(0:不可读不可写;  1:可读;  2:可写;  3:可读写)
int mAreaType 区域类型,与VehicleAreaType对应
int mChangeMode 变化类型(0:该属性值始终不变; 1:发生变化时通知; 2:以一定频率持续通知当前值)
ArrayList<Integer> mConfigArray 额外的配置属性
String mConfigString 额外的配置信息
float mMaxSampleRate 最大频率(仅对持续上报属性有效)
float mMinSampleRate 最小频率(仅对持续上报属性有效)
int mPropertyId 属性ID
SparseArray<AreaConfig<T>> mSupportedAreas 区域属性,包含该区域的取值范围
Class<T> mType 属性的类型

看下这个例子, 通过CarPropertyManager获取当前车辆支持的属性(需要在清单文件中声明对应的权限)

        Car car = Car.createCar(this);
        CarPropertyManager carProperty = (CarPropertyManager)car.getCarManager(Car.PROPERTY_SERVICE);
        List<CarPropertyConfig> carPropertyConfigList = carProperty.getPropertyList();

在来看看CarPropertyValue的成员变量列表

类型 变量名 说明
int mPropertyId 属性ID
int mAreaId 区域ID
int mStatus 状态(0:可用;  1:不可用;  2:错误)
long mTimestamp 时间戳(单位:纳秒)
T mValue 当前值

举个例子 获取当前车速:

        Car car = Car.createCar(this);
        CarPropertyManager carProperty = (CarPropertyManager)car.getCarManager(Car.PROPERTY_SERVICE);

        //获取当前的车速
        CarPropertyValue<Float> value = carProperty.getProperty(Float.class, VehiclePropertyIds.PERF_VEHICLE_SPEED,
                VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL);

猜你喜欢

转载自blog.csdn.net/u012514113/article/details/129841675