xref: /aosp_15_r20/system/chre/host/common/include/chre_host/hal_client.h (revision 84e339476a462649f82315436d70fd732297a399)
1 /*
2  * Copyright (C) 2023 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 CHRE_HOST_HAL_CLIENT_H_
18 #define CHRE_HOST_HAL_CLIENT_H_
19 
20 #include <cinttypes>
21 #include <future>
22 #include <memory>
23 #include <shared_mutex>
24 #include <thread>
25 #include <unordered_map>
26 #include <vector>
27 
28 #include <aidl/android/hardware/contexthub/BnContextHubCallback.h>
29 #include <aidl/android/hardware/contexthub/ContextHubMessage.h>
30 #include <aidl/android/hardware/contexthub/HostEndpointInfo.h>
31 #include <aidl/android/hardware/contexthub/IContextHub.h>
32 #include <aidl/android/hardware/contexthub/IContextHubCallback.h>
33 #include <aidl/android/hardware/contexthub/NanoappBinary.h>
34 #include <android/binder_manager.h>
35 #include <android/binder_process.h>
36 
37 #include "hal_error.h"
38 
39 namespace android::chre {
40 
41 using ::aidl::android::hardware::contexthub::AsyncEventType;
42 using ::aidl::android::hardware::contexthub::BnContextHubCallback;
43 using ::aidl::android::hardware::contexthub::ContextHubInfo;
44 using ::aidl::android::hardware::contexthub::ContextHubMessage;
45 using ::aidl::android::hardware::contexthub::HostEndpointInfo;
46 using ::aidl::android::hardware::contexthub::IContextHub;
47 using ::aidl::android::hardware::contexthub::IContextHubCallback;
48 using ::aidl::android::hardware::contexthub::IContextHubDefault;
49 using ::aidl::android::hardware::contexthub::MessageDeliveryStatus;
50 using ::aidl::android::hardware::contexthub::NanoappBinary;
51 using ::aidl::android::hardware::contexthub::NanoappInfo;
52 using ::aidl::android::hardware::contexthub::NanSessionRequest;
53 using ::aidl::android::hardware::contexthub::Setting;
54 using ::ndk::ScopedAStatus;
55 
56 /**
57  * A class connecting to CHRE Multiclient HAL via binder and taking care of
58  * binder (re)connection.
59  *
60  * <p>HalClient will replace the SocketClient that does the similar
61  * communication with CHRE but through a socket connection.
62  *
63  * <p>HalClient also maintains a set of connected host endpoints, using which
64  * it will enforce in the future that a message can only be sent to/from an
65  * endpoint id that is already connected to HAL.
66  *
67  * <p>When the binder connection to HAL is disconnected HalClient will have a
68  * death recipient re-establish the connection and reconnect the previously
69  * connected endpoints. In a rare case that CHRE also restarts at the same time,
70  * a client should rely on IContextHubCallback.handleContextHubAsyncEvent() to
71  * handle the RESTARTED event which is a signal that CHRE is up running.
72  */
73 class HalClient {
74  public:
75   static constexpr int32_t kDefaultContextHubId = 0;
76 
77   /** Callback interface for a background connection. */
78   class BackgroundConnectionCallback {
79    public:
80     /**
81      * This function is called when the connection to CHRE HAL is finished.
82      *
83      * @param isConnected indicates whether CHRE HAL is successfully connected.
84      */
85     virtual void onInitialization(bool isConnected) = 0;
86     virtual ~BackgroundConnectionCallback() = default;
87   };
88 
89   ~HalClient();
90 
91   /**
92    * Create a HalClient unique pointer used to communicate with CHRE HAL.
93    *
94    * @param callback a non-null callback.
95    * @param contextHubId context hub id that only 0 is supported at this moment.
96    *
97    * @return null pointer if the creation fails.
98    */
99   static std::unique_ptr<HalClient> create(
100       const std::shared_ptr<IContextHubCallback> &callback,
101       int32_t contextHubId = kDefaultContextHubId);
102 
103   /**
104    * Returns true if the multiclient HAL is available.
105    *
106    * <p>Multicleint HAL may not be available on a device that has CHRE enabled.
107    * In this situation, clients are expected to still use SocketClient to
108    * communicate with CHRE.
109    */
110   static bool isServiceAvailable();
111 
112   /** Returns true if this HalClient instance is connected to the HAL. */
isConnected()113   bool isConnected() {
114     return mIsHalConnected;
115   }
116 
117   /** Connects to CHRE HAL synchronously. */
connect()118   bool connect() {
119     return initConnection() == HalError::SUCCESS;
120   }
121 
122   /** Connects to CHRE HAL in background. */
connectInBackground(BackgroundConnectionCallback & callback)123   void connectInBackground(BackgroundConnectionCallback &callback) {
124     std::lock_guard<std::mutex> lock(mBackgroundConnectionFuturesLock);
125     // Policy std::launch::async is required to avoid lazy evaluation which can
126     // postpone the execution until get() of the future returned by std::async
127     // is called.
128     mBackgroundConnectionFutures.emplace_back(
129         std::async(std::launch::async, [&]() {
130           callback.onInitialization(initConnection() == HalError::SUCCESS);
131         }));
132   }
133 
queryNanoapps()134   ScopedAStatus queryNanoapps() {
135     return callIfConnected([&](const std::shared_ptr<IContextHub> &hub) {
136       return hub->queryNanoapps(mContextHubId);
137     });
138   }
139 
140   /** Sends a message to a Nanoapp. */
141   ScopedAStatus sendMessage(const ContextHubMessage &message);
142 
143   /** Connects a host endpoint to CHRE. */
144   ScopedAStatus connectEndpoint(const HostEndpointInfo &hostEndpointInfo);
145 
146   /** Disconnects a host endpoint from CHRE. */
147   ScopedAStatus disconnectEndpoint(char16_t hostEndpointId);
148 
149  protected:
150   class HalClientCallback : public BnContextHubCallback {
151    public:
HalClientCallback(const std::shared_ptr<IContextHubCallback> & callback,HalClient * halClient)152     explicit HalClientCallback(
153         const std::shared_ptr<IContextHubCallback> &callback,
154         HalClient *halClient)
155         : mCallback(callback), mHalClient(halClient) {}
156 
handleNanoappInfo(const std::vector<NanoappInfo> & appInfo)157     ScopedAStatus handleNanoappInfo(
158         const std::vector<NanoappInfo> &appInfo) override {
159       return mCallback->handleNanoappInfo(appInfo);
160     }
161 
handleContextHubMessage(const ContextHubMessage & msg,const std::vector<std::string> & msgContentPerms)162     ScopedAStatus handleContextHubMessage(
163         const ContextHubMessage &msg,
164         const std::vector<std::string> &msgContentPerms) override {
165       return mCallback->handleContextHubMessage(msg, msgContentPerms);
166     }
167 
handleContextHubAsyncEvent(AsyncEventType event)168     ScopedAStatus handleContextHubAsyncEvent(AsyncEventType event) override {
169       if (event == AsyncEventType::RESTARTED) {
170         tryReconnectEndpoints(mHalClient);
171       }
172       return mCallback->handleContextHubAsyncEvent(event);
173     }
174 
handleTransactionResult(int32_t transactionId,bool success)175     ScopedAStatus handleTransactionResult(int32_t transactionId,
176                                           bool success) override {
177       return mCallback->handleTransactionResult(transactionId, success);
178     }
179 
handleNanSessionRequest(const NanSessionRequest & request)180     ScopedAStatus handleNanSessionRequest(
181         const NanSessionRequest &request) override {
182       return mCallback->handleNanSessionRequest(request);
183     }
184 
handleMessageDeliveryStatus(char16_t hostEndPointId,const MessageDeliveryStatus & messageDeliveryStatus)185     ScopedAStatus handleMessageDeliveryStatus(
186         char16_t hostEndPointId,
187         const MessageDeliveryStatus &messageDeliveryStatus) override {
188       return mCallback->handleMessageDeliveryStatus(hostEndPointId,
189                                                     messageDeliveryStatus);
190     }
191 
getUuid(std::array<uint8_t,16> * outUuid)192     ScopedAStatus getUuid(std::array<uint8_t, 16> *outUuid) override {
193       return mCallback->getUuid(outUuid);
194     }
195 
getName(std::string * outName)196     ScopedAStatus getName(std::string *outName) override {
197       return mCallback->getName(outName);
198     }
199 
200    private:
201     std::shared_ptr<IContextHubCallback> mCallback;
202     HalClient *mHalClient;
203   };
204 
205   explicit HalClient(const std::shared_ptr<IContextHubCallback> &callback,
206                      int32_t contextHubId = kDefaultContextHubId)
mContextHubId(contextHubId)207       : mContextHubId(contextHubId) {
208     mCallback = ndk::SharedRefBase::make<HalClientCallback>(callback, this);
209     ABinderProcess_startThreadPool();
210     mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(
211         AIBinder_DeathRecipient_new(onHalDisconnected));
212     mCallback->getName(&mClientName);
213   }
214 
215   /**
216    * Initializes the connection to CHRE HAL.
217    */
218   HalError initConnection();
219 
220   using HostEndpointId = char16_t;
221 
222   const std::string kAidlServiceName =
223       std::string() + IContextHub::descriptor + "/default";
224 
225   /** The callback for a disconnected HAL binder connection. */
226   static void onHalDisconnected(void *cookie);
227 
228   /** Reconnect previously connected endpoints after CHRE or HAL restarts. */
229   static void tryReconnectEndpoints(HalClient *halClient);
230 
callIfConnected(const std::function<ScopedAStatus (const std::shared_ptr<IContextHub> & hub)> & func)231   ScopedAStatus callIfConnected(
232       const std::function<
233           ScopedAStatus(const std::shared_ptr<IContextHub> &hub)> &func) {
234     std::shared_ptr<IContextHub> hub;
235     {
236       // Make a copy of mContextHub so that even if HAL is disconnected and
237       // mContextHub is set to null the copy is kept as non-null to avoid crash.
238       // Still guard the copy by a shared lock to avoid torn writes.
239       std::shared_lock<std::shared_mutex> sharedLock(mConnectionLock);
240       hub = mContextHub;
241     }
242     if (hub == nullptr) {
243       return fromHalError(HalError::BINDER_DISCONNECTED);
244     }
245     return func(hub);
246   }
247 
isEndpointConnected(HostEndpointId hostEndpointId)248   bool isEndpointConnected(HostEndpointId hostEndpointId) {
249     std::shared_lock<std::shared_mutex> sharedLock(mConnectedEndpointsLock);
250     return mConnectedEndpoints.find(hostEndpointId) !=
251            mConnectedEndpoints.end();
252   }
253 
insertConnectedEndpoint(const HostEndpointInfo & hostEndpointInfo)254   void insertConnectedEndpoint(const HostEndpointInfo &hostEndpointInfo) {
255     std::lock_guard<std::shared_mutex> lockGuard(mConnectedEndpointsLock);
256     mConnectedEndpoints[hostEndpointInfo.hostEndpointId] = hostEndpointInfo;
257   }
258 
removeConnectedEndpoint(HostEndpointId hostEndpointId)259   void removeConnectedEndpoint(HostEndpointId hostEndpointId) {
260     std::lock_guard<std::shared_mutex> lockGuard(mConnectedEndpointsLock);
261     mConnectedEndpoints.erase(hostEndpointId);
262   }
263 
fromHalError(HalError errorCode)264   static ScopedAStatus fromHalError(HalError errorCode) {
265     return errorCode == HalError::SUCCESS
266                ? ScopedAStatus::ok()
267                : ScopedAStatus::fromServiceSpecificError(
268                      static_cast<int32_t>(errorCode));
269   }
270 
271   // Multi-contextHub is not supported at this moment.
272   int32_t mContextHubId;
273 
274   // The lock guarding mConnectedEndpoints.
275   std::shared_mutex mConnectedEndpointsLock;
276   std::unordered_map<HostEndpointId, HostEndpointInfo> mConnectedEndpoints{};
277 
278   // The lock guarding the init connection flow.
279   std::shared_mutex mConnectionLock;
280   std::shared_ptr<IContextHub> mContextHub;
281   std::atomic_bool mIsHalConnected = false;
282 
283   // Handler of the binder disconnection event with HAL.
284   ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
285 
286   std::shared_ptr<HalClientCallback> mCallback;
287 
288   std::string mClientName;
289 
290   // Lock guarding background connection threads.
291   std::mutex mBackgroundConnectionFuturesLock;
292   std::vector<std::future<void>> mBackgroundConnectionFutures;
293 };
294 
295 }  // namespace android::chre
296 #endif  // CHRE_HOST_HAL_CLIENT_H_