1 /*
2 * Copyright (C) 2021 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
17 #include "generic_context_hub_aidl.h"
18
19 #include "chre_api/chre/event.h"
20 #include "chre_host/config_util.h"
21 #include "chre_host/file_stream.h"
22 #include "chre_host/fragmented_load_transaction.h"
23 #include "chre_host/host_protocol_host.h"
24 #include "chre_host/log.h"
25 #include "chre_host/napp_header.h"
26 #include "permissions_util.h"
27
28 #include <algorithm>
29 #include <chrono>
30 #include <limits>
31
32 namespace aidl::android::hardware::contexthub {
33
34 // Aliased for consistency with the way these symbols are referenced in
35 // CHRE-side code
36 namespace fbs = ::chre::fbs;
37
38 using ::android::chre::FragmentedLoadTransaction;
39 using ::android::chre::getPreloadedNanoappsFromConfigFile;
40 using ::android::chre::getStringFromByteVector;
41 using ::android::chre::NanoAppBinaryHeader;
42 using ::android::chre::readFileContents;
43 using ::android::hardware::contexthub::common::implementation::
44 chreToAndroidPermissions;
45 using ::android::hardware::contexthub::common::implementation::
46 kSupportedPermissions;
47 using ::ndk::ScopedAStatus;
48
49 namespace {
50 constexpr uint32_t kDefaultHubId = 0;
51 constexpr char kPreloadedNanoappsConfigPath[] =
52 "/vendor/etc/chre/preloaded_nanoapps.json";
53 constexpr std::chrono::duration kTestModeTimeout = std::chrono::seconds(10);
54 constexpr uint16_t kMaxValidHostEndPointId = 0x7fff;
55
56 /*
57 * The starting transaction ID for internal transactions. We choose
58 * the limit + 1 here as any client will only pass non-negative values up to the
59 * limit. The socket connection to CHRE accepts a uint32_t for the transaction
60 * ID, so we can use the value below up to std::numeric_limits<uint32_t>::max()
61 * for internal transaction IDs.
62 */
63 constexpr int32_t kStartingInternalTransactionId = 0x80000000;
64
extractChreApiMajorVersion(uint32_t chreVersion)65 inline constexpr int8_t extractChreApiMajorVersion(uint32_t chreVersion) {
66 return static_cast<int8_t>(chreVersion >> 24);
67 }
68
extractChreApiMinorVersion(uint32_t chreVersion)69 inline constexpr int8_t extractChreApiMinorVersion(uint32_t chreVersion) {
70 return static_cast<int8_t>(chreVersion >> 16);
71 }
72
extractChrePatchVersion(uint32_t chreVersion)73 inline constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) {
74 return static_cast<uint16_t>(chreVersion);
75 }
76
getFbsSetting(const Setting & setting,fbs::Setting * fbsSetting)77 bool getFbsSetting(const Setting &setting, fbs::Setting *fbsSetting) {
78 bool foundSetting = true;
79
80 switch (setting) {
81 case Setting::LOCATION:
82 *fbsSetting = fbs::Setting::LOCATION;
83 break;
84 case Setting::AIRPLANE_MODE:
85 *fbsSetting = fbs::Setting::AIRPLANE_MODE;
86 break;
87 case Setting::MICROPHONE:
88 *fbsSetting = fbs::Setting::MICROPHONE;
89 break;
90 default:
91 foundSetting = false;
92 LOGE("Setting update with invalid enum value %hhu", setting);
93 break;
94 }
95
96 return foundSetting;
97 }
98
toServiceSpecificError(bool success)99 ScopedAStatus toServiceSpecificError(bool success) {
100 return success ? ScopedAStatus::ok()
101 : ScopedAStatus::fromServiceSpecificError(
102 BnContextHub::EX_CONTEXT_HUB_UNSPECIFIED);
103 }
104
105 } // anonymous namespace
106
getContextHubs(std::vector<ContextHubInfo> * out_contextHubInfos)107 ScopedAStatus ContextHub::getContextHubs(
108 std::vector<ContextHubInfo> *out_contextHubInfos) {
109 ::chre::fbs::HubInfoResponseT response;
110 bool success = mConnection->getContextHubs(&response);
111 if (success) {
112 ContextHubInfo hub;
113 hub.name = getStringFromByteVector(response.name);
114 hub.vendor = getStringFromByteVector(response.vendor);
115 hub.toolchain = getStringFromByteVector(response.toolchain);
116 hub.id = kDefaultHubId;
117 hub.peakMips = response.peak_mips;
118 hub.maxSupportedMessageLengthBytes = response.max_msg_len;
119 hub.chrePlatformId = response.platform_id;
120
121 uint32_t version = response.chre_platform_version;
122 hub.chreApiMajorVersion = extractChreApiMajorVersion(version);
123 hub.chreApiMinorVersion = extractChreApiMinorVersion(version);
124 hub.chrePatchVersion = extractChrePatchVersion(version);
125
126 hub.supportedPermissions = kSupportedPermissions;
127
128 hub.supportsReliableMessages = false;
129
130 out_contextHubInfos->push_back(hub);
131 }
132
133 return ndk::ScopedAStatus::ok();
134 }
135
loadNanoapp(int32_t contextHubId,const NanoappBinary & appBinary,int32_t transactionId)136 ScopedAStatus ContextHub::loadNanoapp(int32_t contextHubId,
137 const NanoappBinary &appBinary,
138 int32_t transactionId) {
139 if (contextHubId != kDefaultHubId) {
140 LOGE("Invalid ID %" PRId32, contextHubId);
141 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
142 }
143
144 std::lock_guard<std::mutex> lock(mTestModeMutex);
145 bool success = loadNanoappInternal(appBinary, transactionId);
146 return toServiceSpecificError(success);
147 }
148
unloadNanoapp(int32_t contextHubId,int64_t appId,int32_t transactionId)149 ScopedAStatus ContextHub::unloadNanoapp(int32_t contextHubId, int64_t appId,
150 int32_t transactionId) {
151 if (contextHubId != kDefaultHubId) {
152 LOGE("Invalid ID %" PRId32, contextHubId);
153 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
154 }
155
156 std::lock_guard<std::mutex> lock(mTestModeMutex);
157 bool success = unloadNanoappInternal(appId, transactionId);
158 return toServiceSpecificError(success);
159 }
160
disableNanoapp(int32_t,int64_t appId,int32_t)161 ScopedAStatus ContextHub::disableNanoapp(int32_t /* contextHubId */,
162 int64_t appId,
163 int32_t /* transactionId */) {
164 LOGW("Attempted to disable app ID 0x%016" PRIx64 ", but not supported",
165 appId);
166 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
167 }
168
enableNanoapp(int32_t,int64_t appId,int32_t)169 ScopedAStatus ContextHub::enableNanoapp(int32_t /* contextHubId */,
170 int64_t appId,
171 int32_t /* transactionId */) {
172 LOGW("Attempted to enable app ID 0x%016" PRIx64 ", but not supported", appId);
173 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
174 }
175
onSettingChanged(Setting setting,bool enabled)176 ScopedAStatus ContextHub::onSettingChanged(Setting setting, bool enabled) {
177 mSettingEnabled[setting] = enabled;
178 fbs::Setting fbsSetting;
179 bool isWifiOrBtSetting =
180 (setting == Setting::WIFI_MAIN || setting == Setting::WIFI_SCANNING ||
181 setting == Setting::BT_MAIN || setting == Setting::BT_SCANNING);
182
183 if (!isWifiOrBtSetting && getFbsSetting(setting, &fbsSetting)) {
184 mConnection->sendSettingChangedNotification(fbsSetting,
185 toFbsSettingState(enabled));
186 }
187
188 bool isWifiMainEnabled = isSettingEnabled(Setting::WIFI_MAIN);
189 bool isWifiScanEnabled = isSettingEnabled(Setting::WIFI_SCANNING);
190 bool isAirplaneModeEnabled = isSettingEnabled(Setting::AIRPLANE_MODE);
191
192 // Because the airplane mode impact on WiFi is not standardized in Android,
193 // we write a specific handling in the Context Hub HAL to inform CHRE.
194 // The following definition is a default one, and can be adjusted
195 // appropriately if necessary.
196 bool isWifiAvailable = isAirplaneModeEnabled
197 ? (isWifiMainEnabled)
198 : (isWifiMainEnabled || isWifiScanEnabled);
199 if (!mIsWifiAvailable.has_value() || (isWifiAvailable != mIsWifiAvailable)) {
200 mConnection->sendSettingChangedNotification(
201 fbs::Setting::WIFI_AVAILABLE, toFbsSettingState(isWifiAvailable));
202 mIsWifiAvailable = isWifiAvailable;
203 }
204
205 // The BT switches determine whether we can BLE scan which is why things are
206 // mapped like this into CHRE.
207 bool isBtMainEnabled = isSettingEnabled(Setting::BT_MAIN);
208 bool isBtScanEnabled = isSettingEnabled(Setting::BT_SCANNING);
209 bool isBleAvailable = isBtMainEnabled || isBtScanEnabled;
210 if (!mIsBleAvailable.has_value() || (isBleAvailable != mIsBleAvailable)) {
211 mConnection->sendSettingChangedNotification(
212 fbs::Setting::BLE_AVAILABLE, toFbsSettingState(isBleAvailable));
213 mIsBleAvailable = isBleAvailable;
214 }
215
216 return ndk::ScopedAStatus::ok();
217 }
218
queryNanoapps(int32_t contextHubId)219 ScopedAStatus ContextHub::queryNanoapps(int32_t contextHubId) {
220 if (contextHubId != kDefaultHubId) {
221 LOGE("Invalid ID %" PRId32, contextHubId);
222 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
223 }
224 return toServiceSpecificError(mConnection->queryNanoapps());
225 }
226
getPreloadedNanoappIds(int32_t contextHubId,std::vector<int64_t> * out_preloadedNanoappIds)227 ::ndk::ScopedAStatus ContextHub::getPreloadedNanoappIds(
228 int32_t contextHubId, std::vector<int64_t> *out_preloadedNanoappIds) {
229 if (contextHubId != kDefaultHubId) {
230 LOGE("Invalid ID %" PRId32, contextHubId);
231 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
232 }
233
234 if (out_preloadedNanoappIds == nullptr) {
235 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
236 }
237
238 std::unique_lock<std::mutex> lock(mPreloadedNanoappIdsMutex);
239 if (mPreloadedNanoappIds.has_value()) {
240 *out_preloadedNanoappIds = *mPreloadedNanoappIds;
241 return ScopedAStatus::ok();
242 }
243
244 std::vector<chrePreloadedNanoappInfo> preloadedNanoapps;
245 if (!getPreloadedNanoappIdsFromConfigFile(preloadedNanoapps, nullptr)) {
246 return ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
247 }
248
249 mPreloadedNanoappIds = std::vector<int64_t>();
250 for (const auto &preloadedNanoapp : preloadedNanoapps) {
251 mPreloadedNanoappIds->push_back(preloadedNanoapp.id);
252 out_preloadedNanoappIds->push_back(preloadedNanoapp.id);
253 }
254
255 return ScopedAStatus::ok();
256 }
257
registerCallback(int32_t contextHubId,const std::shared_ptr<IContextHubCallback> & cb)258 ScopedAStatus ContextHub::registerCallback(
259 int32_t contextHubId, const std::shared_ptr<IContextHubCallback> &cb) {
260 if (contextHubId != kDefaultHubId) {
261 LOGE("Invalid ID %" PRId32, contextHubId);
262 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
263 }
264 std::lock_guard<std::mutex> lock(mCallbackMutex);
265 if (mCallback != nullptr) {
266 binder_status_t binder_status = AIBinder_unlinkToDeath(
267 mCallback->asBinder().get(), mDeathRecipient.get(), this);
268 if (binder_status != STATUS_OK) {
269 LOGE("Failed to unlink to death");
270 }
271 }
272 mCallback = cb;
273 if (cb != nullptr) {
274 binder_status_t binder_status =
275 AIBinder_linkToDeath(cb->asBinder().get(), mDeathRecipient.get(), this);
276 if (binder_status != STATUS_OK) {
277 LOGE("Failed to link to death");
278 }
279 }
280 return ScopedAStatus::ok();
281 }
282
sendMessageToHub(int32_t contextHubId,const ContextHubMessage & message)283 ScopedAStatus ContextHub::sendMessageToHub(int32_t contextHubId,
284 const ContextHubMessage &message) {
285 if (contextHubId != kDefaultHubId) {
286 LOGE("Invalid ID %" PRId32, contextHubId);
287 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
288 }
289
290 bool success = mConnection->sendMessageToHub(
291 message.nanoappId, message.messageType, message.hostEndPoint,
292 message.messageBody.data(), message.messageBody.size());
293 mEventLogger.logMessageToNanoapp(message, success);
294
295 return toServiceSpecificError(success);
296 }
297
setTestMode(bool enable)298 ScopedAStatus ContextHub::setTestMode(bool enable) {
299 return enable ? enableTestMode() : disableTestMode();
300 }
301
sendMessageDeliveryStatusToHub(int32_t,const MessageDeliveryStatus &)302 ScopedAStatus ContextHub::sendMessageDeliveryStatusToHub(
303 int32_t /* contextHubId */,
304 const MessageDeliveryStatus & /* messageDeliveryStatus */) {
305 return ndk::ScopedAStatus::ok();
306 }
307
onHostEndpointConnected(const HostEndpointInfo & in_info)308 ScopedAStatus ContextHub::onHostEndpointConnected(
309 const HostEndpointInfo &in_info) {
310 std::lock_guard<std::mutex> lock(mConnectedHostEndpointsMutex);
311 uint8_t type;
312 switch (in_info.type) {
313 case HostEndpointInfo::Type::APP:
314 type = CHRE_HOST_ENDPOINT_TYPE_APP;
315 break;
316 case HostEndpointInfo::Type::NATIVE:
317 type = CHRE_HOST_ENDPOINT_TYPE_NATIVE;
318 break;
319 case HostEndpointInfo::Type::FRAMEWORK:
320 type = CHRE_HOST_ENDPOINT_TYPE_FRAMEWORK;
321 break;
322 default:
323 LOGE("Unsupported host endpoint type %" PRIu32, type);
324 return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
325 }
326 mConnectedHostEndpoints.insert(in_info.hostEndpointId);
327 mConnection->onHostEndpointConnected(
328 in_info.hostEndpointId, type, in_info.packageName.value_or(std::string()),
329 in_info.attributionTag.value_or(std::string()));
330 return ndk::ScopedAStatus::ok();
331 }
332
onHostEndpointDisconnected(char16_t in_hostEndpointId)333 ScopedAStatus ContextHub::onHostEndpointDisconnected(
334 char16_t in_hostEndpointId) {
335 std::lock_guard<std::mutex> lock(mConnectedHostEndpointsMutex);
336 if (mConnectedHostEndpoints.count(in_hostEndpointId) > 0) {
337 mConnectedHostEndpoints.erase(in_hostEndpointId);
338
339 mConnection->onHostEndpointDisconnected(in_hostEndpointId);
340 } else {
341 LOGE("Unknown host endpoint disconnected (ID: %" PRIu16 ")",
342 in_hostEndpointId);
343 }
344
345 return ndk::ScopedAStatus::ok();
346 }
347
onNanSessionStateChanged(const NanSessionStateUpdate &)348 ScopedAStatus ContextHub::onNanSessionStateChanged(
349 const NanSessionStateUpdate & /*in_update*/) {
350 // TODO(271471342): Add support for NAN session management.
351 return ndk::ScopedAStatus::ok();
352 }
353
getHubs(std::vector<HubInfo> * hubs)354 ScopedAStatus ContextHub::getHubs(std::vector<HubInfo> *hubs) {
355 if (mV4Impl) return mV4Impl->getHubs(hubs);
356 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
357 }
358
getEndpoints(std::vector<EndpointInfo> * endpoints)359 ScopedAStatus ContextHub::getEndpoints(std::vector<EndpointInfo> *endpoints) {
360 if (mV4Impl) return mV4Impl->getEndpoints(endpoints);
361 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
362 }
363
registerEndpointHub(const std::shared_ptr<IEndpointCallback> & callback,const HubInfo & hubInfo,std::shared_ptr<IEndpointCommunication> * hubInterface)364 ScopedAStatus ContextHub::registerEndpointHub(
365 const std::shared_ptr<IEndpointCallback> &callback, const HubInfo &hubInfo,
366 std::shared_ptr<IEndpointCommunication> *hubInterface) {
367 if (mV4Impl)
368 return mV4Impl->registerEndpointHub(callback, hubInfo, hubInterface);
369 return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
370 }
371
onNanoappMessage(const::chre::fbs::NanoappMessageT & message)372 void ContextHub::onNanoappMessage(const ::chre::fbs::NanoappMessageT &message) {
373 std::lock_guard<std::mutex> lock(mCallbackMutex);
374 if (mCallback != nullptr) {
375 if (message.host_endpoint > kMaxValidHostEndPointId &&
376 message.host_endpoint != CHRE_HOST_ENDPOINT_BROADCAST) {
377 return;
378 }
379
380 mEventLogger.logMessageFromNanoapp(message);
381 ContextHubMessage outMessage;
382 outMessage.nanoappId = message.app_id;
383 outMessage.hostEndPoint = message.host_endpoint;
384 outMessage.messageType = message.message_type;
385 outMessage.messageBody = message.message;
386 outMessage.permissions = chreToAndroidPermissions(message.permissions);
387
388 std::vector<std::string> messageContentPerms =
389 chreToAndroidPermissions(message.message_permissions);
390 mCallback->handleContextHubMessage(outMessage, messageContentPerms);
391 }
392 }
393
onNanoappListResponse(const::chre::fbs::NanoappListResponseT & response)394 void ContextHub::onNanoappListResponse(
395 const ::chre::fbs::NanoappListResponseT &response) {
396 std::vector<NanoappInfo> appInfoList;
397 for (const std::unique_ptr<::chre::fbs::NanoappListEntryT> &nanoapp :
398 response.nanoapps) {
399 // TODO(b/245202050): determine if this is really required, and if so, have
400 // HostProtocolHost strip out null entries as part of decode
401 if (nanoapp == nullptr) {
402 continue;
403 }
404
405 LOGV("App 0x%016" PRIx64 " ver 0x%" PRIx32 " permissions 0x%" PRIx32
406 " enabled %d system %d",
407 nanoapp->app_id, nanoapp->version, nanoapp->permissions,
408 nanoapp->enabled, nanoapp->is_system);
409 if (!nanoapp->is_system) {
410 NanoappInfo appInfo;
411
412 appInfo.nanoappId = nanoapp->app_id;
413 appInfo.nanoappVersion = nanoapp->version;
414 appInfo.enabled = nanoapp->enabled;
415 appInfo.permissions = chreToAndroidPermissions(nanoapp->permissions);
416
417 std::vector<NanoappRpcService> rpcServices;
418 for (const auto &service : nanoapp->rpc_services) {
419 NanoappRpcService aidlService;
420 aidlService.id = service->id;
421 aidlService.version = service->version;
422 rpcServices.emplace_back(aidlService);
423 }
424 appInfo.rpcServices = rpcServices;
425
426 appInfoList.push_back(appInfo);
427 }
428 }
429
430 {
431 std::lock_guard<std::mutex> lock(mQueryNanoappsInternalMutex);
432 if (!mQueryNanoappsInternalList) {
433 mQueryNanoappsInternalList = appInfoList;
434 mQueryNanoappsInternalCondVar.notify_all();
435 // This was an internal HAL request - do not call callback
436 return;
437 }
438 }
439
440 std::lock_guard<std::mutex> lock(mCallbackMutex);
441 if (mCallback != nullptr) {
442 mCallback->handleNanoappInfo(appInfoList);
443 }
444 }
445
onTransactionResult(uint32_t transactionId,bool success)446 void ContextHub::onTransactionResult(uint32_t transactionId, bool success) {
447 std::unique_lock<std::mutex> lock(mSynchronousLoadUnloadMutex);
448 if (mSynchronousLoadUnloadTransactionId &&
449 transactionId == *mSynchronousLoadUnloadTransactionId) {
450 mSynchronousLoadUnloadSuccess = success;
451 mSynchronousLoadUnloadCondVar.notify_all();
452 } else {
453 std::lock_guard<std::mutex> callbackLock(mCallbackMutex);
454 if (mCallback != nullptr) {
455 mCallback->handleTransactionResult(transactionId, success);
456 }
457 }
458 }
459
onContextHubConnected(bool restart)460 void ContextHub::onContextHubConnected(bool restart) {
461 // Handle any initialization on the first CHRE connection.
462 if (!restart) {
463 if (mV4Impl) mV4Impl->init();
464 return;
465 }
466
467 if (mV4Impl) {
468 mV4Impl->onChreDisconnected();
469 mV4Impl->onChreRestarted();
470 }
471
472 std::lock_guard<std::mutex> lock(mCallbackMutex);
473 mIsWifiAvailable.reset();
474 {
475 std::lock_guard<std::mutex> endpointLock(mConnectedHostEndpointsMutex);
476 mConnectedHostEndpoints.clear();
477 mEventLogger.logContextHubRestart();
478 }
479 if (mCallback != nullptr) {
480 mCallback->handleContextHubAsyncEvent(AsyncEventType::RESTARTED);
481 }
482 }
483
onDebugDumpData(const::chre::fbs::DebugDumpDataT & data)484 void ContextHub::onDebugDumpData(const ::chre::fbs::DebugDumpDataT &data) {
485 auto str = std::string(reinterpret_cast<const char *>(data.debug_str.data()),
486 data.debug_str.size());
487 debugDumpAppend(str);
488 }
489
onDebugDumpComplete(const::chre::fbs::DebugDumpResponseT &)490 void ContextHub::onDebugDumpComplete(
491 const ::chre::fbs::DebugDumpResponseT & /* response */) {
492 debugDumpComplete();
493 }
494
onContextHubV4Message(const::chre::fbs::ChreMessageUnion & message)495 bool ContextHub::onContextHubV4Message(
496 const ::chre::fbs::ChreMessageUnion &message) {
497 if (mV4Impl) return mV4Impl->handleMessageFromChre(message);
498 return false;
499 }
500
handleServiceDeath()501 void ContextHub::handleServiceDeath() {
502 LOGI("Context Hub Service died ...");
503 {
504 std::lock_guard<std::mutex> lock(mCallbackMutex);
505 mCallback.reset();
506 }
507 {
508 std::lock_guard<std::mutex> lock(mConnectedHostEndpointsMutex);
509 mConnectedHostEndpoints.clear();
510 }
511 }
512
onServiceDied(void * cookie)513 void ContextHub::onServiceDied(void *cookie) {
514 auto *contexthub = static_cast<ContextHub *>(cookie);
515 contexthub->handleServiceDeath();
516 }
517
dump(int fd,const char **,uint32_t)518 binder_status_t ContextHub::dump(int fd, const char ** /* args */,
519 uint32_t /* numArgs */) {
520 debugDumpStart(fd);
521 debugDumpFinish();
522 return STATUS_OK;
523 }
524
debugDumpFinish()525 void ContextHub::debugDumpFinish() {
526 if (checkDebugFd()) {
527 const std::string &dump = mEventLogger.dump();
528 writeToDebugFile(dump.c_str());
529 writeToDebugFile("\n-- End of CHRE/ASH debug info --\n");
530 invalidateDebugFd();
531 }
532 }
533
writeToDebugFile(const char * str)534 void ContextHub::writeToDebugFile(const char *str) {
535 if (!::android::base::WriteStringToFd(std::string(str), getDebugFd())) {
536 LOGW("Failed to write %zu bytes to debug dump fd", strlen(str));
537 }
538 }
539
enableTestMode()540 ScopedAStatus ContextHub::enableTestMode() {
541 std::unique_lock<std::mutex> lock(mTestModeMutex);
542
543 bool success = false;
544 std::vector<int64_t> loadedNanoappIds;
545 std::vector<int64_t> preloadedNanoappIds;
546 std::vector<int64_t> nanoappIdsToUnload;
547 if (mIsTestModeEnabled) {
548 success = true;
549 } else if (mConnection->isLoadTransactionPending()) {
550 /**
551 * There is already a pending load transaction. We cannot change the test
552 * mode state if there is a pending load transaction. We do not consider
553 * pending unload transactions as they can happen asynchronously and
554 * multiple at a time.
555 */
556 LOGE("There exists a pending load transaction. Cannot enable test mode.");
557 } else if (!queryNanoappsInternal(kDefaultHubId, &loadedNanoappIds)) {
558 LOGE("Could not query nanoapps to enable test mode.");
559 } else if (!getPreloadedNanoappIds(kDefaultHubId, &preloadedNanoappIds)
560 .isOk()) {
561 LOGE("Unable to get preloaded nanoapp IDs from the config file.");
562 } else {
563 std::sort(loadedNanoappIds.begin(), loadedNanoappIds.end());
564 std::sort(preloadedNanoappIds.begin(), preloadedNanoappIds.end());
565
566 // Calculate the system nanoapp IDs. They are preloaded, but not loaded.
567 mSystemNanoappIds.clear();
568 std::set_difference(preloadedNanoappIds.begin(), preloadedNanoappIds.end(),
569 loadedNanoappIds.begin(), loadedNanoappIds.end(),
570 std::back_inserter(mSystemNanoappIds));
571
572 /*
573 * Unload all preloaded and loaded nanoapps (set intersection).
574 * Both vectors need to be sorted for std::set_intersection to work.
575 * We explicitly choose not to use std::set here to avoid the
576 * copying cost as well as the tree balancing cost for the
577 * red-black tree.
578 */
579 std::set_intersection(loadedNanoappIds.begin(), loadedNanoappIds.end(),
580 preloadedNanoappIds.begin(),
581 preloadedNanoappIds.end(),
582 std::back_inserter(nanoappIdsToUnload));
583 if (!unloadNanoappsInternal(kDefaultHubId, nanoappIdsToUnload)) {
584 LOGE("Unable to unload all loaded and preloaded nanoapps.");
585 }
586 success = true;
587 }
588
589 if (success) {
590 mIsTestModeEnabled = true;
591 LOGI("Successfully enabled test mode.");
592 return ScopedAStatus::ok();
593 } else {
594 return ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
595 }
596 }
597
disableTestMode()598 ScopedAStatus ContextHub::disableTestMode() {
599 std::unique_lock<std::mutex> lock(mTestModeMutex);
600
601 bool success = false;
602 std::vector<chrePreloadedNanoappInfo> preloadedNanoapps;
603 std::string preloadedNanoappDirectory;
604 if (!mIsTestModeEnabled) {
605 success = true;
606 } else if (mConnection->isLoadTransactionPending()) {
607 /**
608 * There is already a pending load transaction. We cannot change the test
609 * mode state if there is a pending load transaction. We do not consider
610 * pending unload transactions as they can happen asynchronously and
611 * multiple at a time.
612 */
613 LOGE("There exists a pending load transaction. Cannot disable test mode.");
614 } else if (!getPreloadedNanoappIdsFromConfigFile(
615 preloadedNanoapps, &preloadedNanoappDirectory)) {
616 LOGE("Unable to get preloaded nanoapp IDs from the config file.");
617 } else {
618 std::vector<NanoappBinary> nanoappsToLoad = selectPreloadedNanoappsToLoad(
619 preloadedNanoapps, preloadedNanoappDirectory);
620
621 if (!loadNanoappsInternal(kDefaultHubId, nanoappsToLoad)) {
622 LOGE("Unable to load all preloaded, non-system nanoapps.");
623 }
624 // Any attempt to load non-test nanoapps should disable test mode, even if
625 // not all nanoapps are successfully loaded.
626 success = true;
627 }
628
629 if (success) {
630 mIsTestModeEnabled = false;
631 LOGI("Successfully disabled test mode.");
632 return ScopedAStatus::ok();
633 } else {
634 return ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
635 }
636 }
637
queryNanoappsInternal(int32_t contextHubId,std::vector<int64_t> * nanoappIdList)638 bool ContextHub::queryNanoappsInternal(int32_t contextHubId,
639 std::vector<int64_t> *nanoappIdList) {
640 if (contextHubId != kDefaultHubId) {
641 LOGE("Invalid ID %" PRId32, contextHubId);
642 return false;
643 }
644
645 std::unique_lock<std::mutex> lock(mQueryNanoappsInternalMutex);
646 mQueryNanoappsInternalList.reset();
647
648 bool success =
649 queryNanoapps(contextHubId).isOk() &&
650 mQueryNanoappsInternalCondVar.wait_for(lock, kTestModeTimeout, [this]() {
651 return mQueryNanoappsInternalList.has_value();
652 });
653 if (success && nanoappIdList != nullptr) {
654 std::transform(
655 mQueryNanoappsInternalList->begin(), mQueryNanoappsInternalList->end(),
656 std::back_inserter(*nanoappIdList),
657 [](const NanoappInfo &nanoapp) { return nanoapp.nanoappId; });
658 }
659 return success;
660 }
661
loadNanoappInternal(const NanoappBinary & appBinary,int32_t transactionId)662 bool ContextHub::loadNanoappInternal(const NanoappBinary &appBinary,
663 int32_t transactionId) {
664 uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
665 (appBinary.targetChreApiMinorVersion << 16);
666 FragmentedLoadTransaction transaction(
667 transactionId, appBinary.nanoappId, appBinary.nanoappVersion,
668 appBinary.flags, targetApiVersion, appBinary.customBinary);
669 bool success = mConnection->loadNanoapp(transaction);
670 mEventLogger.logNanoappLoad(appBinary.nanoappId,
671 appBinary.customBinary.size(),
672 appBinary.nanoappVersion, success);
673 return success;
674 }
675
loadNanoappsInternal(int32_t contextHubId,const std::vector<NanoappBinary> & nanoappBinaryList)676 bool ContextHub::loadNanoappsInternal(
677 int32_t contextHubId, const std::vector<NanoappBinary> &nanoappBinaryList) {
678 if (contextHubId != kDefaultHubId) {
679 LOGE("Invalid ID %" PRId32, contextHubId);
680 return false;
681 }
682
683 std::unique_lock<std::mutex> lock(mSynchronousLoadUnloadMutex);
684 mSynchronousLoadUnloadTransactionId = kStartingInternalTransactionId;
685
686 for (const NanoappBinary &nanoappToLoad : nanoappBinaryList) {
687 LOGI("Loading nanoapp with ID: 0x%016" PRIx64, nanoappToLoad.nanoappId);
688
689 bool success = false;
690 if (!loadNanoappInternal(nanoappToLoad,
691 *mSynchronousLoadUnloadTransactionId)) {
692 LOGE("Failed to request loading nanoapp with ID 0x%" PRIx64,
693 nanoappToLoad.nanoappId);
694 } else {
695 mSynchronousLoadUnloadSuccess.reset();
696 mSynchronousLoadUnloadCondVar.wait_for(lock, kTestModeTimeout, [this]() {
697 return mSynchronousLoadUnloadSuccess.has_value();
698 });
699 if (mSynchronousLoadUnloadSuccess.has_value() &&
700 *mSynchronousLoadUnloadSuccess) {
701 LOGI("Successfully loaded nanoapp with ID: 0x%016" PRIx64,
702 nanoappToLoad.nanoappId);
703 success = true;
704 }
705 }
706
707 if (!success) {
708 LOGE("Failed to load nanoapp with ID 0x%" PRIx64,
709 nanoappToLoad.nanoappId);
710 }
711 ++(*mSynchronousLoadUnloadTransactionId);
712 }
713
714 return true;
715 }
716
unloadNanoappInternal(int64_t appId,int32_t transactionId)717 bool ContextHub::unloadNanoappInternal(int64_t appId, int32_t transactionId) {
718 bool success = mConnection->unloadNanoapp(appId, transactionId);
719 mEventLogger.logNanoappUnload(appId, success);
720 return success;
721 }
722
unloadNanoappsInternal(int32_t contextHubId,const std::vector<int64_t> & nanoappIdList)723 bool ContextHub::unloadNanoappsInternal(
724 int32_t contextHubId, const std::vector<int64_t> &nanoappIdList) {
725 if (contextHubId != kDefaultHubId) {
726 LOGE("Invalid ID %" PRId32, contextHubId);
727 return false;
728 }
729
730 std::unique_lock<std::mutex> lock(mSynchronousLoadUnloadMutex);
731 mSynchronousLoadUnloadTransactionId = kStartingInternalTransactionId;
732
733 for (int64_t nanoappIdToUnload : nanoappIdList) {
734 LOGI("Unloading nanoapp with ID: 0x%016" PRIx64, nanoappIdToUnload);
735
736 bool success = false;
737 if (!unloadNanoappInternal(nanoappIdToUnload,
738 *mSynchronousLoadUnloadTransactionId)) {
739 LOGE("Failed to request unloading nanoapp with ID 0x%" PRIx64,
740 nanoappIdToUnload);
741 } else {
742 mSynchronousLoadUnloadSuccess.reset();
743 mSynchronousLoadUnloadCondVar.wait_for(lock, kTestModeTimeout, [this]() {
744 return mSynchronousLoadUnloadSuccess.has_value();
745 });
746 if (mSynchronousLoadUnloadSuccess.has_value() &&
747 *mSynchronousLoadUnloadSuccess) {
748 LOGI("Successfully unloaded nanoapp with ID: 0x%016" PRIx64,
749 nanoappIdToUnload);
750
751 success = true;
752 }
753 }
754
755 if (!success) {
756 LOGE("Failed to unload nanoapp with ID 0x%" PRIx64, nanoappIdToUnload);
757 }
758 ++(*mSynchronousLoadUnloadTransactionId);
759 }
760
761 return true;
762 }
763
getPreloadedNanoappIdsFromConfigFile(std::vector<chrePreloadedNanoappInfo> & out_preloadedNanoapps,std::string * out_directory) const764 bool ContextHub::getPreloadedNanoappIdsFromConfigFile(
765 std::vector<chrePreloadedNanoappInfo> &out_preloadedNanoapps,
766 std::string *out_directory) const {
767 std::vector<std::string> nanoappNames;
768 std::string directory;
769
770 bool success = getPreloadedNanoappsFromConfigFile(
771 kPreloadedNanoappsConfigPath, directory, nanoappNames);
772 if (!success) {
773 LOGE("Failed to parse preloaded nanoapps config file");
774 }
775
776 for (const std::string &nanoappName : nanoappNames) {
777 std::string headerFile = directory + "/" + nanoappName + ".napp_header";
778 std::vector<uint8_t> headerBuffer;
779 if (!readFileContents(headerFile.c_str(), headerBuffer)) {
780 LOGE("Cannot read header file: %s", headerFile.c_str());
781 continue;
782 }
783
784 if (headerBuffer.size() != sizeof(NanoAppBinaryHeader)) {
785 LOGE("Header size mismatch");
786 continue;
787 }
788
789 const auto *appHeader =
790 reinterpret_cast<const NanoAppBinaryHeader *>(headerBuffer.data());
791 out_preloadedNanoapps.emplace_back(static_cast<int64_t>(appHeader->appId),
792 nanoappName, *appHeader);
793 }
794
795 if (out_directory != nullptr) {
796 *out_directory = directory;
797 }
798
799 return true;
800 }
801
selectPreloadedNanoappsToLoad(std::vector<chrePreloadedNanoappInfo> & preloadedNanoapps,const std::string & preloadedNanoappDirectory)802 std::vector<NanoappBinary> ContextHub::selectPreloadedNanoappsToLoad(
803 std::vector<chrePreloadedNanoappInfo> &preloadedNanoapps,
804 const std::string &preloadedNanoappDirectory) {
805 std::vector<NanoappBinary> nanoappsToLoad;
806
807 for (auto &preloadedNanoapp : preloadedNanoapps) {
808 int64_t nanoappId = preloadedNanoapp.id;
809
810 // A nanoapp is a system nanoapp if it is in the preloaded nanoapp list
811 // but not in the loaded nanoapp list as CHRE hides system nanoapps
812 // from the HAL.
813 bool isSystemNanoapp =
814 std::any_of(mSystemNanoappIds.begin(), mSystemNanoappIds.end(),
815 [nanoappId](int64_t systemNanoappId) {
816 return systemNanoappId == nanoappId;
817 });
818 if (!isSystemNanoapp) {
819 std::vector<uint8_t> nanoappBuffer;
820 std::string nanoappFile =
821 preloadedNanoappDirectory + "/" + preloadedNanoapp.name + ".so";
822 if (!readFileContents(nanoappFile.c_str(), nanoappBuffer)) {
823 LOGE("Cannot read header file: %s", nanoappFile.c_str());
824 } else {
825 NanoappBinary nanoapp;
826 nanoapp.nanoappId = preloadedNanoapp.header.appId;
827 nanoapp.nanoappVersion = preloadedNanoapp.header.appVersion;
828 nanoapp.flags = preloadedNanoapp.header.flags;
829 nanoapp.targetChreApiMajorVersion =
830 preloadedNanoapp.header.targetChreApiMajorVersion;
831 nanoapp.targetChreApiMinorVersion =
832 preloadedNanoapp.header.targetChreApiMinorVersion;
833 nanoapp.customBinary = nanoappBuffer;
834
835 nanoappsToLoad.push_back(nanoapp);
836 }
837 }
838 }
839 return nanoappsToLoad;
840 }
841
842 } // namespace aidl::android::hardware::contexthub
843