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