/* * Copyright (c) 2022, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef CPP_VHAL_CLIENT_INCLUDE_IVHALCLIENT_H_ #define CPP_VHAL_CLIENT_INCLUDE_IVHALCLIENT_H_ #include "IHalPropConfig.h" #include "IHalPropValue.h" #include #include #include #include #include #include #include #include #include namespace android { namespace frameworks { namespace automotive { namespace vhal { struct HalPropError { int32_t propId; int32_t areaId; aidl::android::hardware::automotive::vehicle::StatusCode status; }; // 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>& values) = 0; /** * Called when property set errors happen. */ virtual void onPropertySetError(const std::vector& 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, // The operation is not supported for the current VHAL. NOT_SUPPORTED = 8, }; // Convert the VHAL {@code StatusCode} to {@code ErrorCode}. static ErrorCode statusCodeToErrorCode( const aidl::android::hardware::automotive::vehicle::StatusCode& code) { switch (code) { case aidl::android::hardware::automotive::vehicle::StatusCode::OK: return ErrorCode::OK; case aidl::android::hardware::automotive::vehicle::StatusCode::TRY_AGAIN: return ErrorCode::TRY_AGAIN_FROM_VHAL; case aidl::android::hardware::automotive::vehicle::StatusCode::INVALID_ARG: return ErrorCode::INVALID_ARG; case aidl::android::hardware::automotive::vehicle::StatusCode::NOT_AVAILABLE: return ErrorCode::NOT_AVAILABLE_FROM_VHAL; case aidl::android::hardware::automotive::vehicle::StatusCode::ACCESS_DENIED: return ErrorCode::ACCESS_DENIED_FROM_VHAL; case aidl::android::hardware::automotive::vehicle::StatusCode::INTERNAL_ERROR: return ErrorCode::INTERNAL_ERROR_FROM_VHAL; default: return ErrorCode::INTERNAL_ERROR_FROM_VHAL; } } // VhalClientError is a wrapper class for {@code ErrorCode} that could act as E in {@code // Result}. class VhalClientError final { public: VhalClientError() : mCode(ErrorCode::OK) {} VhalClientError(ErrorCode&& code) : mCode(code) {} VhalClientError(const ErrorCode& code) : mCode(code) {} VhalClientError(aidl::android::hardware::automotive::vehicle::StatusCode&& code) : mCode(statusCodeToErrorCode(code)) {} VhalClientError(const aidl::android::hardware::automotive::vehicle::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 using VhalClientResult = android::base::Result; // 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; // ISubscriptionCallback is a client that could be used to subscribe/unsubscribe. // // Before destroying this client instance, client must call unsubscribeAll, otherwise, the // subscribed properties will still be subscribed and the callback will be kept alive until // the process ends. class ISubscriptionClient { public: virtual ~ISubscriptionClient() = default; // Subscribes to properties. It is recommended to use SubscribeOptionsBuilder to build // the options. virtual VhalClientResult subscribe( const std::vector& options) = 0; virtual VhalClientResult unsubscribe(const std::vector& propIds) = 0; virtual void unsubscribeAll() = 0; }; class SubscribeOptionsBuilder { private: int32_t mPropId; std::vector mAreaIds; float mSampleRate = 0; float mResolution = 0.0f; // VUR is on by default. bool mEnableVariableUpdateRate = true; public: explicit SubscribeOptionsBuilder(int32_t propId) : mPropId(propId) {} // Sets the sample rate. void setSampleRate(float sampleRate) { mSampleRate = sampleRate; } // Adds an area ID to subscribe. // // Only supported for AIDL VHAL client. // // For HIDL VHAL client, per-area ID subscription is not supported, this is ignored and all // area IDs for the property will be subscribed. void addAreaId(int32_t areaId) { mAreaIds.push_back(areaId); } // Sets the resolution of property updates for continuous property. // // Only supported for continuous property. Ignored for non-cotinuous property. // // Only supported for AIDL VHAL client. Ignored for HIDL VHAL client. // // This value indicates the resolution at which continuous property updates should be sent to // the platform. For example, if resolution is 0.01, the subscribed property value should be // rounded to two decimal places. // // The resolution must be an integer power of 10, (e.g. 0.01, 0.1, 1, 10, etc.). void setResolution(float resolution) { mResolution = resolution; } // Sets whether to enable varialbe update rate. // // Only supported for continuous property. Ignored for non-cotinuous property. // // Only supported for AIDL VHAL client. Ignored for HIDL VHAL client. // // If variable update rate is enabled, for each given areaId, if VHAL supports variable update // rate for the [propId, areaId], VHAL must ignore duplicate property value events // and only sends changed value events (a.k.a treat continuous as an on-change property). void setEnableVariableUpdateRate(bool enableVariableUpdateRate) { mEnableVariableUpdateRate = enableVariableUpdateRate; } // Builds the SubscribeOptions. aidl::android::hardware::automotive::vehicle::SubscribeOptions build() { return { .propId = mPropId, .areaIds = mAreaIds, .sampleRate = mSampleRate, .resolution = mResolution, .enableVariableUpdateRate = mEnableVariableUpdateRate, }; } }; // IVhalClient is a thread-safe client for AIDL or HIDL VHAL backend. class IVhalClient { public: [[deprecated("Use create(startThreadPool) instead")]] static std::shared_ptr create() { return create(/*startThreadPool=*/true); } // Waits for VHAL service and create a client. Return nullptr if failed to connect to VHAL. // // This waits for a certain short time period determined by the system. It is recommended to // use tryCreate if you do not want to block. // // startThreadPool indicates whether the IVhalClient will create a binder thread pool for // receiving callbacks. It is recommended to call ABinderProcess_startThreadPool only once from // the app's main function and then call this method with startThreadPool as false. static std::shared_ptr create(bool startThreadPool); [[deprecated("Use tryCreate(startThreadPool) instead")]] static std::shared_ptr tryCreate() { return tryCreate(/*startThreadPool=*/true); } // Tries to get the VHAL service and create a client. Return nullptr if failed to connect to // VHAL. // // This function does not block and returns immediately. It is possible that VHAL is still // starting up so the client should typically retry if failed to connect to VHAL. // // startThreadPool indicates whether the IVhalClient will create a binder thread pool for // receiving callbacks. It is recommended to call ABinderProcess_startThreadPool only once from // the app's main function and then call this method with startThreadPool as false. static std::shared_ptr tryCreate(bool startThreadPool); [[deprecated("Use tryCreateAidlClient(descriptor, startThreadPool) instead")]] static std::shared_ptr tryCreateAidlClient(const char* descriptor) { return tryCreateAidlClient(descriptor, /*startThreadPool=*/true); } // Tries to create a client based on the AIDL VHAL service descriptor. // // startThreadPool indicates whether the IVhalClient will create a binder thread pool for // receiving callbacks. It is recommended to call ABinderProcess_startThreadPool only once from // the app's main function and then call this method with startThreadPool as false. static std::shared_ptr tryCreateAidlClient(const char* descriptor, bool startThreadPool); // Tries to create a client based on the HIDL VHAL service descriptor. static std::shared_ptr 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>)>; using SetValueCallbackFunc = std::function)>; using OnBinderDiedCallbackFunc = std::function; /** * Checks 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; /** * Creates a new {@code IHalpropValue}. * * @param propId The property ID. * @return The created {@code IHalPropValue}. */ virtual std::unique_ptr createHalPropValue(int32_t propId) = 0; /** * Creates 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 createHalPropValue(int32_t propId, int32_t areaId) = 0; /** * Gets 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 callback) = 0; /** * Gets 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> getValueSync( const IHalPropValue& requestValue); /** * Sets 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 callback) = 0; /** * Sets 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 setValueSync(const IHalPropValue& requestValue); /** * Adds 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 addOnBinderDiedCallback( std::shared_ptr callback) = 0; /** * Removes 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 removeOnBinderDiedCallback( std::shared_ptr callback) = 0; /** * Gets all the property configurations. * * @return An okay result that contains all property configs on success or an error on failure. */ virtual VhalClientResult>> getAllPropConfigs() = 0; /** * Gets 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>> getPropConfigs( std::vector propIds) = 0; /** * Gets 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 getSubscriptionClient( std::shared_ptr callback) = 0; /** * Gets the min/max supported values for the specified [propId, areaId]s if they are provided * by VHAL. * * This is only supported for AIDL VHAL V4 and above. * * @param propIdAreaIds A list of [propId, areaId] to get the min/max supported values. * @return A list of results, one for each [propId, areaId], or error if failed to get any * get any results. Caller must check the status inside each result before accessing * the min/max value. Even if status is OK, minSupportedValue or maxSupportedValue may * be std::nullopt if it is not specified by VHAL. */ virtual VhalClientResult< std::vector<::aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult>> getMinMaxSupportedValue( [[maybe_unused]] const std::vector< ::aidl::android::hardware::automotive::vehicle::PropIdAreaId>& propIdAreaIds) { return ClientStatusError(ErrorCode::NOT_SUPPORTED) << "getMinMaxSupportedValue is not supported for the VHAL implementation"; } /** * Gets the supported values lists for the specified [propId, areaId]s if they are provided * by VHAL. * * This is only supported for AIDL VHAL V4 and above. * * @param propIdAreaIds A list of [propId, areaId] to get the supported values lists. * @return A list of results, one for each [propId, areaId], or error if failed to get any * get any results. Caller must check the status inside each result before accessing * the supported values list. Even if status is OK, supportedValuesList may be * {@code std::nullopt} if it is not specified by VHAL. If the supportedValuesList has * value, each element inside the list is guaranteed to have value. */ virtual VhalClientResult< std::vector<::aidl::android::hardware::automotive::vehicle::SupportedValuesListResult>> getSupportedValuesLists( [[maybe_unused]] const std::vector< ::aidl::android::hardware::automotive::vehicle::PropIdAreaId>& propIdAreaIds) { return ClientStatusError(ErrorCode::NOT_SUPPORTED) << "getSupportedValuesLists is not supported for the VHAL implementation"; } /** * Gets the VHAL interface version used by VHAL. * * This is only useful for AIDL VHAL. */ virtual int32_t getRemoteInterfaceVersion() { return 0; } }; namespace internal { inline std::string toString(const std::vector& values) { std::vector strings; for (int32_t value : values) { strings.push_back(std::to_string(value)); } return "[" + android::base::Join(strings, ",") + "]"; } // SubscriptionClient is the common base class for Aidl and Hidl Subscription Client. class SubscriptionClient : public ISubscriptionClient { protected: virtual std::unordered_set getSubscribedPropIds() = 0; // This should be called inside subclass's destructor. void verifySubscribedPropIdsEmpty() { const auto& subscribedPropIds = getSubscribedPropIds(); if (!subscribedPropIds.empty()) { ALOGW("Properties: %s are still subscribed when the SubscriptionClient is destroyed, " "they will always be subscribed until the client process ends, do you forget" " to call unsubscribeAll?", toString(std::vector(subscribedPropIds.begin(), subscribedPropIds.end())) .c_str()); } } }; } // namespace internal } // namespace vhal } // namespace automotive } // namespace frameworks } // namespace android #endif // CPP_VHAL_CLIENT_INCLUDE_IVHALCLIENT_H_