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