Automotive——Vehicle添加新的property示例

Automotive——Vehicle添加新的property示例

本文以添加Car Air purifier空气净化器为例,介绍如何在Automotive里添加新的属性,并在模拟器里验证

一、Hal层

1.1 type.hal

路径hardware\interfaces\automotive\vehicle\2.0\type.hal

首先要在type.hal中定义新的property CAP_POWER_ON CAP_LEVEL CAP_MODE三个新的属性,其中CAP_MODE里会使用自定义的属性类型,提前给他加上。

其中0x0601 为自定义ID值,前面4位有特殊含义:

第一位 参考 enum VehiclePropertyGroup 属性组 SYSTEM 1 VENDOR 2

第二位 参考 enum VehicleArea 属性作用域 GLOBAL 01 SEAT 05

第三、四位 参考 enum VehiclePropertyType 属性值类型 BOOLEAN 20 INT32 40

// CAP feature
    /**
     * car air purifier power on
     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
     * @access VehiclePropertyAccess:READ_WRITE
     */
    CAP_POWER_ON =(
        0x0601
        | VehiclePropertyGroup:SYSTEM
        | VehiclePropertyType:BOOLEAN
        | VehicleArea:GLOBAL),

    /**
     * car air purifier level
     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
     * @access VehiclePropertyAccess:READ_WRITE
     */
    CAP_LEVEL =(
        0x0602
        | VehiclePropertyGroup:SYSTEM
        | VehiclePropertyType:INT32
        | VehicleArea:SEAT),

    /**
     * car air purifier MODE
     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
     * @access VehiclePropertyAccess:READ_WRITE
     */
    CAP_MODE =(
        0x0603
        | VehiclePropertyGroup:SYSTEM
        | VehiclePropertyType:INT32
        | VehicleArea:GLOBAL),
// CAP feature

用于CAP_MODE的类型 AUTO自动模式 STERILIZE除菌模式 DEODORISER除臭模式

//CAP feature
enum VehicleCapMode : int32_t {
    AUTO = 0x1,
    STERILIZE = 0x2,
    DEODORISER = 0x3,
};
//CAP feature

1.2 DefaultConfig.h

路径hardware\interfaces\automotive\vehicle\2.0\default\impl\vhal_v2_0\DefaultConfig.h

自定义cap的作用位置

//CAP feature
constexpr int CAP_1_LEFT = (int)VehicleAreaSeat::ROW_1_LEFT;
constexpr int CAP_1_RIGHT = (int)VehicleAreaSeat::ROW_1_RIGHT;
constexpr int CAP_2_LEFT = (int)VehicleAreaSeat::ROW_2_LEFT;
constexpr int CAP_2_RIGHT = (int)VehicleAreaSeat::ROW_2_RIGHT;
//CAP feature

添加三个属性的具体设置和初始化值

// CAP feature
     {.config = {.prop = toInt(VehicleProperty::CAP_POWER_ON),
                 .access = VehiclePropertyAccess::READ_WRITE,
                 .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                 .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}},
      .initialValue = {.int32Values = {0}}},

    {.config = {.prop = toInt(VehicleProperty::CAP_LEVEL),
              .access = VehiclePropertyAccess::READ_WRITE,
              .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
                // 引用之前定义的位置 并设置最大最小值
                              .areaConfigs = {VehicleAreaConfig{
                                                  .areaId = CAP_1_LEFT, .minInt32Value = 0, .maxInt32Value = 5,
                                              },
                                              VehicleAreaConfig{
                                                  .areaId = CAP_1_RIGHT, .minInt32Value = 0, .maxInt32Value = 5,
                                              },
                                              VehicleAreaConfig{
                                                  .areaId = CAP_2_LEFT, .minInt32Value = 0, .maxInt32Value = 5,
                                              },
                                              VehicleAreaConfig{
                                                  .areaId = CAP_2_RIGHT, .minInt32Value = 0, .maxInt32Value = 5,
                                              }}},
     // 给个初始值
         .initialAreaValues = {{CAP_1_LEFT, {.int32Values = {2}}},
                               {CAP_1_RIGHT, {.int32Values = {3}}},
                               {CAP_2_LEFT, {.int32Values = {4}}},
                               {CAP_2_RIGHT, {.int32Values = {5}}}}},

    {.config = {.prop = toInt(VehicleProperty::CAP_MODE),
               .access = VehiclePropertyAccess::READ_WRITE,
               .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
               .areaConfigs = {VehicleAreaConfig{.areaId = (0)}}},
    .initialValue = {.int32Values = {toInt(VehicleCapMode::AUTO)}}},
// CAP feature

1.3 EmulatedVehicleHal.cpp

路径hardware\interfaces\automotive\vehicle\2.0\default\impl\vhal_v2_0\EmulatedVehicleHal.cpp

为了测试方便加个假的回调 在set方法里调用doHalEvent逻辑。

StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
    ······
//mock hal callback event
    ALOGI("%s propId: 0x%x, areaId: 0x%x", __func__, propValue.prop, propValue.areaId);
    doHalEvent(getValuePool()->obtain(propValue));
//mock hal callback event
    ······
    getEmulatorOrDie()->doSetValueFromClient(propValue);

    return StatusCode::OK;
}

1.4 Android.bp

路径hardware\interfaces\automotive\vehicle\2.0\Android.bp

将之前定义的VehicleCapMode 类型加入

types: [
//CAP feature
        "VehicleCapMode",
//CAP feature
]

二、car-lib

2.1 CarApManager.java

路径 packages\services\Car\car-lib\src\android\car\hardware\cap\CarApManager.java

package android.car.hardware.cap;

import android.car.CarManagerBase;
import android.car.CarNotConnectedException;
import android.car.hardware.CarPropertyConfig;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.CarPropertyManager;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
import android.util.ArraySet;
import android.util.Log;

import android.annotation.IntDef;
import android.annotation.SystemApi;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

/**
 * @author : GW00175635
 * @date : 2019/11/13 14:33
 * desc   :
 * version: 1.0
 */
public final class CarApManager implements CarManagerBase {
    private final static boolean DBG = false;
    private final String TAG = "CarApManager";
    private final CarPropertyManager mCarPropertyMgr;
    private final ArraySet<CarApEventCallback> mCallbacks = new ArraySet<>();
    private CarPropertyEventListenerToBase mListenerToBase = null;
	
    //hal层定义的三个ID值
    public static final int ID_CAP_POWER_ON = 0x11200601;
    public static final int ID_CAP_LEVEL = 0x15400602;
    public static final int ID_CAP_MODE = 0x11400603;
	//hal层定义的三个Mode值
    public static final int AUTO_MODE = 0x1;
    public static final int STERILIZE_MODE  = 0x2;
    public static final int DEODORISER_MODE  = 0x3;


	//注解ArraySet 用于检测是否为本类可执行的ID
    /** @hide */
    @IntDef({
            ID_CAP_POWER_ON,
            ID_CAP_LEVEL,
            ID_CAP_MODE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface CapPropertyId {}
    private final ArraySet<Integer> mCapPropertyIds = new ArraySet<>(Arrays.asList(new Integer [] {
            ID_CAP_POWER_ON,
            ID_CAP_LEVEL,
            ID_CAP_MODE
    }));
	//CarApEventCallback 上层回调接口   hal的 doHalEvent 最后回调到这
    public interface CarApEventCallback {
        /**
         * Called when a property is updated
         * @param value Property that has been updated.
         */
        void onChangeEvent(CarPropertyValue value);

        /**
         * Called when an error is detected with a property
         * @param propertyId
         * @param zone
         */
        void onErrorEvent(@CapPropertyId int propertyId, int zone);
    }

    private static class CarPropertyEventListenerToBase implements
            CarPropertyManager.CarPropertyEventListener {
        private final WeakReference<CarApManager> mManager;

        public CarPropertyEventListenerToBase(CarApManager manager) {
            mManager = new WeakReference<>(manager);
        }

        @Override
        public void onChangeEvent(CarPropertyValue value) {
            CarApManager manager = mManager.get();
            if (manager != null) {
                manager.handleOnChangeEvent(value);
            }
        }

        @Override
        public void onErrorEvent(int propertyId, int zone) {
            CarApManager manager = mManager.get();
            if (manager != null) {
                manager.handleOnErrorEvent(propertyId, zone);
            }
        }
    }

    private void handleOnChangeEvent(CarPropertyValue value) {
        Collection<CarApEventCallback> callbacks;
        synchronized (this) {
            callbacks = new ArraySet<>(mCallbacks);
        }
        if (!callbacks.isEmpty()) {
            for (CarApEventCallback l: callbacks) {
                l.onChangeEvent(value);
            }
        }
    }

    private void handleOnErrorEvent(int propertyId, int zone) {
        Collection<CarApEventCallback> callbacks;
        synchronized (this) {
            callbacks = new ArraySet<>(mCallbacks);
        }
        if (!callbacks.isEmpty()) {
            for (CarApEventCallback l: callbacks) {
                l.onErrorEvent(propertyId, zone);
            }
        }
    }

    public CarApManager(IBinder service, Context context, Handler handler){
        mCarPropertyMgr = new CarPropertyManager(service, handler, DBG, TAG);
    }

    public synchronized void registerCallback(CarApEventCallback 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 synchronized void unregisterCallback(CarApEventCallback callback) {
        mCallbacks.remove(callback);
        try {
            List<CarPropertyConfig> configs = getPropertyList();
            for (CarPropertyConfig c : configs) {
                // Register each individual propertyId
                mCarPropertyMgr.unregisterListener(mListenerToBase, c.getPropertyId());
            }
        } catch (Exception e) {
            Log.e(TAG, "getPropertyList exception ", e);
        }
        if (mCallbacks.isEmpty()) {
            mCarPropertyMgr.unregisterListener(mListenerToBase);
            mListenerToBase = null;
        }
    }
// 获取propertylist   从可用的propertylist(可用是通过当前APP的权限进行判定)挑出匹配到ArraySet的property
    public List<CarPropertyConfig> getPropertyList() throws CarNotConnectedException {
        return mCarPropertyMgr.getPropertyList(mCapPropertyIds);
    }

    public boolean isPropertyAvailable(@CapPropertyId int propertyId, int area)
            throws CarNotConnectedException {
        return mCarPropertyMgr.isPropertyAvailable(propertyId, area);
    }

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

    public float getFloatProperty(@CapPropertyId int propertyId, int area)
            throws CarNotConnectedException {
        return mCarPropertyMgr.getFloatProperty(propertyId, area);
    }

    public int getIntProperty(@CapPropertyId int propertyId, int area)
            throws CarNotConnectedException {
        return mCarPropertyMgr.getIntProperty(propertyId, area);
    }

    public void setBooleanProperty(@CapPropertyId int propertyId, int area, boolean val)
            throws CarNotConnectedException {
        if (mCapPropertyIds.contains(propertyId)) {
            mCarPropertyMgr.setBooleanProperty(propertyId, area, val);
        }
    }

    public void setFloatProperty(@CapPropertyId int propertyId, int area, float val)
            throws CarNotConnectedException {
        if (mCapPropertyIds.contains(propertyId)) {
            mCarPropertyMgr.setFloatProperty(propertyId, area, val);
        }
    }

    public void setIntProperty(@CapPropertyId int propertyId, int area, int val)
            throws CarNotConnectedException {
        if (mCapPropertyIds.contains(propertyId)) {
            mCarPropertyMgr.setIntProperty(propertyId, area, val);
        }
    }

    /** @hide */
    @Override
    public void onCarDisconnected() {
        mCarPropertyMgr.onCarDisconnected();
    }
}

2.2 Car.java

路径packages\services\Car\car-lib\src\android\car\Car.java

在Car中添加CarApManager的专属权限,并初始化CarApManager。

import android.car.hardware.cap.CarApManager;

//CAP feature
    /**
     * @hide
     */
    @SystemApi
    public static final String CAP_SERVICE = "cap";
//CAP feature

//Cap feature
    /**
     * Permission necessary to access Car AP APIs.
     * @hide
     */
    @SystemApi
    public static final String PERMISSION_CONTROL_CAR_AP =
            "android.car.permission.CONTROL_CAR_AP";
//Cap feature

    private CarManagerBase createCarManager(String serviceName, IBinder binder)
            throws CarNotConnectedException {
        CarManagerBase manager = null;
        switch (serviceName) {
            case AUDIO_SERVICE:
                manager = new CarAudioManager(binder, mContext, mEventHandler);
                break;
            case HVAC_SERVICE:
                manager = new CarHvacManager(binder, mContext, mEventHandler);
                break;
//CAP feature
            case CAP_SERVICE:
                manager = new CarApManager(binder,mContext,mEventHandler);
                break;
//CAP feature
            case POWER_SERVICE:
                manager = new CarPowerManager(binder, mContext, mEventHandler);
                break;
            case CAR_CONFIGURATION_SERVICE:
                manager = new CarConfigurationManager(binder);
                break;
            default:
                break;
        }
        return manager;
    }

2.3 VehiclePropertyIds.java

路径packages\services\Car\car-lib\src\android\car\VehiclePropertyIds.java

添加新的ID

//Cap feature
    public static final int CAP_POWER_ON = 287311361;
    public static final int CAP_LEVEL = 356517378;
    public static final int CAP_MODE = 289408515;

//Cap feature

    public static  String toString(int o) {
//Cap feature
        if (o == CAP_POWER_ON) {
            return "CAP_POWER_ON";
        }
        if (o == CAP_LEVEL) {
            return "CAP_LEVEL";
        }
        if (o == CAP_MODE) {
            return "CAP_MODE";
        }
//Cap feature
    }

2.4 system-current.txt

路径packages\services\Car\car-lib\api\system-current.txt

将car-lib的改动添加到这里

 public final class Car {
····
         field public static final java.lang.String CAP_SERVICE = "cap";
         field public static final java.lang.String PERMISSION_CONTROL_CAR_AP = "android.car.permission.CONTROL_CAR_AP";
····
 }



package android.car.hardware.cap {

  public final class CarApManager {
    method public boolean getBooleanProperty(int, int) throws android.car.CarNotConnectedException;
    method public float getFloatProperty(int, int) throws android.car.CarNotConnectedException;
    method public int getIntProperty(int, int) throws android.car.CarNotConnectedException;
    method public java.util.List<android.car.hardware.CarPropertyConfig> getPropertyList() throws android.car.CarNotConnectedException;
    method public static boolean isZonedProperty(int);
    method public synchronized void registerCallback(android.car.hardware.cap.CarApManager.CarApEventCallback) throws android.car.CarNotConnectedException;
    method public void setBooleanProperty(int, int, boolean) throws android.car.CarNotConnectedException;
    method public void setFloatProperty(int, int, float) throws android.car.CarNotConnectedException;
    method public void setIntProperty(int, int, int) throws android.car.CarNotConnectedException;
    method public synchronized void unregisterCallback(android.car.hardware.cap.CarApManager.CarApEventCallback);
    field public static final int ID_CAP_POWER_ON = 287311361; // 0x11200601
    field public static final int ID_CAP_LEVEL = 356517378; // 0x15400602
    field public static final int ID_CAP_MODE = 289408515; // 0x11400603
  }

  public static abstract interface CarApManager.CarApEventCallback {
    method public abstract void onChangeEvent(android.car.hardware.CarPropertyValue);
    method public abstract void onErrorEvent(int, int);
  }

}

三、carService

car-lib的真正实现者,对接hal的so库

3.1 ICarImpl.java

路径packages\services\Car\service\src\com\android\car\ ICarImpl.java

CarApManager是对CarPropertyManager的封装

@Override
    public IBinder getCarService(String serviceName) {
        switch (serviceName) {
·····
            case Car.CABIN_SERVICE:
            case Car.HVAC_SERVICE:
//Cap feature
            case Car.CAP_SERVICE:
//Cap feature
            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;
        }
    }


3.2 PropertyHalServiceIds.java

路径packages\services\Car\service\src\com\android\car\hal\PropertyHalServiceIds.java

将权限和property对应放入map里

 public PropertyHalServiceIds() {
 `````
 //Cap feature
        mProps.put(VehicleProperty.CAP_POWER_ON, new Pair<>(
                Car.PERMISSION_CONTROL_CAR_AP,
                Car.PERMISSION_CONTROL_CAR_AP));
        mProps.put(VehicleProperty.CAP_LEVEL, new Pair<>(
                Car.PERMISSION_CONTROL_CAR_AP,
                Car.PERMISSION_CONTROL_CAR_AP));
        mProps.put(VehicleProperty.CAP_MODE, new Pair<>(
                Car.PERMISSION_CONTROL_CAR_AP,
                Car.PERMISSION_CONTROL_CAR_AP));
//Cap feature

 `````
 }

3.3 AndroidManifest.xml

路径packages\services\Car\service\AndroidManifest.xml

在service里加入car ap权限

<permission
        android:name="android.car.permission.CONTROL_CAR_AP"
        android:protectionLevel="system|signature"
        android:label="@string/car_permission_label_ap"
        android:description="@string/car_permission_desc_ap" />


四、调用和测试

app AndroidManifest 里添加android.car.permission.CONTROL_CAR_AP 权限

private void init(Car car){
    mCarApManager = (CarApManager) car.getCarManager(Car.CAP_SERVICE);
    List<CarPropertyConfig> carPropertyConfigs = mCarApManager.getPropertyList();
}

截图

在这里插入图片描述

log
在这里插入图片描述

11-16 05:10:39.560 D 6153     6153     BaseCarTestFragment_ApFragment:  onStartTrackingTouch:  
11-16 05:10:39.560 D 6153     6153     BaseCarTestFragment_ApFragment:  onProgressChanged: isFloatValue false value 1 
11-16 05:10:39.653 D 6153     6153     BaseCarTestFragment_ApFragment:  onStopTrackingTouch:  
11-16 05:10:39.673 D 6153     6153     CapCarViewModel:                 handleMessage: propertyId = 356517378 areaId = 1 type = 1 value = 1 
11-16 05:10:39.674 I 1614     5898     DefaultVehicleHal_v2_0:          set propId: 0x15400602, areaId: 0x1 
11-16 05:10:39.688 D 6153     6153     CapCarViewModel:                 onChangeEvent: propertyId = 356517378 areaId = 1 value = 1 
11-16 05:10:39.688 D 6153     6153     CapCarViewModel:                 onChangeEvent: CarPropertyValue{mPropertyId=0x15400602, mAreaId=0x1, mStatus=0, mTimestamp=0, mValue=1} 
11-16 05:10:39.688 D 6153     6153     CapCarViewModel:                 onChangeEvent: find and update list ID_CAP_LEVEL 
11-16 05:10:39.689 D 6153     6153     ApFragment:                      onChanged: 2 0 [BaseCarProperty{propertyId=356517378, propertyName=ID_CAP_LEVEL, access=3, changeMode=1, configString=, configArray=[], maxSampleRate=0.0, minSampleRate=0.0, global=false, type=class java.lang.Integer, description=unDefineDes, areaCount=4, areaIds=[1, 4, 16, 64], mCarPropertyConfig=CarPropertyConfig{mPropertyId=356517378, mAccess=3, mAreaType=3, mChangeMode=1, mConfigArray=[], mConfigString=, mMaxSampleRate=0.0, mMinSampleRate=0.0, mSupportedAreas={1=CarAreaConfig{mMinValue=0, mMaxValue=5}, 4=CarAreaConfig{mMinValue=0, mMaxValue=5}, 16=CarAreaConfig{mMinValue=0, mMaxValue=5}, 64=CarAreaConfig{mMinValue=0, mMaxValue=5}}, mType=class java.lang.Integer}}, BaseCarProperty{propertyId=289408515, propertyName=ID_CAP_MODE, access=3, changeMode=1, configString=, configArray=[], maxSampleRate=0.0, minSampleRate=0.0, global=true, type=class java.lang.Integer, description=unDefineDes, areaCount=1, areaIds=[0], mCarPropertyConfig=CarPropertyConfig{mPropertyId=289408515, mAccess=3, mAreaType=0, mChangeMode=1, mConfigArray=[], mConfigString=, mMaxSampleRate=0.0, mMinSampleRate=0.0, mSupportedAreas={0=CarAreaConfig{mMinValue=0, mMaxValue=0}}, mType=class java.lang.Integer}}, BaseCarProperty{propertyId=287311361, propertyName=ID_CAP_POWER_ON, access=3, changeMode=1, configString=, configArray=[], maxSampleRate=0.0, minSampleRate=0.0, global=true, type=class java.lang.Boolean, description=unDefineDes, areaCount=1, areaIds=[0], mCarPropertyConfig=CarPropertyConfig{mPropertyId=287311361, mAccess=3, mAreaType=0, mChangeMode=1, mConfigArray=[], mConfigString=, mMaxSampleRate=0.0, mMinSampleRate=0.0, mSupportedAreas={0=null}, mType=class java.lang.Boolean}}] 


五、问题bug

模拟器设置里直接点击ID设值,模拟器会崩溃,原因未知,后续解决

  • 6
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值