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