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