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 
extractChreApiMajorVersion(uint32_t chreVersion)51 constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) {
52   return static_cast<uint8_t>(chreVersion >> 24);
53 }
54 
extractChreApiMinorVersion(uint32_t chreVersion)55 constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) {
56   return static_cast<uint8_t>(chreVersion >> 16);
57 }
58 
extractChrePatchVersion(uint32_t chreVersion)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  */
hidlHandleToFileDescriptor(const hidl_handle & hh)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 
DeathRecipient(sp<GenericContextHub> contexthub)73 GenericContextHub::DeathRecipient::DeathRecipient(
74     sp<GenericContextHub> contexthub) : mGenericContextHub(contexthub){}
75 
serviceDied(uint64_t cookie,const wp<::android::hidl::base::V1_0::IBase> &)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 
GenericContextHub()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 
debug(const hidl_handle & hh_fd,const hidl_vec<hidl_string> &)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 
getHubs(getHubs_cb _hidl_cb)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 
registerCallback(uint32_t hubId,const sp<IContexthubCallback> & cb)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 
sendMessageToHub(uint32_t hubId,const ContextHubMsg & msg)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 
loadNanoApp(uint32_t hubId,const NanoAppBinary & appBinary,uint32_t transactionId)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 
sendFragmentedLoadNanoAppRequest(FragmentedLoadTransaction & transaction)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 
unloadNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)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 
enableNanoApp(uint32_t,uint64_t appId,uint32_t)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 
disableNanoApp(uint32_t,uint64_t appId,uint32_t)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 
queryApps(uint32_t hubId)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 
SocketCallbacks(GenericContextHub & parent)335 GenericContextHub::SocketCallbacks::SocketCallbacks(GenericContextHub& parent)
336     : mParent(parent) {}
337 
onMessageReceived(const void * data,size_t length)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 
onConnected()345 void GenericContextHub::SocketCallbacks::onConnected() {
346   if (mHaveConnected) {
347     ALOGI("Reconnected to CHRE daemon");
348     invokeClientCallback([&]() {
349       return mParent.mCallbacks->handleHubEvent(AsyncEventType::RESTARTED);
350     });
351   }
352   mHaveConnected = true;
353 }
354 
onDisconnected()355 void GenericContextHub::SocketCallbacks::onDisconnected() {
356   ALOGW("Lost connection to CHRE daemon");
357 }
358 
handleNanoappMessage(const fbs::NanoappMessageT & message)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     return mParent.mCallbacks->handleClientMsg(msg);
369   });
370 }
371 
handleHubInfoResponse(const fbs::HubInfoResponseT & response)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 
handleNanoappListResponse(const fbs::NanoappListResponseT & response)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     return mParent.mCallbacks->handleAppsInfo(appInfoList);
434   });
435 }
436 
handleLoadNanoappResponse(const::chre::fbs::LoadNanoappResponseT & response)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           return mParent.mCallbacks->handleTxnResult(
478               response.transaction_id, result);
479         });
480       }
481     }
482   }
483 }
484 
isExpectedLoadResponseLocked(const::chre::fbs::LoadNanoappResponseT & response)485 bool GenericContextHub::isExpectedLoadResponseLocked(
486     const ::chre::fbs::LoadNanoappResponseT& response) {
487   return mPendingLoadTransaction.has_value()
488       && (mPendingLoadTransaction->getTransactionId()
489           == response.transaction_id)
490       && (response.fragment_id == 0
491           || mCurrentFragmentId == response.fragment_id);
492 }
493 
handleUnloadNanoappResponse(const::chre::fbs::UnloadNanoappResponseT & response)494 void GenericContextHub::SocketCallbacks::handleUnloadNanoappResponse(
495     const ::chre::fbs::UnloadNanoappResponseT& response) {
496   ALOGV("Got unload nanoapp response for transaction %" PRIu32 " with result "
497         "%d", response.transaction_id, response.success);
498 
499   invokeClientCallback([&]() {
500     TransactionResult result = (response.success) ?
501         TransactionResult::SUCCESS : TransactionResult::FAILURE;
502     return mParent.mCallbacks->handleTxnResult(response.transaction_id, result);
503   });
504 }
505 
handleDebugDumpData(const::chre::fbs::DebugDumpDataT & data)506 void GenericContextHub::SocketCallbacks::handleDebugDumpData(
507     const ::chre::fbs::DebugDumpDataT& data) {
508   ALOGV("Got debug dump data, size %zu", data.debug_str.size());
509   if (mParent.mDebugFd == kInvalidFd) {
510     ALOGW("Got unexpected debug dump data message");
511   } else {
512     mParent.writeToDebugFile(
513         reinterpret_cast<const char *>(data.debug_str.data()),
514         data.debug_str.size());
515   }
516 }
517 
handleDebugDumpResponse(const::chre::fbs::DebugDumpResponseT & response)518 void GenericContextHub::SocketCallbacks::handleDebugDumpResponse(
519     const ::chre::fbs::DebugDumpResponseT& response) {
520   ALOGV("Got debug dump response, success %d, data count %" PRIu32,
521         response.success, response.data_count);
522   std::lock_guard<std::mutex> lock(mParent.mDebugDumpMutex);
523   if (!mParent.mDebugDumpPending) {
524     ALOGI("Ignoring duplicate/unsolicited debug dump response");
525   } else {
526     mParent.mDebugDumpPending = false;
527     mParent.mDebugDumpCond.notify_all();
528   }
529 }
530 
invokeClientCallback(std::function<Return<void> ()> callback)531 void GenericContextHub::SocketCallbacks::invokeClientCallback(
532     std::function<Return<void>()> callback) {
533   std::lock_guard<std::mutex> lock(mParent.mCallbacksLock);
534   if (mParent.mCallbacks != nullptr && !callback().isOk()) {
535     ALOGE("Failed to invoke client callback");
536   }
537 }
538 
writeToDebugFile(const char * str)539 void GenericContextHub::writeToDebugFile(const char *str) {
540   writeToDebugFile(str, strlen(str));
541 }
542 
writeToDebugFile(const char * str,size_t len)543 void GenericContextHub::writeToDebugFile(const char *str, size_t len) {
544   ssize_t written = write(mDebugFd, str, len);
545   if (written != (ssize_t) len) {
546     ALOGW("Couldn't write to debug header: returned %zd, expected %zu "
547           "(errno %d)", written, len, errno);
548   }
549 }
550 
handleServiceDeath(uint32_t hubId)551 void GenericContextHub::handleServiceDeath(uint32_t hubId) {
552   std::lock_guard<std::mutex> lock(mCallbacksLock);
553   ALOGI("Context hub service died for hubId %" PRIu32, hubId);
554   mCallbacks.clear();
555 }
556 
HIDL_FETCH_IContexthub(const char *)557 IContexthub* HIDL_FETCH_IContexthub(const char* /* name */) {
558   return new GenericContextHub();
559 }
560 
561 }  // namespace implementation
562 }  // namespace V1_0
563 }  // namespace contexthub
564 }  // namespace hardware
565 }  // namespace android
566