• 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 #include "hal_client_manager.h"
17 #include <aidl/android/hardware/contexthub/AsyncEventType.h>
18 #include <android-base/strings.h>
19 #include <json/json.h>
20 #include <utils/SystemClock.h>
21 #include <fstream>
22 
23 namespace android::hardware::contexthub::common::implementation {
24 
25 using aidl::android::hardware::contexthub::AsyncEventType;
26 using aidl::android::hardware::contexthub::ContextHubMessage;
27 using aidl::android::hardware::contexthub::HostEndpointInfo;
28 using aidl::android::hardware::contexthub::IContextHubCallback;
29 
30 namespace {
getClientMappingsFromFile(const char * filePath,Json::Value & mappings)31 bool getClientMappingsFromFile(const char *filePath, Json::Value &mappings) {
32   std::fstream file(filePath);
33   Json::CharReaderBuilder builder;
34   return file.good() &&
35          Json::parseFromStream(builder, file, &mappings, /* errs= */ nullptr);
36 }
37 }  // namespace
38 
createClientIdLocked(const std::string & processName)39 std::optional<HalClientId> HalClientManager::createClientIdLocked(
40     const std::string &processName) {
41   if (mPIdsToClientIds.size() > kMaxNumOfHalClients ||
42       mNextClientId > kMaxHalClientId) {
43     LOGE("Too many HAL clients registered which should never happen.");
44     return std::nullopt;
45   }
46   if (mProcessNamesToClientIds.find(processName) !=
47       mProcessNamesToClientIds.end()) {
48     return mProcessNamesToClientIds[processName];
49   }
50   // Update the json list with the new mapping
51   mProcessNamesToClientIds.emplace(processName, mNextClientId);
52   Json::Value mappings;
53   for (const auto &[name, clientId] : mProcessNamesToClientIds) {
54     Json::Value mapping;
55     mapping[kJsonProcessName] = name;
56     mapping[kJsonClientId] = clientId;
57     mappings.append(mapping);
58   }
59   // write to the file; Create the file if it doesn't exist
60   Json::StreamWriterBuilder factory;
61   std::unique_ptr<Json::StreamWriter> const writer(factory.newStreamWriter());
62   std::ofstream fileStream(kClientMappingFilePath);
63   writer->write(mappings, &fileStream);
64   fileStream << std::endl;
65   return {mNextClientId++};
66 }
67 
getClientId()68 HalClientId HalClientManager::getClientId() {
69   pid_t pid = AIBinder_getCallingPid();
70   const std::lock_guard<std::mutex> lock(mLock);
71   if (isKnownPIdLocked(pid)) {
72     return mPIdsToClientIds[pid];
73   }
74   LOGE("Failed to find the client id for pid %d", pid);
75   return kDefaultHalClientId;
76 }
77 
getCallback(HalClientId clientId)78 std::shared_ptr<IContextHubCallback> HalClientManager::getCallback(
79     HalClientId clientId) {
80   const std::lock_guard<std::mutex> lock(mLock);
81   if (isAllocatedClientIdLocked(clientId)) {
82     return mClientIdsToClientInfo.at(clientId).callback;
83   }
84   LOGE("Failed to find the callback for the client id %" PRIu16, clientId);
85   return nullptr;
86 }
87 
registerCallback(const std::shared_ptr<IContextHubCallback> & callback,const ndk::ScopedAIBinder_DeathRecipient & deathRecipient,void * deathRecipientCookie)88 bool HalClientManager::registerCallback(
89     const std::shared_ptr<IContextHubCallback> &callback,
90     const ndk::ScopedAIBinder_DeathRecipient &deathRecipient,
91     void *deathRecipientCookie) {
92   pid_t pid = AIBinder_getCallingPid();
93   const std::lock_guard<std::mutex> lock(mLock);
94   if (AIBinder_linkToDeath(callback->asBinder().get(), deathRecipient.get(),
95                            deathRecipientCookie) != STATUS_OK) {
96     LOGE("Failed to link client binder to death recipient.");
97     return false;
98   }
99   if (isKnownPIdLocked(pid)) {
100     LOGW("The pid %d has already registered. Overriding its callback.", pid);
101     return overrideCallbackLocked(pid, callback, deathRecipient,
102                                   deathRecipientCookie);
103   }
104   std::string processName = getProcessName(pid);
105   std::optional<HalClientId> clientIdOptional =
106       createClientIdLocked(processName);
107   if (clientIdOptional == std::nullopt) {
108     LOGE("Failed to generate a valid client id for process %s",
109          processName.c_str());
110     return false;
111   }
112   HalClientId clientId = clientIdOptional.value();
113   if (mClientIdsToClientInfo.find(clientId) != mClientIdsToClientInfo.end()) {
114     LOGE("Process %s already has a connection to HAL.", processName.c_str());
115     return false;
116   }
117   mPIdsToClientIds[pid] = clientId;
118   mClientIdsToClientInfo.emplace(clientId,
119                                  HalClientInfo(callback, deathRecipientCookie));
120   if (mFrameworkServiceClientId == kDefaultHalClientId &&
121       processName == kSystemServerName) {
122     mFrameworkServiceClientId = clientId;
123   }
124   return true;
125 }
126 
overrideCallbackLocked(pid_t pid,const std::shared_ptr<IContextHubCallback> & callback,const ndk::ScopedAIBinder_DeathRecipient & deathRecipient,void * deathRecipientCookie)127 bool HalClientManager::overrideCallbackLocked(
128     pid_t pid, const std::shared_ptr<IContextHubCallback> &callback,
129     const ndk::ScopedAIBinder_DeathRecipient &deathRecipient,
130     void *deathRecipientCookie) {
131   LOGI("Overriding the callback for pid %d", pid);
132   HalClientInfo &clientInfo =
133       mClientIdsToClientInfo.at(mPIdsToClientIds.at(pid));
134   if (AIBinder_unlinkToDeath(clientInfo.callback->asBinder().get(),
135                              deathRecipient.get(),
136                              clientInfo.deathRecipientCookie) != STATUS_OK) {
137     LOGE("Unable to unlink the old callback for pid %d", pid);
138     return false;
139   }
140   clientInfo.callback.reset();
141   clientInfo.callback = callback;
142   clientInfo.deathRecipientCookie = deathRecipientCookie;
143   return true;
144 }
145 
handleClientDeath(pid_t pid,const ndk::ScopedAIBinder_DeathRecipient & deathRecipient)146 void HalClientManager::handleClientDeath(
147     pid_t pid, const ndk::ScopedAIBinder_DeathRecipient &deathRecipient) {
148   const std::lock_guard<std::mutex> lock(mLock);
149   if (!isKnownPIdLocked(pid)) {
150     LOGE("Failed to locate the dead pid %d", pid);
151     return;
152   }
153   HalClientId clientId = mPIdsToClientIds[pid];
154   mPIdsToClientIds.erase(mPIdsToClientIds.find(pid));
155   if (!isAllocatedClientIdLocked(clientId)) {
156     LOGE("Failed to locate the dead client id %" PRIu16, clientId);
157     return;
158   }
159 
160   for (const auto &[procName, id] : mProcessNamesToClientIds) {
161     if (id == clientId && procName == kSystemServerName) {
162       LOGE("System server is disconnected");
163       mIsFirstClient = true;
164     }
165   }
166 
167   HalClientInfo &clientInfo = mClientIdsToClientInfo.at(clientId);
168   if (AIBinder_unlinkToDeath(clientInfo.callback->asBinder().get(),
169                              deathRecipient.get(),
170                              clientInfo.deathRecipientCookie) != STATUS_OK) {
171     LOGE("Unable to unlink the old callback for pid %d in death handler", pid);
172   }
173   clientInfo.callback.reset();
174   if (mPendingLoadTransaction.has_value() &&
175       mPendingLoadTransaction->clientId == clientId) {
176     mPendingLoadTransaction.reset();
177   }
178   if (mPendingUnloadTransaction.has_value() &&
179       mPendingUnloadTransaction->clientId == clientId) {
180     mPendingLoadTransaction.reset();
181   }
182   mClientIdsToClientInfo.erase(clientId);
183   if (mFrameworkServiceClientId == clientId) {
184     mFrameworkServiceClientId = kDefaultHalClientId;
185   }
186   LOGI("Process %" PRIu32 " is disconnected from HAL.", pid);
187 }
188 
registerPendingLoadTransaction(std::unique_ptr<chre::FragmentedLoadTransaction> transaction)189 bool HalClientManager::registerPendingLoadTransaction(
190     std::unique_ptr<chre::FragmentedLoadTransaction> transaction) {
191   if (transaction->isComplete()) {
192     LOGW("No need to register a completed load transaction.");
193     return false;
194   }
195   pid_t pid = AIBinder_getCallingPid();
196 
197   const std::lock_guard<std::mutex> lock(mLock);
198   if (!isKnownPIdLocked(pid)) {
199     LOGE("Unknown HAL client when registering its pending load transaction.");
200     return false;
201   }
202   auto clientId = mPIdsToClientIds[pid];
203   if (!isNewTransactionAllowedLocked(clientId)) {
204     return false;
205   }
206   mPendingLoadTransaction.emplace(
207       clientId, /* registeredTimeMs= */ android::elapsedRealtime(),
208       /* currentFragmentId= */ 0, std::move(transaction));
209   return true;
210 }
211 
212 std::optional<chre::FragmentedLoadRequest>
getNextFragmentedLoadRequest()213 HalClientManager::getNextFragmentedLoadRequest() {
214   const std::lock_guard<std::mutex> lock(mLock);
215   if (mPendingLoadTransaction->transaction->isComplete()) {
216     LOGI("Pending load transaction %" PRIu32
217          " is finished with client %" PRIu16,
218          mPendingLoadTransaction->transaction->getTransactionId(),
219          mPendingLoadTransaction->clientId);
220     mPendingLoadTransaction.reset();
221     return std::nullopt;
222   }
223   auto request = mPendingLoadTransaction->transaction->getNextRequest();
224   mPendingLoadTransaction->currentFragmentId = request.fragmentId;
225   LOGD("Client %" PRIu16 " has fragment #%zu ready",
226        mPendingLoadTransaction->clientId, request.fragmentId);
227   return request;
228 }
229 
registerPendingUnloadTransaction(uint32_t transactionId)230 bool HalClientManager::registerPendingUnloadTransaction(
231     uint32_t transactionId) {
232   pid_t pid = AIBinder_getCallingPid();
233   const std::lock_guard<std::mutex> lock(mLock);
234   if (!isKnownPIdLocked(pid)) {
235     LOGE("Unknown HAL client when registering its pending unload transaction.");
236     return false;
237   }
238   auto clientId = mPIdsToClientIds[pid];
239   if (!isNewTransactionAllowedLocked(clientId)) {
240     return false;
241   }
242   mPendingUnloadTransaction.emplace(
243       clientId, transactionId,
244       /* registeredTimeMs= */ android::elapsedRealtime());
245   return true;
246 }
247 
isNewTransactionAllowedLocked(HalClientId clientId)248 bool HalClientManager::isNewTransactionAllowedLocked(HalClientId clientId) {
249   if (mPendingLoadTransaction.has_value()) {
250     auto timeElapsedMs =
251         android::elapsedRealtime() - mPendingLoadTransaction->registeredTimeMs;
252     if (timeElapsedMs < kTransactionTimeoutThresholdMs) {
253       LOGE("Rejects client %" PRIu16
254            "'s transaction because an active load "
255            "transaction %" PRIu32 " with current fragment id %" PRIu32
256            " from client %" PRIu16 " exists. Try again later.",
257            clientId, mPendingLoadTransaction->transaction->getTransactionId(),
258            mPendingLoadTransaction->currentFragmentId,
259            mPendingLoadTransaction->clientId);
260       return false;
261     }
262     LOGE("Client %" PRIu16 "'s pending load transaction %" PRIu32
263          " with current fragment id %" PRIu32
264          " is overridden by client %" PRIu16
265          " after holding the slot for %" PRIu64 " ms",
266          mPendingLoadTransaction->clientId,
267          mPendingLoadTransaction->transaction->getTransactionId(),
268          mPendingLoadTransaction->currentFragmentId, clientId, timeElapsedMs);
269     mPendingLoadTransaction.reset();
270     return true;
271   }
272   if (mPendingUnloadTransaction.has_value()) {
273     auto timeElapsedMs = android::elapsedRealtime() -
274                          mPendingUnloadTransaction->registeredTimeMs;
275     if (timeElapsedMs < kTransactionTimeoutThresholdMs) {
276       LOGE("Rejects client %" PRIu16
277            "'s transaction because an active unload "
278            "transaction %" PRIu32 " from client %" PRIu16
279            " exists. Try again later.",
280            clientId, mPendingUnloadTransaction->transactionId,
281            mPendingUnloadTransaction->clientId);
282       return false;
283     }
284     LOGE("A pending unload transaction %" PRIu32
285          " registered by client %" PRIu16
286          " is overridden by a new transaction from client %" PRIu16
287          " after holding the slot for %" PRIu64 "ms",
288          mPendingUnloadTransaction->transactionId,
289          mPendingUnloadTransaction->clientId, clientId, timeElapsedMs);
290     mPendingUnloadTransaction.reset();
291     return true;
292   }
293   return true;
294 }
295 
registerEndpointId(const HostEndpointId & endpointId)296 bool HalClientManager::registerEndpointId(const HostEndpointId &endpointId) {
297   pid_t pid = AIBinder_getCallingPid();
298   const std::lock_guard<std::mutex> lock(mLock);
299   if (!isKnownPIdLocked(pid)) {
300     LOGE(
301         "Unknown HAL client (pid %d). Register the callback before registering "
302         "an endpoint.",
303         pid);
304     return false;
305   }
306   HalClientId clientId = mPIdsToClientIds[pid];
307   if (!isValidEndpointId(clientId, endpointId)) {
308     LOGE("Endpoint id %" PRIu16 " from process %d is out of range.", endpointId,
309          pid);
310     return false;
311   }
312   if (mClientIdsToClientInfo[clientId].endpointIds.find(endpointId) !=
313       mClientIdsToClientInfo[clientId].endpointIds.end()) {
314     LOGW("The endpoint %" PRIu16 " is already connected.", endpointId);
315     return false;
316   }
317   mClientIdsToClientInfo[clientId].endpointIds.insert(endpointId);
318   LOGI("Endpoint id %" PRIu16 " is connected to client %" PRIu16, endpointId,
319        clientId);
320   return true;
321 }
322 
removeEndpointId(const HostEndpointId & endpointId)323 bool HalClientManager::removeEndpointId(const HostEndpointId &endpointId) {
324   pid_t pid = AIBinder_getCallingPid();
325   const std::lock_guard<std::mutex> lock(mLock);
326   if (!isKnownPIdLocked(pid)) {
327     LOGE(
328         "Unknown HAL client (pid %d). A callback should have been registered "
329         "before removing an endpoint.",
330         pid);
331     return false;
332   }
333   HalClientId clientId = mPIdsToClientIds[pid];
334   if (!isValidEndpointId(clientId, endpointId)) {
335     LOGE("Endpoint id %" PRIu16 " from process %d is out of range.", endpointId,
336          pid);
337     return false;
338   }
339   if (mClientIdsToClientInfo[clientId].endpointIds.find(endpointId) ==
340       mClientIdsToClientInfo[clientId].endpointIds.end()) {
341     LOGW("The endpoint %" PRIu16 " is not connected.", endpointId);
342     return false;
343   }
344   mClientIdsToClientInfo[clientId].endpointIds.erase(endpointId);
345   LOGI("Endpoint id %" PRIu16 " is removed from client %" PRIu16, endpointId,
346        clientId);
347   return true;
348 }
349 
getCallbackForEndpoint(const HostEndpointId & endpointId)350 std::shared_ptr<IContextHubCallback> HalClientManager::getCallbackForEndpoint(
351     const HostEndpointId &endpointId) {
352   const std::lock_guard<std::mutex> lock(mLock);
353   HalClientId clientId = getClientIdFromEndpointId(endpointId);
354   if (!isAllocatedClientIdLocked(clientId)) {
355     LOGE("Unknown endpoint id %" PRIu16 ". Please register the callback first.",
356          endpointId);
357     return nullptr;
358   }
359   return mClientIdsToClientInfo[clientId].callback;
360 }
361 
sendMessageForAllCallbacks(const ContextHubMessage & message,const std::vector<std::string> & messageParams)362 void HalClientManager::sendMessageForAllCallbacks(
363     const ContextHubMessage &message,
364     const std::vector<std::string> &messageParams) {
365   const std::lock_guard<std::mutex> lock(mLock);
366   for (const auto &[_, clientInfo] : mClientIdsToClientInfo) {
367     if (clientInfo.callback != nullptr) {
368       clientInfo.callback->handleContextHubMessage(message, messageParams);
369     }
370   }
371 }
372 
373 const std::unordered_set<HostEndpointId>
getAllConnectedEndpoints(pid_t pid)374     *HalClientManager::getAllConnectedEndpoints(pid_t pid) {
375   const std::lock_guard<std::mutex> lock(mLock);
376   if (!isKnownPIdLocked(pid)) {
377     LOGE("Unknown HAL client with pid %d", pid);
378     return nullptr;
379   }
380   HalClientId clientId = mPIdsToClientIds[pid];
381   if (mClientIdsToClientInfo.find(clientId) == mClientIdsToClientInfo.end()) {
382     LOGE("Can't find any information for client id %" PRIu16, clientId);
383     return nullptr;
384   }
385   return &mClientIdsToClientInfo[clientId].endpointIds;
386 }
387 
mutateEndpointIdFromHostIfNeeded(const pid_t & pid,HostEndpointId & endpointId)388 bool HalClientManager::mutateEndpointIdFromHostIfNeeded(
389     const pid_t &pid, HostEndpointId &endpointId) {
390   const std::lock_guard<std::mutex> lock(mLock);
391   if (!isKnownPIdLocked(pid)) {
392     LOGE("Unknown HAL client with pid %d", pid);
393     return false;
394   }
395   // no need to mutate client id for framework service
396   if (mPIdsToClientIds[pid] != mFrameworkServiceClientId) {
397     HalClientId clientId = mPIdsToClientIds[pid];
398     endpointId = kVendorEndpointIdBitMask |
399                  clientId << kNumOfBitsForEndpointId | endpointId;
400   }
401   return true;
402 }
403 
convertToOriginalEndpointId(const HostEndpointId & endpointId)404 HostEndpointId HalClientManager::convertToOriginalEndpointId(
405     const HostEndpointId &endpointId) {
406   if (endpointId & kVendorEndpointIdBitMask) {
407     return endpointId & kMaxVendorEndpointId;
408   }
409   return endpointId;
410 }
411 
HalClientManager()412 HalClientManager::HalClientManager() {
413   // Parses the file to construct a mapping from process names to client ids.
414   Json::Value mappings;
415   if (!getClientMappingsFromFile(kClientMappingFilePath, mappings)) {
416     // TODO(b/247124878): When the device was firstly booted up the file doesn't
417     //   exist which is expected. Consider to create a default file to avoid
418     //   confusions.
419     LOGW("Unable to find and read %s.", kClientMappingFilePath);
420     return;
421   }
422   for (int i = 0; i < mappings.size(); i++) {
423     Json::Value mapping = mappings[i];
424     if (!mapping.isMember(kJsonClientId) ||
425         !mapping.isMember(kJsonProcessName)) {
426       LOGE("Unable to find expected key name for the entry %d", i);
427       continue;
428     }
429     std::string processName = mapping[kJsonProcessName].asString();
430     auto clientId = static_cast<HalClientId>(mapping[kJsonClientId].asUInt());
431     mProcessNamesToClientIds[processName] = clientId;
432     // mNextClientId should always hold the next available client id
433     if (mNextClientId <= clientId) {
434       mNextClientId = clientId + 1;
435     }
436   }
437 }
438 
isPendingLoadTransactionMatchedLocked(HalClientId clientId,uint32_t transactionId,uint32_t currentFragmentId)439 bool HalClientManager::isPendingLoadTransactionMatchedLocked(
440     HalClientId clientId, uint32_t transactionId, uint32_t currentFragmentId) {
441   bool success =
442       isPendingTransactionMatchedLocked(clientId, transactionId,
443                                         mPendingLoadTransaction) &&
444       mPendingLoadTransaction->currentFragmentId == currentFragmentId;
445   if (!success) {
446     if (mPendingLoadTransaction.has_value()) {
447       LOGE("Transaction of client %" PRIu16 " transaction %" PRIu32
448            " fragment %" PRIu32
449            " doesn't match the current pending transaction (client %" PRIu16
450            " transaction %" PRIu32 " fragment %" PRIu32 ").",
451            clientId, transactionId, currentFragmentId,
452            mPendingLoadTransaction->clientId,
453            mPendingLoadTransaction->transactionId,
454            mPendingLoadTransaction->currentFragmentId);
455     } else {
456       LOGE("Transaction of client %" PRIu16 " transaction %" PRIu32
457            " fragment %" PRIu32 " doesn't match any pending transaction.",
458            clientId, transactionId, currentFragmentId);
459     }
460   }
461   return success;
462 }
463 
resetPendingLoadTransaction()464 void HalClientManager::resetPendingLoadTransaction() {
465   const std::lock_guard<std::mutex> lock(mLock);
466   mPendingLoadTransaction.reset();
467 }
468 
resetPendingUnloadTransaction(HalClientId clientId,uint32_t transactionId)469 bool HalClientManager::resetPendingUnloadTransaction(HalClientId clientId,
470                                                      uint32_t transactionId) {
471   const std::lock_guard<std::mutex> lock(mLock);
472   // Only clear a pending transaction when the client id and the transaction id
473   // are both matched
474   if (isPendingTransactionMatchedLocked(clientId, transactionId,
475                                         mPendingUnloadTransaction)) {
476     LOGI("Clears out the pending unload transaction: client id %" PRIu16
477          ", transaction id %" PRIu32,
478          clientId, transactionId);
479     mPendingUnloadTransaction.reset();
480     return true;
481   }
482   LOGW("Client %" PRIu16 " doesn't have a pending unload transaction %" PRIu32
483        ". Skip resetting",
484        clientId, transactionId);
485   return false;
486 }
487 
handleChreRestart()488 void HalClientManager::handleChreRestart() {
489   {
490     const std::lock_guard<std::mutex> lock(mLock);
491     mPendingLoadTransaction.reset();
492     mPendingUnloadTransaction.reset();
493     for (auto &[_, clientInfo] : mClientIdsToClientInfo) {
494       clientInfo.endpointIds.clear();
495     }
496   }
497   // Incurs callbacks without holding the lock to avoid deadlocks.
498   for (auto &[_, clientInfo] : mClientIdsToClientInfo) {
499     if (clientInfo.callback != nullptr) {
500       clientInfo.callback->handleContextHubAsyncEvent(
501           AsyncEventType::RESTARTED);
502     }
503   }
504 }
505 }  // namespace android::hardware::contexthub::common::implementation