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:
TxnManager()162 TxnManager() {
163 mData = nullptr;
164 mIsPending = false;
165 }
166
~TxnManager()167 ~TxnManager() {
168 closeTxn();
169 }
170
addTxn(HubMessageType txnIdentifier,void * txnData)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
closeTxn()185 int closeTxn() {
186 std::lock_guard<std::mutex>lock(mLock);
187 closeTxnUnlocked();
188 return 0;
189 }
190
isTxnPending()191 bool isTxnPending() {
192 std::lock_guard<std::mutex>lock(mLock);
193 return mIsPending;
194 }
195
closeAnyStaleTxns()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
fetchTxnData(HubMessageType * id,void ** data)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.
closeTxnUnlocked()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
ContextHubServiceCallbackandroid::ContextHubServiceCallback242 ContextHubServiceCallback(uint32_t hubId) {
243 mContextHubId = hubId;
244 }
245
handleClientMsgandroid::ContextHubServiceCallback246 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
handleHubEventandroid::ContextHubServiceCallback264 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
handleTxnResultandroid::ContextHubServiceCallback275 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
handleAppsInfoandroid::ContextHubServiceCallback332 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
handleAppAbortandroid::ContextHubServiceCallback340 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
setContextHubIdandroid::ContextHubServiceCallback349 void setContextHubId(uint32_t id) {
350 mContextHubId = id;
351 }
352
getContextHubIdandroid::ContextHubServiceCallback353 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
getHubIdForHubHandle(int hubHandle,uint32_t * hubId)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
getHubHandleForAppInstance(jint id)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
getAppInstanceForAppId(uint64_t app_id)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
getAppIdForAppInstance(jint id)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
queryHubForApps(uint32_t hubId)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
sendQueryForApps()426 void sendQueryForApps() {
427 for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
428 queryHubForApps(db.hubInfo.hubs[i].hubId);
429 }
430 }
431
returnId(jint id)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
generateId()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
addAppInstance(const HubAppInfo * appInfo,uint32_t hubHandle,jint appInstanceHandle,JNIEnv * env)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
deleteAppInstance(jint id,JNIEnv * env)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
startLoadAppTxn(uint64_t appId,int hubHandle)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
startUnloadAppTxn(jint appInstanceHandle)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
getHubsCb(const::android::hardware::hidl_vec<ContextHub> & hubs)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
initContextHubService()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
onHubReset(uint32_t hubId)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
onMessageReceipt(const uint32_t * header,size_t headerLen,const char * msg,size_t msgLen)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
handleQueryAppsResponse(const std::vector<HubAppInfo> apps,uint32_t hubHandle)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.
passOnOsResponse(uint32_t hubHandle,uint32_t msgType,TransactionResult result,const int8_t * additionalData,size_t additionalDataLen)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
closeUnloadTxn(bool success)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
closeLoadTxn(bool success,jint * appInstanceHandle)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
initJni(JNIEnv * env,jobject instance)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
constructJContextHubInfo(JNIEnv * env,const ContextHub & hub)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
nativeInitialize(JNIEnv * env,jobject instance)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
sendLoadNanoAppRequest(uint32_t hubId,jbyte * data,size_t dataBufferLength)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
nativeSendMessage(JNIEnv * env,jobject instance,jintArray header_,jbyteArray data_)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
register_android_server_location_ContextHubService(JNIEnv * env)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