Home | History | Annotate | Download | only in hal_generic
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "ContextHubHal"
     18 #define LOG_NDEBUG 0
     19 
     20 #include "generic_context_hub.h"
     21 
     22 #include <chrono>
     23 #include <cinttypes>
     24 #include <vector>
     25 
     26 #include <unistd.h>
     27 #include <utils/Log.h>
     28 
     29 namespace android {
     30 namespace hardware {
     31 namespace contexthub {
     32 namespace V1_0 {
     33 namespace implementation {
     34 
     35 using ::android::hardware::Return;
     36 using ::android::hardware::contexthub::V1_0::AsyncEventType;
     37 using ::android::hardware::contexthub::V1_0::Result;
     38 using ::android::hardware::contexthub::V1_0::TransactionResult;
     39 using ::android::chre::HostProtocolHost;
     40 using ::flatbuffers::FlatBufferBuilder;
     41 
     42 // Aliased for consistency with the way these symbols are referenced in
     43 // CHRE-side code
     44 namespace fbs = ::chre::fbs;
     45 
     46 namespace {
     47 
     48 constexpr uint32_t kDefaultHubId = 0;
     49 
     50 constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) {
     51   return static_cast<uint8_t>(chreVersion >> 24);
     52 }
     53 
     54 constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) {
     55   return static_cast<uint8_t>(chreVersion >> 16);
     56 }
     57 
     58 constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) {
     59   return static_cast<uint16_t>(chreVersion);
     60 }
     61 
     62 /**
     63  * @return file descriptor contained in the hidl_handle, or -1 if there is none
     64  */
     65 int hidlHandleToFileDescriptor(const hidl_handle& hh) {
     66   const native_handle_t *handle = hh.getNativeHandle();
     67   return (handle->numFds >= 1) ? handle->data[0] : -1;
     68 }
     69 
     70 }  // anonymous namespace
     71 
     72 GenericContextHub::DeathRecipient::DeathRecipient(
     73     sp<GenericContextHub> contexthub) : mGenericContextHub(contexthub){}
     74 
     75 void GenericContextHub::DeathRecipient::serviceDied(
     76     uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& /* who */) {
     77   uint32_t hubId = static_cast<uint32_t>(cookie);
     78   mGenericContextHub->handleServiceDeath(hubId);
     79 }
     80 
     81 GenericContextHub::GenericContextHub() {
     82   constexpr char kChreSocketName[] = "chre";
     83 
     84   mSocketCallbacks = new SocketCallbacks(*this);
     85   if (!mClient.connectInBackground(kChreSocketName, mSocketCallbacks)) {
     86     ALOGE("Couldn't start socket client");
     87   }
     88 
     89   mDeathRecipient = new DeathRecipient(this);
     90 }
     91 
     92 Return<void> GenericContextHub::debug(
     93     const hidl_handle& hh_fd, const hidl_vec<hidl_string>& /*options*/) {
     94   // Timeout inside CHRE is typically 5 seconds, grant 500ms extra here to let
     95   // the data reach us
     96   constexpr auto kDebugDumpTimeout = std::chrono::milliseconds(5500);
     97 
     98   mDebugFd = hidlHandleToFileDescriptor(hh_fd);
     99   if (mDebugFd < 0) {
    100     ALOGW("Can't dump debug info to invalid fd");
    101   } else {
    102     writeToDebugFile("-- Dumping CHRE/ASH debug info --\n");
    103 
    104     ALOGV("Sending debug dump request");
    105     FlatBufferBuilder builder;
    106     HostProtocolHost::encodeDebugDumpRequest(builder);
    107     std::unique_lock<std::mutex> lock(mDebugDumpMutex);
    108     mDebugDumpPending = true;
    109     if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
    110       ALOGW("Couldn't send debug dump request");
    111     } else {
    112       mDebugDumpCond.wait_for(lock, kDebugDumpTimeout,
    113                               [this]() { return !mDebugDumpPending; });
    114       if (mDebugDumpPending) {
    115         ALOGI("Timed out waiting on debug dump data");
    116         mDebugDumpPending = false;
    117       }
    118     }
    119     writeToDebugFile("\n-- End of CHRE/ASH debug info --\n");
    120 
    121     mDebugFd = kInvalidFd;
    122     ALOGV("Debug dump complete");
    123   }
    124 
    125   return Void();
    126 }
    127 
    128 Return<void> GenericContextHub::getHubs(getHubs_cb _hidl_cb) {
    129   constexpr auto kHubInfoQueryTimeout = std::chrono::seconds(5);
    130   std::vector<ContextHub> hubs;
    131   ALOGV("%s", __func__);
    132 
    133   // If we're not connected yet, give it some time
    134   // TODO refactor from polling into conditional wait
    135   int maxSleepIterations = 250;
    136   while (!mHubInfoValid && !mClient.isConnected() && --maxSleepIterations > 0) {
    137     std::this_thread::sleep_for(std::chrono::milliseconds(20));
    138   }
    139 
    140   if (!mClient.isConnected()) {
    141     ALOGE("Couldn't connect to hub daemon");
    142   } else if (!mHubInfoValid) {
    143     // We haven't cached the hub details yet, so send a request and block
    144     // waiting on a response
    145     std::unique_lock<std::mutex> lock(mHubInfoMutex);
    146     FlatBufferBuilder builder;
    147     HostProtocolHost::encodeHubInfoRequest(builder);
    148 
    149     ALOGD("Sending hub info request");
    150     if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
    151       ALOGE("Couldn't send hub info request");
    152     } else {
    153       mHubInfoCond.wait_for(lock, kHubInfoQueryTimeout,
    154                             [this]() { return mHubInfoValid; });
    155     }
    156   }
    157 
    158   if (mHubInfoValid) {
    159     hubs.push_back(mHubInfo);
    160   } else {
    161     ALOGE("Unable to get hub info from CHRE");
    162   }
    163 
    164   _hidl_cb(hubs);
    165   return Void();
    166 }
    167 
    168 Return<Result> GenericContextHub::registerCallback(
    169     uint32_t hubId, const sp<IContexthubCallback>& cb) {
    170   Result result;
    171   ALOGV("%s", __func__);
    172 
    173   // TODO: currently we only support 1 hub behind this HAL implementation
    174   if (hubId == kDefaultHubId) {
    175     std::lock_guard<std::mutex> lock(mCallbacksLock);
    176 
    177     if (cb != nullptr) {
    178       if (mCallbacks != nullptr) {
    179         ALOGD("Modifying callback for hubId %" PRIu32, hubId);
    180         mCallbacks->unlinkToDeath(mDeathRecipient);
    181       }
    182       Return<bool> linkReturn = cb->linkToDeath(mDeathRecipient, hubId);
    183       if (!linkReturn.withDefault(false)) {
    184         ALOGW("Could not link death recipient to hubId %" PRIu32, hubId);
    185       }
    186     }
    187 
    188     mCallbacks = cb;
    189     result = Result::OK;
    190   } else {
    191     result = Result::BAD_PARAMS;
    192   }
    193 
    194   return result;
    195 }
    196 
    197 Return<Result> GenericContextHub::sendMessageToHub(uint32_t hubId,
    198                                                    const ContextHubMsg& msg) {
    199   Result result;
    200   ALOGV("%s", __func__);
    201 
    202   if (hubId != kDefaultHubId) {
    203     result = Result::BAD_PARAMS;
    204   } else {
    205     FlatBufferBuilder builder(1024);
    206     HostProtocolHost::encodeNanoappMessage(
    207         builder, msg.appName, msg.msgType, msg.hostEndPoint, msg.msg.data(),
    208         msg.msg.size());
    209 
    210     if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
    211       result = Result::UNKNOWN_FAILURE;
    212     } else {
    213       result = Result::OK;
    214     }
    215   }
    216 
    217   return result;
    218 }
    219 
    220 Return<Result> GenericContextHub::loadNanoApp(
    221     uint32_t hubId, const NanoAppBinary& appBinary, uint32_t transactionId) {
    222   Result result;
    223   ALOGV("%s", __func__);
    224 
    225   if (hubId != kDefaultHubId) {
    226     result = Result::BAD_PARAMS;
    227   } else {
    228     FlatBufferBuilder builder(128 + appBinary.customBinary.size());
    229     uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
    230                                 (appBinary.targetChreApiMinorVersion << 16);
    231     HostProtocolHost::encodeLoadNanoappRequest(
    232         builder, transactionId, appBinary.appId, appBinary.appVersion,
    233         targetApiVersion, appBinary.customBinary);
    234     if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
    235       result = Result::UNKNOWN_FAILURE;
    236     } else {
    237       result = Result::OK;
    238     }
    239   }
    240 
    241   ALOGD("Attempted to send load nanoapp request for app of size %zu with ID "
    242         "0x%016" PRIx64 " as transaction ID %" PRIu32 ": result %" PRIu32,
    243         appBinary.customBinary.size(), appBinary.appId, transactionId, result);
    244 
    245   return result;
    246 }
    247 
    248 Return<Result> GenericContextHub::unloadNanoApp(
    249     uint32_t hubId, uint64_t appId, uint32_t transactionId) {
    250   Result result;
    251   ALOGV("%s", __func__);
    252 
    253   if (hubId != kDefaultHubId) {
    254     result = Result::BAD_PARAMS;
    255   } else {
    256     FlatBufferBuilder builder(64);
    257     HostProtocolHost::encodeUnloadNanoappRequest(
    258         builder, transactionId, appId, false /* allowSystemNanoappUnload */);
    259     if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
    260       result = Result::UNKNOWN_FAILURE;
    261     } else {
    262       result = Result::OK;
    263     }
    264   }
    265 
    266   ALOGD("Attempted to send unload nanoapp request for app ID 0x%016" PRIx64
    267         " as transaction ID %" PRIu32 ": result %" PRIu32, appId, transactionId,
    268         result);
    269 
    270   return result;
    271 }
    272 
    273 Return<Result> GenericContextHub::enableNanoApp(
    274     uint32_t /* hubId */, uint64_t appId, uint32_t /* transactionId */) {
    275   // TODO
    276   ALOGW("Attempted to enable app ID 0x%016" PRIx64 ", but not supported",
    277         appId);
    278   return Result::TRANSACTION_FAILED;
    279 }
    280 
    281 Return<Result> GenericContextHub::disableNanoApp(
    282     uint32_t /* hubId */, uint64_t appId, uint32_t /* transactionId */) {
    283   // TODO
    284   ALOGW("Attempted to disable app ID 0x%016" PRIx64 ", but not supported",
    285         appId);
    286   return Result::TRANSACTION_FAILED;
    287 }
    288 
    289 Return<Result> GenericContextHub::queryApps(uint32_t hubId) {
    290   Result result;
    291   ALOGV("%s", __func__);
    292 
    293   if (hubId != kDefaultHubId) {
    294     result = Result::BAD_PARAMS;
    295   } else {
    296     FlatBufferBuilder builder(64);
    297     HostProtocolHost::encodeNanoappListRequest(builder);
    298     if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
    299       result = Result::UNKNOWN_FAILURE;
    300     } else {
    301       result = Result::OK;
    302     }
    303   }
    304 
    305   return result;
    306 }
    307 
    308 GenericContextHub::SocketCallbacks::SocketCallbacks(GenericContextHub& parent)
    309     : mParent(parent) {}
    310 
    311 void GenericContextHub::SocketCallbacks::onMessageReceived(const void *data,
    312                                                            size_t length) {
    313   if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
    314     ALOGE("Failed to decode message");
    315   }
    316 }
    317 
    318 void GenericContextHub::SocketCallbacks::onConnected() {
    319   if (mHaveConnected) {
    320     ALOGI("Reconnected to CHRE daemon");
    321     invokeClientCallback([&]() {
    322       mParent.mCallbacks->handleHubEvent(AsyncEventType::RESTARTED);
    323     });
    324   }
    325   mHaveConnected = true;
    326 }
    327 
    328 void GenericContextHub::SocketCallbacks::onDisconnected() {
    329   ALOGW("Lost connection to CHRE daemon");
    330 }
    331 
    332 void GenericContextHub::SocketCallbacks::handleNanoappMessage(
    333     uint64_t appId, uint32_t messageType, uint16_t hostEndpoint,
    334     const void *messageData, size_t messageDataLen) {
    335   ContextHubMsg msg;
    336   msg.appName = appId;
    337   msg.hostEndPoint = hostEndpoint;
    338   msg.msgType = messageType;
    339 
    340   // Dropping const from messageData when we wrap it in hidl_vec here. This is
    341   // safe because we won't modify it here, and the ContextHubMsg we pass to
    342   // the callback is const.
    343   msg.msg.setToExternal(
    344       const_cast<uint8_t *>(static_cast<const uint8_t *>(messageData)),
    345       messageDataLen, false /* shouldOwn */);
    346   invokeClientCallback([&]() {
    347     mParent.mCallbacks->handleClientMsg(msg);
    348   });
    349 }
    350 
    351 void GenericContextHub::SocketCallbacks::handleHubInfoResponse(
    352     const char *name, const char *vendor,
    353     const char *toolchain, uint32_t legacyPlatformVersion,
    354     uint32_t legacyToolchainVersion, float peakMips, float stoppedPower,
    355     float sleepPower, float peakPower, uint32_t maxMessageLen,
    356     uint64_t platformId, uint32_t version) {
    357   ALOGD("Got hub info response");
    358 
    359   std::lock_guard<std::mutex> lock(mParent.mHubInfoMutex);
    360   if (mParent.mHubInfoValid) {
    361     ALOGI("Ignoring duplicate/unsolicited hub info response");
    362   } else {
    363     mParent.mHubInfo.name = name;
    364     mParent.mHubInfo.vendor = vendor;
    365     mParent.mHubInfo.toolchain = toolchain;
    366     mParent.mHubInfo.platformVersion = legacyPlatformVersion;
    367     mParent.mHubInfo.toolchainVersion = legacyToolchainVersion;
    368     mParent.mHubInfo.hubId = kDefaultHubId;
    369 
    370     mParent.mHubInfo.peakMips = peakMips;
    371     mParent.mHubInfo.stoppedPowerDrawMw = stoppedPower;
    372     mParent.mHubInfo.sleepPowerDrawMw = sleepPower;
    373     mParent.mHubInfo.peakPowerDrawMw = peakPower;
    374 
    375     mParent.mHubInfo.maxSupportedMsgLen = maxMessageLen;
    376     mParent.mHubInfo.chrePlatformId = platformId;
    377 
    378     mParent.mHubInfo.chreApiMajorVersion = extractChreApiMajorVersion(version);
    379     mParent.mHubInfo.chreApiMinorVersion = extractChreApiMinorVersion(version);
    380     mParent.mHubInfo.chrePatchVersion = extractChrePatchVersion(version);
    381 
    382     mParent.mHubInfoValid = true;
    383     mParent.mHubInfoCond.notify_all();
    384   }
    385 }
    386 
    387 void GenericContextHub::SocketCallbacks::handleNanoappListResponse(
    388     const fbs::NanoappListResponseT& response) {
    389   std::vector<HubAppInfo> appInfoList;
    390 
    391   ALOGV("Got nanoapp list response with %zu apps", response.nanoapps.size());
    392   for (const std::unique_ptr<fbs::NanoappListEntryT>& nanoapp
    393          : response.nanoapps) {
    394     // TODO: determine if this is really required, and if so, have
    395     // HostProtocolHost strip out null entries as part of decode
    396     if (nanoapp == nullptr) {
    397       continue;
    398     }
    399 
    400     ALOGV("App 0x%016" PRIx64 " ver 0x%" PRIx32 " enabled %d system %d",
    401           nanoapp->app_id, nanoapp->version, nanoapp->enabled,
    402           nanoapp->is_system);
    403     if (!nanoapp->is_system) {
    404       HubAppInfo appInfo;
    405 
    406       appInfo.appId = nanoapp->app_id;
    407       appInfo.version = nanoapp->version;
    408       appInfo.enabled = nanoapp->enabled;
    409 
    410       appInfoList.push_back(appInfo);
    411     }
    412   }
    413 
    414   invokeClientCallback([&]() {
    415     mParent.mCallbacks->handleAppsInfo(appInfoList);
    416   });
    417 }
    418 
    419 void GenericContextHub::SocketCallbacks::handleLoadNanoappResponse(
    420     const ::chre::fbs::LoadNanoappResponseT& response) {
    421   ALOGV("Got load nanoapp response for transaction %" PRIu32 " with result %d",
    422         response.transaction_id, response.success);
    423 
    424   invokeClientCallback([&]() {
    425     TransactionResult result = (response.success) ?
    426         TransactionResult::SUCCESS : TransactionResult::FAILURE;
    427     mParent.mCallbacks->handleTxnResult(response.transaction_id, result);
    428   });
    429 }
    430 
    431 void GenericContextHub::SocketCallbacks::handleUnloadNanoappResponse(
    432     const ::chre::fbs::UnloadNanoappResponseT& response) {
    433   ALOGV("Got unload nanoapp response for transaction %" PRIu32 " with result "
    434         "%d", response.transaction_id, response.success);
    435 
    436   invokeClientCallback([&]() {
    437     TransactionResult result = (response.success) ?
    438         TransactionResult::SUCCESS : TransactionResult::FAILURE;
    439     mParent.mCallbacks->handleTxnResult(response.transaction_id, result);
    440   });
    441 }
    442 
    443 void GenericContextHub::SocketCallbacks::handleDebugDumpData(
    444     const ::chre::fbs::DebugDumpDataT& data) {
    445   ALOGV("Got debug dump data, size %zu", data.debug_str.size());
    446   if (mParent.mDebugFd == kInvalidFd) {
    447     ALOGW("Got unexpected debug dump data message");
    448   } else {
    449     mParent.writeToDebugFile(
    450         reinterpret_cast<const char *>(data.debug_str.data()),
    451         data.debug_str.size());
    452   }
    453 }
    454 
    455 void GenericContextHub::SocketCallbacks::handleDebugDumpResponse(
    456     const ::chre::fbs::DebugDumpResponseT& response) {
    457   ALOGV("Got debug dump response, success %d, data count %" PRIu32,
    458         response.success, response.data_count);
    459   std::lock_guard<std::mutex> lock(mParent.mDebugDumpMutex);
    460   if (!mParent.mDebugDumpPending) {
    461     ALOGI("Ignoring duplicate/unsolicited debug dump response");
    462   } else {
    463     mParent.mDebugDumpPending = false;
    464     mParent.mDebugDumpCond.notify_all();
    465   }
    466 }
    467 
    468 void GenericContextHub::SocketCallbacks::invokeClientCallback(
    469     std::function<void()> callback) {
    470   std::lock_guard<std::mutex> lock(mParent.mCallbacksLock);
    471   if (mParent.mCallbacks != nullptr) {
    472     callback();
    473   }
    474 }
    475 
    476 void GenericContextHub::writeToDebugFile(const char *str) {
    477   writeToDebugFile(str, strlen(str));
    478 }
    479 
    480 void GenericContextHub::writeToDebugFile(const char *str, size_t len) {
    481   ssize_t written = write(mDebugFd, str, len);
    482   if (written != (ssize_t) len) {
    483     ALOGW("Couldn't write to debug header: returned %zd, expected %zu "
    484           "(errno %d)", written, len, errno);
    485   }
    486 }
    487 
    488 void GenericContextHub::handleServiceDeath(uint32_t hubId) {
    489   std::lock_guard<std::mutex> lock(mCallbacksLock);
    490   ALOGI("Context hub service died for hubId %" PRIu32, hubId);
    491   mCallbacks.clear();
    492 }
    493 
    494 IContexthub* HIDL_FETCH_IContexthub(const char* /* name */) {
    495   return new GenericContextHub();
    496 }
    497 
    498 }  // namespace implementation
    499 }  // namespace V1_0
    500 }  // namespace contexthub
    501 }  // namespace hardware
    502 }  // namespace android
    503