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