• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/fragmented_load_transaction.h"
21 #include "chre_host/host_protocol_host.h"
22 #include "permissions_util.h"
23 
24 namespace aidl {
25 namespace android {
26 namespace hardware {
27 namespace contexthub {
28 
29 // Aliased for consistency with the way these symbols are referenced in
30 // CHRE-side code
31 namespace fbs = ::chre::fbs;
32 
33 using ::android::chre::FragmentedLoadTransaction;
34 using ::android::chre::getStringFromByteVector;
35 using ::android::hardware::contexthub::common::implementation::
36     chreToAndroidPermissions;
37 using ::android::hardware::contexthub::common::implementation::
38     kSupportedPermissions;
39 using ::ndk::ScopedAStatus;
40 
41 namespace {
42 constexpr uint32_t kDefaultHubId = 0;
43 
extractChreApiMajorVersion(uint32_t chreVersion)44 inline constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) {
45   return static_cast<uint8_t>(chreVersion >> 24);
46 }
47 
extractChreApiMinorVersion(uint32_t chreVersion)48 inline constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) {
49   return static_cast<uint8_t>(chreVersion >> 16);
50 }
51 
extractChrePatchVersion(uint32_t chreVersion)52 inline constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) {
53   return static_cast<uint16_t>(chreVersion);
54 }
55 
getFbsSetting(const Setting & setting,fbs::Setting * fbsSetting)56 bool getFbsSetting(const Setting &setting, fbs::Setting *fbsSetting) {
57   bool foundSetting = true;
58 
59   switch (setting) {
60     case Setting::LOCATION:
61       *fbsSetting = fbs::Setting::LOCATION;
62       break;
63     case Setting::AIRPLANE_MODE:
64       *fbsSetting = fbs::Setting::AIRPLANE_MODE;
65       break;
66     case Setting::MICROPHONE:
67       *fbsSetting = fbs::Setting::MICROPHONE;
68       break;
69     default:
70       foundSetting = false;
71       ALOGE("Setting update with invalid enum value %hhu", setting);
72       break;
73   }
74 
75   return foundSetting;
76 }
77 
toServiceSpecificError(bool success)78 ScopedAStatus toServiceSpecificError(bool success) {
79   return success ? ScopedAStatus::ok()
80                  : ScopedAStatus::fromServiceSpecificError(
81                        BnContextHub::EX_CONTEXT_HUB_UNSPECIFIED);
82 }
83 
84 }  // anonymous namespace
85 
getContextHubs(std::vector<ContextHubInfo> * out_contextHubInfos)86 ScopedAStatus ContextHub::getContextHubs(
87     std::vector<ContextHubInfo> *out_contextHubInfos) {
88   ::chre::fbs::HubInfoResponseT response;
89   bool success = mConnection.getContextHubs(&response);
90   if (success) {
91     ContextHubInfo hub;
92     hub.name = getStringFromByteVector(response.name);
93     hub.vendor = getStringFromByteVector(response.vendor);
94     hub.toolchain = getStringFromByteVector(response.toolchain);
95     hub.id = kDefaultHubId;
96     hub.peakMips = response.peak_mips;
97     hub.maxSupportedMessageLengthBytes = response.max_msg_len;
98     hub.chrePlatformId = response.platform_id;
99 
100     uint32_t version = response.chre_platform_version;
101     hub.chreApiMajorVersion = extractChreApiMajorVersion(version);
102     hub.chreApiMinorVersion = extractChreApiMinorVersion(version);
103     hub.chrePatchVersion = extractChrePatchVersion(version);
104 
105     hub.supportedPermissions = kSupportedPermissions;
106 
107     out_contextHubInfos->push_back(hub);
108   }
109 
110   return ndk::ScopedAStatus::ok();
111 }
112 
loadNanoapp(int32_t contextHubId,const NanoappBinary & appBinary,int32_t transactionId)113 ScopedAStatus ContextHub::loadNanoapp(int32_t contextHubId,
114                                       const NanoappBinary &appBinary,
115                                       int32_t transactionId) {
116   if (contextHubId != kDefaultHubId) {
117     ALOGE("Invalid ID %" PRId32, contextHubId);
118     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
119   } else {
120     uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
121                                 (appBinary.targetChreApiMinorVersion << 16);
122     FragmentedLoadTransaction transaction(
123         transactionId, appBinary.nanoappId, appBinary.nanoappVersion,
124         appBinary.flags, targetApiVersion, appBinary.customBinary);
125     const bool success = mConnection.loadNanoapp(transaction);
126     mEventLogger.logNanoappLoad(appBinary, success);
127     return toServiceSpecificError(success);
128   }
129 }
130 
unloadNanoapp(int32_t contextHubId,int64_t appId,int32_t transactionId)131 ScopedAStatus ContextHub::unloadNanoapp(int32_t contextHubId, int64_t appId,
132                                         int32_t transactionId) {
133   if (contextHubId != kDefaultHubId) {
134     ALOGE("Invalid ID %" PRId32, contextHubId);
135     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
136   } else {
137     const bool success = mConnection.unloadNanoapp(appId, transactionId);
138     mEventLogger.logNanoappUnload(appId, success);
139     return toServiceSpecificError(success);
140   }
141 }
142 
disableNanoapp(int32_t,int64_t appId,int32_t)143 ScopedAStatus ContextHub::disableNanoapp(int32_t /* contextHubId */,
144                                          int64_t appId,
145                                          int32_t /* transactionId */) {
146   ALOGW("Attempted to disable app ID 0x%016" PRIx64 ", but not supported",
147         appId);
148   return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
149 }
150 
enableNanoapp(int32_t,int64_t appId,int32_t)151 ScopedAStatus ContextHub::enableNanoapp(int32_t /* contextHubId */,
152                                         int64_t appId,
153                                         int32_t /* transactionId */) {
154   ALOGW("Attempted to enable app ID 0x%016" PRIx64 ", but not supported",
155         appId);
156   return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
157 }
158 
onSettingChanged(Setting setting,bool enabled)159 ScopedAStatus ContextHub::onSettingChanged(Setting setting, bool enabled) {
160   mSettingEnabled[setting] = enabled;
161   fbs::Setting fbsSetting;
162   bool isWifiOrBtSetting =
163       (setting == Setting::WIFI_MAIN || setting == Setting::WIFI_SCANNING ||
164        setting == Setting::BT_MAIN || setting == Setting::BT_SCANNING);
165 
166   if (!isWifiOrBtSetting && getFbsSetting(setting, &fbsSetting)) {
167     mConnection.sendSettingChangedNotification(fbsSetting,
168                                                toFbsSettingState(enabled));
169   }
170 
171   bool isWifiMainEnabled = isSettingEnabled(Setting::WIFI_MAIN);
172   bool isWifiScanEnabled = isSettingEnabled(Setting::WIFI_SCANNING);
173   bool isAirplaneModeEnabled = isSettingEnabled(Setting::AIRPLANE_MODE);
174 
175   // Because the airplane mode impact on WiFi is not standardized in Android,
176   // we write a specific handling in the Context Hub HAL to inform CHRE.
177   // The following definition is a default one, and can be adjusted
178   // appropriately if necessary.
179   bool isWifiAvailable = isAirplaneModeEnabled
180                              ? (isWifiMainEnabled)
181                              : (isWifiMainEnabled || isWifiScanEnabled);
182   if (!mIsWifiAvailable.has_value() || (isWifiAvailable != mIsWifiAvailable)) {
183     mConnection.sendSettingChangedNotification(
184         fbs::Setting::WIFI_AVAILABLE, toFbsSettingState(isWifiAvailable));
185     mIsWifiAvailable = isWifiAvailable;
186   }
187 
188   // The BT switches determine whether we can BLE scan which is why things are
189   // mapped like this into CHRE.
190   bool isBtMainEnabled = isSettingEnabled(Setting::BT_MAIN);
191   bool isBtScanEnabled = isSettingEnabled(Setting::BT_SCANNING);
192   bool isBleAvailable = isBtMainEnabled || isBtScanEnabled;
193   if (!mIsBleAvailable.has_value() || (isBleAvailable != mIsBleAvailable)) {
194     mConnection.sendSettingChangedNotification(
195         fbs::Setting::BLE_AVAILABLE, toFbsSettingState(isBleAvailable));
196     mIsBleAvailable = isBleAvailable;
197   }
198 
199   return ndk::ScopedAStatus::ok();
200 }
201 
queryNanoapps(int32_t contextHubId)202 ScopedAStatus ContextHub::queryNanoapps(int32_t contextHubId) {
203   if (contextHubId != kDefaultHubId) {
204     ALOGE("Invalid ID %" PRId32, contextHubId);
205     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
206   } else {
207     return toServiceSpecificError(mConnection.queryNanoapps());
208   }
209 }
210 
registerCallback(int32_t contextHubId,const std::shared_ptr<IContextHubCallback> & cb)211 ScopedAStatus ContextHub::registerCallback(
212     int32_t contextHubId, const std::shared_ptr<IContextHubCallback> &cb) {
213   if (contextHubId != kDefaultHubId) {
214     ALOGE("Invalid ID %" PRId32, contextHubId);
215     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
216   } else {
217     std::lock_guard<std::mutex> lock(mCallbackMutex);
218     if (mCallback != nullptr) {
219       binder_status_t binder_status = AIBinder_unlinkToDeath(
220           mCallback->asBinder().get(), mDeathRecipient.get(), this);
221       if (binder_status != STATUS_OK) {
222         ALOGE("Failed to unlink to death");
223       }
224     }
225 
226     mCallback = cb;
227 
228     if (cb != nullptr) {
229       binder_status_t binder_status = AIBinder_linkToDeath(
230           cb->asBinder().get(), mDeathRecipient.get(), this);
231 
232       if (binder_status != STATUS_OK) {
233         ALOGE("Failed to link to death");
234       }
235     }
236 
237     return ScopedAStatus::ok();
238   }
239 }
240 
sendMessageToHub(int32_t contextHubId,const ContextHubMessage & message)241 ScopedAStatus ContextHub::sendMessageToHub(int32_t contextHubId,
242                                            const ContextHubMessage &message) {
243   if (contextHubId != kDefaultHubId) {
244     ALOGE("Invalid ID %" PRId32, contextHubId);
245     return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
246   }
247 
248   const bool success = mConnection.sendMessageToHub(
249       message.nanoappId, message.messageType, message.hostEndPoint,
250       message.messageBody.data(), message.messageBody.size());
251   mEventLogger.logMessageToNanoapp(message, success);
252 
253   return toServiceSpecificError(success);
254 }
255 
onHostEndpointConnected(const HostEndpointInfo & in_info)256 ScopedAStatus ContextHub::onHostEndpointConnected(
257     const HostEndpointInfo &in_info) {
258   std::lock_guard<std::mutex> lock(mConnectedHostEndpointsMutex);
259   mConnectedHostEndpoints.insert(in_info.hostEndpointId);
260 
261   uint8_t type = (in_info.type == HostEndpointInfo::Type::FRAMEWORK)
262                      ? CHRE_HOST_ENDPOINT_TYPE_FRAMEWORK
263                      : CHRE_HOST_ENDPOINT_TYPE_APP;
264 
265   mConnection.onHostEndpointConnected(
266       in_info.hostEndpointId, type, in_info.packageName.value_or(std::string()),
267       in_info.attributionTag.value_or(std::string()));
268 
269   return ndk::ScopedAStatus::ok();
270 }
271 
onHostEndpointDisconnected(char16_t in_hostEndpointId)272 ScopedAStatus ContextHub::onHostEndpointDisconnected(
273     char16_t in_hostEndpointId) {
274   std::lock_guard<std::mutex> lock(mConnectedHostEndpointsMutex);
275   if (mConnectedHostEndpoints.count(in_hostEndpointId) > 0) {
276     mConnectedHostEndpoints.erase(in_hostEndpointId);
277 
278     mConnection.onHostEndpointDisconnected(in_hostEndpointId);
279   } else {
280     ALOGE("Unknown host endpoint disconnected (ID: %" PRIu16 ")",
281           in_hostEndpointId);
282   }
283 
284   return ndk::ScopedAStatus::ok();
285 }
286 
onNanoappMessage(const::chre::fbs::NanoappMessageT & message)287 void ContextHub::onNanoappMessage(const ::chre::fbs::NanoappMessageT &message) {
288   std::lock_guard<std::mutex> lock(mCallbackMutex);
289   if (mCallback != nullptr) {
290     mEventLogger.logMessageFromNanoapp(message);
291     ContextHubMessage outMessage;
292     outMessage.nanoappId = message.app_id;
293     outMessage.hostEndPoint = message.host_endpoint;
294     outMessage.messageType = message.message_type;
295     outMessage.messageBody = message.message;
296     outMessage.permissions = chreToAndroidPermissions(message.permissions);
297 
298     std::vector<std::string> messageContentPerms =
299         chreToAndroidPermissions(message.message_permissions);
300     mCallback->handleContextHubMessage(outMessage, messageContentPerms);
301   }
302 }
303 
onNanoappListResponse(const::chre::fbs::NanoappListResponseT & response)304 void ContextHub::onNanoappListResponse(
305     const ::chre::fbs::NanoappListResponseT &response) {
306   std::lock_guard<std::mutex> lock(mCallbackMutex);
307   if (mCallback != nullptr) {
308     std::vector<NanoappInfo> appInfoList;
309 
310     for (const std::unique_ptr<::chre::fbs::NanoappListEntryT> &nanoapp :
311          response.nanoapps) {
312       // TODO: determine if this is really required, and if so, have
313       // HostProtocolHost strip out null entries as part of decode
314       if (nanoapp == nullptr) {
315         continue;
316       }
317 
318       ALOGV("App 0x%016" PRIx64 " ver 0x%" PRIx32 " permissions 0x%" PRIx32
319             " enabled %d system %d",
320             nanoapp->app_id, nanoapp->version, nanoapp->permissions,
321             nanoapp->enabled, nanoapp->is_system);
322       if (!nanoapp->is_system) {
323         NanoappInfo appInfo;
324 
325         appInfo.nanoappId = nanoapp->app_id;
326         appInfo.nanoappVersion = nanoapp->version;
327         appInfo.enabled = nanoapp->enabled;
328         appInfo.permissions = chreToAndroidPermissions(nanoapp->permissions);
329 
330         std::vector<NanoappRpcService> rpcServices;
331         for (const auto &service : nanoapp->rpc_services) {
332           NanoappRpcService aidlService;
333           aidlService.id = service->id;
334           aidlService.version = service->version;
335           rpcServices.emplace_back(aidlService);
336         }
337         appInfo.rpcServices = rpcServices;
338 
339         appInfoList.push_back(appInfo);
340       }
341     }
342 
343     mCallback->handleNanoappInfo(appInfoList);
344   }
345 }
346 
onTransactionResult(uint32_t transactionId,bool success)347 void ContextHub::onTransactionResult(uint32_t transactionId, bool success) {
348   std::lock_guard<std::mutex> lock(mCallbackMutex);
349   if (mCallback != nullptr) {
350     mCallback->handleTransactionResult(transactionId, success);
351   }
352 }
353 
onContextHubRestarted()354 void ContextHub::onContextHubRestarted() {
355   std::lock_guard<std::mutex> lock(mCallbackMutex);
356   mIsWifiAvailable.reset();
357   {
358     std::lock_guard<std::mutex> lock(mConnectedHostEndpointsMutex);
359     mConnectedHostEndpoints.clear();
360     mEventLogger.logContextHubRestart();
361   }
362   if (mCallback != nullptr) {
363     mCallback->handleContextHubAsyncEvent(AsyncEventType::RESTARTED);
364   }
365 }
366 
onDebugDumpData(const::chre::fbs::DebugDumpDataT & data)367 void ContextHub::onDebugDumpData(const ::chre::fbs::DebugDumpDataT &data) {
368   if (mDebugFd == kInvalidFd) {
369     ALOGW("Got unexpected debug dump data message");
370   } else {
371     writeToDebugFile(reinterpret_cast<const char *>(data.debug_str.data()),
372                      data.debug_str.size());
373   }
374 }
375 
onDebugDumpComplete(const::chre::fbs::DebugDumpResponseT &)376 void ContextHub::onDebugDumpComplete(
377     const ::chre::fbs::DebugDumpResponseT & /* response */) {
378   std::lock_guard<std::mutex> lock(mDebugDumpMutex);
379   if (!mDebugDumpPending) {
380     ALOGI("Ignoring duplicate/unsolicited debug dump response");
381   } else {
382     mDebugDumpPending = false;
383     mDebugDumpCond.notify_all();
384   }
385 }
386 
handleServiceDeath()387 void ContextHub::handleServiceDeath() {
388   ALOGI("Context Hub Service died ...");
389   {
390     std::lock_guard<std::mutex> lock(mCallbackMutex);
391     mCallback.reset();
392   }
393   {
394     std::lock_guard<std::mutex> lock(mConnectedHostEndpointsMutex);
395     mConnectedHostEndpoints.clear();
396   }
397 }
398 
onServiceDied(void * cookie)399 void ContextHub::onServiceDied(void *cookie) {
400   auto *contexthub = static_cast<ContextHub *>(cookie);
401   contexthub->handleServiceDeath();
402 }
403 
dump(int fd,const char **,uint32_t)404 binder_status_t ContextHub::dump(int fd, const char ** /* args */,
405                                  uint32_t /* numArgs */) {
406   // Timeout inside CHRE is typically 5 seconds, grant 500ms extra here to let
407   // the data reach us
408   constexpr auto kDebugDumpTimeout = std::chrono::milliseconds(5500);
409 
410   mDebugFd = fd;
411   if (mDebugFd < 0) {
412     ALOGW("Can't dump debug info to invalid fd %d", mDebugFd);
413   } else {
414     writeToDebugFile("-- Dumping CHRE/ASH debug info --\n");
415 
416     ALOGV("Sending debug dump request");
417     std::unique_lock<std::mutex> lock(mDebugDumpMutex);
418     mDebugDumpPending = true;
419     if (!mConnection.requestDebugDump()) {
420       ALOGW("Couldn't send debug dump request");
421     } else {
422       mDebugDumpCond.wait_for(lock, kDebugDumpTimeout,
423                               [this]() { return !mDebugDumpPending; });
424       if (mDebugDumpPending) {
425         ALOGE("Timed out waiting on debug dump data");
426         mDebugDumpPending = false;
427       }
428     }
429 
430     writeToDebugFile(mEventLogger.dump());
431     writeToDebugFile("\n-- End of CHRE/ASH debug info --\n");
432 
433     mDebugFd = kInvalidFd;
434     ALOGV("Debug dump complete");
435   }
436 
437   return STATUS_OK;
438 }
439 
440 }  // namespace contexthub
441 }  // namespace hardware
442 }  // namespace android
443 }  // namespace aidl
444