libvhalclient(IVehicle)使用文档

更新日志

更新日期修订人更新描述
2025.05.14(xcd主线)
2025.06.06 (rls-xcd-u-L43K31C32)
@金卓1. 增加可以上报初始值的订阅接口
2. 增加PropValue的debugString接口,可以把信号值转换成字符串,方便日志输出。
座舱内部demo:> 📎 附件:demo tar 包(20.0 KB,内部 authcode 链接,见原文档附件)
住户demo:> 📎 附件:demo tar 包(580.0 KB,内部 authcode 链接,见原文档附件)

概览

libvhalclient库封装了aidl IVehicle, 和hidl IVehicle接口,提供了一套通用的信号服务接口给native组件使用。native组件通过使用这套通用接口,可以不用关心底层调用的实际是aidl VehicleHal还是hidl VehicleHal。目前xcd平台虽然和dcd一样运行的是hidl vehiclehal,但接下来一段时间我们会将其迁移至使用aidl vehiclehal,所以业务方尽早切换至使用libvhalclient, 这样在后续底层迁移的时候能够避免受到影响。

仓库路径:packages/services/Car/cpp/vhal/client

结构:

├── Android.bp
├── include
│   ├── AidlHalPropConfig.h
│   ├── AidlHalPropValue.h
│   ├── AidlVhalClient.h
│   ├── HidlHalPropConfig.h
│   ├── HidlHalPropValue.h
│   ├── HidlVhalClient.h
│   ├── IHalPropConfig.h
│   ├── IHalPropValue.h
│   └── IVhalClient.h
├── src
│   ├── AidlHalPropConfig.cpp
│   ├── AidlHalPropValue.cpp
│   ├── AidlVhalClient.cpp
│   ├── HidlHalPropConfig.cpp
│   ├── HidlHalPropValue.cpp
│   ├── HidlVhalClient.cpp
│   └── IVhalClient.cpp

通信链路

📷 [图:libvhalclient 通信链路架构图(原文档为可交互 diagram,含 IVhalClient/AidlVhalClient/HidlVhalClient 与 aidl/hidl IVehicle backend 的分层关系)]

接口

// ISubscriptionCallback is a general interface to delivery property events caused by subscription.
class ISubscriptionCallback {
public:
    virtual ~ISubscriptionCallback() = default;
    /**
     * Called when new property events happen.
     */
    virtual void onPropertyEvent(const std::vector<std::unique_ptr<IHalPropValue>>& values) = 0;
 
    /**
     * Called when property set errors happen.
     */
    virtual void onPropertySetError(const std::vector<HalPropError>& errors) = 0;
};
 
// ISubscriptionCallback is a client that could be used to subscribe/unsubscribe.
class IVhalClient {
public:
    // Wait for VHAL service and create a client. Return nullptr if failed to connect to VHAL.
    static std::shared_ptr<IVhalClient> create();
 
    // Try to get the VHAL service and create a client. Return nullptr if failed to connect to VHAL.
    static std::shared_ptr<IVhalClient> tryCreate();
 
    // Try to create a client based on the AIDL VHAL service descriptor.
    static std::shared_ptr<IVhalClient> tryCreateAidlClient(const char* descriptor);
 
    // Try to create a client based on the HIDL VHAL service descriptor.
    static std::shared_ptr<IVhalClient> tryCreateHidlClient(const char* descriptor);
 
    // The default timeout for callbacks.
    constexpr static int64_t DEFAULT_TIMEOUT_IN_SEC = 10;
 
    virtual ~IVhalClient() = default;
 
    using GetValueCallbackFunc =
            std::function<void(VhalClientResult<std::unique_ptr<IHalPropValue>>)>;
    using SetValueCallbackFunc = std::function<void(VhalClientResult<void>)>;
    using OnBinderDiedCallbackFunc = std::function<void()>;
 
    /**
     * Check whether we are connected to AIDL VHAL backend.
     *
     * Returns {@code true} if we are connected to AIDL VHAL backend, {@code false} if we are
     * connected to HIDL backend.
     */
    virtual bool isAidlVhal() = 0;
 
    /**
     * Create a new {@code IHalpropValue}.
     *
     * @param propId The property ID.
     * @return The created {@code IHalPropValue}.
     */
    virtual std::unique_ptr<IHalPropValue> createHalPropValue(int32_t propId) = 0;
 
    /**
     * Create a new {@code IHalpropValue}.
     *
     * @param propId The property ID.
     * @param areaId The area ID for the property.
     * @return The created {@code IHalPropValue}.
     */
    virtual std::unique_ptr<IHalPropValue> createHalPropValue(int32_t propId, int32_t areaId) = 0;
 
    /**
     * Get a property value asynchronously.
     *
     * @param requestValue The value to request.
     * @param callback The callback that would be called when the result is ready. The callback
     *    would be called with an okay result with the got value inside on success. The callback
     *    would be called with an error result with error code as the returned status code on
     *    failure.
     */
    virtual void getValue(const IHalPropValue& requestValue,
                          std::shared_ptr<GetValueCallbackFunc> callback) = 0;
 
    /**
     * Get a property value synchronously.
     *
     * @param requestValue the value to request.
     * @return An okay result with the returned value on success or an error result with returned
     *    status code as error code. For AIDL backend, this would return TRY_AGAIN error on timeout.
     *    For HIDL backend, because HIDL backend is synchronous, timeout does not apply.
     */
    virtual VhalClientResult<std::unique_ptr<IHalPropValue>> getValueSync(
            const IHalPropValue& requestValue);
 
    /**
     * Set a property value asynchronously.
     *
     * @param requestValue The value to set.
     * @param callback The callback that would be called when the request is processed. The callback
     *    would be called with an empty okay result on success. The callback would be called with
     *    an error result with error code as the returned status code on failure.
     */
    virtual void setValue(const IHalPropValue& requestValue,
                          std::shared_ptr<SetValueCallbackFunc> callback) = 0;
 
    /**
     * Set a property value synchronously.
     *
     * @param requestValue the value to set.
     * @return An empty okay result on success or an error result with returned status code as
     *    error code. For AIDL backend, this would return TIMEOUT error on timeout.
     *    For HIDL backend, because HIDL backend is synchronous, timeout does not apply.
     */
    virtual VhalClientResult<void> setValueSync(const IHalPropValue& requestValue);
 
    /**
     * Add a callback that would be called when the binder connection to VHAL died.
     *
     * @param callback The callback that would be called when the binder died.
     * @return An okay result on success or an error on failure.
     */
    virtual VhalClientResult<void> addOnBinderDiedCallback(
            std::shared_ptr<OnBinderDiedCallbackFunc> callback) = 0;
 
    /**
     * Remove a previously added OnBinderDied callback.
     *
     * @param callback The callback that would be removed.
     * @return An okay result on success, or an error if the callback is not added before.
     */
    virtual VhalClientResult<void> removeOnBinderDiedCallback(
            std::shared_ptr<OnBinderDiedCallbackFunc> callback) = 0;
 
    /**
     * Get all the property configurations.
     *
     * @return An okay result that contains all property configs on success or an error on failure.
     */
    virtual VhalClientResult<std::vector<std::unique_ptr<IHalPropConfig>>> getAllPropConfigs() = 0;
 
    /**
     * Get the configs for specified properties.
     *
     * @param propIds A list of property IDs to get configs for.
     * @return An okay result that contains property configs for specified properties on success or
     *    an error if failed to get any of the property configs.
     */
    virtual VhalClientResult<std::vector<std::unique_ptr<IHalPropConfig>>> getPropConfigs(
            std::vector<int32_t> propIds) = 0;
 
    /**
     * Get a {@code ISubscriptionClient} that could be used to subscribe/unsubscribe to properties.
     *
     * @param callback The callback that would be called when property event happens.
     * @return A {@code ISubscriptionClient} used to subscribe/unsubscribe.
     */
    virtual std::unique_ptr<ISubscriptionClient> getSubscriptionClient(
            std::shared_ptr<ISubscriptionCallback> callback) = 0;
};
 

使用样例

代码见更新日志

getService

xcd主线当前直接使用 tryCreateHidlClient 来获取Hidl服务。

f3平台需要使用tryCreateAidlClient(SERVICE_DESCRIPTOR) 来获取aidl服务.

xcd主线后续会和f3平台保持一致

 // xcd:
 while (stub_ == nullptr) {
            std::cout << "try get hidl vhal service!!!" << std::endl;
            stub_ = IVhalClient::tryCreateHidlClient("default");
            std::this_thread::sleep_for(std::chrono::seconds(1));
 }
 
 //  f3
  while (stub_ == nullptr) {
            std::cout << "try get aidl vhal service!!!" << std::endl;
            stub_ = IVhalClient::tryCreateAidlClient("android.hardware.automotive.vehicle.IVehicle/micar");
            std::this_thread::sleep_for(std::chrono::seconds(1));
 }

💡 如何适应多平台实现(住户无需关注)

#include <android/binder_manager.h>
    constexpr const char* MIVHAL_DESCRIPTOR = "android.hardware.automotive.vehicle.IVehicle/micar";
    if (AServiceManager_isDeclared(MIVHAL_DESCRIPTOR)) {
        ALOGI("mi aidl vhal configured, use micar vhal instead");
        return IVhalClient::tryCreateAidlClient(MIVHAL_DESCRIPTOR);
    } else {
        return IVhalClient::tryCreateHidlClient("default");;
    }

subscribe property

    subscriptionCallback_ = std::make_shared<SubscriptionCallback>();
    subscriptionClient_ = stub->getSubscriptionClient(subscriptionCallback_);
 
    std::vector<SubscribeOptions> options{
            //  normal property
            {
                    .propId = 0x61501734 /*SYSTEM_HEART_BEAT*/,
                    .areaIds = {0},
                    .sampleRate = 0.0f,
            },
            //  multy areaId property
            {
                    .propId = 0x65404101 /*HVAC_POWER*/,
                    // 不填或者填空默认订阅所有Propid
                    .areaIds = {7 /*VehicleAreaSeatExt::FRONT*/, 1904 /*VehicleAreaSeatExt::REAR*/},
                    .sampleRate = 0.0f,
            },
            //  continous property
            {
                    .propId = 0x61407201 /*BASIC_INFO_SPEED*/,
                    .areaIds = {0},
                    .sampleRate = 1.0f,
            }};
 
    auto result = subscriptionClient_->subscribe(options);

set property

同步下发:

    std::unique_ptr<IHalPropValue> value_with_areaid =
            stub->createHalPropValue(0x65404101 /*HVAC_POWER*/, 1904 /*VehicleAreaSeatExt::REAR*/);
 
    value_with_areaid->setInt32Values({0});
 
    std::condition_variable result_cv;
    std::mutex result_lock;
    VhalClientResult<void> setResult;
    bool done = false;
 
    setResult = stub->setValueSync(*value_with_areaid);

异步下发:

    std::shared_ptr<IVhalClient> stub = getStub();
    if (stub == nullptr) {
        return;
    }
    std::unique_ptr<IHalPropValue> value_with_areaid = stub->createHalPropValue(
            0x65402402 /*READING_LIGHT_DETAIL*/, 64 /*VehicleAreaSeat::ROW_2_RIGHT*/);
 
    value_with_areaid->setInt32Values({0});
 
    std::condition_variable result_cv;
    std::mutex result_lock;
    VhalClientResult<void> setResult;
    bool done = false;
 
    auto setValueCallback =
            std::make_shared<IVhalClient::SetValueCallbackFunc>([&](VhalClientResult<void> result) {
                std::lock_guard lock(result_lock);
                setResult = result;
                done = true;
                result_cv.notify_all();
            });
 
    stub->setValue(*value_with_areaid, setValueCallback);
    {
        std::unique_lock l(result_lock);
        result_cv.wait(l, [&]() {
            return done;
        });
        std::cout << "Set value() "
                  << (setResult.ok() ? ("success") : "failed : " + setResult.error().message())
                  << std::endl;
    }

Get property

同步获取

    std::unique_ptr<IHalPropValue> request_prop =
            stub->createHalPropValue(0x61101712 /*SYSTEM_VIN*/);
 
    auto getResult = stub->getValueSync(*request_prop);

异步获取

    std::unique_ptr<IHalPropValue> request_prop =
            stub->createHalPropValue(0x61101712 /*SYSTEM_VIN*/);
 
    // or create prop with areaid
    //   std::unique_ptr<IHalPropValue> request_prop =
    //      stub->createHalPropValue(propid, areaid);
 
    std::condition_variable result_cv;
    std::mutex result_lock;
    VhalClientResult<std::unique_ptr<IHalPropValue>> getResult;
    bool done = false;
 
    auto getValueCallback = std::make_shared<IVhalClient::GetValueCallbackFunc>(
            [&](VhalClientResult<std::unique_ptr<IHalPropValue>> result) {
                std::lock_guard lock(result_lock);
                getResult = std::move(result);
                done = true;
                result_cv.notify_all();
            });
 
    std::cout << "Complete get-value-async request, wait for response" << std::endl;
 
    stub->getValue(*request_prop, getValueCallback);
    {
        std::unique_lock l(result_lock);
        result_cv.wait(l, [&]() {
            return done;
        });
        std::cout << "Get value (" << request_prop->getPropId() << ") "
                  << (getResult.ok() ? ("success :" + getResult.value()->getStringValue()) :
                                       "failed : " + getResult.error().message())
                  << std::endl;
    }

断链处理

binderDiedHandler_ = std::make_shared<IVhalClient::OnBinderDiedCallbackFunc>([&]() {
    onBinderDied();
});
 
stub_->addOnBinderDiedCallback(binderDiedHandler_);
 
void onBinderDied() {
        std::lock_guard l(lock_);
        ready_ = false;
        getServiceFuture_ = std::async([&]() {
            {
                std::lock_guard l(lock_);
                stub_ = nullptr;
                getServiceLocked();
                stub_->addOnBinderDiedCallback(binderDiedHandler_);
            }
            sub();  //  sub如果要去获取lock_就得小心小心死锁
        });
}
 

status获取

以sub的回调获取status为例,get同理

    void onPropertyEvent(const std::vector<std::unique_ptr<IHalPropValue>> &values) override {
        for (const auto &value : values) {
            std::cout << "onPropertyEvent : " << value->getPropId() << " status : " << static_cast<int32_t>(value->getStatus()) << std::endl;
        }
    }
 

Status 枚举映射,由于平台和历史原因,用Int32来解析该枚举值

enum VehiclePropStatus {
    AVAILABLE           = 0;
    UNAVAILABLE         = 1; // 0x1
    ERROR               = 2; // 0x1 << 1
    TIME_OUT            = 4; // 0x1 << 2
    TIMEOUT_UNAVAILABLE = 5; // 0x1 << 1 | 0x1 << 2
}

prebuild依赖(座舱住户使用)

libmivhalclient 是对libvhalclient 的简单封装,隐藏了对andriod内部其他模块的依赖细节,对非座舱模块来说使用起来更方便,直接依赖更少。接口与libvhalclient 几乎完全一致,仅VhalClientResult 的使用上有些许不同,可以参考上面几个小节使用。

接口

/*
 * Copyright (C) 2024 Xiaomi Inc. All rights reserved.
 */
 
#ifndef MICAR_VEHICLE_CLIENT_IMIVEHICLECLIENT_HPP
#define MICAR_VEHICLE_CLIENT_IMIVEHICLECLIENT_HPP
 
#include <cstdint>
#include <functional>
#include <memory>
 
#include "IMiVhalPropValue.hpp"
#include "IMiVhalPropConfig.hpp"
 
namespace vendor::micar::hardware::vehicle::interface {
 
enum class StatusCode {
  OK = 0,
  TRY_AGAIN = 1,
  INVALID_ARG = 2,
  NOT_AVAILABLE = 3,
  ACCESS_DENIED = 4,
  INTERNAL_ERROR = 5,
  NOT_AVAILABLE_DISABLED = 6,
  NOT_AVAILABLE_SPEED_LOW = 7,
  NOT_AVAILABLE_SPEED_HIGH = 8,
  NOT_AVAILABLE_POOR_VISIBILITY = 9,
  NOT_AVAILABLE_SAFETY = 10,
};
 
struct MiVhalPropError {
    int32_t propId;
    int32_t areaId;
    StatusCode status;
};
 
struct SubscribeOptions {
    /** Property to subscribe */
    int32_t propId;
    /**
     * Optional areas to subscribe for this property, if empty, would subscribe
     * to all areas configured for this property.
     */
    std::vector<int32_t> areaIds;
    /**
     * 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;
};
 
// ISubscriptionCallback is a general interface to delivery property events caused by subscription.
class ISubscriptionCallback {
public:
    virtual ~ISubscriptionCallback() = default;
    /**
     * Called when new property events happen.
     */
    virtual void onPropertyEvent(const std::vector<std::unique_ptr<IMiVhalPropValue>>& values) = 0;
 
    /**
     * Called when property set errors happen.
     */
    virtual void onPropertySetError(const std::vector<MiVhalPropError>& errors) = 0;
};
 
// Errors for vehicle HAL client interface.
enum class ErrorCode : int {
    // Response status is OK. No errors.
    OK = 0,
    // The argument is invalid.
    INVALID_ARG = 1,
    // The request timed out. The client may try again.
    TIMEOUT = 2,
    // Some errors occur while connecting VHAL. The client may try again.
    TRANSACTION_ERROR = 3,
    // Some unexpected errors happen in VHAL. Needs to try again.
    TRY_AGAIN_FROM_VHAL = 4,
    // The device of corresponding vehicle property is not available.
    // Example: the HVAC unit is turned OFF when user wants to adjust temperature.
    NOT_AVAILABLE_FROM_VHAL = 5,
    // The request is unauthorized.
    ACCESS_DENIED_FROM_VHAL = 6,
    // Some unexpected errors, for example OOM, happen in VHAL.
    INTERNAL_ERROR_FROM_VHAL = 7,
};
 
// Convert the VHAL {@code StatusCode} to {@code ErrorCode}.
static ErrorCode statusCodeToErrorCode(
        const StatusCode& code) {
    switch (code) {
        case StatusCode::OK:
            return ErrorCode::OK;
        case StatusCode::TRY_AGAIN:
            return ErrorCode::TRY_AGAIN_FROM_VHAL;
        case StatusCode::INVALID_ARG:
            return ErrorCode::INVALID_ARG;
        case StatusCode::NOT_AVAILABLE:
            return ErrorCode::NOT_AVAILABLE_FROM_VHAL;
        case StatusCode::ACCESS_DENIED:
            return ErrorCode::ACCESS_DENIED_FROM_VHAL;
        case StatusCode::INTERNAL_ERROR:
            return ErrorCode::INTERNAL_ERROR_FROM_VHAL;
        default:
            return ErrorCode::INTERNAL_ERROR_FROM_VHAL;
    }
}
 
class VhalClientError final {
public:
    VhalClientError() : mCode(ErrorCode::OK) {}
 
    VhalClientError(ErrorCode&& code) : mCode(code) {}
 
    VhalClientError(const ErrorCode& code) : mCode(code) {}
 
    VhalClientError(StatusCode&& code) :
          mCode(statusCodeToErrorCode(code)) {}
 
    VhalClientError(const StatusCode& code) :
          mCode(statusCodeToErrorCode(code)) {}
 
    ErrorCode value() const;
 
    inline operator ErrorCode() const { return value(); }
 
    static std::string toString(ErrorCode code);
 
    std::string print() const;
 
private:
    ErrorCode mCode;
};
 
// VhalClientResult is a {@code Result} that contains {@code ErrorCode} as error type.
// template <class T>
// using VhalClientResult = android::base::Result<T, VhalClientError>;
// Since it is unable to use android::base::expected or std::expected, so we wrapp it into this.
template<class T>
class VhalClientResult final {
public:
    VhalClientResult() = default;
    VhalClientResult(VhalClientError vhalError) : value_(), error_(vhalError) {}
    VhalClientResult(const T& value) : value_(value), error_() {}
    VhalClientResult(T&& value) : value_(std::move(value)), error_() {}
    T& value() { return value_; }
    VhalClientError error() { return error_; }
    bool ok() { return error_.value() == ErrorCode::OK; }
private:
    T value_;
    VhalClientError error_;
};
 
template<>
class VhalClientResult<void> final {
public:
    VhalClientResult() = default;
    VhalClientResult(VhalClientError vhalError) : error_(vhalError) {}
    VhalClientError error() { return error_; }
    bool ok() { return error_.value() == ErrorCode::OK; }
private:
    VhalClientError error_;
};
 
// // ClientStatusError could be cast to {@code ResultError} with a {@code ErrorCode}
// // and should be used as error type for {@VhalClientResult}.
// using ClientStatusError = android::base::Error<VhalClientError>;
 
// ISubscriptionCallback is a client that could be used to subscribe/unsubscribe.
class ISubscriptionClient {
public:
    virtual ~ISubscriptionClient() = default;
 
    virtual VhalClientResult<void> subscribe(
            const std::vector<SubscribeOptions>&
                    options) = 0;
 
    virtual VhalClientResult<void> unsubscribe(const std::vector<int32_t>& propIds) = 0;
 
    /**
     * Subscribe to property updates.
     * @param options The options to subscribe.
     * @param report_current_value Whether to report the current value of the subscribed
     *        properties on
     */
    virtual VhalClientResult<void> subscribe(
        const std::vector<SubscribeOptions>& options, bool report_current_value) = 0;
};
 
// IMiVhalClient is a thread-safe client for AIDL or HIDL VHAL backend.
class IMiVhalClient {
public:
    // Wait for VHAL service and create a client. Return nullptr if failed to connect to VHAL.
    static std::shared_ptr<IMiVhalClient> create();
 
    // The default timeout for callbacks.
    constexpr static int64_t DEFAULT_TIMEOUT_IN_SEC = 10;
 
    virtual ~IMiVhalClient() = default;
 
    using GetValueCallbackFunc =
            std::function<void(VhalClientResult<std::unique_ptr<IMiVhalPropValue>>)>;
    using SetValueCallbackFunc = std::function<void(VhalClientResult<void>)>;
    using OnBinderDiedCallbackFunc = std::function<void()>;
 
    /**
     * Create a new {@code IMiVhalPropValue}.
     *
     * @param propId The property ID.
     * @return The created {@code IMiVhalPropValue}.
     */
    virtual std::unique_ptr<IMiVhalPropValue> createHalPropValue(int32_t propId) = 0;
 
    /**
     * Create a new {@code IMiVhalPropValue}.
     *
     * @param propId The property ID.
     * @param areaId The area ID for the property.
     * @return The created {@code IMiVhalPropValue}.
     */
    virtual std::unique_ptr<IMiVhalPropValue>
            createHalPropValue(int32_t propId, int32_t areaId) = 0;
 
    /**
     * Get a property value asynchronously.
     *
     * @param requestValue The value to request.
     * @param callback The callback that would be called when the result is ready. The callback
     *    would be called with an okay result with the got value inside on success. The callback
     *    would be called with an error result with error code as the returned status code on
     *    failure.
     */
    virtual VhalClientResult<void> getValue(const IMiVhalPropValue& requestValue,
                          std::shared_ptr<GetValueCallbackFunc> callback) = 0;
 
    /**
     * Get a property value synchronously.
     *
     * @param requestValue the value to request.
     * @return An okay result with the returned value on success or an error result with returned
     *    status code as error code. For AIDL backend, this would return TRY_AGAIN error on timeout.
     *    For HIDL backend, because HIDL backend is synchronous, timeout does not apply.
     */
    virtual VhalClientResult<std::unique_ptr<IMiVhalPropValue>> getValueSync(
            const IMiVhalPropValue& requestValue);
 
    /**
     * Set a property value asynchronously.
     *
     * @param requestValue The value to set.
     * @param callback The callback that would be called when the request is processed. The callback
     *    would be called with an empty okay result on success. The callback would be called with
     *    an error result with error code as the returned status code on failure.
     */
    virtual VhalClientResult<void> setValue(const IMiVhalPropValue& requestValue,
                          std::shared_ptr<SetValueCallbackFunc> callback) = 0;
 
    /**
     * Set a property value synchronously.
     *
     * @param requestValue the value to set.
     * @return An empty okay result on success or an error result with returned status code as
     *    error code. For AIDL backend, this would return TIMEOUT error on timeout.
     *    For HIDL backend, because HIDL backend is synchronous, timeout does not apply.
     */
    virtual VhalClientResult<void> setValueSync(const IMiVhalPropValue& requestValue);
 
    /**
     * Add a callback that would be called when the binder connection to VHAL died.
     *
     * @param callback The callback that would be called when the binder died.
     * @return An okay result on success or an error on failure.
     */
    virtual VhalClientResult<void> addOnBinderDiedCallback(
            std::shared_ptr<OnBinderDiedCallbackFunc> callback) = 0;
 
    /**
     * Remove a previously added OnBinderDied callback.
     *
     * @param callback The callback that would be removed.
     * @return An okay result on success, or an error if the callback is not added before.
     */
    virtual VhalClientResult<void> removeOnBinderDiedCallback(
            std::shared_ptr<OnBinderDiedCallbackFunc> callback) = 0;
 
    /**
     * Get all the property configurations.
     *
     * @return An okay result that contains all property configs on success or an error on failure.
     */
    virtual VhalClientResult<std::vector<std::unique_ptr<IMiVhalPropConfig>>>
            getAllPropConfigs() = 0;
 
    /**
     * Get the configs for specified properties.
     *
     * @param propIds A list of property IDs to get configs for.
     * @return An okay result that contains property configs for specified properties on success or
     *    an error if failed to get any of the property configs.
     */
    virtual VhalClientResult<std::vector<std::unique_ptr<IMiVhalPropConfig>>>
            getPropConfigs(std::vector<int32_t> propIds) = 0;
 
    /**
     * Get a {@code ISubscriptionClient} that could be used to subscribe/unsubscribe to properties.
     *
     * @param callback The callback that would be called when property event happens.
     * @return A {@code ISubscriptionClient} used to subscribe/unsubscribe.
     */
    virtual std::unique_ptr<ISubscriptionClient> getSubscriptionClient(
            std::shared_ptr<ISubscriptionCallback> callback) = 0;
};
 
}  //  namespace vendor::micar::hardware::vehicle::interface
 
#endif  // MICAR_VEHICLE_CLIENT_IMIVEHICLECLIENT_HPP
 

使用样例

注意: 样例使用了c++17语法,需要指定c++17标准。

见更新日志

编译环境

上一小节的样例使用aosp基线的bp编译,这里另提供一个使用bazel搭配ndk构建的样例Xcd 住户构建 vhalclient

车机信号调试

DCD & XCD(AndroidU国内版本)

可参考下面文档第2小节

车辆信号调试-Q&ACarService与VHAL

f1udp & f3 & 所有海外版本(AndroidV及后续版本)

# 以下所有命令都需要在在adb shell中执行
 
#注意,后续使用的hal名称为 android.hardware.automotive.vehicle@2.0::IVehicle/micar
# 例如 :
# dumpsys android.hardware.automotive.vehicle.IVehicle/micar
 
# 获取指定prop
dumpsys android.hardware.automotive.vehicle.IVehicle/micar --get 1715482881
 
# 下行,设置prop,如下例子中:
#    1715482881 代表具体propid
#    i 代表int, f 代表float, s 代表 string
#    a 代表area, b 代表bytes
#    下发数组信号 [-f f1 f2...] [-i i1 i2...] [-i64 i1 i2...] [-s s1 s2...] [-b b1 b2...] [-a a]
dumpsys android.hardware.automotive.vehicle.IVehicle/micar --set 1715482881 -i 1 -a 16
 
# 上行,模拟底层发信号到vhal,格式和--set相同
#    --stat 0代表AVALIABLE;1代表UNAVALIABLE
dumpsys android.hardware.automotive.vehicle.IVehicle/micar --mock_from_car 1715482881 -i 1 -a 16 --stat 0
 
# mock bytes类型信号
# 如下示例 为mock {0x12, 0x23, 0x56} 的bytes数组(仅xcd可用)
dumpsys android.hardware.automotive.vehicle.IVehicle/micar --mock_from_car 0x6170b40d -b 0x123456
 
# 上下行模拟数组信号
#  dumpsys android.hardware.automotive.vehicle.IVehicle/micar [--command] [-type] [value] [-type] [value]...
#    整体格式同上
dumpsys android.hardware.automotive.vehicle.IVehicle/micar --mock_from_car 0x6141415c -i 1 -i 1
 

sepolicy配置

libvhalclientlibmivhalclient 实际上是封装了对Vehicle hal服务的访问,使用方需要配置访问vehiclehal的相关sepolicy.

已com.android.car为例,需要在se文件中添加以下描述.

// carservice_app 对应业务方进程的标签
hal_client_domain(carservice_app, hal_vehicle)

FAQ

💬 飞书群「信号nativesdk接入QA群」

如何做到线程安全的处理onBinderDied

  1. 不要在onBinderDied回调中重新获取服务或将stub置空

错误示范:

 
    void getService() {
        while (stub_ == nullptr) {
            std::cout << "try get service!!!" << std::endl;
            stub_ = IMiVhalClient::create();
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
    }
 
    void onBinderDied() {
        std::cout << "vhal died, we need to rebuild connection and resend sub request"
                << std::endl;
        std::lock_guard l(lock_);
        stub_->removeOnBinderDiedCallback(binderDiedHandler_);
        stub_ = nullptr;
        getServiceFuture_ = std::async([&]() {
            std::lock_guard l(lock_);
            getService();
            stub_->addOnBinderDiedCallback(binderDiedHandler_);
        });
    }
 

正确示范:

 
    void getServiceLocked() {
        while (stub_ == nullptr) {
            std::cout << "try get service!!!" << std::endl;
            stub_ = IMiVhalClient::create();
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }
        ready_ = true;
    }
 
    void onBinderDied() {
        std::cout << "vhal died, we need to rebuild connection and resend sub request"
                << std::endl;
        std::lock_guard l(lock_);
        ready_ = false;
        getServiceFuture_ = std::async([&]() {
            {
                std::lock_guard l(lock_);
                stub_->removeOnBinderDiedCallback(binderDiedHandler_);
                stub_ = nullptr;
                getServiceLocked();
                stub_->addOnBinderDiedCallback(binderDiedHandler_);
            }
             // 此处省略重新订阅,业务实现需要加上这部分逻辑。注意死锁可能
        });
    }

Why:

  1. BinderDied回调保存在stub中,触发Binderdied时,会从系统库中调到stub的成员函数中,再调用到用户的onBinderDied回调里,如果在用户回调中将stub置为null,而此时栈帧中还包含stub的成员函数,自然会崩溃。
  2. ready 的详细使用请参考提供的最新demo,用于保证多线程下(触发binderdie的系统库线程和用户访问stub的线程间)能安全的使用stub。避免出现BinderDied后在重新创建stub的同时用户访问stub而产生竞态问题。

常见接口错误返回值参考

😶 并非所有调用都一定成功,一定要检查get,set,sub等接口的返回值并把错误输出打印出来,能帮助解决90%问题!!!

A. 如何检查返回值

  以sub为例,座舱内部接口和住户接口有细微差异,已用绿字标记出

座舱接口

auto result = subscriptionClient_->subscribe(options);
if (!result.ok()) {
    // 订阅失败了,一定要打印日志
    std::cout << result.error().message();
}

住户接口

auto result = subscriptionClient_->subscribe(options);
if (!result.ok()) {
    // 订阅失败了,一定要打印日志
    std::cout << result.error().print();
}

B.常见错误码解析

错误码接口可能的原因及解决方法
INVALID_ARGall1. 检查传入的propid是否有效或是否泄漏或缺失是否areaid定义。
2. 检查soc版本是否老旧,过旧的版本中间件可能会缺失相关信号定义。
3. 确认无误后仍报该错误转人工
TIMEOUTall转人工@金卓
TRY_AGAINall转人工@金卓
NOT_AVAILABLE/NOT_AVAILABLE_FROM_VHALset一般出现在台架测试环境中,中间件缓存的信号为非有效状态。需要接canoe或者用mock命令将信号缓存值修改为available(参考第6小节lshal mock_from_car命令)
其他错误all转人工@金卓

订阅后信号初始值上报

旧版本订阅信号后不会上报当前值,需要用户主动调用get来拿到当前值,处理不当可能导致收到的信号时序不对,用起来也不太方便。

2025.5.14主线更新后添加额外的subscribe接口,可以在订阅后自动上报当前值

class ISubscriptionClient {
 
    virtual VhalClientResult<void> subscribe(
            const std::vector<SubscribeOptions>&
                    options) = 0;
    /**
     * Subscribe to property updates.
     * @param options The options to subscribe.
     * @param report_current_value Whether to report the current value of the subscribed
     *        properties on
     */
    virtual VhalClientResult<void> subscribe(
        const std::vector<SubscribeOptions>& options, bool report_current_value) = 0;
};

如上段代码所示, 在ISubscriptionClient中额外重载了一个subscribe接口,指定report_current_value为true后会把optionsex中的信号的当前值自动上报。

    subscriptionCallback_ = std::make_shared<SubscriptionCallback>();
    subscriptionClient_ = stub->getSubscriptionClient(subscriptionCallback_);
 
    // 不上报初始值的订阅流程
    std::vector<SubscribeOptions> options{
            //  normal property
            {
                    .propId = 0x61501734 /*SYSTEM_HEART_BEAT*/,
                    .areaIds = {0},
                    .sampleRate = 0.0f,
            },
            //  multy areaId property
            {
                    .propId = 0x65404101 /*HVAC_POWER*/,
                    .areaIds = {7 /*VehicleAreaSeatExt::FRONT*/, 1904 /*VehicleAreaSeatExt::REAR*/},
                    .sampleRate = 0.0f,
            },
            //  continous property
            {
                    .propId = 0x61407201 /*BASIC_INFO_SPEED*/,
                    .areaIds = {0},
                    .sampleRate = 1.0f,
            }};
 
    auto result = subscriptionClient_->subscribe(options);
    if (!result.ok()) {
        std::cout << "subscribe() failed : " << result.error().print() << std::endl;
    }
 
    //  订阅后上报初始值的流程
    std::vector<SubscribeOptions> optionsex{
            {
                .propId = 0x61407207 /*BASIC_INFO_POWER_MODE*/,
                .areaIds = {0},
                .sampleRate = 0.0f,
            },
            {
                .propId = 0x67407231 /*BASIC_INFO_TIRE_TEMPERATURE_LEVEL*/,
                .areaIds = {0},
                .sampleRate = 0.0f,
            },
    };
 
    auto resultex = subscriptionClient_->subscribe(optionsex, true);
    if (!resultex.ok()) {
        std::cout << "subscribe() failed : " << resultex.error().print() << std::endl;
    }
 

另外两种sub是可以混用的,创建subscriptionClient_后可以直接复用,先subscribe(options1)subscribe(options2, true),最后订阅的信号是options1和options2的并集,也就是options1和options2中的信号后续变化都能通过回调收到,唯一的区别是只有options2中的信号当前值会上报。