/* * Copyright (C) 2017 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. */ #define LOG_TAG "ContextHubHal" #define LOG_NDEBUG 0 #include "generic_context_hub.h" #include <chrono> #include <cinttypes> #include <vector> #include <utils/Log.h> namespace android { namespace hardware { namespace contexthub { namespace V1_0 { namespace implementation { using ::android::hardware::Return; using ::android::hardware::contexthub::V1_0::AsyncEventType; using ::android::hardware::contexthub::V1_0::Result; using ::android::hardware::contexthub::V1_0::TransactionResult; using ::android::chre::HostProtocolHost; using ::flatbuffers::FlatBufferBuilder; // Aliased for consistency with the way these symbols are referenced in // CHRE-side code namespace fbs = ::chre::fbs; namespace { constexpr uint32_t kDefaultHubId = 0; // TODO: remove this macro once all methods are implemented #define UNUSED(param) (void) (param) constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) { return static_cast<uint8_t>(chreVersion >> 24); } constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) { return static_cast<uint8_t>(chreVersion >> 16); } constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) { return static_cast<uint16_t>(chreVersion); } } // anonymous namespace GenericContextHub::GenericContextHub() { constexpr char kChreSocketName[] = "chre"; mSocketCallbacks = new SocketCallbacks(*this); if (!mClient.connectInBackground(kChreSocketName, mSocketCallbacks)) { ALOGE("Couldn't start socket client"); } } Return<void> GenericContextHub::getHubs(getHubs_cb _hidl_cb) { constexpr auto kHubInfoQueryTimeout = std::chrono::seconds(5); std::vector<ContextHub> hubs; ALOGV("%s", __func__); // If we're not connected yet, give it some time int maxSleepIterations = 50; while (!mHubInfoValid && !mClient.isConnected() && --maxSleepIterations > 0) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } if (!mClient.isConnected()) { ALOGE("Couldn't connect to hub daemon"); } else if (!mHubInfoValid) { // We haven't cached the hub details yet, so send a request and block // waiting on a response std::unique_lock<std::mutex> lock(mHubInfoMutex); FlatBufferBuilder builder; HostProtocolHost::encodeHubInfoRequest(builder); ALOGD("Sending hub info request"); if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) { ALOGE("Couldn't send hub info request"); } else { mHubInfoCond.wait_for(lock, kHubInfoQueryTimeout, [this]() { return mHubInfoValid; }); } } if (mHubInfoValid) { hubs.push_back(mHubInfo); } else { ALOGE("Unable to get hub info from CHRE"); } _hidl_cb(hubs); return Void(); } Return<Result> GenericContextHub::registerCallback( uint32_t hubId, const sp<IContexthubCallback>& cb) { Result result; ALOGV("%s", __func__); // TODO: currently we only support 1 hub behind this HAL implementation if (hubId == kDefaultHubId) { mCallbacks = cb; // TODO: special handling for null? result = Result::OK; } else { result = Result::BAD_PARAMS; } return result; } Return<Result> GenericContextHub::sendMessageToHub(uint32_t hubId, const ContextHubMsg& msg) { Result result; ALOGV("%s", __func__); if (hubId != kDefaultHubId) { result = Result::BAD_PARAMS; } else { FlatBufferBuilder builder(1024); HostProtocolHost::encodeNanoappMessage( builder, msg.appName, msg.msgType, msg.hostEndPoint, msg.msg.data(), msg.msg.size()); if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) { result = Result::UNKNOWN_FAILURE; } else { result = Result::OK; } } return result; } Return<Result> GenericContextHub::loadNanoApp( uint32_t hubId, const NanoAppBinary& appBinary, uint32_t transactionId) { Result result; ALOGV("%s", __func__); if (hubId != kDefaultHubId) { result = Result::BAD_PARAMS; } else { FlatBufferBuilder builder(128 + appBinary.customBinary.size()); uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) | (appBinary.targetChreApiMinorVersion << 16); HostProtocolHost::encodeLoadNanoappRequest( builder, transactionId, appBinary.appId, appBinary.appVersion, targetApiVersion, appBinary.customBinary); if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) { result = Result::UNKNOWN_FAILURE; } else { result = Result::OK; } } ALOGD("Attempted to send load nanoapp request for app of size %zu with ID " "0x%016" PRIx64 " as transaction ID %" PRIu32 ": result %" PRIu32, appBinary.customBinary.size(), appBinary.appId, transactionId, result); return result; } Return<Result> GenericContextHub::unloadNanoApp( uint32_t hubId, uint64_t appId, uint32_t transactionId) { // TODO UNUSED(hubId); UNUSED(appId); UNUSED(transactionId); ALOGV("%s", __func__); return Result::UNKNOWN_FAILURE; } Return<Result> GenericContextHub::enableNanoApp( uint32_t hubId, uint64_t appId, uint32_t transactionId) { // TODO UNUSED(hubId); UNUSED(appId); UNUSED(transactionId); ALOGV("%s", __func__); return Result::UNKNOWN_FAILURE; } Return<Result> GenericContextHub::disableNanoApp( uint32_t hubId, uint64_t appId, uint32_t transactionId) { // TODO UNUSED(hubId); UNUSED(appId); UNUSED(transactionId); ALOGV("%s", __func__); return Result::UNKNOWN_FAILURE; } Return<Result> GenericContextHub::queryApps(uint32_t hubId) { Result result; ALOGV("%s", __func__); if (hubId != kDefaultHubId) { result = Result::BAD_PARAMS; } else { FlatBufferBuilder builder(64); HostProtocolHost::encodeNanoappListRequest(builder); if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) { result = Result::UNKNOWN_FAILURE; } else { result = Result::OK; } } return Result::UNKNOWN_FAILURE; } GenericContextHub::SocketCallbacks::SocketCallbacks(GenericContextHub& parent) : mParent(parent) {} void GenericContextHub::SocketCallbacks::onMessageReceived(const void *data, size_t length) { if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) { ALOGE("Failed to decode message"); } } void GenericContextHub::SocketCallbacks::onConnected() { if (mHaveConnected) { ALOGI("Reconnected to CHRE daemon"); if (mParent.mCallbacks != nullptr) { mParent.mCallbacks->handleHubEvent(AsyncEventType::RESTARTED); } } mHaveConnected = true; } void GenericContextHub::SocketCallbacks::onDisconnected() { ALOGW("Lost connection to CHRE daemon"); } void GenericContextHub::SocketCallbacks::handleNanoappMessage( uint64_t appId, uint32_t messageType, uint16_t hostEndpoint, const void *messageData, size_t messageDataLen) { // TODO: this is not thread-safe w/registerCallback... we need something else // to confirm that it's safe for us to invoke the callback, and likely a lock // on stuff if (mParent.mCallbacks != nullptr) { ContextHubMsg msg; msg.appName = appId; msg.hostEndPoint = hostEndpoint; msg.msgType = messageType; // Dropping const from messageData when we wrap it in hidl_vec here. This is // safe because we won't modify it here, and the ContextHubMsg we pass to // the callback is const. msg.msg.setToExternal( const_cast<uint8_t *>(static_cast<const uint8_t *>(messageData)), messageDataLen, false /* shouldOwn */); mParent.mCallbacks->handleClientMsg(msg); } } void GenericContextHub::SocketCallbacks::handleHubInfoResponse( const char *name, const char *vendor, const char *toolchain, uint32_t legacyPlatformVersion, uint32_t legacyToolchainVersion, float peakMips, float stoppedPower, float sleepPower, float peakPower, uint32_t maxMessageLen, uint64_t platformId, uint32_t version) { ALOGD("Got hub info response"); std::lock_guard<std::mutex> lock(mParent.mHubInfoMutex); if (mParent.mHubInfoValid) { ALOGI("Ignoring duplicate/unsolicited hub info response"); } else { mParent.mHubInfo.name = name; mParent.mHubInfo.vendor = vendor; mParent.mHubInfo.toolchain = toolchain; mParent.mHubInfo.platformVersion = legacyPlatformVersion; mParent.mHubInfo.toolchainVersion = legacyToolchainVersion; mParent.mHubInfo.hubId = kDefaultHubId; mParent.mHubInfo.peakMips = peakMips; mParent.mHubInfo.stoppedPowerDrawMw = stoppedPower; mParent.mHubInfo.sleepPowerDrawMw = sleepPower; mParent.mHubInfo.peakPowerDrawMw = peakPower; mParent.mHubInfo.maxSupportedMsgLen = maxMessageLen; mParent.mHubInfo.chrePlatformId = platformId; mParent.mHubInfo.chreApiMajorVersion = extractChreApiMajorVersion(version); mParent.mHubInfo.chreApiMinorVersion = extractChreApiMinorVersion(version); mParent.mHubInfo.chrePatchVersion = extractChrePatchVersion(version); mParent.mHubInfoValid = true; mParent.mHubInfoCond.notify_all(); } } void GenericContextHub::SocketCallbacks::handleNanoappListResponse( const fbs::NanoappListResponseT& response) { std::vector<HubAppInfo> appInfoList; ALOGV("Got nanoapp list response with %zu apps", response.nanoapps.size()); for (const std::unique_ptr<fbs::NanoappListEntryT>& nanoapp : response.nanoapps) { // TODO: determine if this is really required, and if so, have // HostProtocolHost strip out null entries as part of decode if (nanoapp == nullptr) { continue; } ALOGV("App 0x%016" PRIx64 " ver 0x%" PRIx32 " enabled %d system %d", nanoapp->app_id, nanoapp->version, nanoapp->enabled, nanoapp->is_system); if (!nanoapp->is_system) { HubAppInfo appInfo; appInfo.appId = nanoapp->app_id; appInfo.version = nanoapp->version; appInfo.enabled = nanoapp->enabled; appInfoList.push_back(appInfo); } } // TODO: make this thread-safe w/setCallback mParent.mCallbacks->handleAppsInfo(appInfoList); } void GenericContextHub::SocketCallbacks::handleLoadNanoappResponse( const ::chre::fbs::LoadNanoappResponseT& response) { ALOGV("Got load nanoapp response for transaction %" PRIu32 " with result %d", response.transaction_id, response.success); TransactionResult result = (response.success) ? TransactionResult::SUCCESS : TransactionResult::FAILURE; mParent.mCallbacks->handleTxnResult(response.transaction_id, result); } IContexthub* HIDL_FETCH_IContexthub(const char* /* name */) { return new GenericContextHub(); } } // namespace implementation } // namespace V1_0 } // namespace contexthub } // namespace hardware } // namespace android