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/MQDescriptor.h>
27 #include <hidl/Status.h>
28 #include <log/log.h>
29 
30 #include "IContextHubCallbackWrapper.h"
31 #include "chre_host/fragmented_load_transaction.h"
32 #include "chre_host/host_protocol_host.h"
33 #include "chre_host/socket_client.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::FragmentedLoadRequest;
44 using ::android::chre::FragmentedLoadTransaction;
45 using ::android::chre::getStringFromByteVector;
46 using ::android::chre::HostProtocolHost;
47 using ::android::hardware::hidl_handle;
48 using ::android::hardware::hidl_string;
49 using ::android::hardware::hidl_vec;
50 using ::android::hardware::Return;
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 using ::flatbuffers::FlatBufferBuilder;
66 
67 constexpr uint32_t kDefaultHubId = 0;
68 
extractChreApiMajorVersion(uint32_t chreVersion)69 inline constexpr uint8_t extractChreApiMajorVersion(uint32_t chreVersion) {
70   return static_cast<uint8_t>(chreVersion >> 24);
71 }
72 
extractChreApiMinorVersion(uint32_t chreVersion)73 inline constexpr uint8_t extractChreApiMinorVersion(uint32_t chreVersion) {
74   return static_cast<uint8_t>(chreVersion >> 16);
75 }
76 
extractChrePatchVersion(uint32_t chreVersion)77 inline constexpr uint16_t extractChrePatchVersion(uint32_t chreVersion) {
78   return static_cast<uint16_t>(chreVersion);
79 }
80 
81 /**
82  * @return file descriptor contained in the hidl_handle, or -1 if there is none
83  */
hidlHandleToFileDescriptor(const hidl_handle & hh)84 inline int hidlHandleToFileDescriptor(const hidl_handle &hh) {
85   const native_handle_t *handle = hh.getNativeHandle();
86   return (handle != nullptr && handle->numFds >= 1) ? handle->data[0] : -1;
87 }
88 
89 template <class IContexthubT>
90 class GenericContextHubBase : public IContexthubT {
91  public:
GenericContextHubBase()92   GenericContextHubBase() {
93     constexpr char kChreSocketName[] = "chre";
94 
95     mSocketCallbacks = new SocketCallbacks(*this);
96     if (!mClient.connectInBackground(kChreSocketName, mSocketCallbacks)) {
97       ALOGE("Couldn't start socket client");
98     }
99 
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       FlatBufferBuilder builder;
117       HostProtocolHost::encodeDebugDumpRequest(builder);
118       std::unique_lock<std::mutex> lock(mDebugDumpMutex);
119       mDebugDumpPending = true;
120       if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
121         ALOGW("Couldn't send debug dump request");
122       } else {
123         mDebugDumpCond.wait_for(lock, kDebugDumpTimeout,
124                                 [this]() { return !mDebugDumpPending; });
125         if (mDebugDumpPending) {
126           ALOGI("Timed out waiting on debug dump data");
127           mDebugDumpPending = false;
128         }
129       }
130       writeToDebugFile("\n-- End of CHRE/ASH debug info --\n");
131 
132       mDebugFd = kInvalidFd;
133       ALOGV("Debug dump complete");
134     }
135 
136     return Void();
137   }
138 
139   // Methods from ::android::hardware::contexthub::V1_0::IContexthub follow.
getHubs(V1_0::IContexthub::getHubs_cb _hidl_cb)140   Return<void> getHubs(V1_0::IContexthub::getHubs_cb _hidl_cb) override {
141     constexpr auto kHubInfoQueryTimeout = std::chrono::seconds(5);
142     std::vector<ContextHub> hubs;
143     ALOGV("%s", __func__);
144 
145     // If we're not connected yet, give it some time
146     // TODO refactor from polling into conditional wait
147     int maxSleepIterations = 250;
148     while (!mHubInfoValid && !mClient.isConnected() &&
149            --maxSleepIterations > 0) {
150       std::this_thread::sleep_for(std::chrono::milliseconds(20));
151     }
152 
153     if (!mClient.isConnected()) {
154       ALOGE("Couldn't connect to hub daemon");
155     } else if (!mHubInfoValid) {
156       // We haven't cached the hub details yet, so send a request and block
157       // waiting on a response
158       std::unique_lock<std::mutex> lock(mHubInfoMutex);
159       FlatBufferBuilder builder;
160       HostProtocolHost::encodeHubInfoRequest(builder);
161 
162       ALOGD("Sending hub info request");
163       if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
164         ALOGE("Couldn't send hub info request");
165       } else {
166         mHubInfoCond.wait_for(lock, kHubInfoQueryTimeout,
167                               [this]() { return mHubInfoValid; });
168       }
169     }
170 
171     if (mHubInfoValid) {
172       hubs.push_back(mHubInfo);
173     } else {
174       ALOGE("Unable to get hub info from CHRE");
175     }
176 
177     _hidl_cb(hubs);
178     return Void();
179   }
180 
registerCallback(uint32_t hubId,const sp<IContexthubCallback> & cb)181   Return<Result> registerCallback(uint32_t hubId,
182                                   const sp<IContexthubCallback> &cb) override {
183     sp<IContextHubCallbackWrapperBase> wrappedCallback;
184     if (cb != nullptr) {
185       wrappedCallback = new IContextHubCallbackWrapperV1_0(cb);
186     }
187     return registerCallbackCommon(hubId, wrappedCallback);
188   }
189 
190   // Common logic shared between pre-V1.2 and V1.2 HALs.
registerCallbackCommon(uint32_t hubId,const sp<IContextHubCallbackWrapperBase> & cb)191   Return<Result> registerCallbackCommon(
192       uint32_t hubId, const sp<IContextHubCallbackWrapperBase> &cb) {
193     Result result;
194     ALOGV("%s", __func__);
195 
196     // TODO: currently we only support 1 hub behind this HAL implementation
197     if (hubId == kDefaultHubId) {
198       std::lock_guard<std::mutex> lock(mCallbacksLock);
199 
200       if (cb != nullptr) {
201         if (mCallbacks != nullptr) {
202           ALOGD("Modifying callback for hubId %" PRIu32, hubId);
203           mCallbacks->unlinkToDeath(mDeathRecipient);
204         }
205         Return<bool> linkReturn = cb->linkToDeath(mDeathRecipient, hubId);
206         if (!linkReturn.withDefault(false)) {
207           ALOGW("Could not link death recipient to hubId %" PRIu32, hubId);
208         }
209       }
210 
211       mCallbacks = cb;
212       result = Result::OK;
213     } else {
214       result = Result::BAD_PARAMS;
215     }
216 
217     return result;
218   }
219 
sendMessageToHub(uint32_t hubId,const V1_0::ContextHubMsg & msg)220   Return<Result> sendMessageToHub(uint32_t hubId,
221                                   const V1_0::ContextHubMsg &msg) override {
222     Result result;
223     ALOGV("%s", __func__);
224 
225     if (hubId != kDefaultHubId) {
226       result = Result::BAD_PARAMS;
227     } else {
228       FlatBufferBuilder builder(1024);
229       HostProtocolHost::encodeNanoappMessage(builder, msg.appName, msg.msgType,
230                                              msg.hostEndPoint, msg.msg.data(),
231                                              msg.msg.size());
232 
233       if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
234         result = Result::UNKNOWN_FAILURE;
235       } else {
236         result = Result::OK;
237       }
238     }
239 
240     return result;
241   }
242 
loadNanoApp(uint32_t hubId,const NanoAppBinary & appBinary,uint32_t transactionId)243   Return<Result> loadNanoApp(uint32_t hubId, const NanoAppBinary &appBinary,
244                              uint32_t transactionId) override {
245     Result result;
246     ALOGV("%s", __func__);
247 
248     if (hubId != kDefaultHubId) {
249       result = Result::BAD_PARAMS;
250     } else {
251       std::lock_guard<std::mutex> lock(mPendingLoadTransactionMutex);
252 
253       if (mPendingLoadTransaction.has_value()) {
254         ALOGE("Pending load transaction exists. Overriding pending request");
255       }
256 
257       uint32_t targetApiVersion = (appBinary.targetChreApiMajorVersion << 24) |
258                                   (appBinary.targetChreApiMinorVersion << 16);
259       mPendingLoadTransaction = FragmentedLoadTransaction(
260           transactionId, appBinary.appId, appBinary.appVersion, appBinary.flags,
261           targetApiVersion, appBinary.customBinary);
262 
263       result =
264           sendFragmentedLoadNanoAppRequest(mPendingLoadTransaction.value());
265       if (result != Result::OK) {
266         mPendingLoadTransaction.reset();
267       }
268     }
269 
270     ALOGD(
271         "Attempted to send load nanoapp request for app of size %zu with ID "
272         "0x%016" PRIx64 " as transaction ID %" PRIu32 ": result %" PRIu32,
273         appBinary.customBinary.size(), appBinary.appId, transactionId, result);
274 
275     return result;
276   }
277 
unloadNanoApp(uint32_t hubId,uint64_t appId,uint32_t transactionId)278   Return<Result> unloadNanoApp(uint32_t hubId, uint64_t appId,
279                                uint32_t transactionId) override {
280     Result result;
281     ALOGV("%s", __func__);
282 
283     if (hubId != kDefaultHubId) {
284       result = Result::BAD_PARAMS;
285     } else {
286       FlatBufferBuilder builder(64);
287       HostProtocolHost::encodeUnloadNanoappRequest(
288           builder, transactionId, appId, false /* allowSystemNanoappUnload */);
289       if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
290         result = Result::UNKNOWN_FAILURE;
291       } else {
292         result = Result::OK;
293       }
294     }
295 
296     ALOGD("Attempted to send unload nanoapp request for app ID 0x%016" PRIx64
297           " as transaction ID %" PRIu32 ": result %" PRIu32,
298           appId, transactionId, result);
299 
300     return result;
301   }
302 
enableNanoApp(uint32_t,uint64_t appId,uint32_t)303   Return<Result> enableNanoApp(uint32_t /* hubId */, uint64_t appId,
304                                uint32_t /* transactionId */) override {
305     // TODO
306     ALOGW("Attempted to enable app ID 0x%016" PRIx64 ", but not supported",
307           appId);
308     return Result::TRANSACTION_FAILED;
309   }
310 
disableNanoApp(uint32_t,uint64_t appId,uint32_t)311   Return<Result> disableNanoApp(uint32_t /* hubId */, uint64_t appId,
312                                 uint32_t /* transactionId */) override {
313     // TODO
314     ALOGW("Attempted to disable app ID 0x%016" PRIx64 ", but not supported",
315           appId);
316     return Result::TRANSACTION_FAILED;
317   }
318 
queryApps(uint32_t hubId)319   Return<Result> queryApps(uint32_t hubId) override {
320     Result result;
321     ALOGV("%s", __func__);
322 
323     if (hubId != kDefaultHubId) {
324       result = Result::BAD_PARAMS;
325     } else {
326       FlatBufferBuilder builder(64);
327       HostProtocolHost::encodeNanoappListRequest(builder);
328       if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
329         result = Result::UNKNOWN_FAILURE;
330       } else {
331         result = Result::OK;
332       }
333     }
334 
335     return result;
336   }
337 
338  protected:
339   ::android::chre::SocketClient mClient;
340   sp<IContextHubCallbackWrapperBase> mCallbacks;
341   std::mutex mCallbacksLock;
342 
343   class SocketCallbacks : public ::android::chre::SocketClient::ICallbacks,
344                           public ::android::chre::IChreMessageHandlers {
345    public:
SocketCallbacks(GenericContextHubBase & parent)346     explicit SocketCallbacks(GenericContextHubBase &parent) : mParent(parent) {}
347 
onMessageReceived(const void * data,size_t length)348     void onMessageReceived(const void *data, size_t length) override {
349       if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
350         ALOGE("Failed to decode message");
351       }
352     }
353 
onConnected()354     void onConnected() override {
355       if (mHaveConnected) {
356         ALOGI("Reconnected to CHRE daemon");
357         invokeClientCallback([&]() {
358           return mParent.mCallbacks->handleHubEvent(AsyncEventType::RESTARTED);
359         });
360       }
361       mHaveConnected = true;
362     }
363 
onDisconnected()364     void onDisconnected() override {
365       ALOGW("Lost connection to CHRE daemon");
366     }
367 
handleNanoappMessage(const::chre::fbs::NanoappMessageT & message)368     void handleNanoappMessage(
369         const ::chre::fbs::NanoappMessageT &message) override {
370       ContextHubMsg msg;
371       msg.msg_1_0.appName = message.app_id;
372       msg.msg_1_0.hostEndPoint = message.host_endpoint;
373       msg.msg_1_0.msgType = message.message_type;
374       msg.msg_1_0.msg = message.message;
375       // Set of nanoapp permissions required to communicate with this nanoapp.
376       msg.permissions = chreToAndroidPermissions(message.permissions);
377       // Set of permissions required to consume this message and what will be
378       // attributed when the host endpoint consumes this on the Android side.
379       hidl_vec<hidl_string> msgContentPerms =
380           chreToAndroidPermissions(message.message_permissions);
381 
382       invokeClientCallback([&]() {
383         return mParent.mCallbacks->handleClientMsg(msg, msgContentPerms);
384       });
385     }
386 
handleHubInfoResponse(const::chre::fbs::HubInfoResponseT & response)387     void handleHubInfoResponse(
388         const ::chre::fbs::HubInfoResponseT &response) override {
389       ALOGD("Got hub info response");
390 
391       std::lock_guard<std::mutex> lock(mParent.mHubInfoMutex);
392       if (mParent.mHubInfoValid) {
393         ALOGI("Ignoring duplicate/unsolicited hub info response");
394       } else {
395         mParent.mHubInfo.name = getStringFromByteVector(response.name);
396         mParent.mHubInfo.vendor = getStringFromByteVector(response.vendor);
397         mParent.mHubInfo.toolchain =
398             getStringFromByteVector(response.toolchain);
399         mParent.mHubInfo.platformVersion = response.platform_version;
400         mParent.mHubInfo.toolchainVersion = response.toolchain_version;
401         mParent.mHubInfo.hubId = kDefaultHubId;
402 
403         mParent.mHubInfo.peakMips = response.peak_mips;
404         mParent.mHubInfo.stoppedPowerDrawMw = response.stopped_power;
405         mParent.mHubInfo.sleepPowerDrawMw = response.sleep_power;
406         mParent.mHubInfo.peakPowerDrawMw = response.peak_power;
407 
408         mParent.mHubInfo.maxSupportedMsgLen = response.max_msg_len;
409         mParent.mHubInfo.chrePlatformId = response.platform_id;
410 
411         uint32_t version = response.chre_platform_version;
412         mParent.mHubInfo.chreApiMajorVersion =
413             extractChreApiMajorVersion(version);
414         mParent.mHubInfo.chreApiMinorVersion =
415             extractChreApiMinorVersion(version);
416         mParent.mHubInfo.chrePatchVersion = extractChrePatchVersion(version);
417 
418         mParent.mHubInfoValid = true;
419         mParent.mHubInfoCond.notify_all();
420       }
421     }
422 
handleNanoappListResponse(const::chre::fbs::NanoappListResponseT & response)423     void handleNanoappListResponse(
424         const ::chre::fbs::NanoappListResponseT &response) override {
425       std::vector<HubAppInfo> appInfoList;
426 
427       ALOGV("Got nanoapp list response with %zu apps",
428             response.nanoapps.size());
429       for (const std::unique_ptr<::chre::fbs::NanoappListEntryT> &nanoapp :
430            response.nanoapps) {
431         // TODO: determine if this is really required, and if so, have
432         // HostProtocolHost strip out null entries as part of decode
433         if (nanoapp == nullptr) {
434           continue;
435         }
436 
437         ALOGV("App 0x%016" PRIx64 " ver 0x%" PRIx32 " permissions 0x%" PRIx32
438               " enabled %d system %d",
439               nanoapp->app_id, nanoapp->version, nanoapp->permissions,
440               nanoapp->enabled, nanoapp->is_system);
441         if (!nanoapp->is_system) {
442           HubAppInfo appInfo;
443 
444           appInfo.info_1_0.appId = nanoapp->app_id;
445           appInfo.info_1_0.version = nanoapp->version;
446           appInfo.info_1_0.enabled = nanoapp->enabled;
447           appInfo.permissions = chreToAndroidPermissions(nanoapp->permissions);
448 
449           appInfoList.push_back(appInfo);
450         }
451       }
452 
453       invokeClientCallback(
454           [&]() { return mParent.mCallbacks->handleAppsInfo(appInfoList); });
455     }
456 
handleLoadNanoappResponse(const::chre::fbs::LoadNanoappResponseT & response)457     void handleLoadNanoappResponse(
458         const ::chre::fbs::LoadNanoappResponseT &response) override {
459       ALOGV("Got load nanoapp response for transaction %" PRIu32
460             " fragment %" PRIu32 " with result %d",
461             response.transaction_id, response.fragment_id, response.success);
462       std::unique_lock<std::mutex> lock(mParent.mPendingLoadTransactionMutex);
463 
464       // TODO: Handle timeout in receiving load response
465       if (!mParent.mPendingLoadTransaction.has_value()) {
466         ALOGE(
467             "Dropping unexpected load response (no pending transaction "
468             "exists)");
469       } else {
470         FragmentedLoadTransaction &transaction =
471             mParent.mPendingLoadTransaction.value();
472 
473         if (!mParent.isExpectedLoadResponseLocked(response)) {
474           ALOGE(
475               "Dropping unexpected load response, expected transaction %" PRIu32
476               " fragment %" PRIu32 ", received transaction %" PRIu32
477               " fragment %" PRIu32,
478               transaction.getTransactionId(), mParent.mCurrentFragmentId,
479               response.transaction_id, response.fragment_id);
480         } else {
481           TransactionResult result;
482           bool continueLoadRequest = false;
483           if (response.success && !transaction.isComplete()) {
484             if (mParent.sendFragmentedLoadNanoAppRequest(transaction) ==
485                 Result::OK) {
486               continueLoadRequest = true;
487               result = TransactionResult::SUCCESS;
488             } else {
489               result = TransactionResult::FAILURE;
490             }
491           } else {
492             result = (response.success) ? TransactionResult::SUCCESS
493                                         : TransactionResult::FAILURE;
494           }
495 
496           if (!continueLoadRequest) {
497             mParent.mPendingLoadTransaction.reset();
498             lock.unlock();
499             invokeClientCallback([&]() {
500               return mParent.mCallbacks->handleTxnResult(
501                   response.transaction_id, result);
502             });
503           }
504         }
505       }
506     }
507 
handleUnloadNanoappResponse(const::chre::fbs::UnloadNanoappResponseT & response)508     void handleUnloadNanoappResponse(
509         const ::chre::fbs::UnloadNanoappResponseT &response) override {
510       ALOGV("Got unload nanoapp response for transaction %" PRIu32
511             " with result %d",
512             response.transaction_id, response.success);
513 
514       invokeClientCallback([&]() {
515         TransactionResult result = (response.success)
516                                        ? TransactionResult::SUCCESS
517                                        : TransactionResult::FAILURE;
518         return mParent.mCallbacks->handleTxnResult(response.transaction_id,
519                                                    result);
520       });
521     }
522 
handleDebugDumpData(const::chre::fbs::DebugDumpDataT & data)523     void handleDebugDumpData(const ::chre::fbs::DebugDumpDataT &data) override {
524       ALOGV("Got debug dump data, size %zu", data.debug_str.size());
525       if (mParent.mDebugFd == kInvalidFd) {
526         ALOGW("Got unexpected debug dump data message");
527       } else {
528         mParent.writeToDebugFile(
529             reinterpret_cast<const char *>(data.debug_str.data()),
530             data.debug_str.size());
531       }
532     }
533 
handleDebugDumpResponse(const::chre::fbs::DebugDumpResponseT & response)534     void handleDebugDumpResponse(
535         const ::chre::fbs::DebugDumpResponseT &response) override {
536       ALOGV("Got debug dump response, success %d, data count %" PRIu32,
537             response.success, response.data_count);
538       std::lock_guard<std::mutex> lock(mParent.mDebugDumpMutex);
539       if (!mParent.mDebugDumpPending) {
540         ALOGI("Ignoring duplicate/unsolicited debug dump response");
541       } else {
542         mParent.mDebugDumpPending = false;
543         mParent.mDebugDumpCond.notify_all();
544       }
545     }
546 
547    private:
548     GenericContextHubBase &mParent;
549     bool mHaveConnected = false;
550 
551     /**
552      * Acquires mParent.mCallbacksLock and invokes the synchronous callback
553      * argument if mParent.mCallbacks is not null.
554      */
invokeClientCallback(std::function<Return<void> ()> callback)555     void invokeClientCallback(std::function<Return<void>()> callback) {
556       std::lock_guard<std::mutex> lock(mParent.mCallbacksLock);
557       if (mParent.mCallbacks != nullptr && !callback().isOk()) {
558         ALOGE("Failed to invoke client callback");
559       }
560     }
561   };
562 
563   class DeathRecipient : public hidl_death_recipient {
564    public:
DeathRecipient(const sp<GenericContextHubBase> contexthub)565     explicit DeathRecipient(const sp<GenericContextHubBase> contexthub)
566         : mGenericContextHub(contexthub) {}
serviceDied(uint64_t cookie,const wp<::android::hidl::base::V1_0::IBase> &)567     void serviceDied(
568         uint64_t cookie,
569         const wp<::android::hidl::base::V1_0::IBase> & /* who */) override {
570       uint32_t hubId = static_cast<uint32_t>(cookie);
571       mGenericContextHub->handleServiceDeath(hubId);
572     }
573 
574    private:
575     sp<GenericContextHubBase> mGenericContextHub;
576   };
577 
578   sp<SocketCallbacks> mSocketCallbacks;
579   sp<DeathRecipient> mDeathRecipient;
580 
581   // Cached hub info used for getHubs(), and synchronization primitives to make
582   // that function call synchronous if we need to query it
583   ContextHub mHubInfo;
584   bool mHubInfoValid = false;
585   std::mutex mHubInfoMutex;
586   std::condition_variable mHubInfoCond;
587 
588   static constexpr int kInvalidFd = -1;
589   int mDebugFd = kInvalidFd;
590   bool mDebugDumpPending = false;
591   std::mutex mDebugDumpMutex;
592   std::condition_variable mDebugDumpCond;
593 
594   // The pending fragmented load request
595   uint32_t mCurrentFragmentId = 0;
596   std::optional<FragmentedLoadTransaction> mPendingLoadTransaction;
597   std::mutex mPendingLoadTransactionMutex;
598 
599   // Write a string to mDebugFd
writeToDebugFile(const char * str)600   void writeToDebugFile(const char *str) {
601     writeToDebugFile(str, strlen(str));
602   }
603 
writeToDebugFile(const char * str,size_t len)604   void writeToDebugFile(const char *str, size_t len) {
605     ssize_t written = write(mDebugFd, str, len);
606     if (written != (ssize_t)len) {
607       ALOGW(
608           "Couldn't write to debug header: returned %zd, expected %zu (errno "
609           "%d)",
610           written, len, errno);
611     }
612   }
613 
614   // Unregisters callback when context hub service dies
handleServiceDeath(uint32_t hubId)615   void handleServiceDeath(uint32_t hubId) {
616     std::lock_guard<std::mutex> lock(mCallbacksLock);
617     ALOGI("Context hub service died for hubId %" PRIu32, hubId);
618     mCallbacks.clear();
619   }
620 
621   /**
622    * Checks to see if a load response matches the currently pending
623    * fragmented load transaction. mPendingLoadTransactionMutex must
624    * be acquired prior to calling this function.
625    *
626    * @param response the received load response
627    *
628    * @return true if the response matches a pending load transaction
629    *         (if any), false otherwise
630    */
isExpectedLoadResponseLocked(const::chre::fbs::LoadNanoappResponseT & response)631   bool isExpectedLoadResponseLocked(
632       const ::chre::fbs::LoadNanoappResponseT &response) {
633     return mPendingLoadTransaction.has_value() &&
634            (mPendingLoadTransaction->getTransactionId() ==
635             response.transaction_id) &&
636            (response.fragment_id == 0 ||
637             mCurrentFragmentId == response.fragment_id);
638   }
639 
640   /**
641    * Sends a fragmented load request to CHRE. The caller must ensure that
642    * transaction.isComplete() returns false prior to invoking this method.
643    *
644    * @param transaction the FragmentedLoadTransaction object
645    *
646    * @return the result of the request
647    */
sendFragmentedLoadNanoAppRequest(FragmentedLoadTransaction & transaction)648   Result sendFragmentedLoadNanoAppRequest(
649       FragmentedLoadTransaction &transaction) {
650     Result result;
651     const FragmentedLoadRequest &request = transaction.getNextRequest();
652 
653     FlatBufferBuilder builder(128 + request.binary.size());
654     HostProtocolHost::encodeFragmentedLoadNanoappRequest(builder, request);
655 
656     if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
657       ALOGE("Failed to send load request message (fragment ID = %zu)",
658             request.fragmentId);
659       result = Result::UNKNOWN_FAILURE;
660     } else {
661       mCurrentFragmentId = request.fragmentId;
662       result = Result::OK;
663     }
664 
665     return result;
666   }
667 };
668 
669 }  // namespace implementation
670 }  // namespace common
671 }  // namespace contexthub
672 }  // namespace hardware
673 }  // namespace android
674 
675 #endif  // ANDROID_HARDWARE_CONTEXTHUB_COMMON_CONTEXTHUB_H
676