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