1 /*
2 * Copyright (C) 2020 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 #include <cstdlib>
18 #include <fstream>
19
20 #include "chre_host/daemon_base.h"
21 #include "chre_host/log.h"
22 #include "chre_host/napp_header.h"
23
24 #include <json/json.h>
25
26 #ifdef CHRE_DAEMON_METRIC_ENABLED
27 #include <aidl/android/frameworks/stats/IStats.h>
28 #include <android/binder_manager.h>
29 #include <hardware/google/pixel/pixelstats/pixelatoms.pb.h>
30
31 using ::aidl::android::frameworks::stats::IStats;
32 using ::aidl::android::frameworks::stats::VendorAtom;
33 using ::aidl::android::frameworks::stats::VendorAtomValue;
34 namespace PixelAtoms = ::android::hardware::google::pixel::PixelAtoms;
35 #endif // CHRE_DAEMON_METRIC_ENABLED
36
37 // Aliased for consistency with the way these symbols are referenced in
38 // CHRE-side code
39 namespace fbs = ::chre::fbs;
40
41 namespace android {
42 namespace chre {
43
ChreDaemonBase()44 ChreDaemonBase::ChreDaemonBase() : mChreShutdownRequested(false) {
45 mLogger.init();
46 }
47
loadPreloadedNanoapps()48 void ChreDaemonBase::loadPreloadedNanoapps() {
49 constexpr char kPreloadedNanoappsConfigPath[] =
50 "/vendor/etc/chre/preloaded_nanoapps.json";
51 std::ifstream configFileStream(kPreloadedNanoappsConfigPath);
52
53 Json::CharReaderBuilder builder;
54 Json::Value config;
55 if (!configFileStream) {
56 LOGE("Failed to open config file '%s': %d (%s)",
57 kPreloadedNanoappsConfigPath, errno, strerror(errno));
58 } else if (!Json::parseFromStream(builder, configFileStream, &config,
59 /* errorMessage = */ nullptr)) {
60 LOGE("Failed to parse nanoapp config file");
61 } else if (!config.isMember("nanoapps") || !config.isMember("source_dir")) {
62 LOGE("Malformed preloaded nanoapps config");
63 } else {
64 const Json::Value &directory = config["source_dir"];
65 for (Json::ArrayIndex i = 0; i < config["nanoapps"].size(); i++) {
66 const Json::Value &nanoapp = config["nanoapps"][i];
67 loadPreloadedNanoapp(directory.asString(), nanoapp.asString(),
68 static_cast<uint32_t>(i));
69 }
70 }
71 }
72
loadPreloadedNanoapp(const std::string & directory,const std::string & name,uint32_t transactionId)73 void ChreDaemonBase::loadPreloadedNanoapp(const std::string &directory,
74 const std::string &name,
75 uint32_t transactionId) {
76 std::vector<uint8_t> headerBuffer;
77
78 std::string headerFile = directory + "/" + name + ".napp_header";
79
80 // Only create the nanoapp filename as the CHRE framework will load from
81 // within the directory its own binary resides in.
82 std::string nanoappFilename = name + ".so";
83
84 if (readFileContents(headerFile.c_str(), &headerBuffer) &&
85 !loadNanoapp(headerBuffer, nanoappFilename, transactionId)) {
86 LOGE("Failed to load nanoapp: '%s'", name.c_str());
87 }
88 }
89
loadNanoapp(const std::vector<uint8_t> & header,const std::string & nanoappName,uint32_t transactionId)90 bool ChreDaemonBase::loadNanoapp(const std::vector<uint8_t> &header,
91 const std::string &nanoappName,
92 uint32_t transactionId) {
93 bool success = false;
94 if (header.size() != sizeof(NanoAppBinaryHeader)) {
95 LOGE("Header size mismatch");
96 } else {
97 // The header blob contains the struct above.
98 const auto *appHeader =
99 reinterpret_cast<const NanoAppBinaryHeader *>(header.data());
100
101 // Build the target API version from major and minor.
102 uint32_t targetApiVersion = (appHeader->targetChreApiMajorVersion << 24) |
103 (appHeader->targetChreApiMinorVersion << 16);
104
105 success = sendNanoappLoad(appHeader->appId, appHeader->appVersion,
106 targetApiVersion, nanoappName, transactionId);
107 }
108
109 return success;
110 }
111
sendNanoappLoad(uint64_t appId,uint32_t appVersion,uint32_t appTargetApiVersion,const std::string & appBinaryName,uint32_t transactionId)112 bool ChreDaemonBase::sendNanoappLoad(uint64_t appId, uint32_t appVersion,
113 uint32_t appTargetApiVersion,
114 const std::string &appBinaryName,
115 uint32_t transactionId) {
116 flatbuffers::FlatBufferBuilder builder;
117 HostProtocolHost::encodeLoadNanoappRequestForFile(
118 builder, transactionId, appId, appVersion, appTargetApiVersion,
119 appBinaryName.c_str());
120
121 bool success = sendMessageToChre(
122 kHostClientIdDaemon, builder.GetBufferPointer(), builder.GetSize());
123
124 if (!success) {
125 LOGE("Failed to send nanoapp filename.");
126 } else {
127 Transaction transaction = {
128 .transactionId = transactionId,
129 .nanoappId = appId,
130 };
131 mPreloadedNanoappPendingTransactions.push(transaction);
132 }
133
134 return success;
135 }
136
sendTimeSync(bool logOnError)137 bool ChreDaemonBase::sendTimeSync(bool logOnError) {
138 bool success = false;
139 int64_t timeOffset = getTimeOffset(&success);
140
141 if (success) {
142 flatbuffers::FlatBufferBuilder builder(64);
143 HostProtocolHost::encodeTimeSyncMessage(builder, timeOffset);
144 success = sendMessageToChre(kHostClientIdDaemon, builder.GetBufferPointer(),
145 builder.GetSize());
146
147 if (!success && logOnError) {
148 LOGE("Failed to deliver time sync message from host to CHRE");
149 }
150 }
151
152 return success;
153 }
154
sendTimeSyncWithRetry(size_t numRetries,useconds_t retryDelayUs,bool logOnError)155 bool ChreDaemonBase::sendTimeSyncWithRetry(size_t numRetries,
156 useconds_t retryDelayUs,
157 bool logOnError) {
158 bool success = false;
159 while (!success && (numRetries-- != 0)) {
160 success = sendTimeSync(logOnError);
161 if (!success) {
162 usleep(retryDelayUs);
163 }
164 }
165 return success;
166 }
167
sendNanConfigurationUpdate(bool nanEnabled)168 bool ChreDaemonBase::sendNanConfigurationUpdate(bool nanEnabled) {
169 flatbuffers::FlatBufferBuilder builder(32);
170 HostProtocolHost::encodeNanconfigurationUpdate(builder, nanEnabled);
171 return sendMessageToChre(kHostClientIdDaemon, builder.GetBufferPointer(),
172 builder.GetSize());
173 }
174
sendMessageToChre(uint16_t clientId,void * data,size_t length)175 bool ChreDaemonBase::sendMessageToChre(uint16_t clientId, void *data,
176 size_t length) {
177 bool success = false;
178 if (!HostProtocolHost::mutateHostClientId(data, length, clientId)) {
179 LOGE("Couldn't set host client ID in message container!");
180 } else {
181 LOGV("Delivering message from host (size %zu)", length);
182 getLogger().dump(static_cast<const uint8_t *>(data), length);
183 success = doSendMessage(data, length);
184 }
185
186 return success;
187 }
188
onMessageReceived(const unsigned char * messageBuffer,size_t messageLen)189 void ChreDaemonBase::onMessageReceived(const unsigned char *messageBuffer,
190 size_t messageLen) {
191 getLogger().dump(messageBuffer, messageLen);
192
193 uint16_t hostClientId;
194 fbs::ChreMessage messageType;
195 if (!HostProtocolHost::extractHostClientIdAndType(
196 messageBuffer, messageLen, &hostClientId, &messageType)) {
197 LOGW("Failed to extract host client ID from message - sending broadcast");
198 hostClientId = ::chre::kHostClientIdUnspecified;
199 }
200
201 if (messageType == fbs::ChreMessage::LogMessage) {
202 std::unique_ptr<fbs::MessageContainerT> container =
203 fbs::UnPackMessageContainer(messageBuffer);
204 const auto *logMessage = container->message.AsLogMessage();
205 const std::vector<int8_t> &logData = logMessage->buffer;
206
207 getLogger().log(reinterpret_cast<const uint8_t *>(logData.data()),
208 logData.size());
209 } else if (messageType == fbs::ChreMessage::LogMessageV2) {
210 std::unique_ptr<fbs::MessageContainerT> container =
211 fbs::UnPackMessageContainer(messageBuffer);
212 const auto *logMessage = container->message.AsLogMessageV2();
213 const std::vector<int8_t> &logDataBuffer = logMessage->buffer;
214 const auto *logData =
215 reinterpret_cast<const uint8_t *>(logDataBuffer.data());
216 uint32_t numLogsDropped = logMessage->num_logs_dropped;
217
218 getLogger().logV2(logData, logDataBuffer.size(), numLogsDropped);
219 } else if (messageType == fbs::ChreMessage::TimeSyncRequest) {
220 sendTimeSync(true /* logOnError */);
221 } else if (messageType == fbs::ChreMessage::LowPowerMicAccessRequest) {
222 configureLpma(true /* enabled */);
223 } else if (messageType == fbs::ChreMessage::LowPowerMicAccessRelease) {
224 configureLpma(false /* enabled */);
225 } else if (messageType == fbs::ChreMessage::MetricLog) {
226 #ifdef CHRE_DAEMON_METRIC_ENABLED
227 std::unique_ptr<fbs::MessageContainerT> container =
228 fbs::UnPackMessageContainer(messageBuffer);
229 const auto *metricMsg = container->message.AsMetricLog();
230 handleMetricLog(metricMsg);
231 #endif // CHRE_DAEMON_METRIC_ENABLED
232 } else if (messageType == fbs::ChreMessage::NanConfigurationRequest) {
233 std::unique_ptr<fbs::MessageContainerT> container =
234 fbs::UnPackMessageContainer(messageBuffer);
235 configureNan(container->message.AsNanConfigurationRequest()->enable);
236 } else if (hostClientId == kHostClientIdDaemon) {
237 handleDaemonMessage(messageBuffer);
238 } else if (hostClientId == ::chre::kHostClientIdUnspecified) {
239 mServer.sendToAllClients(messageBuffer, static_cast<size_t>(messageLen));
240 } else {
241 mServer.sendToClientById(messageBuffer, static_cast<size_t>(messageLen),
242 hostClientId);
243 }
244 }
245
readFileContents(const char * filename,std::vector<uint8_t> * buffer)246 bool ChreDaemonBase::readFileContents(const char *filename,
247 std::vector<uint8_t> *buffer) {
248 bool success = false;
249 std::ifstream file(filename, std::ios::binary | std::ios::ate);
250 if (!file) {
251 LOGE("Couldn't open file '%s': %d (%s)", filename, errno, strerror(errno));
252 } else {
253 ssize_t size = file.tellg();
254 file.seekg(0, std::ios::beg);
255
256 buffer->resize(size);
257 if (!file.read(reinterpret_cast<char *>(buffer->data()), size)) {
258 LOGE("Couldn't read from file '%s': %d (%s)", filename, errno,
259 strerror(errno));
260 } else {
261 success = true;
262 }
263 }
264
265 return success;
266 }
267
handleDaemonMessage(const uint8_t * message)268 void ChreDaemonBase::handleDaemonMessage(const uint8_t *message) {
269 std::unique_ptr<fbs::MessageContainerT> container =
270 fbs::UnPackMessageContainer(message);
271 if (container->message.type != fbs::ChreMessage::LoadNanoappResponse) {
272 LOGE("Invalid message from CHRE directed to daemon");
273 } else {
274 const auto *response = container->message.AsLoadNanoappResponse();
275 if (mPreloadedNanoappPendingTransactions.empty()) {
276 LOGE("Received nanoapp load response with no pending load");
277 } else if (mPreloadedNanoappPendingTransactions.front().transactionId !=
278 response->transaction_id) {
279 LOGE("Received nanoapp load response with ID %" PRIu32
280 " expected transaction id %" PRIu32,
281 response->transaction_id,
282 mPreloadedNanoappPendingTransactions.front().transactionId);
283 } else {
284 if (!response->success) {
285 LOGE("Received unsuccessful nanoapp load response with ID %" PRIu32,
286 mPreloadedNanoappPendingTransactions.front().transactionId);
287
288 #ifdef CHRE_DAEMON_METRIC_ENABLED
289 std::vector<VendorAtomValue> values(3);
290 values[0].set<VendorAtomValue::longValue>(
291 mPreloadedNanoappPendingTransactions.front().nanoappId);
292 values[1].set<VendorAtomValue::intValue>(
293 PixelAtoms::ChreHalNanoappLoadFailed::TYPE_PRELOADED);
294 values[2].set<VendorAtomValue::intValue>(
295 PixelAtoms::ChreHalNanoappLoadFailed::REASON_ERROR_GENERIC);
296 const VendorAtom atom{
297 .reverseDomainName = "",
298 .atomId = PixelAtoms::Atom::kChreHalNanoappLoadFailed,
299 .values{std::move(values)},
300 };
301 reportMetric(atom);
302 #endif // CHRE_DAEMON_METRIC_ENABLED
303 }
304 mPreloadedNanoappPendingTransactions.pop();
305 }
306 }
307 }
308
309 #ifdef CHRE_DAEMON_METRIC_ENABLED
handleMetricLog(const::chre::fbs::MetricLogT * metricMsg)310 void ChreDaemonBase::handleMetricLog(const ::chre::fbs::MetricLogT *metricMsg) {
311 const std::vector<int8_t> &encodedMetric = metricMsg->encoded_metric;
312
313 switch (metricMsg->id) {
314 case PixelAtoms::Atom::kChrePalOpenFailed: {
315 PixelAtoms::ChrePalOpenFailed metric;
316 if (!metric.ParseFromArray(encodedMetric.data(), encodedMetric.size())) {
317 LOGE("Failed to parse metric data");
318 } else {
319 std::vector<VendorAtomValue> values(2);
320 values[0].set<VendorAtomValue::intValue>(metric.pal());
321 values[1].set<VendorAtomValue::intValue>(metric.type());
322 const VendorAtom atom{
323 .reverseDomainName = "",
324 .atomId = PixelAtoms::Atom::kChrePalOpenFailed,
325 .values{std::move(values)},
326 };
327 reportMetric(atom);
328 }
329 break;
330 }
331 case PixelAtoms::Atom::kChreEventQueueSnapshotReported: {
332 PixelAtoms::ChreEventQueueSnapshotReported metric;
333 if (!metric.ParseFromArray(encodedMetric.data(), encodedMetric.size())) {
334 LOGE("Failed to parse metric data");
335 } else {
336 std::vector<VendorAtomValue> values(6);
337 values[0].set<VendorAtomValue::intValue>(
338 metric.snapshot_chre_get_time_ms());
339 values[1].set<VendorAtomValue::intValue>(metric.max_event_queue_size());
340 values[2].set<VendorAtomValue::intValue>(
341 metric.mean_event_queue_size());
342 values[3].set<VendorAtomValue::intValue>(metric.num_dropped_events());
343 // Last two values are not currently populated and will be implemented
344 // later. To avoid confusion of the interpretation, we use UINT32_MAX
345 // as a placeholder value.
346 values[4].set<VendorAtomValue::intValue>(
347 UINT32_MAX); // max_queue_delay_us
348 values[5].set<VendorAtomValue::intValue>(
349 UINT32_MAX); // mean_queue_delay_us
350 const VendorAtom atom{
351 .reverseDomainName = "",
352 .atomId = PixelAtoms::Atom::kChreEventQueueSnapshotReported,
353 .values{std::move(values)},
354 };
355 reportMetric(atom);
356 }
357 break;
358 }
359 default: {
360 #ifdef CHRE_LOG_ATOM_EXTENSION_ENABLED
361 handleVendorMetricLog(metricMsg);
362 #else
363 LOGW("Unknown metric ID %" PRIu32, metricMsg->id);
364 #endif // CHRE_LOG_ATOM_EXTENSION_ENABLED
365 }
366 }
367 }
368
reportMetric(const VendorAtom & atom)369 void ChreDaemonBase::reportMetric(const VendorAtom &atom) {
370 const std::string statsServiceName =
371 std::string(IStats::descriptor).append("/default");
372 if (!AServiceManager_isDeclared(statsServiceName.c_str())) {
373 LOGE("Stats service is not declared.");
374 return;
375 }
376
377 std::shared_ptr<IStats> stats_client = IStats::fromBinder(ndk::SpAIBinder(
378 AServiceManager_waitForService(statsServiceName.c_str())));
379 if (stats_client == nullptr) {
380 LOGE("Failed to get IStats service");
381 return;
382 }
383
384 const ndk::ScopedAStatus ret = stats_client->reportVendorAtom(atom);
385 if (!ret.isOk()) {
386 LOGE("Failed to report vendor atom");
387 }
388 }
389 #endif // CHRE_DAEMON_METRIC_ENABLED
390
configureNan(bool)391 void ChreDaemonBase::configureNan(bool /*enabled*/) {
392 LOGE("NAN not supported");
393 }
394
395 } // namespace chre
396 } // namespace android
397