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