xref: /aosp_15_r20/system/chre/core/nanoapp.cc (revision 84e339476a462649f82315436d70fd732297a399)
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 &reg = 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 &reg = 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 &reg = 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