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