Home | History | Annotate | Download | only in contexthubhal
      1 /*
      2  * Copyright (C) 2018 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  /*
     18   * This file is based on:
     19   * hardware/interfaces/contexthub/1.0/default/Contexthub.cpp
     20   * with modifications to connect directly to the NanohubHAL and
     21   * support endpoints.
     22   */
     23 
     24 #include "NanohubHidlAdapter.h"
     25 #include "nanohub_perdevice.h"
     26 
     27 #include <inttypes.h>
     28 
     29 #include <log/log.h>
     30 #include <utils/String8.h>
     31 #include <sys/stat.h>
     32 
     33 #include <android/hardware/contexthub/1.0/IContexthub.h>
     34 #include <hardware/context_hub.h>
     35 #include <sys/endian.h>
     36 
     37 #undef LOG_TAG
     38 #define LOG_TAG "NanohubHidlAdapter"
     39 
     40 using namespace android::nanohub;
     41 
     42 namespace android {
     43 namespace hardware {
     44 namespace contexthub {
     45 namespace V1_0 {
     46 namespace implementation {
     47 
     48 static constexpr uint64_t ALL_APPS = UINT64_C(0xFFFFFFFFFFFFFFFF);
     49 
     50 Contexthub::Contexthub()
     51         : mDeathRecipient(new DeathRecipient(this)),
     52           mIsTransactionPending(false) {
     53 }
     54 
     55 bool Contexthub::setOsAppAsDestination(hub_message_t *msg, int hubId) {
     56     if (!isValidHubId(hubId)) {
     57         ALOGW("%s: Hub information is null for hubHandle %d",
     58               __FUNCTION__,
     59               hubId);
     60         return false;
     61     } else {
     62         msg->app_name = mCachedHubInfo[hubId].osAppName;
     63         return true;
     64     }
     65 }
     66 
     67 Return<void> Contexthub::getHubs(getHubs_cb _hidl_cb) {
     68     std::vector<ContextHub> hubs;
     69     const context_hub_t *hub = nanohub::get_hub_info();
     70 
     71     mCachedHubInfo.clear();
     72 
     73     CachedHubInformation info;
     74     ContextHub c;
     75 
     76     c.name = hub->name;
     77     c.vendor = hub->vendor;
     78     c.toolchain = hub->toolchain;
     79     c.platformVersion = hub->platform_version;
     80     c.toolchainVersion = hub->toolchain_version;
     81     c.hubId = hub->hub_id;
     82     c.peakMips = hub->peak_mips;
     83     c.stoppedPowerDrawMw = hub->stopped_power_draw_mw;
     84     c.sleepPowerDrawMw = hub->sleep_power_draw_mw;
     85     c.peakPowerDrawMw = hub->peak_power_draw_mw;
     86     // c.connectedSensors =
     87     c.maxSupportedMsgLen = hub->max_supported_msg_len;
     88     // TODO: get this information from nanohub
     89     c.chrePlatformId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0);
     90     c.chreApiMajorVersion = 0x01;
     91     c.chreApiMinorVersion = 0x02;
     92     c.chrePatchVersion = NANOHUB_OS_PATCH_LEVEL;
     93 
     94     info.callback = nullptr;
     95     info.osAppName = hub->os_app_name;
     96     mCachedHubInfo[hub->hub_id] = info;
     97 
     98     hubs.push_back(c);
     99 
    100     _hidl_cb(hubs);
    101     return Void();
    102 }
    103 
    104 Contexthub::DeathRecipient::DeathRecipient(sp<Contexthub> contexthub)
    105         : mContexthub(contexthub) {}
    106 
    107 void Contexthub::DeathRecipient::serviceDied(
    108         uint64_t cookie,
    109         const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
    110     uint32_t hubId = static_cast<uint32_t>(cookie);
    111     mContexthub->handleServiceDeath(hubId);
    112 }
    113 
    114 bool Contexthub::isValidHubId(uint32_t hubId) {
    115     if (!mCachedHubInfo.count(hubId)) {
    116         ALOGW("Hub information not found for hubId %" PRIu32, hubId);
    117         return false;
    118     } else {
    119         return true;
    120     }
    121 }
    122 
    123 sp<IContexthubCallback> Contexthub::getCallBackForHubId(uint32_t hubId) {
    124     if (!isValidHubId(hubId)) {
    125         return nullptr;
    126     } else {
    127         return mCachedHubInfo[hubId].callback;
    128     }
    129 }
    130 
    131 Return<Result> Contexthub::sendMessageToHub(uint32_t hubId,
    132                                             const ContextHubMsg &msg) {
    133     if (!isValidHubId(hubId) || msg.msg.size() > UINT32_MAX) {
    134         return Result::BAD_PARAMS;
    135     }
    136 
    137     hub_message_t txMsg = {
    138         .app_name.id = msg.appName,
    139         .message_type = msg.msgType,
    140         .message_len = static_cast<uint32_t>(msg.msg.size()), // Note the check above
    141         .message = static_cast<const uint8_t *>(msg.msg.data()),
    142     };
    143 
    144     // Use a dummy to prevent send_message with empty message from failing prematurely
    145     static uint8_t dummy;
    146     if (txMsg.message_len == 0 && txMsg.message == nullptr) {
    147         txMsg.message = &dummy;
    148     }
    149 
    150     ALOGI("Sending msg of type %" PRIu32 ", size %" PRIu32 " to app 0x%" PRIx64,
    151           txMsg.message_type,
    152           txMsg.message_len,
    153           txMsg.app_name.id);
    154 
    155     if(NanoHub::sendToNanohub(hubId, &txMsg, 0, msg.hostEndPoint) != 0) {
    156         return Result::TRANSACTION_FAILED;
    157     }
    158 
    159     return Result::OK;
    160 }
    161 
    162 Return<Result> Contexthub::registerCallback(uint32_t hubId,
    163                                             const sp<IContexthubCallback> &cb) {
    164     Return<Result> retVal = Result::BAD_PARAMS;
    165 
    166     if (!isValidHubId(hubId)) {
    167         // Initialized, but hubId is  not valid
    168         retVal = Result::BAD_PARAMS;
    169     } else if (NanoHub::subscribeMessages(hubId,
    170                                           contextHubCb,
    171                                           this) == 0) {
    172         // Initialized && valid hub && subscription successful
    173         if (mCachedHubInfo[hubId].callback != nullptr) {
    174             ALOGD("Modifying callback for hubId %" PRIu32, hubId);
    175             mCachedHubInfo[hubId].callback->unlinkToDeath(mDeathRecipient);
    176         }
    177 
    178         mCachedHubInfo[hubId].callback = cb;
    179         if (cb != nullptr) {
    180             Return<bool> linkResult = cb->linkToDeath(mDeathRecipient, hubId);
    181             bool linkSuccess = linkResult.isOk() ?
    182                 static_cast<bool>(linkResult) : false;
    183             if (!linkSuccess) {
    184                 ALOGW("Couldn't link death recipient for hubId %" PRIu32,
    185                       hubId);
    186             }
    187         }
    188         retVal = Result::OK;
    189     } else {
    190         // Initalized && valid hubId - but subscription unsuccessful
    191         // This is likely an internal error in the HAL implementation, but we
    192         // cannot add more information.
    193         ALOGW("Could not subscribe to the hub for callback");
    194         retVal = Result::UNKNOWN_FAILURE;
    195     }
    196 
    197     return retVal;
    198 }
    199 
    200 static bool isValidOsStatus(const uint8_t *msg,
    201                             size_t msgLen,
    202                             status_response_t *rsp) {
    203     // Workaround a bug in some HALs
    204     if (msgLen == 1) {
    205         rsp->result = msg[0];
    206         return true;
    207     }
    208 
    209     if (msg == nullptr || msgLen != sizeof(*rsp)) {
    210         ALOGI("Received invalid response (is null : %d, size %zu)",
    211               msg == nullptr ? 1 : 0,
    212               msgLen);
    213         return false;
    214     }
    215 
    216     memcpy(rsp, msg, sizeof(*rsp));
    217 
    218     // No sanity checks on return values
    219     return true;
    220 }
    221 
    222 int Contexthub::handleOsMessage(sp<IContexthubCallback> cb,
    223                                 uint32_t msgType,
    224                                 const uint8_t *msg,
    225                                 int msgLen,
    226                                 uint32_t transactionId) {
    227     int retVal = -1;
    228 
    229 
    230     switch(msgType) {
    231         case CONTEXT_HUB_APPS_ENABLE:
    232         case CONTEXT_HUB_APPS_DISABLE:
    233         case CONTEXT_HUB_LOAD_APP:
    234         case CONTEXT_HUB_UNLOAD_APP:
    235         {
    236             struct status_response_t rsp;
    237             TransactionResult result;
    238             if (isValidOsStatus(msg, msgLen, &rsp) && rsp.result == 0) {
    239                 retVal = 0;
    240                 result = TransactionResult::SUCCESS;
    241             } else {
    242                 result = TransactionResult::FAILURE;
    243             }
    244 
    245             mIsTransactionPending = false;
    246             if (cb != nullptr) {
    247                 cb->handleTxnResult(transactionId, result);
    248             }
    249             retVal = 0;
    250             break;
    251         }
    252 
    253         case CONTEXT_HUB_QUERY_APPS:
    254         {
    255             std::vector<HubAppInfo> apps;
    256             int numApps = msgLen / sizeof(hub_app_info);
    257             const hub_app_info *unalignedInfoAddr = reinterpret_cast<const hub_app_info *>(msg);
    258 
    259             for (int i = 0; i < numApps; i++) {
    260                 hub_app_info query_info;
    261                 memcpy(&query_info, &unalignedInfoAddr[i], sizeof(query_info));
    262                 HubAppInfo app;
    263                 app.appId = query_info.app_name.id;
    264                 app.version = query_info.version;
    265                 // TODO :: Add memory ranges
    266 
    267                 apps.push_back(app);
    268             }
    269 
    270             if (cb != nullptr) {
    271                 cb->handleAppsInfo(apps);
    272             }
    273             retVal = 0;
    274             break;
    275         }
    276 
    277         case CONTEXT_HUB_QUERY_MEMORY:
    278         {
    279             // Deferring this use
    280             retVal = 0;
    281             break;
    282         }
    283 
    284         case CONTEXT_HUB_OS_REBOOT:
    285         {
    286             mIsTransactionPending = false;
    287             if (cb != nullptr) {
    288                 cb->handleHubEvent(AsyncEventType::RESTARTED);
    289             }
    290             retVal = 0;
    291             break;
    292         }
    293 
    294         default:
    295         {
    296             retVal = -1;
    297             break;
    298         }
    299       }
    300 
    301       return retVal;
    302 }
    303 
    304 void Contexthub::handleServiceDeath(uint32_t hubId) {
    305     ALOGI("Callback/service died for hubId %" PRIu32, hubId);
    306     int ret = NanoHub::subscribeMessages(hubId, nullptr, nullptr);
    307     if (ret != 0) {
    308         ALOGW("Failed to unregister callback from hubId %" PRIu32 ": %d",
    309               hubId, ret);
    310     }
    311     mCachedHubInfo[hubId].callback.clear();
    312 }
    313 
    314 int Contexthub::contextHubCb(uint32_t hubId,
    315                              const nanohub::HubMessage &rxMsg,
    316                              void *cookie) {
    317     Contexthub *obj = static_cast<Contexthub *>(cookie);
    318 
    319     if (!obj->isValidHubId(hubId)) {
    320         ALOGW("Invalid hub Id %" PRIu32, hubId);
    321         return -1;
    322     }
    323 
    324     sp<IContexthubCallback> cb = obj->getCallBackForHubId(hubId);
    325 
    326     if (cb == nullptr) {
    327         // This should not ever happen
    328         ALOGW("No callback registered, returning");
    329         return -1;
    330     }
    331 
    332     if (rxMsg.message_type < CONTEXT_HUB_TYPE_PRIVATE_MSG_BASE) {
    333         obj->handleOsMessage(cb,
    334                              rxMsg.message_type,
    335                              static_cast<const uint8_t *>(rxMsg.message),
    336                              rxMsg.message_len,
    337                              rxMsg.message_transaction_id);
    338     } else {
    339         ContextHubMsg msg;
    340 
    341         msg.appName = rxMsg.app_name.id;
    342         msg.msgType = rxMsg.message_type;
    343         msg.hostEndPoint = rxMsg.message_endpoint;
    344         msg.msg = std::vector<uint8_t>(static_cast<const uint8_t *>(rxMsg.message),
    345                                        static_cast<const uint8_t *>(rxMsg.message) +
    346                                        rxMsg.message_len);
    347 
    348         cb->handleClientMsg(msg);
    349     }
    350 
    351     return 0;
    352 }
    353 
    354 Return<Result> Contexthub::unloadNanoApp(uint32_t hubId,
    355                                          uint64_t appId,
    356                                          uint32_t transactionId) {
    357     if (mIsTransactionPending) {
    358         return Result::TRANSACTION_PENDING;
    359     }
    360 
    361     hub_message_t msg;
    362 
    363     if (setOsAppAsDestination(&msg, hubId) == false) {
    364         return Result::BAD_PARAMS;
    365     }
    366 
    367     struct apps_disable_request_t req;
    368 
    369     msg.message_type = CONTEXT_HUB_UNLOAD_APP;
    370     msg.message_len = sizeof(req);
    371     msg.message = &req;
    372     req.app_name.id = appId;
    373 
    374     if(NanoHub::sendToNanohub(hubId,
    375                               &msg,
    376                               transactionId,
    377                               static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) {
    378         return Result::TRANSACTION_FAILED;
    379     } else {
    380         mIsTransactionPending = true;
    381         return Result::OK;
    382     }
    383 }
    384 
    385 Return<Result> Contexthub::loadNanoApp(uint32_t hubId,
    386                                        const NanoAppBinary& appBinary,
    387                                        uint32_t transactionId) {
    388     if (mIsTransactionPending) {
    389         return Result::TRANSACTION_PENDING;
    390     }
    391 
    392     hub_message_t hubMsg;
    393 
    394     if (setOsAppAsDestination(&hubMsg, hubId) == false) {
    395         return Result::BAD_PARAMS;
    396     }
    397 
    398     // Data from the nanoapp header is passed through HIDL as explicit fields,
    399     // but the legacy HAL expects it prepended to the binary, therefore we must
    400     // reconstruct it here prior to passing to the legacy HAL.
    401     const struct nano_app_binary_t header = {
    402         .header_version = htole32(1),
    403         .magic = htole32(NANOAPP_MAGIC),
    404         .app_id.id = htole64(appBinary.appId),
    405         .app_version = htole32(appBinary.appVersion),
    406         .flags = htole32(appBinary.flags),
    407         .hw_hub_type = htole64(0),
    408         .target_chre_api_major_version = appBinary.targetChreApiMajorVersion,
    409         .target_chre_api_minor_version = appBinary.targetChreApiMinorVersion,
    410     };
    411     const uint8_t *headerBytes = reinterpret_cast<const uint8_t *>(&header);
    412 
    413     std::vector<uint8_t> binaryWithHeader(appBinary.customBinary);
    414     binaryWithHeader.insert(binaryWithHeader.begin(),
    415                             headerBytes,
    416                             headerBytes + sizeof(header));
    417 
    418     hubMsg.message_type = CONTEXT_HUB_LOAD_APP;
    419     hubMsg.message_len = binaryWithHeader.size();
    420     hubMsg.message = binaryWithHeader.data();
    421 
    422     if(NanoHub::sendToNanohub(hubId,
    423                               &hubMsg,
    424                               transactionId,
    425                               static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) {
    426         return Result::TRANSACTION_FAILED;
    427     } else {
    428         mIsTransactionPending = true;
    429         return Result::OK;
    430     }
    431 }
    432 
    433 Return<Result> Contexthub::enableNanoApp(uint32_t hubId,
    434                                          uint64_t appId,
    435                                          uint32_t transactionId) {
    436     if (mIsTransactionPending) {
    437         return Result::TRANSACTION_PENDING;
    438     }
    439 
    440     hub_message_t msg;
    441 
    442     if (setOsAppAsDestination(&msg, hubId) == false) {
    443         return Result::BAD_PARAMS;
    444     }
    445 
    446     struct apps_enable_request_t req;
    447 
    448     msg.message_type = CONTEXT_HUB_APPS_ENABLE;
    449     msg.message_len = sizeof(req);
    450     req.app_name.id = appId;
    451     msg.message = &req;
    452 
    453     if(NanoHub::sendToNanohub(hubId,
    454                               &msg,
    455                               transactionId,
    456                               static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) {
    457         return Result::TRANSACTION_FAILED;
    458     } else {
    459         mIsTransactionPending = true;
    460         return Result::OK;
    461     }
    462 }
    463 
    464 Return<Result> Contexthub::disableNanoApp(uint32_t hubId,
    465                                           uint64_t appId,
    466                                           uint32_t transactionId) {
    467     if (mIsTransactionPending) {
    468         return Result::TRANSACTION_PENDING;
    469     }
    470 
    471     hub_message_t msg;
    472 
    473     if (setOsAppAsDestination(&msg, hubId) == false) {
    474         return Result::BAD_PARAMS;
    475     }
    476 
    477     struct apps_disable_request_t req;
    478 
    479     msg.message_type = CONTEXT_HUB_APPS_DISABLE;
    480     msg.message_len = sizeof(req);
    481     req.app_name.id = appId;
    482     msg.message = &req;
    483 
    484     if(NanoHub::sendToNanohub(hubId,
    485                               &msg,
    486                               transactionId,
    487                               static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) {
    488         return Result::TRANSACTION_FAILED;
    489     } else {
    490         mIsTransactionPending = true;
    491         return Result::OK;
    492     }
    493 }
    494 
    495 Return<Result> Contexthub::queryApps(uint32_t hubId) {
    496     hub_message_t msg;
    497 
    498     if (setOsAppAsDestination(&msg, hubId) == false) {
    499         ALOGW("Could not find hubId %" PRIu32, hubId);
    500         return Result::BAD_PARAMS;
    501     }
    502 
    503     query_apps_request_t payload;
    504     payload.app_name.id = ALL_APPS; // TODO : Pass this in as a parameter
    505     msg.message = &payload;
    506     msg.message_len = sizeof(payload);
    507     msg.message_type = CONTEXT_HUB_QUERY_APPS;
    508 
    509     if(NanoHub::sendToNanohub(hubId,
    510                               &msg,
    511                               0,
    512                               static_cast<uint16_t>(HostEndPoint::UNSPECIFIED)) != 0) {
    513         ALOGW("Query Apps sendMessage failed");
    514         return Result::TRANSACTION_FAILED;
    515     }
    516 
    517     return Result::OK;
    518 }
    519 
    520 IContexthub *HIDL_FETCH_IContexthub(const char *) {
    521     return new Contexthub();
    522 }
    523 
    524 static bool readApp(const char *file, NanoAppBinary *appBinary)
    525 {
    526     bool success = false;
    527     int fd = open(file, O_RDONLY);
    528 
    529     if (fd >= 0) {
    530         struct stat sb;
    531         if (fstat(fd, &sb) == 0) {
    532             void *buf = malloc(sb.st_size);
    533             if (buf != nullptr && read(fd, buf, sb.st_size) == sb.st_size) {
    534                 success = true;
    535                 const struct nano_app_binary_t *header = static_cast<const struct nano_app_binary_t *>(buf);
    536                 appBinary->appId = header->app_id.id;
    537                 appBinary->appVersion = header->app_version;
    538                 appBinary->flags = header->flags;
    539                 appBinary->targetChreApiMajorVersion = header->target_chre_api_major_version;
    540                 appBinary->targetChreApiMinorVersion = header->target_chre_api_minor_version;
    541                 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);
    542             }
    543             free(buf);
    544         }
    545         close(fd);
    546     }
    547     return success;
    548 }
    549 
    550 Return<void> Contexthub::debug(const hidl_handle& hh_fd,
    551                                const hidl_vec<hidl_string>& hh_data) {
    552     if (hh_fd == nullptr || hh_fd->numFds < 1) {
    553         return Void();
    554     }
    555 
    556     String8 result;
    557     int fd = hh_fd.getNativeHandle()->data[0];
    558 
    559     if (hh_data.size() == 0) {
    560         result.appendFormat("debug: %d\n", NanoHub::getDebugFlags());
    561         std::string appInfo;
    562         NanoHub::dumpAppInfo(appInfo);
    563         result.append(appInfo.c_str());
    564     } else if (hh_data.size() == 1) {
    565         NanoHub::setDebugFlags(atoi(hh_data[0].c_str()));
    566         result.appendFormat("debug: %d\n", NanoHub::getDebugFlags());
    567     } else if (hh_data.size() == 2) {
    568         if (strncmp(hh_data[0].c_str(), "load", 4) == 0) {
    569             NanoAppBinary appBinary;
    570             if (readApp(hh_data[1].c_str(), &appBinary))
    571                 loadNanoApp(0, appBinary, 0);
    572         } else if (strncmp(hh_data[0].c_str(), "unload", 6) == 0) {
    573             unloadNanoApp(0, strtoul(hh_data[1].c_str(), NULL, 16), 0);
    574         } else if (strncmp(hh_data[0].c_str(), "enable", 6) == 0) {
    575             enableNanoApp(0, strtoul(hh_data[1].c_str(), NULL, 16), 0);
    576         } else if (strncmp(hh_data[0].c_str(), "disable", 7) == 0) {
    577             disableNanoApp(0, strtoul(hh_data[1].c_str(), NULL, 16), 0);
    578         }
    579     } else {
    580         result.appendFormat("unknown debug options");
    581     }
    582     write(fd, result.string(), result.size());
    583 
    584     return Void();
    585 }
    586 
    587 }  // namespace implementation
    588 }  // namespace V1_0
    589 }  // namespace contexthub
    590 }  // namespace hardware
    591 }  // namespace android
    592