1 /*
2 * Copyright (C) 2021 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 #define LOG_TAG "ContextHubHal"
18 #define LOG_NDEBUG 1
19
20 #include "hal_chre_socket_connection.h"
21
22 #include <log/log.h>
23
24 #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED
25 #include <aidl/android/frameworks/stats/IStats.h>
26 #include <android/binder_manager.h>
27 #include <chre_atoms_log.h>
28 #include <utils/SystemClock.h>
29 #endif // CHRE_HAL_SOCKET_METRICS_ENABLED
30
31 namespace android {
32 namespace hardware {
33 namespace contexthub {
34 namespace common {
35 namespace implementation {
36
37 using chre::FragmentedLoadRequest;
38 using chre::FragmentedLoadTransaction;
39 using chre::HostProtocolHost;
40 using flatbuffers::FlatBufferBuilder;
41
42 #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED
43 using ::aidl::android::frameworks::stats::IStats;
44 using ::aidl::android::frameworks::stats::VendorAtom;
45 using ::aidl::android::frameworks::stats::VendorAtomValue;
46 #endif // CHRE_HAL_SOCKET_METRICS_ENABLED
47
HalChreSocketConnection(IChreSocketCallback * callback)48 HalChreSocketConnection::HalChreSocketConnection(
49 IChreSocketCallback *callback) {
50 constexpr char kChreSocketName[] = "chre";
51
52 mSocketCallbacks = sp<SocketCallbacks>::make(*this, callback);
53 if (!mClient.connectInBackground(kChreSocketName, mSocketCallbacks)) {
54 ALOGE("Couldn't start socket client");
55 }
56 }
57
getContextHubs(::chre::fbs::HubInfoResponseT * response)58 bool HalChreSocketConnection::getContextHubs(
59 ::chre::fbs::HubInfoResponseT *response) {
60 constexpr auto kHubInfoQueryTimeout = std::chrono::seconds(5);
61 ALOGV("%s", __func__);
62
63 // If we're not connected yet, give it some time
64 // TODO refactor from polling into conditional wait
65 int maxSleepIterations = 250;
66 while (!mHubInfoValid && !mClient.isConnected() && --maxSleepIterations > 0) {
67 std::this_thread::sleep_for(std::chrono::milliseconds(20));
68 }
69
70 if (!mClient.isConnected()) {
71 ALOGE("Couldn't connect to hub daemon");
72 } else if (!mHubInfoValid) {
73 // We haven't cached the hub details yet, so send a request and block
74 // waiting on a response
75 std::unique_lock<std::mutex> lock(mHubInfoMutex);
76 FlatBufferBuilder builder;
77 HostProtocolHost::encodeHubInfoRequest(builder);
78
79 ALOGD("Sending hub info request");
80 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
81 ALOGE("Couldn't send hub info request");
82 } else {
83 mHubInfoCond.wait_for(lock, kHubInfoQueryTimeout,
84 [this]() { return mHubInfoValid; });
85 }
86 }
87
88 if (mHubInfoValid) {
89 *response = mHubInfoResponse;
90 } else {
91 ALOGE("Unable to get hub info from CHRE");
92 }
93
94 return mHubInfoValid;
95 }
96
sendDebugConfiguration()97 bool HalChreSocketConnection::sendDebugConfiguration() {
98 FlatBufferBuilder builder;
99 HostProtocolHost::encodeDebugConfiguration(builder);
100 return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize());
101 }
102
sendMessageToHub(uint64_t nanoappId,uint32_t messageType,uint16_t hostEndpointId,const unsigned char * payload,size_t payloadLength)103 bool HalChreSocketConnection::sendMessageToHub(uint64_t nanoappId,
104 uint32_t messageType,
105 uint16_t hostEndpointId,
106 const unsigned char *payload,
107 size_t payloadLength) {
108 FlatBufferBuilder builder(1024);
109 HostProtocolHost::encodeNanoappMessage(
110 builder, nanoappId, messageType, hostEndpointId, payload, payloadLength);
111 return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize());
112 }
113
loadNanoapp(FragmentedLoadTransaction & transaction)114 bool HalChreSocketConnection::loadNanoapp(
115 FragmentedLoadTransaction &transaction) {
116 bool success = false;
117 std::lock_guard<std::mutex> lock(mPendingLoadTransactionMutex);
118
119 if (mPendingLoadTransaction.has_value()) {
120 ALOGE("Pending load transaction exists. Overriding pending request");
121 }
122
123 mPendingLoadTransaction = transaction;
124 success = sendFragmentedLoadNanoAppRequest(mPendingLoadTransaction.value());
125 if (!success) {
126 mPendingLoadTransaction.reset();
127 }
128
129 return success;
130 }
131
unloadNanoapp(uint64_t appId,uint32_t transactionId)132 bool HalChreSocketConnection::unloadNanoapp(uint64_t appId,
133 uint32_t transactionId) {
134 FlatBufferBuilder builder(64);
135 HostProtocolHost::encodeUnloadNanoappRequest(
136 builder, transactionId, appId, false /* allowSystemNanoappUnload */);
137 return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize());
138 }
139
queryNanoapps()140 bool HalChreSocketConnection::queryNanoapps() {
141 FlatBufferBuilder builder(64);
142 HostProtocolHost::encodeNanoappListRequest(builder);
143 return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize());
144 }
145
requestDebugDump()146 bool HalChreSocketConnection::requestDebugDump() {
147 FlatBufferBuilder builder;
148 HostProtocolHost::encodeDebugDumpRequest(builder);
149 return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize());
150 }
151
sendSettingChangedNotification(::chre::fbs::Setting fbsSetting,::chre::fbs::SettingState fbsState)152 bool HalChreSocketConnection::sendSettingChangedNotification(
153 ::chre::fbs::Setting fbsSetting, ::chre::fbs::SettingState fbsState) {
154 FlatBufferBuilder builder(64);
155 HostProtocolHost::encodeSettingChangeNotification(builder, fbsSetting,
156 fbsState);
157 return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize());
158 }
159
onHostEndpointConnected(uint16_t hostEndpointId,uint8_t type,const std::string & package_name,const std::string & attribution_tag)160 bool HalChreSocketConnection::onHostEndpointConnected(
161 uint16_t hostEndpointId, uint8_t type, const std::string &package_name,
162 const std::string &attribution_tag) {
163 FlatBufferBuilder builder(64);
164 HostProtocolHost::encodeHostEndpointConnected(builder, hostEndpointId, type,
165 package_name, attribution_tag);
166 return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize());
167 }
168
onHostEndpointDisconnected(uint16_t hostEndpointId)169 bool HalChreSocketConnection::onHostEndpointDisconnected(
170 uint16_t hostEndpointId) {
171 FlatBufferBuilder builder(64);
172 HostProtocolHost::encodeHostEndpointDisconnected(builder, hostEndpointId);
173 return mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize());
174 }
175
isLoadTransactionPending()176 bool HalChreSocketConnection::isLoadTransactionPending() {
177 std::lock_guard<std::mutex> lock(mPendingLoadTransactionMutex);
178 return mPendingLoadTransaction.has_value();
179 }
180
SocketCallbacks(HalChreSocketConnection & parent,IChreSocketCallback * callback)181 HalChreSocketConnection::SocketCallbacks::SocketCallbacks(
182 HalChreSocketConnection &parent, IChreSocketCallback *callback)
183 : mParent(parent), mCallback(callback) {
184 #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED
185 mLastClearedTimestamp = elapsedRealtime();
186 #endif // CHRE_HAL_SOCKET_METRICS_ENABLED
187 }
188
onMessageReceived(const void * data,size_t length)189 void HalChreSocketConnection::SocketCallbacks::onMessageReceived(
190 const void *data, size_t length) {
191 if (!HostProtocolHost::decodeMessageFromChre(data, length, *this)) {
192 ALOGE("Failed to decode message");
193 }
194 }
195
onConnected()196 void HalChreSocketConnection::SocketCallbacks::onConnected() {
197 ALOGI("Reconnected to CHRE daemon");
198 if (mHaveConnected) {
199 ALOGI("Reconnected to CHRE daemon");
200 mCallback->onContextHubRestarted();
201 }
202 mParent.sendDebugConfiguration();
203 mHaveConnected = true;
204 }
205
onDisconnected()206 void HalChreSocketConnection::SocketCallbacks::onDisconnected() {
207 ALOGW("Lost connection to CHRE daemon");
208 }
209
handleNanoappMessage(const::chre::fbs::NanoappMessageT & message)210 void HalChreSocketConnection::SocketCallbacks::handleNanoappMessage(
211 const ::chre::fbs::NanoappMessageT &message) {
212 ALOGD("Got message from nanoapp: ID 0x%" PRIx64, message.app_id);
213 mCallback->onNanoappMessage(message);
214
215 #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED
216 if (message.woke_host) {
217 // check and update the 24hour timer
218 std::lock_guard<std::mutex> lock(mNanoappWokeApCountMutex);
219 long nanoappId = message.app_id;
220 long timeElapsed = elapsedRealtime() - mLastClearedTimestamp;
221 if (timeElapsed > kOneDayinMillis) {
222 mNanoappWokeUpCount = 0;
223 mLastClearedTimestamp = elapsedRealtime();
224 }
225
226 // update and report the AP woke up metric
227 mNanoappWokeUpCount++;
228 if (mNanoappWokeUpCount < kMaxDailyReportedApWakeUp) {
229 // create and report the vendor atom
230 std::vector<VendorAtomValue> values(1);
231 values[0].set<VendorAtomValue::longValue>(nanoappId);
232
233 const VendorAtom atom{
234 .atomId = chre::Atoms::CHRE_AP_WAKE_UP_OCCURRED,
235 .values{std::move(values)},
236 };
237
238 mParent.reportMetric(atom);
239 }
240 }
241 #endif // CHRE_HAL_SOCKET_METRICS_ENABLED
242 }
243
handleHubInfoResponse(const::chre::fbs::HubInfoResponseT & response)244 void HalChreSocketConnection::SocketCallbacks::handleHubInfoResponse(
245 const ::chre::fbs::HubInfoResponseT &response) {
246 ALOGD("Got hub info response");
247
248 std::lock_guard<std::mutex> lock(mParent.mHubInfoMutex);
249 if (mParent.mHubInfoValid) {
250 ALOGI("Ignoring duplicate/unsolicited hub info response");
251 } else {
252 mParent.mHubInfoResponse = response;
253 mParent.mHubInfoValid = true;
254 mParent.mHubInfoCond.notify_all();
255 }
256 }
257
handleNanoappListResponse(const::chre::fbs::NanoappListResponseT & response)258 void HalChreSocketConnection::SocketCallbacks::handleNanoappListResponse(
259 const ::chre::fbs::NanoappListResponseT &response) {
260 ALOGD("Got nanoapp list response with %zu apps", response.nanoapps.size());
261 mCallback->onNanoappListResponse(response);
262 }
263
handleLoadNanoappResponse(const::chre::fbs::LoadNanoappResponseT & response)264 void HalChreSocketConnection::SocketCallbacks::handleLoadNanoappResponse(
265 const ::chre::fbs::LoadNanoappResponseT &response) {
266 ALOGD("Got load nanoapp response for transaction %" PRIu32
267 " fragment %" PRIu32 " with result %d",
268 response.transaction_id, response.fragment_id, response.success);
269 std::unique_lock<std::mutex> lock(mParent.mPendingLoadTransactionMutex);
270
271 // TODO: Handle timeout in receiving load response
272 if (!mParent.mPendingLoadTransaction.has_value()) {
273 ALOGE(
274 "Dropping unexpected load response (no pending transaction "
275 "exists)");
276 } else {
277 FragmentedLoadTransaction &transaction =
278 mParent.mPendingLoadTransaction.value();
279
280 if (!mParent.isExpectedLoadResponseLocked(response)) {
281 ALOGE("Dropping unexpected load response, expected transaction %" PRIu32
282 " fragment %" PRIu32 ", received transaction %" PRIu32
283 " fragment %" PRIu32,
284 transaction.getTransactionId(), mParent.mCurrentFragmentId,
285 response.transaction_id, response.fragment_id);
286 } else {
287 bool success = false;
288 bool continueLoadRequest = false;
289 if (response.success && !transaction.isComplete()) {
290 if (mParent.sendFragmentedLoadNanoAppRequest(transaction)) {
291 continueLoadRequest = true;
292 success = true;
293 }
294 } else {
295 success = response.success;
296 }
297
298 if (!continueLoadRequest) {
299 mParent.mPendingLoadTransaction.reset();
300 lock.unlock();
301 mCallback->onTransactionResult(response.transaction_id, success);
302 }
303 }
304 }
305 }
306
handleUnloadNanoappResponse(const::chre::fbs::UnloadNanoappResponseT & response)307 void HalChreSocketConnection::SocketCallbacks::handleUnloadNanoappResponse(
308 const ::chre::fbs::UnloadNanoappResponseT &response) {
309 ALOGV("Got unload nanoapp response for transaction %" PRIu32
310 " with result %d",
311 response.transaction_id, response.success);
312 mCallback->onTransactionResult(response.transaction_id, response.success);
313 }
314
handleDebugDumpData(const::chre::fbs::DebugDumpDataT & data)315 void HalChreSocketConnection::SocketCallbacks::handleDebugDumpData(
316 const ::chre::fbs::DebugDumpDataT &data) {
317 ALOGV("Got debug dump data, size %zu", data.debug_str.size());
318 mCallback->onDebugDumpData(data);
319 }
320
handleDebugDumpResponse(const::chre::fbs::DebugDumpResponseT & response)321 void HalChreSocketConnection::SocketCallbacks::handleDebugDumpResponse(
322 const ::chre::fbs::DebugDumpResponseT &response) {
323 ALOGV("Got debug dump response, success %d, data count %" PRIu32,
324 response.success, response.data_count);
325 mCallback->onDebugDumpComplete(response);
326 }
327
isExpectedLoadResponseLocked(const::chre::fbs::LoadNanoappResponseT & response)328 bool HalChreSocketConnection::isExpectedLoadResponseLocked(
329 const ::chre::fbs::LoadNanoappResponseT &response) {
330 return mPendingLoadTransaction.has_value() &&
331 (mPendingLoadTransaction->getTransactionId() ==
332 response.transaction_id) &&
333 (response.fragment_id == 0 ||
334 mCurrentFragmentId == response.fragment_id);
335 }
336
sendFragmentedLoadNanoAppRequest(FragmentedLoadTransaction & transaction)337 bool HalChreSocketConnection::sendFragmentedLoadNanoAppRequest(
338 FragmentedLoadTransaction &transaction) {
339 bool success = false;
340 const FragmentedLoadRequest &request = transaction.getNextRequest();
341
342 FlatBufferBuilder builder(128 + request.binary.size());
343 HostProtocolHost::encodeFragmentedLoadNanoappRequest(builder, request);
344
345 if (!mClient.sendMessage(builder.GetBufferPointer(), builder.GetSize())) {
346 ALOGE("Failed to send load request message (fragment ID = %zu)",
347 request.fragmentId);
348
349 #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED
350 // create and report the vendor atom
351 std::vector<VendorAtomValue> values(3);
352 values[0].set<VendorAtomValue::longValue>(request.appId);
353 values[1].set<VendorAtomValue::intValue>(
354 chre::Atoms::ChreHalNanoappLoadFailed::TYPE_DYNAMIC);
355 values[2].set<VendorAtomValue::intValue>(
356 chre::Atoms::ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC);
357
358 const VendorAtom atom{
359 .atomId = chre::Atoms::CHRE_HAL_NANOAPP_LOAD_FAILED,
360 .values{std::move(values)},
361 };
362 reportMetric(atom);
363 #endif // CHRE_HAL_SOCKET_METRICS_ENABLED
364
365 } else {
366 mCurrentFragmentId = request.fragmentId;
367 success = true;
368 }
369
370 return success;
371 }
372
373 #ifdef CHRE_HAL_SOCKET_METRICS_ENABLED
reportMetric(const VendorAtom atom)374 void HalChreSocketConnection::reportMetric(const VendorAtom atom) {
375 const std::string statsServiceName =
376 std::string(IStats::descriptor).append("/default");
377 if (!AServiceManager_isDeclared(statsServiceName.c_str())) {
378 ALOGE("Stats service is not declared.");
379 return;
380 }
381
382 std::shared_ptr<IStats> stats_client = IStats::fromBinder(ndk::SpAIBinder(
383 AServiceManager_waitForService(statsServiceName.c_str())));
384 if (stats_client == nullptr) {
385 ALOGE("Failed to get IStats service");
386 return;
387 }
388
389 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(atom);
390 if (!ret.isOk()) {
391 ALOGE("Failed to report vendor atom");
392 }
393 }
394 #endif // CHRE_HAL_SOCKET_METRICS_ENABLED
395
396 } // namespace implementation
397 } // namespace common
398 } // namespace contexthub
399 } // namespace hardware
400 } // namespace android
401