/* * 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. */ #ifndef _NANOHUB_SYSTEM_COMMS_H_ #define _NANOHUB_SYSTEM_COMMS_H_ #include <utils/Condition.h> #include <chrono> #include <condition_variable> #include <map> #include <mutex> #include <vector> #include <hardware/context_hub.h> #include <nanohub/nanohub.h> #include "nanohubhal.h" #include "message_buf.h" //rx: return 0 if handled, > 0 if not handled, < 0 if error happened #define MSG_HANDLED 0 //messages to the HostIf nanoapp & their replies (mesages and replies both begin with u8 message_type) #define NANOHUB_HAL_APP_MGMT 0x10 // (char cmd, u64 appId, u64 appMsk) -> (int errno, u32 results) #define NANOHUB_HAL_APP_MGMT_START 0 #define NANOHUB_HAL_APP_MGMT_STOP 1 #define NANOHUB_HAL_APP_MGMT_UNLOAD 2 #define NANOHUB_HAL_APP_MGMT_DELETE 3 #define NANOHUB_HAL_SYS_MGMT 0x11 // (char cmd) -> (int errno) #define NANOHUB_HAL_SYS_MGMT_ERASE 0 #define NANOHUB_HAL_SYS_MGMT_REBOOT 1 #define NANOHUB_HAL_APP_INFO 0x12 #define NANOHUB_HAL_APP_INFO_APPID 0x00 #define NANOHUB_HAL_APP_INFO_CRC 0x01 #define NANOHUB_HAL_APP_INFO_TID 0x02 #define NANOHUB_HAL_APP_INFO_VERSION 0x03 #define NANOHUB_HAL_APP_INFO_ADDR 0x04 #define NANOHUB_HAL_APP_INFO_SIZE 0x05 #define NANOHUB_HAL_APP_INFO_HEAP 0x06 #define NANOHUB_HAL_APP_INFO_DATA 0x07 #define NANOHUB_HAL_APP_INFO_BSS 0x08 #define NANOHUB_HAL_APP_INFO_CHRE_MAJOR 0x09 #define NANOHUB_HAL_APP_INFO_CHRE_MINOR 0x0A #define NANOHUB_HAL_APP_INFO_END 0xFF #define NANOHUB_HAL_SYS_INFO 0x13 #define NANOHUB_HAL_SYS_INFO_HEAP_FREE 0x0F #define NANOHUB_HAL_SYS_INFO_RAM_SIZE 0x12 #define NANOHUB_HAL_SYS_INFO_EEDATA_SIZE 0x13 #define NANOHUB_HAL_SYS_INFO_EEDATA_FREE 0x14 #define NANOHUB_HAL_SYS_INFO_CODE_SIZE 0x15 #define NANOHUB_HAL_SYS_INFO_CODE_FREE 0x16 #define NANOHUB_HAL_SYS_INFO_SHARED_SIZE 0x17 #define NANOHUB_HAL_SYS_INFO_SHARED_FREE 0x18 #define NANOHUB_HAL_SYS_INFO_END 0xFF #define NANOHUB_HAL_KEY_INFO 0x14 #define NANOHUB_HAL_START_UPLOAD 0x16 #define NANOHUB_HAL_CONT_UPLOAD 0x17 #define NANOHUB_HAL_FINISH_UPLOAD 0x18 #define NANOHUB_HAL_UPLOAD_ACCEPTED 0 #define NANOHUB_HAL_UPLOAD_WAIT 1 #define NANOHUB_HAL_UPLOAD_RESEND 2 #define NANOHUB_HAL_UPLOAD_RESTART 3 #define NANOHUB_HAL_UPLOAD_CANCEL 4 #define NANOHUB_HAL_UPLOAD_CANCEL_NO_RETRY 5 #define NANOHUB_HAL_UPLOAD_NO_SPACE 6 #define NANOHUB_APP_NOT_LOADED (-1) #define NANOHUB_APP_LOADED (0) #define NANOHUB_UPLOAD_CHUNK_SZ_MAX 64 #define NANOHUB_MEM_SZ_UNKNOWN 0xFFFFFFFFUL #define NANOHUB_TID_UNKNOWN 0xFFFFFFFFUL #define CONTEXT_HUB_START_APPS 8 namespace android { namespace nanohub { int system_comms_handle_rx(const nano_message_raw *msg); int system_comms_handle_tx(const hub_message_t *outMsg); struct MgmtStatus { union { uint32_t value; struct { uint8_t app; uint8_t task; uint8_t op; uint8_t erase; } __attribute__((packed)); }; } __attribute__((packed)); struct NanohubMemInfo { //sizes uint32_t flashSz, blSz, osSz, sharedSz, eeSz; uint32_t ramSz; //use uint32_t blUse, osUse, sharedUse, eeUse; uint32_t ramUse; } __attribute__((packed)); struct NanohubRsp { uint32_t mCmd; uint32_t mTransactionId; int32_t mStatus; explicit NanohubRsp(MessageBuf &buf, uint32_t transactionId, bool chre); }; inline bool operator == (const hub_app_name_t &a, const hub_app_name_t &b) { return a.id == b.id; } inline bool operator != (const hub_app_name_t &a, const hub_app_name_t &b) { return !(a == b); } class SystemComm { private: class AppManager; /* * Nanohub HAL sessions * * Session is an object that can group several message exchanges with FW, * maintain state, and be waited for completion by someone else. * * As of this moment, since all sessions are triggered by client thread, * and all the exchange is happening in local worker thread, it is only possible * for client thread to wait on session completion. * Allowing sessions to wait on each other will require a worker thread pool. * It is now unnecessary, and not implemented. */ class ISession { public: virtual int setup(const hub_message_t *app_msg, uint32_t transactionId, AppManager &appManager) = 0; virtual int handleRx(MessageBuf &buf, uint32_t transactionId, AppManager &appManager, bool chre) = 0; virtual int getState() const = 0; // FSM state virtual int getStatus() const = 0; // execution status (result code) virtual void abort(int32_t) = 0; virtual ~ISession() {} }; class SessionManager; class Session : public ISession { friend class SessionManager; mutable std::mutex mDoneMutex; // controls condition and state transitions std::condition_variable mDoneCond; volatile int mState; protected: mutable std::mutex mLock; // serializes message handling int32_t mStatus; enum { SESSION_INIT = 0, SESSION_DONE = 1, SESSION_USER = 2, }; void complete() { std::unique_lock<std::mutex> lk(mDoneMutex); if (mState != SESSION_DONE) { mState = SESSION_DONE; lk.unlock(); mDoneCond.notify_all(); } } void abort(int32_t status) { std::lock_guard<std::mutex> _l(mLock); mStatus = status; complete(); } void setState(int state) { if (state == SESSION_DONE) { complete(); } else { std::lock_guard<std::mutex> _l(mDoneMutex); mState = state; } } public: Session() { mState = SESSION_INIT; mStatus = -1; } int getStatus() const { std::lock_guard<std::mutex> _l(mLock); return mStatus; } int wait() { std::unique_lock<std::mutex> lk(mDoneMutex); bool success = mDoneCond.wait_for( lk, std::chrono::seconds(30), [this] { return mState == SESSION_DONE; }); if (!success) { ALOGE("Timed out waiting for response"); } return success ? 0 : -1; } virtual int getState() const override { std::lock_guard<std::mutex> _l(mDoneMutex); return mState; } virtual bool isDone() const { std::lock_guard<std::mutex> _l(mDoneMutex); return mState == SESSION_DONE; } virtual bool isRunning() const { std::lock_guard<std::mutex> _l(mDoneMutex); return mState > SESSION_DONE; } }; class AppMgmtSession : public Session { enum { TRANSFER = SESSION_USER, QUERY_START, START, STOP_TRANSFER, FINISH, RUN, STOP_RUN, RUN_FAILED, REBOOT, ERASE_TRANSFER, MGMT, INFO, }; uint32_t mCmd; // LOAD_APP, UNLOAD_APP, ENABLE_APP, DISABLE_APP uint32_t mResult; std::vector<uint8_t> mData; uint32_t mLen; uint32_t mPos; uint32_t mNextPos; uint32_t mErrCnt; hub_app_name_t mAppName; uint32_t mFlashAddr; std::vector<hub_app_name_t> mAppList; int setupMgmt(const hub_message_t *appMsg, uint32_t transactionId, uint32_t cmd, AppManager &appManager); int handleTransfer(NanohubRsp &rsp, MessageBuf &, AppManager &appManager); int handleStopTransfer(NanohubRsp &rsp, MessageBuf &buf, AppManager &); int handleQueryStart(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager); int handleStart(NanohubRsp &rsp, MessageBuf &buf, AppManager &); int handleFinish(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager); int handleRun(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager); int handleStopRun(NanohubRsp &rsp, MessageBuf &buf, AppManager &); int handleReboot(NanohubRsp &rsp, MessageBuf &buf, AppManager &); int handleEraseTransfer(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager); int handleMgmt(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager); int handleInfo(NanohubRsp &rsp, MessageBuf &buf, AppManager &appManager); public: AppMgmtSession() { mCmd = 0; mResult = 0; mPos = 0; mLen = 0; memset(&mAppName, 0, sizeof(mAppName)); } virtual int handleRx(MessageBuf &buf, uint32_t transactionId, AppManager &appManager, bool chre) override; virtual int setup(const hub_message_t *app_msg, uint32_t transactionId, AppManager &appManager) override; }; class MemInfoSession : public Session { public: virtual int setup(const hub_message_t *app_msg, uint32_t transactionId, AppManager &) override; virtual int handleRx(MessageBuf &buf, uint32_t transactionId, AppManager &, bool chre) override; }; class KeyInfoSession : public Session { std::vector<uint8_t> mRsaKeyData; uint32_t mKeyNum; uint32_t mKeyOffset; int requestRsaKeys(uint32_t transactionId); public: virtual int setup(const hub_message_t *, uint32_t, AppManager &) override; virtual int handleRx(MessageBuf &buf, uint32_t transactionId, AppManager &, bool chre) override; bool haveKeys() const { std::lock_guard<std::mutex> _l(mLock); return mRsaKeyData.size() > 0 && !isRunning(); } }; class AppManager { struct AppData { uint32_t version, flashUse, ramUse; uint32_t tid, crc, flashAddr; uint8_t chre_major, chre_minor; bool chre, running, loaded; bool cached_start, cached_napp; uint32_t cached_version, cached_crc; }; typedef std::map<uint64_t, std::unique_ptr<AppData>> AppMap; AppMap apps_; public: AppManager() { restoreApps(); } void dumpAppInfo(std::string &result); bool saveApps(); bool restoreApps(); bool eraseApps(); bool writeApp(hub_app_name_t &appName, const uint8_t *data, int32_t len); int32_t readApp(hub_app_name_t &appName, void **data); bool cmpApp(hub_app_name_t &appName, const uint8_t *data, uint32_t len); uint32_t readNanohubAppInfo(MessageBuf &buf); void sendAppInfoToApp(uint32_t transactionId); int getAppsToStart(std::vector<hub_app_name_t> &apps); bool setCachedCrc(hub_app_name_t &appName, uint32_t crc) { if (!isAppPresent(appName)) return false; else { apps_[appName.id]->cached_napp = true; apps_[appName.id]->cached_crc = crc; apps_[appName.id]->cached_start = false; saveApps(); return true; } } bool clearCachedApp(hub_app_name_t &appName) { if (!isAppPresent(appName)) return false; else { apps_[appName.id]->cached_napp = false; apps_[appName.id]->cached_start = false; saveApps(); return true; } } bool clearRunning(hub_app_name_t &appName) { if (!isAppLoaded(appName)) return false; else { apps_[appName.id]->running = false; return true; } } bool setCachedVersion(hub_app_name_t &appName, uint32_t version) { if (!isAppPresent(appName)) return false; else { apps_[appName.id]->cached_version = version; return true; } } bool setCachedStart(hub_app_name_t &appName, bool start) { if (!isAppPresent(appName)) return false; else { apps_[appName.id]->cached_start = start; saveApps(); return true; } } bool addNewApp(hub_app_name_t &appName, uint32_t version) { if (isAppLoaded(appName)) return false; else apps_[appName.id] = std::unique_ptr<AppData>(new AppData); apps_[appName.id]->loaded = false; apps_[appName.id]->running = false; apps_[appName.id]->chre = false; apps_[appName.id]->cached_napp = false; apps_[appName.id]->cached_version = version; return true; } bool isAppPresent(hub_app_name_t &appName) { return apps_.count(appName.id) != 0; } bool isAppLoaded(hub_app_name_t &appName) { return apps_.count(appName.id) != 0 && apps_[appName.id]->loaded; } bool isAppRunning(hub_app_name_t &appName) { return apps_.count(appName.id) != 0 && apps_[appName.id]->running; } uint32_t getFlashAddr(hub_app_name_t &appName) { if (isAppPresent(appName)) return apps_[appName.id]->flashAddr; else return 0xFFFFFFFF; } }; class SessionManager { typedef std::map<int, Session* > SessionMap; std::mutex lock; SessionMap sessions_; bool isActive(const SessionMap::iterator &pos) const { return !pos->second->isDone(); } void next(SessionMap::iterator &pos) { isActive(pos) ? pos++ : pos = sessions_.erase(pos); } public: int handleRx(MessageBuf &buf, uint32_t transactionId, AppManager &appManager, bool chre, bool &reboot, uint32_t &rebootStatus); int setup_and_add(int id, Session *session, const hub_message_t *appMsg, uint32_t transactionId, AppManager &appManager); } mSessions; const hub_app_name_t mHostIfAppName = { .id = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0) }; static SystemComm *getSystem() { // this is thread-safe in c++11 static SystemComm theInstance; return &theInstance; } SystemComm () = default; ~SystemComm() = default; int doHandleTx(const hub_message_t *txMsg, uint32_t transactionId); int doHandleRx(uint64_t appId, uint32_t transactionId, const char *data, int len, bool chre); void doDumpAppInfo(std::string &result); int doHandleRx(const nano_message_raw *rxMsg) { return doHandleRx(rxMsg->hdr.appId, 0, reinterpret_cast<const char*>(rxMsg->data), rxMsg->hdr.len, false); } int doHandleRx(const nano_message_chre *rxMsg) { return doHandleRx(rxMsg->hdr.appId, rxMsg->hdr.appEventId, reinterpret_cast<const char*>(rxMsg->data), rxMsg->hdr.len, true); } static void sendToApp(uint32_t typ, uint32_t transactionId, const void *data, uint32_t len) { if (NanoHub::messageTracingEnabled()) { dumpBuffer("HAL -> APP", get_hub_info()->os_app_name, transactionId, 0, data, len); } NanoHub::sendToApp(HubMessage(&get_hub_info()->os_app_name, typ, transactionId, ENDPOINT_BROADCAST, data, len)); } static int sendToSystem(const void *data, size_t len, uint32_t transactionId); KeyInfoSession mKeySession; AppMgmtSession mAppMgmtSession; MemInfoSession mMemInfoSession; AppManager mAppManager; public: static int handleTx(const hub_message_t *txMsg, uint32_t transactionId) { return getSystem()->doHandleTx(txMsg, transactionId); } static int handleRx(const nano_message_raw *rxMsg) { return getSystem()->doHandleRx(rxMsg); } static int handleRx(const nano_message_chre *rxMsg) { return getSystem()->doHandleRx(rxMsg); } static void dumpAppInfo(std::string &result) { return getSystem()->doDumpAppInfo(result); } }; }; // namespace nanohub }; // namespace android #endif