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