当前位置:   article > 正文

Android 9.0 AutoMotive模块之Vehicle_android vehicle

android vehicle

Vehicle模块

简介

AutoMotive是啥? 就是google看大家现在都用Android操作系统做中控车机,但是你们每家跟车子沟通的方式都不一样呀,所以它就提供了一个平台给你,把车子的属性定义,值更新,值设置等都分离出来了,上层应用开发的不管下面如何与汽车进行通讯,按照Car API开发就行;各个Tier1/车厂你也不要管我上层如何实现,按照底层标准接口,对接实现通讯就完事。

架构

Android 9.0中的Vehicle模块实现相对8.0有了一定的变化,其实现的架构图如下所示,像HVAC这一类的,就是上层应用的具体实现,car-lib是一个java的分享库,是提供API提供给app调用设置以及查询汽车属性的,CarService是一个服务层,编译出来就是类似于SystemUI这种系统级服务应用,该服务内部了定义了诸多的子服务来与系统通信,与hal层的vehicle通信。hidl interface层主要是提供连两个接口定义文件,一个是IVehicle.hal,另外一个是IVehicleCallBack.hal; 前面一个是定义了Service层访问hal层的接口,后面一个顾名思义就是Hal层回调数据到Service层的回调接口。.hal文件是Android 8.0开始加入的hidl接口定义文件。关于hidl,后续在framework专栏里面再梳理一下。hal层的实现定义在hardware/interface/automotive里面,这里按照以前的话讲,叫做硬件抽象层。升级到8.0以后,原来以共享库存在,并提供jni的方式给到应用访问的方式叫做直通式访问,vhal给到应用的访问方式是通过hwservicemanager来进行的,这种方式叫做绑定式访问。各层编译出来的产物,以及路径定义如下:

功能: 上层API库
代码位置:/platform/packages/services/Car/car-lib
编译产物: android.car.jar

功能: 服务层
代码位置: /platform/packages/services/Car/service
编译产物: CarService.apk

功能: hidl通信接口
代码位置: /platform/hardware/interface/automotive/vehicle/2.0
Android.bp
IVehicleCallback.hal
IVehicle.hal
types.hal
编译产物: android.hardware.automotive.vehicle-V2.0-java.jar
android.hardware.automotive.vehicle@2.0.so

功能: hal层具体实现
代码位置: /platform/hardware/interface/automotive/vehicle/2.0/default
编译产物: 可执行文件 android.hardware.automotive.vehicle@2.0-service
动态库 android.hardware.automotive.vehicle@2.0-manager-lib.so
静态库 android.hardware.automotive.vehicle@2.0-default-impl-lib.a

在这里插入图片描述

功能流程

Hal层功能:首先看一下整个hal层中的vehicle模块代码目录。
在这里插入图片描述
这是vhal层的实现,首先一步步来看, 分析Android.bp文件,我们就知道该模块下哪些文件对应的被编译成了哪些产物。Android.bp里面一共配置编译了三个产物,其中包括一个动态库,对应如下:

// Vehicle reference implementation lib
cc_library {
    srcs: [
        "common/src/Obd2SensorStore.cpp",
        "common/src/SubscriptionManager.cpp",
        "common/src/VehicleHalManager.cpp",
        "common/src/VehicleObjectPool.cpp",
        "common/src/VehiclePropertyStore.cpp",
        "common/src/VehicleUtils.cpp",
        "common/src/VmsUtils.cpp",
    ],
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

以上包含的cpp文件将会被编译后将会生成名为android.hardware.automotive.vehicle@2.0-manager-lib的库文件,名称太长了,姑且称之为manager-lib左先锋;

接下来另外一个

// Vehicle default VehicleHAL implementation
    srcs: [
        "impl/vhal_v2_0/EmulatedVehicleHal.cpp",
        "impl/vhal_v2_0/VehicleEmulator.cpp",
        "impl/vhal_v2_0/PipeComm.cpp",
        "impl/vhal_v2_0/SocketComm.cpp",
        "impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
        "impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
    ],
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

以上几个文件将会被编译成一个名为
android.hardware.automotive.vehicle@2.0-default-impl-lib的库文件,名称也太长了,姑且称之为impl-lib右先锋。

大家都知道左右先锋都是归中军帐下,为元帅所有,所以这两个库跟VehicleService.cpp文件最终被一起编译成android.hardware.automotive.vehicle@2.0-service这个可执行文件,这里称它为service大帅。

众所周知,大帅一般要有虎符以及皇帝的命令才会调兵遣将,开始工作,android.hardware.automotive.vehicle@2.0-service.rc 这个玩意就类似皇帝密令,开机的时候,init进程扫到这个文件之后,会调用其中的命令拉起service大帅运行。

这里还要介绍一下左右先锋的作用,manager-lib左先锋主要负责与空中系统的通讯,就是我们所说的上层,VehicleHalManager类正是集成了IVehicle接口,所以只有它有天线,基本能够实现上下通达;另外还有一个数据缓存的功能;另外一个类基本就是一些数据订阅管理,还有工具类,实现一些模块的基础。

impl-lib右先锋是一个虚拟车身属性模块的实现,从它的组成就知道,骨血里流的都是Emulator, EmulatedVehicleHal是VehicleHal类的子类,而VehicleHal这个类,正是Android定义给我们自行客制化实现hal层功能的interface. 所以这里,右先锋是一个模拟车身通讯实现。所以这里右先锋是不能上战场的,它是个泥菩萨,只能跟模拟器玩玩,我们后期要么改造它,要么仿制它。

介绍完了之后,按照启动过程,来看下它的运行流程。

首先,我们知道可执行模块的入口,那必定是main函数,我们大帅的main函数就在VehicleService.cpp这个文件中,这个文件的内容比较简单,可以看下

int main(int /* argc */, char* /* argv */ []) {
    auto store = std::make_unique<VehiclePropertyStore>();
    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get());
    auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
    auto service = std::make_unique<VehicleHalManager>(hal.get());
    configureRpcThreadpool(4, true /* callerWillJoin */);
    ALOGI("Registering as service...");
    status_t status = service->registerAsService();
    if (status != OK) {
        ALOGE("Unable to register vehicle service (%d)", status);
        return 1;
    }
    ALOGI("Ready");
    joinRpcThreadpool();
    return 1;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

把代码翻译成汉语,流程如下:
1.初始化VehiclePropertyStore,得到store指针
2.初始化EmulatedVehicleHal,得到hal指针,初始化时,将VehiclePropertyStore指针作为参数传输了EmulatedVehicleHal构造方法中
3.初始化VehicleEmulator,得到emulator指针,初始化时,将EmulatedVehicleHal指针作为参数传入VehicleEmulator的构造方法中。
4.初始化VehicleHalManager,获得service智能指针。
5.之前介绍了VehicleHalManager继承自IVehicle hidl接口,该接口在编译的时候自动生成了registerAsService方法,该方法就是将服务本身通过binder注册到hwservicemanager里面供其他进程连接。这里不再深入介绍。

这里初始化模块总共只有那么几个东东,我们来一个个分析。首先是VehiclePropertyStore, 在看它之前,首先了解几个数据结构,

/**
 * Encapsulates the property name and the associated value. It
 * is used across various API calls to set values, get values or to register for
 * events.
 */
struct VehiclePropValue {
    /** Time is elapsed nanoseconds since boot */
    int64_t timestamp;

    /**
     * Area type(s) for non-global property it must be one of the value from
     * VehicleArea* enums or 0 for global properties.
     */
    int32_t areaId;

    /** Property identifier */
    int32_t prop;

    /** Status of the property */
    VehiclePropertyStatus status;

    /**
     * Contains value for a single property. Depending on property data type of
     * this property (VehiclePropetyType) one field of this structure must be filled in.
     */
    struct RawValue {
        /**
         * This is used for properties of types VehiclePropertyType#INT
         * and VehiclePropertyType#INT_VEC
         */
        vec<int32_t> int32Values;

        /**
         * This is used for properties of types VehiclePropertyType#FLOAT
         * and VehiclePropertyType#FLOAT_VEC
         */
        vec<float> floatValues;

        /** This is used for properties of type VehiclePropertyType#INT64 */
        vec<int64_t> int64Values;

        /** This is used for properties of type VehiclePropertyType#BYTES */
        vec<uint8_t> bytes;

        /** This is used for properties of type VehiclePropertyType#STRING */
        string stringValue;
    };

    RawValue value;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

VehiclePropValue 作为一个数据结构包含属性名称和关联的值。它
用于各种API调用,以设置值、获取值或注册事件。


struct VehiclePropConfig {
    /** Property identifier */
    int32_t prop;

    /**
     * Defines if the property is read or write or both.
     */
    VehiclePropertyAccess access;

    /**
     * Defines the change mode of the property.
     */
    VehiclePropertyChangeMode changeMode;

    /**
     * Contains per-area configuration.
     */
    vec<VehicleAreaConfig> areaConfigs;

    /** Contains additional configuration parameters */
    vec<int32_t> configArray;

    /**
     * Some properties may require additional information passed over this
     * string. Most properties do not need to set this.
     */
    string configString;

    /**
     * Min sample rate in Hz.
     * Must be defined for VehiclePropertyChangeMode::CONTINUOUS
     */
    float minSampleRate;

    /**
     * Must be defined for VehiclePropertyChangeMode::CONTINUOUS
     * Max sample rate in Hz.
     */
    float maxSampleRate;
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

VehiclePropConfig 是车身属性值配置。以上两个数据结构均定义在type.hal文件中,type.hal文件是hidl定义数据结构的文件,在编译时会自动生成对应的数据结构,了解了这两个数据结构的内容之后,再看看下VehiclePropertyStore中怎么用的,首先也从数据接口开始切入,再VehiclePropertyStore.h文件中,定义了如下结构体

     struct RecordConfig {
        VehiclePropConfig propConfig;
        TokenFunction tokenFunction;
    };

    struct RecordId {
        int32_t prop;
        int32_t area;
        int64_t token;
        bool operator==(const RecordId& other) const;
        bool operator<(const RecordId& other) const;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

RecordConfig可以理解为属性记录配置,RecordId可以理解为属性记录id.

定义了一个PropertyMap的map表来保存属性值。

  using PropertyMap = std::map<RecordId, VehiclePropValue>;
  PropertyMap mPropertyValues;  // Sorted map of RecordId : VehiclePropValue.
  • 1
  • 2

定义了一个无序map来保存属性配置

 std::unordered_map<int32_t /* VehicleProperty */, RecordConfig> mConfigs;
  • 1

既然知道了保存的值是什么,保存在哪里了,接下来看看store的增删查改。

注册属性:

void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
                                            VehiclePropertyStore::TokenFunction tokenFunc) {
    MuxGuard g(mLock);
    //很简单,mConfigs键值对插入key为config.prop, 值为RecordConfig, RecordConfig是个结构体,成员就是VehiclePropConfig跟一个函数指针。
    mConfigs.insert({ config.prop, RecordConfig { config, tokenFunc } });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

写入属性值:

bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
                                        bool updateStatus) {
    MuxGuard g(mLock);
    //首先从键值对的key集合里面查看是否当前需要写入属性值的属性id是否已经注册,如果当前属性id没有注册,则返回false,写入失败。
    if (!mConfigs.count(propValue.prop)) return false;
//查找RecordId
    RecordId recId = getRecordIdLocked(propValue);
    //根据RecordId从map中获取Value值
    VehiclePropValue* valueToUpdate = const_cast<VehiclePropValue*>(getValueOrNullLocked(recId));
    //如果当前没有保存该属性,则加入一条新的记录,否则的话,更新对应的值
    if (valueToUpdate == nullptr) {
        mPropertyValues.insert({ recId, propValue });
    } else {
        valueToUpdate->timestamp = propValue.timestamp;
        valueToUpdate->value = propValue.value;
        if (updateStatus) {
            valueToUpdate->status = propValue.status;
        }
    }
    return true;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

属性临时存储的增删改查基本都差不多,都是操作map, 这个不多说了,感兴趣的朋友可以自行查阅VehiclePropertyStore.cpp这个文件。

上面介绍了属性值临时保存方式,接下来看下EmulatedVehicleHal这个类,该类继承自EmulatedVehicleHal接口,而该接口又继承自VehicleHal,上文说过,VehicleHal接口是android定义在hal层用于实现VHal相关功能的接口。 因此EmulatedVehicleHal就里就实现了类似车身数据管理的功能。

首先看下其初始化的构造函数:

EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore)
    : mPropStore(propStore),
      mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
      //mRecurrentTimer是一个工具类,内部维护一个线程,用来处理指定时间触发的事件,这个跟上层的Handler比较类似。
      mRecurrentTimer(
          std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)),
//LinearFakeValueGenerator是一个模拟事件生成器,内部跟RecurrentTimer相互配合      mLinearFakeValueGenerator(std::make_unique<LinearFakeValueGenerator>(
          std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1))),
          //JsonFakeValueGenerator跟LinearFakeValueGenerator类似,不过它是根据json配置产生假事件
      mJsonFakeValueGenerator(std::make_unique<JsonFakeValueGenerator>(
          std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1))) {
          //注册DefaultConfig.h中定义的属性值
    initStaticConfig();
    for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
        mPropStore->registerProperty(kVehicleProperties[i].config);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

构造函数完事之后,简单看下VHal接口中的set/get/subscribe是怎么实现的

VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
        const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
        //当前我们要拿的属性值的属性ID是多少
    auto propId = requestedPropValue.prop;
    //这个pool是一个用于存储VehiclePropValue的对象池,这个跟Message的实现好像。
    auto& pool = *getValuePool();
    VehiclePropValuePtr v = nullptr;
    //这个就是根据propId来获取值了,OBD2_FREEZE_FRAME是OBD检测到故障信
    //息,OBD2_FREEZE_FRAME_INFO是故障检测到得时间戳。一般要获取OBD2_FREEZE_FRAME的数据之前,都要通过OBD2_FREEZE_FRAME_INFO获取时间戳。
    //除了这两个属性值,其他的都直接从临时的Store里面获取当前属性的状态值。
    switch (propId) {
        case OBD2_FREEZE_FRAME:
            v = pool.obtainComplex();
            *outStatus = fillObd2FreezeFrame(requestedPropValue, v.get());
            break;
        case OBD2_FREEZE_FRAME_INFO:
            v = pool.obtainComplex();
            *outStatus = fillObd2DtcInfo(v.get());
            break;
        default:
            auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
            if (internalPropValue != nullptr) {
                v = getValuePool()->obtain(*internalPropValue);
            }

            *outStatus = v != nullptr ? StatusCode::OK : StatusCode::INVALID_ARG;
            break;
    }

    return v;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

分析完了get函数,接下来就是set函数了,按照国际惯例,先贴代码,再分析,这个函数代码有点长,主要是各种判断,下面依次看下每个步骤都干啥了。

StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
    //这个常量定义为false,是因为这个set函数是给上层调用的,Android层
    //不能够改变属性值的状态,只有车身发送了该属性值过来了,才可改变
    //属性状态,这个在下面会有体现。
    static constexpr bool shouldUpdateStatus = false;
    //这段代码用于测试的,生产一个假的数据请求事件。
    if (propValue.prop == kGenerateFakeDataControllingProperty) {
        StatusCode status = handleGenerateFakeDataRequest(propValue);
        if (status != StatusCode::OK) {
            return status;
        }
    } else if (mHvacPowerProps.count(propValue.prop)) {
        //这里是判断当前属性值是否属于空调电源开关,如果是的情况下,去拿它值,如果当前开关没开,则返回当前状态不可用,设置失败的CODE
        auto hvacPowerOn = mPropStore->readValueOrNull(
            toInt(VehicleProperty::HVAC_POWER_ON),
            (VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
             VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
             VehicleAreaSeat::ROW_2_RIGHT));

        if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1
                && hvacPowerOn->value.int32Values[0] == 0) {
            return StatusCode::NOT_AVAILABLE;
        }
    } else {
        // Handle property specific code
        switch (propValue.prop) {
            case OBD2_FREEZE_FRAME_CLEAR:
                return clearObd2FreezeFrames(propValue);
            case VEHICLE_MAP_SERVICE:
                // Placeholder for future implementation of VMS property in the default hal. For
                // now, just returns OK; otherwise, hal clients crash with property not supported.
                return StatusCode::OK;
            case AP_POWER_STATE_REPORT:
                // This property has different behavior between get/set.  When it is set, the value
                //  goes to the vehicle but is NOT updated in the property store back to Android.
                // Commented out for now, because it may mess up automated testing that use the
                //  emulator interface.
                // getEmulatorOrDie()->doSetValueFromClient(propValue);
                return StatusCode::OK;
        }
    }
	//status默认值为AVAILABLE
    if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
        // Android side cannot set property status - this value is the
        // purview of the HAL implementation to reflect the state of
        // its underlying hardware
        return StatusCode::INVALID_ARG;
    }
    //读取该属性值id的当前存储的Prop
    auto currentPropValue = mPropStore->readValueOrNull(propValue);

    if (currentPropValue == nullptr) {
        return StatusCode::INVALID_ARG;
    }
    //如果目前属性值状态不可用,则上层不能设置,返回失败
    if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
        // do not allow Android side to set() a disabled/error property
        return StatusCode::NOT_AVAILABLE;
    }
	//更新属性值,开头说过,shouldUpdateStatus为false, 也就是android层更新属性值,不改变属性状态
    if (!mPropStore->writeValue(propValue, shouldUpdateStatus)) {
        return StatusCode::INVALID_ARG;
    }
    //通知汽车,设置属性值,这里是告诉模拟器,该值需要重新设置,调用的这个函数等下再说。
    getEmulatorOrDie()->doSetValueFromClient(propValue);
    return StatusCode::OK;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

通过set函数我们知道,属性值的设置,先将属性值写入到内存中保存,然后再通知车身更新该属性值,doSetValueFromClient这个函数就实现了相关的功能,这里暂且按下不表。这个set事件是来自上层service的调用,那车身信息如果发生变化时,如何set呢,答案是该模块还有一个名称setPropertyFromVehicle的函数,正是这个函数实现了车身数据变化之后,更新缓存的属性,并通知上层。

bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
    static constexpr bool shouldUpdateStatus = true;
    if (propValue.prop == kGenerateFakeDataControllingProperty) {
        StatusCode status = handleGenerateFakeDataRequest(propValue);
        if (status != StatusCode::OK) {
            return false;
        }
    }
    //更新属性值,注意这个shouldUpdateStaus为true,也就是要更新属性的status, 
    //刚刚上面那个set函数该值为false,这是为啥? 因为属性值只有由车身改变的时候才能改变其状态值,android层不行。
    if (mPropStore->writeValue(propValue, shouldUpdateStatus)) {
        //触发回调,通知上层
        doHalEvent(getValuePool()->obtain(propValue));
        return true;
    } else {
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

既然车身属性设置,跟上层设置都讲完了,下面看下该模块的属性值订阅实现,老规矩,贴代码:

StatusCode EmulatedVehicleHal::subscribe(int32_t property, float sampleRate) {
    ALOGI("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);

    if (isContinuousProperty(property)) {
        mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
    }
    return StatusCode::OK;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这个个代码比较简单,传输的参数有两个 property是属性ID,sampleRate是属性值更新的频率。 isContinuousProperty主要是判断该属性值的change类型是不是连续类型的,如果是连续类型的,就向RecurrentTimer中注册事件。RecurrentTimer是一个工具类,具体代码这里就不分析了,可以自行查阅,可以把它理解为一个另类的Handler, 其内部运行着一个线程维护着一个循环,当向其注册一个事件时,内部根据事件频率算出触发事件的事件,然后定期触发回调方法,跟Handler唯一不同的是,Handler的sendMesssageAtTime发完就没了,这个RecurrentTimer是如果你注册了事件,如果不取消注册,则事件会一直定期触发。

这里说了RecurrentTimer有个触发回调,那我们订阅了一个属性id,当达到时间后,触发的是哪个回调呢? 当然是EmulatedVehicleHal中的onContinuousPropertyTimer函数啦,这个函数指针在EmulatedVehicleHal初始化的时候,就作为参数传给RecurrentTimer,然后在这个函数中调用 doHalEvent(std::move(v)); 触发回调事件,将属性值上报。doHalEvent中其实没做啥,可以看下它的代码

    void doHalEvent(VehiclePropValuePtr v) {
        mOnHalEvent(std::move(v));
    }
  • 1
  • 2
  • 3

这里mOnHalEvent是一个函数指针,其对应函数定义在VehicleHalManager中,如下

void VehicleHalManager::onHalEvent(VehiclePropValuePtr v) {
    mEventQueue.push(std::move(v));
}
  • 1
  • 2
  • 3

最终由BatchingConsumer取出该事件,回调给上层;mOnHalEvent函数指针再VehicleHalManager初始化的时候,会将其作为参数传给EmulatedVehicleHal
在这里插入图片描述
写到这里, 基本上把hal层数据管理讲的差不多了;算一下,还剩下两个点,一个点是上层service如何跟hal层通信,一个是hal层如何跟车身通信。

Vhal如何跟VService进行数据交互?
上文介绍过,实现IVehicle接口的天线宝宝是谁? 就是VehicleHalManager这个宝宝,先看下IVehicle.hal通过hidl-gen生成的接口文件中定义了哪些方法, 大概摘录了其中主要的一些方法定义,如下:
在这里插入图片描述
getAllPropConfigs作用是获取所有的属性配置,对应实现在VehicleHalManager中的getlAllPropConfigs方法,代码如下:

Return<void> VehicleHalManager::getAllPropConfigs(getAllPropConfigs_cb _hidl_cb) {
    ALOGI("getAllPropConfigs called");
    //_hidl_cb是一个函数指针,定义在IVehicle.h里面
    hidl_vec<VehiclePropConfig> hidlConfigs;
    //从vector集合中读取所有当前的属性配置数据
    auto& halConfig = mConfigIndex->getAllConfigs();
    //写入到集合中
    hidlConfigs.setToExternal(
            const_cast<VehiclePropConfig *>(halConfig.data()),
            halConfig.size());
     //回调将数据发送到上层
    _hidl_cb(hidlConfigs);

    return Void();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

上面这个函数是上层获取当前hal层支持的所有属性配置,接下来看根据属性id获取属性配置,对应的函数为getPropConfigs,代码如下:

Return<void> VehicleHalManager::getPropConfigs(const hidl_vec<int32_t> &properties,
                                               getPropConfigs_cb _hidl_cb) {
    //这个函数也比较简单,基本上就是判断当前属性id是否已经默认配置了,如果存在,则保存配置到集合中,否则的话,就针对未配置的属性id上报状态错误的消息。
    std::vector<VehiclePropConfig> configs;
    for (size_t i = 0; i < properties.size(); i++) {
        auto prop = properties[i];
        if (mConfigIndex->hasConfig(prop)) {
            configs.push_back(mConfigIndex->getConfig(prop));
        } else {
            ALOGW("Requested config for undefined property: 0x%x", prop);
            _hidl_cb(StatusCode::INVALID_ARG, hidl_vec<VehiclePropConfig>());
        }
    }

    _hidl_cb(StatusCode::OK, configs);

    return Void();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

上面两个是关于属性配置获取的,属性配置跟属性值的区别是啥,一个是描述你这个配置的一些行为,如访问权限,更新最大的频率等;一个是用于具聚合一些具体值信息的载体,下面看一下属性值的设置跟获取,set/get函数,首先是set函数

//这个函数是上层调用set设置属性值的时候,参数value来自上层
Return<StatusCode> VehicleHalManager::set(const VehiclePropValue &value) {
    auto prop = value.prop;
    //获取属性配置
    const auto* config = getPropConfigOrNull(prop);
    //如果获取不到这个属性ID的配置,就证明当前hal层没配这个属性值,也就是不支持它
    if (config == nullptr) {
        ALOGE("Failed to set value: config not found, property: 0x%x", prop);
        return StatusCode::INVALID_ARG;
    }
//检查权限,上面说了,属性配置里面有关于访问权限的值定义,如果该属性值定义的配置不支持写权限,那就返回失败
    if (!checkWritePermission(*config)) {
        return StatusCode::ACCESS_DENIED;
    }
   //告诉上层订阅了该属性id的监听器,该值有更新。
    handlePropertySetEvent(value);
	//这里就到了上面介绍EmulatorVehicleHal中的set函数
    auto status = mHal->set(value);

    return Return<StatusCode>(status);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

接下来是get函数,获取属性值

Return<void> VehicleHalManager::get(const VehiclePropValue& requestedPropValue, get_cb _hidl_cb) {
//逻辑跟set差不读,先获取属性配置,然后再检查权限,最后从缓存中取值。
    const auto* config = getPropConfigOrNull(requestedPropValue.prop);
    if (config == nullptr) {
        ALOGE("Failed to get value: config not found, property: 0x%x",
              requestedPropValue.prop);
        _hidl_cb(StatusCode::INVALID_ARG, kEmptyValue);
        return Void();
    }

    if (!checkReadPermission(*config)) {
        _hidl_cb(StatusCode::ACCESS_DENIED, kEmptyValue);
        return Void();
    }

    StatusCode status;
    auto value = mHal->get(requestedPropValue, &status);
    _hidl_cb(status, value.get() ? *value : kEmptyValue);
    return Void();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

get函数跟set函数最终都是EmulatorVehicleHal.cpp中去执行具体的数据更新。基本上讲到这里,咱们VHal层的功能,除了具体跟车身通讯相关的部分没讲,其他的都说完了,对照下图,简单的总结一下。
在这里插入图片描述
VHal中的VehicleHalManager跟上层进行数据通讯,包括数据回调,跟上层调用,都均由此实现。
EmulatorVehicleHal模块承接了VehicleHalManager中的一部分属性值设置获取的功能,当上层设置值的时候,EmulatorVehicleHal会去更新内存中缓存的值,然后通知车身;当车身有数据更新时,也会该模块更新缓存值,并触发回调,通知上层。

VehicleEmulator这里不准备总结了,这个通讯实现比较简单,主要是运用Pipe管道或者socket通讯的方式,跟模拟器之间收发通过protobuf封装的数据,模块内部实现了protobuf数据的解析与封装,用来触发设置,获取属性值的事件等。

当然vhal不一定会跟can总线直接通讯,这里需要根据项目的硬件架构方案而定,各家实现不一。

至此,VHal模块所实现的功能,均总结完毕,下一篇总结一个上层Car Service的实现。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/636437
推荐阅读
相关标签
  

闽ICP备14008679号