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
onNanoappMessage(const::chre::fbs::NanoappMessageT & message)354 void ContextHub::onNanoappMessage(const ::chre::fbs::NanoappMessageT &message) {
355 std::lock_guard<std::mutex> lock(mCallbackMutex);
356 if (mCallback != nullptr) {
357 if (message.host_endpoint > kMaxValidHostEndPointId &&
358 message.host_endpoint != CHRE_HOST_ENDPOINT_BROADCAST) {
359 return;
360 }
361
362 mEventLogger.logMessageFromNanoapp(message);
363 ContextHubMessage outMessage;
364 outMessage.nanoappId = message.app_id;
365 outMessage.hostEndPoint = message.host_endpoint;
366 outMessage.messageType = message.message_type;
367 outMessage.messageBody = message.message;
368 outMessage.permissions = chreToAndroidPermissions(message.permissions);
369
370 std::vector<std::string> messageContentPerms =
371 chreToAndroidPermissions(message.message_permissions);
372 mCallback->handleContextHubMessage(outMessage, messageContentPerms);
373 }
374 }
375
onNanoappListResponse(const::chre::fbs::NanoappListResponseT & response)376 void ContextHub::onNanoappListResponse(
377 const ::chre::fbs::NanoappListResponseT &response) {
378 std::vector<NanoappInfo> appInfoList;
379 for (const std::unique_ptr<::chre::fbs::NanoappListEntryT> &nanoapp :
380 response.nanoapps) {
381 // TODO(b/245202050): determine if this is really required, and if so, have
382 // HostProtocolHost strip out null entries as part of decode
383 if (nanoapp == nullptr) {
384 continue;
385 }
386
387 LOGV("App 0x%016" PRIx64 " ver 0x%" PRIx32 " permissions 0x%" PRIx32
388 " enabled %d system %d",
389 nanoapp->app_id, nanoapp->version, nanoapp->permissions,
390 nanoapp->enabled, nanoapp->is_system);
391 if (!nanoapp->is_system) {
392 NanoappInfo appInfo;
393
394 appInfo.nanoappId = nanoapp->app_id;
395 appInfo.nanoappVersion = nanoapp->version;
396 appInfo.enabled = nanoapp->enabled;
397 appInfo.permissions = chreToAndroidPermissions(nanoapp->permissions);
398
399 std::vector<NanoappRpcService> rpcServices;
400 for (const auto &service : nanoapp->rpc_services) {
401 NanoappRpcService aidlService;
402 aidlService.id = service->id;
403 aidlService.version = service->version;
404 rpcServices.emplace_back(aidlService);
405 }
406 appInfo.rpcServices = rpcServices;
407
408 appInfoList.push_back(appInfo);
409 }
410 }
411
412 {
413 std::lock_guard<std::mutex> lock(mQueryNanoappsInternalMutex);
414 if (!mQueryNanoappsInternalList) {
415 mQueryNanoappsInternalList = appInfoList;
416 mQueryNanoappsInternalCondVar.notify_all();
417 }
418 }
419
420 std::lock_guard<std::mutex> lock(mCallbackMutex);
421 if (mCallback != nullptr) {
422 mCallback->handleNanoappInfo(appInfoList);
423 }
424 }
425
onTransactionResult(uint32_t transactionId,bool success)426 void ContextHub::onTransactionResult(uint32_t transactionId, bool success) {
427 std::unique_lock<std::mutex> lock(mSynchronousLoadUnloadMutex);
428 if (mSynchronousLoadUnloadTransactionId &&
429 transactionId == *mSynchronousLoadUnloadTransactionId) {
430 mSynchronousLoadUnloadSuccess = success;
431 mSynchronousLoadUnloadCondVar.notify_all();
432 } else {
433 std::lock_guard<std::mutex> callbackLock(mCallbackMutex);
434 if (mCallback != nullptr) {
435 mCallback->handleTransactionResult(transactionId, success);
436 }
437 }
438 }
439
onContextHubRestarted()440 void ContextHub::onContextHubRestarted() {
441 std::lock_guard<std::mutex> lock(mCallbackMutex);
442 mIsWifiAvailable.reset();
443 {
444 std::lock_guard<std::mutex> endpointLock(mConnectedHostEndpointsMutex);
445 mConnectedHostEndpoints.clear();
446 mEventLogger.logContextHubRestart();
447 }
448 if (mCallback != nullptr) {
449 mCallback->handleContextHubAsyncEvent(AsyncEventType::RESTARTED);
450 }
451 }
452
onDebugDumpData(const::chre::fbs::DebugDumpDataT & data)453 void ContextHub::onDebugDumpData(const ::chre::fbs::DebugDumpDataT &data) {
454 auto str = std::string(reinterpret_cast<const char *>(data.debug_str.data()),
455 data.debug_str.size());
456 debugDumpAppend(str);
457 }
458
onDebugDumpComplete(const::chre::fbs::DebugDumpResponseT &)459 void ContextHub::onDebugDumpComplete(
460 const ::chre::fbs::DebugDumpResponseT & /* response */) {
461 debugDumpComplete();
462 }
463
handleServiceDeath()464 void ContextHub::handleServiceDeath() {
465 LOGI("Context Hub Service died ...");
466 {
467 std::lock_guard<std::mutex> lock(mCallbackMutex);
468 mCallback.reset();
469 }
470 {
471 std::lock_guard<std::mutex> lock(mConnectedHostEndpointsMutex);
472 mConnectedHostEndpoints.clear();
473 }
474 }
475
onServiceDied(void * cookie)476 void ContextHub::onServiceDied(void *cookie) {
477 auto *contexthub = static_cast<ContextHub *>(cookie);
478 contexthub->handleServiceDeath();
479 }
480
dump(int fd,const char **,uint32_t)481 binder_status_t ContextHub::dump(int fd, const char ** /* args */,
482 uint32_t /* numArgs */) {
483 debugDumpStart(fd);
484 debugDumpFinish();
485 return STATUS_OK;
486 }
487
debugDumpFinish()488 void ContextHub::debugDumpFinish() {
489 if (checkDebugFd()) {
490 const std::string &dump = mEventLogger.dump();
491 writeToDebugFile(dump.c_str());
492 writeToDebugFile("\n-- End of CHRE/ASH debug info --\n");
493 invalidateDebugFd();
494 }
495 }
496
writeToDebugFile(const char * str)497 void ContextHub::writeToDebugFile(const char *str) {
498 if (!::android::base::WriteStringToFd(std::string(str), getDebugFd())) {
499 LOGW("Failed to write %zu bytes to debug dump fd", strlen(str));
500 }
501 }
502
enableTestMode()503 ScopedAStatus ContextHub::enableTestMode() {
504 std::unique_lock<std::mutex> lock(mTestModeMutex);
505
506 bool success = false;
507 std::vector<int64_t> loadedNanoappIds;
508 std::vector<int64_t> preloadedNanoappIds;
509 std::vector<int64_t> nanoappIdsToUnload;
510 if (mIsTestModeEnabled) {
511 success = true;
512 } else if (mConnection.isLoadTransactionPending()) {
513 /**
514 * There is already a pending load transaction. We cannot change the test
515 * mode state if there is a pending load transaction. We do not consider
516 * pending unload transactions as they can happen asynchronously and
517 * multiple at a time.
518 */
519 LOGE("There exists a pending load transaction. Cannot enable test mode.");
520 } else if (!queryNanoappsInternal(kDefaultHubId, &loadedNanoappIds)) {
521 LOGE("Could not query nanoapps to enable test mode.");
522 } else if (!getPreloadedNanoappIds(kDefaultHubId, &preloadedNanoappIds)
523 .isOk()) {
524 LOGE("Unable to get preloaded nanoapp IDs from the config file.");
525 } else {
526 std::sort(loadedNanoappIds.begin(), loadedNanoappIds.end());
527 std::sort(preloadedNanoappIds.begin(), preloadedNanoappIds.end());
528
529 // Calculate the system nanoapp IDs. They are preloaded, but not loaded.
530 mSystemNanoappIds.clear();
531 std::set_difference(preloadedNanoappIds.begin(), preloadedNanoappIds.end(),
532 loadedNanoappIds.begin(), loadedNanoappIds.end(),
533 std::back_inserter(mSystemNanoappIds));
534
535 /*
536 * Unload all preloaded and loaded nanoapps (set intersection).
537 * Both vectors need to be sorted for std::set_intersection to work.
538 * We explicitly choose not to use std::set here to avoid the
539 * copying cost as well as the tree balancing cost for the
540 * red-black tree.
541 */
542 std::set_intersection(loadedNanoappIds.begin(), loadedNanoappIds.end(),
543 preloadedNanoappIds.begin(),
544 preloadedNanoappIds.end(),
545 std::back_inserter(nanoappIdsToUnload));
546 if (!unloadNanoappsInternal(kDefaultHubId, nanoappIdsToUnload)) {
547 LOGE("Unable to unload all loaded and preloaded nanoapps.");
548 }
549 success = true;
550 }
551
552 if (success) {
553 mIsTestModeEnabled = true;
554 LOGI("Successfully enabled test mode.");
555 return ScopedAStatus::ok();
556 } else {
557 return ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
558 }
559 }
560
disableTestMode()561 ScopedAStatus ContextHub::disableTestMode() {
562 std::unique_lock<std::mutex> lock(mTestModeMutex);
563
564 bool success = false;
565 std::vector<chrePreloadedNanoappInfo> preloadedNanoapps;
566 std::string preloadedNanoappDirectory;
567 if (!mIsTestModeEnabled) {
568 success = true;
569 } else if (mConnection.isLoadTransactionPending()) {
570 /**
571 * There is already a pending load transaction. We cannot change the test
572 * mode state if there is a pending load transaction. We do not consider
573 * pending unload transactions as they can happen asynchronously and
574 * multiple at a time.
575 */
576 LOGE("There exists a pending load transaction. Cannot disable test mode.");
577 } else if (!getPreloadedNanoappIdsFromConfigFile(
578 preloadedNanoapps, &preloadedNanoappDirectory)) {
579 LOGE("Unable to get preloaded nanoapp IDs from the config file.");
580 } else {
581 std::vector<NanoappBinary> nanoappsToLoad = selectPreloadedNanoappsToLoad(
582 preloadedNanoapps, preloadedNanoappDirectory);
583
584 if (!loadNanoappsInternal(kDefaultHubId, nanoappsToLoad)) {
585 LOGE("Unable to load all preloaded, non-system nanoapps.");
586 }
587 // Any attempt to load non-test nanoapps should disable test mode, even if
588 // not all nanoapps are successfully loaded.
589 success = true;
590 }
591
592 if (success) {
593 mIsTestModeEnabled = false;
594 LOGI("Successfully disabled test mode.");
595 return ScopedAStatus::ok();
596 } else {
597 return ScopedAStatus::fromExceptionCode(EX_SERVICE_SPECIFIC);
598 }
599 }
600
queryNanoappsInternal(int32_t contextHubId,std::vector<int64_t> * nanoappIdList)601 bool ContextHub::queryNanoappsInternal(int32_t contextHubId,
602 std::vector<int64_t> *nanoappIdList) {
603 if (contextHubId != kDefaultHubId) {
604 LOGE("Invalid ID %" PRId32, contextHubId);
605 return false;
606 }
607
608 std::unique_lock<std::mutex> lock(mQueryNanoappsInternalMutex);
609 mQueryNanoappsInternalList.reset();
610
611 bool success =
612 queryNanoapps(contextHubId).isOk() &&
613 mQueryNanoappsInternalCondVar.wait_for(lock, kTestModeTimeout, [this]() {
614 return mQueryNanoappsInternalList.has_value();
615 });
616 if (success && nanoappIdList != nullptr) {
617 std::transform(
618 mQueryNanoappsInternalList->begin(), mQueryNanoappsInternalList->end(),
619 std::back_inserter(*nanoappIdList),
620 [](const NanoappInfo &nanoapp) { return nanoapp.nanoappId; });
621 }
622 return success;
623 }
624
loadNanoappInternal(const NanoappBinary & appBinary,int32_t transactionId)625 bool ContextHub::loadNanoappInternal(const NanoappBinary &appBinary,
626 int32_t transactionId) {
627 uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
628 (appBinary.targetChreApiMinorVersion << 16);
629 FragmentedLoadTransaction transaction(
630 transactionId, appBinary.nanoappId, appBinary.nanoappVersion,
631 appBinary.flags, targetApiVersion, appBinary.customBinary);
632 bool success = mConnection.loadNanoapp(transaction);
633 mEventLogger.logNanoappLoad(appBinary.nanoappId,
634 appBinary.customBinary.size(),
635 appBinary.nanoappVersion, success);
636 return success;
637 }
638
loadNanoappsInternal(int32_t contextHubId,const std::vector<NanoappBinary> & nanoappBinaryList)639 bool ContextHub::loadNanoappsInternal(
640 int32_t contextHubId, const std::vector<NanoappBinary> &nanoappBinaryList) {
641 if (contextHubId != kDefaultHubId) {
642 LOGE("Invalid ID %" PRId32, contextHubId);
643 return false;
644 }
645
646 std::unique_lock<std::mutex> lock(mSynchronousLoadUnloadMutex);
647 mSynchronousLoadUnloadTransactionId = kStartingInternalTransactionId;
648
649 for (const NanoappBinary &nanoappToLoad : nanoappBinaryList) {
650 LOGI("Loading nanoapp with ID: 0x%016" PRIx64, nanoappToLoad.nanoappId);
651
652 bool success = false;
653 if (!loadNanoappInternal(nanoappToLoad,
654 *mSynchronousLoadUnloadTransactionId)) {
655 LOGE("Failed to request loading nanoapp with ID 0x%" PRIx64,
656 nanoappToLoad.nanoappId);
657 } else {
658 mSynchronousLoadUnloadSuccess.reset();
659 mSynchronousLoadUnloadCondVar.wait_for(lock, kTestModeTimeout, [this]() {
660 return mSynchronousLoadUnloadSuccess.has_value();
661 });
662 if (mSynchronousLoadUnloadSuccess.has_value() &&
663 *mSynchronousLoadUnloadSuccess) {
664 LOGI("Successfully loaded nanoapp with ID: 0x%016" PRIx64,
665 nanoappToLoad.nanoappId);
666 success = true;
667 }
668 }
669
670 if (!success) {
671 LOGE("Failed to load nanoapp with ID 0x%" PRIx64,
672 nanoappToLoad.nanoappId);
673 }
674 ++(*mSynchronousLoadUnloadTransactionId);
675 }
676
677 return true;
678 }
679
unloadNanoappInternal(int64_t appId,int32_t transactionId)680 bool ContextHub::unloadNanoappInternal(int64_t appId, int32_t transactionId) {
681 bool success = mConnection.unloadNanoapp(appId, transactionId);
682 mEventLogger.logNanoappUnload(appId, success);
683 return success;
684 }
685
unloadNanoappsInternal(int32_t contextHubId,const std::vector<int64_t> & nanoappIdList)686 bool ContextHub::unloadNanoappsInternal(
687 int32_t contextHubId, const std::vector<int64_t> &nanoappIdList) {
688 if (contextHubId != kDefaultHubId) {
689 LOGE("Invalid ID %" PRId32, contextHubId);
690 return false;
691 }
692
693 std::unique_lock<std::mutex> lock(mSynchronousLoadUnloadMutex);
694 mSynchronousLoadUnloadTransactionId = kStartingInternalTransactionId;
695
696 for (int64_t nanoappIdToUnload : nanoappIdList) {
697 LOGI("Unloading nanoapp with ID: 0x%016" PRIx64, nanoappIdToUnload);
698
699 bool success = false;
700 if (!unloadNanoappInternal(nanoappIdToUnload,
701 *mSynchronousLoadUnloadTransactionId)) {
702 LOGE("Failed to request unloading nanoapp with ID 0x%" PRIx64,
703 nanoappIdToUnload);
704 } else {
705 mSynchronousLoadUnloadSuccess.reset();
706 mSynchronousLoadUnloadCondVar.wait_for(lock, kTestModeTimeout, [this]() {
707 return mSynchronousLoadUnloadSuccess.has_value();
708 });
709 if (mSynchronousLoadUnloadSuccess.has_value() &&
710 *mSynchronousLoadUnloadSuccess) {
711 LOGI("Successfully unloaded nanoapp with ID: 0x%016" PRIx64,
712 nanoappIdToUnload);
713
714 success = true;
715 }
716 }
717
718 if (!success) {
719 LOGE("Failed to unload nanoapp with ID 0x%" PRIx64, nanoappIdToUnload);
720 }
721 ++(*mSynchronousLoadUnloadTransactionId);
722 }
723
724 return true;
725 }
726
getPreloadedNanoappIdsFromConfigFile(std::vector<chrePreloadedNanoappInfo> & out_preloadedNanoapps,std::string * out_directory) const727 bool ContextHub::getPreloadedNanoappIdsFromConfigFile(
728 std::vector<chrePreloadedNanoappInfo> &out_preloadedNanoapps,
729 std::string *out_directory) const {
730 std::vector<std::string> nanoappNames;
731 std::string directory;
732
733 bool success = getPreloadedNanoappsFromConfigFile(
734 kPreloadedNanoappsConfigPath, directory, nanoappNames);
735 if (!success) {
736 LOGE("Failed to parse preloaded nanoapps config file");
737 }
738
739 for (const std::string &nanoappName : nanoappNames) {
740 std::string headerFile = directory + "/" + nanoappName + ".napp_header";
741 std::vector<uint8_t> headerBuffer;
742 if (!readFileContents(headerFile.c_str(), headerBuffer)) {
743 LOGE("Cannot read header file: %s", headerFile.c_str());
744 continue;
745 }
746
747 if (headerBuffer.size() != sizeof(NanoAppBinaryHeader)) {
748 LOGE("Header size mismatch");
749 continue;
750 }
751
752 const auto *appHeader =
753 reinterpret_cast<const NanoAppBinaryHeader *>(headerBuffer.data());
754 out_preloadedNanoapps.emplace_back(static_cast<int64_t>(appHeader->appId),
755 nanoappName, *appHeader);
756 }
757
758 if (out_directory != nullptr) {
759 *out_directory = directory;
760 }
761
762 return true;
763 }
764
selectPreloadedNanoappsToLoad(std::vector<chrePreloadedNanoappInfo> & preloadedNanoapps,const std::string & preloadedNanoappDirectory)765 std::vector<NanoappBinary> ContextHub::selectPreloadedNanoappsToLoad(
766 std::vector<chrePreloadedNanoappInfo> &preloadedNanoapps,
767 const std::string &preloadedNanoappDirectory) {
768 std::vector<NanoappBinary> nanoappsToLoad;
769
770 for (auto &preloadedNanoapp : preloadedNanoapps) {
771 int64_t nanoappId = preloadedNanoapp.id;
772
773 // A nanoapp is a system nanoapp if it is in the preloaded nanoapp list
774 // but not in the loaded nanoapp list as CHRE hides system nanoapps
775 // from the HAL.
776 bool isSystemNanoapp =
777 std::any_of(mSystemNanoappIds.begin(), mSystemNanoappIds.end(),
778 [nanoappId](int64_t systemNanoappId) {
779 return systemNanoappId == nanoappId;
780 });
781 if (!isSystemNanoapp) {
782 std::vector<uint8_t> nanoappBuffer;
783 std::string nanoappFile =
784 preloadedNanoappDirectory + "/" + preloadedNanoapp.name + ".so";
785 if (!readFileContents(nanoappFile.c_str(), nanoappBuffer)) {
786 LOGE("Cannot read header file: %s", nanoappFile.c_str());
787 } else {
788 NanoappBinary nanoapp;
789 nanoapp.nanoappId = preloadedNanoapp.header.appId;
790 nanoapp.nanoappVersion = preloadedNanoapp.header.appVersion;
791 nanoapp.flags = preloadedNanoapp.header.flags;
792 nanoapp.targetChreApiMajorVersion =
793 preloadedNanoapp.header.targetChreApiMajorVersion;
794 nanoapp.targetChreApiMinorVersion =
795 preloadedNanoapp.header.targetChreApiMinorVersion;
796 nanoapp.customBinary = nanoappBuffer;
797
798 nanoappsToLoad.push_back(nanoapp);
799 }
800 }
801 }
802 return nanoappsToLoad;
803 }
804
805 } // namespace aidl::android::hardware::contexthub
806