/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "NanohubHAL"

#include "file.h"
#include <json/json.h>

#include <cassert>
#include <cerrno>
#include <cinttypes>
#include <string>

#include <endian.h>

#include <vector>

#include <log/log.h>

#include <endian.h>
#include <sys/stat.h>

#include <media/stagefright/foundation/ADebug.h>

#include <hardware/context_hub.h>
#include "nanohub_perdevice.h"
#include "system_comms.h"
#include "nanohubhal.h"

#define CHRE_APP_DIR        "/data/vendor/sensor/chre"
#define CHRE_APP_DIR_PERMS  (S_IRUSR | S_IWUSR | S_IXUSR)
#define CHRE_APP_FILE_PERMS (S_IRUSR | S_IWUSR)
#define CHRE_APP_SETTINGS   CHRE_APP_DIR "/apps.json"

namespace android {

namespace nanohub {

static void readAppName(MessageBuf &buf, hub_app_name_t &name)
{
    name.id = buf.readU64();
}

static void writeAppName(MessageBuf &buf, const hub_app_name_t &name)
{
    buf.writeU64(name.id);
}

static void readNanohubMemInfo(MessageBuf &buf, NanohubMemInfo &mi)
{
    uint8_t type, len;
    uint32_t ramFree = NANOHUB_MEM_SZ_UNKNOWN;
    uint32_t eeFree = NANOHUB_MEM_SZ_UNKNOWN;
    uint32_t sharedFree = NANOHUB_MEM_SZ_UNKNOWN;
    uint32_t osFree = NANOHUB_MEM_SZ_UNKNOWN;

    mi.flashSz = NANOHUB_MEM_SZ_UNKNOWN;
    mi.blSz = NANOHUB_MEM_SZ_UNKNOWN;
    mi.osSz = NANOHUB_MEM_SZ_UNKNOWN;
    mi.sharedSz = NANOHUB_MEM_SZ_UNKNOWN;
    mi.eeSz = NANOHUB_MEM_SZ_UNKNOWN;
    mi.ramSz = NANOHUB_MEM_SZ_UNKNOWN;

    mi.blUse = NANOHUB_MEM_SZ_UNKNOWN;
    mi.osUse = NANOHUB_MEM_SZ_UNKNOWN;
    mi.sharedUse = NANOHUB_MEM_SZ_UNKNOWN;
    mi.eeUse = NANOHUB_MEM_SZ_UNKNOWN;
    mi.ramUse = NANOHUB_MEM_SZ_UNKNOWN;

    while (buf.getRoom() >= 2) {
        type = buf.readU8();
        len = buf.readU8();
        if (buf.getRoom() >= len) {
            switch(type) {
            case NANOHUB_HAL_SYS_INFO_HEAP_FREE:
                if (len == sizeof(ramFree))
                    ramFree = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_SYS_INFO_RAM_SIZE:
                if (len == sizeof(mi.ramSz))
                    mi.ramSz = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_SYS_INFO_EEDATA_SIZE:
                if (len == sizeof(mi.ramSz))
                    mi.eeSz = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_SYS_INFO_EEDATA_FREE:
                if (len == sizeof(eeFree))
                    eeFree = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_SYS_INFO_CODE_SIZE:
                if (len == sizeof(mi.osSz))
                    mi.osSz = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_SYS_INFO_CODE_FREE:
                if (len == sizeof(osFree))
                    osFree = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_SYS_INFO_SHARED_SIZE:
                if (len == sizeof(mi.sharedSz))
                    mi.sharedSz = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_SYS_INFO_SHARED_FREE:
                if (len == sizeof(sharedFree))
                    sharedFree = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_SYS_INFO_END:
                if (len != 0 || buf.getRoom() != 0) {
                    ALOGE("%s: failed to read object", __func__);
                    return;
                }
                break;
            default:
                ALOGI("%s: unsupported type: %d", __func__, type);
                buf.readRaw(len);
                break;
            }
        } else {
            ALOGE("%s: failed to read object", __func__);
            return;
        }
    }

    if (buf.getRoom() != 0) {
        ALOGE("%s: failed to read object", __func__);
        return;
    }

    if (mi.ramSz != NANOHUB_MEM_SZ_UNKNOWN && ramFree != NANOHUB_MEM_SZ_UNKNOWN)
        mi.ramUse = mi.ramSz - ramFree;
    if (mi.eeSz != NANOHUB_MEM_SZ_UNKNOWN && eeFree != NANOHUB_MEM_SZ_UNKNOWN)
        mi.eeUse = mi.eeSz - eeFree;
    if (mi.osSz != NANOHUB_MEM_SZ_UNKNOWN && osFree != NANOHUB_MEM_SZ_UNKNOWN)
        mi.osUse = mi.osSz - osFree;
    if (mi.sharedSz != NANOHUB_MEM_SZ_UNKNOWN && sharedFree != NANOHUB_MEM_SZ_UNKNOWN)
        mi.sharedUse = mi.sharedSz - sharedFree;
}

NanohubRsp::NanohubRsp(MessageBuf &buf, uint32_t transactionId, bool chre)
{
    // all responses start with command and have a 4-byte status (result code)
    buf.reset();
    if (buf.getSize() < 5) {
        mStatus = -EINVAL;
    } else {
        mCmd = buf.readU8();
        mStatus = buf.readU32();
        if (chre)
            mTransactionId = transactionId;
        else
            mTransactionId = 0;
    }
}

int SystemComm::sendToSystem(const void *data, size_t len, uint32_t transactionId)
{
    if (NanoHub::messageTracingEnabled()) {
        dumpBuffer("HAL -> SYS", getSystem()->mHostIfAppName, transactionId, 0, data, len);
    }
    return NanoHub::sendToDevice(&getSystem()->mHostIfAppName, data, len, transactionId);
}

inline hub_app_name_t deviceAppNameToHost(const hub_app_name_t src)
{
    hub_app_name_t res = { .id = le64toh(src.id) };
    return res;
}

inline hub_app_name_t hostAppNameToDevice(const hub_app_name_t src)
{
    hub_app_name_t res = { .id = htole64(src.id) };
    return res;
}

const uint8_t app_info_tags[] =
{
    NANOHUB_HAL_APP_INFO_APPID,
    NANOHUB_HAL_APP_INFO_CRC,
    NANOHUB_HAL_APP_INFO_TID,
    NANOHUB_HAL_APP_INFO_VERSION,
    NANOHUB_HAL_APP_INFO_ADDR,
    NANOHUB_HAL_APP_INFO_SIZE,
    NANOHUB_HAL_APP_INFO_HEAP,
    NANOHUB_HAL_APP_INFO_DATA,
    NANOHUB_HAL_APP_INFO_BSS,
    NANOHUB_HAL_APP_INFO_CHRE_MAJOR,
    NANOHUB_HAL_APP_INFO_CHRE_MINOR,
    NANOHUB_HAL_APP_INFO_END,
};

const uint8_t sys_info_tags[] =
{
    NANOHUB_HAL_SYS_INFO_HEAP_FREE,
    NANOHUB_HAL_SYS_INFO_RAM_SIZE,
    NANOHUB_HAL_SYS_INFO_EEDATA_SIZE,
    NANOHUB_HAL_SYS_INFO_EEDATA_FREE,
    NANOHUB_HAL_SYS_INFO_CODE_SIZE,
    NANOHUB_HAL_SYS_INFO_CODE_FREE,
    NANOHUB_HAL_SYS_INFO_SHARED_SIZE,
    NANOHUB_HAL_SYS_INFO_SHARED_FREE,
    NANOHUB_HAL_SYS_INFO_END,
};

int SystemComm::MemInfoSession::setup(const hub_message_t *, uint32_t transactionId, AppManager &)
{
    std::lock_guard<std::mutex> _l(mLock);
    char data[MAX_RX_PACKET];
    MessageBuf buf(data, sizeof(data));
    buf.writeU8(NANOHUB_HAL_SYS_INFO);
    buf.writeRaw(sys_info_tags, sizeof(sys_info_tags));

    setState(SESSION_USER);
    return sendToSystem(buf.getData(), buf.getPos(), transactionId);
}

int SystemComm::MemInfoSession::handleRx(MessageBuf &buf, uint32_t transactionId, AppManager &, bool chre)
{
    std::lock_guard<std::mutex> _l(mLock);
    NanohubRsp rsp(buf, transactionId, chre);

    if (rsp.mCmd != NANOHUB_HAL_SYS_INFO)
        return 1;

    size_t len = buf.getRoom();

    if (getState() != SESSION_USER) {
        ALOGE("%s: Invalid state; have %d, need %d", __func__, getState(), SESSION_USER);
        return -EINVAL;
    }

    std::vector<mem_range_t> ranges;
    ranges.reserve(4);
    if (len) {
        NanohubMemInfo mi;
        readNanohubMemInfo(buf, mi);

        //if each is valid, copy to output area
        if (mi.sharedSz != NANOHUB_MEM_SZ_UNKNOWN &&
            mi.sharedUse != NANOHUB_MEM_SZ_UNKNOWN)
            ranges.push_back({
                .type = HUB_MEM_TYPE_MAIN,
                .total_bytes = mi.sharedSz,
                .free_bytes = mi.sharedSz - mi.sharedUse,
            });

        if (mi.osSz != NANOHUB_MEM_SZ_UNKNOWN &&
            mi.osUse != NANOHUB_MEM_SZ_UNKNOWN)
            ranges.push_back({
                .type = HUB_MEM_TYPE_OS,
                .total_bytes = mi.osSz,
                .free_bytes = mi.osSz - mi.osUse,
            });

        if (mi.eeSz != NANOHUB_MEM_SZ_UNKNOWN &&
            mi.eeUse != NANOHUB_MEM_SZ_UNKNOWN)
            ranges.push_back({
                .type = HUB_MEM_TYPE_EEDATA,
                .total_bytes = mi.eeSz,
                .free_bytes = mi.eeSz - mi.eeUse,
            });

        if (mi.ramSz != NANOHUB_MEM_SZ_UNKNOWN &&
            mi.ramUse != NANOHUB_MEM_SZ_UNKNOWN)
            ranges.push_back({
                .type = HUB_MEM_TYPE_RAM,
                .total_bytes = mi.ramSz,
                .free_bytes = mi.ramSz - mi.ramUse,
            });
    }

    //send it out
    sendToApp(CONTEXT_HUB_QUERY_MEMORY, transactionId,
              static_cast<const void *>(ranges.data()),
              ranges.size() * sizeof(ranges[0]));

    complete();
    return 0;
}

int SystemComm::AppMgmtSession::setup(const hub_message_t *appMsg, uint32_t transactionId, AppManager &appManager)
{
    std::lock_guard<std::mutex> _l(mLock);

    char data[MAX_RX_PACKET];
    MessageBuf buf(data, sizeof(data));
    const uint8_t *msgData = static_cast<const uint8_t*>(appMsg->message);

    mCmd = appMsg->message_type;
    mLen = appMsg->message_len;
    mPos = 0;
    mNextPos = 0;
    mErrCnt = 0;

    switch (mCmd) {
    case  CONTEXT_HUB_APPS_ENABLE:
        return setupMgmt(appMsg, transactionId, NANOHUB_HAL_APP_MGMT_START, appManager);
    case  CONTEXT_HUB_APPS_DISABLE:
        return setupMgmt(appMsg, transactionId, NANOHUB_HAL_APP_MGMT_STOP, appManager);
    case  CONTEXT_HUB_UNLOAD_APP:
        return setupMgmt(appMsg, transactionId, NANOHUB_HAL_APP_MGMT_UNLOAD, appManager);
    case  CONTEXT_HUB_LOAD_APP:
    {
        const load_app_request_t *appReq = static_cast<const load_app_request_t*>(appMsg->message);
        if (appReq == nullptr || mLen <= sizeof(*appReq)) {
            ALOGE("%s: Invalid app header: too short\n", __func__);
            return -EINVAL;
        }
        mAppName = appReq->app_binary.app_id;
        if (!appManager.isAppLoaded(mAppName)) {
            appManager.addNewApp(mAppName, appReq->app_binary.app_version);
            appManager.writeApp(mAppName, msgData, mLen);
            mData.clear();
            mData = std::vector<uint8_t>(msgData, msgData + mLen);
            setState(TRANSFER);

            buf.writeU8(NANOHUB_HAL_START_UPLOAD);
            buf.writeU8(0);
            buf.writeU32(mLen);

            return sendToSystem(buf.getData(), buf.getPos(), transactionId);
        } else {
            if (appManager.cmpApp(mAppName, msgData, mLen)) {
                mFlashAddr = appManager.getFlashAddr(mAppName);
                if (appManager.isAppRunning(mAppName)) {
                    setState(STOP_RUN);

                    buf.writeU8(NANOHUB_HAL_APP_MGMT);
                    writeAppName(buf, mAppName);
                    buf.writeU8(NANOHUB_HAL_APP_MGMT_STOP);

                    return sendToSystem(buf.getData(), buf.getPos(), transactionId);
                } else {
                    setState(RUN);

                    buf.writeU8(NANOHUB_HAL_APP_MGMT);
                    writeAppName(buf, mAppName);
                    buf.writeU8(NANOHUB_HAL_APP_MGMT_START);

                    return sendToSystem(buf.getData(), buf.getPos(), transactionId);
                }
            } else {
                appManager.setCachedVersion(mAppName, appReq->app_binary.app_version);
                appManager.writeApp(mAppName, msgData, mLen);
                mData.clear();
                mData = std::vector<uint8_t>(msgData, msgData + mLen);
                if (appManager.isAppRunning(mAppName)) {
                    setState(STOP_TRANSFER);

                    buf.writeU8(NANOHUB_HAL_APP_MGMT);
                    writeAppName(buf, mAppName);
                    buf.writeU8(NANOHUB_HAL_APP_MGMT_STOP);

                    return sendToSystem(buf.getData(), buf.getPos(), transactionId);
                } else {
                    setState(TRANSFER);

                    buf.writeU8(NANOHUB_HAL_START_UPLOAD);
                    buf.writeU8(0);
                    buf.writeU32(mLen);

                    return sendToSystem(buf.getData(), buf.getPos(), transactionId);
                }
            }
        }
    }
    case  CONTEXT_HUB_OS_REBOOT:
        setState(REBOOT);

        buf.writeU8(NANOHUB_HAL_SYS_MGMT);
        buf.writeU8(NANOHUB_HAL_SYS_MGMT_REBOOT);

        return sendToSystem(buf.getData(), buf.getPos(), transactionId);

    case  CONTEXT_HUB_START_APPS:
        if (mLen == sizeof(mStatus))
            memcpy(&mStatus, msgData, mLen);
        appManager.eraseApps();
        setState(QUERY_START);

        buf.writeU8(NANOHUB_HAL_APP_INFO);
        buf.writeU32(0);
        buf.writeRaw(app_info_tags, sizeof(app_info_tags));

        return sendToSystem(buf.getData(), buf.getPos(), transactionId);
    }

    return -EINVAL;
}

int SystemComm::AppMgmtSession::setupMgmt(const hub_message_t *appMsg, uint32_t transactionId, uint32_t cmd, AppManager &appManager)
{
    int32_t result = 0;
    const hub_app_name_t &appName = *static_cast<const hub_app_name_t*>(appMsg->message);
    if (appMsg->message_len != sizeof(appName)) {
        return -EINVAL;
    }
    mAppName = appName;

    switch (cmd) {
    case NANOHUB_HAL_APP_MGMT_START:
        if (appManager.isAppRunning(mAppName)) {
            appManager.setCachedStart(mAppName, true);
            sendToApp(mCmd, transactionId, &result, sizeof(result));
            complete();
            return 0;
        }
        break;
    case NANOHUB_HAL_APP_MGMT_STOP:
    case NANOHUB_HAL_APP_MGMT_UNLOAD:
        appManager.setCachedStart(mAppName, false);
        if (!appManager.isAppRunning(mAppName)) {
            sendToApp(mCmd, transactionId, &result, sizeof(result));
            complete();
            return 0;
        }
        break;
    }
    char data[MAX_RX_PACKET];
    MessageBuf buf(data, sizeof(data));
    buf.writeU8(NANOHUB_HAL_APP_MGMT);
    writeAppName(buf, appName);
    buf.writeU8(cmd);
    setState(MGMT);

    return sendToSystem(buf.getData(), buf.getPos(), transactionId);
}

int SystemComm::AppMgmtSession::handleRx(MessageBuf &buf, uint32_t transactionId, AppManager &appManager, bool chre)
{
    int ret = 0;
    std::lock_guard<std::mutex> _l(mLock);
    NanohubRsp rsp(buf, transactionId, chre);

    switch (getState()) {
    case TRANSFER:
        ret = handleTransfer(rsp, buf, appManager);
        break;
    case STOP_TRANSFER:
        ret = handleStopTransfer(rsp, buf, appManager);
         break;
    case QUERY_START:
        ret = handleQueryStart(rsp, buf, appManager);
        break;
    case START:
        ret = handleStart(rsp, buf, appManager);
        break;
    case FINISH:
        ret = handleFinish(rsp, buf, appManager);
        break;
    case RUN:
        ret = handleRun(rsp, buf, appManager);
        break;
    case STOP_RUN:
        ret = handleStopRun(rsp, buf, appManager);
        break;
    case REBOOT:
        ret = handleReboot(rsp, buf, appManager);
        break;
    case ERASE_TRANSFER:
        ret = handleEraseTransfer(rsp, buf, appManager);
        break;
    case MGMT:
        ret = handleMgmt(rsp, buf, appManager);
        break;
    case INFO:
        ret = handleInfo(rsp, buf, appManager);
        break;
    }

    return ret;
}

int SystemComm::AppMgmtSession::handleTransfer(NanohubRsp &rsp, MessageBuf &, AppManager &appManager)
{
    if (rsp.mCmd != NANOHUB_HAL_CONT_UPLOAD && rsp.mCmd != NANOHUB_HAL_START_UPLOAD)
        return 1;

    char data[MAX_RX_PACKET];
    MessageBuf buf(data, sizeof(data));
    int32_t result = 0;

    static_assert(NANOHUB_UPLOAD_CHUNK_SZ_MAX <= (MAX_RX_PACKET-5),
                  "Invalid chunk size");

    if (rsp.mStatus == NANOHUB_HAL_UPLOAD_ACCEPTED) {
        mPos = mNextPos;
        mErrCnt = 0;
    } else if (rsp.mStatus == NANOHUB_HAL_UPLOAD_RESEND) {
        mErrCnt ++;
    } else if (rsp.mStatus == NANOHUB_HAL_UPLOAD_RESTART) {
        mPos = 0;
        mErrCnt ++;
    } else if (rsp.mStatus == NANOHUB_HAL_UPLOAD_CANCEL ||
               rsp.mStatus == NANOHUB_HAL_UPLOAD_CANCEL_NO_RETRY) {
        mPos = mLen;
        result = NANOHUB_APP_NOT_LOADED;
    } else if (rsp.mStatus == NANOHUB_HAL_UPLOAD_NO_SPACE) {
        mPos = 0;
        mErrCnt = 0;
        setState(ERASE_TRANSFER);

        buf.writeU8(NANOHUB_HAL_SYS_MGMT);
        buf.writeU8(NANOHUB_HAL_SYS_MGMT_ERASE);

        return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
    } else if (mErrCnt > 5) {
        mPos = mLen;
        result = NANOHUB_APP_NOT_LOADED;
    } else {
        mErrCnt ++;
    }

    if (result != 0) {
        appManager.clearCachedApp(mAppName);

        sendToApp(mCmd, rsp.mTransactionId, &result, sizeof(result));
        complete();
        return 0;
    } else if (mPos < mLen) {
        uint32_t chunkSize = mLen - mPos;

        if (chunkSize > NANOHUB_UPLOAD_CHUNK_SZ_MAX) {
            chunkSize = NANOHUB_UPLOAD_CHUNK_SZ_MAX;
        }

        buf.writeU8(NANOHUB_HAL_CONT_UPLOAD);
        buf.writeU32(mPos);
        buf.writeRaw(&mData[mPos], chunkSize);
        mNextPos = mPos + chunkSize;
    } else {
        buf.writeU8(NANOHUB_HAL_FINISH_UPLOAD);
        setState(FINISH);
    }

    return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
}

int SystemComm::AppMgmtSession::handleStopTransfer(NanohubRsp &rsp, MessageBuf &buf, AppManager &)
{
    if (rsp.mCmd != NANOHUB_HAL_APP_MGMT)
        return 1;

    uint8_t cmd = buf.readU8();

    if (cmd != NANOHUB_HAL_APP_MGMT_STOP)
        return 1;

    MgmtStatus sts = { .value = buf.readU32() };

    ALOGI("Nanohub NEW APP STOP: %08" PRIX32 "\n", sts.value);
    if (rsp.mStatus == 0) {
        char data[MAX_RX_PACKET];
        MessageBuf buf(data, sizeof(data));
        setState(TRANSFER);

        buf.writeU8(NANOHUB_HAL_START_UPLOAD);
        buf.writeU8(0);
        buf.writeU32(mLen);
        return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
    } else {
        int32_t result = NANOHUB_APP_NOT_LOADED;

        sendToApp(mCmd, rsp.mTransactionId, &result, sizeof(result));
        complete();
        return 0;
    }
}

int SystemComm::AppMgmtSession::handleQueryStart(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager)
{

    if (rsp.mCmd != NANOHUB_HAL_APP_INFO)
        return 1;

    size_t len = buf.getRoom();
    if (len) {
        uint32_t nextAddr = appManager.readNanohubAppInfo(buf);

        if (nextAddr) {
            char data[MAX_RX_PACKET];
            MessageBuf buf(data, sizeof(data));

            buf.writeU8(NANOHUB_HAL_APP_INFO);
            buf.writeU32(nextAddr);
            buf.writeRaw(app_info_tags, sizeof(app_info_tags));

            return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
        }
    }

    appManager.getAppsToStart(mAppList);
    if (mAppList.empty()) {
        sendToApp(CONTEXT_HUB_OS_REBOOT, 0, &mStatus, sizeof(mStatus));
        complete();
        return 0;
    } else {
        char data[MAX_RX_PACKET];
        MessageBuf buf(data, sizeof(data));
        mAppName = mAppList.back();
        mAppList.pop_back();
        setState(START);

        buf.writeU8(NANOHUB_HAL_APP_MGMT);
        writeAppName(buf, mAppName);
        buf.writeU8(NANOHUB_HAL_APP_MGMT_START);
        return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
    }
}

int SystemComm::AppMgmtSession::handleStart(NanohubRsp &rsp, MessageBuf &buf, AppManager &)
{
    if (rsp.mCmd != NANOHUB_HAL_APP_MGMT)
        return 1;

    uint8_t cmd = buf.readU8();

    if (cmd != NANOHUB_HAL_APP_MGMT_START)
        return 1;

    MgmtStatus sts = { .value = buf.readU32() };

    ALOGI("Nanohub EXISTING APP START: %08" PRIX32 "\n", sts.value);
    if (mAppList.empty()) {
        sendToApp(CONTEXT_HUB_OS_REBOOT, 0, &mStatus, sizeof(mStatus));
        complete();
        return 0;
    } else {
        char data[MAX_RX_PACKET];
        MessageBuf buf(data, sizeof(data));
        mAppName = mAppList.back();
        mAppList.pop_back();

        buf.writeU8(NANOHUB_HAL_APP_MGMT);
        writeAppName(buf, mAppName);
        buf.writeU8(NANOHUB_HAL_APP_MGMT_START);
        return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
    }
}

int SystemComm::AppMgmtSession::handleFinish(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager)
{
    if (rsp.mCmd != NANOHUB_HAL_FINISH_UPLOAD)
        return 1;

    mFlashAddr = buf.readU32();
    uint32_t crc = buf.readU32();
    mData.clear();

    if (rsp.mStatus == 0) {
        appManager.setCachedCrc(mAppName, crc);
        char data[MAX_RX_PACKET];
        MessageBuf buf(data, sizeof(data));
        setState(RUN);

        buf.writeU8(NANOHUB_HAL_APP_MGMT);
        writeAppName(buf, mAppName);
        buf.writeU8(NANOHUB_HAL_APP_MGMT_START);
        return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
    } else {
        int32_t result = NANOHUB_APP_NOT_LOADED;
        appManager.clearCachedApp(mAppName);

        sendToApp(mCmd, rsp.mTransactionId, &result, sizeof(result));
        complete();
        return 0;
    }
}

int SystemComm::AppMgmtSession::handleRun(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager)
{
    if (rsp.mCmd != NANOHUB_HAL_APP_MGMT)
        return 1;

    uint8_t cmd = buf.readU8();

    if (cmd != NANOHUB_HAL_APP_MGMT_START)
        return 1;

    MgmtStatus sts = { .value = buf.readU32() };

    ALOGI("Nanohub NEW APP START: %08" PRIX32 "\n", sts.value);
    if (rsp.mStatus == 0) {
        appManager.setCachedStart(mAppName, true);
        char data[MAX_RX_PACKET];
        MessageBuf buf(data, sizeof(data));
        setState(INFO);

        buf.writeU8(NANOHUB_HAL_APP_INFO);
        buf.writeU32(mFlashAddr);
        buf.writeRaw(app_info_tags, sizeof(app_info_tags));

        return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
    } else {
        appManager.setCachedStart(mAppName, false);
        int32_t result = NANOHUB_APP_NOT_LOADED;
        sendToApp(mCmd, rsp.mTransactionId, &result, sizeof(result));
        complete();
        return 0;
    }
}

int SystemComm::AppMgmtSession::handleInfo(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager)
{
    if (rsp.mCmd != NANOHUB_HAL_APP_INFO)
        return 1;
    int32_t result = 0;
    size_t len = buf.getRoom();
    if (len) {
        appManager.readNanohubAppInfo(buf);
        appManager.saveApps();
    }
    sendToApp(mCmd, rsp.mTransactionId, &result, sizeof(result));
    complete();
    return 0;
}

int SystemComm::AppMgmtSession::handleStopRun(NanohubRsp &rsp, MessageBuf &buf, AppManager &)
{
    if (rsp.mCmd != NANOHUB_HAL_APP_MGMT)
        return 1;

    uint8_t cmd = buf.readU8();

    if (cmd != NANOHUB_HAL_APP_MGMT_STOP)
        return 1;

    MgmtStatus sts = { .value = buf.readU32() };

    ALOGI("Nanohub NEW APP STOP: %08" PRIX32 "\n", sts.value);
    if (rsp.mStatus == 0) {
        char data[MAX_RX_PACKET];
        MessageBuf buf(data, sizeof(data));
        setState(RUN);

        buf.writeU8(NANOHUB_HAL_APP_MGMT);
        writeAppName(buf, mAppName);
        buf.writeU8(NANOHUB_HAL_APP_MGMT_START);
        return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
    } else {
        int32_t result = NANOHUB_APP_NOT_LOADED;

        sendToApp(mCmd, rsp.mTransactionId, &result, sizeof(result));
        complete();
        return 0;
    }
}

/* reboot notification, when triggered by App request */
int SystemComm::AppMgmtSession::handleReboot(NanohubRsp &rsp, MessageBuf &buf, AppManager &)
{
    if (rsp.mCmd != NANOHUB_HAL_SYS_MGMT)
        return 1;

    uint8_t cmd = buf.readU8();

    if (cmd == NANOHUB_HAL_SYS_MGMT_REBOOT)
        ALOGI("Nanohub reboot status [USER REQ]: %08" PRIX32 "\n", rsp.mStatus);

    // reboot notification is sent by SessionManager
    complete();

    return 0;
}

int SystemComm::AppMgmtSession::handleEraseTransfer(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager)
{
    if (rsp.mCmd != NANOHUB_HAL_SYS_MGMT)
        return 1;

    uint8_t cmd = buf.readU8();

    if (cmd == NANOHUB_HAL_SYS_MGMT_ERASE && rsp.mStatus == 0) {
        char data[MAX_RX_PACKET];
        MessageBuf buf(data, sizeof(data));
        appManager.eraseApps();
        setState(TRANSFER);

        buf.writeU8(NANOHUB_HAL_START_UPLOAD);
        buf.writeU8(0);
        buf.writeU32(mLen);
        return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
    } else {
        int32_t result = NANOHUB_APP_NOT_LOADED;

        sendToApp(mCmd, rsp.mTransactionId, &result, sizeof(result));
        complete();
        return 0;
    }
}

int SystemComm::AppMgmtSession::handleMgmt(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager)
{
    if (rsp.mCmd != NANOHUB_HAL_APP_MGMT)
        return 1;

    uint8_t cmd = buf.readU8();
    MgmtStatus sts = { .value = buf.readU32() };

    bool valid = false;

    int32_t result = rsp.mStatus;

    if (result != 0)
        result = -1;

    switch (cmd) {
    case NANOHUB_HAL_APP_MGMT_STOP:
        valid = mCmd == CONTEXT_HUB_APPS_DISABLE;
        if (valid && rsp.mStatus == 0)
            appManager.clearRunning(mAppName);
        break;
    case NANOHUB_HAL_APP_MGMT_START:
        valid = mCmd == CONTEXT_HUB_APPS_ENABLE;
        if (valid && rsp.mStatus == 0) {
            appManager.setCachedStart(mAppName, true);
            char data[MAX_RX_PACKET];
            MessageBuf buf(data, sizeof(data));
            setState(INFO);

            buf.writeU8(NANOHUB_HAL_APP_INFO);
            buf.writeU32(appManager.getFlashAddr(mAppName));
            buf.writeRaw(app_info_tags, sizeof(app_info_tags));

            return sendToSystem(buf.getData(), buf.getPos(), rsp.mTransactionId);
        }
        break;
    case NANOHUB_HAL_APP_MGMT_UNLOAD:
        valid = mCmd == CONTEXT_HUB_UNLOAD_APP;
        if (valid && rsp.mStatus == 0)
            appManager.clearRunning(mAppName);
        break;
    default:
        return 1;
    }

    ALOGI("Nanohub MGMT response: CMD=%02X; STATUS=%08" PRIX32, rsp.mCmd, sts.value);
    if (!valid) {
        ALOGE("Invalid response for this state: APP CMD=%02X", mCmd);
        return -EINVAL;
    }

    sendToApp(mCmd, rsp.mTransactionId, &result, sizeof(result));
    complete();
    return 0;
}

int SystemComm::KeyInfoSession::setup(const hub_message_t *, uint32_t transactionId, AppManager &) {
    std::lock_guard<std::mutex> _l(mLock);
    mKeyNum = 0;
    mKeyOffset = 0;
    mRsaKeyData.clear();
    setState(SESSION_USER);
    mStatus = -EBUSY;
    return requestRsaKeys(transactionId);
}

int SystemComm::KeyInfoSession::handleRx(MessageBuf &buf, uint32_t transactionId, AppManager &, bool chre)
{
    std::lock_guard<std::mutex> _l(mLock);
    NanohubRsp rsp(buf, transactionId, chre);

    if (getState() != SESSION_USER) {
        // invalid state
        mStatus = -EFAULT;
        return mStatus;
    }

    uint32_t keyLen = buf.readU32();
    uint32_t dataLen = buf.getRoom();

    if (dataLen) {
        mRsaKeyData.insert(mRsaKeyData.end(),
                           buf.getData() + buf.getPos(),
                           buf.getData() + buf.getSize());
        if (mKeyOffset + dataLen >= keyLen) {
            mKeyNum++;
            mKeyOffset = 0;
        } else {
            mKeyOffset += dataLen;
        }
        return requestRsaKeys(transactionId);
    } else {
        mStatus = 0;
        complete();
        return 0;
    }
}

int SystemComm::KeyInfoSession::requestRsaKeys(uint32_t transactionId)
{
    char data[MAX_RX_PACKET];
    MessageBuf buf(data, sizeof(data));

    buf.writeU8(NANOHUB_HAL_KEY_INFO);
    buf.writeU32(mKeyNum);
    buf.writeU32(mKeyOffset);

    return sendToSystem(buf.getData(), buf.getPos(), transactionId);
}

void SystemComm::AppManager::dumpAppInfo(std::string &result)
{
    char buffer[256];

    for (auto &it : apps_) {
        uint64_t id = it.first;
        const auto &app = it.second;

        snprintf(buffer, sizeof(buffer), "App: 0x%016" PRIx64 "\n", id);
        result.append(buffer);
        if (app->loaded) {
            snprintf(buffer, sizeof(buffer),
                "  Version: 0x%08" PRIx32 "\n"
                "  flashAddr: 0x%08" PRIx32 "\n"
                "  Running: %s\n",
                app->version,
                app->flashAddr,
                app->running ? "true" : "false");
            result.append(buffer);

            if (app->flashUse != NANOHUB_MEM_SZ_UNKNOWN) {
                snprintf(buffer, sizeof(buffer),
                    "  flashUse: %d\n"
                    "  CRC: 0x%08" PRIx32 "\n",
                    app->flashUse,
                    app->crc);
                result.append(buffer);
            }

            if (app->running) {
                snprintf(buffer, sizeof(buffer),
                    "  TID: %04x\n"
                    "  ramUse: %d\n",
                    app->tid,
                    app->ramUse);
                result.append(buffer);
            }

            if (app->chre) {
                snprintf(buffer, sizeof(buffer), "  CHRE: %d.%d\n",
                    app->chre_major, app->chre_minor);
                result.append(buffer);
            }
        }

        if (app->cached_napp) {
            snprintf(buffer, sizeof(buffer),
                "  Cached Version: 0x%08" PRIx32 "\n"
                "  Cached Start: %s\n"
                "  Cached CRC: 0x%08" PRIx32 "\n",
                app->cached_version,
                app->cached_start ? "true" : "false",
                app->cached_crc);
            result.append(buffer);
        }
    }
}

bool SystemComm::AppManager::saveApps()
{
    mkdir(CHRE_APP_DIR, CHRE_APP_DIR_PERMS);
    File saved_apps_file(CHRE_APP_SETTINGS, "w");
    std::shared_ptr<Json::Value> appsObject(new Json::Value);
    status_t err;

    if ((err = saved_apps_file.initCheck()) != OK) {
        ALOGW("saved_apps file open (w) failed %d (%s)",
              err,
              strerror(-err));
        return false;
    }

    for (auto &it : apps_) {
        uint64_t id = it.first;
        const auto &app = it.second;

        if (app->cached_napp) {
            char hexId[17];
            snprintf(hexId, sizeof(hexId), "%016" PRIX64, id);
            Json::Value array(Json::arrayValue);
            array[0] = app->cached_version;
            array[1] = app->cached_start;
            array[2] = app->cached_crc;
            (*appsObject)[hexId] = array;
        }
    }

    // Write the JSON string to disk.
    Json::StyledWriter writer;
    std::string serializedSettings(writer.write(*appsObject));
    size_t size = serializedSettings.size();
    if ((err = saved_apps_file.write(serializedSettings.c_str(), size)) != (ssize_t)size) {
        ALOGW("saved_apps file write failed %d (%s)",
              err,
              strerror(-err));
        return false;
    }

    return true;
}

bool SystemComm::AppManager::restoreApps()
{
    File saved_apps_file(CHRE_APP_SETTINGS, "r");
    std::shared_ptr<Json::Value> appsObject;
    status_t err;

    if ((err = saved_apps_file.initCheck()) != OK) {
        ALOGW("saved_apps file open (r) failed %d (%s)",
              err,
              strerror(-err));
        return false;
    }

    off64_t size = saved_apps_file.seekTo(0, SEEK_END);
    saved_apps_file.seekTo(0, SEEK_SET);

    if (size > 0) {
        char *buf = (char *)malloc(size);
        CHECK_EQ(saved_apps_file.read(buf, size), (ssize_t)size);

        std::string str(buf);
        std::shared_ptr<Json::Value> in(new Json::Value);
        Json::Reader reader;
        bool valid = reader.parse(str, *in);
        free(buf);

        if (valid && in->isObject()) {
            appsObject = in;
        }
    }

    if (appsObject == nullptr) {
        appsObject = std::shared_ptr<Json::Value>(new Json::Value(Json::objectValue));
    }

    Json::Value::Members apps = appsObject->getMemberNames();
    for (auto &it : apps) {
        Json::Value &val = (*appsObject)[it];
        if (val.isArray()) {
            uint32_t version = val[0].asUInt();
            uint32_t start = val[1].asUInt();
            uint32_t crc = val[2].asUInt();

            uint64_t id = strtoull(it.c_str(), nullptr, 16);
            apps_[id] = std::unique_ptr<AppData>(new AppData);
            apps_[id]->loaded = false;
            apps_[id]->running = false;
            apps_[id]->chre = false;
            apps_[id]->cached_napp = true;
            apps_[id]->cached_version = version;
            apps_[id]->cached_start = start;
            apps_[id]->cached_crc = crc;
        }
    }

    return true;
}

bool SystemComm::AppManager::eraseApps()
{
    for (auto it=apps_.begin(); it != apps_.end();) {
        if (!it->second->cached_napp)
            it = apps_.erase(it);
        else {
            it->second->loaded = false;
            it->second->running = false;
            it->second->chre = false;
            ++it;
        }
    }

    return true;
}

bool SystemComm::AppManager::writeApp(hub_app_name_t &appName, const uint8_t *data, int32_t len)
{
    mkdir(CHRE_APP_DIR, CHRE_APP_DIR_PERMS);
    char file[strlen(CHRE_APP_DIR) + strlen("/") + 16 + strlen(".napp") + 1];

    snprintf(file, sizeof(file), "%s/%016" PRIX64 ".napp", CHRE_APP_DIR, appName.id);

    int fd = open(file, O_CREAT | O_WRONLY | O_TRUNC, CHRE_APP_FILE_PERMS);
    if (fd == -1)
        return false;

    if (write(fd, data, len) == len) {
        close(fd);
        return true;
    } else {
        close(fd);
        return false;
    }
}

int32_t SystemComm::AppManager::readApp(hub_app_name_t &appName, void **data)
{
    char file[strlen(CHRE_APP_DIR) + strlen("/") + 16 + strlen(".napp") + 1];

    snprintf(file, sizeof(file), "%s/%016" PRIX64 ".napp", CHRE_APP_DIR, appName.id);

    int32_t ret = -1;
    *data = nullptr;
    int fd = open(file, O_RDONLY);

    if (fd >= 0) {
        struct stat sb;
        if (fstat(fd, &sb) == 0) {
            *data = malloc(sb.st_size);
            if (*data != nullptr && read(fd, *data, sb.st_size) == sb.st_size)
                ret = sb.st_size;
            else {
                free(*data);
                *data = nullptr;
            }
        }
        close(fd);
    }
    return ret;
}

bool SystemComm::AppManager::cmpApp(hub_app_name_t &appName, const uint8_t *data, uint32_t len)
{
    char file[strlen(CHRE_APP_DIR) + strlen("/") + 16 + strlen(".napp") + 1];

    snprintf(file, sizeof(file), "%s/%016" PRIX64 ".napp", CHRE_APP_DIR, appName.id);

    if (!isAppLoaded(appName))
        return false;

    if ((!apps_[appName.id]->cached_napp) ||
        (apps_[appName.id]->crc != apps_[appName.id]->cached_crc))
        return false;

    bool success = false;
    int fd = open(file, O_RDONLY);

    if (fd >= 0) {
        struct stat sb;
        if (fstat(fd, &sb) == 0 && sb.st_size == len) {
            void *buf = malloc(len);
            if (buf != nullptr && read(fd, buf, sb.st_size) == sb.st_size && memcmp(data, buf, len) == 0)
                success = true;
            free(buf);
        }
        close(fd);
    }
    return success;
}

uint32_t SystemComm::AppManager::readNanohubAppInfo(MessageBuf &buf)
{
    hub_app_name_t name;

    uint8_t tag, len;
    uint32_t ramUse = 0;
    bool ramUseValid = true;

    // first tag must be the appid
    if (buf.getRoom() < 2 + sizeof(name.id)) {
        ALOGE("%s: failed to read object", __func__);
        return 0;
    }

    tag = buf.readU8();
    len = buf.readU8();
    if (tag != NANOHUB_HAL_APP_INFO_APPID || len != sizeof(name.id)) {
        ALOGE("%s: invalid first tag: %d", __func__, tag);
        return 0;
    }

    readAppName(buf, name);
    if (!isAppPresent(name)) {
        apps_[name.id] = std::unique_ptr<AppData>(new AppData);
        apps_[name.id]->loaded = false;
        apps_[name.id]->chre = false;
        apps_[name.id]->running = false;
        apps_[name.id]->cached_napp = false;
    }
    const auto &app = apps_[name.id];

    while (buf.getRoom() >= 2) {
        tag = buf.readU8();
        len = buf.readU8();
        if (buf.getRoom() >= len) {
            switch(tag) {
            case NANOHUB_HAL_APP_INFO_CRC:
                if (len == 0)
                    app->crc = 0;
                else if (len == sizeof(app->crc))
                    app->crc = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_APP_INFO_TID:
                if (len == 0) {
                    app->tid = NANOHUB_TID_UNKNOWN;
                    ramUseValid = false;
                    app->loaded = true;
                    app->running = false;
                } else if (len  == sizeof(app->tid)) {
                    app->tid = buf.readU32();
                    app->loaded = true;
                    app->running = true;
                } else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_APP_INFO_VERSION:
                if (len == sizeof(app->version))
                    app->version = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_APP_INFO_ADDR:
                if (len == sizeof(app->flashAddr))
                    app->flashAddr = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_APP_INFO_SIZE:
                if (len == 0)
                    app->flashUse = NANOHUB_MEM_SZ_UNKNOWN;
                else if (len == sizeof(app->flashUse))
                    app->flashUse = buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_APP_INFO_HEAP:
            case NANOHUB_HAL_APP_INFO_DATA:
            case NANOHUB_HAL_APP_INFO_BSS:
                if (len == 0)
                    ramUseValid = false;
                else if (len == sizeof(uint32_t))
                    ramUse += buf.readU32();
                else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_APP_INFO_CHRE_MAJOR:
                if (len == 0)
                    app->chre = false;
                else if (len == sizeof(app->chre_major)) {
                    app->chre = true;
                    app->chre_major = buf.readU8();
                } else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_APP_INFO_CHRE_MINOR:
                if (len == 0)
                    app->chre = false;
                else if (len == sizeof(app->chre_minor)) {
                    app->chre = true;
                    app->chre_minor = buf.readU8();
                } else
                    buf.readRaw(len);
                break;
            case NANOHUB_HAL_APP_INFO_END:
                if (len != 0 || buf.getRoom() != 0) {
                    ALOGE("%s: failed to read object", __func__);
                    return 0;
                }
                break;
            default:
                ALOGI("%s: unsupported tag: %d", __func__, tag);
                buf.readRaw(len);
                break;
            }
        } else {
            ALOGE("%s: failed to read object", __func__);
            return 0;
        }
    }

    if (buf.getRoom() != 0) {
        ALOGE("%s: failed to read object", __func__);
        return 0;
    }

    if (ramUseValid)
        app->ramUse = ramUse;
    else
        app->ramUse = NANOHUB_MEM_SZ_UNKNOWN;

    return app->flashAddr +
        (app->flashUse != NANOHUB_MEM_SZ_UNKNOWN ? ((app->flashUse+3)&~3) : 4);
}

void SystemComm::AppManager::sendAppInfoToApp(uint32_t transactionId) {
    std::vector<hub_app_info> appInfo;
    for (auto &it : apps_) {
        uint64_t id = it.first;
        const auto &app = it.second;

        // TODO: Still have some non-chre apps that need to be reported
        // if (!app->chre || !app->running || app->flashUse == NANOHUB_MEM_SZ_UNKNOWN)
        if (!app->running || app->flashUse == NANOHUB_MEM_SZ_UNKNOWN)
            continue;

        hub_app_info info;
        info.app_name = { .id = id };
        info.version = app->version;
        info.num_mem_ranges = 0;
        if (app->flashUse != NANOHUB_MEM_SZ_UNKNOWN) {
            mem_range_t &range = info.mem_usage[info.num_mem_ranges++];
            range.type = HUB_MEM_TYPE_MAIN;
            range.total_bytes = app->flashUse;
        }
        if (app->ramUse != NANOHUB_MEM_SZ_UNKNOWN) {
            mem_range_t &range = info.mem_usage[info.num_mem_ranges++];
            range.type = HUB_MEM_TYPE_RAM;
            range.total_bytes = app->ramUse;
        }

        appInfo.push_back(info);
    }
    sendToApp(CONTEXT_HUB_QUERY_APPS, transactionId,
              static_cast<const void *>(appInfo.data()),
              appInfo.size() * sizeof(appInfo[0]));
}

int SystemComm::AppManager::getAppsToStart(std::vector<hub_app_name_t> &apps)
{
    int cnt = 0;
    apps.clear();

    for (auto &it : apps_) {
        uint64_t id = it.first;
        const auto &app = it.second;

        if (app->cached_napp && app->cached_start && app->loaded &&
            !app->running && app->flashUse != NANOHUB_MEM_SZ_UNKNOWN) {
            apps.push_back({ .id = id });
            cnt++;
        }
    }

    return cnt;
}

int SystemComm::doHandleRx(uint64_t appId, uint32_t transactionId, const char *data, int len, bool chre)
{
    bool reboot = false;
    uint32_t rebootStatus;
    //we only care for messages from HostIF
    if (appId != mHostIfAppName.id)
        return 1;

    //they must all be at least 1 byte long
    if (!len) {
        return -EINVAL;
    }
    MessageBuf buf(data, len);
    if (NanoHub::messageTracingEnabled()) {
        dumpBuffer("SYS -> HAL", mHostIfAppName, transactionId, 0, buf.getData(), buf.getSize());
    }
    int status = mSessions.handleRx(buf, transactionId, mAppManager, chre, reboot, rebootStatus);
    if (status) {
        // provide default handler for any system message, that is not properly handled
        dumpBuffer(status > 0 ? "HAL (not handled)" : "HAL (error)",
                   mHostIfAppName, transactionId, 0, buf.getData(), buf.getSize(), status);
        status = status > 0 ? 0 : status;
    }
    if (reboot) {
        hub_message_t msg =
        {
            .app_name.id = appId,
            .message_type = CONTEXT_HUB_START_APPS,
            .message_len = sizeof(rebootStatus),
            .message = &rebootStatus,
        };

        status = doHandleTx(&msg, 0xFFFFFFFF);
    }

    return status;
}

void SystemComm::doDumpAppInfo(std::string &result)
{
    mAppManager.dumpAppInfo(result);
}

int SystemComm::SessionManager::handleRx(MessageBuf &buf, uint32_t transactionId, AppManager &appManager, bool chre, bool &reboot, uint32_t &rebootStatus)
{
    int status = 1;
    std::unique_lock<std::mutex> lk(lock);

    // pass message to all active sessions, in arbitrary order
    // 1st session that handles the message terminates the loop
    for (auto pos = sessions_.begin(); pos != sessions_.end() && status > 0; next(pos)) {
        if (!isActive(pos)) {
            continue;
        }
        Session *session = pos->second;
        status = session->handleRx(buf, transactionId, appManager, chre);
        if (status < 0) {
            session->complete();
        }
    }

    NanohubRsp rsp(buf, transactionId, chre);
    if (rsp.mCmd == NANOHUB_HAL_SYS_MGMT) {
        uint8_t cmd = buf.readU8();

        if (cmd == NANOHUB_HAL_SYS_MGMT_REBOOT) {
            // if this is reboot notification, kill all sessions
            for (auto pos = sessions_.begin(); pos != sessions_.end(); next(pos)) {
                if (!isActive(pos)) {
                    continue;
                }
                Session *session = pos->second;
                session->abort(-EINTR);
            }
            lk.unlock();
            // log the reboot event, if not handled
            if (status > 0) {
                ALOGW("Nanohub reboot status [UNSOLICITED]: %08" PRIX32, rsp.mStatus);
                status = 0;
            }
            reboot = true;
            rebootStatus = rsp.mStatus;
        }
    }

    return status;
}

int SystemComm::SessionManager::setup_and_add(int id, Session *session, const hub_message_t *appMsg, uint32_t transactionId, AppManager &appManager)
{
    std::lock_guard<std::mutex> _l(lock);

    // scan sessions to release those that are already done
    for (auto pos = sessions_.begin(); pos != sessions_.end(); next(pos)) {
        continue;
    }

    if (sessions_.count(id) == 0 && !session->isRunning()) {
        sessions_[id] = session;
        int ret = session->setup(appMsg, transactionId, appManager);
        if (ret < 0) {
            session->complete();
        }
        return ret;
    }
    return -EBUSY;
}

int SystemComm::doHandleTx(const hub_message_t *appMsg, uint32_t transactionId)
{
    int status = 0;

    switch (appMsg->message_type) {
    case CONTEXT_HUB_LOAD_APP:
        if (!mKeySession.haveKeys()) {
            status = mSessions.setup_and_add(CONTEXT_HUB_LOAD_APP, &mKeySession, appMsg, transactionId, mAppManager);
            if (status < 0) {
                break;
            }
            mKeySession.wait();
            status = mKeySession.getStatus();
            if (status < 0) {
                break;
            }
        }
        status = mSessions.setup_and_add(CONTEXT_HUB_LOAD_APP, &mAppMgmtSession, appMsg, transactionId, mAppManager);
        break;
    case CONTEXT_HUB_APPS_ENABLE:
    case CONTEXT_HUB_APPS_DISABLE:
    case CONTEXT_HUB_UNLOAD_APP:
        // all APP-modifying commands share session key, to ensure they can't happen at the same time
        status = mSessions.setup_and_add(CONTEXT_HUB_LOAD_APP, &mAppMgmtSession, appMsg, transactionId, mAppManager);
        break;

    case CONTEXT_HUB_QUERY_APPS:
        mAppManager.sendAppInfoToApp(transactionId);
        break;

    case CONTEXT_HUB_QUERY_MEMORY:
        status = mSessions.setup_and_add(CONTEXT_HUB_QUERY_MEMORY, &mMemInfoSession, appMsg, transactionId, mAppManager);
        break;

    case CONTEXT_HUB_START_APPS:
        status = mSessions.setup_and_add(CONTEXT_HUB_START_APPS, &mAppMgmtSession, appMsg, transactionId, mAppManager);
        break;

    default:
        ALOGW("Unknown os message type %u\n", appMsg->message_type);
        return -EINVAL;
    }

   return status;
}

}; // namespace nanohub

}; // namespace android