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