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