1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef CHRE_HOST_HAL_CLIENT_H_ 18 #define CHRE_HOST_HAL_CLIENT_H_ 19 20 #include <cinttypes> 21 #include <future> 22 #include <memory> 23 #include <shared_mutex> 24 #include <thread> 25 #include <unordered_map> 26 #include <vector> 27 28 #include <aidl/android/hardware/contexthub/BnContextHubCallback.h> 29 #include <aidl/android/hardware/contexthub/ContextHubMessage.h> 30 #include <aidl/android/hardware/contexthub/HostEndpointInfo.h> 31 #include <aidl/android/hardware/contexthub/IContextHub.h> 32 #include <aidl/android/hardware/contexthub/IContextHubCallback.h> 33 #include <aidl/android/hardware/contexthub/NanoappBinary.h> 34 #include <android/binder_manager.h> 35 #include <android/binder_process.h> 36 37 #include "hal_error.h" 38 39 namespace android::chre { 40 41 using ::aidl::android::hardware::contexthub::AsyncEventType; 42 using ::aidl::android::hardware::contexthub::BnContextHubCallback; 43 using ::aidl::android::hardware::contexthub::ContextHubInfo; 44 using ::aidl::android::hardware::contexthub::ContextHubMessage; 45 using ::aidl::android::hardware::contexthub::HostEndpointInfo; 46 using ::aidl::android::hardware::contexthub::IContextHub; 47 using ::aidl::android::hardware::contexthub::IContextHubCallback; 48 using ::aidl::android::hardware::contexthub::IContextHubDefault; 49 using ::aidl::android::hardware::contexthub::MessageDeliveryStatus; 50 using ::aidl::android::hardware::contexthub::NanoappBinary; 51 using ::aidl::android::hardware::contexthub::NanoappInfo; 52 using ::aidl::android::hardware::contexthub::NanSessionRequest; 53 using ::aidl::android::hardware::contexthub::Setting; 54 using ::ndk::ScopedAStatus; 55 56 /** 57 * A class connecting to CHRE Multiclient HAL via binder and taking care of 58 * binder (re)connection. 59 * 60 * <p>HalClient will replace the SocketClient that does the similar 61 * communication with CHRE but through a socket connection. 62 * 63 * <p>HalClient also maintains a set of connected host endpoints, using which 64 * it will enforce in the future that a message can only be sent to/from an 65 * endpoint id that is already connected to HAL. 66 * 67 * <p>When the binder connection to HAL is disconnected HalClient will have a 68 * death recipient re-establish the connection and reconnect the previously 69 * connected endpoints. In a rare case that CHRE also restarts at the same time, 70 * a client should rely on IContextHubCallback.handleContextHubAsyncEvent() to 71 * handle the RESTARTED event which is a signal that CHRE is up running. 72 */ 73 class HalClient { 74 public: 75 static constexpr int32_t kDefaultContextHubId = 0; 76 77 /** Callback interface for a background connection. */ 78 class BackgroundConnectionCallback { 79 public: 80 /** 81 * This function is called when the connection to CHRE HAL is finished. 82 * 83 * @param isConnected indicates whether CHRE HAL is successfully connected. 84 */ 85 virtual void onInitialization(bool isConnected) = 0; 86 virtual ~BackgroundConnectionCallback() = default; 87 }; 88 89 ~HalClient(); 90 91 /** 92 * Create a HalClient unique pointer used to communicate with CHRE HAL. 93 * 94 * @param callback a non-null callback. 95 * @param contextHubId context hub id that only 0 is supported at this moment. 96 * 97 * @return null pointer if the creation fails. 98 */ 99 static std::unique_ptr<HalClient> create( 100 const std::shared_ptr<IContextHubCallback> &callback, 101 int32_t contextHubId = kDefaultContextHubId); 102 103 /** 104 * Returns true if the multiclient HAL is available. 105 * 106 * <p>Multicleint HAL may not be available on a device that has CHRE enabled. 107 * In this situation, clients are expected to still use SocketClient to 108 * communicate with CHRE. 109 */ 110 static bool isServiceAvailable(); 111 112 /** A bug fix flag guarding the lock holding reduction change. */ 113 static bool reduceLockHolding(); 114 115 /** Returns true if this HalClient instance is connected to the HAL. */ isConnected()116 bool isConnected() { 117 return mContextHub != nullptr; 118 } 119 120 /** Connects to CHRE HAL synchronously. */ connect()121 bool connect() { 122 return initConnection() == HalError::SUCCESS; 123 } 124 125 /** Connects to CHRE HAL in background. */ connectInBackground(BackgroundConnectionCallback & callback)126 void connectInBackground(BackgroundConnectionCallback &callback) { 127 std::lock_guard<std::mutex> lock(mBackgroundConnectionFuturesLock); 128 // Policy std::launch::async is required to avoid lazy evaluation which can 129 // postpone the execution until get() of the future returned by std::async 130 // is called. 131 mBackgroundConnectionFutures.emplace_back( 132 std::async(std::launch::async, [&]() { 133 callback.onInitialization(initConnection() == HalError::SUCCESS); 134 })); 135 } 136 queryNanoapps()137 ScopedAStatus queryNanoapps() { 138 return callIfConnected([&](const std::shared_ptr<IContextHub> &hub) { 139 return hub->queryNanoapps(mContextHubId); 140 }); 141 } 142 143 /** Sends a message to a Nanoapp. */ 144 ScopedAStatus sendMessage(const ContextHubMessage &message); 145 146 /** Connects a host endpoint to CHRE. */ 147 ScopedAStatus connectEndpoint(const HostEndpointInfo &hostEndpointInfo); 148 149 /** Disconnects a host endpoint from CHRE. */ 150 ScopedAStatus disconnectEndpoint(char16_t hostEndpointId); 151 152 protected: 153 class HalClientCallback : public BnContextHubCallback { 154 public: HalClientCallback(const std::shared_ptr<IContextHubCallback> & callback,HalClient * halClient)155 explicit HalClientCallback( 156 const std::shared_ptr<IContextHubCallback> &callback, 157 HalClient *halClient) 158 : mCallback(callback), mHalClient(halClient) {} 159 handleNanoappInfo(const std::vector<NanoappInfo> & appInfo)160 ScopedAStatus handleNanoappInfo( 161 const std::vector<NanoappInfo> &appInfo) override { 162 return mCallback->handleNanoappInfo(appInfo); 163 } 164 handleContextHubMessage(const ContextHubMessage & msg,const std::vector<std::string> & msgContentPerms)165 ScopedAStatus handleContextHubMessage( 166 const ContextHubMessage &msg, 167 const std::vector<std::string> &msgContentPerms) override { 168 return mCallback->handleContextHubMessage(msg, msgContentPerms); 169 } 170 handleContextHubAsyncEvent(AsyncEventType event)171 ScopedAStatus handleContextHubAsyncEvent(AsyncEventType event) override { 172 if (event == AsyncEventType::RESTARTED) { 173 tryReconnectEndpoints(mHalClient); 174 } 175 return mCallback->handleContextHubAsyncEvent(event); 176 } 177 handleTransactionResult(int32_t transactionId,bool success)178 ScopedAStatus handleTransactionResult(int32_t transactionId, 179 bool success) override { 180 return mCallback->handleTransactionResult(transactionId, success); 181 } 182 handleNanSessionRequest(const NanSessionRequest & request)183 ScopedAStatus handleNanSessionRequest( 184 const NanSessionRequest &request) override { 185 return mCallback->handleNanSessionRequest(request); 186 } 187 handleMessageDeliveryStatus(char16_t hostEndPointId,const MessageDeliveryStatus & messageDeliveryStatus)188 ScopedAStatus handleMessageDeliveryStatus( 189 char16_t hostEndPointId, 190 const MessageDeliveryStatus &messageDeliveryStatus) override { 191 return mCallback->handleMessageDeliveryStatus(hostEndPointId, 192 messageDeliveryStatus); 193 } 194 getUuid(std::array<uint8_t,16> * outUuid)195 ScopedAStatus getUuid(std::array<uint8_t, 16> *outUuid) override { 196 return mCallback->getUuid(outUuid); 197 } 198 getName(std::string * outName)199 ScopedAStatus getName(std::string *outName) override { 200 return mCallback->getName(outName); 201 } 202 203 private: 204 std::shared_ptr<IContextHubCallback> mCallback; 205 HalClient *mHalClient; 206 }; 207 208 explicit HalClient(const std::shared_ptr<IContextHubCallback> &callback, 209 int32_t contextHubId = kDefaultContextHubId) mContextHubId(contextHubId)210 : mContextHubId(contextHubId) { 211 mCallback = ndk::SharedRefBase::make<HalClientCallback>(callback, this); 212 ABinderProcess_startThreadPool(); 213 mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient( 214 AIBinder_DeathRecipient_new(onHalDisconnected)); 215 mCallback->getName(&mClientName); 216 } 217 218 /** 219 * Initializes the connection to CHRE HAL. 220 */ 221 HalError initConnection(); 222 223 using HostEndpointId = char16_t; 224 225 const std::string kAidlServiceName = 226 std::string() + IContextHub::descriptor + "/default"; 227 228 /** The callback for a disconnected HAL binder connection. */ 229 static void onHalDisconnected(void *cookie); 230 231 /** Reconnect previously connected endpoints after CHRE or HAL restarts. */ 232 static void tryReconnectEndpoints(HalClient *halClient); 233 callIfConnected(const std::function<ScopedAStatus (const std::shared_ptr<IContextHub> & hub)> & func)234 ScopedAStatus callIfConnected( 235 const std::function< 236 ScopedAStatus(const std::shared_ptr<IContextHub> &hub)> &func) { 237 std::shared_ptr<IContextHub> hub; 238 { 239 // Make a copy of mContextHub so that even if HAL is disconnected and 240 // mContextHub is set to null the copy is kept as non-null to avoid crash. 241 // Still guard the copy by a shared lock to avoid torn writes. 242 std::shared_lock<std::shared_mutex> sharedLock(mConnectionLock); 243 hub = mContextHub; 244 } 245 if (hub == nullptr) { 246 return fromHalError(HalError::BINDER_DISCONNECTED); 247 } 248 return func(hub); 249 } 250 isEndpointConnected(HostEndpointId hostEndpointId)251 bool isEndpointConnected(HostEndpointId hostEndpointId) { 252 std::shared_lock<std::shared_mutex> sharedLock(mConnectedEndpointsLock); 253 return mConnectedEndpoints.find(hostEndpointId) != 254 mConnectedEndpoints.end(); 255 } 256 insertConnectedEndpoint(const HostEndpointInfo & hostEndpointInfo)257 void insertConnectedEndpoint(const HostEndpointInfo &hostEndpointInfo) { 258 std::lock_guard<std::shared_mutex> lockGuard(mConnectedEndpointsLock); 259 mConnectedEndpoints[hostEndpointInfo.hostEndpointId] = hostEndpointInfo; 260 } 261 removeConnectedEndpoint(HostEndpointId hostEndpointId)262 void removeConnectedEndpoint(HostEndpointId hostEndpointId) { 263 std::lock_guard<std::shared_mutex> lockGuard(mConnectedEndpointsLock); 264 mConnectedEndpoints.erase(hostEndpointId); 265 } 266 fromHalError(HalError errorCode)267 static ScopedAStatus fromHalError(HalError errorCode) { 268 return errorCode == HalError::SUCCESS 269 ? ScopedAStatus::ok() 270 : ScopedAStatus::fromServiceSpecificError( 271 static_cast<int32_t>(errorCode)); 272 } 273 274 // Multi-contextHub is not supported at this moment. 275 int32_t mContextHubId; 276 277 // The lock guarding mConnectedEndpoints. 278 std::shared_mutex mConnectedEndpointsLock; 279 std::unordered_map<HostEndpointId, HostEndpointInfo> mConnectedEndpoints{}; 280 281 // The lock guarding the init connection flow. 282 std::shared_mutex mConnectionLock; 283 std::shared_ptr<IContextHub> mContextHub; 284 285 // Handler of the binder disconnection event with HAL. 286 ndk::ScopedAIBinder_DeathRecipient mDeathRecipient; 287 288 std::shared_ptr<HalClientCallback> mCallback; 289 290 std::string mClientName; 291 292 // Lock guarding background connection threads. 293 std::mutex mBackgroundConnectionFuturesLock; 294 std::vector<std::future<void>> mBackgroundConnectionFutures; 295 }; 296 297 } // namespace android::chre 298 #endif // CHRE_HOST_HAL_CLIENT_H_