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/event_loop.h"
18 #include <cinttypes>
19 #include <cstdint>
20 #include <type_traits>
21
22 #include "chre/core/event.h"
23 #include "chre/core/event_loop_manager.h"
24 #include "chre/core/nanoapp.h"
25 #include "chre/platform/assert.h"
26 #include "chre/platform/context.h"
27 #include "chre/platform/event_loop_hooks.h"
28 #include "chre/platform/fatal_error.h"
29 #include "chre/platform/system_time.h"
30 #include "chre/util/conditional_lock_guard.h"
31 #include "chre/util/lock_guard.h"
32 #include "chre/util/system/debug_dump.h"
33 #include "chre/util/system/event_callbacks.h"
34 #include "chre/util/system/stats_container.h"
35 #include "chre/util/throttle.h"
36 #include "chre/util/time.h"
37 #include "chre_api/chre/version.h"
38
39 using ::chre::message::EndpointInfo;
40 using ::chre::message::EndpointType;
41
42 namespace chre {
43
44 // Out of line declaration required for nonintegral static types
45 constexpr Nanoseconds EventLoop::kIntervalWakeupBucket;
46
47 namespace {
48
49 #ifndef CHRE_STATIC_EVENT_LOOP
50 using DynamicMemoryPool =
51 SynchronizedExpandableMemoryPool<Event, CHRE_EVENT_PER_BLOCK,
52 CHRE_MAX_EVENT_BLOCKS>;
53 #endif
54 // TODO(b/264108686): Make this a compile time parameter.
55 // How many low priority event to remove if the event queue is full
56 // and a new event needs to be pushed.
57 constexpr size_t targetLowPriorityEventRemove = 4;
58
59 /**
60 * Populates a chreNanoappInfo structure using info from the given Nanoapp
61 * instance.
62 *
63 * @param app A potentially null pointer to the Nanoapp to read from
64 * @param info The structure to populate - should not be null, but this function
65 * will handle that input
66 *
67 * @return true if neither app nor info were null, and info was populated
68 */
populateNanoappInfo(const Nanoapp * app,struct chreNanoappInfo * info)69 bool populateNanoappInfo(const Nanoapp *app, struct chreNanoappInfo *info) {
70 bool success = false;
71
72 if (app != nullptr && info != nullptr) {
73 info->appId = app->getAppId();
74 info->version = app->getAppVersion();
75 info->instanceId = app->getInstanceId();
76 if (app->getTargetApiVersion() >= CHRE_API_VERSION_1_8) {
77 CHRE_ASSERT(app->getRpcServices().size() <= Nanoapp::kMaxRpcServices);
78 info->rpcServiceCount =
79 static_cast<uint8_t>(app->getRpcServices().size());
80 info->rpcServices = app->getRpcServices().data();
81 memset(&info->reserved, 0, sizeof(info->reserved));
82 }
83 success = true;
84 }
85
86 return success;
87 }
88
89 #ifndef CHRE_STATIC_EVENT_LOOP
90 /**
91 * @return true if a event is a low priority event and is not from nanoapp.
92 * Note: data and extraData are needed here to match the
93 * matching function signature. Both are not used here, but
94 * are used in other applications of
95 * SegmentedQueue::removeMatchedFromBack.
96 */
isNonNanoappLowPriorityEvent(Event * event,void *,void *)97 bool isNonNanoappLowPriorityEvent(Event *event, void * /* data */,
98 void * /* extraData */) {
99 CHRE_ASSERT_NOT_NULL(event);
100 return event->isLowPriority && event->senderInstanceId == kSystemInstanceId;
101 }
102
deallocateFromMemoryPool(Event * event,void * memoryPool)103 void deallocateFromMemoryPool(Event *event, void *memoryPool) {
104 static_cast<DynamicMemoryPool *>(memoryPool)->deallocate(event);
105 }
106 #endif
107
108 } // anonymous namespace
109
findNanoappInstanceIdByAppId(uint64_t appId,uint16_t * instanceId) const110 bool EventLoop::findNanoappInstanceIdByAppId(uint64_t appId,
111 uint16_t *instanceId) const {
112 CHRE_ASSERT(instanceId != nullptr);
113 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
114
115 bool found = false;
116 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
117 if (app->getAppId() == appId) {
118 *instanceId = app->getInstanceId();
119 found = true;
120 break;
121 }
122 }
123
124 return found;
125 }
126
forEachNanoapp(NanoappCallbackFunction * callback,void * data)127 void EventLoop::forEachNanoapp(NanoappCallbackFunction *callback, void *data) {
128 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
129
130 for (const UniquePtr<Nanoapp> &nanoapp : mNanoapps) {
131 callback(nanoapp.get(), data);
132 }
133 }
134
invokeMessageFreeFunction(uint64_t appId,chreMessageFreeFunction * freeFunction,void * message,size_t messageSize)135 void EventLoop::invokeMessageFreeFunction(uint64_t appId,
136 chreMessageFreeFunction *freeFunction,
137 void *message, size_t messageSize) {
138 Nanoapp *nanoapp = lookupAppByAppId(appId);
139 if (nanoapp == nullptr) {
140 LOGE("Couldn't find app 0x%016" PRIx64 " for message free callback", appId);
141 } else {
142 auto prevCurrentApp = mCurrentApp;
143 mCurrentApp = nanoapp;
144 freeFunction(message, messageSize);
145 mCurrentApp = prevCurrentApp;
146 }
147 }
148
run()149 void EventLoop::run() {
150 LOGI("EventLoop start");
151
152 while (mRunning) {
153 // Events are delivered in a single stage: they arrive in the inbound event
154 // queue mEvents (potentially posted from another thread), then within
155 // this context these events are distributed to all interested Nanoapps,
156 // with their free callback invoked after distribution.
157 mEventPoolUsage.addValue(static_cast<uint32_t>(mEvents.size()));
158
159 // mEvents.pop() will be a blocking call if mEvents.empty()
160 Event *event = mEvents.pop();
161 // Need size() + 1 since the to-be-processed event has already been removed.
162 mPowerControlManager.preEventLoopProcess(mEvents.size() + 1);
163 distributeEvent(event);
164
165 mPowerControlManager.postEventLoopProcess(mEvents.size());
166 }
167
168 // Purge the main queue of events pending distribution. All nanoapps should be
169 // prevented from sending events or messages at this point via
170 // currentNanoappIsStopping() returning true.
171 while (!mEvents.empty()) {
172 freeEvent(mEvents.pop());
173 }
174
175 // Unload all running nanoapps
176 while (!mNanoapps.empty()) {
177 unloadNanoappAtIndex(mNanoapps.size() - 1);
178 }
179
180 LOGI("Exiting EventLoop");
181 }
182
startNanoapp(UniquePtr<Nanoapp> & nanoapp)183 bool EventLoop::startNanoapp(UniquePtr<Nanoapp> &nanoapp) {
184 CHRE_ASSERT(!nanoapp.isNull());
185 bool success = false;
186 auto *eventLoopManager = EventLoopManagerSingleton::get();
187 EventLoop &eventLoop = eventLoopManager->getEventLoop();
188 uint16_t existingInstanceId;
189
190 if (nanoapp.isNull()) {
191 // no-op, invalid argument
192 } else if (nanoapp->getTargetApiVersion() <
193 CHRE_FIRST_SUPPORTED_API_VERSION) {
194 LOGE("Incompatible nanoapp (target ver 0x%" PRIx32
195 ", first supported ver 0x%" PRIx32 ")",
196 nanoapp->getTargetApiVersion(),
197 static_cast<uint32_t>(CHRE_FIRST_SUPPORTED_API_VERSION));
198 } else if (eventLoop.findNanoappInstanceIdByAppId(nanoapp->getAppId(),
199 &existingInstanceId)) {
200 LOGE("App with ID 0x%016" PRIx64 " already exists as instance ID %" PRIu16,
201 nanoapp->getAppId(), existingInstanceId);
202 } else {
203 Nanoapp *newNanoapp = nanoapp.get();
204 {
205 LockGuard<Mutex> lock(mNanoappsLock);
206 success = mNanoapps.push_back(std::move(nanoapp));
207 // After this point, nanoapp is null as we've transferred ownership into
208 // mNanoapps.back() - use newNanoapp to reference it
209 }
210 if (!success) {
211 LOG_OOM();
212 } else {
213 mCurrentApp = newNanoapp;
214 success = newNanoapp->start();
215 mCurrentApp = nullptr;
216 if (!success) {
217 LOGE("Nanoapp %" PRIu16 " failed to start",
218 newNanoapp->getInstanceId());
219 unloadNanoapp(newNanoapp->getInstanceId(),
220 /*allowSystemNanoappUnload=*/true,
221 /*nanoappStarted=*/false);
222 } else {
223 notifyAppStatusChange(CHRE_EVENT_NANOAPP_STARTED, *newNanoapp);
224 }
225 }
226 }
227
228 return success;
229 }
230
unloadNanoapp(uint16_t instanceId,bool allowSystemNanoappUnload,bool nanoappStarted)231 bool EventLoop::unloadNanoapp(uint16_t instanceId,
232 bool allowSystemNanoappUnload,
233 bool nanoappStarted) {
234 bool unloaded = false;
235
236 for (size_t i = 0; i < mNanoapps.size(); i++) {
237 if (instanceId == mNanoapps[i]->getInstanceId()) {
238 if (!allowSystemNanoappUnload && mNanoapps[i]->isSystemNanoapp()) {
239 LOGE("Refusing to unload system nanoapp");
240 } else {
241 // Make sure all messages sent by this nanoapp at least have their
242 // associated free callback processing pending in the event queue (i.e.
243 // there are no messages pending delivery to the host)
244 EventLoopManagerSingleton::get()
245 ->getHostCommsManager()
246 .flushNanoappMessages(*mNanoapps[i]);
247
248 // Mark that this nanoapp is stopping early, so it can't send events or
249 // messages during the nanoapp event queue flush
250 mStoppingNanoapp = mNanoapps[i].get();
251
252 if (nanoappStarted) {
253 // Distribute all inbound events we have at this time - here we're
254 // interested in handling any message free callbacks generated by
255 // flushNanoappMessages()
256 flushInboundEventQueue();
257
258 // Post the unload event now (so we can reference the Nanoapp instance
259 // directly), but nanoapps won't get it until after the unload
260 // completes. No need to notify status change if nanoapps failed to
261 // start.
262 notifyAppStatusChange(CHRE_EVENT_NANOAPP_STOPPED, *mStoppingNanoapp);
263 }
264
265 // Finally, we are at a point where there should not be any pending
266 // events or messages sent by the app that could potentially reference
267 // the nanoapp's memory, so we are safe to unload it
268 unloadNanoappAtIndex(i, nanoappStarted);
269 mStoppingNanoapp = nullptr;
270
271 LOGD("Unloaded nanoapp with instanceId %" PRIu16, instanceId);
272 unloaded = true;
273 }
274 break;
275 }
276 }
277
278 return unloaded;
279 }
280
removeNonNanoappLowPriorityEventsFromBack(size_t removeNum)281 bool EventLoop::removeNonNanoappLowPriorityEventsFromBack(
282 [[maybe_unused]] size_t removeNum) {
283 #ifdef CHRE_STATIC_EVENT_LOOP
284 return false;
285 #else
286 if (removeNum == 0) {
287 return true;
288 }
289
290 size_t numRemovedEvent = mEvents.removeMatchedFromBack(
291 isNonNanoappLowPriorityEvent, /* data= */ nullptr,
292 /* extraData= */ nullptr, removeNum, deallocateFromMemoryPool,
293 &mEventPool);
294 if (numRemovedEvent == 0 || numRemovedEvent == SIZE_MAX) {
295 LOGW("Cannot remove any low priority event");
296 } else {
297 mNumDroppedLowPriEvents += numRemovedEvent;
298 }
299 return numRemovedEvent > 0;
300 #endif
301 }
302
hasNoSpaceForHighPriorityEvent()303 bool EventLoop::hasNoSpaceForHighPriorityEvent() {
304 return mEventPool.full() && !removeNonNanoappLowPriorityEventsFromBack(
305 targetLowPriorityEventRemove);
306 }
307
distributeEventSync(uint16_t eventType,void * eventData,uint16_t targetInstanceId,uint16_t targetGroupMask)308 bool EventLoop::distributeEventSync(uint16_t eventType, void *eventData,
309 uint16_t targetInstanceId,
310 uint16_t targetGroupMask) {
311 CHRE_ASSERT(inEventLoopThread());
312 Event event(eventType, eventData,
313 /* freeCallback= */ nullptr,
314 /* isLowPriority= */ false,
315 /* senderInstanceId= */ kSystemInstanceId, targetInstanceId,
316 targetGroupMask);
317 return distributeEventCommon(&event);
318 }
319
320 // TODO(b/264108686): Refactor this function and postSystemEvent
postEventOrDie(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,uint16_t targetInstanceId,uint16_t targetGroupMask)321 void EventLoop::postEventOrDie(uint16_t eventType, void *eventData,
322 chreEventCompleteFunction *freeCallback,
323 uint16_t targetInstanceId,
324 uint16_t targetGroupMask) {
325 if (mRunning) {
326 if (hasNoSpaceForHighPriorityEvent() ||
327 !allocateAndPostEvent(eventType, eventData, freeCallback,
328 /* isLowPriority= */ false, kSystemInstanceId,
329 targetInstanceId, targetGroupMask)) {
330 CHRE_HANDLE_FAILED_SYSTEM_EVENT_ENQUEUE(
331 this, eventType, eventData, freeCallback, kSystemInstanceId,
332 targetInstanceId, targetGroupMask);
333 FATAL_ERROR("Failed to post critical system event 0x%" PRIx16, eventType);
334 }
335 } else if (freeCallback != nullptr) {
336 freeCallback(eventType, eventData);
337 }
338 }
339
postSystemEvent(uint16_t eventType,void * eventData,SystemEventCallbackFunction * callback,void * extraData)340 bool EventLoop::postSystemEvent(uint16_t eventType, void *eventData,
341 SystemEventCallbackFunction *callback,
342 void *extraData) {
343 if (!mRunning) {
344 return false;
345 }
346
347 if (hasNoSpaceForHighPriorityEvent()) {
348 CHRE_HANDLE_EVENT_QUEUE_FULL_DURING_SYSTEM_POST(this, eventType, eventData,
349 callback, extraData);
350 FATAL_ERROR("Failed to post critical system event 0x%" PRIx16
351 ": Full of high priority "
352 "events",
353 eventType);
354 }
355
356 Event *event = mEventPool.allocate(eventType, eventData, callback, extraData);
357 if (event == nullptr || !mEvents.push(event)) {
358 CHRE_HANDLE_FAILED_SYSTEM_EVENT_ENQUEUE(
359 this, eventType, eventData, callback, kSystemInstanceId,
360 kBroadcastInstanceId, kDefaultTargetGroupMask);
361 FATAL_ERROR("Failed to post critical system event 0x%" PRIx16
362 ": out of memory",
363 eventType);
364 }
365
366 return true;
367 }
368
postLowPriorityEventOrFree(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,uint16_t senderInstanceId,uint16_t targetInstanceId,uint16_t targetGroupMask)369 bool EventLoop::postLowPriorityEventOrFree(
370 uint16_t eventType, void *eventData,
371 chreEventCompleteFunction *freeCallback, uint16_t senderInstanceId,
372 uint16_t targetInstanceId, uint16_t targetGroupMask) {
373 bool eventPosted = false;
374
375 if (mRunning) {
376 eventPosted =
377 allocateAndPostEvent(eventType, eventData, freeCallback,
378 /* isLowPriority= */ true, senderInstanceId,
379 targetInstanceId, targetGroupMask);
380 if (!eventPosted) {
381 LOGE("Failed to allocate event 0x%" PRIx16 " to instanceId %" PRIu16,
382 eventType, targetInstanceId);
383 CHRE_HANDLE_LOW_PRIORITY_ENQUEUE_FAILURE(
384 this, eventType, eventData, freeCallback, senderInstanceId,
385 targetInstanceId, targetGroupMask);
386 ++mNumDroppedLowPriEvents;
387 }
388 }
389
390 if (!eventPosted && freeCallback != nullptr) {
391 freeCallback(eventType, eventData);
392 }
393
394 return eventPosted;
395 }
396
stop()397 void EventLoop::stop() {
398 auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) {
399 auto *obj = static_cast<EventLoop *>(data);
400 obj->onStopComplete();
401 };
402
403 // Stop accepting new events and tell the main loop to finish
404 postSystemEvent(static_cast<uint16_t>(SystemCallbackType::Shutdown),
405 /*eventData=*/this, callback, /*extraData=*/nullptr);
406 }
407
onStopComplete()408 void EventLoop::onStopComplete() {
409 mRunning = false;
410 }
411
findNanoappByInstanceId(uint16_t instanceId) const412 Nanoapp *EventLoop::findNanoappByInstanceId(uint16_t instanceId) const {
413 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
414 return lookupAppByInstanceId(instanceId);
415 }
416
findNanoappByAppId(uint64_t appId) const417 Nanoapp *EventLoop::findNanoappByAppId(uint64_t appId) const {
418 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
419 return lookupAppByAppId(appId);
420 }
421
populateNanoappInfoForAppId(uint64_t appId,struct chreNanoappInfo * info) const422 bool EventLoop::populateNanoappInfoForAppId(
423 uint64_t appId, struct chreNanoappInfo *info) const {
424 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
425 Nanoapp *app = lookupAppByAppId(appId);
426 return populateNanoappInfo(app, info);
427 }
428
populateNanoappInfoForInstanceId(uint16_t instanceId,struct chreNanoappInfo * info) const429 bool EventLoop::populateNanoappInfoForInstanceId(
430 uint16_t instanceId, struct chreNanoappInfo *info) const {
431 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
432 Nanoapp *app = lookupAppByInstanceId(instanceId);
433 return populateNanoappInfo(app, info);
434 }
435
currentNanoappIsStopping() const436 bool EventLoop::currentNanoappIsStopping() const {
437 return (mCurrentApp == mStoppingNanoapp || !mRunning);
438 }
439
logStateToBuffer(DebugDumpWrapper & debugDump) const440 void EventLoop::logStateToBuffer(DebugDumpWrapper &debugDump) const {
441 debugDump.print("\nEvent Loop:\n");
442 debugDump.print(" Max event pool usage: %" PRIu32 "/%zu\n",
443 mEventPoolUsage.getMax(), kMaxEventCount);
444 debugDump.print(" Number of low priority events dropped: %" PRIu32 "\n",
445 mNumDroppedLowPriEvents);
446
447 Nanoseconds timeSince =
448 SystemTime::getMonotonicTime() - mTimeLastWakeupBucketCycled;
449 uint64_t timeSinceMins =
450 timeSince.toRawNanoseconds() / kOneMinuteInNanoseconds;
451 uint64_t durationMins =
452 kIntervalWakeupBucket.toRawNanoseconds() / kOneMinuteInNanoseconds;
453 debugDump.print(" Nanoapp host wakeup tracking: cycled %" PRIu64
454 " mins ago, bucketDuration=%" PRIu64 "mins\n",
455 timeSinceMins, durationMins);
456
457 debugDump.print("\nNanoapps:\n");
458
459 if (mNanoapps.size()) {
460 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
461 app->logStateToBuffer(debugDump);
462 }
463
464 mNanoapps[0]->logMemAndComputeHeader(debugDump);
465 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
466 app->logMemAndComputeEntry(debugDump);
467 }
468
469 mNanoapps[0]->logMessageHistoryHeader(debugDump);
470 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
471 app->logMessageHistoryEntry(debugDump);
472 }
473 }
474 }
475
onMatchingNanoappEndpoint(const pw::Function<bool (const EndpointInfo &)> & function)476 void EventLoop::onMatchingNanoappEndpoint(
477 const pw::Function<bool(const EndpointInfo &)> &function) {
478 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
479
480 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
481 if (function(getEndpointInfoFromNanoappLocked(*app.get()))) {
482 break;
483 }
484 }
485 }
486
getEndpointInfo(uint64_t appId)487 std::optional<EndpointInfo> EventLoop::getEndpointInfo(uint64_t appId) {
488 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
489 Nanoapp *app = lookupAppByAppId(appId);
490 return app == nullptr
491 ? std::nullopt
492 : std::make_optional(getEndpointInfoFromNanoappLocked(*app));
493 }
494
allocateAndPostEvent(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,bool isLowPriority,uint16_t senderInstanceId,uint16_t targetInstanceId,uint16_t targetGroupMask)495 bool EventLoop::allocateAndPostEvent(uint16_t eventType, void *eventData,
496 chreEventCompleteFunction *freeCallback,
497 bool isLowPriority,
498 uint16_t senderInstanceId,
499 uint16_t targetInstanceId,
500 uint16_t targetGroupMask) {
501 bool success = false;
502
503 Event *event =
504 mEventPool.allocate(eventType, eventData, freeCallback, isLowPriority,
505 senderInstanceId, targetInstanceId, targetGroupMask);
506 if (event != nullptr) {
507 success = mEvents.push(event);
508 }
509 if (!success) {
510 LOG_OOM();
511 }
512
513 return success;
514 }
515
deliverNextEvent(const UniquePtr<Nanoapp> & app,Event * event)516 void EventLoop::deliverNextEvent(const UniquePtr<Nanoapp> &app, Event *event) {
517 constexpr Seconds kLatencyThreshold = Seconds(1);
518 constexpr Seconds kThrottleInterval(1);
519 constexpr uint16_t kThrottleCount = 10;
520
521 // Handle time rollover. If Event ever changes the type used to store the
522 // received time, this will need to be updated.
523 uint32_t now = Event::getTimeMillis();
524 static_assert(
525 std::is_same<decltype(event->receivedTimeMillis), const uint16_t>::value);
526 if (now < event->receivedTimeMillis) {
527 now += UINT16_MAX + 1;
528 }
529 Milliseconds latency(now - event->receivedTimeMillis);
530
531 if (latency >= kLatencyThreshold) {
532 CHRE_THROTTLE(LOGW("Delayed event 0x%" PRIx16 " from instanceId %" PRIu16
533 "->%" PRIu16 " took %" PRIu64 "ms to deliver",
534 event->eventType, event->senderInstanceId,
535 event->targetInstanceId, latency.getMilliseconds()),
536 kThrottleInterval, kThrottleCount,
537 SystemTime::getMonotonicTime());
538 }
539
540 // TODO: cleaner way to set/clear this? RAII-style?
541 mCurrentApp = app.get();
542 app->processEvent(event);
543 mCurrentApp = nullptr;
544 }
545
distributeEvent(Event * event)546 void EventLoop::distributeEvent(Event *event) {
547 distributeEventCommon(event);
548 CHRE_ASSERT(event->isUnreferenced());
549 freeEvent(event);
550 }
551
distributeEventCommon(Event * event)552 bool EventLoop::distributeEventCommon(Event *event) {
553 bool eventDelivered = false;
554 if (event->targetInstanceId == kBroadcastInstanceId) {
555 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
556 if (app->isRegisteredForBroadcastEvent(event)) {
557 eventDelivered = true;
558 deliverNextEvent(app, event);
559 }
560 }
561 } else {
562 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
563 if (event->targetInstanceId == app->getInstanceId()) {
564 eventDelivered = true;
565 deliverNextEvent(app, event);
566 break;
567 }
568 }
569 }
570 // Log if an event unicast to a nanoapp isn't delivered, as this is could be
571 // a bug (e.g. something isn't properly keeping track of when nanoapps are
572 // unloaded), though it could just be a harmless transient issue (e.g. race
573 // condition with nanoapp unload, where we post an event to a nanoapp just
574 // after queues are flushed while it's unloading)
575 if (!eventDelivered && event->targetInstanceId != kBroadcastInstanceId &&
576 event->targetInstanceId != kSystemInstanceId) {
577 LOGW("Dropping event 0x%" PRIx16 " from instanceId %" PRIu16 "->%" PRIu16,
578 event->eventType, event->senderInstanceId, event->targetInstanceId);
579 }
580 return eventDelivered;
581 }
582
flushInboundEventQueue()583 void EventLoop::flushInboundEventQueue() {
584 while (!mEvents.empty()) {
585 distributeEvent(mEvents.pop());
586 }
587 }
588
freeEvent(Event * event)589 void EventLoop::freeEvent(Event *event) {
590 if (event->hasFreeCallback()) {
591 // TODO: find a better way to set the context to the creator of the event
592 mCurrentApp = lookupAppByInstanceId(event->senderInstanceId);
593 event->invokeFreeCallback();
594 mCurrentApp = nullptr;
595 }
596
597 mEventPool.deallocate(event);
598 }
599
lookupAppByAppId(uint64_t appId) const600 Nanoapp *EventLoop::lookupAppByAppId(uint64_t appId) const {
601 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
602 if (app->getAppId() == appId) {
603 return app.get();
604 }
605 }
606
607 return nullptr;
608 }
609
lookupAppByInstanceId(uint16_t instanceId) const610 Nanoapp *EventLoop::lookupAppByInstanceId(uint16_t instanceId) const {
611 // The system instance ID always has nullptr as its Nanoapp pointer, so can
612 // skip iterating through the nanoapp list for that case
613 if (instanceId != kSystemInstanceId) {
614 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
615 if (app->getInstanceId() == instanceId) {
616 return app.get();
617 }
618 }
619 }
620
621 return nullptr;
622 }
623
notifyAppStatusChange(uint16_t eventType,const Nanoapp & nanoapp)624 void EventLoop::notifyAppStatusChange(uint16_t eventType,
625 const Nanoapp &nanoapp) {
626 auto *info = memoryAlloc<chreNanoappInfo>();
627 if (info == nullptr) {
628 LOG_OOM();
629 } else {
630 info->appId = nanoapp.getAppId();
631 info->version = nanoapp.getAppVersion();
632 info->instanceId = nanoapp.getInstanceId();
633
634 postEventOrDie(eventType, info, freeEventDataCallback);
635 }
636 }
637
unloadNanoappAtIndex(size_t index,bool nanoappStarted)638 void EventLoop::unloadNanoappAtIndex(size_t index, bool nanoappStarted) {
639 const UniquePtr<Nanoapp> &nanoapp = mNanoapps[index];
640
641 // Lock here to prevent the nanoapp instance from being accessed between the
642 // time it is ended and fully erased
643 LockGuard<Mutex> lock(mNanoappsLock);
644
645 // Let the app know it's going away
646 mCurrentApp = nanoapp.get();
647
648 // nanoappEnd() is not invoked for nanoapps that return false in
649 // nanoappStart(), per CHRE API
650 if (nanoappStarted) {
651 nanoapp->end();
652 }
653
654 // Cleanup resources.
655 #ifdef CHRE_WIFI_SUPPORT_ENABLED
656 const uint32_t numDisabledWifiSubscriptions =
657 EventLoopManagerSingleton::get()
658 ->getWifiRequestManager()
659 .disableAllSubscriptions(nanoapp.get());
660 logDanglingResources("WIFI subscriptions", numDisabledWifiSubscriptions);
661 #endif // CHRE_WIFI_SUPPORT_ENABLED
662
663 #ifdef CHRE_GNSS_SUPPORT_ENABLED
664 const uint32_t numDisabledGnssSubscriptions =
665 EventLoopManagerSingleton::get()
666 ->getGnssManager()
667 .disableAllSubscriptions(nanoapp.get());
668 logDanglingResources("GNSS subscriptions", numDisabledGnssSubscriptions);
669 #endif // CHRE_GNSS_SUPPORT_ENABLED
670
671 #ifdef CHRE_SENSORS_SUPPORT_ENABLED
672 const uint32_t numDisabledSensorSubscriptions =
673 EventLoopManagerSingleton::get()
674 ->getSensorRequestManager()
675 .disableAllSubscriptions(nanoapp.get());
676 logDanglingResources("Sensor subscriptions", numDisabledSensorSubscriptions);
677 #endif // CHRE_SENSORS_SUPPORT_ENABLED
678
679 #ifdef CHRE_AUDIO_SUPPORT_ENABLED
680 const uint32_t numDisabledAudioRequests =
681 EventLoopManagerSingleton::get()
682 ->getAudioRequestManager()
683 .disableAllAudioRequests(nanoapp.get());
684 logDanglingResources("Audio requests", numDisabledAudioRequests);
685 #endif // CHRE_AUDIO_SUPPORT_ENABLED
686
687 #ifdef CHRE_BLE_SUPPORT_ENABLED
688 const uint32_t numDisabledBleScans = EventLoopManagerSingleton::get()
689 ->getBleRequestManager()
690 .disableActiveScan(nanoapp.get());
691 logDanglingResources("BLE scan", numDisabledBleScans);
692 #endif // CHRE_BLE_SUPPORT_ENABLED
693
694 const uint32_t numCancelledTimers =
695 getTimerPool().cancelAllNanoappTimers(nanoapp.get());
696 logDanglingResources("timers", numCancelledTimers);
697
698 const uint32_t numFreedBlocks =
699 EventLoopManagerSingleton::get()->getMemoryManager().nanoappFreeAll(
700 nanoapp.get());
701 logDanglingResources("heap blocks", numFreedBlocks);
702
703 // Destroy the Nanoapp instance
704 mNanoapps.erase(index);
705
706 mCurrentApp = nullptr;
707 }
708
handleNanoappWakeupBuckets()709 void EventLoop::handleNanoappWakeupBuckets() {
710 Nanoseconds now = SystemTime::getMonotonicTime();
711 Nanoseconds duration = now - mTimeLastWakeupBucketCycled;
712 if (duration > kIntervalWakeupBucket) {
713 mTimeLastWakeupBucketCycled = now;
714 for (auto &nanoapp : mNanoapps) {
715 nanoapp->cycleWakeupBuckets(now);
716 }
717 }
718 }
719
logDanglingResources(const char * name,uint32_t count)720 void EventLoop::logDanglingResources(const char *name, uint32_t count) {
721 if (count > 0) {
722 LOGE("App 0x%016" PRIx64 " had %" PRIu32 " remaining %s at unload",
723 mCurrentApp->getAppId(), count, name);
724 }
725 }
726
getEndpointInfoFromNanoappLocked(const Nanoapp & nanoapp)727 EndpointInfo EventLoop::getEndpointInfoFromNanoappLocked(
728 const Nanoapp &nanoapp) {
729 return EndpointInfo(
730 /* id= */ nanoapp.getAppId(),
731 /* name= */ nanoapp.getAppName(),
732 /* version= */ nanoapp.getAppVersion(),
733 /* type= */ EndpointType::NANOAPP,
734 /* requiredPermissions= */ nanoapp.getAppPermissions());
735 }
736
737 } // namespace chre
738