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 
Contexthub()50 Contexthub::Contexthub()
51         : mDeathRecipient(new DeathRecipient(this)),
52           mIsTransactionPending(false) {
53 }
54 
setOsAppAsDestination(hub_message_t * msg,int hubId)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 
getHubs(getHubs_cb _hidl_cb)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 
DeathRecipient(sp<Contexthub> contexthub)104 Contexthub::DeathRecipient::DeathRecipient(sp<Contexthub> contexthub)
105         : mContexthub(contexthub) {}
106 
serviceDied(uint64_t cookie,const wp<::android::hidl::base::V1_0::IBase> &)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 
isValidHubId(uint32_t hubId)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 
getCallBackForHubId(uint32_t hubId)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 
sendMessageToHub(uint32_t hubId,const ContextHubMsg & msg)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 
registerCallback(uint32_t hubId,const sp<IContexthubCallback> & cb)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 
isValidOsStatus(const uint8_t * msg,size_t msgLen,status_response_t * rsp)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 
handleOsMessage(sp<IContexthubCallback> cb,uint32_t msgType,const uint8_t * msg,int msgLen,uint32_t transactionId)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 
handleServiceDeath(uint32_t hubId)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 
contextHubCb(uint32_t hubId,const nanohub::HubMessage & rxMsg,void * cookie)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 
unloadNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)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 
loadNanoApp(uint32_t hubId,const NanoAppBinary & appBinary,uint32_t transactionId)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 
enableNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)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 
disableNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)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 
queryApps(uint32_t hubId)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 
HIDL_FETCH_IContexthub(const char *)520 IContexthub *HIDL_FETCH_IContexthub(const char *) {
521     return new Contexthub();
522 }
523 
readApp(const char * file,NanoAppBinary * appBinary)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 
debug(const hidl_handle & hh_fd,const hidl_vec<hidl_string> & hh_data)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