You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
592 lines
18 KiB
592 lines
18 KiB
/*
|
|
* 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
|