/* * 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. */ #include "Contexthub.h" #include #include #include #include #include #undef LOG_TAG #define LOG_TAG "ContextHubHalAdapter" namespace android { namespace hardware { namespace contexthub { namespace V1_0 { namespace implementation { static constexpr uint64_t ALL_APPS = UINT64_C(0xFFFFFFFFFFFFFFFF); Contexthub::Contexthub() : mInitCheck(NO_INIT), mContextHubModule(nullptr), mDeathRecipient(new DeathRecipient(this)), mIsTransactionPending(false) { const hw_module_t *module; mInitCheck = hw_get_module(CONTEXT_HUB_MODULE_ID, &module); if (mInitCheck != OK) { ALOGE("Could not load %s module: %s", CONTEXT_HUB_MODULE_ID, strerror(-mInitCheck)); } else if (module == nullptr) { ALOGE("hal returned succes but a null module!"); // Assign an error, this should not really happen... mInitCheck = UNKNOWN_ERROR; } else { ALOGI("Loaded Context Hub module"); mContextHubModule = reinterpret_cast(module); } } bool Contexthub::setOsAppAsDestination(hub_message_t *msg, int hubId) { if (!isValidHubId(hubId)) { ALOGW("%s: Hub information is null for hubHandle %d", __FUNCTION__, hubId); return false; } else { msg->app_name = mCachedHubInfo[hubId].osAppName; return true; } } Return Contexthub::getHubs(getHubs_cb _hidl_cb) { std::vector hubs; if (isInitialized()) { const context_hub_t *hubArray = nullptr; size_t numHubs; // Explicitly discarding const. HAL method discards it. numHubs = mContextHubModule->get_hubs(const_cast(mContextHubModule), &hubArray); ALOGI("Context Hub Hal Adapter reports %zu hubs", numHubs); mCachedHubInfo.clear(); for (size_t i = 0; i < numHubs; i++) { CachedHubInformation info; ContextHub c; c.hubId = hubArray[i].hub_id; c.name = hubArray[i].name; c.vendor = hubArray[i].vendor; c.toolchain = hubArray[i].toolchain; c.toolchainVersion = hubArray[i].toolchain_version; c.platformVersion = hubArray[i].platform_version; c.maxSupportedMsgLen = hubArray[i].max_supported_msg_len; c.peakMips = hubArray[i].peak_mips; c.peakPowerDrawMw = hubArray[i].peak_power_draw_mw; c.stoppedPowerDrawMw = hubArray[i].stopped_power_draw_mw; c.sleepPowerDrawMw = hubArray[i].sleep_power_draw_mw; info.callback = nullptr; info.osAppName = hubArray[i].os_app_name; mCachedHubInfo[hubArray[i].hub_id] = info; hubs.push_back(c); } } else { ALOGW("Context Hub Hal Adapter not initialized"); } _hidl_cb(hubs); return Void(); } Contexthub::DeathRecipient::DeathRecipient(sp contexthub) : mContexthub(contexthub) {} void Contexthub::DeathRecipient::serviceDied( uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& /*who*/) { uint32_t hubId = static_cast(cookie); mContexthub->handleServiceDeath(hubId); } bool Contexthub::isValidHubId(uint32_t hubId) { if (!mCachedHubInfo.count(hubId)) { ALOGW("Hub information not found for hubId %" PRIu32, hubId); return false; } else { return true; } } sp Contexthub::getCallBackForHubId(uint32_t hubId) { if (!isValidHubId(hubId)) { return nullptr; } else { return mCachedHubInfo[hubId].callback; } } Return Contexthub::sendMessageToHub(uint32_t hubId, const ContextHubMsg &msg) { if (!isInitialized()) { return Result::NOT_INIT; } if (!isValidHubId(hubId) || msg.msg.size() > UINT32_MAX) { return Result::BAD_PARAMS; } hub_message_t txMsg = { .app_name.id = msg.appName, .message_type = msg.msgType, .message_len = static_cast(msg.msg.size()), // Note the check above .message = static_cast(msg.msg.data()), }; // Use a placeholder to prevent send_message with empty message from failing prematurely static uint8_t placeholder; if (txMsg.message_len == 0 && txMsg.message == nullptr) { txMsg.message = &placeholder; } ALOGI("Sending msg of type %" PRIu32 ", size %" PRIu32 " to app 0x%" PRIx64, txMsg.message_type, txMsg.message_len, txMsg.app_name.id); if(mContextHubModule->send_message(hubId, &txMsg) != 0) { return Result::TRANSACTION_FAILED; } return Result::OK; } Return Contexthub::reboot(uint32_t hubId) { if (!isInitialized()) { return Result::NOT_INIT; } hub_message_t msg; if (setOsAppAsDestination(&msg, hubId) == false) { return Result::BAD_PARAMS; } msg.message_type = CONTEXT_HUB_OS_REBOOT; msg.message_len = 0; msg.message = nullptr; if(mContextHubModule->send_message(hubId, &msg) != 0) { return Result::TRANSACTION_FAILED; } else { return Result::OK; } } Return Contexthub::registerCallback(uint32_t hubId, const sp &cb) { Return retVal = Result::BAD_PARAMS; if (!isInitialized()) { // Not initilalized ALOGW("Context hub not initialized successfully"); retVal = Result::NOT_INIT; } else if (!isValidHubId(hubId)) { // Initialized, but hubId is not valid retVal = Result::BAD_PARAMS; } else if (mContextHubModule->subscribe_messages(hubId, contextHubCb, this) == 0) { // Initialized && valid hub && subscription successful if (mCachedHubInfo[hubId].callback != nullptr) { ALOGD("Modifying callback for hubId %" PRIu32, hubId); mCachedHubInfo[hubId].callback->unlinkToDeath(mDeathRecipient); } mCachedHubInfo[hubId].callback = cb; if (cb != nullptr) { Return linkResult = cb->linkToDeath(mDeathRecipient, hubId); bool linkSuccess = linkResult.isOk() ? static_cast(linkResult) : false; if (!linkSuccess) { ALOGW("Couldn't link death recipient for hubId %" PRIu32, hubId); } } retVal = Result::OK; } else { // Initalized && valid hubId - but subscription unsuccessful // This is likely an internal error in the HAL implementation, but we // cannot add more information. ALOGW("Could not subscribe to the hub for callback"); retVal = Result::UNKNOWN_FAILURE; } return retVal; } static bool isValidOsStatus(const uint8_t *msg, size_t msgLen, status_response_t *rsp) { // Workaround a bug in some HALs if (msgLen == 1) { rsp->result = msg[0]; return true; } if (msg == nullptr || msgLen != sizeof(*rsp)) { ALOGI("Received invalid response (is null : %d, size %zu)", msg == nullptr ? 1 : 0, msgLen); return false; } memcpy(rsp, msg, sizeof(*rsp)); // No validations on return values return true; } int Contexthub::handleOsMessage(sp cb, uint32_t msgType, const uint8_t *msg, int msgLen) { int retVal = -1; switch(msgType) { case CONTEXT_HUB_APPS_ENABLE: case CONTEXT_HUB_APPS_DISABLE: case CONTEXT_HUB_LOAD_APP: case CONTEXT_HUB_UNLOAD_APP: { struct status_response_t rsp; TransactionResult result; if (isValidOsStatus(msg, msgLen, &rsp) && rsp.result == 0) { retVal = 0; result = TransactionResult::SUCCESS; } else { result = TransactionResult::FAILURE; } mIsTransactionPending = false; if (cb != nullptr) { cb->handleTxnResult(mTransactionId, result); } retVal = 0; break; } case CONTEXT_HUB_QUERY_APPS: { std::vector apps; int numApps = msgLen / sizeof(hub_app_info); const hub_app_info *unalignedInfoAddr = reinterpret_cast(msg); for (int i = 0; i < numApps; i++) { hub_app_info query_info; memcpy(&query_info, &unalignedInfoAddr[i], sizeof(query_info)); HubAppInfo app; app.appId = query_info.app_name.id; app.version = query_info.version; // TODO :: Add memory ranges apps.push_back(app); } if (cb != nullptr) { cb->handleAppsInfo(apps); } retVal = 0; break; } case CONTEXT_HUB_QUERY_MEMORY: { // Deferring this use retVal = 0; break; } case CONTEXT_HUB_OS_REBOOT: { mIsTransactionPending = false; if (cb != nullptr) { cb->handleHubEvent(AsyncEventType::RESTARTED); } retVal = 0; break; } default: { retVal = -1; break; } } return retVal; } void Contexthub::handleServiceDeath(uint32_t hubId) { ALOGI("Callback/service died for hubId %" PRIu32, hubId); int ret = mContextHubModule->subscribe_messages(hubId, nullptr, nullptr); if (ret != 0) { ALOGW("Failed to unregister callback from hubId %" PRIu32 ": %d", hubId, ret); } mCachedHubInfo[hubId].callback.clear(); } int Contexthub::contextHubCb(uint32_t hubId, const struct hub_message_t *rxMsg, void *cookie) { Contexthub *obj = static_cast(cookie); if (rxMsg == nullptr) { ALOGW("Ignoring NULL message"); return -1; } if (!obj->isValidHubId(hubId)) { ALOGW("Invalid hub Id %" PRIu32, hubId); return -1; } sp cb = obj->getCallBackForHubId(hubId); if (cb == nullptr) { // This should not ever happen ALOGW("No callback registered, returning"); return -1; } if (rxMsg->message_type < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE) { obj->handleOsMessage(cb, rxMsg->message_type, static_cast(rxMsg->message), rxMsg->message_len); } else { ContextHubMsg msg; msg.appName = rxMsg->app_name.id; msg.msgType = rxMsg->message_type; msg.hostEndPoint = static_cast(HostEndPoint::BROADCAST); msg.msg = std::vector(static_cast(rxMsg->message), static_cast(rxMsg->message) + rxMsg->message_len); cb->handleClientMsg(msg); } return 0; } Return Contexthub::unloadNanoApp(uint32_t hubId, uint64_t appId, uint32_t transactionId) { if (!isInitialized()) { return Result::NOT_INIT; } if (mIsTransactionPending) { return Result::TRANSACTION_PENDING; } hub_message_t msg; if (setOsAppAsDestination(&msg, hubId) == false) { return Result::BAD_PARAMS; } struct apps_disable_request_t req; msg.message_type = CONTEXT_HUB_UNLOAD_APP; msg.message_len = sizeof(req); msg.message = &req; req.app_name.id = appId; if(mContextHubModule->send_message(hubId, &msg) != 0) { return Result::TRANSACTION_FAILED; } else { mTransactionId = transactionId; mIsTransactionPending = true; return Result::OK; } } Return Contexthub::loadNanoApp(uint32_t hubId, const NanoAppBinary& appBinary, uint32_t transactionId) { if (!isInitialized()) { return Result::NOT_INIT; } if (mIsTransactionPending) { return Result::TRANSACTION_PENDING; } hub_message_t hubMsg; if (setOsAppAsDestination(&hubMsg, hubId) == false) { return Result::BAD_PARAMS; } // Data from the nanoapp header is passed through HIDL as explicit fields, // but the legacy HAL expects it prepended to the binary, therefore we must // reconstruct it here prior to passing to the legacy HAL. const struct nano_app_binary_t header = { .header_version = htole32(1), .magic = htole32(NANOAPP_MAGIC), .app_id.id = htole64(appBinary.appId), .app_version = htole32(appBinary.appVersion), .flags = htole32(appBinary.flags), .hw_hub_type = htole64(0), .target_chre_api_major_version = appBinary.targetChreApiMajorVersion, .target_chre_api_minor_version = appBinary.targetChreApiMinorVersion, }; const uint8_t *headerBytes = reinterpret_cast(&header); std::vector binaryWithHeader(appBinary.customBinary); binaryWithHeader.insert(binaryWithHeader.begin(), headerBytes, headerBytes + sizeof(header)); hubMsg.message_type = CONTEXT_HUB_LOAD_APP; hubMsg.message_len = binaryWithHeader.size(); hubMsg.message = binaryWithHeader.data(); if (mContextHubModule->send_message(hubId, &hubMsg) != 0) { return Result::TRANSACTION_FAILED; } else { mTransactionId = transactionId; mIsTransactionPending = true; return Result::OK; } } Return Contexthub::enableNanoApp(uint32_t hubId, uint64_t appId, uint32_t transactionId) { if (!isInitialized()) { return Result::NOT_INIT; } if (mIsTransactionPending) { return Result::TRANSACTION_PENDING; } hub_message_t msg; if (setOsAppAsDestination(&msg, hubId) == false) { return Result::BAD_PARAMS; } struct apps_enable_request_t req; msg.message_type = CONTEXT_HUB_APPS_ENABLE; msg.message_len = sizeof(req); req.app_name.id = appId; msg.message = &req; if(mContextHubModule->send_message(hubId, &msg) != 0) { return Result::TRANSACTION_FAILED; } else { mTransactionId = transactionId; mIsTransactionPending = true; return Result::OK; } } Return Contexthub::disableNanoApp(uint32_t hubId, uint64_t appId, uint32_t transactionId) { if (!isInitialized()) { return Result::NOT_INIT; } if (mIsTransactionPending) { return Result::TRANSACTION_PENDING; } hub_message_t msg; if (setOsAppAsDestination(&msg, hubId) == false) { return Result::BAD_PARAMS; } struct apps_disable_request_t req; msg.message_type = CONTEXT_HUB_APPS_DISABLE; msg.message_len = sizeof(req); req.app_name.id = appId; msg.message = &req; if(mContextHubModule->send_message(hubId, &msg) != 0) { return Result::TRANSACTION_FAILED; } else { mTransactionId = transactionId; mIsTransactionPending = true; return Result::OK; } } Return Contexthub::queryApps(uint32_t hubId) { if (!isInitialized()) { return Result::NOT_INIT; } hub_message_t msg; if (setOsAppAsDestination(&msg, hubId) == false) { ALOGW("Could not find hubId %" PRIu32, hubId); return Result::BAD_PARAMS; } query_apps_request_t payload; payload.app_name.id = ALL_APPS; // TODO : Pass this in as a parameter msg.message = &payload; msg.message_len = sizeof(payload); msg.message_type = CONTEXT_HUB_QUERY_APPS; if(mContextHubModule->send_message(hubId, &msg) != 0) { ALOGW("Query Apps sendMessage failed"); return Result::TRANSACTION_FAILED; } return Result::OK; } bool Contexthub::isInitialized() { return (mInitCheck == OK && mContextHubModule != nullptr); } IContexthub *HIDL_FETCH_IContexthub(const char * halName) { ALOGI("%s Called for %s", __FUNCTION__, halName); Contexthub *contexthub = new Contexthub; if (!contexthub->isInitialized()) { delete contexthub; contexthub = nullptr; } return contexthub; } } // namespace implementation } // namespace V1_0 } // namespace contexthub } // namespace hardware } // namespace android