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