xref: /aosp_15_r20/system/chre/host/hal_generic/common/hal_client_manager.h (revision 84e339476a462649f82315436d70fd732297a399)
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