• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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