C++程序  |  592行  |  18.45 KB

/*
 * 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