1 /*
2 * Copyright (C) 2017 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 #define LOG_TAG "ContextHubHal"
18 #define LOG_NDEBUG 0
19
20 #include "generic_context_hub.h"
21
22 #include <chrono>
23 #include <cinttypes>
24 #include <vector>
25
26 #include <log/log.h>
27 #include <unistd.h>
28
29 namespace android {
30 namespace hardware {
31 namespace contexthub {
32 namespace V1_0 {
33 namespace implementation {
34
35 using ::android::chre::getStringFromByteVector;
36 using ::android::hardware::Return;
37 using ::android::hardware::contexthub::V1_0::AsyncEventType;
38 using ::android::hardware::contexthub::V1_0::Result;
39 using ::android::hardware::contexthub::V1_0::TransactionResult;
40 using ::android::chre::HostProtocolHost;
41 using ::flatbuffers::FlatBufferBuilder;
42
43 // Aliased for consistency with the way these symbols are referenced in
44 // CHRE-side code
45 namespace fbs = ::chre::fbs;
46
47 namespace {
48
49 constexpr uint32_t kDefaultHubId = 0;
50
extractChreApiMajorVersion(uint32_t chreVersion)51 constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) {
52 return static_cast<uint8_t>(chreVersion >> 24);
53 }
54
extractChreApiMinorVersion(uint32_t chreVersion)55 constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) {
56 return static_cast<uint8_t>(chreVersion >> 16);
57 }
58
extractChrePatchVersion(uint32_t chreVersion)59 constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) {
60 return static_cast<uint16_t>(chreVersion);
61 }
62
63 /**
64 * @return file descriptor contained in the hidl_handle, or -1 if there is none
65 */
hidlHandleToFileDescriptor(const hidl_handle & hh)66 int hidlHandleToFileDescriptor(const hidl_handle& hh) {
67 const native_handle_t *handle = hh.getNativeHandle();
68 return (handle != nullptr && handle->numFds >= 1) ? handle->data[0] : -1;
69 }
70
71 } // anonymous namespace
72
DeathRecipient(sp<GenericContextHub> contexthub)73 GenericContextHub::DeathRecipient::DeathRecipient(
74 sp<GenericContextHub> contexthub) : mGenericContextHub(contexthub){}
75
serviceDied(uint64_t cookie,const wp<::android::hidl::base::V1_0::IBase> &)76 void GenericContextHub::DeathRecipient::serviceDied(
77 uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& /* who */) {
78 uint32_t hubId = static_cast<uint32_t>(cookie);
79 mGenericContextHub->handleServiceDeath(hubId);
80 }
81
GenericContextHub()82 GenericContextHub::GenericContextHub() {
83 constexpr char kChreSocketName[] = "chre";
84
85 mSocketCallbacks = new SocketCallbacks(*this);
86 if (!mClient.connectInBackground(kChreSocketName, mSocketCallbacks)) {
87 ALOGE("Couldn't start socket client");
88 }
89
90 mDeathRecipient = new DeathRecipient(this);
91 }
92
debug(const hidl_handle & hh_fd,const hidl_vec<hidl_string> &)93 Return<void> GenericContextHub::debug(
94 const hidl_handle& hh_fd, const hidl_vec<hidl_string>& /*options*/) {
95 // Timeout inside CHRE is typically 5 seconds, grant 500ms extra here to let
96 // the data reach us
97 constexpr auto kDebugDumpTimeout = std::chrono::milliseconds(5500);
98
99 mDebugFd = hidlHandleToFileDescriptor(hh_fd);
100 if (mDebugFd < 0) {
101 ALOGW("Can't dump debug info to invalid fd");
102 } else {
103 writeToDebugFile("-- Dumping CHRE/ASH debug info --\n");
104
105 ALOGV("Sending debug dump request");
106 FlatBufferBuilder builder;
107 HostProtocolHost::encodeDebugDumpRequest(builder);
108 std::unique_lock<std::mutex> lock(mDebugDumpMutex);
109 mDebugDumpPending = true;
110 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
111 ALOGW("Couldn't send debug dump request");
112 } else {
113 mDebugDumpCond.wait_for(lock, kDebugDumpTimeout,
114 [this]() { return !mDebugDumpPending; });
115 if (mDebugDumpPending) {
116 ALOGI("Timed out waiting on debug dump data");
117 mDebugDumpPending = false;
118 }
119 }
120 writeToDebugFile("\n-- End of CHRE/ASH debug info --\n");
121
122 mDebugFd = kInvalidFd;
123 ALOGV("Debug dump complete");
124 }
125
126 return Void();
127 }
128
getHubs(getHubs_cb _hidl_cb)129 Return<void> GenericContextHub::getHubs(getHubs_cb _hidl_cb) {
130 constexpr auto kHubInfoQueryTimeout = std::chrono::seconds(5);
131 std::vector<ContextHub> hubs;
132 ALOGV("%s", __func__);
133
134 // If we're not connected yet, give it some time
135 // TODO refactor from polling into conditional wait
136 int maxSleepIterations = 250;
137 while (!mHubInfoValid && !mClient.isConnected() && --maxSleepIterations > 0) {
138 std::this_thread::sleep_for(std::chrono::milliseconds(20));
139 }
140
141 if (!mClient.isConnected()) {
142 ALOGE("Couldn't connect to hub daemon");
143 } else if (!mHubInfoValid) {
144 // We haven't cached the hub details yet, so send a request and block
145 // waiting on a response
146 std::unique_lock<std::mutex> lock(mHubInfoMutex);
147 FlatBufferBuilder builder;
148 HostProtocolHost::encodeHubInfoRequest(builder);
149
150 ALOGD("Sending hub info request");
151 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
152 ALOGE("Couldn't send hub info request");
153 } else {
154 mHubInfoCond.wait_for(lock, kHubInfoQueryTimeout,
155 [this]() { return mHubInfoValid; });
156 }
157 }
158
159 if (mHubInfoValid) {
160 hubs.push_back(mHubInfo);
161 } else {
162 ALOGE("Unable to get hub info from CHRE");
163 }
164
165 _hidl_cb(hubs);
166 return Void();
167 }
168
registerCallback(uint32_t hubId,const sp<IContexthubCallback> & cb)169 Return<Result> GenericContextHub::registerCallback(
170 uint32_t hubId, const sp<IContexthubCallback>& cb) {
171 Result result;
172 ALOGV("%s", __func__);
173
174 // TODO: currently we only support 1 hub behind this HAL implementation
175 if (hubId == kDefaultHubId) {
176 std::lock_guard<std::mutex> lock(mCallbacksLock);
177
178 if (cb != nullptr) {
179 if (mCallbacks != nullptr) {
180 ALOGD("Modifying callback for hubId %" PRIu32, hubId);
181 mCallbacks->unlinkToDeath(mDeathRecipient);
182 }
183 Return<bool> linkReturn = cb->linkToDeath(mDeathRecipient, hubId);
184 if (!linkReturn.withDefault(false)) {
185 ALOGW("Could not link death recipient to hubId %" PRIu32, hubId);
186 }
187 }
188
189 mCallbacks = cb;
190 result = Result::OK;
191 } else {
192 result = Result::BAD_PARAMS;
193 }
194
195 return result;
196 }
197
sendMessageToHub(uint32_t hubId,const ContextHubMsg & msg)198 Return<Result> GenericContextHub::sendMessageToHub(uint32_t hubId,
199 const ContextHubMsg& msg) {
200 Result result;
201 ALOGV("%s", __func__);
202
203 if (hubId != kDefaultHubId) {
204 result = Result::BAD_PARAMS;
205 } else {
206 FlatBufferBuilder builder(1024);
207 HostProtocolHost::encodeNanoappMessage(
208 builder, msg.appName, msg.msgType, msg.hostEndPoint, msg.msg.data(),
209 msg.msg.size());
210
211 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
212 result = Result::UNKNOWN_FAILURE;
213 } else {
214 result = Result::OK;
215 }
216 }
217
218 return result;
219 }
220
loadNanoApp(uint32_t hubId,const NanoAppBinary & appBinary,uint32_t transactionId)221 Return<Result> GenericContextHub::loadNanoApp(
222 uint32_t hubId, const NanoAppBinary& appBinary, uint32_t transactionId) {
223 Result result;
224 ALOGV("%s", __func__);
225
226 if (hubId != kDefaultHubId) {
227 result = Result::BAD_PARAMS;
228 } else {
229 std::lock_guard<std::mutex> lock(mPendingLoadTransactionMutex);
230
231 if (mPendingLoadTransaction.has_value()) {
232 ALOGE("Pending load transaction exists. Overriding pending request");
233 }
234
235 uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
236 (appBinary.targetChreApiMinorVersion << 16);
237 mPendingLoadTransaction = FragmentedLoadTransaction(
238 transactionId, appBinary.appId, appBinary.appVersion, targetApiVersion,
239 appBinary.customBinary, kLoadFragmentSizeBytes);
240
241 result = sendFragmentedLoadNanoAppRequest(
242 mPendingLoadTransaction.value());
243 if (result != Result::OK) {
244 mPendingLoadTransaction.reset();
245 }
246 }
247
248 ALOGD("Attempted to send load nanoapp request for app of size %zu with ID "
249 "0x%016" PRIx64 " as transaction ID %" PRIu32 ": result %" PRIu32,
250 appBinary.customBinary.size(), appBinary.appId, transactionId, result);
251
252 return result;
253 }
254
sendFragmentedLoadNanoAppRequest(FragmentedLoadTransaction & transaction)255 Result GenericContextHub::sendFragmentedLoadNanoAppRequest(
256 FragmentedLoadTransaction& transaction) {
257 Result result;
258 const FragmentedLoadRequest& request = transaction.getNextRequest();
259
260 FlatBufferBuilder builder(128 + request.binary.size());
261 HostProtocolHost::encodeFragmentedLoadNanoappRequest(builder, request);
262
263 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
264 ALOGE("Failed to send load request message (fragment ID = %zu)",
265 request.fragmentId);
266 result = Result::UNKNOWN_FAILURE;
267 } else {
268 mCurrentFragmentId = request.fragmentId;
269 result = Result::OK;
270 }
271
272 return result;
273 }
274
unloadNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)275 Return<Result> GenericContextHub::unloadNanoApp(
276 uint32_t hubId, uint64_t appId, uint32_t transactionId) {
277 Result result;
278 ALOGV("%s", __func__);
279
280 if (hubId != kDefaultHubId) {
281 result = Result::BAD_PARAMS;
282 } else {
283 FlatBufferBuilder builder(64);
284 HostProtocolHost::encodeUnloadNanoappRequest(
285 builder, transactionId, appId, false /* allowSystemNanoappUnload */);
286 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
287 result = Result::UNKNOWN_FAILURE;
288 } else {
289 result = Result::OK;
290 }
291 }
292
293 ALOGD("Attempted to send unload nanoapp request for app ID 0x%016" PRIx64
294 " as transaction ID %" PRIu32 ": result %" PRIu32, appId, transactionId,
295 result);
296
297 return result;
298 }
299
enableNanoApp(uint32_t,uint64_t appId,uint32_t)300 Return<Result> GenericContextHub::enableNanoApp(
301 uint32_t /* hubId */, uint64_t appId, uint32_t /* transactionId */) {
302 // TODO
303 ALOGW("Attempted to enable app ID 0x%016" PRIx64 ", but not supported",
304 appId);
305 return Result::TRANSACTION_FAILED;
306 }
307
disableNanoApp(uint32_t,uint64_t appId,uint32_t)308 Return<Result> GenericContextHub::disableNanoApp(
309 uint32_t /* hubId */, uint64_t appId, uint32_t /* transactionId */) {
310 // TODO
311 ALOGW("Attempted to disable app ID 0x%016" PRIx64 ", but not supported",
312 appId);
313 return Result::TRANSACTION_FAILED;
314 }
315
queryApps(uint32_t hubId)316 Return<Result> GenericContextHub::queryApps(uint32_t hubId) {
317 Result result;
318 ALOGV("%s", __func__);
319
320 if (hubId != kDefaultHubId) {
321 result = Result::BAD_PARAMS;
322 } else {
323 FlatBufferBuilder builder(64);
324 HostProtocolHost::encodeNanoappListRequest(builder);
325 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
326 result = Result::UNKNOWN_FAILURE;
327 } else {
328 result = Result::OK;
329 }
330 }
331
332 return result;
333 }
334
SocketCallbacks(GenericContextHub & parent)335 GenericContextHub::SocketCallbacks::SocketCallbacks(GenericContextHub& parent)
336 : mParent(parent) {}
337
onMessageReceived(const void * data,size_t length)338 void GenericContextHub::SocketCallbacks::onMessageReceived(const void *data,
339 size_t length) {
340 if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
341 ALOGE("Failed to decode message");
342 }
343 }
344
onConnected()345 void GenericContextHub::SocketCallbacks::onConnected() {
346 if (mHaveConnected) {
347 ALOGI("Reconnected to CHRE daemon");
348 invokeClientCallback([&]() {
349 return mParent.mCallbacks->handleHubEvent(AsyncEventType::RESTARTED);
350 });
351 }
352 mHaveConnected = true;
353 }
354
onDisconnected()355 void GenericContextHub::SocketCallbacks::onDisconnected() {
356 ALOGW("Lost connection to CHRE daemon");
357 }
358
handleNanoappMessage(const fbs::NanoappMessageT & message)359 void GenericContextHub::SocketCallbacks::handleNanoappMessage(
360 const fbs::NanoappMessageT& message) {
361 ContextHubMsg msg;
362 msg.appName = message.app_id;
363 msg.hostEndPoint = message.host_endpoint;
364 msg.msgType = message.message_type;
365 msg.msg = message.message;
366
367 invokeClientCallback([&]() {
368 return mParent.mCallbacks->handleClientMsg(msg);
369 });
370 }
371
handleHubInfoResponse(const fbs::HubInfoResponseT & response)372 void GenericContextHub::SocketCallbacks::handleHubInfoResponse(
373 const fbs::HubInfoResponseT& response) {
374 ALOGD("Got hub info response");
375
376 std::lock_guard<std::mutex> lock(mParent.mHubInfoMutex);
377 if (mParent.mHubInfoValid) {
378 ALOGI("Ignoring duplicate/unsolicited hub info response");
379 } else {
380 mParent.mHubInfo.name = getStringFromByteVector(response.name);
381 mParent.mHubInfo.vendor = getStringFromByteVector(response.vendor);
382 mParent.mHubInfo.toolchain = getStringFromByteVector(response.toolchain);
383 mParent.mHubInfo.platformVersion = response.platform_version;
384 mParent.mHubInfo.toolchainVersion = response.toolchain_version;
385 mParent.mHubInfo.hubId = kDefaultHubId;
386
387 mParent.mHubInfo.peakMips = response.peak_mips;
388 mParent.mHubInfo.stoppedPowerDrawMw = response.stopped_power;
389 mParent.mHubInfo.sleepPowerDrawMw = response.sleep_power;
390 mParent.mHubInfo.peakPowerDrawMw = response.peak_power;
391
392 mParent.mHubInfo.maxSupportedMsgLen = response.max_msg_len;
393 mParent.mHubInfo.chrePlatformId = response.platform_id;
394
395 uint32_t version = response.chre_platform_version;
396 mParent.mHubInfo.chreApiMajorVersion = extractChreApiMajorVersion(version);
397 mParent.mHubInfo.chreApiMinorVersion = extractChreApiMinorVersion(version);
398 mParent.mHubInfo.chrePatchVersion = extractChrePatchVersion(version);
399
400 mParent.mHubInfoValid = true;
401 mParent.mHubInfoCond.notify_all();
402 }
403 }
404
handleNanoappListResponse(const fbs::NanoappListResponseT & response)405 void GenericContextHub::SocketCallbacks::handleNanoappListResponse(
406 const fbs::NanoappListResponseT& response) {
407 std::vector<HubAppInfo> appInfoList;
408
409 ALOGV("Got nanoapp list response with %zu apps", response.nanoapps.size());
410 for (const std::unique_ptr<fbs::NanoappListEntryT>& nanoapp
411 : response.nanoapps) {
412 // TODO: determine if this is really required, and if so, have
413 // HostProtocolHost strip out null entries as part of decode
414 if (nanoapp == nullptr) {
415 continue;
416 }
417
418 ALOGV("App 0x%016" PRIx64 " ver 0x%" PRIx32 " enabled %d system %d",
419 nanoapp->app_id, nanoapp->version, nanoapp->enabled,
420 nanoapp->is_system);
421 if (!nanoapp->is_system) {
422 HubAppInfo appInfo;
423
424 appInfo.appId = nanoapp->app_id;
425 appInfo.version = nanoapp->version;
426 appInfo.enabled = nanoapp->enabled;
427
428 appInfoList.push_back(appInfo);
429 }
430 }
431
432 invokeClientCallback([&]() {
433 return mParent.mCallbacks->handleAppsInfo(appInfoList);
434 });
435 }
436
handleLoadNanoappResponse(const::chre::fbs::LoadNanoappResponseT & response)437 void GenericContextHub::SocketCallbacks::handleLoadNanoappResponse(
438 const ::chre::fbs::LoadNanoappResponseT& response) {
439 ALOGV("Got load nanoapp response for transaction %" PRIu32 " fragment %"
440 PRIu32 " with result %d", response.transaction_id, response.fragment_id,
441 response.success);
442 std::unique_lock<std::mutex> lock(mParent.mPendingLoadTransactionMutex);
443
444 // TODO: Handle timeout in receiving load response
445 if (!mParent.mPendingLoadTransaction.has_value()) {
446 ALOGE("Dropping unexpected load response (no pending transaction exists)");
447 } else {
448 FragmentedLoadTransaction& transaction =
449 mParent.mPendingLoadTransaction.value();
450
451 if (!mParent.isExpectedLoadResponseLocked(response)) {
452 ALOGE("Dropping unexpected load response, expected transaction %"
453 PRIu32 " fragment %" PRIu32 ", received transaction %" PRIu32
454 " fragment %" PRIu32, transaction.getTransactionId(),
455 mParent.mCurrentFragmentId, response.transaction_id,
456 response.fragment_id);
457 } else {
458 TransactionResult result;
459 bool continueLoadRequest = false;
460 if (response.success && !transaction.isComplete()) {
461 if (mParent.sendFragmentedLoadNanoAppRequest(transaction)
462 == Result::OK) {
463 continueLoadRequest = true;
464 result = TransactionResult::SUCCESS;
465 } else {
466 result = TransactionResult::FAILURE;
467 }
468 } else {
469 result = (response.success) ?
470 TransactionResult::SUCCESS : TransactionResult::FAILURE;
471 }
472
473 if (!continueLoadRequest) {
474 mParent.mPendingLoadTransaction.reset();
475 lock.unlock();
476 invokeClientCallback([&]() {
477 return mParent.mCallbacks->handleTxnResult(
478 response.transaction_id, result);
479 });
480 }
481 }
482 }
483 }
484
isExpectedLoadResponseLocked(const::chre::fbs::LoadNanoappResponseT & response)485 bool GenericContextHub::isExpectedLoadResponseLocked(
486 const ::chre::fbs::LoadNanoappResponseT& response) {
487 return mPendingLoadTransaction.has_value()
488 && (mPendingLoadTransaction->getTransactionId()
489 == response.transaction_id)
490 && (response.fragment_id == 0
491 || mCurrentFragmentId == response.fragment_id);
492 }
493
handleUnloadNanoappResponse(const::chre::fbs::UnloadNanoappResponseT & response)494 void GenericContextHub::SocketCallbacks::handleUnloadNanoappResponse(
495 const ::chre::fbs::UnloadNanoappResponseT& response) {
496 ALOGV("Got unload nanoapp response for transaction %" PRIu32 " with result "
497 "%d", response.transaction_id, response.success);
498
499 invokeClientCallback([&]() {
500 TransactionResult result = (response.success) ?
501 TransactionResult::SUCCESS : TransactionResult::FAILURE;
502 return mParent.mCallbacks->handleTxnResult(response.transaction_id, result);
503 });
504 }
505
handleDebugDumpData(const::chre::fbs::DebugDumpDataT & data)506 void GenericContextHub::SocketCallbacks::handleDebugDumpData(
507 const ::chre::fbs::DebugDumpDataT& data) {
508 ALOGV("Got debug dump data, size %zu", data.debug_str.size());
509 if (mParent.mDebugFd == kInvalidFd) {
510 ALOGW("Got unexpected debug dump data message");
511 } else {
512 mParent.writeToDebugFile(
513 reinterpret_cast<const char *>(data.debug_str.data()),
514 data.debug_str.size());
515 }
516 }
517
handleDebugDumpResponse(const::chre::fbs::DebugDumpResponseT & response)518 void GenericContextHub::SocketCallbacks::handleDebugDumpResponse(
519 const ::chre::fbs::DebugDumpResponseT& response) {
520 ALOGV("Got debug dump response, success %d, data count %" PRIu32,
521 response.success, response.data_count);
522 std::lock_guard<std::mutex> lock(mParent.mDebugDumpMutex);
523 if (!mParent.mDebugDumpPending) {
524 ALOGI("Ignoring duplicate/unsolicited debug dump response");
525 } else {
526 mParent.mDebugDumpPending = false;
527 mParent.mDebugDumpCond.notify_all();
528 }
529 }
530
invokeClientCallback(std::function<Return<void> ()> callback)531 void GenericContextHub::SocketCallbacks::invokeClientCallback(
532 std::function<Return<void>()> callback) {
533 std::lock_guard<std::mutex> lock(mParent.mCallbacksLock);
534 if (mParent.mCallbacks != nullptr && !callback().isOk()) {
535 ALOGE("Failed to invoke client callback");
536 }
537 }
538
writeToDebugFile(const char * str)539 void GenericContextHub::writeToDebugFile(const char *str) {
540 writeToDebugFile(str, strlen(str));
541 }
542
writeToDebugFile(const char * str,size_t len)543 void GenericContextHub::writeToDebugFile(const char *str, size_t len) {
544 ssize_t written = write(mDebugFd, str, len);
545 if (written != (ssize_t) len) {
546 ALOGW("Couldn't write to debug header: returned %zd, expected %zu "
547 "(errno %d)", written, len, errno);
548 }
549 }
550
handleServiceDeath(uint32_t hubId)551 void GenericContextHub::handleServiceDeath(uint32_t hubId) {
552 std::lock_guard<std::mutex> lock(mCallbacksLock);
553 ALOGI("Context hub service died for hubId %" PRIu32, hubId);
554 mCallbacks.clear();
555 }
556
HIDL_FETCH_IContexthub(const char *)557 IContexthub* HIDL_FETCH_IContexthub(const char* /* name */) {
558 return new GenericContextHub();
559 }
560
561 } // namespace implementation
562 } // namespace V1_0
563 } // namespace contexthub
564 } // namespace hardware
565 } // namespace android
566