• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <aidl/android/hardware/contexthub/ContextHubMessage.h>
20 #include <aidl/android/hardware/contexthub/IContextHub.h>
21 #include <aidl/android/hardware/contexthub/IContextHubCallback.h>
22 #include <chre_host/fragmented_load_transaction.h>
23 #include <chre_host/preloaded_nanoapp_loader.h>
24 #include <sys/types.h>
25 #include <cstddef>
26 #include <unordered_map>
27 #include <unordered_set>
28 #include "chre_host/log.h"
29 #include "hal_client_id.h"
30 
31 using aidl::android::hardware::contexthub::ContextHubMessage;
32 using aidl::android::hardware::contexthub::HostEndpointInfo;
33 using aidl::android::hardware::contexthub::IContextHubCallback;
34 using android::chre::FragmentedLoadTransaction;
35 using HostEndpointId = uint16_t;
36 
37 namespace android::hardware::contexthub::common::implementation {
38 
39 /**
40  * A class managing clients for Context Hub HAL.
41  *
42  * A HAL client is defined as a user calling the IContextHub API. The main
43  * purpose of this class are:
44  *   - to assign a unique HalClientId identifying each client;
45  *   - to maintain a mapping between client ids and HalClientInfos;
46  *   - to maintain a mapping between client ids and their endpoint ids.
47  *
48  * There are two types of ids HalClientManager will track, host endpoint id and
49  * client id. A host endpoint id, which is defined at
50  * hardware/interfaces/contexthub/aidl/android/hardware/contexthub/ContextHubMessage.aidl,
51  * identifies a host app that communicates with a HAL client. A client id
52  * identifies a HAL client, which is the layer beneath the host apps, such as
53  * ContextHubService. Multiple apps with different host endpoint IDs can have
54  * the same client ID.
55  *
56  * For a host endpoint connected to ContextHubService, its endpoint id is kept
57  *in the form below during the communication with CHRE.
58  *
59  *  0                   1
60  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
61  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62  * |0|      endpoint_id            |
63  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64  *
65  * For vendor host endpoints,  the client id is embedded into the endpoint id
66  * before sending a message to CHRE. When that happens, the highest bit is set
67  * to 1 and the endpoint id is mutated to the format below:
68  *
69  *  0                   1
70  *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
71  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72  * |1|   client_id     |endpoint_id|
73  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74  *
75  * Note that HalClientManager is not responsible for generating endpoint ids,
76  * which should be managed by HAL clients themselves.
77  */
78 class HalClientManager {
79  public:
80   HalClientManager();
81   virtual ~HalClientManager() = default;
82 
83   /** Disable copy constructor and copy assignment to avoid duplicates. */
84   HalClientManager(HalClientManager &) = delete;
85   void operator=(const HalClientManager &) = delete;
86 
87   /**
88    * Gets the client id allocated to the current HAL client.
89    *
90    * The current HAL client is identified by its process id, which is retrieved
91    * by calling AIBinder_getCallingPid(). If the process doesn't have any client
92    * id assigned, HalClientManager will create one mapped to its process id.
93    *
94    * @return client id assigned to the calling process, or kDefaultHalClientId
95    * if the process id is not found.
96    */
97   HalClientId getClientId();
98 
99   /**
100    * Gets the callback for the current HAL client identified by the clientId.
101    *
102    * @return callback previously registered. nullptr is returned if the clientId
103    * is not found.
104    */
105   std::shared_ptr<IContextHubCallback> getCallback(HalClientId clientId);
106 
107   /**
108    * Registers a IContextHubCallback function mapped to the current client's
109    * client id. @p deathRecipient and @p deathRecipientCookie are used to unlink
110    * the previous registered callback for the same client, if any.
111    *
112    * @param callback a function incurred to handle the client death event.
113    * @param deathRecipient a handle on the death notification.
114    * @param deathRecipientCookie the data used by the callback.
115    *
116    * @return true if success, otherwise false.
117    */
118   bool registerCallback(
119       const std::shared_ptr<IContextHubCallback> &callback,
120       const ndk::ScopedAIBinder_DeathRecipient &deathRecipient,
121       void *deathRecipientCookie);
122 
123   /**
124    * Registers a FragmentedLoadTransaction for the current HAL client.
125    *
126    * At this moment only one active transaction, either load or unload, is
127    * supported.
128    *
129    * @return true if success, otherwise false.
130    */
131   bool registerPendingLoadTransaction(
132       std::unique_ptr<FragmentedLoadTransaction> transaction);
133 
134   /**
135    * Returns true if the load transaction matches the arguments provided.
136    */
isPendingLoadTransactionExpected(HalClientId clientId,uint32_t transactionId,uint32_t currentFragmentId)137   bool isPendingLoadTransactionExpected(HalClientId clientId,
138                                         uint32_t transactionId,
139                                         uint32_t currentFragmentId) {
140     const std::lock_guard<std::mutex> lock(mLock);
141     return isPendingLoadTransactionMatchedLocked(clientId, transactionId,
142                                                  currentFragmentId);
143   }
144 
145   /**
146    * Clears the pending load transaction.
147    *
148    * This function is called to proactively clear out a pending load transaction
149    * that is not timed out yet.
150    *
151    */
152   void resetPendingLoadTransaction();
153 
154   /**
155    * Gets the next FragmentedLoadRequest from PendingLoadTransaction if it's
156    * available.
157    *
158    * @return an optional FragmentedLoadRequest, std::nullopt if unavailable.
159    */
160 
161   std::optional<chre::FragmentedLoadRequest> getNextFragmentedLoadRequest();
162 
163   /**
164    * Registers the current HAL client as having a pending unload transaction.
165    *
166    * At this moment only one active transaction, either load or unload, is
167    * supported.
168    *
169    * @return true if success, otherwise false.
170    */
171   bool registerPendingUnloadTransaction(uint32_t transactionId);
172 
173   /**
174    * Clears the pending unload transaction.
175    *
176    * This function is called to proactively clear out a pending unload
177    * transaction that is not timed out yet. @p clientId and @p
178    * transactionId must match the existing pending transaction.
179    *
180    * @param clientId the client id of the caller.
181    * @param transactionId unique id of the transaction.
182    *
183    * @return true if the pending transaction is cleared, otherwise false.
184    */
185   bool resetPendingUnloadTransaction(HalClientId clientId,
186                                      uint32_t transactionId);
187 
188   /**
189    * Registers an endpoint id when it is connected to HAL.
190    *
191    * @return true if success, otherwise false.
192    */
193   bool registerEndpointId(const HostEndpointId &endpointId);
194 
195   /**
196    * Removes an endpoint id when it is disconnected to HAL.
197    *
198    * @return true if success, otherwise false.
199    */
200   bool removeEndpointId(const HostEndpointId &endpointId);
201 
202   /**
203    * Mutates the endpoint id if the hal client is not the framework service.
204    *
205    * @return true if success, otherwise false.
206    */
207   bool mutateEndpointIdFromHostIfNeeded(const pid_t &pid,
208                                         HostEndpointId &endpointId);
209 
210   /** Returns the original endpoint id sent by the host client. */
211   static HostEndpointId convertToOriginalEndpointId(
212       const HostEndpointId &endpointId);
213 
214   /**
215    * Gets all the connected endpoints for the client identified by the pid.
216    *
217    * @return the pointer to the endpoint id set if the client is identifiable,
218    * otherwise nullptr.
219    */
220   const std::unordered_set<HostEndpointId> *getAllConnectedEndpoints(pid_t pid);
221 
222   /** Sends a message to every connected endpoints. */
223   void sendMessageForAllCallbacks(
224       const ContextHubMessage &message,
225       const std::vector<std::string> &messageParams);
226 
227   std::shared_ptr<IContextHubCallback> getCallbackForEndpoint(
228       const HostEndpointId &endpointId);
229 
230   /**
231    * Handles the client death event.
232    *
233    * @param pid of the client that loses the binder connection to the HAL.
234    * @param deathRecipient to be unlinked with the client's callback
235    */
236   void handleClientDeath(
237       pid_t pid, const ndk::ScopedAIBinder_DeathRecipient &deathRecipient);
238 
239   /** Handles CHRE restart event. */
240   void handleChreRestart();
241 
242  protected:
243   static constexpr char kSystemServerName[] = "system_server";
244   static constexpr char kClientMappingFilePath[] =
245       "/data/vendor/chre/chre_hal_clients.json";
246   static constexpr char kJsonClientId[] = "ClientId";
247   static constexpr char kJsonProcessName[] = "ProcessName";
248   static constexpr int64_t kTransactionTimeoutThresholdMs = 5000;  // 5 seconds
249   static constexpr uint8_t kNumOfBitsForEndpointId = 6;
250   static constexpr HostEndpointId kMaxVendorEndpointId =
251       (1 << kNumOfBitsForEndpointId) - 1;
252   // The endpoint id is from a vendor client if the highest bit is set to 1.
253   static constexpr HostEndpointId kVendorEndpointIdBitMask = 0x8000;
254 
255   struct HalClientInfo {
HalClientInfoHalClientInfo256     explicit HalClientInfo(const std::shared_ptr<IContextHubCallback> &callback,
257                            void *cookie) {
258       this->callback = callback;
259       this->deathRecipientCookie = cookie;
260     }
261     HalClientInfo() = default;
262     std::shared_ptr<IContextHubCallback> callback;
263     // cookie is used by the death recipient's linked callback
264     void *deathRecipientCookie{};
265     std::unordered_set<HostEndpointId> endpointIds{};
266   };
267 
268   struct PendingTransaction {
PendingTransactionPendingTransaction269     PendingTransaction(HalClientId clientId, uint32_t transactionId,
270                        int64_t registeredTimeMs) {
271       this->clientId = clientId;
272       this->transactionId = transactionId;
273       this->registeredTimeMs = registeredTimeMs;
274     }
275     HalClientId clientId;
276     uint32_t transactionId;
277     int64_t registeredTimeMs;
278   };
279 
280   /**
281    * PendingLoadTransaction tracks ongoing load transactions.
282    */
283   struct PendingLoadTransaction : public PendingTransaction {
PendingLoadTransactionPendingLoadTransaction284     PendingLoadTransaction(
285         HalClientId clientId, int64_t registeredTimeMs,
286         uint32_t currentFragmentId,
287         std::unique_ptr<chre::FragmentedLoadTransaction> transaction)
288         : PendingTransaction(clientId, transaction->getTransactionId(),
289                              registeredTimeMs) {
290       this->currentFragmentId = currentFragmentId;
291       this->transaction = std::move(transaction);
292     }
293     uint32_t currentFragmentId;  // the fragment id being sent out.
294     std::unique_ptr<chre::FragmentedLoadTransaction> transaction;
295 
toStringPendingLoadTransaction296     [[nodiscard]] std::string toString() const {
297       using android::internal::ToString;
298       return "[Load transaction: client id " + ToString(clientId) +
299              ", Transaction id " + ToString(transaction->getTransactionId()) +
300              ", fragment id " + ToString(currentFragmentId) + "]";
301     }
302   };
303 
304   /**
305    * Creates a client id to uniquely identify a HAL client.
306    *
307    * A file is maintained on the device for the mappings between client names
308    * and client ids so that if a client has connected to HAL before the same
309    * client id is always assigned to it.
310    *
311    * mLock must be held when this function is called.
312    *
313    * @param processName the process name of the client
314    */
315   virtual std::optional<HalClientId> createClientIdLocked(
316       const std::string &processName);
317 
318   /**
319    * Returns true if @p clientId and @p transactionId match the
320    * corresponding values in @p transaction.
321    *
322    * mLock must be held when this function is called.
323    */
isPendingTransactionMatchedLocked(HalClientId clientId,uint32_t transactionId,const std::optional<PendingTransaction> & transaction)324   static bool isPendingTransactionMatchedLocked(
325       HalClientId clientId, uint32_t transactionId,
326       const std::optional<PendingTransaction> &transaction) {
327     return transaction.has_value() && transaction->clientId == clientId &&
328            transaction->transactionId == transactionId;
329   }
330 
331   /**
332    * Returns true if the load transaction is expected.
333    *
334    * mLock must be held when this function is called.
335    */
336   bool isPendingLoadTransactionMatchedLocked(HalClientId clientId,
337                                              uint32_t transactionId,
338                                              uint32_t currentFragmentId);
339 
340   /**
341    * Checks if the transaction registration is allowed and clears out any stale
342    * pending transaction if possible.
343    *
344    * This function is called when registering a new transaction. The reason that
345    * we still proceed when there is already a pending transaction is because we
346    * don't want a stale one, for whatever reason, to block future transactions.
347    * However, every transaction is guaranteed to have up to
348    * kTransactionTimeoutThresholdMs to finish.
349    *
350    * mLock must be held when this function is called.
351    *
352    * @param clientId id of the client trying to register the transaction
353    *
354    * @return true if registration is allowed, otherwise false.
355    */
356   bool isNewTransactionAllowedLocked(HalClientId clientId);
357 
358   /**
359    * Returns true if the clientId is being used.
360    *
361    * mLock must be held when this function is called.
362    */
isAllocatedClientIdLocked(HalClientId clientId)363   inline bool isAllocatedClientIdLocked(HalClientId clientId) {
364     return mClientIdsToClientInfo.find(clientId) !=
365            mClientIdsToClientInfo.end();
366   }
367 
368   /**
369    * Returns true if the pid is being used to identify a client.
370    *
371    * mLock must be held when this function is called.
372    */
isKnownPIdLocked(pid_t pid)373   inline bool isKnownPIdLocked(pid_t pid) {
374     return mPIdsToClientIds.find(pid) != mPIdsToClientIds.end();
375   }
376 
377   /** Returns true if the endpoint id is within the accepted range. */
isValidEndpointId(const HalClientId & clientId,const HostEndpointId & endpointId)378   [[nodiscard]] inline bool isValidEndpointId(
379       const HalClientId &clientId, const HostEndpointId &endpointId) const {
380     if (clientId != mFrameworkServiceClientId) {
381       return endpointId <= kMaxVendorEndpointId;
382     }
383     return true;
384   }
385 
386   /**
387    * Overrides the old callback registered with the client.
388    *
389    * @return true if success, otherwise false
390    */
391   bool overrideCallbackLocked(
392       pid_t pid, const std::shared_ptr<IContextHubCallback> &callback,
393       const ndk::ScopedAIBinder_DeathRecipient &deathRecipient,
394       void *deathRecipientCookie);
395 
396   /**
397    * Extracts the client id from the endpoint id.
398    *
399    * @param endpointId the endpoint id received from CHRE, before any conversion
400    */
getClientIdFromEndpointId(const HostEndpointId & endpointId)401   [[nodiscard]] inline HalClientId getClientIdFromEndpointId(
402       const HostEndpointId &endpointId) const {
403     if (endpointId & kVendorEndpointIdBitMask) {
404       return endpointId >> kNumOfBitsForEndpointId & kMaxHalClientId;
405     }
406     return mFrameworkServiceClientId;
407   }
408 
getProcessName(pid_t)409   std::string getProcessName(pid_t /*pid*/) {
410     // TODO(b/274597758): this is a temporary solution that should be updated
411     //   after b/274597758 is resolved.
412     if (mIsFirstClient) {
413       mIsFirstClient = false;
414       return kSystemServerName;
415     }
416     return "the_vendor_client";
417   }
418 
419   bool mIsFirstClient = true;
420 
421   // next available client id
422   HalClientId mNextClientId = kDefaultHalClientId + 1;
423   // framework service client id
424   HalClientId mFrameworkServiceClientId = kDefaultHalClientId;
425 
426   // The lock guarding the access to clients' states and pending transactions
427   std::mutex mLock;
428 
429   // Map from process name to client id which stays consistent with the file
430   // stored at kClientMappingFilePath
431   std::unordered_map<std::string, HalClientId> mProcessNamesToClientIds{};
432   // Map from pids to client ids
433   std::unordered_map<pid_t, HalClientId> mPIdsToClientIds{};
434   // Map from client ids to ClientInfos
435   std::unordered_map<HalClientId, HalClientInfo> mClientIdsToClientInfo{};
436 
437   // States tracking pending transactions
438   std::optional<PendingLoadTransaction> mPendingLoadTransaction = std::nullopt;
439   std::optional<PendingTransaction> mPendingUnloadTransaction = std::nullopt;
440 };
441 }  // namespace android::hardware::contexthub::common::implementation
442 
443 #endif  // ANDROID_HARDWARE_CONTEXTHUB_COMMON_HAL_CLIENT_MANAGER_H_
444