/* * 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