1 /* 2 * Copyright 2016, 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 #undef LOG_NDEBUG 18 #undef LOG_TAG 19 #define LOG_NDEBUG 0 20 #define LOG_TAG "ContextHubService" 21 22 #include <inttypes.h> 23 #include <jni.h> 24 #include <stdint.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <sys/endian.h> 29 30 #include <chrono> 31 #include <mutex> 32 #include <queue> 33 #include <unordered_map> 34 #include <utility> 35 36 #include <android-base/macros.h> 37 #include <android/hardware/contexthub/1.0/IContexthub.h> 38 #include <cutils/log.h> 39 40 #include "core_jni_helpers.h" 41 #include "JNIHelp.h" 42 43 using android::hardware::contexthub::V1_0::AsyncEventType; 44 using android::hardware::contexthub::V1_0::ContextHub; 45 using android::hardware::contexthub::V1_0::ContextHubMsg; 46 using android::hardware::contexthub::V1_0::HubAppInfo; 47 using android::hardware::contexthub::V1_0::IContexthub; 48 using android::hardware::contexthub::V1_0::IContexthubCallback; 49 using android::hardware::contexthub::V1_0::NanoAppBinary; 50 using android::hardware::contexthub::V1_0::Result; 51 using android::hardware::contexthub::V1_0::TransactionResult; 52 53 using android::hardware::Return; 54 55 using std::chrono::steady_clock; 56 57 // If a transaction takes longer than this, we'll allow it to be 58 // canceled by a new transaction. Note we do _not_ automatically 59 // cancel a transaction after this much time. We can have a 60 // legal transaction which takes longer than this amount of time, 61 // as long as no other new transactions are attempted after this 62 // time has expired. 63 constexpr auto kMinTransactionCancelTime = std::chrono::seconds(29); 64 65 namespace android { 66 67 constexpr uint32_t kNanoAppBinaryHeaderVersion = 1; 68 69 // Important: this header is explicitly defined as little endian byte order, and 70 // therefore may not match host endianness 71 struct NanoAppBinaryHeader { 72 uint32_t headerVersion; // 0x1 for this version 73 uint32_t magic; // "NANO" (see NANOAPP_MAGIC in context_hub.h) 74 uint64_t appId; // App Id, contains vendor id 75 uint32_t appVersion; // Version of the app 76 uint32_t flags; // Signed, encrypted 77 uint64_t hwHubType; // Which hub type is this compiled for 78 uint8_t targetChreApiMajorVersion; // Which CHRE API version this is compiled for 79 uint8_t targetChreApiMinorVersion; 80 uint8_t reserved[6]; 81 } __attribute__((packed)); 82 83 enum HubMessageType { 84 CONTEXT_HUB_APPS_ENABLE = 1, // Enables loaded nano-app(s) 85 CONTEXT_HUB_APPS_DISABLE = 2, // Disables loaded nano-app(s) 86 CONTEXT_HUB_LOAD_APP = 3, // Load a supplied app 87 CONTEXT_HUB_UNLOAD_APP = 4, // Unload a specified app 88 CONTEXT_HUB_QUERY_APPS = 5, // Query for app(s) info on hub 89 CONTEXT_HUB_QUERY_MEMORY = 6, // Query for memory info 90 CONTEXT_HUB_OS_REBOOT = 7, // Request to reboot context HUB OS 91 }; 92 93 constexpr jint OS_APP_ID = -1; 94 constexpr jint INVALID_APP_ID = -2; 95 96 constexpr jint MIN_APP_ID = 1; 97 constexpr jint MAX_APP_ID = 128; 98 99 constexpr size_t MSG_HEADER_SIZE = 4; 100 constexpr size_t HEADER_FIELD_MSG_TYPE = 0; 101 constexpr size_t HEADER_FIELD_MSG_VERSION = 1; 102 constexpr size_t HEADER_FIELD_HUB_HANDLE = 2; 103 constexpr size_t HEADER_FIELD_APP_INSTANCE = 3; 104 105 constexpr size_t HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE; 106 constexpr size_t HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1; 107 constexpr size_t MSG_HEADER_SIZE_LOAD_APP = MSG_HEADER_SIZE + 2; 108 109 jint getAppInstanceForAppId(uint64_t app_id); 110 int onMessageReceipt(const uint32_t *header, 111 size_t headerLen, 112 const char *msg, 113 size_t msgLen); 114 void onHubReset(uint32_t hubId); 115 void queryHubForApps(uint32_t hubId); 116 void passOnOsResponse(uint32_t hubHandle, 117 uint32_t msgType, 118 TransactionResult result, 119 const int8_t *additionalData, 120 size_t additionalDataLen); 121 122 bool closeLoadTxn(bool success, jint *appInstanceHandle); 123 void closeUnloadTxn(bool success); 124 int handleQueryAppsResponse(const std::vector<HubAppInfo> apps, 125 uint32_t hubHandle); 126 127 struct JniInfo { 128 JavaVM *vm; 129 jclass contextHubInfoClass; 130 jclass contextHubServiceClass; 131 jclass memoryRegionsClass; 132 133 jobject jContextHubService; 134 135 jmethodID msgReceiptCallBack; 136 137 jmethodID contextHubInfoCtor; 138 jmethodID contextHubInfoSetId; 139 jmethodID contextHubInfoSetName; 140 jmethodID contextHubInfoSetVendor; 141 jmethodID contextHubInfoSetToolchain; 142 jmethodID contextHubInfoSetPlatformVersion; 143 jmethodID contextHubInfoSetStaticSwVersion; 144 jmethodID contextHubInfoSetToolchainVersion; 145 jmethodID contextHubInfoSetPeakMips; 146 jmethodID contextHubInfoSetStoppedPowerDrawMw; 147 jmethodID contextHubInfoSetSleepPowerDrawMw; 148 jmethodID contextHubInfoSetPeakPowerDrawMw; 149 jmethodID contextHubInfoSetSupportedSensors; 150 jmethodID contextHubInfoSetMemoryRegions; 151 jmethodID contextHubInfoSetMaxPacketLenBytes; 152 153 jmethodID contextHubServiceMsgReceiptCallback; 154 jmethodID contextHubServiceAddAppInstance; 155 jmethodID contextHubServiceDeleteAppInstance; 156 }; 157 158 159 160 class TxnManager { 161 public: 162 TxnManager() { 163 mData = nullptr; 164 mIsPending = false; 165 } 166 167 ~TxnManager() { 168 closeTxn(); 169 } 170 171 int addTxn(HubMessageType txnIdentifier, void *txnData) { 172 std::lock_guard<std::mutex>lock(mLock); 173 if (mIsPending) { 174 ALOGW("Transaction already found pending when trying to add a new one."); 175 return -1; 176 } 177 mIsPending = true; 178 mFirstTimeTxnCanBeCanceled = steady_clock::now() + kMinTransactionCancelTime; 179 mData = txnData; 180 mIdentifier = txnIdentifier; 181 182 return 0; 183 } 184 185 int closeTxn() { 186 std::lock_guard<std::mutex>lock(mLock); 187 closeTxnUnlocked(); 188 return 0; 189 } 190 191 bool isTxnPending() { 192 std::lock_guard<std::mutex>lock(mLock); 193 return mIsPending; 194 } 195 196 void closeAnyStaleTxns() { 197 std::lock_guard<std::mutex>lock(mLock); 198 if (mIsPending && steady_clock::now() >= mFirstTimeTxnCanBeCanceled) { 199 ALOGW("Stale transaction canceled"); 200 closeTxnUnlocked(); 201 } 202 } 203 204 int fetchTxnData(HubMessageType *id, void **data) { 205 if (id == nullptr || data == nullptr) { 206 ALOGW("Null Params isNull{id, data} {%d, %d}", 207 id == nullptr ? 1 : 0, 208 data == nullptr ? 1 : 0); 209 return -1; 210 } 211 212 std::lock_guard<std::mutex>lock(mLock); 213 if (!mIsPending) { 214 ALOGW("No Transactions pending"); 215 return -1; 216 } 217 218 *id = mIdentifier; 219 *data = mData; 220 return 0; 221 } 222 223 private: 224 bool mIsPending; // Is a transaction pending 225 std::mutex mLock; // mutex for manager 226 HubMessageType mIdentifier; // What are we doing 227 void *mData; // Details 228 steady_clock::time_point mFirstTimeTxnCanBeCanceled; 229 230 // Only call this if you hold the lock. 231 void closeTxnUnlocked() { 232 mIsPending = false; 233 free(mData); 234 mData = nullptr; 235 } 236 }; 237 238 239 struct ContextHubServiceCallback : IContexthubCallback { 240 uint32_t mContextHubId; 241 242 ContextHubServiceCallback(uint32_t hubId) { 243 mContextHubId = hubId; 244 } 245 246 virtual Return<void> handleClientMsg(const ContextHubMsg &msg) { 247 jint appHandle = getAppInstanceForAppId(msg.appName); 248 if (appHandle < 0) { 249 ALOGE("Filtering out message due to invalid App Instance."); 250 } else { 251 uint32_t msgHeader[MSG_HEADER_SIZE] = {}; 252 msgHeader[HEADER_FIELD_MSG_TYPE] = msg.msgType; 253 msgHeader[HEADER_FIELD_HUB_HANDLE] = mContextHubId; 254 msgHeader[HEADER_FIELD_APP_INSTANCE] = appHandle; 255 onMessageReceipt(msgHeader, 256 MSG_HEADER_SIZE, 257 reinterpret_cast<const char *>(msg.msg.data()), 258 msg.msg.size()); 259 } 260 261 return android::hardware::Void(); 262 } 263 264 virtual Return<void> handleHubEvent(AsyncEventType evt) { 265 if (evt == AsyncEventType::RESTARTED) { 266 ALOGW("Context Hub handle %d restarted", mContextHubId); 267 onHubReset(mContextHubId); 268 } else { 269 ALOGW("Cannot handle event %u from hub %d", evt, mContextHubId); 270 } 271 272 return android::hardware::Void(); 273 } 274 275 virtual Return<void> handleTxnResult(uint32_t txnId, 276 TransactionResult result) { 277 ALOGI("Handle transaction result , hubId %" PRIu32 ", txnId %" PRIu32 ", result %" PRIu32, 278 mContextHubId, 279 txnId, 280 result); 281 282 switch(txnId) { 283 case CONTEXT_HUB_APPS_ENABLE: 284 case CONTEXT_HUB_APPS_DISABLE: 285 passOnOsResponse(mContextHubId, txnId, result, nullptr, 0); 286 break; 287 288 case CONTEXT_HUB_UNLOAD_APP: 289 closeUnloadTxn(result == TransactionResult::SUCCESS); 290 passOnOsResponse(mContextHubId, txnId, result, nullptr, 0); 291 break; 292 293 case CONTEXT_HUB_LOAD_APP: 294 { 295 jint appInstanceHandle = INVALID_APP_ID; 296 bool appRunningOnHub = (result == TransactionResult::SUCCESS); 297 if (!(closeLoadTxn(appRunningOnHub, &appInstanceHandle))) { 298 if (appRunningOnHub) { 299 // Now we're in an odd situation. Our nanoapp 300 // is up and running on the Context Hub. However, 301 // something went wrong in our Service code so that 302 // we're not able to properly track this nanoapp 303 // in our Service code. If we tell the Java layer 304 // things are good, it's a lie because the handle 305 // we give them will fail when used with the Service. 306 // If we tell the Java layer this failed, it's kind 307 // of a lie as well, since this nanoapp is running. 308 // 309 // We leave a more robust fix for later, and for 310 // now just tell the user things have failed. 311 // 312 // TODO(b/30835981): Make this situation better. 313 result = TransactionResult::FAILURE; 314 } 315 } 316 317 passOnOsResponse(mContextHubId, 318 txnId, 319 result, 320 reinterpret_cast<int8_t *>(&appInstanceHandle), 321 sizeof(appInstanceHandle)); 322 break; 323 } 324 325 default: 326 ALOGI("unrecognized transction id %" PRIu32, txnId); 327 break; 328 } 329 return android::hardware::Void(); 330 } 331 332 virtual Return<void> handleAppsInfo( 333 const android::hardware::hidl_vec<HubAppInfo>& apps) { 334 TransactionResult result = TransactionResult::SUCCESS; 335 handleQueryAppsResponse(apps,mContextHubId); 336 passOnOsResponse(mContextHubId, CONTEXT_HUB_QUERY_APPS, result, nullptr, 0); 337 return android::hardware::Void(); 338 } 339 340 virtual Return<void> handleAppAbort(uint64_t appId, uint32_t abortCode) { 341 ALOGI("Handle app aport called from %" PRIx64 " with abort code %" PRIu32, 342 appId, 343 abortCode); 344 345 // TODO: Plumb this to the clients interested in this app 346 return android::hardware::Void(); 347 } 348 349 void setContextHubId(uint32_t id) { 350 mContextHubId = id; 351 } 352 353 uint32_t getContextHubId() { 354 return(mContextHubId); 355 } 356 }; 357 358 struct AppInstanceInfo { 359 HubAppInfo appInfo; // returned from the HAL 360 uint64_t truncName; // Possibly truncated name for logging 361 uint32_t hubHandle; // Id of the hub this app is on 362 jint instanceId; // system wide unique instance id - assigned 363 }; 364 365 struct ContextHubInfo { 366 int numHubs; 367 Vector<ContextHub> hubs; 368 sp<IContexthub> contextHub; 369 }; 370 371 struct ContextHubServiceDb { 372 int initialized; 373 ContextHubInfo hubInfo; 374 JniInfo jniInfo; 375 std::queue<jint> freeIds; 376 std::unordered_map<jint, AppInstanceInfo> appInstances; 377 TxnManager txnManager; 378 std::vector<ContextHubServiceCallback *> regCallBacks; 379 }; 380 381 ContextHubServiceDb db; 382 383 bool getHubIdForHubHandle(int hubHandle, uint32_t *hubId) { 384 if (hubHandle < 0 || hubHandle >= db.hubInfo.numHubs || hubId == nullptr) { 385 return false; 386 } else { 387 *hubId = db.hubInfo.hubs[hubHandle].hubId; 388 return true; 389 } 390 } 391 392 int getHubHandleForAppInstance(jint id) { 393 if (!db.appInstances.count(id)) { 394 ALOGD("%s: Cannot find app for app instance %" PRId32, 395 __FUNCTION__, 396 id); 397 return -1; 398 } 399 400 return db.appInstances[id].hubHandle; 401 } 402 403 jint getAppInstanceForAppId(uint64_t app_id) { 404 auto end = db.appInstances.end(); 405 for (auto current = db.appInstances.begin(); current != end; ++current) { 406 if (current->second.appInfo.appId == app_id) { 407 return current->first; 408 } 409 } 410 ALOGD("Cannot find app for app id %" PRIu64 ".", app_id); 411 return -1; 412 } 413 414 uint64_t getAppIdForAppInstance(jint id) { 415 if (!db.appInstances.count(id)) { 416 return INVALID_APP_ID; 417 } 418 return db.appInstances[id].appInfo.appId; 419 } 420 421 void queryHubForApps(uint32_t hubId) { 422 Result r = db.hubInfo.contextHub->queryApps(hubId); 423 ALOGD("Sent query for apps to hub %" PRIu32 " with result %" PRIu32, hubId, r); 424 } 425 426 void sendQueryForApps() { 427 for (int i = 0; i < db.hubInfo.numHubs; i++ ) { 428 queryHubForApps(db.hubInfo.hubs[i].hubId); 429 } 430 } 431 432 int returnId(jint id) { 433 // Note : This method is not thread safe. 434 // id returned is guaranteed to be in use 435 if (id >= 0) { 436 db.freeIds.push(id); 437 return 0; 438 } 439 440 return -1; 441 } 442 443 jint generateId() { 444 // Note : This method is not thread safe. 445 jint retVal = -1; 446 447 if (!db.freeIds.empty()) { 448 retVal = db.freeIds.front(); 449 db.freeIds.pop(); 450 } 451 452 return retVal; 453 } 454 455 jint addAppInstance(const HubAppInfo *appInfo, uint32_t hubHandle, 456 jint appInstanceHandle, JNIEnv *env) { 457 // Not checking if the apps are indeed distinct 458 AppInstanceInfo entry; 459 assert(appInfo); 460 461 462 entry.appInfo = *appInfo; 463 464 entry.instanceId = appInstanceHandle; 465 entry.truncName = appInfo->appId; 466 entry.hubHandle = hubHandle; 467 db.appInstances[appInstanceHandle] = entry; 468 // Finally - let the service know of this app instance, to populate 469 // the Java cache. 470 env->CallIntMethod(db.jniInfo.jContextHubService, 471 db.jniInfo.contextHubServiceAddAppInstance, 472 hubHandle, entry.instanceId, 473 entry.truncName, 474 entry.appInfo.version); 475 476 const char *action = (db.appInstances.count(appInstanceHandle) == 0) ? "Added" : "Updated"; 477 ALOGI("%s App 0x%" PRIx64 " on hub Handle %" PRId32 478 " as appInstance %" PRId32, action, entry.truncName, 479 entry.hubHandle, appInstanceHandle); 480 481 return appInstanceHandle; 482 } 483 484 int deleteAppInstance(jint id, JNIEnv *env) { 485 bool fullyDeleted = true; 486 487 if (db.appInstances.count(id)) { 488 db.appInstances.erase(id); 489 } else { 490 ALOGW("Cannot delete App id (%" PRId32 ") from the JNI C++ cache", id); 491 fullyDeleted = false; 492 } 493 returnId(id); 494 495 if ((env == nullptr) || 496 (env->CallIntMethod(db.jniInfo.jContextHubService, 497 db.jniInfo.contextHubServiceDeleteAppInstance, 498 id) != 0)) { 499 ALOGW("Cannot delete App id (%" PRId32 ") from Java cache", id); 500 fullyDeleted = false; 501 } 502 503 if (fullyDeleted) { 504 ALOGI("Deleted App id : %" PRId32, id); 505 return 0; 506 } 507 return -1; 508 } 509 510 int startLoadAppTxn(uint64_t appId, int hubHandle) { 511 AppInstanceInfo *txnInfo = new AppInstanceInfo(); 512 jint instanceId = generateId(); 513 514 if (!txnInfo || instanceId < 0) { 515 returnId(instanceId); 516 delete txnInfo; 517 return -1; 518 } 519 520 txnInfo->truncName = appId; 521 txnInfo->hubHandle = hubHandle; 522 txnInfo->instanceId = instanceId; 523 524 txnInfo->appInfo.appId = appId; 525 txnInfo->appInfo.version = -1; // Awaited 526 527 if (db.txnManager.addTxn(CONTEXT_HUB_LOAD_APP, txnInfo) != 0) { 528 returnId(instanceId); 529 delete txnInfo; 530 return -1; 531 } 532 533 return 0; 534 } 535 536 int startUnloadAppTxn(jint appInstanceHandle) { 537 jint *txnData = new(jint); 538 if (!txnData) { 539 ALOGW("Cannot allocate memory to start unload transaction"); 540 return -1; 541 } 542 543 *txnData = appInstanceHandle; 544 545 if (db.txnManager.addTxn(CONTEXT_HUB_UNLOAD_APP, txnData) != 0) { 546 delete txnData; 547 ALOGW("Cannot start transaction to unload app"); 548 return -1; 549 } 550 551 return 0; 552 } 553 554 void getHubsCb(const ::android::hardware::hidl_vec<ContextHub>& hubs) { 555 for (size_t i = 0; i < hubs.size(); i++) { 556 db.hubInfo.hubs.push_back(hubs[i]); 557 } 558 } 559 560 void initContextHubService() { 561 db.hubInfo.numHubs = 0; 562 563 db.hubInfo.contextHub = IContexthub::getService(); 564 565 if (db.hubInfo.contextHub == nullptr) { 566 ALOGE("Could not load context hub hal"); 567 } else { 568 ALOGI("Loaded context hub hal, isRemote %s", db.hubInfo.contextHub->isRemote() ? "TRUE" : "FALSE"); 569 } 570 571 // Prep for storing app info 572 for (jint i = MIN_APP_ID; i <= MAX_APP_ID; i++) { 573 db.freeIds.push(i); 574 } 575 576 if (db.hubInfo.contextHub != nullptr) { 577 std::function<void(const ::android::hardware::hidl_vec<ContextHub>& hubs)> f = getHubsCb; 578 if(!db.hubInfo.contextHub->getHubs(f).isOk()) { 579 ALOGW("GetHubs Failed! transport error."); 580 return; 581 }; 582 583 int retNumHubs = db.hubInfo.hubs.size(); 584 ALOGD("ContextHubModule returned %d hubs ", retNumHubs); 585 db.hubInfo.numHubs = retNumHubs; 586 587 for (int i = 0; i < db.hubInfo.numHubs; i++) { 588 ALOGI("Subscribing to hubHandle %d", i); 589 590 ContextHubServiceCallback *callBackPtr = 591 new ContextHubServiceCallback(db.hubInfo.hubs[i].hubId); 592 db.hubInfo.contextHub->registerCallback(db.hubInfo.hubs[i].hubId, 593 callBackPtr); 594 db.regCallBacks.push_back(callBackPtr); 595 } 596 597 sendQueryForApps(); 598 599 } else { 600 ALOGW("No Context Hub Module present"); 601 } 602 } 603 604 void onHubReset(uint32_t hubId) { 605 TransactionResult result = TransactionResult::SUCCESS; 606 db.txnManager.closeTxn(); 607 // TODO : Expose this through an api 608 passOnOsResponse(hubId, CONTEXT_HUB_OS_REBOOT, result, nullptr, 0); 609 queryHubForApps(hubId); 610 } 611 612 int onMessageReceipt(const uint32_t *header, 613 size_t headerLen, 614 const char *msg, 615 size_t msgLen) { 616 JNIEnv *env; 617 618 if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) { 619 return -1; 620 } 621 622 jbyteArray jmsg = env->NewByteArray(msgLen); 623 if (jmsg == nullptr) { 624 ALOGW("Can't allocate %zu byte array", msgLen); 625 return -1; 626 } 627 jintArray jheader = env->NewIntArray(headerLen); 628 if (jheader == nullptr) { 629 env->DeleteLocalRef(jmsg); 630 ALOGW("Can't allocate %zu int array", headerLen); 631 return -1; 632 } 633 634 env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<const jbyte *>(msg)); 635 env->SetIntArrayRegion(jheader, 0, headerLen, reinterpret_cast<const jint *>(header)); 636 637 int ret = (env->CallIntMethod(db.jniInfo.jContextHubService, 638 db.jniInfo.contextHubServiceMsgReceiptCallback, 639 jheader, 640 jmsg) != 0); 641 env->DeleteLocalRef(jmsg); 642 env->DeleteLocalRef(jheader); 643 644 return ret; 645 } 646 647 int handleQueryAppsResponse(const std::vector<HubAppInfo> apps, 648 uint32_t hubHandle) { 649 JNIEnv *env; 650 if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) { 651 return -1; 652 } 653 654 int numApps = apps.size(); 655 656 // We use this information to sync our JNI and Java caches of nanoapp info. 657 // We want to accomplish two things here: 658 // 1) Remove entries from our caches which are stale, and pertained to 659 // apps no longer running on Context Hub. 660 // 2) Populate our caches with the latest information of all these apps. 661 662 // We make a couple of assumptions here: 663 // A) The JNI and Java caches are in sync with each other (this isn't 664 // necessarily true; any failure of a single call into Java land to 665 // update its cache will leave that cache in a bad state. For NYC, 666 // we're willing to tolerate this for now). 667 // B) The total number of apps is relatively small, so horribly inefficent 668 // algorithms aren't too painful. 669 // C) We're going to call this relatively infrequently, so its inefficency 670 // isn't a big impact. 671 672 673 // (1). Looking for stale cache entries. Yes, this is O(N^2). See 674 // assumption (B). Per assumption (A), it is sufficient to iterate 675 // over just the JNI cache. 676 auto end = db.appInstances.end(); 677 for (auto current = db.appInstances.begin(); current != end; ) { 678 AppInstanceInfo cacheEntry = current->second; 679 // We perform our iteration here because if we call 680 // delete_app_instance() below, it will erase() this entry. 681 current++; 682 bool entryIsStale = true; 683 for (int i = 0; i < numApps; i++) { 684 if (apps[i].appId == cacheEntry.appInfo.appId) { 685 // We found a match; this entry is current. 686 entryIsStale = false; 687 break; 688 } 689 } 690 691 if (entryIsStale) { 692 deleteAppInstance(cacheEntry.instanceId, env); 693 } 694 } 695 696 // (2). Update our caches with the latest. 697 for (int i = 0; i < numApps; i++) { 698 // We will only have one instance of the app 699 // TODO : Change this logic once we support multiple instances of the same app 700 jint appInstance = getAppInstanceForAppId(apps[i].appId); 701 if (appInstance == -1) { 702 // This is a previously unknown app, let's allocate an "id" for it. 703 appInstance = generateId(); 704 } 705 addAppInstance(&apps[i], hubHandle, appInstance, env); 706 } 707 return 0; 708 } 709 710 // TODO(b/30807327): Do not use raw bytes for additional data. Use the 711 // JNI interfaces for the appropriate types. 712 void passOnOsResponse(uint32_t hubHandle, 713 uint32_t msgType, 714 TransactionResult result, 715 const int8_t *additionalData, 716 size_t additionalDataLen) { 717 JNIEnv *env; 718 719 if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) { 720 ALOGW("Cannot latch to JNI env, dropping OS response %" PRIu32, 721 msgType); 722 return; 723 } 724 725 uint32_t header[MSG_HEADER_SIZE]; 726 memset(header, 0, sizeof(header)); 727 728 if (!additionalData) { 729 additionalDataLen = 0; // clamp 730 } 731 int msgLen = 1 + additionalDataLen; 732 733 int8_t *msg = new int8_t[msgLen]; 734 735 if (!msg) { 736 ALOGW("Unexpected : Ran out of memory, cannot send response"); 737 return; 738 } 739 740 header[HEADER_FIELD_MSG_TYPE] = msgType; 741 header[HEADER_FIELD_MSG_VERSION] = 0; 742 header[HEADER_FIELD_HUB_HANDLE] = hubHandle; 743 header[HEADER_FIELD_APP_INSTANCE] = OS_APP_ID; 744 745 // Due to API constraints, at the moment we can't change the fact that 746 // we're changing our 4-byte response to a 1-byte value. But we can prevent 747 // the possible change in sign (and thus meaning) that would happen from 748 // a naive cast. Further, we can log when we're losing part of the value. 749 // TODO(b/30918279): Don't truncate this result. 750 int8_t truncatedResult; 751 truncatedResult = static_cast<int8_t>(result); 752 msg[0] = truncatedResult; 753 754 if (additionalData) { 755 memcpy(&msg[1], additionalData, additionalDataLen); 756 } 757 758 jbyteArray jmsg = env->NewByteArray(msgLen); 759 jintArray jheader = env->NewIntArray(arraysize(header)); 760 761 env->SetByteArrayRegion(jmsg, 0, msgLen, reinterpret_cast<jbyte *>(msg)); 762 env->SetIntArrayRegion(jheader, 0, arraysize(header), reinterpret_cast<jint *>(header)); 763 764 ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32, 765 header[HEADER_FIELD_MSG_TYPE], 766 header[HEADER_FIELD_APP_INSTANCE], 767 header[HEADER_FIELD_HUB_HANDLE]); 768 769 env->CallIntMethod(db.jniInfo.jContextHubService, 770 db.jniInfo.contextHubServiceMsgReceiptCallback, 771 jheader, 772 jmsg); 773 774 env->DeleteLocalRef(jmsg); 775 env->DeleteLocalRef(jheader); 776 777 delete[] msg; 778 } 779 780 void closeUnloadTxn(bool success) { 781 void *txnData = nullptr; 782 HubMessageType txnId; 783 784 if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 && 785 txnId == CONTEXT_HUB_UNLOAD_APP) { 786 JNIEnv *env; 787 if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) { 788 ALOGW("Could not attach to JVM !"); 789 env = nullptr; 790 } 791 jint handle = *reinterpret_cast<jint *>(txnData); 792 deleteAppInstance(handle, env); 793 } else { 794 ALOGW("Could not unload the app successfully ! success %d, txnData %p", 795 success, 796 txnData); 797 } 798 799 db.txnManager.closeTxn(); 800 } 801 802 bool closeLoadTxn(bool success, jint *appInstanceHandle) { 803 void *txnData; 804 HubMessageType txnId; 805 806 if (success && db.txnManager.fetchTxnData(&txnId, &txnData) == 0 && 807 txnId == CONTEXT_HUB_LOAD_APP) { 808 AppInstanceInfo *info = static_cast<AppInstanceInfo *>(txnData); 809 *appInstanceHandle = info->instanceId; 810 811 JNIEnv *env; 812 if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) == JNI_OK) { 813 addAppInstance(&info->appInfo, info->hubHandle, info->instanceId, env); 814 } else { 815 ALOGW("Could not attach to JVM !"); 816 success = false; 817 } 818 // While we just called addAppInstance above, our info->appInfo was 819 // incomplete (for example, the 'version' is hardcoded to -1). So we 820 // trigger an additional query to the CHRE, so we'll be able to get 821 // all the app "info", and have our JNI and Java caches with the 822 // full information. 823 sendQueryForApps(); 824 } else { 825 ALOGW("Could not load the app successfully ! Unexpected failure"); 826 *appInstanceHandle = INVALID_APP_ID; 827 success = false; 828 } 829 830 db.txnManager.closeTxn(); 831 return success; 832 } 833 834 int initJni(JNIEnv *env, jobject instance) { 835 if (env->GetJavaVM(&db.jniInfo.vm) != JNI_OK) { 836 return -1; 837 } 838 839 db.jniInfo.jContextHubService = env->NewGlobalRef(instance); 840 841 db.jniInfo.contextHubInfoClass = 842 env->FindClass("android/hardware/location/ContextHubInfo"); 843 db.jniInfo.contextHubServiceClass = 844 env->FindClass("com/android/server/location/ContextHubService"); 845 846 db.jniInfo.memoryRegionsClass = 847 env->FindClass("android/hardware/location/MemoryRegion"); 848 849 db.jniInfo.contextHubInfoCtor = 850 env->GetMethodID(db.jniInfo.contextHubInfoClass, "<init>", "()V"); 851 db.jniInfo.contextHubInfoSetId = 852 env->GetMethodID(db.jniInfo.contextHubInfoClass, "setId", "(I)V"); 853 db.jniInfo.contextHubInfoSetName = 854 env->GetMethodID(db.jniInfo.contextHubInfoClass, "setName", "(Ljava/lang/String;)V"); 855 db.jniInfo.contextHubInfoSetVendor = 856 env->GetMethodID(db.jniInfo.contextHubInfoClass, 857 "setVendor", 858 "(Ljava/lang/String;)V"); 859 db.jniInfo.contextHubInfoSetToolchain = 860 env->GetMethodID(db.jniInfo.contextHubInfoClass, 861 "setToolchain", 862 "(Ljava/lang/String;)V"); 863 db.jniInfo.contextHubInfoSetPlatformVersion = 864 env->GetMethodID(db.jniInfo.contextHubInfoClass, 865 "setPlatformVersion", 866 "(I)V"); 867 db.jniInfo.contextHubInfoSetStaticSwVersion = 868 env->GetMethodID(db.jniInfo.contextHubInfoClass, 869 "setStaticSwVersion", 870 "(I)V"); 871 db.jniInfo.contextHubInfoSetToolchainVersion = 872 env->GetMethodID(db.jniInfo.contextHubInfoClass, 873 "setToolchainVersion", 874 "(I)V"); 875 db.jniInfo.contextHubInfoSetPeakMips = 876 env->GetMethodID(db.jniInfo.contextHubInfoClass, 877 "setPeakMips", 878 "(F)V"); 879 db.jniInfo.contextHubInfoSetStoppedPowerDrawMw = 880 env->GetMethodID(db.jniInfo.contextHubInfoClass, 881 "setStoppedPowerDrawMw", 882 "(F)V"); 883 db.jniInfo.contextHubInfoSetSleepPowerDrawMw = 884 env->GetMethodID(db.jniInfo.contextHubInfoClass, 885 "setSleepPowerDrawMw", 886 "(F)V"); 887 db.jniInfo.contextHubInfoSetPeakPowerDrawMw = 888 env->GetMethodID(db.jniInfo.contextHubInfoClass, 889 "setPeakPowerDrawMw", 890 "(F)V"); 891 db.jniInfo.contextHubInfoSetSupportedSensors = 892 env->GetMethodID(db.jniInfo.contextHubInfoClass, 893 "setSupportedSensors", 894 "([I)V"); 895 db.jniInfo.contextHubInfoSetMemoryRegions = 896 env->GetMethodID(db.jniInfo.contextHubInfoClass, 897 "setMemoryRegions", 898 "([Landroid/hardware/location/MemoryRegion;)V"); 899 db.jniInfo.contextHubInfoSetMaxPacketLenBytes = 900 env->GetMethodID(db.jniInfo.contextHubInfoClass, 901 "setMaxPacketLenBytes", 902 "(I)V"); 903 db.jniInfo.contextHubServiceMsgReceiptCallback = 904 env->GetMethodID(db.jniInfo.contextHubServiceClass, 905 "onMessageReceipt", 906 "([I[B)I"); 907 db.jniInfo.contextHubInfoSetName = 908 env->GetMethodID(db.jniInfo.contextHubInfoClass, 909 "setName", 910 "(Ljava/lang/String;)V"); 911 db.jniInfo.contextHubServiceAddAppInstance = 912 env->GetMethodID(db.jniInfo.contextHubServiceClass, 913 "addAppInstance", 914 "(IIJI)I"); 915 db.jniInfo.contextHubServiceDeleteAppInstance = 916 env->GetMethodID(db.jniInfo.contextHubServiceClass, 917 "deleteAppInstance", 918 "(I)I"); 919 920 return 0; 921 } 922 923 jobject constructJContextHubInfo(JNIEnv *env, const ContextHub &hub) { 924 jstring jstrBuf; 925 jintArray jintBuf; 926 jobjectArray jmemBuf; 927 928 jobject jHub = env->NewObject(db.jniInfo.contextHubInfoClass, 929 db.jniInfo.contextHubInfoCtor); 930 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetId, hub.hubId); 931 932 jstrBuf = env->NewStringUTF(hub.name.c_str()); 933 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetName, jstrBuf); 934 env->DeleteLocalRef(jstrBuf); 935 936 jstrBuf = env->NewStringUTF(hub.vendor.c_str()); 937 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetVendor, jstrBuf); 938 env->DeleteLocalRef(jstrBuf); 939 940 jstrBuf = env->NewStringUTF(hub.toolchain.c_str()); 941 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchain, jstrBuf); 942 env->DeleteLocalRef(jstrBuf); 943 944 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPlatformVersion, hub.platformVersion); 945 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetToolchainVersion, hub.toolchainVersion); 946 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakMips, hub.peakMips); 947 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetStoppedPowerDrawMw, 948 hub.stoppedPowerDrawMw); 949 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSleepPowerDrawMw, 950 hub.sleepPowerDrawMw); 951 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetPeakPowerDrawMw, 952 hub.peakPowerDrawMw); 953 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMaxPacketLenBytes, 954 hub.maxSupportedMsgLen); 955 956 957 jintBuf = env->NewIntArray(hub.connectedSensors.size()); 958 int *connectedSensors = new int[hub.connectedSensors.size()]; 959 960 if (!connectedSensors) { 961 ALOGW("Cannot allocate memory! Unexpected"); 962 assert(false); 963 } else { 964 for (unsigned int i = 0; i < hub.connectedSensors.size(); i++) { 965 // TODO :: Populate connected sensors. 966 //connectedSensors[i] = hub.connectedSensors[i].sensorType; 967 connectedSensors[i] = 0; 968 } 969 } 970 971 env->SetIntArrayRegion(jintBuf, 0, hub.connectedSensors.size(), 972 connectedSensors); 973 974 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetSupportedSensors, jintBuf); 975 env->DeleteLocalRef(jintBuf); 976 977 // We are not getting the memory regions from the CH Hal - change this when it is available 978 jmemBuf = env->NewObjectArray(0, db.jniInfo.memoryRegionsClass, nullptr); 979 // Note the zero size above. We do not need to set any elements 980 env->CallVoidMethod(jHub, db.jniInfo.contextHubInfoSetMemoryRegions, jmemBuf); 981 env->DeleteLocalRef(jmemBuf); 982 983 984 delete[] connectedSensors; 985 return jHub; 986 } 987 988 jobjectArray nativeInitialize(JNIEnv *env, jobject instance) { 989 jobject hub; 990 jobjectArray retArray; 991 992 if (initJni(env, instance) < 0) { 993 return nullptr; 994 } 995 996 initContextHubService(); 997 998 if (db.hubInfo.numHubs > 1) { 999 ALOGW("Clamping the number of hubs to 1"); 1000 db.hubInfo.numHubs = 1; 1001 } 1002 1003 retArray = env->NewObjectArray(db.hubInfo.numHubs, db.jniInfo.contextHubInfoClass, nullptr); 1004 1005 for(int i = 0; i < db.hubInfo.numHubs; i++) { 1006 hub = constructJContextHubInfo(env, db.hubInfo.hubs[i]); 1007 env->SetObjectArrayElement(retArray, i, hub); 1008 } 1009 1010 return retArray; 1011 } 1012 1013 Result sendLoadNanoAppRequest(uint32_t hubId, 1014 jbyte *data, 1015 size_t dataBufferLength) { 1016 auto header = reinterpret_cast<const NanoAppBinaryHeader *>(data); 1017 Result result; 1018 1019 if (dataBufferLength < sizeof(NanoAppBinaryHeader)) { 1020 ALOGE("Got short NanoApp, length %zu", dataBufferLength); 1021 result = Result::BAD_PARAMS; 1022 } else if (header->headerVersion != htole32(kNanoAppBinaryHeaderVersion)) { 1023 ALOGE("Got unexpected NanoApp header version %" PRIu32, 1024 letoh32(header->headerVersion)); 1025 result = Result::BAD_PARAMS; 1026 } else { 1027 NanoAppBinary nanoapp; 1028 1029 // Data from the common nanoapp header goes into explicit fields 1030 nanoapp.appId = letoh64(header->appId); 1031 nanoapp.appVersion = letoh32(header->appVersion); 1032 nanoapp.flags = letoh32(header->flags); 1033 nanoapp.targetChreApiMajorVersion = header->targetChreApiMajorVersion; 1034 nanoapp.targetChreApiMinorVersion = header->targetChreApiMinorVersion; 1035 1036 // Everything past the header goes in customBinary 1037 auto dataBytes = reinterpret_cast<const uint8_t *>(data); 1038 std::vector<uint8_t> customBinary( 1039 dataBytes + sizeof(NanoAppBinaryHeader), 1040 dataBytes + dataBufferLength); 1041 nanoapp.customBinary = std::move(customBinary); 1042 1043 ALOGW("Calling Load NanoApp on hub %d", hubId); 1044 result = db.hubInfo.contextHub->loadNanoApp(hubId, 1045 nanoapp, 1046 CONTEXT_HUB_LOAD_APP); 1047 } 1048 1049 return result; 1050 } 1051 1052 jint nativeSendMessage(JNIEnv *env, 1053 jobject instance, 1054 jintArray header_, 1055 jbyteArray data_) { 1056 // With the new binderized HAL definition, this function can be made much simpler. 1057 // All the magic can be removed. This is not however needed for the default implementation 1058 // TODO :: Change the JNI interface to conform to the new HAL interface and clean up this 1059 // function 1060 jint retVal = -1; // Default to failure 1061 1062 jint *header = env->GetIntArrayElements(header_, 0); 1063 size_t numHeaderElements = env->GetArrayLength(header_); 1064 jbyte *data = env->GetByteArrayElements(data_, 0); 1065 size_t dataBufferLength = env->GetArrayLength(data_); 1066 1067 if (numHeaderElements < MSG_HEADER_SIZE) { 1068 ALOGW("Malformed header len"); 1069 return -1; 1070 } 1071 1072 jint appInstanceHandle = header[HEADER_FIELD_APP_INSTANCE]; 1073 uint32_t msgType = header[HEADER_FIELD_MSG_TYPE]; 1074 int hubHandle = -1; 1075 uint64_t appId; 1076 1077 if (msgType == CONTEXT_HUB_UNLOAD_APP) { 1078 hubHandle = getHubHandleForAppInstance(appInstanceHandle); 1079 } else if (msgType == CONTEXT_HUB_LOAD_APP) { 1080 if (numHeaderElements < MSG_HEADER_SIZE_LOAD_APP) { 1081 return -1; 1082 } 1083 uint64_t appIdLo = header[HEADER_FIELD_LOAD_APP_ID_LO]; 1084 uint64_t appIdHi = header[HEADER_FIELD_LOAD_APP_ID_HI]; 1085 appId = appIdHi << 32 | appIdLo; 1086 1087 hubHandle = header[HEADER_FIELD_HUB_HANDLE]; 1088 } else { 1089 hubHandle = header[HEADER_FIELD_HUB_HANDLE]; 1090 } 1091 1092 uint32_t hubId = -1; 1093 if (!getHubIdForHubHandle(hubHandle, &hubId)) { 1094 ALOGD("Invalid hub Handle %d", hubHandle); 1095 return -1; 1096 } 1097 1098 if (msgType == CONTEXT_HUB_LOAD_APP || 1099 msgType == CONTEXT_HUB_UNLOAD_APP) { 1100 1101 db.txnManager.closeAnyStaleTxns(); 1102 1103 if (db.txnManager.isTxnPending()) { 1104 // TODO : There is a race conditio 1105 ALOGW("Cannot load or unload app while a transaction is pending !"); 1106 return -1; 1107 } else if (msgType == CONTEXT_HUB_LOAD_APP) { 1108 if (startLoadAppTxn(appId, hubHandle) != 0) { 1109 ALOGW("Cannot Start Load Transaction"); 1110 return -1; 1111 } 1112 } else if (msgType == CONTEXT_HUB_UNLOAD_APP) { 1113 if (startUnloadAppTxn(appInstanceHandle) != 0) { 1114 ALOGW("Cannot Start UnLoad Transaction"); 1115 return -1; 1116 } 1117 } 1118 } 1119 1120 Result result; 1121 1122 if (msgType == CONTEXT_HUB_UNLOAD_APP) { 1123 ALOGW("Calling UnLoad NanoApp for app %" PRIx64 " on hub %" PRIu32, 1124 db.appInstances[appInstanceHandle].appInfo.appId, 1125 hubId); 1126 result = db.hubInfo.contextHub->unloadNanoApp( 1127 hubId, db.appInstances[appInstanceHandle].appInfo.appId, CONTEXT_HUB_UNLOAD_APP); 1128 } else { 1129 if (appInstanceHandle == OS_APP_ID) { 1130 if (msgType == CONTEXT_HUB_LOAD_APP) { 1131 result = sendLoadNanoAppRequest(hubId, data, dataBufferLength); 1132 } else if (msgType == CONTEXT_HUB_QUERY_APPS) { 1133 result = db.hubInfo.contextHub->queryApps(hubId); 1134 } else { 1135 ALOGD("Dropping OS addresses message of type - %" PRIu32, msgType); 1136 result = Result::BAD_PARAMS; 1137 } 1138 } else { 1139 appId = getAppIdForAppInstance(appInstanceHandle); 1140 if (appId == static_cast<uint64_t>(INVALID_APP_ID)) { 1141 ALOGD("Cannot find application instance %d", appInstanceHandle); 1142 result = Result::BAD_PARAMS; 1143 } else if (hubHandle != getHubHandleForAppInstance(appInstanceHandle)) { 1144 ALOGE("Given hubHandle (%d) doesn't match expected for app instance (%d)", 1145 hubHandle, 1146 getHubHandleForAppInstance(appInstanceHandle)); 1147 result = Result::BAD_PARAMS; 1148 } else { 1149 ContextHubMsg msg; 1150 msg.appName = appId; 1151 msg.msgType = msgType; 1152 msg.msg.setToExternal((unsigned char *)data, dataBufferLength); 1153 1154 ALOGW("Sending msg of type %" PRIu32 " len %zu to app %" PRIx64 " on hub %" PRIu32, 1155 msgType, 1156 dataBufferLength, 1157 appId, 1158 hubId); 1159 result = db.hubInfo.contextHub->sendMessageToHub(hubId, msg); 1160 } 1161 } 1162 } 1163 1164 if (result != Result::OK) { 1165 ALOGD("Send Message failure - %d", retVal); 1166 if (msgType == CONTEXT_HUB_LOAD_APP) { 1167 jint ignored; 1168 closeLoadTxn(false, &ignored); 1169 } else if (msgType == CONTEXT_HUB_UNLOAD_APP) { 1170 closeUnloadTxn(false); 1171 } 1172 } else { 1173 retVal = 0; 1174 } 1175 1176 env->ReleaseIntArrayElements(header_, header, 0); 1177 env->ReleaseByteArrayElements(data_, data, 0); 1178 1179 return retVal; 1180 } 1181 1182 //-------------------------------------------------------------------------------------------------- 1183 // 1184 const JNINativeMethod gContextHubServiceMethods[] = { 1185 {"nativeInitialize", 1186 "()[Landroid/hardware/location/ContextHubInfo;", 1187 reinterpret_cast<void*>(nativeInitialize)}, 1188 {"nativeSendMessage", 1189 "([I[B)I", 1190 reinterpret_cast<void*>(nativeSendMessage)} 1191 }; 1192 1193 int register_android_server_location_ContextHubService(JNIEnv *env) 1194 { 1195 RegisterMethodsOrDie(env, "com/android/server/location/ContextHubService", 1196 gContextHubServiceMethods, NELEM(gContextHubServiceMethods)); 1197 1198 return 0; 1199 } 1200 1201 }//namespace android 1202