/* * 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 #include #include #include #include #include #include #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 Contexthub::getHubs(getHubs_cb _hidl_cb) { std::vector 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) : 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 (!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 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 Contexthub::registerCallback(uint32_t hubId, const sp &cb) { Return 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 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 sanity checks on return values return true; } int Contexthub::handleOsMessage(sp 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 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 = 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(cookie); 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, 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(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 (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(HostEndPoint::UNSPECIFIED)) != 0) { return Result::TRANSACTION_FAILED; } else { mIsTransactionPending = true; return Result::OK; } } Return 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(&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(NanoHub::sendToNanohub(hubId, &hubMsg, transactionId, static_cast(HostEndPoint::UNSPECIFIED)) != 0) { return Result::TRANSACTION_FAILED; } else { mIsTransactionPending = true; return Result::OK; } } Return 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(HostEndPoint::UNSPECIFIED)) != 0) { return Result::TRANSACTION_FAILED; } else { mIsTransactionPending = true; return Result::OK; } } Return 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(HostEndPoint::UNSPECIFIED)) != 0) { return Result::TRANSACTION_FAILED; } else { mIsTransactionPending = true; return Result::OK; } } Return 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(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(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(static_cast(buf) + sizeof(struct nano_app_binary_t), static_cast(buf) + sb.st_size); } free(buf); } close(fd); } return success; } Return Contexthub::debug(const hidl_handle& hh_fd, const hidl_vec& 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