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