【Android R】车载 Android 核心服务 - CarPropertyService

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

前言

对比开发车载Android和手机Android应用,最大的区别应该就是许多车载应用需要考虑汽车整体的运行状态,例如,控制车载空调或车速达到一定的阈值时,出于安全的考虑多媒体应用要主动保持静音;汽车处于行驶状态下,OTA应用要保持静默等等。APP如何从Framework层获取车辆状态的数据,而Framework层又是从哪里获取到数据,它们的运行机制是怎样的,就是本篇要解释的问题了。

本文是车载Android核心服务系列文章的第二篇,系列目录如下:

1.【Android R】车载 Android 核心服务 - CarService 解析

2.【Android R】车载 Android 核心服务 - CarPropertyService 解析

这个系列文章的主要目的在于整理原生车载Android系统中,一些核心Service的运行原理与源码实现,所以会有大段的源码解读,内容会比较枯燥。如果阅读本文时还没有车载或车载相关的经验并不丰富,建议先阅读从应用工程师的角度再谈车载 Android 系统,了解车载Android系统的基础结构。

本系列涉及的应用层API以及Framework层的实现方式,基于原生车载Android R系统,由于实际项目中各个主机厂商会对CarService做种种修改,本系列内容仅供车载开发者参考。

CarPropertyService 简介

通过上一篇的介绍,我们了解了狭义上的CarService其实只是一系列Binder对象的容器,本身并没有多少特殊功能,有过车载经验的同学可能会有疑问,因为这可能和你正在经历的项目有出入,这是因为部分量产型的车载项目为了保证关键服务的稳定性,CarService中不少功能都被独立出去了,导致CarService实际上只能提供了查询、设置车辆属性的功能,而这部分功能就是本篇的主角 - CarPropertyService实现的。

从实现上来说CarPropertyService的架构如下:

我们从下往上依次来介绍:

  • VehicleHAL

用于接收MCU数据的HAL层程序。VehicleHAL与MCU之间是如何进行通信的,每个车载项目技术选型不同,实现上也千差万别,无法详细介绍。我个人经历过得的某个车载项目是使用DBUS

  • HalClient

HIDL在Client端的HwBinder对象,实现最基本的HIDL通信功能。

  • VehicleHal

用于与HAL层的Vehicle HAL程序的通信接口。它需要对接收到的数据进行基本解析(类型检查),然后将每个事件发送到相应的HalServiceBase实现类里。

由于Framework层的VehicleHal与HAL层的VehicleHAL存在重名,后面为了区分会用VehicleHal(FWK) 表示Framework层的VehicleHal,用VehicleHAL(HAL) 表示HAL层的VehicleHAL。

  • PropertyHalService

负责进一步处理来自VehicleHal(FWK)数据的接口。是HalServiceBase的实现类。

  • CarPropertyService

ICarProperty.aidl的实现类。是应用层与HAL层的通信中继。

  • CarPropertyManager

CarPropertyService在Client端的代理。车载系统中的应用需要通过CarPropertyManager来获取或设置车辆的属性。

先来看 CarPropertyManager 提供的API。

车辆属性 API

在Android R中CarInfoManagerCarCabinManagerCarHvacManagerCarSensorManagerCarVendorExtensionManager均已经过时,在我个人实际经历的车载项目中,负责开发CarService的同事,也会选择将以上Manager移除使用CarPropertyManager替代。

虽然将汽车的Property属性分散到独立的Manager中可以让Car API的易用性、可读性更强,但是随着汽车属性的不断增加,API的维护也会变得愈加复杂,而CarPropertyManager从实现上就让维护工作变得简单,Google可能也是基于以上的考虑选择不再维护独立的Manager。

所以本文不再介绍CarInfoManagerCarCabinManagerCarHvacManagerCarSensorManagerCarVendorExtensionManager的实现,有需要的同学请参考源码中是如何实现的。

CarPropertyManager API 介绍

CarPropertyManager 中定义的常量

类型 常量名
int CAR_SET_PROPERTY_ERROR_CODE_ACCESS_DENIED 表示设置操作失败的状态,汽车拒绝访问。
int CAR_SET_PROPERTY_ERROR_CODE_INVALID_ARG 表示设置操作失败的状态,参数无效。
int CAR_SET_PROPERTY_ERROR_CODE_PROPERTY_NOT_AVAILABLE 表示设置操作失败的状态,属性不可用。
int CAR_SET_PROPERTY_ERROR_CODE_TRY_AGAIN 表示设置操作失败的状态,重新尝试。
int CAR_SET_PROPERTY_ERROR_CODE_UNKNOWN 表示设置操作失败的状态,未知错误。
float SENSOR_RATE_FAST 以10Hz的速率读取传感器。
float SENSOR_RATE_FASTEST 以100Hz的速率读取传感器。
float SENSOR_RATE_NORMAL 以1Hz的速率读取传感器。
float SENSOR_RATE_ONCHANGE 读取ON_CHANGE传感器
float SENSOR_RATE_UI 以5Hz的速率读取传感器。

CarPropertyManager 中定义的方法。

返回值类型 方法名
int getAreaId(int propId, int area) 返回包含车辆属性选定区域的areaId。
boolean getBooleanProperty(int prop, int area) 返回bool类型的车辆属性,此方法可能需要几秒钟才能完成,因此需要从非主线程调用它。
CarPropertyConfig<?> getCarPropertyConfig(int propId) 按属性Id获取CarPropertyConfig。
float getFloatProperty(int prop, int area) 返回float类型的车辆属性,此方法可能需要几秒钟才能完成,因此需要从非主线程调用它。
int[] getIntArrayProperty(int prop, int area) 返回int数组类型的车辆属性,此方法可能需要几秒钟才能完成,因此需要从非主线程调用它。
int getIntProperty(int prop, int area) 返回int类型的车辆属性,此方法可能需要几秒钟才能完成,因此需要从非主线程调用它。
CarPropertyValue getProperty(Class clazz, int propId, int areaId) 返回CarPropertyValue类型的车辆属性,此方法可能需要几秒钟才能完成,因此需要从非主线程调用它。
CarPropertyValue getProperty(int propId, int areaId)
List getPropertyList(ArraySet propertyIds)
List getPropertyList()
boolean isPropertyAvailable(int propId, int area) 根据汽车的当前状态,检查给定属性是否可用或禁用。
boolean registerCallback(CarPropertyManager.CarPropertyEventCallback callback, int propertyId, float rate) 注册CarPropertyEventCallback以获取车辆属性更新。
void setBooleanProperty(int prop, int areaId, boolean val) 修改属性。
void setFloatProperty(int prop, int areaId, float val) 设置float类型的车辆属性,此方法可能需要几秒钟才能完成,因此需要从非主线程调用它。
void setIntProperty(int prop, int areaId, int val) 设置int类型的车辆属性,此方法可能需要几秒钟才能完成,因此需要从非主线程调用它。
void setProperty(Class clazz, int propId, int areaId, E val) 按areaId设置车辆属性的值。
void unregisterCallback(CarPropertyManager.CarPropertyEventCallback callback) 停止监听车辆属性的更新回调
void unregisterCallback(CarPropertyManager.CarPropertyEventCallback callback, int propertyId) 停止监听车辆属性的更新回调

setXXXProperty/getXXXProperty默认只实现了对float、int、boolean、intArray类型的拓展,如需要使用更多的类型,可以使用setProperty/getProperty(Class class)传入需要拓展的类型即可。

CarPropertyManager 的实现原理并不复杂,可以直接参考源码:/packages/services/Car/car-lib/src/android/car/hardware/property/CarPropertyManager.java

CarPropertyConfig API 介绍

CarPropertyConfig表示有关汽车属性的一般信息,例如汽车区域的数据类型和最小/最大范围(如果适用)。也是实际开发中非常常用的类。

CarPropertyConfig 中定义的常量。
| 类型 | 常量名 |
| --- | -------------------------------------------------------- |
| int | VEHICLE_PROPERTY_ACCESS_NONE 属性访问权限未知 |
| int | VEHICLE_PROPERTY_ACCESS_READ 该属性是可读的 |
| int | VEHICLE_PROPERTY_ACCESS_READ_WRITE 该属性是可读、可写的 |
| int | VEHICLE_PROPERTY_ACCESS_WRITE 该属性是可写的 |
| int | VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS 这种属性值会以一定的频率不断上报 |
| int | VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE 该属性的值会在发生变化时上报 |
| int | VEHICLE_PROPERTY_CHANGE_MODE_STATIC 该属性的值始终不会改变 |

CarPropertyConfig 中定义的方法
| 返回值类型 | 方法名 |
| ------------- | -------------------------------------------- |
| int | getAccess() 返回汽车属性的访问类型。具体类型就是上面定义的前4个常量 |
| int[] | getAreaIds() 返回汽车的区域id数组 |
| int | getAreaType() 返回汽车属性的区域类型。 |
| int | getChangeMode() 返回汽车属性的更改模式。具体模式就是上面定义的后3个常量 |
| List | getConfigArray() 返回额外的配置属性 |
| float | getMaxSampleRate() 返回最大频率。仅支持持续上报的属性 |
| float | getMinSampleRate() 返回最小频率。仅支持持续上报的属性 |
| T | getMaxValue(int areaId) |
| T | getMaxValue() |
| T | getMinValue() |
| T | getMinValue(int areaId) |
| int | getPropertyId() 返回属性ID |
| Class | getPropertyType() 返回车辆属性的类型 |
| boolean | isGlobalProperty() 返回 是否是全局属性 |

CarPropertyManager 使用示例

使用CarPropertyManager可以分为以下几个步骤:

1)使用Car连接到 CarService ,并获取到 CarPropertyManager

Car car = Car.createCar(this, workThreadHandler, 2000, new Car.CarServiceLifecycleListener() {
    @Override
    public void onLifecycleChanged(@NonNull Car car, boolean ready) {
        // ready 在Service断开连接时会变为false
if (ready) {
            CarPropertyManager propertyMgr = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);

        } else {
            // CarService 发生异常或连接被断开了,需要client端处理。
}
    }
});

2)给所有的property属性注册监听事件

 // 空调的property id list,需要看hal层是如何定义的
 private final ArraySet<Integer> mHvacPropertyIds = new ArraySet<>(Arrays.asList(new Integer [] {
            ...
    }));


CarPropertyManager propertyMgr = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
List<CarPropertyConfig> propertyList = propertyMgr.getPropertyList(mHvacPropertyIds);
for (CarPropertyConfig config : propertyList) {
    // 给每个单独的propertyId注册监听回调。
propertyMgr.registerCallback(callback,config.getPropertyId(), SENSOR_RATE_ONCHANGE);
}

3)获取单个Property的值

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

虽然使用getBooleanProperty、getIntProperty、getFloatProperty、getIntArrayProperty代码上更简洁一些,但是更建议使用getProperty()getProperty()的返回值是CarPropertyValue,这其中包含了Property的状态信息,可以让使用方覆盖更多的异常场景。

当属性不可用时,getXXXProperty()会返回默认的值,造成使用方读取数据不准确。

public CarPropertyValue<Boolean> getBooleanProperty(int propertyId, int area) {
    return propertyMgr.getProperty(Boolean.class, propertyId, area);
}

CarPropertyValue<Boolean> value = getBooleanProperty(CarHvacManager.ID_ZONED_AC_ON, 0);
if (value == null && value.getStatus() != CarPropertyValue.STATUS_AVAILABLE) {
    // ac 不可用
} else if (value.getValue()) {
    // ac 开
} else {
    // ac 关
}

4)设定单个Property的值

public void setBooleanProperty(@PropertyId int propertyId, int area, boolean val) {
    if (mHvacPropertyIds.contains(propertyId)) {
        propertyMgr.setBooleanProperty(propertyId, area, val);
    }
}

设定的值最终会通过aidl接口,将数据传输到CarPropertyService中,接下来我们继续看数据在CarPropertyService中是如何传递的。

CarPropertyService 实现原理

CarPropertyService 初始化流程

CarPropertyService是在CarService中完成创建的,CarService的初始化流程在之前的文章【Android R】车载 Android 核心服务 - CarService 解析中已经有过介绍,不再赘述。CarPropertyService的初始流程分为以下4步:

1)首先,在ICarImpl中创建VehicleHal(FWK);

@VisibleForTesting
ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,
         CanBusErrorNotifier errorNotifier, String vehicleInterfaceName,
         @Nullable CarUserService carUserService,
         @Nullable CarWatchdogService carWatchdogService) {
    ...
    mHal = new VehicleHal(serviceContext, vehicle);
    // 在任何其他服务组件之前执行此操作,以允许进行功能检查。即使没有初始化,它也应该工作。
 // 为此,vhal-get会被重试,因为它可能太早了。
VehiclePropValue disabledOptionalFeatureValue = mHal.getIfAvailableOrFailForEarlyStage(
            VehicleProperty.DISABLED_OPTIONAL_FEATURES, INITIAL_VHAL_GET_RETRY);
    String[] disabledFeaturesFromVhal = null;
    if (disabledOptionalFeatureValue != null) {
        String disabledFeatures = disabledOptionalFeatureValue.value.stringValue;
        if (disabledFeatures != null && !disabledFeatures.isEmpty()) {
            disabledFeaturesFromVhal = disabledFeatures.split(",");
        }
    }
    if (disabledFeaturesFromVhal == null) {
        disabledFeaturesFromVhal = new String[0];
    }
    ...
}

2)在 VehicleHal(FWK) 创建过程中,同时创建出 PropertyHalService 和 HalClient;

public VehicleHal(Context context, IVehicle vehicle) {
    ...
    mPropertyHal = new PropertyHalService(this);
    ...
mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), this /*IVehicleCallback*/ );
}

3)然后,在ICarImpl中创建 CarPropertyService;

ICarImpl(Context serviceContext, IVehicle vehicle, SystemInterface systemInterface,
         CanBusErrorNotifier errorNotifier, String vehicleInterfaceName,
         @Nullable CarUserService carUserService,
         @Nullable CarWatchdogService carWatchdogService) {
    ...
    mCarPropertyService = new CarPropertyService(serviceContext, mHal.getPropertyHal());
    ...
}

4)最后,在 ICarImpl 中调用 VehicleHal.init() CarPropertyService.init() 完成初始化。

@MainThread
void init() {
    mHal.init();
    for (CarServiceBase service : mAllServices) {
        service.init();
    }
}

接下来,我们依次把这些模块是如何实现数据上报的流程梳理一下,先来看处于Framework最底层的 HalClient。


HalClient

车辆HAL客户端。直接与车辆HAL的HIDL接口IVehicle交互。包含一些可检索属性的逻辑,将车辆通知重定向到给定的looper线程中。

HalClient(IVehicle vehicle, Looper looper, IVehicleCallback callback,
          int waitCapMs, int sleepMs) {
    mVehicle = vehicle;
    Handler handler = new CallbackHandler(looper, callback);
    mInternalCallback = new VehicleCallback(handler);
    mWaitCapMs = waitCapMs;
    mSleepMs = sleepMs;
}

VehicleCallback 是HIDL接口IVehicleCallback.Stub的实现类,负责监听HAL层上报的数据,然后将其发送到CallbackHandler中进行处理。

    private static final class VehicleCallback extends IVehicleCallback.Stub {
        private final Handler mHandler;

        VehicleCallback(Handler handler) {
            mHandler = handler;
        }

        @Override
        public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {
            mHandler.sendMessage(Message.obtain(
                    mHandler, CallbackHandler.MSG_ON_PROPERTY_EVENT, propValues));
        }

        @Override
        public void onPropertySet(VehiclePropValue propValue) {
            mHandler.sendMessage(Message.obtain(
                    mHandler, CallbackHandler.MSG_ON_PROPERTY_SET, propValue));
        }

        @Override
        public void onPropertySetError(int errorCode, int propId, int areaId) {
            mHandler.sendMessage(Message.obtain(
                    mHandler, CallbackHandler.MSG_ON_SET_ERROR,
                    new PropertySetError(errorCode, propId, areaId)));
        }
    }

CallbackHandler是一个自定义的Handler,会将VehicleHal(HAL)上报的数据分类通过callback回调给VehicleHal(FWK)。

private static final class CallbackHandler extends Handler {
    private static final int MSG_ON_PROPERTY_SET = 1;
    private static final int MSG_ON_PROPERTY_EVENT = 2;
    private static final int MSG_ON_SET_ERROR = 3;
    ...
    @Override
    public void handleMessage(Message msg) {
        IVehicleCallback callback = mCallback.get();
        ...
        try {
            switch (msg.what) {
                case MSG_ON_PROPERTY_EVENT:
                    callback.onPropertyEvent((ArrayList<VehiclePropValue>) msg.obj);
                    break;
                case MSG_ON_PROPERTY_SET:
                    callback.onPropertySet((VehiclePropValue) msg.obj);
                    break;
                case MSG_ON_SET_ERROR:
                    PropertySetError obj = (PropertySetError) msg.obj;
                    callback.onPropertySetError(obj.errorCode, obj.propId, obj.areaId);
                    break;
                default:
                    Log.e(TAG, "Unexpected message: " + msg.what);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Message failed: " + msg.what);
        }
    }
}

思考一个问题,为什么HAL上报的数据信息要先经过Handler再处理呢?

这既有线程切换的考虑,还有就是VehicleHAL(HAL)上报的数据有时会非常频繁,将数据放到Looper的MessageQueue中可以便于我们按照上报的顺序,有序地处理数据。

VehicleHal

用于与HAL层的Vehicle HAL程序的通信接口。将HalClient回调过来的数据,进行初步的处理。我们以onPropertyEvent为例,看一下VehicleHAl(FWK)是怎么处理HalClient回调过来的数据的。

VehicleHAl(FWK)的处理方式分为两步

1)第一步,根据上报数据找到对应的HalServiceBase(HalServiceBase是PropertyHalService的父类),将VehiclePropValue添加到PropertyHalService的list中。

@Override
public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {
    synchronized (mLock) {
        for (VehiclePropValue v : propValues) {
            HalServiceBase service = mPropertyHandlers.get(v.prop);
            if(service == null) {
                Log.e(CarLog.TAG_HAL, "HalService not found for prop: 0x"
                        + toHexString(v.prop));
                continue;
            }
            service.getDispatchList().add(v);
            mServicesToDispatch.add(service);
            ...
        }
    }
    ...
}

2)第二步,主动触发PropertyHalService.onHalEvents()将VehiclePropValue发送到PropertyHalService中,紧接着清理掉缓存数据。

@Override
public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) {
    ...
    for (HalServiceBase s : mServicesToDispatch) {
        s.onHalEvents(s.getDispatchList());
        s.getDispatchList().clear();
    }
    mServicesToDispatch.clear();
}

PropertyHalService

PropertyHalService.onHalEvents中处理接收到的value list。将数据转换为CarPropertyValue后,通过PropertyHalListener将处理好的数据回调给CarPropertyService,而最终会由CarPropertyService将数据回调会应用层的接口。

@Override
public void onHalEvents(List<VehiclePropValue> values) {
    PropertyHalListener listener;
    ...
    if (listener != null) {
        for (VehiclePropValue v : values) {
            if (v == null) {
                continue;
            }
            ...
            int mgrPropId = halToManagerPropId(v.prop);
            CarPropertyValue<?> propVal;
            if (isMixedTypeProperty(v.prop)) {
                // parse mixed type property value.
VehiclePropConfig propConfig;
                synchronized (mLock) {
                    propConfig = mHalPropIdToVehiclePropConfig.get(v.prop);
                }
                boolean containBooleanType = propConfig.configArray.get(1) == 1;
                propVal = toMixedCarPropertyValue(v, mgrPropId, containBooleanType);
            } else {
                propVal = toCarPropertyValue(v, mgrPropId);
            }
            // 封装到 CarPropertyEvent
CarPropertyEvent event = new CarPropertyEvent(
                    CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, propVal);
            mEventsToDispatch.add(event);
        }
        listener.onPropertyChange(mEventsToDispatch);
        mEventsToDispatch.clear();
    }
}

注意两个方法,toMixedCarPropertyValue()toCarPropertyValue() 如果数据类型是Integer、Float、Long、Float[]、Long[]、Integer[]、byte[]、String则由 toCarPropertyValue()负责数据转换。除此以外的类型由toMixedCarPropertyValue()负责数据转换。它们都是将VehiclePropValue转换为CarPropertyValue


设定/获取 Property

设定和与获取Property的流程并不复杂,这里就不再粘贴源码了逐个讲解了,贴上一份设定的时序图。

权限控制

上面在分析CarPropertyService监听属性变化的具体实现时,我们提到了使用Car API的接口需要注册对应的权限,那么这些权限是如何管理的呢?

在PropertyHalService的构造方法中,创建了一个PropertyHalServiceIds的对象,而这个对象就是用来存储每个属性所需要的权限的。

PropertyHalServiceIds源码位置:/packages/services/Car/service/src/com/android/car/hal/PropertyHalServiceIds.java

public PropertyHalService(VehicleHal vehicleHal) {
    mPropIds = new PropertyHalServiceIds();
    mSubscribedHalPropIds = new HashSet<Integer>();
    mVehicleHal = vehicleHal;
}

在PropertyHalServiceIds的构造方法中,将每个属性对应需要的权限进行了一一关联,保存在一个SparseArray中。

那么接下来我们以getProperty()方法为例,看一下是如何限制无权限应用的调用的。

@Override
public CarPropertyValue getProperty(int prop, int zone) {
    ...
    ICarImpl.assertPermission(mContext, mHal.getReadPermission(prop));
    return mHal.getProperty(prop, zone);
}

@Nullable
public String getReadPermission(int mgrPropId) {
    int halPropId = managerToHalPropId(mgrPropId);
    return mPropIds.getReadPermission(halPropId);
}

@Nullable
public String getReadPermission(int propId) {
    Pair<String, String> p = mProps.get(propId);
    if (p != null) {
        // 属性ID存在。返回 权限。
if (p.first == null) {
            Log.e(TAG, "propId is not available for reading : 0x" + toHexString(propId));
        }
        return p.first;
    } else if (isVendorProperty(propId)) {
        // 如果属性是供应商属性,并且没有特定权限。
return Car.PERMISSION_VENDOR_EXTENSION;
    } else {
        return null;
    }
}

getReadPermission的调用链很好懂,主要就是将传入的id与PropertyHalServiceIds中关联好的权限比对,并取出对应的权限字符串。

assertPermission方法会判断调用方是否拥有相应的权限,或本次调用是否是自身发起的,如果不是,则会抛出SecurityException。

#########ICarImpl.java###############
public static void assertPermission(Context context, String permission) {
    if (context.checkCallingOrSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
        throw new SecurityException("requires " + permission);
    }
}

setProperty()方法的权限检查与getProperty()方法类似,不过getProperty()方法是检查ReadPermission,setProperty()方法是检查WritePermission。

车辆属性的读取以及操作需要慎重授权给应用,所以权限控制在车载Android系统中就显得尤为重要。

VehicleHAL

VehicleHAL 是由Android Automotive OS定义的硬件抽象层(hardware abstract layer)。它定义了 OEM 可以实现的属性以及与Framework Service交互的接口。用户对车辆APP产生的一系列操作最终都会来到VehicleHAL,并由于VehicleHAL转发出Android系统。

源码地址:/hardware/interfaces/automotive/vehicle/2.0/

源码结构

VehicleHAL主要由IPC接口交互逻辑两部分组成,如下所示

  • IVehicle.hal

定义VehicleHAL对外暴露的方法。

  • IVehicleCallback.hal

定义属性变化时的回调接口

  • types.hal

定义在IVehicle.halIVehicleCallback.hal中使用的数据结构和属性值。

上述三个hal文件的结构与AIDL的通信结构非常相似,不过这种通信方式叫做HIDL(读作:嗨豆)。

有关HIDL的进一步内容,请参考官方文档:https://source.android.google.cn/docs/core/architecture/hidl

  • default/ utils/

是Android Automotive OS对于VechicleHAL的参考实现。但是其中并没有实现与车辆总线进行数据交互这样的业务逻辑,这块的内容需要主机制造商自行实现。

VehicleHAL 接口

VHAL 支持以下接口:

  • getAllPropConfigs() generates (vec propConfigs);

列出 VehicleHAL 所支持的所有属性的配置。CarService 仅使用支持的属性。

  • getPropConfigs(vec props) generates (StatusCode status, vec propConfigs);

返回所选属性的配置。

  • get(VehiclePropValue requestedPropValue) generates (StatusCode status, VehiclePropValue propValue);

获取车辆属性值。

  • set(VehiclePropValue propValue) generates (StatusCode status);

向属性写入一个值。写入的结果是按属性进行定义的。

  • subscribe(IVehicleCallback callback, vec options) generates (StatusCode status);

开始监视属性值的变化。

  • unsubscribe(IVehicleCallback callback, int32_t propId) generates (StatusCode status);

取消订阅属性事件。

VHAL 支持以下回调接口:

  • oneway onPropertyEvent(vecpropValues);

通知车辆属性值的变化。应只针对已订阅属性执行。

  • oneway onPropertySet(VehiclePropValue propValue);

如果客户端使用SubscribeFlags.EVENTS_FROM_ANDROID标志订阅了属性,并且调用了IVehicle.set()方法,则会调用此方法。

  • oneway onPropertySetError(StatusCode errorCode,int32_t propId,int32_tareaId);

返回全局 VHAL 级错误或每个属性的错误。全局错误会导致 HAL 重新启动,这可能会导致包括应用在内的其他组件重新启动。

编译VehicleHAL

HIDL接口定义好之后,与AIDL接口一样需要编译更jar提供给Framework的开发,以下步骤是编译原生的VehicleHAL,实际项目中的VehicleHAL一般会放置vendor里面,但是编译方式一样。

cd hardware/interfaces/automotive/vehicle/2.0
mma

编译好的jar包位于

/out/soong/.intermediates/hardware/interfaces/automotive/vehicle/2.0/android.hardware.automotive.vehicle-V2.0-java/android_common/javac

如下图所示:

由于博主并没有实际从事过HAL层的开发,有关VehicleHAL就只介绍到这里,实际工作中一般会有单独负责HAL层的同事编写这里的代码。

更多内容请参考官方的文档:https://source.android.google.cn/docs/devices/automotive/vhal

总结

本篇介绍了CarPropertyService的实现原理,但是仅通过阅读这篇文章其实并不能完全掌握整个CarPropertyService,这是任何技术性文章都做不到的通病,实际项目依然需要我们仔细阅读源码,分析方法的含义,本篇文章的实际目的是让你弄清楚关键节点的实现方式和运行原理。

我个人也负责过CarPropertyService的开发工作,当时由于对CarPropertyService的运行机制并不了解,选择整个重写CarPropertyService,现在想想着实走了不少弯路。

好了,感谢你的阅读,希望能帮助到你。

目录
相关文章
|
2月前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
Android远程连接和登录FTPS服务代码(commons.net库)
24 1
|
3月前
|
JavaScript 前端开发 Android开发
让Vite+Vue3项目在Android端离线打开(不需要起服务)
让Vite+Vue3项目在Android端离线打开(不需要起服务)
100 10
|
3月前
|
调度 Android开发 UED
Android经典实战之Android 14前台服务适配
本文介绍了在Android 14中适配前台服务的关键步骤与最佳实践,包括指定服务类型、请求权限、优化用户体验及使用WorkManager等。通过遵循这些指南,确保应用在新系统上顺畅运行并提升用户体验。
221 6
|
3月前
|
安全 API 开发工具
Android平台RTMP推送|轻量级RTSP服务如何实现麦克风|扬声器声音采集切换
Android平台扬声器播放声音的采集,在无纸化同屏等场景下,意义很大,早期低版本的Android设备,是没法直接采集扬声器audio的(从Android 10开始支持),所以,如果需要采集扬声器audio,需要先做系统版本判断,添加相应的权限。
|
3月前
|
编解码 开发工具 Android开发
Android平台实现屏幕录制(屏幕投影)|音频播放采集|麦克风采集并推送RTMP或轻量级RTSP服务
Android平台屏幕采集、音频播放声音采集、麦克风采集编码打包推送到RTMP和轻量级RTSP服务的相关技术实现,做成高稳定低延迟的同屏系统,还需要有配套好的RTMP、RTSP直播播放器
|
4月前
|
数据处理 开发工具 数据安全/隐私保护
Android平台RTMP推送|轻量级RTSP服务|GB28181接入之文字、png图片水印的精进之路
本文探讨了Android平台上推流模块中添加文字与PNG水印的技术演进。自2015年起,为了满足应急指挥及安防领域的需求,逐步发展出三代水印技术:第一代为静态文字与图像水印;第二代实现了动态更新水印内容的能力,例如实时位置与时间信息;至第三代,则优化了数据传输效率,直接使用Bitmap对象传递水印数据至JNI层,减少了内存拷贝次数。这些迭代不仅提升了用户体验和技术效率,也体现了开发者追求极致与不断创新的精神。
|
4月前
|
数据采集 编解码 开发工具
Android平台实现无纸化同屏并推送RTMP或轻量级RTSP服务(毫秒级延迟)
一个好的无纸化同屏系统,需要考虑的有整体组网、分辨率、码率、实时延迟、音视频同步和连续性等各个指标,做容易,做好难
|
4月前
|
监控 开发工具 Android开发
Android平台实现RTSP拉流转发至轻量级RTSP服务
为满足Android平台上从外部RTSP摄像头拉流并提供轻量级RTSP服务的需求,利用大牛直播SDK实现了相关功能。SDK支持开始与停止拉流、音频视频数据回调处理及RTSP服务的启动与发布等操作。拉流仅需将未解码数据回调,对性能影响小。音频和视频数据经由特定接口传递给发布端进行处理。此外,SDK还提供了获取RTSP会话数量的功能。此方案适用于监控和巡检等低延迟应用场景,并支持二次水印添加等功能。
|
4月前
|
编解码 API 开发工具
Android平台轻量级RTSP服务模块二次封装版调用说明
本文介绍了Android平台上轻量级RTSP服务模块的二次封装实践,旨在简化开发流程,让开发者能更专注于业务逻辑。通过`LibPublisherWrapper`类提供的API,可在应用中轻松初始化RTSP服务、配置视频参数(如分辨率、编码类型)、启动与停止RTSP服务及流发布,并获取RTSP会话数量。此外,还展示了如何处理音频和视频数据的采集与推送。最后,文章提供了从启动服务到销毁资源的完整示例,帮助开发者快速集成实时流媒体功能。
|
4月前
|
编解码 开发工具 Android开发
Android平台轻量级RTSP服务模块技术接入说明
为满足内网无纸化/电子教室等内网超低延迟需求,避免让用户配置单独的服务器,大牛直播SDK在推送端发布了轻量级RTSP服务SDK。 轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,实现本地的音视频数据(如摄像头、麦克风),编码后,汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,轻量级RTSP服务,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。