Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f16abd98 authored by Greg Kaiser's avatar Greg Kaiser
Browse files

ContextHubService: Keep handles across hub reboot

Previously, when a Context Hub rebooted, this service would reset
all of the "handles" used to identify the nanoapps on the Context
Hub.  This caused many issues for Java application code trying to
track nanoapps, and these issues were exacerbated under nanohub,
which reboots every times it loads an app.

We change this so that we keep the same handle across reboot if
the same nanoapp is still running post-reboot.  We accomplish
this by never "invalidating" our caches, but rather performing
a query post-reboot and removing stale entries.

Due to b/30835598, we needed to change our post-load query to
be all apps.  Without that change, a race between the two
query responses we would have post nanohub app load (one for
post-load, one for post-reboot) would leave us without knowing
which response we're dealing with.  Now, since both requests
are the same, we don't care which response we deal with.

This is all (hopefully) just for the Nougat release.  In the
O release, we hope to remove these caches (b/30835981) and
the idea of the JNI code inventing handles (b/30810861).

Bug: 30950975
Change-Id: I85a84ba5c1a039d69c1adaaea56609e0a38cce42
parent c779cdc5
Loading
Loading
Loading
Loading
+72 −37
Original line number Diff line number Diff line
@@ -337,11 +337,15 @@ static int set_dest_app(hub_message_t *msg, jint id) {
    return 0;
}

static void query_hub_for_apps(uint64_t appId, uint32_t hubHandle) {
static void query_hub_for_apps(uint32_t hubHandle) {
    hub_message_t msg;
    query_apps_request_t queryMsg;

    queryMsg.app_name.id = appId;
    // TODO(b/30835598): When we're able to tell which request our
    //     response matches, then we should allow this to be more
    //     targetted, instead of always being every app in the
    //     system.
    queryMsg.app_name.id = ALL_APPS;

    msg.message_type = CONTEXT_HUB_QUERY_APPS;
    msg.message_len  = sizeof(queryMsg);
@@ -354,9 +358,9 @@ static void query_hub_for_apps(uint64_t appId, uint32_t hubHandle) {
    }
}

static void sendQueryForApps(uint64_t appId) {
static void sendQueryForApps() {
    for (int i = 0; i < db.hubInfo.numHubs; i++ ) {
        query_hub_for_apps(appId, i);
        query_hub_for_apps(i);
    }
}

@@ -386,9 +390,6 @@ static jint generate_id() {

static jint add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle,
        jint appInstanceHandle, JNIEnv *env) {

    ALOGI("Loading App");

    // Not checking if the apps are indeed distinct
    app_instance_info_s entry;
    assert(appInfo);
@@ -404,13 +405,14 @@ static jint add_app_instance(const hub_app_info *appInfo, uint32_t hubHandle,

    db.appInstances[appInstanceHandle] = entry;

    // Finally - let the service know of this app instance
    // Finally - let the service know of this app instance, to populate
    // the Java cache.
    env->CallIntMethod(db.jniInfo.jContextHubService,
                       db.jniInfo.contextHubServiceAddAppInstance,
                       hubHandle, entry.instanceId, entry.truncName,
                       entry.appInfo.version);

    ALOGW("%s App 0x%" PRIx64 " on hub Handle %" PRId32
    ALOGI("%s App 0x%" PRIx64 " on hub Handle %" PRId32
          " as appInstance %" PRId32, action, entry.truncName,
          entry.hubHandle, appInstanceHandle);

@@ -532,7 +534,7 @@ static void initContextHubService() {
            }
        }

        sendQueryForApps(ALL_APPS);
        sendQueryForApps();
    } else {
        ALOGW("No Context Hub Module present");
    }
@@ -577,15 +579,62 @@ int handle_query_apps_response(const uint8_t *msg, int msgLen,
    }

    int numApps = msgLen / sizeof(hub_app_info);
    hub_app_info info;
    const hub_app_info *unalignedInfoAddr = (const hub_app_info*)msg;

    for (int i = 0; i < numApps; i++, unalignedInfoAddr++) {
        memcpy(&info, unalignedInfoAddr, sizeof(info));
    // We use this information to sync our JNI and Java caches of nanoapp info.
    // We want to accomplish two things here:
    // 1) Remove entries from our caches which are stale, and pertained to
    //    apps no longer running on Context Hub.
    // 2) Populate our caches with the latest information of all these apps.

    // We make a couple of assumptions here:
    // A) The JNI and Java caches are in sync with each other (this isn't
    //    necessarily true; any failure of a single call into Java land to
    //    update its cache will leave that cache in a bad state.  For NYC,
    //    we're willing to tolerate this for now).
    // B) The total number of apps is relatively small, so horribly inefficent
    //    algorithms aren't too painful.
    // C) We're going to call this relatively infrequently, so its inefficency
    //    isn't a big impact.


    // (1).  Looking for stale cache entries.  Yes, this is O(N^2).  See
    // assumption (B).  Per assumption (A), it is sufficient to iterate
    // over just the JNI cache.
    auto end = db.appInstances.end();
    for (auto current = db.appInstances.begin(); current != end; ) {
        app_instance_info_s cache_entry = current->second;
        // We perform our iteration here because if we call
        // delete_app_instance() below, it will erase() this entry.
        current++;
        bool entryIsStale = true;
        for (int i = 0; i < numApps; i++) {
            // We use memcmp since this could be unaligned.
            if (memcmp(&unalignedInfoAddr[i].app_name.id,
                       &cache_entry.appInfo.app_name.id,
                       sizeof(cache_entry.appInfo.app_name.id)) == 0) {
                // We found a match; this entry is current.
                entryIsStale = false;
                break;
            }
        }
        if (entryIsStale) {
            delete_app_instance(cache_entry.instanceId, env);
        }
    }

    // (2).  Update our caches with the latest.
    for (int i = 0; i < numApps; i++) {
        hub_app_info query_info;
        memcpy(&query_info, &unalignedInfoAddr[i], sizeof(query_info));
        // We will only have one instance of the app
        // TODO : Change this logic once we support multiple instances of the same app
        jint appInstance = get_app_instance_for_app_id(info.app_name.id);
        add_app_instance(&info, hubHandle, appInstance, env);
        jint appInstance = get_app_instance_for_app_id(query_info.app_name.id);
        if (appInstance == -1) {
            // This is a previously unknown app, let's allocate an "id" for it.
            appInstance = generate_id();
        }
        add_app_instance(&query_info, hubHandle, appInstance, env);
    }

    return 0;
@@ -709,7 +758,12 @@ static bool closeLoadTxn(bool success, jint *appInstanceHandle) {
            ALOGW("Could not attach to JVM !");
            success = false;
        }
        sendQueryForApps(info->appInfo.app_name.id);
        // While we just called add_app_instance above, our info->appInfo was
        // incomplete (for example, the 'version' is hardcoded to -1).  So we
        // trigger an additional query to the CHRE, so we'll be able to get
        // all the app "info", and have our JNI and Java caches with the
        // full information.
        sendQueryForApps();
    } else {
        ALOGW("Could not load the app successfully ! Unexpected failure");
        *appInstanceHandle = INVALID_APP_ID;
@@ -739,24 +793,6 @@ static bool isValidOsStatus(const uint8_t *msg, size_t msgLen,
    return true;
}

static void invalidateNanoApps(uint32_t hubHandle) {
    JNIEnv *env;

    if ((db.jniInfo.vm)->AttachCurrentThread(&env, nullptr) != JNI_OK) {
        ALOGW("Could not attach to JVM !");
        env = nullptr;
    }

    auto end = db.appInstances.end();
    for (auto current = db.appInstances.begin(); current != end; ) {
        app_instance_info_s info = current->second;
        current++;
        if (info.hubHandle == hubHandle) {
             delete_app_instance(info.instanceId, env);
        }
    }
}

static int handle_os_message(uint32_t msgType, uint32_t hubHandle,
                             const uint8_t *msg, int msgLen) {
    int retVal = -1;
@@ -824,8 +860,7 @@ static int handle_os_message(uint32_t msgType, uint32_t hubHandle,
              ALOGW("Context Hub handle %d restarted", hubHandle);
              closeTxn();
              passOnOsResponse(hubHandle, msgType, &rsp, nullptr, 0);
              invalidateNanoApps(hubHandle);
              query_hub_for_apps(ALL_APPS, hubHandle);
              query_hub_for_apps(hubHandle);
              retVal = 0;
          }
          break;