| /* |
| * Copyright (C) 2018 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. |
| */ |
| |
| /* |
| * This file is based on: |
| * hardware/interfaces/contexthub/1.0/default/Contexthub.cpp |
| * with modifications to connect directly to the NanohubHAL and |
| * support endpoints. |
| */ |
| |
| #include "NanohubHidlAdapter.h" |
| #include "nanohub_perdevice.h" |
| |
| #include <inttypes.h> |
| |
| #include <log/log.h> |
| #include <utils/String8.h> |
| #include <sys/stat.h> |
| |
| #include <android/hardware/contexthub/1.0/IContexthub.h> |
| #include <hardware/context_hub.h> |
| #include <sys/endian.h> |
| |
| #undef LOG_TAG |
| #define LOG_TAG "NanohubHidlAdapter" |
| |
| using namespace android::nanohub; |
| |
| namespace android { |
| namespace hardware { |
| namespace contexthub { |
| namespace V1_0 { |
| namespace implementation { |
| |
| static constexpr uint64_t ALL_APPS = UINT64_C(0xFFFFFFFFFFFFFFFF); |
| |
| Contexthub::Contexthub() |
| : mDeathRecipient(new DeathRecipient(this)), |
| mIsTransactionPending(false) { |
| } |
| |
| 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; |
| const context_hub_t *hub = nanohub::get_hub_info(); |
| |
| mCachedHubInfo.clear(); |
| |
| CachedHubInformation info; |
| ContextHub c; |
| |
| c.name = hub->name; |
| c.vendor = hub->vendor; |
| c.toolchain = hub->toolchain; |
| c.platformVersion = hub->platform_version; |
| c.toolchainVersion = hub->toolchain_version; |
| c.hubId = hub->hub_id; |
| c.peakMips = hub->peak_mips; |
| c.stoppedPowerDrawMw = hub->stopped_power_draw_mw; |
| c.sleepPowerDrawMw = hub->sleep_power_draw_mw; |
| c.peakPowerDrawMw = hub->peak_power_draw_mw; |
| // c.connectedSensors = |
| c.maxSupportedMsgLen = hub->max_supported_msg_len; |
| // TODO: get this information from nanohub |
| c.chrePlatformId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0); |
| c.chreApiMajorVersion = 0x01; |
| c.chreApiMinorVersion = 0x02; |
| c.chrePatchVersion = NANOHUB_OS_PATCH_LEVEL; |
| |
| info.callback = nullptr; |
| info.osAppName = hub->os_app_name; |
| mCachedHubInfo[hub->hub_id] = info; |
| |
| hubs.push_back(c); |
| |
| _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 (!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 dummy to prevent send_message with empty message from failing prematurely |
| static uint8_t dummy; |
| if (txMsg.message_len == 0 && txMsg.message == nullptr) { |
| txMsg.message = &dummy; |
| } |
| |
| ALOGI("Sending msg of type %" PRIu32 ", size %" PRIu32 " to app 0x%" PRIx64, |
| txMsg.message_type, |
| txMsg.message_len, |
| txMsg.app_name.id); |
| |
| if(NanoHub::sendToNanohub(hubId, &txMsg, 0, msg.hostEndPoint) != 0) { |
| return Result::TRANSACTION_FAILED; |
| } |
| |
| return Result::OK; |
| } |
| |
| Return<Result> Contexthub::registerCallback(uint32_t hubId, |
| const sp<IContexthubCallback> &cb) { |
| Return<Result> retVal = Result::BAD_PARAMS; |
| |
| if (!isValidHubId(hubId)) { |
| // Initialized, but hubId is not valid |
| retVal = Result::BAD_PARAMS; |
| } else if (NanoHub::subscribeMessages(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 sanity checks on return values |
| return true; |
| } |
| |
| int Contexthub::handleOsMessage(sp<IContexthubCallback> cb, |
| uint32_t msgType, |
| const uint8_t *msg, |
| int msgLen, |
| uint32_t transactionId) { |
| 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(transactionId, 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 = NanoHub::subscribeMessages(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 nanohub::HubMessage &rxMsg, |
| void *cookie) { |
| Contexthub *obj = static_cast<Contexthub *>(cookie); |
| |
| 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, |
| rxMsg.message_transaction_id); |
| } else { |
| ContextHubMsg msg; |
| |
| msg.appName = rxMsg.app_name.id; |
| msg.msgType = rxMsg.message_type; |
| msg.hostEndPoint = rxMsg.message_endpoint; |
| 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 (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(NanoHub::sendToNanohub(hubId, |
| &msg, |
| transactionId, |
| static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { |
| return Result::TRANSACTION_FAILED; |
| } else { |
| mIsTransactionPending = true; |
| return Result::OK; |
| } |
| } |
| |
| Return<Result> Contexthub::loadNanoApp(uint32_t hubId, |
| const NanoAppBinary& appBinary, |
| uint32_t transactionId) { |
| 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(NanoHub::sendToNanohub(hubId, |
| &hubMsg, |
| transactionId, |
| static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { |
| return Result::TRANSACTION_FAILED; |
| } else { |
| mIsTransactionPending = true; |
| return Result::OK; |
| } |
| } |
| |
| Return<Result> Contexthub::enableNanoApp(uint32_t hubId, |
| uint64_t appId, |
| uint32_t transactionId) { |
| 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(NanoHub::sendToNanohub(hubId, |
| &msg, |
| transactionId, |
| static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { |
| return Result::TRANSACTION_FAILED; |
| } else { |
| mIsTransactionPending = true; |
| return Result::OK; |
| } |
| } |
| |
| Return<Result> Contexthub::disableNanoApp(uint32_t hubId, |
| uint64_t appId, |
| uint32_t transactionId) { |
| 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(NanoHub::sendToNanohub(hubId, |
| &msg, |
| transactionId, |
| static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { |
| return Result::TRANSACTION_FAILED; |
| } else { |
| mIsTransactionPending = true; |
| return Result::OK; |
| } |
| } |
| |
| Return<Result> Contexthub::queryApps(uint32_t hubId) { |
| 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(NanoHub::sendToNanohub(hubId, |
| &msg, |
| 0, |
| static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) { |
| ALOGW("Query Apps sendMessage failed"); |
| return Result::TRANSACTION_FAILED; |
| } |
| |
| return Result::OK; |
| } |
| |
| IContexthub *HIDL_FETCH_IContexthub(const char *) { |
| return new Contexthub(); |
| } |
| |
| static bool readApp(const char *file, NanoAppBinary *appBinary) |
| { |
| bool success = false; |
| int fd = open(file, O_RDONLY); |
| |
| if (fd >= 0) { |
| struct stat sb; |
| if (fstat(fd, &sb) == 0) { |
| void *buf = malloc(sb.st_size); |
| if (buf != nullptr && read(fd, buf, sb.st_size) == sb.st_size) { |
| success = true; |
| const struct nano_app_binary_t *header = static_cast<const struct nano_app_binary_t *>(buf); |
| appBinary->appId = header->app_id.id; |
| appBinary->appVersion = header->app_version; |
| appBinary->flags = header->flags; |
| appBinary->targetChreApiMajorVersion = header->target_chre_api_major_version; |
| appBinary->targetChreApiMinorVersion = header->target_chre_api_minor_version; |
| appBinary->customBinary = std::vector<uint8_t>(static_cast<const uint8_t *>(buf) + sizeof(struct nano_app_binary_t), static_cast<const uint8_t *>(buf) + sb.st_size); |
| } |
| free(buf); |
| } |
| close(fd); |
| } |
| return success; |
| } |
| |
| Return<void> Contexthub::debug(const hidl_handle& hh_fd, |
| const hidl_vec<hidl_string>& hh_data) { |
| if (hh_fd == nullptr || hh_fd->numFds < 1) { |
| return Void(); |
| } |
| |
| String8 result; |
| int fd = hh_fd.getNativeHandle()->data[0]; |
| |
| if (hh_data.size() == 0) { |
| result.appendFormat("debug: %d\n", NanoHub::getDebugFlags()); |
| std::string appInfo; |
| NanoHub::dumpAppInfo(appInfo); |
| result.append(appInfo.c_str()); |
| } else if (hh_data.size() == 1) { |
| NanoHub::setDebugFlags(atoi(hh_data[0].c_str())); |
| result.appendFormat("debug: %d\n", NanoHub::getDebugFlags()); |
| } else if (hh_data.size() == 2) { |
| if (strncmp(hh_data[0].c_str(), "load", 4) == 0) { |
| NanoAppBinary appBinary; |
| if (readApp(hh_data[1].c_str(), &appBinary)) |
| loadNanoApp(0, appBinary, 0); |
| } else if (strncmp(hh_data[0].c_str(), "unload", 6) == 0) { |
| unloadNanoApp(0, strtoul(hh_data[1].c_str(), NULL, 16), 0); |
| } else if (strncmp(hh_data[0].c_str(), "enable", 6) == 0) { |
| enableNanoApp(0, strtoul(hh_data[1].c_str(), NULL, 16), 0); |
| } else if (strncmp(hh_data[0].c_str(), "disable", 7) == 0) { |
| disableNanoApp(0, strtoul(hh_data[1].c_str(), NULL, 16), 0); |
| } |
| } else { |
| result.appendFormat("unknown debug options"); |
| } |
| write(fd, result.string(), result.size()); |
| |
| return Void(); |
| } |
| |
| } // namespace implementation |
| } // namespace V1_0 |
| } // namespace contexthub |
| } // namespace hardware |
| } // namespace android |