xref: /aosp_15_r20/system/chre/host/common/hal_client.cc (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 LOG_TAG
18 #define LOG_TAG "CHRE.HAL.CLIENT"
19 #endif
20 
21 #include "chre_host/hal_client.h"
22 #include "chre_host/log.h"
23 
24 #include <android-base/properties.h>
25 #include <android_chre_flags.h>
26 #include <utils/SystemClock.h>
27 
28 #include <cinttypes>
29 #include <thread>
30 
31 namespace android::chre {
32 
33 using ::aidl::android::hardware::contexthub::IContextHub;
34 using ::aidl::android::hardware::contexthub::IContextHubCallback;
35 using ::android::base::GetBoolProperty;
36 using ::ndk::ScopedAStatus;
37 
38 namespace {
39 constexpr char kHalEnabledProperty[]{"vendor.chre.multiclient_hal.enabled"};
40 
41 // Multiclient HAL needs getUuid() added since V3 to identify each client.
42 constexpr int kMinHalInterfaceVersion = 3;
43 }  // namespace
44 
isServiceAvailable()45 bool HalClient::isServiceAvailable() {
46   return GetBoolProperty(kHalEnabledProperty, /* default_value= */ false);
47 }
48 
create(const std::shared_ptr<IContextHubCallback> & callback,int32_t contextHubId)49 std::unique_ptr<HalClient> HalClient::create(
50     const std::shared_ptr<IContextHubCallback> &callback,
51     int32_t contextHubId) {
52   if (callback == nullptr) {
53     LOGE("Callback function must not be null");
54     return nullptr;
55   }
56 
57   if (!isServiceAvailable()) {
58     LOGE("CHRE Multiclient HAL is not enabled on this device");
59     return nullptr;
60   }
61 
62   if (callback->version < kMinHalInterfaceVersion) {
63     LOGE("Callback interface version is %" PRIi32 ". It must be >= %" PRIi32,
64          callback->version, kMinHalInterfaceVersion);
65     return nullptr;
66   }
67 
68   return std::unique_ptr<HalClient>(new HalClient(callback, contextHubId));
69 }
70 
initConnection()71 HalError HalClient::initConnection() {
72   std::lock_guard<std::shared_mutex> lockGuard{mConnectionLock};
73 
74   if (mContextHub != nullptr) {
75     LOGW("%s is already connected to CHRE HAL", mClientName.c_str());
76     return HalError::SUCCESS;
77   }
78 
79   // Wait to connect to the service. Note that we don't do local retries
80   // because we're relying on the internal retries in
81   // AServiceManager_waitForService(). If HAL service has just restarted, it
82   // can take a few seconds to connect.
83   ndk::SpAIBinder binder{
84       AServiceManager_waitForService(kAidlServiceName.c_str())};
85   if (binder.get() == nullptr) {
86     return HalError::BINDER_CONNECTION_FAILED;
87   }
88 
89   // Link the death recipient to handle the binder disconnection event.
90   if (AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), this) !=
91       STATUS_OK) {
92     LOGE("Failed to link the binder death recipient");
93     return HalError::LINK_DEATH_RECIPIENT_FAILED;
94   }
95 
96   // Retrieve a handle of context hub service.
97   mContextHub = IContextHub::fromBinder(binder);
98   if (mContextHub == nullptr) {
99     LOGE("Got null context hub from the binder connection");
100     return HalError::NULL_CONTEXT_HUB_FROM_BINDER;
101   }
102 
103   // Enforce the required interface version for the service.
104   int32_t version = 0;
105   mContextHub->getInterfaceVersion(&version);
106   if (version < kMinHalInterfaceVersion) {
107     LOGE("CHRE multiclient HAL interface version is %" PRIi32
108          ". It must be >= %" PRIi32,
109          version, kMinHalInterfaceVersion);
110     mContextHub = nullptr;
111     return HalError::VERSION_TOO_LOW;
112   }
113 
114   // Register an IContextHubCallback.
115   ScopedAStatus status =
116       mContextHub->registerCallback(kDefaultContextHubId, mCallback);
117   if (!status.isOk()) {
118     LOGE("Unable to register callback: %s", status.getDescription().c_str());
119     // At this moment it's guaranteed that mCallback is not null and
120     // kDefaultContextHubId is valid. So if the registerCallback() still fails
121     // it's a hard failure and CHRE HAL is treated as disconnected.
122     mContextHub = nullptr;
123     return HalError::CALLBACK_REGISTRATION_FAILED;
124   }
125   mIsHalConnected = true;
126   LOGI("%s is successfully (re)connected to CHRE HAL", mClientName.c_str());
127   return HalError::SUCCESS;
128 }
129 
onHalDisconnected(void * cookie)130 void HalClient::onHalDisconnected(void *cookie) {
131   int64_t startTime = ::android::elapsedRealtime();
132   auto *halClient = static_cast<HalClient *>(cookie);
133   {
134     std::lock_guard<std::shared_mutex> lockGuard(halClient->mConnectionLock);
135     halClient->mContextHub = nullptr;
136     halClient->mIsHalConnected = false;
137   }
138   LOGW("%s is disconnected from CHRE HAL. Reconnecting...",
139        halClient->mClientName.c_str());
140 
141   HalError result = halClient->initConnection();
142   uint64_t duration = ::android::elapsedRealtime() - startTime;
143   if (result != HalError::SUCCESS) {
144     LOGE("Failed to fully reconnect to CHRE HAL after %" PRIu64
145          "ms, HalErrorCode: %" PRIi32,
146          duration, result);
147     return;
148   }
149   tryReconnectEndpoints(halClient);
150   LOGI("%s is reconnected to CHRE HAL after %" PRIu64 "ms",
151        halClient->mClientName.c_str(), duration);
152 }
153 
connectEndpoint(const HostEndpointInfo & hostEndpointInfo)154 ScopedAStatus HalClient::connectEndpoint(
155     const HostEndpointInfo &hostEndpointInfo) {
156   HostEndpointId endpointId = hostEndpointInfo.hostEndpointId;
157   if (isEndpointConnected(endpointId)) {
158     // Connecting the endpoint again even though it is already connected to let
159     // HAL and/or CHRE be the single place to control the behavior.
160     LOGW("Endpoint id %" PRIu16 " of %s is already connected", endpointId,
161          mClientName.c_str());
162   }
163   ScopedAStatus result = callIfConnected(
164       [&hostEndpointInfo](const std::shared_ptr<IContextHub> &hub) {
165         return hub->onHostEndpointConnected(hostEndpointInfo);
166       });
167   if (result.isOk()) {
168     insertConnectedEndpoint(hostEndpointInfo);
169   } else {
170     LOGE("Failed to connect endpoint id %" PRIu16 " of %s",
171          hostEndpointInfo.hostEndpointId, mClientName.c_str());
172   }
173   return result;
174 }
175 
disconnectEndpoint(HostEndpointId hostEndpointId)176 ScopedAStatus HalClient::disconnectEndpoint(HostEndpointId hostEndpointId) {
177   if (!isEndpointConnected(hostEndpointId)) {
178     // Disconnecting the endpoint again even though it is already disconnected
179     // to let HAL and/or CHRE be the single place to control the behavior.
180     LOGW("Endpoint id %" PRIu16 " of %s is already disconnected",
181          hostEndpointId, mClientName.c_str());
182   }
183   ScopedAStatus result = callIfConnected(
184       [&hostEndpointId](const std::shared_ptr<IContextHub> &hub) {
185         return hub->onHostEndpointDisconnected(hostEndpointId);
186       });
187   if (result.isOk()) {
188     removeConnectedEndpoint(hostEndpointId);
189   } else {
190     LOGE("Failed to disconnect the endpoint id %" PRIu16 " of %s",
191          hostEndpointId, mClientName.c_str());
192   }
193   return result;
194 }
195 
sendMessage(const ContextHubMessage & message)196 ScopedAStatus HalClient::sendMessage(const ContextHubMessage &message) {
197   uint16_t hostEndpointId = message.hostEndPoint;
198   if (!isEndpointConnected(hostEndpointId)) {
199     // This is still allowed now but in the future an error will be returned.
200     LOGW("Endpoint id %" PRIu16
201          " of %s is unknown or disconnected. Message sending will be skipped "
202          "in the future",
203          hostEndpointId, mClientName.c_str());
204   }
205   return callIfConnected([&](const std::shared_ptr<IContextHub> &hub) {
206     return hub->sendMessageToHub(mContextHubId, message);
207   });
208 }
209 
tryReconnectEndpoints(HalClient * halClient)210 void HalClient::tryReconnectEndpoints(HalClient *halClient) {
211   LOGW("CHRE has restarted. Reconnecting endpoints of %s",
212        halClient->mClientName.c_str());
213   std::lock_guard<std::shared_mutex> lockGuard(
214       halClient->mConnectedEndpointsLock);
215   for (const auto &[endpointId, endpointInfo] :
216        halClient->mConnectedEndpoints) {
217     if (!halClient
218              ->callIfConnected(
219                  [&endpointInfo](const std::shared_ptr<IContextHub> &hub) {
220                    return hub->onHostEndpointConnected(endpointInfo);
221                  })
222              .isOk()) {
223       LOGE("Failed to set up the connected state for endpoint %" PRIu16
224            " of %s after HAL restarts.",
225            endpointId, halClient->mClientName.c_str());
226       halClient->mConnectedEndpoints.erase(endpointId);
227     } else {
228       LOGI("Reconnected endpoint %" PRIu16 " of %s to CHRE HAL", endpointId,
229            halClient->mClientName.c_str());
230     }
231   }
232 }
233 
~HalClient()234 HalClient::~HalClient() {
235   std::lock_guard<std::mutex> lock(mBackgroundConnectionFuturesLock);
236   for (const auto &future : mBackgroundConnectionFutures) {
237     // Calling std::thread.join() has chance to hang if the background thread
238     // being joined is still waiting for connecting to the service. Therefore
239     // waiting for the thread to finish here instead and logging the timeout
240     // every second until system kills the process to report the abnormality.
241     while (future.wait_for(std::chrono::seconds(1)) !=
242            std::future_status::ready) {
243       LOGE(
244           "Failed to finish a background connection in time when HalClient is "
245           "being destructed. Waiting...");
246     }
247   }
248 }
249 }  // namespace android::chre