1 /* 2 * Copyright (C) 2022 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 #ifndef ANDROID_HARDWARE_CONTEXTHUB_COMMON_HAL_CLIENT_MANAGER_H_ 17 #define ANDROID_HARDWARE_CONTEXTHUB_COMMON_HAL_CLIENT_MANAGER_H_ 18 19 #include "chre/platform/shared/host_protocol_common.h" 20 #include "chre_host/fragmented_load_transaction.h" 21 #include "chre_host/log.h" 22 #include "chre_host/preloaded_nanoapp_loader.h" 23 #include "hal_client_id.h" 24 25 #include <sys/types.h> 26 #include <cstddef> 27 #include <unordered_map> 28 #include <unordered_set> 29 #include <utility> 30 31 #include <aidl/android/hardware/contexthub/ContextHubMessage.h> 32 #include <aidl/android/hardware/contexthub/IContextHub.h> 33 #include <aidl/android/hardware/contexthub/IContextHubCallback.h> 34 #include <android-base/thread_annotations.h> 35 36 using aidl::android::hardware::contexthub::ContextHubMessage; 37 using aidl::android::hardware::contexthub::HostEndpointInfo; 38 using aidl::android::hardware::contexthub::IContextHubCallback; 39 using android::chre::FragmentedLoadTransaction; 40 using HostEndpointId = uint16_t; 41 42 namespace android::hardware::contexthub::common::implementation { 43 44 /** 45 * A class managing clients for Context Hub HAL. 46 * 47 * A HAL client is defined as a user calling the IContextHub API. The main 48 * purpose of this class are: 49 * - to assign a unique HalClientId identifying each client; 50 * - to maintain a mapping between a HAL client and its states defined in 51 * HalClient; 52 * - to track the ongoing load/unload transactions 53 * 54 * There are 3 types of ids HalClientManager will track: client uuid, HAL client 55 * id and host endpoint id. 56 * - A uuid uniquely identifies a client when it registers its callback. 57 * After a callback is registered, a HAL client id is created and will be 58 * used to identify the client in the following API calls from/to it 59 * - A client id identifies a HAL client, which is the layer beneath the host 60 * apps, such as ContextHubService. Multiple apps with different host 61 * endpoint IDs can have the same client ID. 62 * - A host endpoint id, which is defined at 63 * hardware/interfaces/contexthub/aidl/android/hardware/contexthub/ContextHubMessage.aidl, 64 * identifies a host app that communicates with a HAL client. 65 * 66 * For a host endpoint connected to ContextHubService, its endpoint id is kept 67 *in the form below during the communication with CHRE. 68 * 69 * 0 1 70 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 71 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 72 * |0| endpoint_id | 73 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 74 * 75 * For vendor host endpoints, the client id is embedded into the endpoint id 76 * before sending a message to CHRE. When that happens, the highest bit is set 77 * to 1 and the endpoint id is mutated to the format below: 78 * 79 * 0 1 80 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 81 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 82 * |1| client_id |endpoint_id| 83 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 84 * 85 * Note that HalClientManager is not responsible for generating endpoint ids, 86 * which should be managed by HAL clients themselves. 87 */ 88 class HalClientManager { 89 public: 90 struct Client { 91 static constexpr pid_t kPidUnset = 0; 92 static constexpr char kNameUnset[]{"undefined"}; 93 ClientClient94 explicit Client(const std::string &uuid, const std::string &name, 95 const HalClientId clientId) 96 : Client(uuid, name, clientId, /* pid= */ kPidUnset, 97 /* callback= */ nullptr, 98 /* deathRecipientCookie= */ nullptr) {} 99 ClientClient100 explicit Client(std::string uuid, std::string name, 101 const HalClientId clientId, pid_t pid, 102 const std::shared_ptr<IContextHubCallback> &callback, 103 void *deathRecipientCookie) 104 : uuid{std::move(uuid)}, 105 name{std::move(name)}, 106 clientId{clientId}, 107 pid{pid}, 108 callback{callback}, 109 deathRecipientCookie{deathRecipientCookie} {} 110 111 /** Resets the client's fields except uuid and clientId. */ resetClient112 void reset(pid_t processId, 113 const std::shared_ptr<IContextHubCallback> &contextHubCallback, 114 void *cookie) { 115 pid = processId; 116 callback = contextHubCallback; 117 deathRecipientCookie = cookie; 118 endpointIds.clear(); 119 } 120 121 const std::string uuid; 122 std::string name; 123 const HalClientId clientId; 124 pid_t pid{}; 125 std::shared_ptr<IContextHubCallback> callback{}; 126 // cookie is used by the death recipient's linked callback 127 void *deathRecipientCookie{}; 128 std::unordered_set<HostEndpointId> endpointIds{}; 129 }; 130 131 // A snapshot of the nanoapp being loaded, for logging purpose. 132 struct PendingLoadNanoappInfo { PendingLoadNanoappInfoPendingLoadNanoappInfo133 PendingLoadNanoappInfo(uint64_t appId, size_t appSize, 134 uint32_t appVersion) { 135 this->appId = static_cast<int64_t>(appId); 136 this->appSize = appSize; 137 this->appVersion = static_cast<int32_t>(appVersion); 138 } 139 int64_t appId; 140 size_t appSize; 141 int32_t appVersion; 142 }; 143 144 // The endpoint id is from a vendor client if the highest bit is set to 1. 145 static constexpr HostEndpointId kVendorEndpointIdBitMask = 0x8000; 146 static constexpr uint8_t kNumOfBitsForEndpointId = 6; 147 148 using DeadClientUnlinker = std::function<bool( 149 const std::shared_ptr<IContextHubCallback> &callback, void *cookie)>; 150 151 explicit HalClientManager( 152 DeadClientUnlinker deadClientUnlinker, 153 const std::string &clientIdMappingFilePath, 154 const std::unordered_set<HalClientId> &reservedClientIds = {}); 155 virtual ~HalClientManager() = default; 156 157 /** Disable copy constructor and copy assignment to avoid duplicates. */ 158 HalClientManager(HalClientManager &) = delete; 159 void operator=(const HalClientManager &) = delete; 160 161 /** 162 * Gets the client id allocated to the current HAL client. 163 * 164 * The current HAL client is identified by its process id. If the process 165 * doesn't have any client id assigned, HalClientManager will create one 166 * mapped to its process id. 167 * 168 * @param pid process id of the current client 169 * 170 * @return client id assigned to the calling process, or 171 * ::chre::kHostClientIdUnspecified if the process id is not found. 172 */ 173 HalClientId getClientId(pid_t pid); 174 175 /** 176 * Gets the callback for the current HAL client identified by the clientId. 177 * 178 * @return callback previously registered. nullptr is returned if the clientId 179 * is not found. 180 */ 181 std::shared_ptr<IContextHubCallback> getCallback(HalClientId clientId); 182 183 /** 184 * Registers a IContextHubCallback function mapped to the current client's 185 * client id. @p deathRecipient and @p deathRecipientCookie are used to unlink 186 * the previous registered callback for the same client, if any. 187 * 188 * @param pid process id of the current client 189 * @param callback a function incurred to handle the client death event. 190 * @param deathRecipientCookie the data used by the callback. 191 * 192 * @return true if success, otherwise false. 193 */ 194 bool registerCallback(pid_t pid, 195 const std::shared_ptr<IContextHubCallback> &callback, 196 void *deathRecipientCookie); 197 198 /** 199 * Registers a FragmentedLoadTransaction for the current HAL client. 200 * 201 * At this moment only one active transaction, either load or unload, is 202 * supported. 203 * 204 * @param pid process id of the current client 205 * @param transaction the transaction being registered 206 * 207 * @return true if success, otherwise false. 208 */ 209 bool registerPendingLoadTransaction( 210 pid_t pid, std::unique_ptr<chre::FragmentedLoadTransaction> transaction); 211 212 /** 213 * Returns a snapshot of the nanoapp being loaded if possible. 214 */ 215 std::optional<PendingLoadNanoappInfo> 216 getNanoappInfoFromPendingLoadTransaction(HalClientId clientId, 217 uint32_t transactionId, 218 uint32_t currentFragmentId); 219 220 /** 221 * Clears the pending load transaction. 222 * 223 * This function is called to proactively clear out a pending load transaction 224 * that is not timed out yet. 225 * 226 */ 227 void resetPendingLoadTransaction(); 228 229 /** 230 * Gets the next FragmentedLoadRequest from PendingLoadTransaction if it's 231 * available. 232 * 233 * This function assumes mPendingLoadTransaction has a valid value. So either 234 * registerPendingLoadTransaction or getNanoappInfoFromPendingLoadTransaction 235 * should be called to make sure this precondition is satisfied before calling 236 * this function. 237 * 238 * @return an optional FragmentedLoadRequest, std::nullopt if unavailable. 239 */ 240 std::optional<chre::FragmentedLoadRequest> getNextFragmentedLoadRequest(); 241 242 /** 243 * Registers the current HAL client as having a pending unload transaction. 244 * 245 * At this moment only one active transaction, either load or unload, is 246 * supported. 247 * 248 * @param pid process id of the current client 249 * @param transaction the transaction being registered 250 * @param nanoappId id of the nanoapp 251 * 252 * @return true if success, otherwise false. 253 */ 254 bool registerPendingUnloadTransaction(pid_t pid, uint32_t transactionId, 255 int64_t nanoappId); 256 257 /** 258 * Clears the pending unload transaction. 259 * 260 * This function is called to proactively clear out a pending unload 261 * transaction that is not timed out yet. @p clientId and @p 262 * transactionId must match the existing pending transaction. 263 * 264 * @param clientId the client id of the caller. 265 * @param transactionId unique id of the transaction. 266 * 267 * @return the nanoapp id of the pending unload transaction being cleared for 268 * logging purpose if a transaction is matched. 269 */ 270 std::optional<int64_t> resetPendingUnloadTransaction(HalClientId clientId, 271 uint32_t transactionId); 272 273 /** 274 * Registers an endpoint id when it is connected to HAL. 275 * 276 * @param pid process id of the current HAL client 277 * @param endpointId the endpointId being registered 278 * 279 * @return true if success, otherwise false. 280 */ 281 bool registerEndpointId(pid_t pid, const HostEndpointId &endpointId); 282 283 /** 284 * Removes an endpoint id when it is disconnected to HAL. 285 * 286 * @param pid process id of the current HAL client 287 * @param endpointId the endpointId being registered 288 * 289 * @return true if success, otherwise false. 290 */ 291 bool removeEndpointId(pid_t pid, const HostEndpointId &endpointId); 292 293 /** 294 * Mutates the endpoint id if the hal client is not the framework service. 295 * 296 * @param pid process id of the current HAL client 297 * @param endpointId the endpointId being registered 298 * 299 * @return true if success, otherwise false. 300 */ 301 bool mutateEndpointIdFromHostIfNeeded(pid_t pid, HostEndpointId &endpointId); 302 303 /** Returns the original endpoint id sent by the host client. */ 304 static HostEndpointId convertToOriginalEndpointId( 305 const HostEndpointId &endpointId); 306 307 /** 308 * Gets all the connected endpoints for the client identified by the @p pid. 309 * 310 * @return the pointer to the endpoint id set if the client is identifiable, 311 * otherwise nullptr. 312 */ 313 const std::unordered_set<HostEndpointId> *getAllConnectedEndpoints(pid_t pid); 314 315 /** Sends a message to every connected endpoints. */ 316 void sendMessageForAllCallbacks( 317 const ContextHubMessage &message, 318 const std::vector<std::string> &messageParams); 319 320 std::shared_ptr<IContextHubCallback> getCallbackForEndpoint( 321 HostEndpointId mutatedEndpointId); 322 323 /** 324 * Handles the client death event. 325 * 326 * @param pid of the client that loses the binder connection to the HAL. 327 */ 328 void handleClientDeath(pid_t pid); 329 330 /** Handles CHRE restart event. */ 331 void handleChreRestart(); 332 333 /** Dumps various states maintained for debugging purpose. */ 334 std::string debugDump(); 335 336 protected: 337 static constexpr char kSystemServerUuid[] = 338 "9a17008d6bf1445a90116d21bd985b6c"; 339 /** Pseudo name shared among vendor clients when uuid is unavailable. */ 340 static constexpr char kVendorClientUuid[] = "vendor-client"; 341 342 /** Keys used in chre_hal_clients.json. */ 343 static constexpr char kJsonClientId[] = "ClientId"; 344 static constexpr char kJsonUuid[] = "uuid"; 345 static constexpr char kJsonName[] = "name"; 346 347 /** Max time allowed for a load/unload transaction to take. */ 348 static constexpr int64_t kTransactionTimeoutThresholdMs = 5000; // 5 seconds 349 350 static constexpr HostEndpointId kMaxVendorEndpointId = 351 (1 << kNumOfBitsForEndpointId) - 1; 352 353 struct PendingTransaction { PendingTransactionPendingTransaction354 PendingTransaction(HalClientId clientId, uint32_t transactionId, 355 int64_t registeredTimeMs) { 356 this->clientId = clientId; 357 this->transactionId = transactionId; 358 this->registeredTimeMs = registeredTimeMs; 359 } 360 HalClientId clientId; 361 uint32_t transactionId; 362 int64_t registeredTimeMs; 363 }; 364 365 /** 366 * PendingLoadTransaction tracks ongoing load transactions. 367 */ 368 struct PendingLoadTransaction : public PendingTransaction { PendingLoadTransactionPendingLoadTransaction369 PendingLoadTransaction( 370 HalClientId clientId, int64_t registeredTimeMs, 371 uint32_t currentFragmentId, 372 std::unique_ptr<chre::FragmentedLoadTransaction> transaction) 373 : PendingTransaction(clientId, transaction->getTransactionId(), 374 registeredTimeMs) { 375 this->currentFragmentId = currentFragmentId; 376 this->transaction = std::move(transaction); 377 } 378 uint32_t currentFragmentId; // the fragment id being sent out. 379 std::unique_ptr<chre::FragmentedLoadTransaction> transaction; 380 getNanoappInfoPendingLoadTransaction381 [[nodiscard]] PendingLoadNanoappInfo getNanoappInfo() const { 382 return PendingLoadNanoappInfo{transaction->getNanoappId(), 383 transaction->getNanoappTotalSize(), 384 transaction->getNanoappVersion()}; 385 } 386 toStringPendingLoadTransaction387 [[nodiscard]] std::string toString() const { 388 using android::internal::ToString; 389 return "[Load transaction: client id " + ToString(clientId) + 390 ", Transaction id " + ToString(transaction->getTransactionId()) + 391 ", fragment id " + ToString(currentFragmentId) + "]"; 392 } 393 }; 394 395 struct PendingUnloadTransaction : public PendingTransaction { PendingUnloadTransactionPendingUnloadTransaction396 PendingUnloadTransaction(HalClientId clientId, uint32_t transactionId, 397 int64_t registeredTimeMs, int64_t appId) 398 : PendingTransaction(clientId, transactionId, registeredTimeMs), 399 nanoappId{appId} {} 400 int64_t nanoappId; 401 }; 402 403 /** 404 * Creates a client id to uniquely identify a HAL client. 405 * 406 * A file is maintained on the device for the mappings between client names 407 * and client ids so that if a client has connected to HAL before the same 408 * client id is always assigned to it. 409 */ 410 bool createClient(const std::string &uuid, pid_t pid, 411 const std::shared_ptr<IContextHubCallback> &callback, 412 void *deathRecipientCookie) REQUIRES(mLock); 413 414 /** 415 * Update @p mNextClientId to be the next available one. 416 * 417 * @return true if success, otherwise false. 418 */ 419 bool updateNextClientId() REQUIRES(mLock); 420 421 /** 422 * Returns true if @p clientId and @p transactionId match the 423 * corresponding values in @p transaction. 424 */ isPendingTransactionMatched(HalClientId clientId,uint32_t transactionId,const std::optional<PendingTransaction> & transaction)425 static bool isPendingTransactionMatched( 426 HalClientId clientId, uint32_t transactionId, 427 const std::optional<PendingTransaction> &transaction) { 428 return transaction.has_value() && transaction->clientId == clientId && 429 transaction->transactionId == transactionId; 430 } 431 432 /** 433 * Checks if the transaction registration is allowed and clears out any stale 434 * pending transaction if possible. 435 * 436 * This function is called when registering a new transaction. The reason that 437 * we still proceed when there is already a pending transaction is because we 438 * don't want a stale one, for whatever reason, to block future transactions. 439 * However, every transaction is guaranteed to have up to 440 * kTransactionTimeoutThresholdMs to finish. 441 * 442 * @param clientId id of the client trying to register the transaction 443 * 444 * @return true if registration is allowed, otherwise false. 445 */ 446 bool isNewTransactionAllowed(HalClientId clientId) REQUIRES(mLock); 447 448 /** Returns true if the endpoint id is within the accepted range. */ isValidEndpointId(const Client * client,const HostEndpointId & endpointId)449 [[nodiscard]] static inline bool isValidEndpointId( 450 const Client *client, const HostEndpointId &endpointId) { 451 return client->uuid == kSystemServerUuid || 452 endpointId <= kMaxVendorEndpointId; 453 } 454 455 /** Updates the mapping file. */ 456 void updateClientIdMappingFile() REQUIRES(mLock); 457 458 /** 459 * Gets the uuid of a client from its callback. 460 * 461 * <p> IContextHubCallback versions before 3 lack the getUuid() API. For 462 * compatibility, the first client connecting to HAL is assumed to be the 463 * system server, and kVendorClientUuid is returned thereafter. 464 * 465 * @warning: 466 * The backward compatibility creates a race condition that a client 467 * connecting before the system server will be treated as the system server, 468 * potentially breaking endpoint mutation logic. Therefore this compatibility 469 * workaround is mainly for manually executed command-line tools used after 470 * system fully boots up. 471 */ 472 std::string getUuid(const std::shared_ptr<IContextHubCallback> &callback) 473 REQUIRES(mLock); 474 475 Client *getClientByField( 476 const std::function<bool(const Client &client)> &fieldMatcher) 477 REQUIRES(mLock); 478 479 Client *getClientByClientId(HalClientId clientId) REQUIRES(mLock); 480 481 Client *getClientByUuid(const std::string &uuid) REQUIRES(mLock); 482 483 Client *getClientByProcessId(pid_t pid) REQUIRES(mLock); 484 485 DeadClientUnlinker mDeadClientUnlinker{}; 486 487 std::string mClientMappingFilePath; 488 489 // next available client id 490 HalClientId mNextClientId = ::chre::kHostClientIdUnspecified; 491 492 // reserved client ids that will not be used 493 std::unordered_set<HalClientId> mReservedClientIds; 494 495 // The lock guarding the access to clients' states and pending transactions 496 std::mutex mLock; 497 498 std::vector<Client> mClients GUARDED_BY(mLock); 499 500 // States tracking pending transactions 501 std::optional<PendingLoadTransaction> mPendingLoadTransaction 502 GUARDED_BY(mLock) = std::nullopt; 503 std::optional<PendingUnloadTransaction> mPendingUnloadTransaction 504 GUARDED_BY(mLock) = std::nullopt; 505 }; 506 } // namespace android::hardware::contexthub::common::implementation 507 508 #endif // ANDROID_HARDWARE_CONTEXTHUB_COMMON_HAL_CLIENT_MANAGER_H_ 509