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) |%7sEvent Time (Ms)\n",
204 "", "", "");
205 debugDump.print("%26s| Current | Max | Mean | Max | Total\n",
206 "");
207 }
208
logMemAndComputeEntry(DebugDumpWrapper & debugDump) const209 void Nanoapp::logMemAndComputeEntry(DebugDumpWrapper &debugDump) const {
210 debugDump.print("%25s |", getAppName());
211 debugDump.print(" %7zu |", getTotalAllocatedBytes());
212 debugDump.print(" %7zu |", getPeakAllocatedBytes());
213 debugDump.print(" %7" PRIu64 " |", mEventProcessTime.getMean());
214 debugDump.print(" %7" PRIu64 " |", mEventProcessTime.getMax());
215 debugDump.print(" %7" PRIu64 "\n", mEventProcessTimeSinceBoot);
216 }
217
logMessageHistoryHeader(DebugDumpWrapper & debugDump) const218 void Nanoapp::logMessageHistoryHeader(DebugDumpWrapper &debugDump) const {
219 // Print time ranges for buckets
220 Nanoseconds now = SystemTime::getMonotonicTime();
221 uint64_t currentTimeMins = 0;
222 uint64_t nextTimeMins = 0;
223 uint64_t nanosecondsSince = 0;
224 char bucketLabel = 'A';
225
226 char bucketTags[kMaxSizeWakeupBuckets][4];
227 for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) {
228 bucketTags[i][0] = '[';
229 bucketTags[i][1] = bucketLabel++;
230 bucketTags[i][2] = ']';
231 bucketTags[i][3] = '\0';
232 }
233
234 debugDump.print(
235 "\nHistogram stat buckets cover the following time ranges:\n");
236
237 for (int32_t i = kMaxSizeWakeupBuckets - 1;
238 i > static_cast<int32_t>(mWakeupBuckets.size() - 1); --i) {
239 debugDump.print(" Bucket%s: N/A (unused)\n", bucketTags[i]);
240 }
241
242 for (int32_t i = static_cast<int32_t>(mWakeupBuckets.size() - 1); i >= 0;
243 --i) {
244 size_t idx = static_cast<size_t>(i);
245 nanosecondsSince =
246 now.toRawNanoseconds() - mWakeupBuckets[idx].creationTimestamp;
247 currentTimeMins = (nanosecondsSince / kOneMinuteInNanoseconds);
248
249 debugDump.print(" Bucket%s:", bucketTags[idx]);
250 debugDump.print(" %3" PRIu64 "", nextTimeMins);
251 debugDump.print(" - %3" PRIu64 " mins ago\n", currentTimeMins);
252 nextTimeMins = currentTimeMins;
253 }
254
255 // Precompute column widths for Wakeup Histogram, Message Histogram, and Event
256 // Time Histogram (ms). This allows the column width to be known and optimized
257 // at compile time, and avoids use of inconsistently supported "%*" in printf
258 //
259 // A static_assert is used to ensure these calculations are updated whenever
260 // the value of kMaxSizeWakeupBuckets changes
261 static_assert(kMaxSizeWakeupBuckets == 5,
262 "Update of nanoapp debug dump column widths requrired");
263
264 // Print table header
265 debugDump.print("\n%26s|", " Nanoapp ");
266 debugDump.print("%11s|", " Total w/u ");
267 // Wakeup Histogram = 2 + (4 * kMaxSizeWakeupBuckets);
268 debugDump.print("%22s|", " Wakeup Histogram ");
269 debugDump.print("%12s|", " Total Msgs ");
270 // Message Histogram = 2 + (4 * kMaxSizeWakeupBuckets);
271 debugDump.print("%22s|", " Message Histogram ");
272 debugDump.print("%12s|", " Event Time ");
273 // Event Time Histogram (ms) = 2 + (7 * kMaxSizeWakeupBuckets);
274 debugDump.print("%37s", " Event Time Histogram (ms) ");
275
276 debugDump.print("\n%26s|%11s|", "", "");
277 for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) {
278 debugDump.print(" %3s", bucketTags[i]);
279 }
280 debugDump.print(" |%12s|", "");
281 for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) {
282 debugDump.print(" %3s", bucketTags[i]);
283 }
284 debugDump.print(" |%12s|", "");
285 for (int32_t i = kMaxSizeWakeupBuckets - 1; i >= 0; --i) {
286 debugDump.print(" %7s", bucketTags[i]);
287 }
288 debugDump.print("\n");
289 }
290
logMessageHistoryEntry(DebugDumpWrapper & debugDump) const291 void Nanoapp::logMessageHistoryEntry(DebugDumpWrapper &debugDump) const {
292 debugDump.print("%25s |", getAppName());
293
294 // Print wakeupCount and histogram
295 debugDump.print(" %9" PRIu32 " | ", mNumWakeupsSinceBoot);
296 for (size_t i = kMaxSizeWakeupBuckets - 1; i > 0; --i) {
297 if (i >= mWakeupBuckets.size()) {
298 debugDump.print(" --,");
299 } else {
300 debugDump.print(" %2" PRIu16 ",", mWakeupBuckets[i].wakeupCount);
301 }
302 }
303 debugDump.print(" %2" PRIu16 " |", mWakeupBuckets.front().wakeupCount);
304
305 // Print hostMessage count and histogram
306 debugDump.print(" %10" PRIu32 " | ", mNumMessagesSentSinceBoot);
307 for (size_t i = kMaxSizeWakeupBuckets - 1; i > 0; --i) {
308 if (i >= mWakeupBuckets.size()) {
309 debugDump.print(" --,");
310 } else {
311 debugDump.print(" %2" PRIu16 ",", mWakeupBuckets[i].hostMessageCount);
312 }
313 }
314 debugDump.print(" %2" PRIu16 " |", mWakeupBuckets.front().hostMessageCount);
315
316 // Print eventProcessingTime count and histogram
317 debugDump.print(" %10" PRIu64 " | ", mEventProcessTimeSinceBoot);
318 for (size_t i = kMaxSizeWakeupBuckets - 1; i > 0; --i) {
319 if (i >= mWakeupBuckets.size()) {
320 debugDump.print(" --,");
321 } else {
322 debugDump.print(" %6" PRIu64 ",", mWakeupBuckets[i].eventProcessTime);
323 }
324 }
325 debugDump.print(" %6" PRIu64 "\n", mWakeupBuckets.front().eventProcessTime);
326 }
327
permitPermissionUse(uint32_t permission) const328 bool Nanoapp::permitPermissionUse(uint32_t permission) const {
329 return !supportsAppPermissions() ||
330 ((getAppPermissions() & permission) == permission);
331 }
332
registrationIndex(uint16_t eventType) const333 size_t Nanoapp::registrationIndex(uint16_t eventType) const {
334 size_t foundIndex = 0;
335 for (; foundIndex < mRegisteredEvents.size(); ++foundIndex) {
336 const EventRegistration ® = mRegisteredEvents[foundIndex];
337 if (reg.eventType == eventType) {
338 break;
339 }
340 }
341 return foundIndex;
342 }
343
handleGnssMeasurementDataEvent(const Event * event)344 void Nanoapp::handleGnssMeasurementDataEvent(const Event *event) {
345 #ifdef CHRE_GNSS_MEASUREMENT_BACK_COMPAT_ENABLED
346 const struct chreGnssDataEvent *data =
347 static_cast<const struct chreGnssDataEvent *>(event->eventData);
348 if (getTargetApiVersion() < CHRE_API_VERSION_1_5 &&
349 data->measurement_count > CHRE_GNSS_MAX_MEASUREMENT_PRE_1_5) {
350 chreGnssDataEvent localEvent;
351 memcpy(&localEvent, data, sizeof(struct chreGnssDataEvent));
352 localEvent.measurement_count = CHRE_GNSS_MAX_MEASUREMENT_PRE_1_5;
353 handleEvent(event->senderInstanceId, event->eventType, &localEvent);
354 } else
355 #endif // CHRE_GNSS_MEASUREMENT_BACK_COMPAT_ENABLED
356 {
357 handleEvent(event->senderInstanceId, event->eventType, event->eventData);
358 }
359 }
360
configureHostEndpointNotifications(uint16_t hostEndpointId,bool enable)361 bool Nanoapp::configureHostEndpointNotifications(uint16_t hostEndpointId,
362 bool enable) {
363 bool success = true;
364 bool registered = isRegisteredForHostEndpointNotifications(hostEndpointId);
365 if (enable && !registered) {
366 success = mRegisteredHostEndpoints.push_back(hostEndpointId);
367 if (!success) {
368 LOG_OOM();
369 }
370 } else if (!enable && registered) {
371 size_t index = mRegisteredHostEndpoints.find(hostEndpointId);
372 mRegisteredHostEndpoints.erase(index);
373 }
374
375 return success;
376 }
377
publishRpcServices(struct chreNanoappRpcService * services,size_t numServices)378 bool Nanoapp::publishRpcServices(struct chreNanoappRpcService *services,
379 size_t numServices) {
380 if (!mIsInNanoappStart) {
381 LOGE("publishRpcServices must be called from nanoappStart");
382 return false;
383 }
384
385 const size_t startSize = mRpcServices.size();
386 const size_t endSize = startSize + numServices;
387 if (endSize > kMaxRpcServices) {
388 return false;
389 }
390
391 mRpcServices.reserve(endSize);
392
393 bool success = true;
394
395 for (size_t i = 0; i < numServices; i++) {
396 if (!mRpcServices.push_back(services[i])) {
397 LOG_OOM();
398 success = false;
399 break;
400 }
401 }
402
403 if (success && mRpcServices.size() > 1) {
404 for (size_t i = 0; i < mRpcServices.size() - 1; i++) {
405 for (size_t j = i + 1; j < mRpcServices.size(); j++) {
406 if (mRpcServices[i].id == mRpcServices[j].id) {
407 LOGE("Service id = 0x%016" PRIx64 " can only be published once",
408 mRpcServices[i].id);
409 success = false;
410 }
411 }
412 }
413 }
414
415 if (!success) {
416 mRpcServices.resize(startSize);
417 }
418
419 return success;
420 }
421
linkHeapBlock(HeapBlockHeader * header)422 void Nanoapp::linkHeapBlock(HeapBlockHeader *header) {
423 header->data.next = mFirstHeader;
424 mFirstHeader = header;
425 }
426
unlinkHeapBlock(HeapBlockHeader * header)427 void Nanoapp::unlinkHeapBlock(HeapBlockHeader *header) {
428 if (mFirstHeader == nullptr) {
429 // The list is empty.
430 return;
431 }
432
433 if (header == mFirstHeader) {
434 mFirstHeader = header->data.next;
435 return;
436 }
437
438 HeapBlockHeader *previous = mFirstHeader;
439 HeapBlockHeader *current = mFirstHeader->data.next;
440
441 while (current != nullptr) {
442 if (current == header) {
443 previous->data.next = current->data.next;
444 break;
445 }
446 previous = current;
447 current = current->data.next;
448 }
449 }
450
451 } // namespace chre
452