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
20 #include "chre/core/event.h"
21 #include "chre/core/event_loop_manager.h"
22 #include "chre/core/nanoapp.h"
23 #include "chre/platform/assert.h"
24 #include "chre/platform/context.h"
25 #include "chre/platform/fatal_error.h"
26 #include "chre/platform/log.h"
27 #include "chre/platform/system_time.h"
28 #include "chre/util/conditional_lock_guard.h"
29 #include "chre/util/lock_guard.h"
30 #include "chre/util/system/debug_dump.h"
31 #include "chre/util/system/stats_container.h"
32 #include "chre/util/time.h"
33 #include "chre_api/chre/version.h"
34
35 namespace chre {
36
37 // Out of line declaration required for nonintegral static types
38 constexpr Nanoseconds EventLoop::kIntervalWakeupBucket;
39
40 namespace {
41
42 /**
43 * Populates a chreNanoappInfo structure using info from the given Nanoapp
44 * instance.
45 *
46 * @param app A potentially null pointer to the Nanoapp to read from
47 * @param info The structure to populate - should not be null, but this function
48 * will handle that input
49 *
50 * @return true if neither app nor info were null, and info was populated
51 */
populateNanoappInfo(const Nanoapp * app,struct chreNanoappInfo * info)52 bool populateNanoappInfo(const Nanoapp *app, struct chreNanoappInfo *info) {
53 bool success = false;
54
55 if (app != nullptr && info != nullptr) {
56 info->appId = app->getAppId();
57 info->version = app->getAppVersion();
58 info->instanceId = app->getInstanceId();
59 success = true;
60 }
61
62 return success;
63 }
64
65 } // anonymous namespace
66
findNanoappInstanceIdByAppId(uint64_t appId,uint16_t * instanceId) const67 bool EventLoop::findNanoappInstanceIdByAppId(uint64_t appId,
68 uint16_t *instanceId) const {
69 CHRE_ASSERT(instanceId != nullptr);
70 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
71
72 bool found = false;
73 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
74 if (app->getAppId() == appId) {
75 *instanceId = app->getInstanceId();
76 found = true;
77 break;
78 }
79 }
80
81 return found;
82 }
83
forEachNanoapp(NanoappCallbackFunction * callback,void * data)84 void EventLoop::forEachNanoapp(NanoappCallbackFunction *callback, void *data) {
85 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
86
87 for (const UniquePtr<Nanoapp> &nanoapp : mNanoapps) {
88 callback(nanoapp.get(), data);
89 }
90 }
91
invokeMessageFreeFunction(uint64_t appId,chreMessageFreeFunction * freeFunction,void * message,size_t messageSize)92 void EventLoop::invokeMessageFreeFunction(uint64_t appId,
93 chreMessageFreeFunction *freeFunction,
94 void *message, size_t messageSize) {
95 Nanoapp *nanoapp = lookupAppByAppId(appId);
96 if (nanoapp == nullptr) {
97 LOGE("Couldn't find app 0x%016" PRIx64 " for message free callback", appId);
98 } else {
99 auto prevCurrentApp = mCurrentApp;
100 mCurrentApp = nanoapp;
101 freeFunction(message, messageSize);
102 mCurrentApp = prevCurrentApp;
103 }
104 }
105
run()106 void EventLoop::run() {
107 LOGI("EventLoop start");
108
109 while (mRunning) {
110 // Events are delivered in a single stage: they arrive in the inbound event
111 // queue mEvents (potentially posted from another thread), then within
112 // this context these events are distributed to all interested Nanoapps,
113 // with their free callback invoked after distribution.
114 mEventPoolUsage.addValue(static_cast<uint32_t>(mEvents.size()));
115
116 // mEvents.pop() will be a blocking call if mEvents.empty()
117 Event *event = mEvents.pop();
118 // Need size() + 1 since the to-be-processed event has already been removed.
119 mPowerControlManager.preEventLoopProcess(mEvents.size() + 1);
120 distributeEvent(event);
121
122 mPowerControlManager.postEventLoopProcess(mEvents.size());
123 }
124
125 // Purge the main queue of events pending distribution. All nanoapps should be
126 // prevented from sending events or messages at this point via
127 // currentNanoappIsStopping() returning true.
128 while (!mEvents.empty()) {
129 freeEvent(mEvents.pop());
130 }
131
132 // Unload all running nanoapps
133 while (!mNanoapps.empty()) {
134 unloadNanoappAtIndex(mNanoapps.size() - 1);
135 }
136
137 LOGI("Exiting EventLoop");
138 }
139
startNanoapp(UniquePtr<Nanoapp> & nanoapp)140 bool EventLoop::startNanoapp(UniquePtr<Nanoapp> &nanoapp) {
141 CHRE_ASSERT(!nanoapp.isNull());
142 bool success = false;
143 auto *eventLoopManager = EventLoopManagerSingleton::get();
144 EventLoop &eventLoop = eventLoopManager->getEventLoop();
145 uint16_t existingInstanceId;
146
147 if (nanoapp.isNull()) {
148 // no-op, invalid argument
149 } else if (nanoapp->getTargetApiVersion() <
150 CHRE_FIRST_SUPPORTED_API_VERSION) {
151 LOGE("Incompatible nanoapp (target ver 0x%" PRIx32
152 ", first supported ver 0x%" PRIx32 ")",
153 nanoapp->getTargetApiVersion(),
154 static_cast<uint32_t>(CHRE_FIRST_SUPPORTED_API_VERSION));
155 } else if (eventLoop.findNanoappInstanceIdByAppId(nanoapp->getAppId(),
156 &existingInstanceId)) {
157 LOGE("App with ID 0x%016" PRIx64 " already exists as instance ID %" PRIu16,
158 nanoapp->getAppId(), existingInstanceId);
159 } else if (!mNanoapps.prepareForPush()) {
160 LOG_OOM();
161 } else {
162 nanoapp->setInstanceId(eventLoopManager->getNextInstanceId());
163 LOGD("Instance ID %" PRIu16 " assigned to app ID 0x%016" PRIx64,
164 nanoapp->getInstanceId(), nanoapp->getAppId());
165
166 Nanoapp *newNanoapp = nanoapp.get();
167 {
168 LockGuard<Mutex> lock(mNanoappsLock);
169 mNanoapps.push_back(std::move(nanoapp));
170 // After this point, nanoapp is null as we've transferred ownership into
171 // mNanoapps.back() - use newNanoapp to reference it
172 }
173
174 mCurrentApp = newNanoapp;
175 success = newNanoapp->start();
176 mCurrentApp = nullptr;
177 if (!success) {
178 // TODO: to be fully safe, need to purge/flush any events and messages
179 // sent by the nanoapp here (but don't call nanoappEnd). For now, we just
180 // destroy the Nanoapp instance.
181 LOGE("Nanoapp %" PRIu16 " failed to start", newNanoapp->getInstanceId());
182
183 // Note that this lock protects against concurrent read and modification
184 // of mNanoapps, but we are assured that no new nanoapps were added since
185 // we pushed the new nanoapp
186 LockGuard<Mutex> lock(mNanoappsLock);
187 mNanoapps.pop_back();
188 } else {
189 notifyAppStatusChange(CHRE_EVENT_NANOAPP_STARTED, *newNanoapp);
190 }
191 }
192
193 return success;
194 }
195
unloadNanoapp(uint16_t instanceId,bool allowSystemNanoappUnload)196 bool EventLoop::unloadNanoapp(uint16_t instanceId,
197 bool allowSystemNanoappUnload) {
198 bool unloaded = false;
199
200 for (size_t i = 0; i < mNanoapps.size(); i++) {
201 if (instanceId == mNanoapps[i]->getInstanceId()) {
202 if (!allowSystemNanoappUnload && mNanoapps[i]->isSystemNanoapp()) {
203 LOGE("Refusing to unload system nanoapp");
204 } else {
205 // Make sure all messages sent by this nanoapp at least have their
206 // associated free callback processing pending in the event queue (i.e.
207 // there are no messages pending delivery to the host)
208 EventLoopManagerSingleton::get()
209 ->getHostCommsManager()
210 .flushMessagesSentByNanoapp(mNanoapps[i]->getAppId());
211
212 // Mark that this nanoapp is stopping early, so it can't send events or
213 // messages during the nanoapp event queue flush
214 mStoppingNanoapp = mNanoapps[i].get();
215
216 // Distribute all inbound events we have at this time - here we're
217 // interested in handling any message free callbacks generated by
218 // flushInboundEventQueue()
219 flushInboundEventQueue();
220
221 // Post the unload event now (so we can reference the Nanoapp instance
222 // directly), but nanoapps won't get it until after the unload completes
223 notifyAppStatusChange(CHRE_EVENT_NANOAPP_STOPPED, *mStoppingNanoapp);
224
225 // Finally, we are at a point where there should not be any pending
226 // events or messages sent by the app that could potentially reference
227 // the nanoapp's memory, so we are safe to unload it
228 unloadNanoappAtIndex(i);
229 mStoppingNanoapp = nullptr;
230
231 LOGD("Unloaded nanoapp with instanceId %" PRIu16, instanceId);
232 unloaded = true;
233 }
234 break;
235 }
236 }
237
238 return unloaded;
239 }
240
postEventOrDie(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,uint16_t targetInstanceId,uint16_t targetGroupMask)241 void EventLoop::postEventOrDie(uint16_t eventType, void *eventData,
242 chreEventCompleteFunction *freeCallback,
243 uint16_t targetInstanceId,
244 uint16_t targetGroupMask) {
245 if (mRunning) {
246 if (!allocateAndPostEvent(eventType, eventData, freeCallback,
247 kSystemInstanceId, targetInstanceId,
248 targetGroupMask)) {
249 FATAL_ERROR("Failed to post critical system event 0x%" PRIx16, eventType);
250 }
251 } else if (freeCallback != nullptr) {
252 freeCallback(eventType, eventData);
253 }
254 }
255
postSystemEvent(uint16_t eventType,void * eventData,SystemEventCallbackFunction * callback,void * extraData)256 bool EventLoop::postSystemEvent(uint16_t eventType, void *eventData,
257 SystemEventCallbackFunction *callback,
258 void *extraData) {
259 if (mRunning) {
260 Event *event =
261 mEventPool.allocate(eventType, eventData, callback, extraData);
262
263 if (event == nullptr || !mEvents.push(event)) {
264 FATAL_ERROR("Failed to post critical system event 0x%" PRIx16, eventType);
265 }
266 return true;
267 }
268 return false;
269 }
270
postLowPriorityEventOrFree(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,uint16_t senderInstanceId,uint16_t targetInstanceId,uint16_t targetGroupMask)271 bool EventLoop::postLowPriorityEventOrFree(
272 uint16_t eventType, void *eventData,
273 chreEventCompleteFunction *freeCallback, uint16_t senderInstanceId,
274 uint16_t targetInstanceId, uint16_t targetGroupMask) {
275 bool eventPosted = false;
276
277 if (mRunning) {
278 if (mEventPool.getFreeBlockCount() > kMinReservedHighPriorityEventCount) {
279 eventPosted = allocateAndPostEvent(eventType, eventData, freeCallback,
280 senderInstanceId, targetInstanceId,
281 targetGroupMask);
282 if (!eventPosted) {
283 LOGE("Failed to allocate event 0x%" PRIx16 " to instanceId %" PRIu16,
284 eventType, targetInstanceId);
285 ++mNumDroppedLowPriEvents;
286 }
287 }
288 }
289
290 if (!eventPosted && freeCallback != nullptr) {
291 freeCallback(eventType, eventData);
292 }
293
294 return eventPosted;
295 }
296
stop()297 void EventLoop::stop() {
298 auto callback = [](uint16_t /*type*/, void *data, void * /*extraData*/) {
299 auto *obj = static_cast<EventLoop *>(data);
300 obj->onStopComplete();
301 };
302
303 // Stop accepting new events and tell the main loop to finish
304 postSystemEvent(static_cast<uint16_t>(SystemCallbackType::Shutdown),
305 /*eventData=*/this, callback, /*extraData=*/nullptr);
306 }
307
onStopComplete()308 void EventLoop::onStopComplete() {
309 mRunning = false;
310 }
311
findNanoappByInstanceId(uint16_t instanceId) const312 Nanoapp *EventLoop::findNanoappByInstanceId(uint16_t instanceId) const {
313 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
314 return lookupAppByInstanceId(instanceId);
315 }
316
populateNanoappInfoForAppId(uint64_t appId,struct chreNanoappInfo * info) const317 bool EventLoop::populateNanoappInfoForAppId(
318 uint64_t appId, struct chreNanoappInfo *info) const {
319 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
320 Nanoapp *app = lookupAppByAppId(appId);
321 return populateNanoappInfo(app, info);
322 }
323
populateNanoappInfoForInstanceId(uint16_t instanceId,struct chreNanoappInfo * info) const324 bool EventLoop::populateNanoappInfoForInstanceId(
325 uint16_t instanceId, struct chreNanoappInfo *info) const {
326 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
327 Nanoapp *app = lookupAppByInstanceId(instanceId);
328 return populateNanoappInfo(app, info);
329 }
330
currentNanoappIsStopping() const331 bool EventLoop::currentNanoappIsStopping() const {
332 return (mCurrentApp == mStoppingNanoapp || !mRunning);
333 }
334
logStateToBuffer(DebugDumpWrapper & debugDump) const335 void EventLoop::logStateToBuffer(DebugDumpWrapper &debugDump) const {
336 debugDump.print("\nEvent Loop:\n");
337 debugDump.print(" Max event pool usage: %" PRIu32 "/%zu\n",
338 mEventPoolUsage.getMax(), kMaxEventCount);
339 debugDump.print(" Number of low priority events dropped: %" PRIu32 "\n",
340 mNumDroppedLowPriEvents);
341 debugDump.print(" Mean event pool usage: %" PRIu32 "/%zu\n",
342 mEventPoolUsage.getMean(), kMaxEventCount);
343
344 Nanoseconds timeSince =
345 SystemTime::getMonotonicTime() - mTimeLastWakeupBucketCycled;
346 uint64_t timeSinceMins =
347 timeSince.toRawNanoseconds() / kOneMinuteInNanoseconds;
348 uint64_t durationMins =
349 kIntervalWakeupBucket.toRawNanoseconds() / kOneMinuteInNanoseconds;
350 debugDump.print(" Nanoapp host wakeup tracking: cycled %" PRIu64
351 "mins ago, bucketDuration=%" PRIu64 "mins\n",
352 timeSinceMins, durationMins);
353
354 debugDump.print("\nNanoapps:\n");
355 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
356 app->logStateToBuffer(debugDump);
357 }
358 }
359
allocateAndPostEvent(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,uint16_t senderInstanceId,uint16_t targetInstanceId,uint16_t targetGroupMask)360 bool EventLoop::allocateAndPostEvent(uint16_t eventType, void *eventData,
361 chreEventCompleteFunction *freeCallback,
362 uint16_t senderInstanceId,
363 uint16_t targetInstanceId,
364 uint16_t targetGroupMask) {
365 bool success = false;
366
367 Event *event =
368 mEventPool.allocate(eventType, eventData, freeCallback, senderInstanceId,
369 targetInstanceId, targetGroupMask);
370 if (event != nullptr) {
371 success = mEvents.push(event);
372 }
373
374 return success;
375 }
376
deliverNextEvent(const UniquePtr<Nanoapp> & app,Event * event)377 void EventLoop::deliverNextEvent(const UniquePtr<Nanoapp> &app, Event *event) {
378 // TODO: cleaner way to set/clear this? RAII-style?
379 mCurrentApp = app.get();
380 app->processEvent(event);
381 mCurrentApp = nullptr;
382 }
383
distributeEvent(Event * event)384 void EventLoop::distributeEvent(Event *event) {
385 bool eventDelivered = false;
386 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
387 if ((event->targetInstanceId == chre::kBroadcastInstanceId &&
388 app->isRegisteredForBroadcastEvent(event)) ||
389 event->targetInstanceId == app->getInstanceId()) {
390 eventDelivered = true;
391 deliverNextEvent(app, event);
392 }
393 }
394 // Log if an event unicast to a nanoapp isn't delivered, as this is could be
395 // a bug (e.g. something isn't properly keeping track of when nanoapps are
396 // unloaded), though it could just be a harmless transient issue (e.g. race
397 // condition with nanoapp unload, where we post an event to a nanoapp just
398 // after queues are flushed while it's unloading)
399 if (!eventDelivered && event->targetInstanceId != kBroadcastInstanceId &&
400 event->targetInstanceId != kSystemInstanceId) {
401 LOGW("Dropping event 0x%" PRIx16 " from instanceId %" PRIu16 "->%" PRIu16,
402 event->eventType, event->senderInstanceId, event->targetInstanceId);
403 }
404 CHRE_ASSERT(event->isUnreferenced());
405 freeEvent(event);
406 }
407
flushInboundEventQueue()408 void EventLoop::flushInboundEventQueue() {
409 while (!mEvents.empty()) {
410 distributeEvent(mEvents.pop());
411 }
412 }
413
freeEvent(Event * event)414 void EventLoop::freeEvent(Event *event) {
415 if (event->hasFreeCallback()) {
416 // TODO: find a better way to set the context to the creator of the event
417 mCurrentApp = lookupAppByInstanceId(event->senderInstanceId);
418 event->invokeFreeCallback();
419 mCurrentApp = nullptr;
420 }
421
422 mEventPool.deallocate(event);
423 }
424
lookupAppByAppId(uint64_t appId) const425 Nanoapp *EventLoop::lookupAppByAppId(uint64_t appId) const {
426 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
427 if (app->getAppId() == appId) {
428 return app.get();
429 }
430 }
431
432 return nullptr;
433 }
434
lookupAppByInstanceId(uint16_t instanceId) const435 Nanoapp *EventLoop::lookupAppByInstanceId(uint16_t instanceId) const {
436 // The system instance ID always has nullptr as its Nanoapp pointer, so can
437 // skip iterating through the nanoapp list for that case
438 if (instanceId != kSystemInstanceId) {
439 for (const UniquePtr<Nanoapp> &app : mNanoapps) {
440 if (app->getInstanceId() == instanceId) {
441 return app.get();
442 }
443 }
444 }
445
446 return nullptr;
447 }
448
notifyAppStatusChange(uint16_t eventType,const Nanoapp & nanoapp)449 void EventLoop::notifyAppStatusChange(uint16_t eventType,
450 const Nanoapp &nanoapp) {
451 auto *info = memoryAlloc<chreNanoappInfo>();
452 if (info == nullptr) {
453 LOG_OOM();
454 } else {
455 info->appId = nanoapp.getAppId();
456 info->version = nanoapp.getAppVersion();
457 info->instanceId = nanoapp.getInstanceId();
458
459 postEventOrDie(eventType, info, freeEventDataCallback);
460 }
461 }
462
unloadNanoappAtIndex(size_t index)463 void EventLoop::unloadNanoappAtIndex(size_t index) {
464 const UniquePtr<Nanoapp> &nanoapp = mNanoapps[index];
465
466 // Lock here to prevent the nanoapp instance from being accessed between the
467 // time it is ended and fully erased
468 LockGuard<Mutex> lock(mNanoappsLock);
469
470 // Let the app know it's going away
471 mCurrentApp = nanoapp.get();
472 nanoapp->end();
473
474 // Cleanup resources.
475 #ifdef CHRE_WIFI_SUPPORT_ENABLED
476 const uint32_t numDisabledWifiSubscriptions =
477 EventLoopManagerSingleton::get()
478 ->getWifiRequestManager()
479 .disableAllSubscriptions(nanoapp.get());
480 logDanglingResources("WIFI subscriptions", numDisabledWifiSubscriptions);
481 #endif // CHRE_WIFI_SUPPORT_ENABLED
482
483 #ifdef CHRE_GNSS_SUPPORT_ENABLED
484 const uint32_t numDisabledGnssSubscriptions =
485 EventLoopManagerSingleton::get()
486 ->getGnssManager()
487 .disableAllSubscriptions(nanoapp.get());
488 logDanglingResources("GNSS subscriptions", numDisabledGnssSubscriptions);
489 #endif // CHRE_GNSS_SUPPORT_ENABLED
490
491 #ifdef CHRE_SENSORS_SUPPORT_ENABLED
492 const uint32_t numDisabledSensorSubscriptions =
493 EventLoopManagerSingleton::get()
494 ->getSensorRequestManager()
495 .disableAllSubscriptions(nanoapp.get());
496 logDanglingResources("Sensor subscriptions", numDisabledSensorSubscriptions);
497 #endif // CHRE_SENSORS_SUPPORT_ENABLED
498
499 #ifdef CHRE_AUDIO_SUPPORT_ENABLED
500 const uint32_t numDisabledAudioRequests =
501 EventLoopManagerSingleton::get()
502 ->getAudioRequestManager()
503 .disableAllAudioRequests(nanoapp.get());
504 logDanglingResources("Audio requests", numDisabledAudioRequests);
505 #endif // CHRE_AUDIO_SUPPORT_ENABLED
506
507 #ifdef CHRE_BLE_SUPPORT_ENABLED
508 const uint32_t numDisabledBleScans = EventLoopManagerSingleton::get()
509 ->getBleRequestManager()
510 .disableActiveScan(nanoapp.get());
511 logDanglingResources("BLE scan", numDisabledBleScans);
512 #endif // CHRE_BLE_SUPPORT_ENABLED
513
514 const uint32_t numCancelledTimers =
515 getTimerPool().cancelAllNanoappTimers(nanoapp.get());
516 logDanglingResources("timers", numCancelledTimers);
517
518 const uint32_t numFreedBlocks =
519 EventLoopManagerSingleton::get()->getMemoryManager().nanoappFreeAll(
520 nanoapp.get());
521 logDanglingResources("heap blocks", numFreedBlocks);
522
523 mCurrentApp = nullptr;
524
525 // Destroy the Nanoapp instance
526 mNanoapps.erase(index);
527 }
528
handleNanoappWakeupBuckets()529 void EventLoop::handleNanoappWakeupBuckets() {
530 Nanoseconds now = SystemTime::getMonotonicTime();
531 Nanoseconds duration = now - mTimeLastWakeupBucketCycled;
532 if (duration > kIntervalWakeupBucket) {
533 size_t numBuckets = static_cast<size_t>(
534 duration.toRawNanoseconds() / kIntervalWakeupBucket.toRawNanoseconds());
535 mTimeLastWakeupBucketCycled = now;
536 for (auto &nanoapp : mNanoapps) {
537 nanoapp->cycleWakeupBuckets(numBuckets);
538 }
539 }
540 }
541
logDanglingResources(const char * name,uint32_t count)542 void EventLoop::logDanglingResources(const char *name, uint32_t count) {
543 if (count > 0) {
544 LOGE("App 0x%016" PRIx64 " had %" PRIu32 " remaining %s at unload",
545 mCurrentApp->getAppId(), count, name);
546 }
547 }
548
549 } // namespace chre
550