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