| /* |
| * 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 <inttypes.h> |
| |
| #include <log/log.h> |
| |
| #include <android/hardware/contexthub/1.0/IContexthub.h> |
| #include <hardware/context_hub.h> |
| #include <sys/endian.h> |
| |
| #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<const context_hub_module_t *>(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<void> Contexthub::getHubs(getHubs_cb _hidl_cb) { |
| std::vector<ContextHub> 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<context_hub_module_t *>(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> contexthub) |
| : mContexthub(contexthub) {} |
| |
| void Contexthub::DeathRecipient::serviceDied( |
| uint64_t cookie, |
| const wp<::android::hidl::base::V1_0::IBase>& /*who*/) { |
| uint32_t hubId = static_cast<uint32_t>(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<IContexthubCallback> Contexthub::getCallBackForHubId(uint32_t hubId) { |
| if (!isValidHubId(hubId)) { |
| return nullptr; |
| } else { |
| return mCachedHubInfo[hubId].callback; |
| } |
| } |
| |
| Return<Result> 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<uint32_t>(msg.msg.size()), // Note the check above |
| .message = static_cast<const uint8_t *>(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<Result> 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<Result> Contexthub::registerCallback(uint32_t hubId, |
| const sp<IContexthubCallback> &cb) { |
| Return<Result> 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<bool> linkResult = cb->linkToDeath(mDeathRecipient, hubId); |
| bool linkSuccess = linkResult.isOk() ? |
| static_cast<bool>(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<IContexthubCallback> 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<HubAppInfo> apps; |
| int numApps = msgLen / sizeof(hub_app_info); |
| const hub_app_info *unalignedInfoAddr = reinterpret_cast<const hub_app_info *>(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<Contexthub *>(cookie); |
| |
| if (rxMsg == nullptr) { |
| ALOGW("Ignoring NULL message"); |
| return -1; |
| } |
| |
| if (!obj->isValidHubId(hubId)) { |
| ALOGW("Invalid hub Id %" PRIu32, hubId); |
| return -1; |
| } |
| |
| sp<IContexthubCallback> 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<const uint8_t *>(rxMsg->message), |
| rxMsg->message_len); |
| } else { |
| ContextHubMsg msg; |
| |
| msg.appName = rxMsg->app_name.id; |
| msg.msgType = rxMsg->message_type; |
| msg.hostEndPoint = static_cast<uint16_t>(HostEndPoint::BROADCAST); |
| msg.msg = std::vector<uint8_t>(static_cast<const uint8_t *>(rxMsg->message), |
| static_cast<const uint8_t *>(rxMsg->message) + |
| rxMsg->message_len); |
| |
| cb->handleClientMsg(msg); |
| } |
| |
| return 0; |
| } |
| |
| Return<Result> 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<Result> 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<const uint8_t *>(&header); |
| |
| std::vector<uint8_t> 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<Result> 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<Result> 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<Result> 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 |