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