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 51 constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) { 52 return static_cast<uint8_t>(chreVersion >> 24); 53 } 54 55 constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) { 56 return static_cast<uint8_t>(chreVersion >> 16); 57 } 58 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 */ 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 73 GenericContextHub::DeathRecipient::DeathRecipient( 74 sp<GenericContextHub> contexthub) : mGenericContextHub(contexthub){} 75 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 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 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 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 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 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 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 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 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 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 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 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 335 GenericContextHub::SocketCallbacks::SocketCallbacks(GenericContextHub& parent) 336 : mParent(parent) {} 337 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 345 void GenericContextHub::SocketCallbacks::onConnected() { 346 if (mHaveConnected) { 347 ALOGI("Reconnected to CHRE daemon"); 348 invokeClientCallback([&]() { 349 mParent.mCallbacks->handleHubEvent(AsyncEventType::RESTARTED); 350 }); 351 } 352 mHaveConnected = true; 353 } 354 355 void GenericContextHub::SocketCallbacks::onDisconnected() { 356 ALOGW("Lost connection to CHRE daemon"); 357 } 358 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 mParent.mCallbacks->handleClientMsg(msg); 369 }); 370 } 371 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 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 mParent.mCallbacks->handleAppsInfo(appInfoList); 434 }); 435 } 436 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 mParent.mCallbacks->handleTxnResult(response.transaction_id, result); 478 }); 479 } 480 } 481 } 482 } 483 484 bool GenericContextHub::isExpectedLoadResponseLocked( 485 const ::chre::fbs::LoadNanoappResponseT& response) { 486 return mPendingLoadTransaction.has_value() 487 && (mPendingLoadTransaction->getTransactionId() 488 == response.transaction_id) 489 && (response.fragment_id == 0 490 || mCurrentFragmentId == response.fragment_id); 491 } 492 493 void GenericContextHub::SocketCallbacks::handleUnloadNanoappResponse( 494 const ::chre::fbs::UnloadNanoappResponseT& response) { 495 ALOGV("Got unload nanoapp response for transaction %" PRIu32 " with result " 496 "%d", response.transaction_id, response.success); 497 498 invokeClientCallback([&]() { 499 TransactionResult result = (response.success) ? 500 TransactionResult::SUCCESS : TransactionResult::FAILURE; 501 mParent.mCallbacks->handleTxnResult(response.transaction_id, result); 502 }); 503 } 504 505 void GenericContextHub::SocketCallbacks::handleDebugDumpData( 506 const ::chre::fbs::DebugDumpDataT& data) { 507 ALOGV("Got debug dump data, size %zu", data.debug_str.size()); 508 if (mParent.mDebugFd == kInvalidFd) { 509 ALOGW("Got unexpected debug dump data message"); 510 } else { 511 mParent.writeToDebugFile( 512 reinterpret_cast<const char *>(data.debug_str.data()), 513 data.debug_str.size()); 514 } 515 } 516 517 void GenericContextHub::SocketCallbacks::handleDebugDumpResponse( 518 const ::chre::fbs::DebugDumpResponseT& response) { 519 ALOGV("Got debug dump response, success %d, data count %" PRIu32, 520 response.success, response.data_count); 521 std::lock_guard<std::mutex> lock(mParent.mDebugDumpMutex); 522 if (!mParent.mDebugDumpPending) { 523 ALOGI("Ignoring duplicate/unsolicited debug dump response"); 524 } else { 525 mParent.mDebugDumpPending = false; 526 mParent.mDebugDumpCond.notify_all(); 527 } 528 } 529 530 void GenericContextHub::SocketCallbacks::invokeClientCallback( 531 std::function<void()> callback) { 532 std::lock_guard<std::mutex> lock(mParent.mCallbacksLock); 533 if (mParent.mCallbacks != nullptr) { 534 callback(); 535 } 536 } 537 538 void GenericContextHub::writeToDebugFile(const char *str) { 539 writeToDebugFile(str, strlen(str)); 540 } 541 542 void GenericContextHub::writeToDebugFile(const char *str, size_t len) { 543 ssize_t written = write(mDebugFd, str, len); 544 if (written != (ssize_t) len) { 545 ALOGW("Couldn't write to debug header: returned %zd, expected %zu " 546 "(errno %d)", written, len, errno); 547 } 548 } 549 550 void GenericContextHub::handleServiceDeath(uint32_t hubId) { 551 std::lock_guard<std::mutex> lock(mCallbacksLock); 552 ALOGI("Context hub service died for hubId %" PRIu32, hubId); 553 mCallbacks.clear(); 554 } 555 556 IContexthub* HIDL_FETCH_IContexthub(const char* /* name */) { 557 return new GenericContextHub(); 558 } 559 560 } // namespace implementation 561 } // namespace V1_0 562 } // namespace contexthub 563 } // namespace hardware 564 } // namespace android 565