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   /** A bug fix flag guarding the lock holding reduction change. */
113   static bool reduceLockHolding();
114 
115   /** Returns true if this HalClient instance is connected to the HAL. */
isConnected()116   bool isConnected() {
117     return mContextHub != nullptr;
118   }
119 
120   /** Connects to CHRE HAL synchronously. */
connect()121   bool connect() {
122     return initConnection() == HalError::SUCCESS;
123   }
124 
125   /** Connects to CHRE HAL in background. */
connectInBackground(BackgroundConnectionCallback & callback)126   void connectInBackground(BackgroundConnectionCallback &callback) {
127     std::lock_guard<std::mutex> lock(mBackgroundConnectionFuturesLock);
128     // Policy std::launch::async is required to avoid lazy evaluation which can
129     // postpone the execution until get() of the future returned by std::async
130     // is called.
131     mBackgroundConnectionFutures.emplace_back(
132         std::async(std::launch::async, [&]() {
133           callback.onInitialization(initConnection() == HalError::SUCCESS);
134         }));
135   }
136 
queryNanoapps()137   ScopedAStatus queryNanoapps() {
138     return callIfConnected([&](const std::shared_ptr<IContextHub> &hub) {
139       return hub->queryNanoapps(mContextHubId);
140     });
141   }
142 
143   /** Sends a message to a Nanoapp. */
144   ScopedAStatus sendMessage(const ContextHubMessage &message);
145 
146   /** Connects a host endpoint to CHRE. */
147   ScopedAStatus connectEndpoint(const HostEndpointInfo &hostEndpointInfo);
148 
149   /** Disconnects a host endpoint from CHRE. */
150   ScopedAStatus disconnectEndpoint(char16_t hostEndpointId);
151 
152  protected:
153   class HalClientCallback : public BnContextHubCallback {
154    public:
HalClientCallback(const std::shared_ptr<IContextHubCallback> & callback,HalClient * halClient)155     explicit HalClientCallback(
156         const std::shared_ptr<IContextHubCallback> &callback,
157         HalClient *halClient)
158         : mCallback(callback), mHalClient(halClient) {}
159 
handleNanoappInfo(const std::vector<NanoappInfo> & appInfo)160     ScopedAStatus handleNanoappInfo(
161         const std::vector<NanoappInfo> &appInfo) override {
162       return mCallback->handleNanoappInfo(appInfo);
163     }
164 
handleContextHubMessage(const ContextHubMessage & msg,const std::vector<std::string> & msgContentPerms)165     ScopedAStatus handleContextHubMessage(
166         const ContextHubMessage &msg,
167         const std::vector<std::string> &msgContentPerms) override {
168       return mCallback->handleContextHubMessage(msg, msgContentPerms);
169     }
170 
handleContextHubAsyncEvent(AsyncEventType event)171     ScopedAStatus handleContextHubAsyncEvent(AsyncEventType event) override {
172       if (event == AsyncEventType::RESTARTED) {
173         tryReconnectEndpoints(mHalClient);
174       }
175       return mCallback->handleContextHubAsyncEvent(event);
176     }
177 
handleTransactionResult(int32_t transactionId,bool success)178     ScopedAStatus handleTransactionResult(int32_t transactionId,
179                                           bool success) override {
180       return mCallback->handleTransactionResult(transactionId, success);
181     }
182 
handleNanSessionRequest(const NanSessionRequest & request)183     ScopedAStatus handleNanSessionRequest(
184         const NanSessionRequest &request) override {
185       return mCallback->handleNanSessionRequest(request);
186     }
187 
handleMessageDeliveryStatus(char16_t hostEndPointId,const MessageDeliveryStatus & messageDeliveryStatus)188     ScopedAStatus handleMessageDeliveryStatus(
189         char16_t hostEndPointId,
190         const MessageDeliveryStatus &messageDeliveryStatus) override {
191       return mCallback->handleMessageDeliveryStatus(hostEndPointId,
192                                                     messageDeliveryStatus);
193     }
194 
getUuid(std::array<uint8_t,16> * outUuid)195     ScopedAStatus getUuid(std::array<uint8_t, 16> *outUuid) override {
196       return mCallback->getUuid(outUuid);
197     }
198 
getName(std::string * outName)199     ScopedAStatus getName(std::string *outName) override {
200       return mCallback->getName(outName);
201     }
202 
203    private:
204     std::shared_ptr<IContextHubCallback> mCallback;
205     HalClient *mHalClient;
206   };
207 
208   explicit HalClient(const std::shared_ptr<IContextHubCallback> &callback,
209                      int32_t contextHubId = kDefaultContextHubId)
mContextHubId(contextHubId)210       : mContextHubId(contextHubId) {
211     mCallback = ndk::SharedRefBase::make<HalClientCallback>(callback, this);
212     ABinderProcess_startThreadPool();
213     mDeathRecipient = ndk::ScopedAIBinder_DeathRecipient(
214         AIBinder_DeathRecipient_new(onHalDisconnected));
215     mCallback->getName(&mClientName);
216   }
217 
218   /**
219    * Initializes the connection to CHRE HAL.
220    */
221   HalError initConnection();
222 
223   using HostEndpointId = char16_t;
224 
225   const std::string kAidlServiceName =
226       std::string() + IContextHub::descriptor + "/default";
227 
228   /** The callback for a disconnected HAL binder connection. */
229   static void onHalDisconnected(void *cookie);
230 
231   /** Reconnect previously connected endpoints after CHRE or HAL restarts. */
232   static void tryReconnectEndpoints(HalClient *halClient);
233 
callIfConnected(const std::function<ScopedAStatus (const std::shared_ptr<IContextHub> & hub)> & func)234   ScopedAStatus callIfConnected(
235       const std::function<
236           ScopedAStatus(const std::shared_ptr<IContextHub> &hub)> &func) {
237     std::shared_ptr<IContextHub> hub;
238     {
239       // Make a copy of mContextHub so that even if HAL is disconnected and
240       // mContextHub is set to null the copy is kept as non-null to avoid crash.
241       // Still guard the copy by a shared lock to avoid torn writes.
242       std::shared_lock<std::shared_mutex> sharedLock(mConnectionLock);
243       hub = mContextHub;
244     }
245     if (hub == nullptr) {
246       return fromHalError(HalError::BINDER_DISCONNECTED);
247     }
248     return func(hub);
249   }
250 
isEndpointConnected(HostEndpointId hostEndpointId)251   bool isEndpointConnected(HostEndpointId hostEndpointId) {
252     std::shared_lock<std::shared_mutex> sharedLock(mConnectedEndpointsLock);
253     return mConnectedEndpoints.find(hostEndpointId) !=
254            mConnectedEndpoints.end();
255   }
256 
insertConnectedEndpoint(const HostEndpointInfo & hostEndpointInfo)257   void insertConnectedEndpoint(const HostEndpointInfo &hostEndpointInfo) {
258     std::lock_guard<std::shared_mutex> lockGuard(mConnectedEndpointsLock);
259     mConnectedEndpoints[hostEndpointInfo.hostEndpointId] = hostEndpointInfo;
260   }
261 
removeConnectedEndpoint(HostEndpointId hostEndpointId)262   void removeConnectedEndpoint(HostEndpointId hostEndpointId) {
263     std::lock_guard<std::shared_mutex> lockGuard(mConnectedEndpointsLock);
264     mConnectedEndpoints.erase(hostEndpointId);
265   }
266 
fromHalError(HalError errorCode)267   static ScopedAStatus fromHalError(HalError errorCode) {
268     return errorCode == HalError::SUCCESS
269                ? ScopedAStatus::ok()
270                : ScopedAStatus::fromServiceSpecificError(
271                      static_cast<int32_t>(errorCode));
272   }
273 
274   // Multi-contextHub is not supported at this moment.
275   int32_t mContextHubId;
276 
277   // The lock guarding mConnectedEndpoints.
278   std::shared_mutex mConnectedEndpointsLock;
279   std::unordered_map<HostEndpointId, HostEndpointInfo> mConnectedEndpoints{};
280 
281   // The lock guarding the init connection flow.
282   std::shared_mutex mConnectionLock;
283   std::shared_ptr<IContextHub> mContextHub;
284 
285   // Handler of the binder disconnection event with HAL.
286   ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
287 
288   std::shared_ptr<HalClientCallback> mCallback;
289 
290   std::string mClientName;
291 
292   // Lock guarding background connection threads.
293   std::mutex mBackgroundConnectionFuturesLock;
294   std::vector<std::future<void>> mBackgroundConnectionFutures;
295 };
296 
297 }  // namespace android::chre
298 #endif  // CHRE_HOST_HAL_CLIENT_H_