Android车载——VehicleHal运行流程(Android 11)

1 概述

本篇主要讲解VehicleHal的主要运行流程,包括设置属性、获取属性、订阅属性、取消订阅、持续上报属性订阅等。

2 获取属性流程

2.1 获取属性流程源码分析

作为服务注册到hwServiceManager中的类是VehicleHalManager,所以,CarService对服务端的调用的hidl接口都是调用到了VehicleHalManager中。

cpp 复制代码
get(VehiclePropValue requestedPropValue)
          generates (StatusCode status, VehiclePropValue propValue);

IVehicle这个hidl接口中的定义如上

cpp 复制代码
Return<void> VehicleHalManager::get(const VehiclePropValue& requestedPropValue, get_cb _hidl_cb) {
    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();
}

调用之后会调用到VehicleHalManager中的get函数

cpp 复制代码
const VehiclePropConfig* VehicleHalManager::getPropConfigOrNull(
        int32_t prop) const {
    return mConfigIndex->hasConfig(prop)
           ? &mConfigIndex->getConfig(prop) : nullptr;
}

首先,会从属性配置列表中是否存在这个属性,这个是初始化的时候缓存的,缓存的是所有的属性配置。

如果获取的属性不在属性配置列表中,则不能够获取,如果存在,会判断访问权限,访问权限校验通过之后,会调用mHal的get函数。

cpp 复制代码
VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
        const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
    auto propId = requestedPropValue.prop;
    ALOGV("get(%d)", propId);

    auto& pool = *getValuePool();
    VehiclePropValuePtr v = nullptr;

    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:
            if (mEmulatedUserHal != nullptr && mEmulatedUserHal->isSupported(propId)) {
                ALOGI("get(): getting value for prop %d from User HAL", propId);
                const auto& ret = mEmulatedUserHal->onGetProperty(requestedPropValue);
                if (!ret.ok()) {
                    ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
                    *outStatus = StatusCode(ret.error().code());
                } else {
                    auto value = ret.value().get();
                    if (value != nullptr) {
                        ALOGI("get(): User HAL returned value: %s", toString(*value).c_str());
                        v = getValuePool()->obtain(*value);
                        *outStatus = StatusCode::OK;
                    } else {
                        ALOGE("get(): User HAL returned null value");
                        *outStatus = StatusCode::INTERNAL_ERROR;
                    }
                }
                break;
            }

            auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
            if (internalPropValue != nullptr) {
                v = getValuePool()->obtain(*internalPropValue);
            }

            *outStatus = v != nullptr ? StatusCode::OK : StatusCode::INVALID_ARG;
            break;
    }
    if (v.get()) {
        v->timestamp = elapsedRealtimeNano();
    }
    return v;
}

首先,会对userhal的一些判断操作,这个EmulatedUserHal是跟用户身份相关的hal定义,用于处理用户切换相关事件。如果是用户hal相关的prop获取,则获取完成之后就直接跳出。如果不是,则走普通的property获取路径,从VehiclePropertyStore中读取。

cpp 复制代码
using PropertyMap = std::map<RecordId, VehiclePropValue>;
PropertyMap mPropertyValues;

std::unique_ptr<VehiclePropValue> VehiclePropertyStore::readValueOrNull(
        int32_t prop, int32_t area, int64_t token) const {
    RecordId recId = {prop, isGlobalProp(prop) ? 0 : area, token };
    MuxGuard g(mLock);
    const VehiclePropValue* internalValue = getValueOrNullLocked(recId);
    return internalValue ? std::make_unique<VehiclePropValue>(*internalValue) : nullptr;
}

const VehiclePropValue* VehiclePropertyStore::getValueOrNullLocked(
        const VehiclePropertyStore::RecordId& recId) const  {
    auto it = mPropertyValues.find(recId);
    return it == mPropertyValues.end() ? nullptr : &it->second;
}

从mPropertyValues这个map中去获取对应propId的property。mPropertyValues里面的值是在哪填充的呢?

cpp 复制代码
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
                                        bool updateStatus) {
    MuxGuard g(mLock);
    if (!mConfigs.count(propValue.prop)) return false;

    RecordId recId = getRecordIdLocked(propValue);
    VehiclePropValue* valueToUpdate = const_cast<VehiclePropValue*>(getValueOrNullLocked(recId));
    if (valueToUpdate == nullptr) {
        mPropertyValues.insert({ recId, propValue });
        return true;
    }

    // propValue is outdated and drops it.
    if (valueToUpdate->timestamp > propValue.timestamp) {
        return false;
    }
    // update the propertyValue.
    // The timestamp in propertyStore should only be updated by the server side. It indicates
    // the time when the event is generated by the server.
    valueToUpdate->timestamp = propValue.timestamp;
    valueToUpdate->value = propValue.value;
    if (updateStatus) {
        valueToUpdate->status = propValue.status;
    }
    return true;
}

是在这个函数中,这个函数是在VHAL初始化的时候调用的,初始化的时候,会遍历一个定义了所有支持属性的列表,并调用writeValue函数将属性配置和属性值缓存到VehiclePropertyStore中。

以上就是CarService从VHAL获取属性的流程,总结来说就是:从VHAL的缓存map中获取属性。

2.2 获取属性流程图

plantuml

cpp 复制代码
@startuml

participant CarService
box
participant VehicleHalManager
participant EmulatedVehicleHal
participant VehiclePropertyStore
endbox

CarService -> VehicleHalManager: get(const VehiclePropValue& \n\trequestedPropValue, get_cb _hidl_cb)
VehicleHalManager -> EmulatedVehicleHal: get(const VehiclePropValue& \n\trequestedPropValue, get_cb _hidl_cb)
EmulatedVehicleHal -> VehiclePropertyStore: readValueOrNull(int32_t prop, \n\tint32_t area, int64_t token)
VehiclePropertyStore -> EmulatedVehicleHal: propValue
EmulatedVehicleHal -> VehicleHalManager: propValue
VehicleHalManager -> CarService: _hidl_cb(propValue)

@enduml

3 设置属性流程

3.1 设置属性流程源码分析

hidl调用后还是从VehicleHalManager开始的

cpp 复制代码
Return<StatusCode> VehicleHalManager::set(const VehiclePropValue &value) {
    auto prop = value.prop;
    const auto* config = getPropConfigOrNull(prop);
    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;
    }

    handlePropertySetEvent(value);

    auto status = mHal->set(value);

    return Return<StatusCode>(status);
}

首先判断缓存中是否有该属性的属性配置,有才支持后续的set操作

handlePropertySetEvent是对带有EVENTS_FROM_ANDROID这个订阅标签属性的处理,这种属性的设置需要直接上报给上层。

然后是调用EmulatedVehicleHal的set函数

cpp 复制代码
StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
    constexpr bool updateStatus = false;

	//这里是模拟车辆属性
    if (propValue.prop == kGenerateFakeDataControllingProperty) {
        // Send the generator controlling request to the server.
        // 'updateStatus' flag is only for the value sent by setProperty (propValue in this case)
        // instead of the generated values triggered by it. 'propValue' works as a control signal
        // here, since we never send the control signal back, the value of 'updateStatus' flag
        // does not matter here.
        auto status = mVehicleClient->setProperty(propValue, updateStatus);
        return status;
    //处理空调相关的属性
    } else if (mHvacPowerProps.count(propValue.prop)) {
        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;
        }
    }

    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;
    }
    //读取当前值
    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;
    }

    /**
     * After checking all conditions, such as the property is available, a real vhal will
     * sent the events to Car ECU to take actions.
     */

    // Send the value to the vehicle server, the server will talk to the (real or emulated) car
    //设置属性到VehicleClient,通过这个设置到模拟车辆或者实际车辆
    auto setValueStatus = mVehicleClient->setProperty(propValue, updateStatus);
    if (setValueStatus != StatusCode::OK) {
        return setValueStatus;
    }

    return StatusCode::OK;
}

其中mVehicleClient是初始化时传入的EmulatedVehicleConnector对象,所以调用的是EmulatedVehicleConnector的setProperty函数。

cpp 复制代码
StatusCode setProperty(const VehiclePropValue& value, bool updateStatus) override {
    return this->onSetProperty(value, updateStatus);
}

setProperty函数在EmulatedVehicleConnector的父类IPassThroughConnector中,然后调用onSetProperty函数,这个函数在EmulatedVehicleConnector类中。

cpp 复制代码
StatusCode EmulatedVehicleConnector::onSetProperty(const VehiclePropValue& value,
                                                   bool updateStatus) {
    if (mEmulatedUserHal.isSupported(value.prop)) {
        LOG(INFO) << "onSetProperty(): property " << value.prop << " will be handled by UserHal";

        const auto& ret = mEmulatedUserHal.onSetProperty(value);
        if (!ret.ok()) {
            LOG(ERROR) << "onSetProperty(): HAL returned error: " << ret.error().message();
            return StatusCode(ret.error().code());
        }
        auto updatedValue = ret.value().get();
        if (updatedValue != nullptr) {
            LOG(INFO) << "onSetProperty(): updating property returned by HAL: "
                      << toString(*updatedValue);
            onPropertyValueFromCar(*updatedValue, updateStatus);
        }
        return StatusCode::OK;
    }
    return this->VehicleHalServer::onSetProperty(value, updateStatus);
}

首先处理UserHal相关的属性操作。然后调用VehicleHalServer中的onSetProperty函数。

cpp 复制代码
StatusCode VehicleHalServer::onSetProperty(const VehiclePropValue& value, bool updateStatus) {
    LOG(DEBUG) << "onSetProperty(" << value.prop << ")";

    // Some properties need to be treated non-trivially
    switch (value.prop) {
        case kGenerateFakeDataControllingProperty:
            return handleGenerateFakeDataRequest(value);

        // set the value from vehicle side, used in end to end test.
        case kSetIntPropertyFromVehicleForTest: {
            auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::INT32, 1);
            updatedPropValue->prop = value.value.int32Values[0];
            updatedPropValue->value.int32Values[0] = value.value.int32Values[1];
            updatedPropValue->timestamp = value.value.int64Values[0];
            updatedPropValue->areaId = value.areaId;
            onPropertyValueFromCar(*updatedPropValue, updateStatus);
            return StatusCode::OK;
        }
        case kSetFloatPropertyFromVehicleForTest: {
            auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::FLOAT, 1);
            updatedPropValue->prop = value.value.int32Values[0];
            updatedPropValue->value.floatValues[0] = value.value.floatValues[0];
            updatedPropValue->timestamp = value.value.int64Values[0];
            updatedPropValue->areaId = value.areaId;
            onPropertyValueFromCar(*updatedPropValue, updateStatus);
            return StatusCode::OK;
        }
        case kSetBooleanPropertyFromVehicleForTest: {
            auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::BOOLEAN, 1);
            updatedPropValue->prop = value.value.int32Values[1];
            updatedPropValue->value.int32Values[0] = value.value.int32Values[0];
            updatedPropValue->timestamp = value.value.int64Values[0];
            updatedPropValue->areaId = value.areaId;
            onPropertyValueFromCar(*updatedPropValue, updateStatus);
            return StatusCode::OK;
        }

        case AP_POWER_STATE_REPORT:
            switch (value.value.int32Values[0]) {
                case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED):
                case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL):
                    // CPMS is in WAIT_FOR_VHAL state, simply move to ON
                    // Send back to HAL
                    // ALWAYS update status for generated property value
                    onPropertyValueFromCar(*createApPowerStateReq(VehicleApPowerStateReq::ON, 0),
                                           true /* updateStatus */);
                    break;
                case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_START):
                    // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command
                    // Send back to HAL
                    // ALWAYS update status for generated property value
                    onPropertyValueFromCar(
                            *createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0),
                            true /* updateStatus */);
                    break;
                case toInt(VehicleApPowerStateReport::ON):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
                case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
                    // Do nothing
                    break;
                default:
                    // Unknown state
                    break;
            }
            break;
        default:
            break;
    }

    // In the real vhal, the value will be sent to Car ECU.
    // We just pretend it is done here and send back to HAL
    auto updatedPropValue = getValuePool()->obtain(value);
    updatedPropValue->timestamp = elapsedRealtimeNano();

    onPropertyValueFromCar(*updatedPropValue, updateStatus);
    return StatusCode::OK;
}

上面是模拟属性设置流程,这里就相当于模拟属性设置完成了。更新属性的值和时间戳之后,调用onPropertyValueFromCar模拟属性设置成功的上报操作

3.2 设置属性流程图

plantuml

cpp 复制代码
@startuml

participant CarService
box
participant VehicleHalManager
participant SubscriptionManager
participant EmulatedVehicleHal
participant IPassThroughConnector
participant EmulatedVehicleConnector
participant VehicleHalServer
endbox

CarService -> VehicleHalManager: set(const VehiclePropValue& value)
VehicleHalManager -> VehicleHalManager: handlePropertySetEvent(const VehiclePropValue& value)
VehicleHalManager -> SubscriptionManager: getSubscribedClients(int32_t propId, SubscribeFlags flags)
SubscriptionManager -> VehicleHalManager: value
alt flags == SubscribeFlags::EVENTS_FROM_ANDROID
    VehicleHalManager -> CarService: onPropertySet(value)
end
VehicleHalManager -> EmulatedVehicleHal: set(const VehiclePropValue& value)
EmulatedVehicleHal -> IPassThroughConnector: setProperty(const VehiclePropValue& \n\tvalue, bool updateStatus)
IPassThroughConnector -> EmulatedVehicleConnector: onSetProperty(const VehiclePropValue& \n\tvalue, bool updateStatus)
EmulatedVehicleConnector -> VehicleHalServer: onSetProperty(const VehiclePropValue& value, bool updateStatus)

@enduml

流程图:

这里由于没有实际车辆,没有往下设置,OEM厂商需要设置到ECU中。至于设置之后,VHAL缓存的改变则是由设置成功的通知上报之后才会写入缓存的。

4 订阅属性流程

4.1 普通订阅属性流程源码分析

订阅入口也是在VehicleHalManager中

cpp 复制代码
Return<StatusCode> VehicleHalManager::subscribe(const sp<IVehicleCallback> &callback,
                                                const hidl_vec<SubscribeOptions> &options) {
    hidl_vec<SubscribeOptions> verifiedOptions(options);
    for (size_t i = 0; i < verifiedOptions.size(); i++) {
        SubscribeOptions& ops = verifiedOptions[i];
        auto prop = ops.propId;

        const auto* config = getPropConfigOrNull(prop);
        if (config == nullptr) {
            ALOGE("Failed to subscribe: config not found, property: 0x%x",
                  prop);
            return StatusCode::INVALID_ARG;
        }

        if (ops.flags == SubscribeFlags::UNDEFINED) {
            ALOGE("Failed to subscribe: undefined flag in options provided");
            return StatusCode::INVALID_ARG;
        }

        if (!isSubscribable(*config, ops.flags)) {
            ALOGE("Failed to subscribe: property 0x%x is not subscribable",
                  prop);
            return StatusCode::INVALID_ARG;
        }

        ops.sampleRate = checkSampleRate(*config, ops.sampleRate);
    }

    std::list<SubscribeOptions> updatedOptions;
    auto res = mSubscriptionManager.addOrUpdateSubscription(getClientId(callback),
                                                            callback, verifiedOptions,
                                                            &updatedOptions);
    if (StatusCode::OK != res) {
        ALOGW("%s failed to subscribe, error code: %d", __func__, res);
        return res;
    }

    for (auto opt : updatedOptions) {
        mHal->subscribe(opt.propId, opt.sampleRate);
    }

    return StatusCode::OK;
}

首先判断属性配置是否存在,存在才支持订阅

然后判断是否可以订阅

cpp 复制代码
bool VehicleHalManager::isSubscribable(const VehiclePropConfig& config,
                                       SubscribeFlags flags) {
    bool isReadable = config.access & VehiclePropertyAccess::READ;

    if (!isReadable && (SubscribeFlags::EVENTS_FROM_CAR & flags)) {
        ALOGW("Cannot subscribe, property 0x%x is not readable", config.prop);
        return false;
    }
    if (config.changeMode == VehiclePropertyChangeMode::STATIC) {
        ALOGW("Cannot subscribe, property 0x%x is static", config.prop);
        return false;
    }
    return true;
}

判断访问权限,判断flag,如果flag是从car来的,则返回false。然后是判断属性的changeMode,如果changeMode是STATIC,表示属性不变,则不支持订阅。

然后添加订阅addOrUpdateSubscription,添加之前首先创建clientId

cpp 复制代码
using ClientId = uint64_t;
ClientId VehicleHalManager::getClientId(const sp<IVehicleCallback>& callback) {
    //TODO(b/32172906): rework this to get some kind of unique id for callback interface when this
    // feature is ready in HIDL.

    if (callback->isRemote()) {
        BpHwVehicleCallback* hwCallback = static_cast<BpHwVehicleCallback*>(callback.get());
        return static_cast<ClientId>(reinterpret_cast<intptr_t>(hwCallback->onAsBinder()));
    } else {
        return static_cast<ClientId>(reinterpret_cast<intptr_t>(callback.get()));
    }
}

根据传入的回调函数的额指针来强转成ClientId这种int类型,作为客户端的唯一标识。

cpp 复制代码
StatusCode SubscriptionManager::addOrUpdateSubscription(
        ClientId clientId,
        const sp<IVehicleCallback> &callback,
        const hidl_vec<SubscribeOptions> &optionList,
        std::list<SubscribeOptions>* outUpdatedSubscriptions) {
    outUpdatedSubscriptions->clear();

    MuxGuard g(mLock);

    ALOGI("SubscriptionManager::addOrUpdateSubscription, callback: %p", callback.get());

    const sp<HalClient>& client = getOrCreateHalClientLocked(clientId, callback);
    if (client.get() == nullptr) {
        return StatusCode::INTERNAL_ERROR;
    }

    for (size_t i = 0; i < optionList.size(); i++) {
        const SubscribeOptions& opts = optionList[i];
        ALOGI("SubscriptionManager::addOrUpdateSubscription, prop: 0x%x", opts.propId);
        client->addOrUpdateSubscription(opts);

        addClientToPropMapLocked(opts.propId, client);

        if (SubscribeFlags::EVENTS_FROM_CAR & opts.flags) {
            SubscribeOptions updated;
            if (updateHalEventSubscriptionLocked(opts, &updated)) {
                outUpdatedSubscriptions->push_back(updated);
            }
        }
    }

    return StatusCode::OK;
}

首先,创建客户端对象HalClient

cpp 复制代码
std::map<ClientId, sp<HalClient>> mClients;

sp<HalClient> SubscriptionManager::getOrCreateHalClientLocked(
        ClientId clientId, const sp<IVehicleCallback>& callback) {
    auto it = mClients.find(clientId);

    if (it == mClients.end()) {
        uint64_t cookie = reinterpret_cast<uint64_t>(clientId);
        ALOGI("Creating new client and linking to death recipient, cookie: 0x%" PRIx64, cookie);
        auto res = callback->linkToDeath(mCallbackDeathRecipient, cookie);
        if (!res.isOk()) {  // Client is already dead?
            ALOGW("%s failed to link to death, client %p, err: %s",
                  __func__, callback.get(), res.description().c_str());
            return nullptr;
        }

        sp<HalClient> client = new HalClient(callback);
        mClients.insert({clientId, client});
        return client;
    } else {
        return it->second;
    }
}

mClients是保存订阅客户端的map,key是ClientId,value是HalClient对象。这里会先判断mClients这个map中是否存在对应的客户端,如果没有,则创建HalClient对象,并加入到这个map之中,如果有则直接返回。

然后for循环遍历所有的订阅项,由于订阅的时候,CarService传入的是SubscribeOptions的列表:

cpp 复制代码
struct SubscribeOptions {
    /** Property to subscribe */
    int32_t propId;

    /**
     * Sample rate in Hz.
     *
     * Must be provided for properties with
     * VehiclePropertyChangeMode::CONTINUOUS. The value must be within
     * VehiclePropConfig#minSamplingRate .. VehiclePropConfig#maxSamplingRate
     * for a given property.
     * This value indicates how many updates per second client wants to receive.
     */
    float sampleRate;

    /** Flags that indicate to which event sources to listen. */
    SubscribeFlags flags;
};

所以可以一次性订阅多个属性。但是这多个属性是一次订阅,也是走一个回调函数上去的。这里会遍历所有的SubscribeOptions,然后调用addOrUpdateSubscription函数。

cpp 复制代码
std::map<int32_t, SubscribeOptions> mSubscriptions;

void HalClient::addOrUpdateSubscription(const SubscribeOptions &opts)  {
    ALOGI("%s opts.propId: 0x%x", __func__, opts.propId);

    auto it = mSubscriptions.find(opts.propId);
    if (it == mSubscriptions.end()) {
        mSubscriptions.emplace(opts.propId, opts);
    } else {
        const SubscribeOptions& oldOpts = it->second;
        SubscribeOptions updatedOptions;
        if (mergeSubscribeOptions(oldOpts, opts, &updatedOptions)) {
            mSubscriptions.erase(it);
            mSubscriptions.emplace(opts.propId, updatedOptions);
        }
    }
}
bool mergeSubscribeOptions(const SubscribeOptions &oldOpts,
                           const SubscribeOptions &newOpts,
                           SubscribeOptions *outResult) {
    float updatedRate = std::max(oldOpts.sampleRate, newOpts.sampleRate);
    SubscribeFlags updatedFlags = SubscribeFlags(oldOpts.flags | newOpts.flags);

    bool updated = (updatedRate > oldOpts.sampleRate) || (updatedFlags != oldOpts.flags);
    if (updated) {
        *outResult = oldOpts;
        outResult->sampleRate = updatedRate;
        outResult->flags = updatedFlags;
    }

    return updated;
}

首先判断这个SubscribeOptions中包含的propId是否已经存在于mSubscriptions这个map中,如果存在,则更新flag和采样率和相应的SubscribeOptions对象,如果不存在,则添加。

这个map的key是propId,value是SubscribeOptions。

cpp 复制代码
std::map<int32_t, sp<HalClientVector>> mPropToClients;

void SubscriptionManager::addClientToPropMapLocked(
        int32_t propId, const sp<HalClient> &client) {
    auto it = mPropToClients.find(propId);
    sp<HalClientVector> propClients;
    if (it == mPropToClients.end()) {
        propClients = new HalClientVector();
        mPropToClients.insert(std::make_pair(propId, propClients));
    } else {
        propClients = it->second;
    }
    propClients->addOrUpdate(client);
}

mPropToClients是SubscriptionManager类中的一个map,key是propId,value是HalClientVector对象。HalClientVectot中存储的是对于同一个propId订阅的不同HalClient对象。

这里,先判断mPropToClients中是否有对应的propId的HalClientVector,如果没有,则表示这个propId没有客户端订阅过。如果有则将当前的client对象添加到HalClientVector中。

总结一下属性订阅做的事就是:

  1. 根据传入的callback指针,转换成ClientId,作为客户端标识
  2. 创建HalClient对象,作为客户端实例,并保存客户端的callback回调
  3. 缓存所有订阅属性到HalClient对象的mSubscriptions中
  4. 缓存HalClient到mClients这个map中
  5. 缓存HalClient到mPropToClients这个map中

4.2 连续类型属性订阅流程源码分析

连续类型属性,订阅之后需要VHAL周期性上报给CarService

连续属性订阅,其他和上面的普通订阅相同,最后会走到EmulatedVehicleHal中进行处理

cpp 复制代码
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;
}

bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const {
    const VehiclePropConfig* config = mPropStore->getConfigOrNull(propId);
    if (config == nullptr) {
        ALOGW("Config not found for property: 0x%x", propId);
        return false;
    }
    return config->changeMode == VehiclePropertyChangeMode::CONTINUOUS;
}

主要就是通过属性配置的changeMode是否是CONTINUOUS来判断,判断如果还连续属性,调用registerRecurrentEvent

cpp 复制代码
std::unordered_map<int32_t, RecurrentEvent> mCookieToEventsMap;

void registerRecurrentEvent(std::chrono::nanoseconds interval, int32_t cookie) {
    TimePoint now = Clock::now();
    // Align event time point among all intervals. Thus if we have two intervals 1ms and 2ms,
    // during every second wake-up both intervals will be triggered.
    TimePoint absoluteTime = now - Nanos(now.time_since_epoch().count() % interval.count());

    {
        std::lock_guard<std::mutex> g(mLock);
        mCookieToEventsMap[cookie] = { interval, cookie, absoluteTime };
    }
    mCond.notify_one();
}

用propId作为key,将定时事件的定时周期,propId,当前转换后时间封装成RecurrentEvent对象,存储到mCookieToEventsMap这个map中,然后通过mCond唤醒线程,至此连续订阅完成,等待特定时间上报。

4.3 订阅流程图

plantuml

cpp 复制代码
@startuml

participant CarService
box
participant VehicleHalManager
participant SubscriptionManager
participant HalClient
participant HalClientVector
participant EmulatedVehicleHal
participant RecurrentTimer
endbox

CarService -> VehicleHalManager: subscribe(const sp<IVehicleCallback> &callback, \n\tconst hidl_vec<SubscribeOptions> &options)
VehicleHalManager -> SubscriptionManager: addOrUpdateSubscription(\n\tClientId clientId, \n\tconst sp<IVehicleCallback> &callback, \n\tconst hidl_vec<SubscribeOptions> &optionList, \n\tstd::list<SubscribeOptions>* outUpdatedSubscriptions)
SubscriptionManager -> SubscriptionManager: getClientId(const sp<IVehicleCallback>& callback)
SubscriptionManager -> SubscriptionManager: getOrCreateHalClientLocked(ClientId clientId, \n\tconst sp<IVehicleCallback>& callback)
alt not in mClients
    SubscriptionManager -> HalClient: new HalClient(const sp<IVehicleCallback> &callback)
    HalClient -> SubscriptionManager: client
    SubscriptionManager -> SubscriptionManager: mClients.insert({clientId, client})
end
loop i in optionList.size
    SubscriptionManager -> HalClient: addOrUpdateSubscription(const SubscribeOptions &opts)
    alt not in mSubscriptions
        HalClient -> HalClient: emplace(opts.propId, opts)
    else in mSubscriptions
        HalClient -> HalClient: mergeSubscribeOptions(oldOpts, opts, &updatedOptions)
        HalClient -> HalClient: erase(it)
        HalClient -> HalClient: emplace(opts.propId, opts)
    end
    SubscriptionManager -> SubscriptionManager: addClientToPropMapLocked(opts.propId, client)
    alt not in mPropToClients
        SubscriptionManager -> HalClientVector: new HalClientVector()
        HalClientVector -> SubscriptionManager: propClients
        SubscriptionManager -> SubscriptionManager: mPropToClients.insert(std::make_pair(propId, propClients));
    end
    SubscriptionManager -> SubscriptionManager: propClients->addOrUpdate(client)
end
loop opt in updatedOptions
    SubscriptionManager -> EmulatedVehicleHal: subscribe(opt.propId, opt.sampleRate)
    alt isContinuousProperty
        EmulatedVehicleHal -> RecurrentTimer: registerRecurrentEvent(hertzToNanoseconds(sampleRate), property)
        RecurrentTimer -> RecurrentTimer: mCookieToEventsMap[cookie] = \n\t{ interval, cookie, absoluteTime }
    end
end

@enduml

流程图如下:

5 取消订阅流程

5.1 取消订阅流程源码分析

取消订阅入口也是在VehicleHalManager中

cpp 复制代码
Return<StatusCode> VehicleHalManager::unsubscribe(const sp<IVehicleCallback>& callback,
                                                  int32_t propId) {
    mSubscriptionManager.unsubscribe(getClientId(callback), propId);
    return StatusCode::OK;
}

根据callback获取ClientId,并转到SubscriptionManager中处理

cpp 复制代码
void SubscriptionManager::unsubscribe(ClientId clientId,
                                      int32_t propId) {
    MuxGuard g(mLock);
    auto propertyClients = getClientsForPropertyLocked(propId);
    auto clientIter = mClients.find(clientId);
    if (clientIter == mClients.end()) {
        ALOGW("Unable to unsubscribe: no callback found, propId: 0x%x", propId);
    } else {
        auto client = clientIter->second;

        if (propertyClients != nullptr) {
            propertyClients->remove(client);

            if (propertyClients->isEmpty()) {
                mPropToClients.erase(propId);
            }
        }

        bool isClientSubscribedToOtherProps = false;
        for (const auto& propClient : mPropToClients) {
            if (propClient.second->indexOf(client) >= 0) {
                isClientSubscribedToOtherProps = true;
                break;
            }
        }

        if (!isClientSubscribedToOtherProps) {
            auto res = client->getCallback()->unlinkToDeath(mCallbackDeathRecipient);
            if (!res.isOk()) {
                ALOGW("%s failed to unlink to death, client: %p, err: %s",
                      __func__, client->getCallback().get(), res.description().c_str());
            }
            mClients.erase(clientIter);
        }
    }

    if (propertyClients == nullptr || propertyClients->isEmpty()) {
        mHalEventSubscribeOptions.erase(propId);
        mOnPropertyUnsubscribed(propId);
    }
}
sp<HalClientVector> SubscriptionManager::getClientsForPropertyLocked(
        int32_t propId) const {
    auto it = mPropToClients.find(propId);
    return it == mPropToClients.end() ? nullptr : it->second;
}

首先,调用getClientsForPropertyLocked函数,根据propId获取HalClientVector对象。然后从mClients中根据ClientId找到对应的HalClient对象,如果该HalClient存在,则从HalClientVector中移除,如果HalClientVector中是最后一个HalClient,则从mPropToClients中移除这个propId的HalClientVector对象。

如果这个client还订阅了其他propId,则不把这个client从mClients中移除,如果没有订阅其他的propId,则从mClients中移除。

同时,如果这个HalClientVector为空了,则移除mHalEventSubscribeOptions中的对应propId的订阅属性,并调用mOnPropertyUnsubscribed这个回调函数。

cpp 复制代码
void VehicleHalManager::onAllClientsUnsubscribed(int32_t propertyId) {
    mHal->unsubscribe(propertyId);
}

StatusCode EmulatedVehicleHal::unsubscribe(int32_t property) {
    ALOGI("%s propId: 0x%x", __func__, property);
    if (isContinuousProperty(property)) {
        mRecurrentTimer.unregisterRecurrentEvent(property);
    }
    return StatusCode::OK;
}

void unregisterRecurrentEvent(int32_t cookie) {
        {
            std::lock_guard<std::mutex> g(mLock);
            mCookieToEventsMap.erase(cookie);
        }
        mCond.notify_one();
    }

这个地方就是对连续属性的取消订阅处理,从mCookieToEventsMap中移除。

5.2 取消订阅流程图

plantuml

cpp 复制代码
@startuml

participant CarService
box
participant VehicleHalManager
participant SubscriptionManager
participant EmulatedVehicleHal
participant RecurrentTimer
endbox

CarService -> VehicleHalManager: unsubscribe(const sp<IVehicleCallback>& callback,\n\t int32_t propId)
VehicleHalManager -> SubscriptionManager: unsubscribe(getClientId(callback), propId)
SubscriptionManager -> SubscriptionManager: propertyClients = getClientsForPropertyLocked(propId)
alt client in mClients
    SubscriptionManager -> SubscriptionManager: propertyClients->remove(client)
    alt propertyClients->isEmpty()
        SubscriptionManager -> SubscriptionManager: mPropToClients.erase(propId)
    end
    loop propClient in mPropToClients
        alt client in propClient
            SubscriptionManager -> SubscriptionManager: isClientSubscribedToOtherProps = true
        end
    end
    alt isClientSubscribedToOtherProps == false
        SubscriptionManager -> SubscriptionManager: client->getCallback()->\n\tunlinkToDeath(mCallbackDeathRecipient)
    end
end
alt propertyClients == nullptr || propertyClients->isEmpty()
    SubscriptionManager -> VehicleHalManager: onAllClientsUnsubscribed(propertyId)
    VehicleHalManager -> EmulatedVehicleHal: unsubscribe(property)
    alt isContinuousProperty
        EmulatedVehicleHal -> RecurrentTimer: unregisterRecurrentEvent(property)
        RecurrentTimer -> RecurrentTimer: mCookieToEventsMap.erase(cookie)
    end
end

@enduml

流程图:

6 属性上报流程

6.1 属性上报流程源码分析

cpp 复制代码
void onPropertyValueFromCar(const VehiclePropValue& value, bool updateStatus) override {
    return this->onPropertyValue(value, updateStatus);
}

由于EmulatedVehicleConnector同时继承VehicleHalServer和VehicleHalClient,所以这个onPropertyValue继承于VehicleHalClient,调用的是VehicleHalClient中的onPropertyValue函数。

cpp 复制代码
void VehicleHalClient::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
    if (!mPropCallback) {
        LOG(ERROR) << __func__ << ": PropertyCallBackType is not registered!";
        return;
    }
    return mPropCallback(value, updateStatus);
}

调用mPropCallback这个注册的回调函数,这个回调函数是:

cpp 复制代码
void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
    VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);

    if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
        getEmulatorOrDie()->doSetValueFromClient(*updatedPropValue);
        doHalEvent(std::move(updatedPropValue));
    }
}

首先,将上报的属性写入缓存中。然后调用doSetValueFromClient

cpp 复制代码
void VehicleEmulator::doSetValueFromClient(const VehiclePropValue& propValue) {
    vhal_proto::EmulatorMessage msg;
    vhal_proto::VehiclePropValue* val = msg.add_value();
    populateProtoVehiclePropValue(val, &propValue);
    msg.set_status(vhal_proto::RESULT_OK);
    msg.set_msg_type(vhal_proto::SET_PROPERTY_ASYNC);

    mSocketComm->sendMessage(msg);
    if (mPipeComm) {
        mPipeComm->sendMessage(msg);
    }
}

这里会通过SocketComm和PipeComm通知其他客户端。

然后会调用doHalEvent上报给CarService。

cpp 复制代码
void doHalEvent(VehiclePropValuePtr v) {
    mOnHalEvent(std::move(v));
}

mOnHalEvent是VehicleHalManager::onHalEvent函数

cpp 复制代码
void VehicleHalManager::onHalEvent(VehiclePropValuePtr v) {
    mEventQueue.push(std::move(v));
}

将VehiclePropValuePtr传入mEventQueue,这个是初始化的时候与BatchingConsumer中的mQueue绑定的一个queue,用于处理上报事件。同时BatchingConsumer这个类初始化的时候也会创建一个线程来处理上报事件,运行的函数为:

cpp 复制代码
void runInternal(const OnBatchReceivedFunc& onBatchReceived) {
    if (mState.exchange(State::RUNNING) == State::INIT) {
        while (State::RUNNING == mState) {
            mQueue->waitForItems();
            if (State::STOP_REQUESTED == mState) break;

            std::this_thread::sleep_for(mBatchInterval);
            if (State::STOP_REQUESTED == mState) break;

            std::vector<T> items = mQueue->flush();

            if (items.size() > 0) {
                onBatchReceived(items);
            }
        }
    }

    mState = State::STOPPED;
}

void waitForItems() {
    std::unique_lock<std::mutex> g(mLock);
    while (mQueue.empty() && mIsActive) {
        mCond.wait(g);
    }
}

std::vector<T> flush() {
    std::vector<T> items;

    MuxGuard g(mLock);
    if (mQueue.empty() || !mIsActive) {
        return items;
    }
    while (!mQueue.empty()) {
        items.push_back(std::move(mQueue.front()));
        mQueue.pop();
    }
    return items;
}

void push(T&& item) {
    {
        MuxGuard g(mLock);
        if (!mIsActive) {
            return;
        }
        mQueue.push(std::move(item));
    }
    mCond.notify_one();
}

当没有上报事件时,会waitForItems阻塞线程。如果有事件,则会在循环中处理,等待一个时间间隔mBatchInterval=10,如果在这个事件内没有上报事件,会等10s,如果有,则会在push的时候立马唤醒线程执行。

然后将queue中的所有事件都取出,调用回调函数处理,回调函数是:

cpp 复制代码
void VehicleHalManager::onBatchHalEvent(const std::vector<VehiclePropValuePtr>& values) {
    const auto& clientValues =
        mSubscriptionManager.distributeValuesToClients(values, SubscribeFlags::EVENTS_FROM_CAR);

    for (const HalClientValues& cv : clientValues) {
        auto vecSize = cv.values.size();
        hidl_vec<VehiclePropValue> vec;
        if (vecSize < kMaxHidlVecOfVehiclPropValuePoolSize) {
            vec.setToExternal(&mHidlVecOfVehiclePropValuePool[0], vecSize);
        } else {
            vec.resize(vecSize);
        }

        int i = 0;
        for (VehiclePropValue* pValue : cv.values) {
            shallowCopy(&(vec)[i++], *pValue);
        }
        auto status = cv.client->getCallback()->onPropertyEvent(vec);
        if (!status.isOk()) {
            ALOGE("Failed to notify client %s, err: %s",
                  toString(cv.client->getCallback()).c_str(),
                  status.description().c_str());
        }
    }
}

先看下封装

cpp 复制代码
std::list<HalClientValues> SubscriptionManager::distributeValuesToClients(
        const std::vector<recyclable_ptr<VehiclePropValue>>& propValues,
        SubscribeFlags flags) const {
    //创建一个map,key是HalClient,value是VehiclePropValue
    std::map<sp<HalClient>, std::list<VehiclePropValue*>> clientValuesMap;

    {
        MuxGuard g(mLock);
        //遍历所有的propValue
        for (const auto& propValue: propValues) {
            VehiclePropValue* v = propValue.get();
            //获取所有订阅了该属性的客户端HalClient对象
            auto clients = getSubscribedClientsLocked(v->prop, flags);
            //遍历所有HalClient,并将其和propValue一起封装加入clientValuesMap中
            for (const auto& client : clients) {
                clientValuesMap[client].push_back(v);
            }
        }
    }

	//遍历map中所有的对象,并封装成HalClientValues对象,存入clientValues中
    std::list<HalClientValues> clientValues;
    for (const auto& entry : clientValuesMap) {
        clientValues.push_back(HalClientValues {
            .client = entry.first,
            .values = entry.second
        });
    }

    return clientValues;
}

std::list<sp<HalClient>> SubscriptionManager::getSubscribedClientsLocked(
    int32_t propId, SubscribeFlags flags) const {
    std::list<sp<HalClient>> subscribedClients;
	
	//通过propId获取该id对应的HalClientVector对象
    sp<HalClientVector> propClients = getClientsForPropertyLocked(propId);
    if (propClients.get() != nullptr) {
    	//遍历HalClientVector中HalClient,并返回
        for (size_t i = 0; i < propClients->size(); i++) {
            const auto& client = propClients->itemAt(i);
            if (client->isSubscribed(propId, flags)) {
                subscribedClients.push_back(client);
            }
        }
    }

    return subscribedClients;
}

sp<HalClientVector> SubscriptionManager::getClientsForPropertyLocked(
        int32_t propId) const {
    auto it = mPropToClients.find(propId);
    return it == mPropToClients.end() ? nullptr : it->second;
}

对客户端和属性值进行封装,然后遍历封装后的数组,逐个调用其回调函数onPropertyEvent,这个就是客户端传入的回调函数。

6.2 连续属性上报流程源码分析

连续属性订阅之后,会根据传入的采样率转换后的频率进行上报,订阅时,将订阅事件保存在mCookieToEventsMap这个map中,并唤醒了处理线程。

这个线程是什么时候初始化,在执行什么呢?这个可以参考https://editor.csdn.net/md/?articleId=140752076

cpp 复制代码
void loop(const Action& action) {
    static constexpr auto kInvalidTime = TimePoint(Nanos::max());

    std::vector<int32_t> cookies;

    while (!mStopRequested) {
        auto now = Clock::now();
        auto nextEventTime = kInvalidTime;
        cookies.clear();

        {
            std::unique_lock<std::mutex> g(mLock);

            for (auto&& it : mCookieToEventsMap) {
                RecurrentEvent& event = it.second;
                if (event.absoluteTime <= now) {
                    event.updateNextEventTime(now);
                    cookies.push_back(event.cookie);
                }

                if (nextEventTime > event.absoluteTime) {
                    nextEventTime = event.absoluteTime;
                }
            }
        }

        if (cookies.size() != 0) {
            action(cookies);
        }

        std::unique_lock<std::mutex> g(mLock);
        mCond.wait_until(g, nextEventTime);  // nextEventTime can be nanoseconds::max()
    }
}

唤醒这个线程,并在这个里面从mCookieToEventsMap中取出RecurrentEvent,判断时间戳,如果已经到时了,则执行里面的action回调函数。

cpp 复制代码
void EmulatedVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
    VehiclePropValuePtr v;

    auto& pool = *getValuePool();

    for (int32_t property : properties) {
        if (isContinuousProperty(property)) {
            auto internalPropValue = mPropStore->readValueOrNull(property);
            if (internalPropValue != nullptr) {
                v = pool.obtain(*internalPropValue);
            }
        } else {
            ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
        }

        if (v.get()) {
            v->timestamp = elapsedRealtimeNano();
            doHalEvent(std::move(v));
        }
    }
}

如果是连续属性,则从VehiclePropertyStore读取缓存,并调用doHalEvent往上报,这个和6.1节中的上报流程相同。

6.3 属性上报流程图

plantuml

cpp 复制代码
@startuml

participant CarService
box
participant EmulatedVehicleHal
participant VehicleHalManager
participant SubscriptionManager
participant HalClient
participant IPassThroughConnector
participant VehicleHalClient
participant VehiclePropertyStore
participant VehicleEmulator
participant SocketComm
participant ConcurrentQueue
participant BatchingConsumer
endbox

EmulatedVehicleHal -> IPassThroughConnector: onPropertyValueFromCar(*updatedPropValue, updateStatus)
IPassThroughConnector -> VehicleHalClient: onPropertyValue(value, updateStatus)
VehicleHalClient -> EmulatedVehicleHal: onPropertyValue(value, updateStatus)
EmulatedVehicleHal -> VehiclePropertyStore: writeValue(*updatedPropValue, updateStatus)
alt writeValue success
    EmulatedVehicleHal -> VehicleEmulator: doSetValueFromClient(*updatedPropValue)
    VehicleEmulator -> SocketComm: sendMessage(msg)
    EmulatedVehicleHal -> EmulatedVehicleHal: doHalEvent(std::move(updatedPropValue))
    EmulatedVehicleHal -> VehicleHalManager::onHalEvent(updatedPropValue)
    VehicleHalManager -> VehicleHalManager: mEventQueue.push(std::move(updatedPropValue))
    VehicleHalManager -> ConcurrentQueue: mQueue.push(updatedPropValue)
    ConcurrentQueue -> ConcurrentQueue: mCond.notify_one()
    ConcurrentQueue -> BatchingConsumer: runInternal(const OnBatchReceivedFunc& onBatchReceived)
    BatchingConsumer -> VehicleHalManager: onBatchHalEvent(const std::vector<VehiclePropValuePtr>& values)
    VehicleHalManager -> SubscriptionManager: distributeValuesToClients(values, \n\tSubscribeFlags::EVENTS_FROM_CAR)
    SubscriptionManager -> VehicleHalManager: clientValues
    loop cv in clientValues
        VehicleHalManager -> HalClient: getCallback()
        HalClient -> VehicleHalManager: callback
        VehicleHalManager -> CarService: callback->onPropertyEvent(values)
    end
end

@enduml

流程图

连续属性上报流程图省略,大致和这个上面差不多,只是触发在定时器中。

相关推荐
Dnelic-2 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen4 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年12 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿14 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神15 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛16 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法16 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter17 小时前
Android吸顶效果,并有着ViewPager左右切换
android
_祝你今天愉快18 小时前
分析android :The binary version of its metadata is 1.8.0, expected version is 1.5.
android
暮志未晚Webgl19 小时前
109. UE5 GAS RPG 实现检查点的存档功能
android·java·ue5