• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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