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 /** Returns true if this HalClient instance is connected to the HAL. */ isConnected()113 bool isConnected() { 114 return mIsHalConnected; 115 } 116 117 /** Connects to CHRE HAL synchronously. */ connect()118 bool connect() { 119 return initConnection() == HalError::SUCCESS; 120 } 121 122 /** Connects to CHRE HAL in background. */ connectInBackground(BackgroundConnectionCallback & callback)123 void connectInBackground(BackgroundConnectionCallback &callback) { 124 std::lock_guard<std::mutex> lock(mBackgroundConnectionFuturesLock); 125 // Policy std::launch::async is required to avoid lazy evaluation which can 126 // postpone the execution until get() of the future returned by std::async 127 // is called. 128 mBackgroundConnectionFutures.emplace_back( 129 std::async(std::launch::async, [&]() { 130 callback.onInitialization(initConnection() == HalError::SUCCESS); 131 })); 132 } 133 queryNanoapps()134 ScopedAStatus queryNanoapps() { 135 return callIfConnected([&](const std::shared_ptr<IContextHub> &hub) { 136 return hub->queryNanoapps(mContextHubId); 137 }); 138 } 139 140 /** Sends a message to a Nanoapp. */ 141 ScopedAStatus sendMessage(const ContextHubMessage &message); 142 143 /** Connects a host endpoint to CHRE. */ 144 ScopedAStatus connectEndpoint(const HostEndpointInfo &hostEndpointInfo); 145 146 /** Disconnects a host endpoint from CHRE. */ 147 ScopedAStatus disconnectEndpoint(char16_t hostEndpointId); 148 149 protected: 150 class HalClientCallback : public BnContextHubCallback { 151 public: HalClientCallback(const std::shared_ptr<IContextHubCallback> & callback,HalClient * halClient)152 explicit HalClientCallback( 153 const std::shared_ptr<IContextHubCallback> &callback, 154 HalClient *halClient) 155 : mCallback(callback), mHalClient(halClient) {} 156 handleNanoappInfo(const std::vector<NanoappInfo> & appInfo)157 ScopedAStatus handleNanoappInfo( 158 const std::vector<NanoappInfo> &appInfo) override { 159 return mCallback->handleNanoappInfo(appInfo); 160 } 161 handleContextHubMessage(const ContextHubMessage & msg,const std::vector<std::string> & msgContentPerms)162 ScopedAStatus handleContextHubMessage( 163 const ContextHubMessage &msg, 164 const std::vector<std::string> &msgContentPerms) override { 165 return mCallback->handleContextHubMessage(msg, msgContentPerms); 166 } 167 handleContextHubAsyncEvent(AsyncEventType event)168 ScopedAStatus handleContextHubAsyncEvent(AsyncEventType event) override { 169 if (event == AsyncEventType::RESTARTED) { 170 tryReconnectEndpoints(mHalClient); 171 } 172 return mCallback->handleContextHubAsyncEvent(event); 173 } 174 handleTransactionResult(int32_t transactionId,bool success)175 ScopedAStatus handleTransactionResult(int32_t transactionId, 176 bool success) override { 177 return mCallback->handleTransactionResult(transactionId, success); 178 } 179 handleNanSessionRequest(const NanSessionRequest & request)180 ScopedAStatus handleNanSessionRequest( 181 const NanSessionRequest &request) override { 182 return mCallback->handleNanSessionRequest(request); 183 } 184 handleMessageDeliveryStatus(char16_t hostEndPointId,const MessageDeliveryStatus & messageDeliveryStatus)185 ScopedAStatus handleMessageDeliveryStatus( 186 char16_t hostEndPointId, 187 const MessageDeliveryStatus &messageDeliveryStatus) override { 188 return mCallback->handleMessageDeliveryStatus(hostEndPointId, 189 messageDeliveryStatus); 190 } 191 getUuid(std::array<uint8_t,16> * outUuid)192 ScopedAStatus getUuid(std::array<uint8_t, 16> *outUuid) override { 193 return mCallback->getUuid(outUuid); 194 } 195 getName(std::string * outName)196 ScopedAStatus getName(std::string *outName) override { 197 return mCallback->getName(outName); 198 } 199 200 private: 201 std::shared_ptr<IContextHubCallback> mCallback; 202 HalClient *mHalClient; 203 }; 204 205 explicit HalClient(const std::shared_ptr<IContextHubCallback> &callback, 206 int32_t contextHubId = kDefaultContextHubId) mContextHubId(contextHubId)207 : mContextHubId(contextHubId) { 208 mCallback = ndk::SharedRefBase::make<HalClientCallback>(callback, this); 209 ABinderProcess_startThreadPool(); 210 mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient( 211 AIBinder_DeathRecipient_new(onHalDisconnected)); 212 mCallback->getName(&mClientName); 213 } 214 215 /** 216 * Initializes the connection to CHRE HAL. 217 */ 218 HalError initConnection(); 219 220 using HostEndpointId = char16_t; 221 222 const std::string kAidlServiceName = 223 std::string() + IContextHub::descriptor + "/default"; 224 225 /** The callback for a disconnected HAL binder connection. */ 226 static void onHalDisconnected(void *cookie); 227 228 /** Reconnect previously connected endpoints after CHRE or HAL restarts. */ 229 static void tryReconnectEndpoints(HalClient *halClient); 230 callIfConnected(const std::function<ScopedAStatus (const std::shared_ptr<IContextHub> & hub)> & func)231 ScopedAStatus callIfConnected( 232 const std::function< 233 ScopedAStatus(const std::shared_ptr<IContextHub> &hub)> &func) { 234 std::shared_ptr<IContextHub> hub; 235 { 236 // Make a copy of mContextHub so that even if HAL is disconnected and 237 // mContextHub is set to null the copy is kept as non-null to avoid crash. 238 // Still guard the copy by a shared lock to avoid torn writes. 239 std::shared_lock<std::shared_mutex> sharedLock(mConnectionLock); 240 hub = mContextHub; 241 } 242 if (hub == nullptr) { 243 return fromHalError(HalError::BINDER_DISCONNECTED); 244 } 245 return func(hub); 246 } 247 isEndpointConnected(HostEndpointId hostEndpointId)248 bool isEndpointConnected(HostEndpointId hostEndpointId) { 249 std::shared_lock<std::shared_mutex> sharedLock(mConnectedEndpointsLock); 250 return mConnectedEndpoints.find(hostEndpointId) != 251 mConnectedEndpoints.end(); 252 } 253 insertConnectedEndpoint(const HostEndpointInfo & hostEndpointInfo)254 void insertConnectedEndpoint(const HostEndpointInfo &hostEndpointInfo) { 255 std::lock_guard<std::shared_mutex> lockGuard(mConnectedEndpointsLock); 256 mConnectedEndpoints[hostEndpointInfo.hostEndpointId] = hostEndpointInfo; 257 } 258 removeConnectedEndpoint(HostEndpointId hostEndpointId)259 void removeConnectedEndpoint(HostEndpointId hostEndpointId) { 260 std::lock_guard<std::shared_mutex> lockGuard(mConnectedEndpointsLock); 261 mConnectedEndpoints.erase(hostEndpointId); 262 } 263 fromHalError(HalError errorCode)264 static ScopedAStatus fromHalError(HalError errorCode) { 265 return errorCode == HalError::SUCCESS 266 ? ScopedAStatus::ok() 267 : ScopedAStatus::fromServiceSpecificError( 268 static_cast<int32_t>(errorCode)); 269 } 270 271 // Multi-contextHub is not supported at this moment. 272 int32_t mContextHubId; 273 274 // The lock guarding mConnectedEndpoints. 275 std::shared_mutex mConnectedEndpointsLock; 276 std::unordered_map<HostEndpointId, HostEndpointInfo> mConnectedEndpoints{}; 277 278 // The lock guarding the init connection flow. 279 std::shared_mutex mConnectionLock; 280 std::shared_ptr<IContextHub> mContextHub; 281 std::atomic_bool mIsHalConnected = false; 282 283 // Handler of the binder disconnection event with HAL. 284 ndk::ScopedAIBinder_DeathRecipient mDeathRecipient; 285 286 std::shared_ptr<HalClientCallback> mCallback; 287 288 std::string mClientName; 289 290 // Lock guarding background connection threads. 291 std::mutex mBackgroundConnectionFuturesLock; 292 std::vector<std::future<void>> mBackgroundConnectionFutures; 293 }; 294 295 } // namespace android::chre 296 #endif // CHRE_HOST_HAL_CLIENT_H_