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