/* * Copyright (C) 2023 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 CHRE_HOST_HAL_CLIENT_H_ #define CHRE_HOST_HAL_CLIENT_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hal_error.h" namespace android::chre { using ::aidl::android::hardware::contexthub::AsyncEventType; using ::aidl::android::hardware::contexthub::BnContextHubCallback; using ::aidl::android::hardware::contexthub::ContextHubInfo; using ::aidl::android::hardware::contexthub::ContextHubMessage; using ::aidl::android::hardware::contexthub::HostEndpointInfo; using ::aidl::android::hardware::contexthub::IContextHub; using ::aidl::android::hardware::contexthub::IContextHubCallback; using ::aidl::android::hardware::contexthub::IContextHubDefault; using ::aidl::android::hardware::contexthub::MessageDeliveryStatus; using ::aidl::android::hardware::contexthub::NanoappBinary; using ::aidl::android::hardware::contexthub::NanoappInfo; using ::aidl::android::hardware::contexthub::NanSessionRequest; using ::aidl::android::hardware::contexthub::Setting; using ::ndk::ScopedAStatus; /** * A class connecting to CHRE Multiclient HAL via binder and taking care of * binder (re)connection. * *

HalClient will replace the SocketClient that does the similar * communication with CHRE but through a socket connection. * *

HalClient also maintains a set of connected host endpoints, using which * it will enforce in the future that a message can only be sent to/from an * endpoint id that is already connected to HAL. * *

When the binder connection to HAL is disconnected HalClient will have a * death recipient re-establish the connection and reconnect the previously * connected endpoints. In a rare case that CHRE also restarts at the same time, * a client should rely on IContextHubCallback.handleContextHubAsyncEvent() to * handle the RESTARTED event which is a signal that CHRE is up running. */ class HalClient { public: static constexpr int32_t kDefaultContextHubId = 0; /** Callback interface for a background connection. */ class BackgroundConnectionCallback { public: /** * This function is called when the connection to CHRE HAL is finished. * * @param isConnected indicates whether CHRE HAL is successfully connected. */ virtual void onInitialization(bool isConnected) = 0; virtual ~BackgroundConnectionCallback() = default; }; ~HalClient(); /** * Create a HalClient unique pointer used to communicate with CHRE HAL. * * @param callback a non-null callback. * @param contextHubId context hub id that only 0 is supported at this moment. * * @return null pointer if the creation fails. */ static std::unique_ptr create( const std::shared_ptr &callback, int32_t contextHubId = kDefaultContextHubId); /** * Returns true if the multiclient HAL is available. * *

Multicleint HAL may not be available on a device that has CHRE enabled. * In this situation, clients are expected to still use SocketClient to * communicate with CHRE. */ static bool isServiceAvailable(); /** A bug fix flag guarding the lock holding reduction change. */ static bool reduceLockHolding(); /** Returns true if this HalClient instance is connected to the HAL. */ bool isConnected() { return mContextHub != nullptr; } /** Connects to CHRE HAL synchronously. */ bool connect() { return initConnection() == HalError::SUCCESS; } /** Connects to CHRE HAL in background. */ void connectInBackground(BackgroundConnectionCallback &callback) { std::lock_guard lock(mBackgroundConnectionFuturesLock); // Policy std::launch::async is required to avoid lazy evaluation which can // postpone the execution until get() of the future returned by std::async // is called. mBackgroundConnectionFutures.emplace_back( std::async(std::launch::async, [&]() { callback.onInitialization(initConnection() == HalError::SUCCESS); })); } ScopedAStatus queryNanoapps() { return callIfConnected([&](const std::shared_ptr &hub) { return hub->queryNanoapps(mContextHubId); }); } /** Sends a message to a Nanoapp. */ ScopedAStatus sendMessage(const ContextHubMessage &message); /** Connects a host endpoint to CHRE. */ ScopedAStatus connectEndpoint(const HostEndpointInfo &hostEndpointInfo); /** Disconnects a host endpoint from CHRE. */ ScopedAStatus disconnectEndpoint(char16_t hostEndpointId); protected: class HalClientCallback : public BnContextHubCallback { public: explicit HalClientCallback( const std::shared_ptr &callback, HalClient *halClient) : mCallback(callback), mHalClient(halClient) {} ScopedAStatus handleNanoappInfo( const std::vector &appInfo) override { return mCallback->handleNanoappInfo(appInfo); } ScopedAStatus handleContextHubMessage( const ContextHubMessage &msg, const std::vector &msgContentPerms) override { return mCallback->handleContextHubMessage(msg, msgContentPerms); } ScopedAStatus handleContextHubAsyncEvent(AsyncEventType event) override { if (event == AsyncEventType::RESTARTED) { tryReconnectEndpoints(mHalClient); } return mCallback->handleContextHubAsyncEvent(event); } ScopedAStatus handleTransactionResult(int32_t transactionId, bool success) override { return mCallback->handleTransactionResult(transactionId, success); } ScopedAStatus handleNanSessionRequest( const NanSessionRequest &request) override { return mCallback->handleNanSessionRequest(request); } ScopedAStatus handleMessageDeliveryStatus( char16_t hostEndPointId, const MessageDeliveryStatus &messageDeliveryStatus) override { return mCallback->handleMessageDeliveryStatus(hostEndPointId, messageDeliveryStatus); } ScopedAStatus getUuid(std::array *outUuid) override { return mCallback->getUuid(outUuid); } ScopedAStatus getName(std::string *outName) override { return mCallback->getName(outName); } private: std::shared_ptr mCallback; HalClient *mHalClient; }; explicit HalClient(const std::shared_ptr &callback, int32_t contextHubId = kDefaultContextHubId) : mContextHubId(contextHubId) { mCallback = ndk::SharedRefBase::make(callback, this); ABinderProcess_startThreadPool(); mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient( AIBinder_DeathRecipient_new(onHalDisconnected)); mCallback->getName(&mClientName); } /** * Initializes the connection to CHRE HAL. */ HalError initConnection(); using HostEndpointId = char16_t; const std::string kAidlServiceName = std::string() + IContextHub::descriptor + "/default"; /** The callback for a disconnected HAL binder connection. */ static void onHalDisconnected(void *cookie); /** Reconnect previously connected endpoints after CHRE or HAL restarts. */ static void tryReconnectEndpoints(HalClient *halClient); ScopedAStatus callIfConnected( const std::function< ScopedAStatus(const std::shared_ptr &hub)> &func) { std::shared_ptr hub; { // Make a copy of mContextHub so that even if HAL is disconnected and // mContextHub is set to null the copy is kept as non-null to avoid crash. // Still guard the copy by a shared lock to avoid torn writes. std::shared_lock sharedLock(mConnectionLock); hub = mContextHub; } if (hub == nullptr) { return fromHalError(HalError::BINDER_DISCONNECTED); } return func(hub); } bool isEndpointConnected(HostEndpointId hostEndpointId) { std::shared_lock sharedLock(mConnectedEndpointsLock); return mConnectedEndpoints.find(hostEndpointId) != mConnectedEndpoints.end(); } void insertConnectedEndpoint(const HostEndpointInfo &hostEndpointInfo) { std::lock_guard lockGuard(mConnectedEndpointsLock); mConnectedEndpoints[hostEndpointInfo.hostEndpointId] = hostEndpointInfo; } void removeConnectedEndpoint(HostEndpointId hostEndpointId) { std::lock_guard lockGuard(mConnectedEndpointsLock); mConnectedEndpoints.erase(hostEndpointId); } static ScopedAStatus fromHalError(HalError errorCode) { return errorCode == HalError::SUCCESS ? ScopedAStatus::ok() : ScopedAStatus::fromServiceSpecificError( static_cast(errorCode)); } // Multi-contextHub is not supported at this moment. int32_t mContextHubId; // The lock guarding mConnectedEndpoints. std::shared_mutex mConnectedEndpointsLock; std::unordered_map mConnectedEndpoints{}; // The lock guarding the init connection flow. std::shared_mutex mConnectionLock; std::shared_ptr mContextHub; // Handler of the binder disconnection event with HAL. ndk::ScopedAIBinder_DeathRecipient mDeathRecipient; std::shared_ptr mCallback; std::string mClientName; // Lock guarding background connection threads. std::mutex mBackgroundConnectionFuturesLock; std::vector> mBackgroundConnectionFutures; }; } // namespace android::chre #endif // CHRE_HOST_HAL_CLIENT_H_