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