1 /* 2 * Copyright (C) 2018 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 /* 18 * This file is based on: 19 * hardware/interfaces/contexthub/1.0/default/Contexthub.cpp 20 * with modifications to connect directly to the NanohubHAL and 21 * support endpoints. 22 */ 23 24 #include "NanohubHidlAdapter.h" 25 #include "nanohub_perdevice.h" 26 27 #include <inttypes.h> 28 29 #include <log/log.h> 30 #include <utils/String8.h> 31 #include <sys/stat.h> 32 33 #include <android/hardware/contexthub/1.0/IContexthub.h> 34 #include <hardware/context_hub.h> 35 #include <sys/endian.h> 36 37 #undef LOG_TAG 38 #define LOG_TAG "NanohubHidlAdapter" 39 40 using namespace android::nanohub; 41 42 namespace android { 43 namespace hardware { 44 namespace contexthub { 45 namespace V1_0 { 46 namespace implementation { 47 48 static constexpr uint64_t ALL_APPS = UINT64_C(0xFFFFFFFFFFFFFFFF); 49 50 Contexthub::Contexthub() 51 : mDeathRecipient(new DeathRecipient(this)), 52 mIsTransactionPending(false) { 53 } 54 55 bool Contexthub::setOsAppAsDestination(hub_message_t *msg, int hubId) { 56 if (!isValidHubId(hubId)) { 57 ALOGW("%s: Hub information is null for hubHandle %d", 58 __FUNCTION__, 59 hubId); 60 return false; 61 } else { 62 msg->app_name = mCachedHubInfo[hubId].osAppName; 63 return true; 64 } 65 } 66 67 Return<void> Contexthub::getHubs(getHubs_cb _hidl_cb) { 68 std::vector<ContextHub> hubs; 69 const context_hub_t *hub = nanohub::get_hub_info(); 70 71 mCachedHubInfo.clear(); 72 73 CachedHubInformation info; 74 ContextHub c; 75 76 c.name = hub->name; 77 c.vendor = hub->vendor; 78 c.toolchain = hub->toolchain; 79 c.platformVersion = hub->platform_version; 80 c.toolchainVersion = hub->toolchain_version; 81 c.hubId = hub->hub_id; 82 c.peakMips = hub->peak_mips; 83 c.stoppedPowerDrawMw = hub->stopped_power_draw_mw; 84 c.sleepPowerDrawMw = hub->sleep_power_draw_mw; 85 c.peakPowerDrawMw = hub->peak_power_draw_mw; 86 // c.connectedSensors = 87 c.maxSupportedMsgLen = hub->max_supported_msg_len; 88 // TODO: get this information from nanohub 89 c.chrePlatformId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0); 90 c.chreApiMajorVersion = 0x01; 91 c.chreApiMinorVersion = 0x02; 92 c.chrePatchVersion = NANOHUB_OS_PATCH_LEVEL; 93 94 info.callback = nullptr; 95 info.osAppName = hub->os_app_name; 96 mCachedHubInfo[hub->hub_id] = info; 97 98 hubs.push_back(c); 99 100 _hidl_cb(hubs); 101 return Void(); 102 } 103 104 Contexthub::DeathRecipient::DeathRecipient(sp<Contexthub> contexthub) 105 : mContexthub(contexthub) {} 106 107 void Contexthub::DeathRecipient::serviceDied( 108 uint64_t cookie, 109 const wp<::android::hidl::base::V1_0::IBase>& /*who*/) { 110 uint32_t hubId = static_cast<uint32_t>(cookie); 111 mContexthub->handleServiceDeath(hubId); 112 } 113 114 bool Contexthub::isValidHubId(uint32_t hubId) { 115 if (!mCachedHubInfo.count(hubId)) { 116 ALOGW("Hub information not found for hubId %" PRIu32, hubId); 117 return false; 118 } else { 119 return true; 120 } 121 } 122 123 sp<IContexthubCallback> Contexthub::getCallBackForHubId(uint32_t hubId) { 124 if (!isValidHubId(hubId)) { 125 return nullptr; 126 } else { 127 return mCachedHubInfo[hubId].callback; 128 } 129 } 130 131 Return<Result> Contexthub::sendMessageToHub(uint32_t hubId, 132 const ContextHubMsg &msg) { 133 if (!isValidHubId(hubId) || msg.msg.size() > UINT32_MAX) { 134 return Result::BAD_PARAMS; 135 } 136 137 hub_message_t txMsg = { 138 .app_name.id = msg.appName, 139 .message_type = msg.msgType, 140 .message_len = static_cast<uint32_t>(msg.msg.size()), // Note the check above 141 .message = static_cast<const uint8_t *>(msg.msg.data()), 142 }; 143 144 // Use a dummy to prevent send_message with empty message from failing prematurely 145 static uint8_t dummy; 146 if (txMsg.message_len == 0 && txMsg.message == nullptr) { 147 txMsg.message = &dummy; 148 } 149 150 ALOGI("Sending msg of type %" PRIu32 ", size %" PRIu32 " to app 0x%" PRIx64, 151 txMsg.message_type, 152 txMsg.message_len, 153 txMsg.app_name.id); 154 155 if(NanoHub::sendToNanohub(hubId, &txMsg, 0, msg.hostEndPoint) != 0) { 156 return Result::TRANSACTION_FAILED; 157 } 158 159 return Result::OK; 160 } 161 162 Return<Result> Contexthub::registerCallback(uint32_t hubId, 163 const sp<IContexthubCallback> &cb) { 164 Return<Result> retVal = Result::BAD_PARAMS; 165 166 if (!isValidHubId(hubId)) { 167 // Initialized, but hubId is not valid 168 retVal = Result::BAD_PARAMS; 169 } else if (NanoHub::subscribeMessages(hubId, 170 contextHubCb, 171 this) == 0) { 172 // Initialized && valid hub && subscription successful 173 if (mCachedHubInfo[hubId].callback != nullptr) { 174 ALOGD("Modifying callback for hubId %" PRIu32, hubId); 175 mCachedHubInfo[hubId].callback->unlinkToDeath(mDeathRecipient); 176 } 177 178 mCachedHubInfo[hubId].callback = cb; 179 if (cb != nullptr) { 180 Return<bool> linkResult = cb->linkToDeath(mDeathRecipient, hubId); 181 bool linkSuccess = linkResult.isOk() ? 182 static_cast<bool>(linkResult) : false; 183 if (!linkSuccess) { 184 ALOGW("Couldn't link death recipient for hubId %" PRIu32, 185 hubId); 186 } 187 } 188 retVal = Result::OK; 189 } else { 190 // Initalized && valid hubId - but subscription unsuccessful 191 // This is likely an internal error in the HAL implementation, but we 192 // cannot add more information. 193 ALOGW("Could not subscribe to the hub for callback"); 194 retVal = Result::UNKNOWN_FAILURE; 195 } 196 197 return retVal; 198 } 199 200 static bool isValidOsStatus(const uint8_t *msg, 201 size_t msgLen, 202 status_response_t *rsp) { 203 // Workaround a bug in some HALs 204 if (msgLen == 1) { 205 rsp->result = msg[0]; 206 return true; 207 } 208 209 if (msg == nullptr || msgLen != sizeof(*rsp)) { 210 ALOGI("Received invalid response (is null : %d, size %zu)", 211 msg == nullptr ? 1 : 0, 212 msgLen); 213 return false; 214 } 215 216 memcpy(rsp, msg, sizeof(*rsp)); 217 218 // No sanity checks on return values 219 return true; 220 } 221 222 int Contexthub::handleOsMessage(sp<IContexthubCallback> cb, 223 uint32_t msgType, 224 const uint8_t *msg, 225 int msgLen, 226 uint32_t transactionId) { 227 int retVal = -1; 228 229 230 switch(msgType) { 231 case CONTEXT_HUB_APPS_ENABLE: 232 case CONTEXT_HUB_APPS_DISABLE: 233 case CONTEXT_HUB_LOAD_APP: 234 case CONTEXT_HUB_UNLOAD_APP: 235 { 236 struct status_response_t rsp; 237 TransactionResult result; 238 if (isValidOsStatus(msg, msgLen, &rsp) && rsp.result == 0) { 239 retVal = 0; 240 result = TransactionResult::SUCCESS; 241 } else { 242 result = TransactionResult::FAILURE; 243 } 244 245 mIsTransactionPending = false; 246 if (cb != nullptr) { 247 cb->handleTxnResult(transactionId, result); 248 } 249 retVal = 0; 250 break; 251 } 252 253 case CONTEXT_HUB_QUERY_APPS: 254 { 255 std::vector<HubAppInfo> apps; 256 int numApps = msgLen / sizeof(hub_app_info); 257 const hub_app_info *unalignedInfoAddr = reinterpret_cast<const hub_app_info *>(msg); 258 259 for (int i = 0; i < numApps; i++) { 260 hub_app_info query_info; 261 memcpy(&query_info, &unalignedInfoAddr[i], sizeof(query_info)); 262 HubAppInfo app; 263 app.appId = query_info.app_name.id; 264 app.version = query_info.version; 265 // TODO :: Add memory ranges 266 267 apps.push_back(app); 268 } 269 270 if (cb != nullptr) { 271 cb->handleAppsInfo(apps); 272 } 273 retVal = 0; 274 break; 275 } 276 277 case CONTEXT_HUB_QUERY_MEMORY: 278 { 279 // Deferring this use 280 retVal = 0; 281 break; 282 } 283 284 case CONTEXT_HUB_OS_REBOOT: 285 { 286 mIsTransactionPending = false; 287 if (cb != nullptr) { 288 cb->handleHubEvent(AsyncEventType::RESTARTED); 289 } 290 retVal = 0; 291 break; 292 } 293 294 default: 295 { 296 retVal = -1; 297 break; 298 } 299 } 300 301 return retVal; 302 } 303 304 void Contexthub::handleServiceDeath(uint32_t hubId) { 305 ALOGI("Callback/service died for hubId %" PRIu32, hubId); 306 int ret = NanoHub::subscribeMessages(hubId, nullptr, nullptr); 307 if (ret != 0) { 308 ALOGW("Failed to unregister callback from hubId %" PRIu32 ": %d", 309 hubId, ret); 310 } 311 mCachedHubInfo[hubId].callback.clear(); 312 } 313 314 int Contexthub::contextHubCb(uint32_t hubId, 315 const nanohub::HubMessage &rxMsg, 316 void *cookie) { 317 Contexthub *obj = static_cast<Contexthub *>(cookie); 318 319 if (!obj->isValidHubId(hubId)) { 320 ALOGW("Invalid hub Id %" PRIu32, hubId); 321 return -1; 322 } 323 324 sp<IContexthubCallback> cb = obj->getCallBackForHubId(hubId); 325 326 if (cb == nullptr) { 327 // This should not ever happen 328 ALOGW("No callback registered, returning"); 329 return -1; 330 } 331 332 if (rxMsg.message_type < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE) { 333 obj->handleOsMessage(cb, 334 rxMsg.message_type, 335 static_cast<const uint8_t *>(rxMsg.message), 336 rxMsg.message_len, 337 rxMsg.message_transaction_id); 338 } else { 339 ContextHubMsg msg; 340 341 msg.appName = rxMsg.app_name.id; 342 msg.msgType = rxMsg.message_type; 343 msg.hostEndPoint = rxMsg.message_endpoint; 344 msg.msg = std::vector<uint8_t>(static_cast<const uint8_t *>(rxMsg.message), 345 static_cast<const uint8_t *>(rxMsg.message) + 346 rxMsg.message_len); 347 348 cb->handleClientMsg(msg); 349 } 350 351 return 0; 352 } 353 354 Return<Result> Contexthub::unloadNanoApp(uint32_t hubId, 355 uint64_t appId, 356 uint32_t transactionId) { 357 if (mIsTransactionPending) { 358 return Result::TRANSACTION_PENDING; 359 } 360 361 hub_message_t msg; 362 363 if (setOsAppAsDestination(&msg, hubId) == false) { 364 return Result::BAD_PARAMS; 365 } 366 367 struct apps_disable_request_t req; 368 369 msg.message_type = CONTEXT_HUB_UNLOAD_APP; 370 msg.message_len = sizeof(req); 371 msg.message = &req; 372 req.app_name.id = appId; 373 374 if(NanoHub::sendToNanohub(hubId, 375 &msg, 376 transactionId, 377 static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { 378 return Result::TRANSACTION_FAILED; 379 } else { 380 mIsTransactionPending = true; 381 return Result::OK; 382 } 383 } 384 385 Return<Result> Contexthub::loadNanoApp(uint32_t hubId, 386 const NanoAppBinary& appBinary, 387 uint32_t transactionId) { 388 if (mIsTransactionPending) { 389 return Result::TRANSACTION_PENDING; 390 } 391 392 hub_message_t hubMsg; 393 394 if (setOsAppAsDestination(&hubMsg, hubId) == false) { 395 return Result::BAD_PARAMS; 396 } 397 398 // Data from the nanoapp header is passed through HIDL as explicit fields, 399 // but the legacy HAL expects it prepended to the binary, therefore we must 400 // reconstruct it here prior to passing to the legacy HAL. 401 const struct nano_app_binary_t header = { 402 .header_version = htole32(1), 403 .magic = htole32(NANOAPP_MAGIC), 404 .app_id.id = htole64(appBinary.appId), 405 .app_version = htole32(appBinary.appVersion), 406 .flags = htole32(appBinary.flags), 407 .hw_hub_type = htole64(0), 408 .target_chre_api_major_version = appBinary.targetChreApiMajorVersion, 409 .target_chre_api_minor_version = appBinary.targetChreApiMinorVersion, 410 }; 411 const uint8_t *headerBytes = reinterpret_cast<const uint8_t *>(&header); 412 413 std::vector<uint8_t> binaryWithHeader(appBinary.customBinary); 414 binaryWithHeader.insert(binaryWithHeader.begin(), 415 headerBytes, 416 headerBytes + sizeof(header)); 417 418 hubMsg.message_type = CONTEXT_HUB_LOAD_APP; 419 hubMsg.message_len = binaryWithHeader.size(); 420 hubMsg.message = binaryWithHeader.data(); 421 422 if(NanoHub::sendToNanohub(hubId, 423 &hubMsg, 424 transactionId, 425 static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { 426 return Result::TRANSACTION_FAILED; 427 } else { 428 mIsTransactionPending = true; 429 return Result::OK; 430 } 431 } 432 433 Return<Result> Contexthub::enableNanoApp(uint32_t hubId, 434 uint64_t appId, 435 uint32_t transactionId) { 436 if (mIsTransactionPending) { 437 return Result::TRANSACTION_PENDING; 438 } 439 440 hub_message_t msg; 441 442 if (setOsAppAsDestination(&msg, hubId) == false) { 443 return Result::BAD_PARAMS; 444 } 445 446 struct apps_enable_request_t req; 447 448 msg.message_type = CONTEXT_HUB_APPS_ENABLE; 449 msg.message_len = sizeof(req); 450 req.app_name.id = appId; 451 msg.message = &req; 452 453 if(NanoHub::sendToNanohub(hubId, 454 &msg, 455 transactionId, 456 static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { 457 return Result::TRANSACTION_FAILED; 458 } else { 459 mIsTransactionPending = true; 460 return Result::OK; 461 } 462 } 463 464 Return<Result> Contexthub::disableNanoApp(uint32_t hubId, 465 uint64_t appId, 466 uint32_t transactionId) { 467 if (mIsTransactionPending) { 468 return Result::TRANSACTION_PENDING; 469 } 470 471 hub_message_t msg; 472 473 if (setOsAppAsDestination(&msg, hubId) == false) { 474 return Result::BAD_PARAMS; 475 } 476 477 struct apps_disable_request_t req; 478 479 msg.message_type = CONTEXT_HUB_APPS_DISABLE; 480 msg.message_len = sizeof(req); 481 req.app_name.id = appId; 482 msg.message = &req; 483 484 if(NanoHub::sendToNanohub(hubId, 485 &msg, 486 transactionId, 487 static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { 488 return Result::TRANSACTION_FAILED; 489 } else { 490 mIsTransactionPending = true; 491 return Result::OK; 492 } 493 } 494 495 Return<Result> Contexthub::queryApps(uint32_t hubId) { 496 hub_message_t msg; 497 498 if (setOsAppAsDestination(&msg, hubId) == false) { 499 ALOGW("Could not find hubId %" PRIu32, hubId); 500 return Result::BAD_PARAMS; 501 } 502 503 query_apps_request_t payload; 504 payload.app_name.id = ALL_APPS; // TODO : Pass this in as a parameter 505 msg.message = &payload; 506 msg.message_len = sizeof(payload); 507 msg.message_type = CONTEXT_HUB_QUERY_APPS; 508 509 if(NanoHub::sendToNanohub(hubId, 510 &msg, 511 0, 512 static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { 513 ALOGW("Query Apps sendMessage failed"); 514 return Result::TRANSACTION_FAILED; 515 } 516 517 return Result::OK; 518 } 519 520 IContexthub *HIDL_FETCH_IContexthub(const char *) { 521 return new Contexthub(); 522 } 523 524 static bool readApp(const char *file, NanoAppBinary *appBinary) 525 { 526 bool success = false; 527 int fd = open(file, O_RDONLY); 528 529 if (fd >= 0) { 530 struct stat sb; 531 if (fstat(fd, &sb) == 0) { 532 void *buf = malloc(sb.st_size); 533 if (buf != nullptr && read(fd, buf, sb.st_size) == sb.st_size) { 534 success = true; 535 const struct nano_app_binary_t *header = static_cast<const struct nano_app_binary_t *>(buf); 536 appBinary->appId = header->app_id.id; 537 appBinary->appVersion = header->app_version; 538 appBinary->flags = header->flags; 539 appBinary->targetChreApiMajorVersion = header->target_chre_api_major_version; 540 appBinary->targetChreApiMinorVersion = header->target_chre_api_minor_version; 541 appBinary->customBinary = std::vector<uint8_t>(static_cast<const uint8_t *>(buf) + sizeof(struct nano_app_binary_t), static_cast<const uint8_t *>(buf) + sb.st_size); 542 } 543 free(buf); 544 } 545 close(fd); 546 } 547 return success; 548 } 549 550 Return<void> Contexthub::debug(const hidl_handle& hh_fd, 551 const hidl_vec<hidl_string>& hh_data) { 552 if (hh_fd == nullptr || hh_fd->numFds < 1) { 553 return Void(); 554 } 555 556 String8 result; 557 int fd = hh_fd.getNativeHandle()->data[0]; 558 559 if (hh_data.size() == 0) { 560 result.appendFormat("debug: %d\n", NanoHub::getDebugFlags()); 561 std::string appInfo; 562 NanoHub::dumpAppInfo(appInfo); 563 result.append(appInfo.c_str()); 564 } else if (hh_data.size() == 1) { 565 NanoHub::setDebugFlags(atoi(hh_data[0].c_str())); 566 result.appendFormat("debug: %d\n", NanoHub::getDebugFlags()); 567 } else if (hh_data.size() == 2) { 568 if (strncmp(hh_data[0].c_str(), "load", 4) == 0) { 569 NanoAppBinary appBinary; 570 if (readApp(hh_data[1].c_str(), &appBinary)) 571 loadNanoApp(0, appBinary, 0); 572 } else if (strncmp(hh_data[0].c_str(), "unload", 6) == 0) { 573 unloadNanoApp(0, strtoul(hh_data[1].c_str(), NULL, 16), 0); 574 } else if (strncmp(hh_data[0].c_str(), "enable", 6) == 0) { 575 enableNanoApp(0, strtoul(hh_data[1].c_str(), NULL, 16), 0); 576 } else if (strncmp(hh_data[0].c_str(), "disable", 7) == 0) { 577 disableNanoApp(0, strtoul(hh_data[1].c_str(), NULL, 16), 0); 578 } 579 } else { 580 result.appendFormat("unknown debug options"); 581 } 582 write(fd, result.string(), result.size()); 583 584 return Void(); 585 } 586 587 } // namespace implementation 588 } // namespace V1_0 589 } // namespace contexthub 590 } // namespace hardware 591 } // namespace android 592