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
43using android::hardware::contexthub::V1_0::AsyncEventType;
44using android::hardware::contexthub::V1_0::ContextHub;
45using android::hardware::contexthub::V1_0::ContextHubMsg;
46using android::hardware::contexthub::V1_0::HubAppInfo;
47using android::hardware::contexthub::V1_0::IContexthub;
48using android::hardware::contexthub::V1_0::IContexthubCallback;
49using android::hardware::contexthub::V1_0::NanoAppBinary;
50using android::hardware::contexthub::V1_0::Result;
51using android::hardware::contexthub::V1_0::TransactionResult;
52
53using android::hardware::Return;
54
55using 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.
63constexpr auto kMinTransactionCancelTime = std::chrono::seconds(29);
64
65namespace android {
66
67constexpr 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
71struct 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
83enum 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
93constexpr jint OS_APP_ID = -1;
94constexpr jint INVALID_APP_ID = -2;
95
96constexpr jint MIN_APP_ID = 1;
97constexpr jint MAX_APP_ID = 128;
98
99constexpr size_t MSG_HEADER_SIZE = 4;
100constexpr size_t HEADER_FIELD_MSG_TYPE = 0;
101constexpr size_t HEADER_FIELD_MSG_VERSION = 1;
102constexpr size_t HEADER_FIELD_HUB_HANDLE = 2;
103constexpr size_t HEADER_FIELD_APP_INSTANCE = 3;
104
105constexpr size_t HEADER_FIELD_LOAD_APP_ID_LO = MSG_HEADER_SIZE;
106constexpr size_t HEADER_FIELD_LOAD_APP_ID_HI = MSG_HEADER_SIZE + 1;
107constexpr size_t MSG_HEADER_SIZE_LOAD_APP = MSG_HEADER_SIZE + 2;
108
109jint getAppInstanceForAppId(uint64_t app_id);
110int onMessageReceipt(const uint32_t *header,
111                     size_t headerLen,
112                     const char *msg,
113                     size_t msgLen);
114void onHubReset(uint32_t hubId);
115void queryHubForApps(uint32_t hubId);
116void passOnOsResponse(uint32_t hubHandle,
117                      uint32_t msgType,
118                      TransactionResult result,
119                      const int8_t *additionalData,
120                      size_t additionalDataLen);
121
122bool closeLoadTxn(bool success, jint *appInstanceHandle);
123void closeUnloadTxn(bool success);
124int handleQueryAppsResponse(const std::vector<HubAppInfo> apps,
125                               uint32_t hubHandle);
126
127struct 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
160class TxnManager {
161public:
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
239struct 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
358struct 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
365struct ContextHubInfo {
366    int numHubs;
367    Vector<ContextHub> hubs;
368    sp<IContexthub> contextHub;
369};
370
371struct 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
381ContextHubServiceDb db;
382
383bool 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
392int 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
403jint 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
414uint64_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
421void 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
426void sendQueryForApps() {
427    for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
428        queryHubForApps(db.hubInfo.hubs[i].hubId);
429    }
430}
431
432int 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
443jint 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
455jint 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
484int 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
510int 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        free(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        free(txnInfo);
530        return -1;
531    }
532
533    return 0;
534}
535
536int 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        free(txnData);
547        ALOGW("Cannot start transaction to unload app");
548        return -1;
549    }
550
551    return 0;
552}
553
554void 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
560void 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
604void 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
612int 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
647int 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.
712void 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
780void 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
802bool 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
834int 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
923jobject 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
988jobjectArray 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
1013Result 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
1052jint 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//
1184const 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
1193int 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