• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef ANDROID_HARDWARE_CONTEXTHUB_COMMON_CONTEXTHUB_H
18 #define ANDROID_HARDWARE_CONTEXTHUB_COMMON_CONTEXTHUB_H
19 
20 #include <condition_variable>
21 #include <functional>
22 #include <mutex>
23 #include <optional>
24 
25 #include <android/hardware/contexthub/1.0/IContexthub.h>
26 #include <hidl/HidlSupport.h>
27 #include <hidl/MQDescriptor.h>
28 #include <hidl/Status.h>
29 #include <log/log.h>
30 
31 #include "IContextHubCallbackWrapper.h"
32 #include "hal_chre_socket_connection.h"
33 #include "permissions_util.h"
34 
35 namespace android {
36 namespace hardware {
37 namespace contexthub {
38 namespace common {
39 namespace implementation {
40 
41 using ::android::sp;
42 using ::android::chre::FragmentedLoadTransaction;
43 using ::android::chre::getStringFromByteVector;
44 using ::android::chre::HostProtocolHost;
45 using ::android::hardware::hidl_handle;
46 using ::android::hardware::hidl_string;
47 using ::android::hardware::hidl_vec;
48 using ::android::hardware::Return;
49 using ::android::hardware::contexthub::common::implementation::
50     chreToAndroidPermissions;
51 using ::android::hardware::contexthub::V1_0::AsyncEventType;
52 using ::android::hardware::contexthub::V1_0::ContextHub;
53 using ::android::hardware::contexthub::V1_0::IContexthubCallback;
54 using ::android::hardware::contexthub::V1_0::NanoAppBinary;
55 using ::android::hardware::contexthub::V1_0::Result;
56 using ::android::hardware::contexthub::V1_0::TransactionResult;
57 using ::android::hardware::contexthub::V1_2::ContextHubMsg;
58 using ::android::hardware::contexthub::V1_2::HubAppInfo;
59 using ::android::hardware::contexthub::V1_X::implementation::
60     IContextHubCallbackWrapperBase;
61 using ::android::hardware::contexthub::V1_X::implementation::
62     IContextHubCallbackWrapperV1_0;
63 
64 constexpr uint32_t kDefaultHubId = 0;
65 
extractChreApiMajorVersion(uint32_t chreVersion)66 inline constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) {
67   return static_cast<uint8_t>(chreVersion >> 24);
68 }
69 
extractChreApiMinorVersion(uint32_t chreVersion)70 inline constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) {
71   return static_cast<uint8_t>(chreVersion >> 16);
72 }
73 
extractChrePatchVersion(uint32_t chreVersion)74 inline constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) {
75   return static_cast<uint16_t>(chreVersion);
76 }
77 
stringVectorToHidl(const std::vector<std::string> & list)78 inline hidl_vec<hidl_string> stringVectorToHidl(
79     const std::vector<std::string> &list) {
80   std::vector<hidl_string> outList;
81   for (const std::string &item : list) {
82     outList.push_back(item);
83   }
84 
85   return hidl_vec(outList);
86 }
87 
88 /**
89  * @return file descriptor contained in the hidl_handle, or -1 if there is none
90  */
hidlHandleToFileDescriptor(const hidl_handle & hh)91 inline int hidlHandleToFileDescriptor(const hidl_handle &hh) {
92   const native_handle_t *handle = hh.getNativeHandle();
93   return (handle != nullptr && handle->numFds >= 1) ? handle->data[0] : -1;
94 }
95 
96 template <class IContexthubT>
97 class GenericContextHubBase : public IContexthubT, public IChreSocketCallback {
98  public:
GenericContextHubBase()99   GenericContextHubBase() {
100     mDeathRecipient = new DeathRecipient(this);
101   }
102 
debug(const hidl_handle & fd,const hidl_vec<hidl_string> &)103   Return<void> debug(const hidl_handle &fd,
104                      const hidl_vec<hidl_string> & /* options */) override {
105     // Timeout inside CHRE is typically 5 seconds, grant 500ms extra here to let
106     // the data reach us
107     constexpr auto kDebugDumpTimeout = std::chrono::milliseconds(5500);
108 
109     mDebugFd = hidlHandleToFileDescriptor(fd);
110     if (mDebugFd < 0) {
111       ALOGW("Can't dump debug info to invalid fd");
112     } else {
113       writeToDebugFile("-- Dumping CHRE/ASH debug info --\n");
114 
115       ALOGV("Sending debug dump request");
116       std::unique_lock<std::mutex> lock(mDebugDumpMutex);
117       mDebugDumpPending = true;
118       if (!mConnection.requestDebugDump()) {
119         ALOGW("Couldn't send debug dump request");
120       } else {
121         mDebugDumpCond.wait_for(lock, kDebugDumpTimeout,
122                                 [this]() { return !mDebugDumpPending; });
123         if (mDebugDumpPending) {
124           ALOGI("Timed out waiting on debug dump data");
125           mDebugDumpPending = false;
126         }
127       }
128       writeToDebugFile("\n-- End of CHRE/ASH debug info --\n");
129 
130       mDebugFd = kInvalidFd;
131       ALOGV("Debug dump complete");
132     }
133 
134     return Void();
135   }
136 
137   // Methods from ::android::hardware::contexthub::V1_0::IContexthub follow.
getHubs(V1_0::IContexthub::getHubs_cb _hidl_cb)138   Return<void> getHubs(V1_0::IContexthub::getHubs_cb _hidl_cb) override {
139     std::vector<ContextHub> hubs;
140 
141     ::chre::fbs::HubInfoResponseT response;
142     bool success = mConnection.getContextHubs(&response);
143     if (success) {
144       mHubInfo.name = getStringFromByteVector(response.name);
145       mHubInfo.vendor = getStringFromByteVector(response.vendor);
146       mHubInfo.toolchain = getStringFromByteVector(response.toolchain);
147       mHubInfo.platformVersion = response.platform_version;
148       mHubInfo.toolchainVersion = response.toolchain_version;
149       mHubInfo.hubId = kDefaultHubId;
150 
151       mHubInfo.peakMips = response.peak_mips;
152       mHubInfo.stoppedPowerDrawMw = response.stopped_power;
153       mHubInfo.sleepPowerDrawMw = response.sleep_power;
154       mHubInfo.peakPowerDrawMw = response.peak_power;
155 
156       mHubInfo.maxSupportedMsgLen = response.max_msg_len;
157       mHubInfo.chrePlatformId = response.platform_id;
158 
159       uint32_t version = response.chre_platform_version;
160       mHubInfo.chreApiMajorVersion = extractChreApiMajorVersion(version);
161       mHubInfo.chreApiMinorVersion = extractChreApiMinorVersion(version);
162       mHubInfo.chrePatchVersion = extractChrePatchVersion(version);
163 
164       hubs.push_back(mHubInfo);
165     }
166 
167     _hidl_cb(hubs);
168     return Void();
169   }
170 
registerCallback(uint32_t hubId,const sp<IContexthubCallback> & cb)171   Return<Result> registerCallback(uint32_t hubId,
172                                   const sp<IContexthubCallback> &cb) override {
173     sp<IContextHubCallbackWrapperBase> wrappedCallback;
174     if (cb != nullptr) {
175       wrappedCallback = new IContextHubCallbackWrapperV1_0(cb);
176     }
177     return registerCallbackCommon(hubId, wrappedCallback);
178   }
179 
180   // Common logic shared between pre-V1.2 and V1.2 HALs.
registerCallbackCommon(uint32_t hubId,const sp<IContextHubCallbackWrapperBase> & cb)181   Return<Result> registerCallbackCommon(
182       uint32_t hubId, const sp<IContextHubCallbackWrapperBase> &cb) {
183     Result result;
184     ALOGV("%s", __func__);
185 
186     // TODO: currently we only support 1 hub behind this HAL implementation
187     if (hubId == kDefaultHubId) {
188       std::lock_guard<std::mutex> lock(mCallbacksLock);
189 
190       if (cb != nullptr) {
191         if (mCallbacks != nullptr) {
192           ALOGD("Modifying callback for hubId %" PRIu32, hubId);
193           mCallbacks->unlinkToDeath(mDeathRecipient);
194         }
195         Return<bool> linkReturn = cb->linkToDeath(mDeathRecipient, hubId);
196         if (!linkReturn.withDefault(false)) {
197           ALOGW("Could not link death recipient to hubId %" PRIu32, hubId);
198         }
199       }
200 
201       mCallbacks = cb;
202       result = Result::OK;
203     } else {
204       result = Result::BAD_PARAMS;
205     }
206 
207     return result;
208   }
209 
sendMessageToHub(uint32_t hubId,const V1_0::ContextHubMsg & msg)210   Return<Result> sendMessageToHub(uint32_t hubId,
211                                   const V1_0::ContextHubMsg &msg) override {
212     Result result;
213     ALOGV("%s", __func__);
214 
215     if (hubId != kDefaultHubId) {
216       result = Result::BAD_PARAMS;
217     } else {
218       result = toHidlResult(mConnection.sendMessageToHub(
219           msg.appName, msg.msgType, msg.hostEndPoint, msg.msg.data(),
220           msg.msg.size()));
221     }
222 
223     return result;
224   }
225 
loadNanoApp(uint32_t hubId,const NanoAppBinary & appBinary,uint32_t transactionId)226   Return<Result> loadNanoApp(uint32_t hubId, const NanoAppBinary &appBinary,
227                              uint32_t transactionId) override {
228     Result result;
229     ALOGV("%s", __func__);
230 
231     if (hubId != kDefaultHubId) {
232       result = Result::BAD_PARAMS;
233     } else {
234       uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
235                                   (appBinary.targetChreApiMinorVersion << 16);
236       FragmentedLoadTransaction transaction(
237           transactionId, appBinary.appId, appBinary.appVersion, appBinary.flags,
238           targetApiVersion, appBinary.customBinary);
239       result = toHidlResult(mConnection.loadNanoapp(transaction));
240     }
241 
242     ALOGD(
243         "Attempted to send load nanoapp request for app of size %zu with ID "
244         "0x%016" PRIx64 " as transaction ID %" PRIu32 ": result %" PRIu32,
245         appBinary.customBinary.size(), appBinary.appId, transactionId, result);
246 
247     return result;
248   }
249 
unloadNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)250   Return<Result> unloadNanoApp(uint32_t hubId, uint64_t appId,
251                                uint32_t transactionId) override {
252     Result result;
253     ALOGV("%s", __func__);
254 
255     if (hubId != kDefaultHubId) {
256       result = Result::BAD_PARAMS;
257     } else {
258       result = toHidlResult(mConnection.unloadNanoapp(appId, transactionId));
259     }
260 
261     ALOGD("Attempted to send unload nanoapp request for app ID 0x%016" PRIx64
262           " as transaction ID %" PRIu32 ": result %" PRIu32,
263           appId, transactionId, result);
264 
265     return result;
266   }
267 
enableNanoApp(uint32_t,uint64_t appId,uint32_t)268   Return<Result> enableNanoApp(uint32_t /* hubId */, uint64_t appId,
269                                uint32_t /* transactionId */) override {
270     // TODO
271     ALOGW("Attempted to enable app ID 0x%016" PRIx64 ", but not supported",
272           appId);
273     return Result::TRANSACTION_FAILED;
274   }
275 
disableNanoApp(uint32_t,uint64_t appId,uint32_t)276   Return<Result> disableNanoApp(uint32_t /* hubId */, uint64_t appId,
277                                 uint32_t /* transactionId */) override {
278     // TODO
279     ALOGW("Attempted to disable app ID 0x%016" PRIx64 ", but not supported",
280           appId);
281     return Result::TRANSACTION_FAILED;
282   }
283 
queryApps(uint32_t hubId)284   Return<Result> queryApps(uint32_t hubId) override {
285     Result result;
286     ALOGV("%s", __func__);
287 
288     if (hubId != kDefaultHubId) {
289       result = Result::BAD_PARAMS;
290     } else {
291       result = toHidlResult(mConnection.queryNanoapps());
292     }
293 
294     return result;
295   }
296 
onNanoappMessage(const::chre::fbs::NanoappMessageT & message)297   void onNanoappMessage(const ::chre::fbs::NanoappMessageT &message) override {
298     ContextHubMsg msg;
299     msg.msg_1_0.appName = message.app_id;
300     msg.msg_1_0.hostEndPoint = message.host_endpoint;
301     msg.msg_1_0.msgType = message.message_type;
302     msg.msg_1_0.msg = message.message;
303     // Set of nanoapp permissions required to communicate with this nanoapp.
304     msg.permissions =
305         stringVectorToHidl(chreToAndroidPermissions(message.permissions));
306     // Set of permissions required to consume this message and what will be
307     // attributed when the host endpoint consumes this on the Android side.
308     hidl_vec<hidl_string> msgContentPerms = stringVectorToHidl(
309         chreToAndroidPermissions(message.message_permissions));
310 
311     invokeClientCallback(
312         [&]() { return mCallbacks->handleClientMsg(msg, msgContentPerms); });
313   }
314 
onNanoappListResponse(const::chre::fbs::NanoappListResponseT & response)315   void onNanoappListResponse(
316       const ::chre::fbs::NanoappListResponseT &response) override {
317     std::vector<HubAppInfo> appInfoList;
318 
319     for (const std::unique_ptr<::chre::fbs::NanoappListEntryT> &nanoapp :
320          response.nanoapps) {
321       // TODO: determine if this is really required, and if so, have
322       // HostProtocolHost strip out null entries as part of decode
323       if (nanoapp == nullptr) {
324         continue;
325       }
326 
327       ALOGV("App 0x%016" PRIx64 " ver 0x%" PRIx32 " permissions 0x%" PRIx32
328             " enabled %d system %d",
329             nanoapp->app_id, nanoapp->version, nanoapp->permissions,
330             nanoapp->enabled, nanoapp->is_system);
331       if (!nanoapp->is_system) {
332         HubAppInfo appInfo;
333 
334         appInfo.info_1_0.appId = nanoapp->app_id;
335         appInfo.info_1_0.version = nanoapp->version;
336         appInfo.info_1_0.enabled = nanoapp->enabled;
337         appInfo.permissions =
338             stringVectorToHidl(chreToAndroidPermissions(nanoapp->permissions));
339 
340         appInfoList.push_back(appInfo);
341       }
342     }
343 
344     invokeClientCallback(
345         [&]() { return mCallbacks->handleAppsInfo(appInfoList); });
346   }
347 
onTransactionResult(uint32_t transactionId,bool success)348   void onTransactionResult(uint32_t transactionId, bool success) override {
349     TransactionResult result =
350         success ? TransactionResult::SUCCESS : TransactionResult::FAILURE;
351     invokeClientCallback(
352         [&]() { return mCallbacks->handleTxnResult(transactionId, result); });
353   }
354 
onContextHubRestarted()355   void onContextHubRestarted() override {
356     invokeClientCallback([&]() {
357       return mCallbacks->handleHubEvent(AsyncEventType::RESTARTED);
358     });
359   }
360 
onDebugDumpData(const::chre::fbs::DebugDumpDataT & data)361   void onDebugDumpData(const ::chre::fbs::DebugDumpDataT &data) override {
362     if (mDebugFd == kInvalidFd) {
363       ALOGW("Got unexpected debug dump data message");
364     } else {
365       writeToDebugFile(reinterpret_cast<const char *>(data.debug_str.data()),
366                        data.debug_str.size());
367     }
368   }
369 
onDebugDumpComplete(const::chre::fbs::DebugDumpResponseT &)370   void onDebugDumpComplete(
371       const ::chre::fbs::DebugDumpResponseT & /* response */) override {
372     std::lock_guard<std::mutex> lock(mDebugDumpMutex);
373     if (!mDebugDumpPending) {
374       ALOGI("Ignoring duplicate/unsolicited debug dump response");
375     } else {
376       mDebugDumpPending = false;
377       mDebugDumpCond.notify_all();
378     }
379   }
380 
381  protected:
382   sp<IContextHubCallbackWrapperBase> mCallbacks;
383   std::mutex mCallbacksLock;
384 
385   class DeathRecipient : public hidl_death_recipient {
386    public:
DeathRecipient(const sp<GenericContextHubBase> contexthub)387     explicit DeathRecipient(const sp<GenericContextHubBase> contexthub)
388         : mGenericContextHub(contexthub) {}
serviceDied(uint64_t cookie,const wp<::android::hidl::base::V1_0::IBase> &)389     void serviceDied(
390         uint64_t cookie,
391         const wp<::android::hidl::base::V1_0::IBase> & /* who */) override {
392       uint32_t hubId = static_cast<uint32_t>(cookie);
393       mGenericContextHub->handleServiceDeath(hubId);
394     }
395 
396    private:
397     sp<GenericContextHubBase> mGenericContextHub;
398   };
399 
400   HalChreSocketConnection mConnection{this};
401 
402   sp<DeathRecipient> mDeathRecipient;
403 
404   // Cached hub info used for getHubs(), and synchronization primitives to make
405   // that function call synchronous if we need to query it
406   ContextHub mHubInfo;
407   bool mHubInfoValid = false;
408   std::mutex mHubInfoMutex;
409   std::condition_variable mHubInfoCond;
410 
411   static constexpr int kInvalidFd = -1;
412   int mDebugFd = kInvalidFd;
413   bool mDebugDumpPending = false;
414   std::mutex mDebugDumpMutex;
415   std::condition_variable mDebugDumpCond;
416 
417   // Write a string to mDebugFd
writeToDebugFile(const char * str)418   void writeToDebugFile(const char *str) {
419     writeToDebugFile(str, strlen(str));
420   }
421 
writeToDebugFile(const char * str,size_t len)422   void writeToDebugFile(const char *str, size_t len) {
423     ssize_t written = write(mDebugFd, str, len);
424     if (written != (ssize_t)len) {
425       ALOGW(
426           "Couldn't write to debug header: returned %zd, expected %zu (errno "
427           "%d)",
428           written, len, errno);
429     }
430   }
431 
432   // Unregisters callback when context hub service dies
handleServiceDeath(uint32_t hubId)433   void handleServiceDeath(uint32_t hubId) {
434     std::lock_guard<std::mutex> lock(mCallbacksLock);
435     ALOGI("Context hub service died for hubId %" PRIu32, hubId);
436     mCallbacks.clear();
437   }
438 
invokeClientCallback(std::function<Return<void> ()> callback)439   void invokeClientCallback(std::function<Return<void>()> callback) {
440     std::lock_guard<std::mutex> lock(mCallbacksLock);
441     if (mCallbacks != nullptr && !callback().isOk()) {
442       ALOGE("Failed to invoke client callback");
443     }
444   }
445 
toHidlResult(bool success)446   Result toHidlResult(bool success) {
447     return success ? Result::OK : Result::UNKNOWN_FAILURE;
448   }
449 };
450 
451 }  // namespace implementation
452 }  // namespace common
453 }  // namespace contexthub
454 }  // namespace hardware
455 }  // namespace android
456 
457 #endif  // ANDROID_HARDWARE_CONTEXTHUB_COMMON_CONTEXTHUB_H
458