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