1 /*
2 * Copyright (C) 2016 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 "chre/core/nanoapp.h"
18
19 #include "chre/core/event_loop_manager.h"
20 #include "chre/platform/assert.h"
21 #include "chre/platform/fatal_error.h"
22 #include "chre/platform/log.h"
23 #include "chre/platform/tracing.h"
24 #include "chre/util/system/debug_dump.h"
25 #include "chre_api/chre/gnss.h"
26 #include "chre_api/chre/version.h"
27
28 #include <algorithm>
29 #include <cstdint>
30
31 #if CHRE_FIRST_SUPPORTED_API_VERSION < CHRE_API_VERSION_1_5
32 #define CHRE_GNSS_MEASUREMENT_BACK_COMPAT_ENABLED
33 #endif
34
35 namespace chre {
36
37 constexpr size_t Nanoapp::kMaxSizeWakeupBuckets;
38
Nanoapp()39 Nanoapp::Nanoapp()
40 : Nanoapp(EventLoopManagerSingleton::get()->getNextInstanceId()) {}
41
Nanoapp(uint16_t instanceId)42 Nanoapp::Nanoapp(uint16_t instanceId) {
43 // Push first bucket onto wakeup bucket queue
44 cycleWakeupBuckets(SystemTime::getMonotonicTime());
45 mInstanceId = instanceId;
46 }
47
start()48 bool Nanoapp::start() {
49 // TODO(b/294116163): update trace with nanoapp instance id and nanoapp name
50 CHRE_TRACE_INSTANT("Nanoapp start");
51 mIsInNanoappStart = true;
52 bool success = PlatformNanoapp::start();
53 mIsInNanoappStart = false;
54 return success;
55 }
56
isRegisteredForBroadcastEvent(const Event * event) const57 bool Nanoapp::isRegisteredForBroadcastEvent(const Event *event) const {
58 bool registered = false;
59 uint16_t eventType = event->eventType;
60 uint16_t targetGroupIdMask = event->targetAppGroupMask;
61
62 // The host endpoint notification is a special case, because it requires
63 // explicit registration using host endpoint IDs rather than masks.
64 if (eventType == CHRE_EVENT_HOST_ENDPOINT_NOTIFICATION) {
65 const auto *data =
66 static_cast<const chreHostEndpointNotification *>(event->eventData);
67 registered = isRegisteredForHostEndpointNotifications(data->hostEndpointId);
68 } else {
69 size_t foundIndex = registrationIndex(eventType);
70 if (foundIndex < mRegisteredEvents.size()) {
71 const EventRegistration ® = mRegisteredEvents[foundIndex];
72 if (targetGroupIdMask & reg.groupIdMask) {
73 registered = true;
74 }
75 }
76 }
77 return registered;
78 }
79
registerForBroadcastEvent(uint16_t eventType,uint16_t groupIdMask)80 void Nanoapp::registerForBroadcastEvent(uint16_t eventType,
81 uint16_t groupIdMask) {
82 size_t foundIndex = registrationIndex(eventType);
83 if (foundIndex < mRegisteredEvents.size()) {
84 mRegisteredEvents[foundIndex].groupIdMask |= groupIdMask;
85 } else if (!mRegisteredEvents.push_back(
86 EventRegistration(eventType, groupIdMask))) {
87 FATAL_ERROR_OOM();
88 }
89 }
90
unregisterForBroadcastEvent(uint16_t eventType,uint16_t groupIdMask)91 void Nanoapp::unregisterForBroadcastEvent(uint16_t eventType,
92 uint16_t groupIdMask) {
93 size_t foundIndex = registrationIndex(eventType);
94 if (foundIndex < mRegisteredEvents.size()) {
95 EventRegistration ® = mRegisteredEvents[foundIndex];
96 reg.groupIdMask &= ~groupIdMask;
97 if (reg.groupIdMask == 0) {
98 mRegisteredEvents.erase(foundIndex);
99 }
100 }
101 }
102
configureNanoappInfoEvents(bool enable)103 void Nanoapp::configureNanoappInfoEvents(bool enable) {
104 if (enable) {
105 registerForBroadcastEvent(CHRE_EVENT_NANOAPP_STARTED);
106 registerForBroadcastEvent(CHRE_EVENT_NANOAPP_STOPPED);
107 } else {
108 unregisterForBroadcastEvent(CHRE_EVENT_NANOAPP_STARTED);
109 unregisterForBroadcastEvent(CHRE_EVENT_NANOAPP_STOPPED);
110 }
111 }
112
configureHostSleepEvents(bool enable)113 void Nanoapp::configureHostSleepEvents(bool enable) {
114 if (enable) {
115 registerForBroadcastEvent(CHRE_EVENT_HOST_AWAKE);
116 registerForBroadcastEvent(CHRE_EVENT_HOST_ASLEEP);
117 } else {
118 unregisterForBroadcastEvent(CHRE_EVENT_HOST_AWAKE);
119 unregisterForBroadcastEvent(CHRE_EVENT_HOST_ASLEEP);
120 }
121 }
122
configureDebugDumpEvent(bool enable)123 void Nanoapp::configureDebugDumpEvent(bool enable) {
124 if (enable) {
125 registerForBroadcastEvent(CHRE_EVENT_DEBUG_DUMP);
126 } else {
127 unregisterForBroadcastEvent(CHRE_EVENT_DEBUG_DUMP);
128 }
129 }
130
configureUserSettingEvent(uint8_t setting,bool enable)131 void Nanoapp::configureUserSettingEvent(uint8_t setting, bool enable) {
132 if (enable) {
133 registerForBroadcastEvent(CHRE_EVENT_SETTING_CHANGED_FIRST_EVENT + setting);
134 } else {
135 unregisterForBroadcastEvent(CHRE_EVENT_SETTING_CHANGED_FIRST_EVENT +
136 setting);
137 }
138 }
139
processEvent(Event * event)140 void Nanoapp::processEvent(Event *event) {
141 Nanoseconds eventStartTime = SystemTime::getMonotonicTime();
142 // TODO(b/294116163): update trace with event type and nanoapp name so it can
143 // be differentiated from other events
144 CHRE_TRACE_START("Handle event", "nanoapp", getInstanceId());
145 if (event->eventType == CHRE_EVENT_GNSS_DATA) {
146 handleGnssMeasurementDataEvent(event);
147 } else {
148 handleEvent(event->senderInstanceId, event->eventType, event->eventData);
149 }
150 // TODO(b/294116163): update trace with nanoapp name
151 CHRE_TRACE_END("Handle event", "nanoapp", getInstanceId());
152 Nanoseconds eventProcessTime =
153 SystemTime::getMonotonicTime() - eventStartTime;
154 uint64_t eventTimeMs = Milliseconds(eventProcessTime).getMilliseconds();
155 if (Milliseconds(eventProcessTime) >= Milliseconds(100)) {
156 LOGE("Nanoapp 0x%" PRIx64 " took %" PRIu64
157 " ms to process event type 0x%" PRIx16,
158 getAppId(), eventTimeMs, event->eventType);
159 }
160 mEventProcessTime.addValue(eventTimeMs);
161 mEventProcessTimeSinceBoot += eventTimeMs;
162 mWakeupBuckets.back().eventProcessTime += eventTimeMs;
163 }
164
blameHostWakeup()165 void Nanoapp::blameHostWakeup() {
166 if (mWakeupBuckets.back().wakeupCount < UINT16_MAX) {
167 ++mWakeupBuckets.back().wakeupCount;
168 }
169 if (mNumWakeupsSinceBoot < UINT32_MAX) ++mNumWakeupsSinceBoot;
170 }
171
blameHostMessageSent()172 void Nanoapp::blameHostMessageSent() {
173 if (mWakeupBuckets.back().hostMessageCount < UINT16_MAX) {
174 ++mWakeupBuckets.back().hostMessageCount;
175 }
176 if (mNumMessagesSentSinceBoot < UINT32_MAX) ++mNumMessagesSentSinceBoot;
177 }
178
cycleWakeupBuckets(Nanoseconds timestamp)179 void Nanoapp::cycleWakeupBuckets(Nanoseconds timestamp) {
180 if (mWakeupBuckets.full()) {
181 mWakeupBuckets.erase(0);
182 }
183 mWakeupBuckets.push_back(
184 BucketedStats(0, 0, 0, timestamp.toRawNanoseconds()));
185 }
186
logStateToBuffer(DebugDumpWrapper & debugDump) const187 void Nanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const {
188 debugDump.print(" Id=%" PRIu16 " 0x%016" PRIx64 " ", getInstanceId(),
189 getAppId());
190 PlatformNanoapp::logStateToBuffer(debugDump);
191 debugDump.print(" v%" PRIu32 ".%" PRIu32 ".%" PRIu32 " tgtAPI=%" PRIu32
192 ".%" PRIu32 "\n",
193 CHRE_EXTRACT_MAJOR_VERSION(getAppVersion()),
194 CHRE_EXTRACT_MINOR_VERSION(getAppVersion()),
195 CHRE_EXTRACT_PATCH_VERSION(getAppVersion()),
196 CHRE_EXTRACT_MAJOR_VERSION(getTargetApiVersion()),
197 CHRE_EXTRACT_MINOR_VERSION(getTargetApiVersion()));
198 }
199
logMemAndComputeHeader(DebugDumpWrapper & debugDump) const200 void Nanoapp::logMemAndComputeHeader(DebugDumpWrapper &debugDump) const {
201 // Print table header
202 // Nanoapp column sized to accommodate largest known name
203 debugDump.print("\n%10sNanoapp%9s| Mem Alloc (Bytes) |%2sEvent Time (Ms)\n",
204 "", "", "");
205 debugDump.print("%26s| Current | Max | Max | Total\n", "");
206 }
207
logMemAndComputeEntry(DebugDumpWrapper & debugDump) const208 void Nanoapp::logMemAndComputeEntry(DebugDumpWrapper &debugDump) const {
209 debugDump.print("%25s |", getAppName());
210 debugDump.print(" %7zu |", getTotalAllocatedBytes());
211 debugDump.print(" %7zu |", getPeakAllocatedBytes());
212 debugDump.print(" %7" PRIu64 " |", mEventProcessTime.getMax());
213 debugDump.print(" %7" PRIu64 "\n", mEventProcessTimeSinceBoot);
214 }
215
logMessageHistoryHeader(DebugDumpWrapper & debugDump) const216 void Nanoapp::logMessageHistoryHeader(DebugDumpWrapper &debugDump) const {
217 // Print time ranges for buckets
218 Nanoseconds now = SystemTime::getMonotonicTime();
219 uint64_t currentTimeMins = 0;
220 uint64_t nextTimeMins = 0;
221 uint64_t nanosecondsSince = 0;
222 char bucketLabel = 'A';
223
224 char bucketTags[kMaxSizeWakeupBuckets][4];
225 for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) {
226 bucketTags[i][0] = '[';
227 bucketTags[i][1] = bucketLabel++;
228 bucketTags[i][2] = ']';
229 bucketTags[i][3] = '\0';
230 }
231
232 debugDump.print(
233 "\nHistogram stat buckets cover the following time ranges:\n");
234
235 for (int32_t i = kMaxSizeWakeupBuckets - 1;
236 i > static_cast<int32_t>(mWakeupBuckets.size() - 1); --i) {
237 debugDump.print(" Bucket%s: N/A (unused)\n", bucketTags[i]);
238 }
239
240 for (int32_t i = static_cast<int32_t>(mWakeupBuckets.size() - 1); i >= 0;
241 --i) {
242 size_t idx = static_cast<size_t>(i);
243 nanosecondsSince =
244 now.toRawNanoseconds() - mWakeupBuckets[idx].creationTimestamp;
245 currentTimeMins = (nanosecondsSince / kOneMinuteInNanoseconds);
246
247 debugDump.print(" Bucket%s:", bucketTags[idx]);
248 debugDump.print(" %3" PRIu64 "", nextTimeMins);
249 debugDump.print(" - %3" PRIu64 " mins ago\n", currentTimeMins);
250 nextTimeMins = currentTimeMins;
251 }
252
253 // Precompute column widths for Wakeup Histogram, Message Histogram, and Event
254 // Time Histogram (ms). This allows the column width to be known and optimized
255 // at compile time, and avoids use of inconsistently supported "%*" in printf
256 //
257 // A static_assert is used to ensure these calculations are updated whenever
258 // the value of kMaxSizeWakeupBuckets changes
259 static_assert(kMaxSizeWakeupBuckets == 5,
260 "Update of nanoapp debug dump column widths requrired");
261
262 // Print table header
263 debugDump.print("\n%26s|", " Nanoapp ");
264 debugDump.print("%11s|", " Total w/u ");
265 // Wakeup Histogram = 2 + (4 * kMaxSizeWakeupBuckets);
266 debugDump.print("%22s|", " Wakeup Histogram ");
267 debugDump.print("%12s|", " Total Msgs ");
268 // Message Histogram = 2 + (4 * kMaxSizeWakeupBuckets);
269 debugDump.print("%22s|", " Message Histogram ");
270 debugDump.print("%12s|", " Event Time ");
271 // Event Time Histogram (ms) = 2 + (7 * kMaxSizeWakeupBuckets);
272 debugDump.print("%37s", " Event Time Histogram (ms) ");
273
274 debugDump.print("\n%26s|%11s|", "", "");
275 for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) {
276 debugDump.print(" %3s", bucketTags[i]);
277 }
278 debugDump.print(" |%12s|", "");
279 for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) {
280 debugDump.print(" %3s", bucketTags[i]);
281 }
282 debugDump.print(" |%12s|", "");
283 for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) {
284 debugDump.print(" %7s", bucketTags[i]);
285 }
286 debugDump.print("\n");
287 }
288
logMessageHistoryEntry(DebugDumpWrapper & debugDump) const289 void Nanoapp::logMessageHistoryEntry(DebugDumpWrapper &debugDump) const {
290 debugDump.print("%25s |", getAppName());
291
292 // Print wakeupCount and histogram
293 debugDump.print(" %9" PRIu32 " | ", mNumWakeupsSinceBoot);
294 for (size_t i = kMaxSizeWakeupBuckets - 1; i > 0; --i) {
295 if (i >= mWakeupBuckets.size()) {
296 debugDump.print(" --,");
297 } else {
298 debugDump.print(" %2" PRIu16 ",", mWakeupBuckets[i].wakeupCount);
299 }
300 }
301 debugDump.print(" %2" PRIu16 " |", mWakeupBuckets.front().wakeupCount);
302
303 // Print hostMessage count and histogram
304 debugDump.print(" %10" PRIu32 " | ", mNumMessagesSentSinceBoot);
305 for (size_t i = kMaxSizeWakeupBuckets - 1; i > 0; --i) {
306 if (i >= mWakeupBuckets.size()) {
307 debugDump.print(" --,");
308 } else {
309 debugDump.print(" %2" PRIu16 ",", mWakeupBuckets[i].hostMessageCount);
310 }
311 }
312 debugDump.print(" %2" PRIu16 " |", mWakeupBuckets.front().hostMessageCount);
313
314 // Print eventProcessingTime count and histogram
315 debugDump.print(" %10" PRIu64 " | ", mEventProcessTimeSinceBoot);
316 for (size_t i = kMaxSizeWakeupBuckets - 1; i > 0; --i) {
317 if (i >= mWakeupBuckets.size()) {
318 debugDump.print(" --,");
319 } else {
320 debugDump.print(" %6" PRIu64 ",", mWakeupBuckets[i].eventProcessTime);
321 }
322 }
323 debugDump.print(" %6" PRIu64 "\n", mWakeupBuckets.front().eventProcessTime);
324 }
325
permitPermissionUse(uint32_t permission) const326 bool Nanoapp::permitPermissionUse(uint32_t permission) const {
327 return !supportsAppPermissions() ||
328 ((getAppPermissions() & permission) == permission);
329 }
330
registrationIndex(uint16_t eventType) const331 size_t Nanoapp::registrationIndex(uint16_t eventType) const {
332 size_t foundIndex = 0;
333 for (; foundIndex < mRegisteredEvents.size(); ++foundIndex) {
334 const EventRegistration ® = mRegisteredEvents[foundIndex];
335 if (reg.eventType == eventType) {
336 break;
337 }
338 }
339 return foundIndex;
340 }
341
handleGnssMeasurementDataEvent(const Event * event)342 void Nanoapp::handleGnssMeasurementDataEvent(const Event *event) {
343 #ifdef CHRE_GNSS_MEASUREMENT_BACK_COMPAT_ENABLED
344 const struct chreGnssDataEvent *data =
345 static_cast<const struct chreGnssDataEvent *>(event->eventData);
346 if (getTargetApiVersion() < CHRE_API_VERSION_1_5 &&
347 data->measurement_count > CHRE_GNSS_MAX_MEASUREMENT_PRE_1_5) {
348 chreGnssDataEvent localEvent;
349 memcpy(&localEvent, data, sizeof(struct chreGnssDataEvent));
350 localEvent.measurement_count = CHRE_GNSS_MAX_MEASUREMENT_PRE_1_5;
351 handleEvent(event->senderInstanceId, event->eventType, &localEvent);
352 } else
353 #endif // CHRE_GNSS_MEASUREMENT_BACK_COMPAT_ENABLED
354 {
355 handleEvent(event->senderInstanceId, event->eventType, event->eventData);
356 }
357 }
358
configureHostEndpointNotifications(uint16_t hostEndpointId,bool enable)359 bool Nanoapp::configureHostEndpointNotifications(uint16_t hostEndpointId,
360 bool enable) {
361 bool success = true;
362 bool registered = isRegisteredForHostEndpointNotifications(hostEndpointId);
363 if (enable && !registered) {
364 success = mRegisteredHostEndpoints.push_back(hostEndpointId);
365 if (!success) {
366 LOG_OOM();
367 }
368 } else if (!enable && registered) {
369 size_t index = mRegisteredHostEndpoints.find(hostEndpointId);
370 mRegisteredHostEndpoints.erase(index);
371 }
372
373 return success;
374 }
375
publishRpcServices(struct chreNanoappRpcService * services,size_t numServices)376 bool Nanoapp::publishRpcServices(struct chreNanoappRpcService *services,
377 size_t numServices) {
378 if (!mIsInNanoappStart) {
379 LOGE("publishRpcServices must be called from nanoappStart");
380 return false;
381 }
382
383 const size_t startSize = mRpcServices.size();
384 const size_t endSize = startSize + numServices;
385 if (endSize > kMaxRpcServices) {
386 return false;
387 }
388
389 mRpcServices.reserve(endSize);
390
391 bool success = true;
392
393 for (size_t i = 0; i < numServices; i++) {
394 if (!mRpcServices.push_back(services[i])) {
395 LOG_OOM();
396 success = false;
397 break;
398 }
399 }
400
401 if (success && mRpcServices.size() > 1) {
402 for (size_t i = 0; i < mRpcServices.size() - 1; i++) {
403 for (size_t j = i + 1; j < mRpcServices.size(); j++) {
404 if (mRpcServices[i].id == mRpcServices[j].id) {
405 LOGE("Service id = 0x%016" PRIx64 " can only be published once",
406 mRpcServices[i].id);
407 success = false;
408 }
409 }
410 }
411 }
412
413 if (!success) {
414 mRpcServices.resize(startSize);
415 }
416
417 return success;
418 }
419
linkHeapBlock(HeapBlockHeader * header)420 void Nanoapp::linkHeapBlock(HeapBlockHeader *header) {
421 header->data.next = mFirstHeader;
422 mFirstHeader = header;
423 }
424
unlinkHeapBlock(HeapBlockHeader * header)425 void Nanoapp::unlinkHeapBlock(HeapBlockHeader *header) {
426 if (mFirstHeader == nullptr) {
427 // The list is empty.
428 return;
429 }
430
431 if (header == mFirstHeader) {
432 mFirstHeader = header->data.next;
433 return;
434 }
435
436 HeapBlockHeader *previous = mFirstHeader;
437 HeapBlockHeader *current = mFirstHeader->data.next;
438
439 while (current != nullptr) {
440 if (current == header) {
441 previous->data.next = current->data.next;
442 break;
443 }
444 previous = current;
445 current = current->data.next;
446 }
447 }
448
449 } // namespace chre
450