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小节
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配置
libvhalclient 和 libmivhalclient 实际上是封装了对Vehicle hal服务的访问,使用方需要配置访问vehiclehal的相关sepolicy.
已com.android.car为例,需要在se文件中添加以下描述.
// carservice_app 对应业务方进程的标签
hal_client_domain(carservice_app, hal_vehicle)FAQ
💬 飞书群「信号nativesdk接入QA群」
如何做到线程安全的处理onBinderDied
- 不要在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:
- BinderDied回调保存在stub中,触发Binderdied时,会从系统库中调到stub的成员函数中,再调用到用户的onBinderDied回调里,如果在用户回调中将stub置为null,而此时栈帧中还包含stub的成员函数,自然会崩溃。
- 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_ARG | all | 1. 检查传入的propid是否有效或是否泄漏或缺失是否areaid定义。 2. 检查soc版本是否老旧,过旧的版本中间件可能会缺失相关信号定义。 3. 确认无误后仍报该错误转人工 |
| TIMEOUT | all | 转人工@金卓 |
| TRY_AGAIN | all | 转人工@金卓 |
| NOT_AVAILABLE/NOT_AVAILABLE_FROM_VHAL | set | 一般出现在台架测试环境中,中间件缓存的信号为非有效状态。需要接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中的信号当前值会上报。