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
19 #include "chre/core/event.h"
20 #include "chre/core/event_loop_manager.h"
21 #include "chre/core/nanoapp.h"
22 #include "chre/platform/context.h"
23 #include "chre/platform/fatal_error.h"
24 #include "chre/platform/log.h"
25 #include "chre/util/conditional_lock_guard.h"
26 #include "chre/util/lock_guard.h"
27 #include "chre/util/system/debug_dump.h"
28 #include "chre_api/chre/version.h"
29
30 namespace chre {
31
32 namespace {
33
34 /**
35 * Populates a chreNanoappInfo structure using info from the given Nanoapp
36 * instance.
37 *
38 * @param app A potentially null pointer to the Nanoapp to read from
39 * @param info The structure to populate - should not be null, but this function
40 * will handle that input
41 *
42 * @return true if neither app nor info were null, and info was populated
43 */
populateNanoappInfo(const Nanoapp * app,struct chreNanoappInfo * info)44 bool populateNanoappInfo(const Nanoapp *app, struct chreNanoappInfo *info) {
45 bool success = false;
46
47 if (app != nullptr && info != nullptr) {
48 info->appId = app->getAppId();
49 info->version = app->getAppVersion();
50 info->instanceId = app->getInstanceId();
51 success = true;
52 }
53
54 return success;
55 }
56
57 } // anonymous namespace
58
findNanoappInstanceIdByAppId(uint64_t appId,uint32_t * instanceId) const59 bool EventLoop::findNanoappInstanceIdByAppId(uint64_t appId,
60 uint32_t *instanceId) const {
61 CHRE_ASSERT(instanceId != nullptr);
62 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
63
64 bool found = false;
65 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
66 if (app->getAppId() == appId) {
67 *instanceId = app->getInstanceId();
68 found = true;
69 break;
70 }
71 }
72
73 return found;
74 }
75
forEachNanoapp(NanoappCallbackFunction * callback,void * data)76 void EventLoop::forEachNanoapp(NanoappCallbackFunction *callback, void *data) {
77 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
78
79 for (const UniquePtr<Nanoapp>& nanoapp : mNanoapps) {
80 callback(nanoapp.get(), data);
81 }
82 }
83
invokeMessageFreeFunction(uint64_t appId,chreMessageFreeFunction * freeFunction,void * message,size_t messageSize)84 void EventLoop::invokeMessageFreeFunction(
85 uint64_t appId, chreMessageFreeFunction *freeFunction, void *message,
86 size_t messageSize) {
87 Nanoapp *nanoapp = lookupAppByAppId(appId);
88 if (nanoapp == nullptr) {
89 LOGE("Couldn't find app 0x%016" PRIx64 " for message free callback", appId);
90 } else {
91 auto prevCurrentApp = mCurrentApp;
92 mCurrentApp = nanoapp;
93 freeFunction(message, messageSize);
94 mCurrentApp = prevCurrentApp;
95 }
96 }
97
run()98 void EventLoop::run() {
99 LOGI("EventLoop start");
100
101 bool havePendingEvents = false;
102 while (mRunning) {
103 // Events are delivered in two stages: first they arrive in the inbound
104 // event queue mEvents (potentially posted from another thread), then within
105 // this context these events are distributed to smaller event queues
106 // associated with each Nanoapp that should receive the event. Once the
107 // event is delivered to all interested Nanoapps, its free callback is
108 // invoked.
109 if (!havePendingEvents || !mEvents.empty()) {
110 if (mEvents.size() > mMaxEventPoolUsage) {
111 mMaxEventPoolUsage = mEvents.size();
112 }
113
114 // mEvents.pop() will be a blocking call if mEvents.empty()
115 distributeEvent(mEvents.pop());
116 }
117
118 havePendingEvents = deliverEvents();
119
120 mPowerControlManager.postEventLoopProcess(mEvents.size());
121 }
122
123 // Deliver any events sitting in Nanoapps' own queues (we could drop them to
124 // exit faster, but this is less code and should complete quickly under normal
125 // conditions), then purge the main queue of events pending distribution. All
126 // nanoapps should be prevented from sending events or messages at this point
127 // via currentNanoappIsStopping() returning true.
128 flushNanoappEventQueues();
129 while (!mEvents.empty()) {
130 freeEvent(mEvents.pop());
131 }
132
133 // Unload all running nanoapps
134 while (!mNanoapps.empty()) {
135 unloadNanoappAtIndex(mNanoapps.size() - 1);
136 }
137
138 LOGI("Exiting EventLoop");
139 }
140
startNanoapp(UniquePtr<Nanoapp> & nanoapp)141 bool EventLoop::startNanoapp(UniquePtr<Nanoapp>& nanoapp) {
142 CHRE_ASSERT(!nanoapp.isNull());
143 bool success = false;
144 auto *eventLoopManager = EventLoopManagerSingleton::get();
145 EventLoop& eventLoop = eventLoopManager->getEventLoop();
146 uint32_t existingInstanceId;
147
148 if (nanoapp.isNull()) {
149 // no-op, invalid argument
150 } else if (eventLoop.findNanoappInstanceIdByAppId(nanoapp->getAppId(),
151 &existingInstanceId)) {
152 LOGE("App with ID 0x%016" PRIx64 " already exists as instance ID 0x%"
153 PRIx32, nanoapp->getAppId(), existingInstanceId);
154 } else if (!mNanoapps.prepareForPush()) {
155 LOG_OOM();
156 } else {
157 nanoapp->setInstanceId(eventLoopManager->getNextInstanceId());
158 LOGD("Instance ID %" PRIu32 " assigned to app ID 0x%016" PRIx64,
159 nanoapp->getInstanceId(), nanoapp->getAppId());
160
161 Nanoapp *newNanoapp = nanoapp.get();
162 {
163 LockGuard<Mutex> lock(mNanoappsLock);
164 mNanoapps.push_back(std::move(nanoapp));
165 // After this point, nanoapp is null as we've transferred ownership into
166 // mNanoapps.back() - use newNanoapp to reference it
167 }
168
169 mCurrentApp = newNanoapp;
170 success = newNanoapp->start();
171 mCurrentApp = nullptr;
172 if (!success) {
173 // TODO: to be fully safe, need to purge/flush any events and messages
174 // sent by the nanoapp here (but don't call nanoappEnd). For now, we just
175 // destroy the Nanoapp instance.
176 LOGE("Nanoapp %" PRIu32 " failed to start", newNanoapp->getInstanceId());
177
178 // Note that this lock protects against concurrent read and modification
179 // of mNanoapps, but we are assured that no new nanoapps were added since
180 // we pushed the new nanoapp
181 LockGuard<Mutex> lock(mNanoappsLock);
182 mNanoapps.pop_back();
183 } else {
184 notifyAppStatusChange(CHRE_EVENT_NANOAPP_STARTED, *newNanoapp);
185 }
186 }
187
188 return success;
189 }
190
unloadNanoapp(uint32_t instanceId,bool allowSystemNanoappUnload)191 bool EventLoop::unloadNanoapp(uint32_t instanceId,
192 bool allowSystemNanoappUnload) {
193 bool unloaded = false;
194
195 for (size_t i = 0; i < mNanoapps.size(); i++) {
196 if (instanceId == mNanoapps[i]->getInstanceId()) {
197 if (!allowSystemNanoappUnload && mNanoapps[i]->isSystemNanoapp()) {
198 LOGE("Refusing to unload system nanoapp");
199 } else {
200 // Make sure all messages sent by this nanoapp at least have their
201 // associated free callback processing pending in the event queue (i.e.
202 // there are no messages pending delivery to the host)
203 EventLoopManagerSingleton::get()->getHostCommsManager()
204 .flushMessagesSentByNanoapp(mNanoapps[i]->getAppId());
205
206 // Distribute all inbound events we have at this time - here we're
207 // interested in handling any message free callbacks generated by
208 // flushMessagesSentByNanoapp()
209 flushInboundEventQueue();
210
211 // Mark that this nanoapp is stopping early, so it can't send events or
212 // messages during the nanoapp event queue flush
213 mStoppingNanoapp = mNanoapps[i].get();
214
215 // Process any pending events, with the intent of ensuring that we free
216 // all events generated by this nanoapp
217 flushNanoappEventQueues();
218
219 // Post the unload event now (so we can reference the Nanoapp instance
220 // directly), but nanoapps won't get it until after the unload completes
221 notifyAppStatusChange(CHRE_EVENT_NANOAPP_STOPPED, *mStoppingNanoapp);
222
223 // Finally, we are at a point where there should not be any pending
224 // events or messages sent by the app that could potentially reference
225 // the nanoapp's memory, so we are safe to unload it
226 unloadNanoappAtIndex(i);
227 mStoppingNanoapp = nullptr;
228
229 // TODO: right now we assume that the nanoapp will clean up all of its
230 // resource allocations in its nanoappEnd callback (memory, sensor
231 // subscriptions, etc.), otherwise we're leaking resources. We should
232 // perform resource cleanup automatically here to avoid these types of
233 // potential leaks.
234
235 LOGD("Unloaded nanoapp with instanceId %" PRIu32, instanceId);
236 unloaded = true;
237 }
238 break;
239 }
240 }
241
242 return unloaded;
243 }
244
postEvent(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,uint32_t senderInstanceId,uint32_t targetInstanceId)245 bool EventLoop::postEvent(uint16_t eventType, void *eventData,
246 chreEventCompleteFunction *freeCallback, uint32_t senderInstanceId,
247 uint32_t targetInstanceId) {
248 bool success = false;
249
250 if (mRunning && (senderInstanceId == kSystemInstanceId ||
251 mEventPool.getFreeBlockCount() > kMinReservedSystemEventCount)) {
252 success = allocateAndPostEvent(eventType, eventData, freeCallback,
253 senderInstanceId,targetInstanceId);
254 if (!success) {
255 // This can only happen if the event is a system event type. This
256 // postEvent method will fail if a non-system event is posted when the
257 // memory pool is close to full.
258 FATAL_ERROR("Failed to allocate system event type %" PRIu16, eventType);
259 }
260 }
261
262 return success;
263 }
264
postEventOrFree(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,uint32_t senderInstanceId,uint32_t targetInstanceId)265 bool EventLoop::postEventOrFree(uint16_t eventType, void *eventData,
266 chreEventCompleteFunction *freeCallback, uint32_t senderInstanceId,
267 uint32_t targetInstanceId) {
268 bool success = false;
269
270 if (mRunning) {
271 success = allocateAndPostEvent(eventType, eventData, freeCallback,
272 senderInstanceId,targetInstanceId);
273 if (!success) {
274 freeCallback(eventType, eventData);
275 LOGE("Failed to allocate event 0x%" PRIx16 " to instanceId %" PRIu32,
276 eventType, targetInstanceId);
277 }
278 }
279
280 return success;
281 }
282
stop()283 void EventLoop::stop() {
284 auto callback = [](uint16_t /* type */, void * /* data */) {
285 EventLoopManagerSingleton::get()->getEventLoop().onStopComplete();
286 };
287
288 // Stop accepting new events and tell the main loop to finish.
289 postEvent(0, nullptr, callback, kSystemInstanceId, kSystemInstanceId);
290 }
291
onStopComplete()292 void EventLoop::onStopComplete() {
293 mRunning = false;
294 }
295
findNanoappByInstanceId(uint32_t instanceId) const296 Nanoapp *EventLoop::findNanoappByInstanceId(uint32_t instanceId) const {
297 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
298 return lookupAppByInstanceId(instanceId);
299 }
300
populateNanoappInfoForAppId(uint64_t appId,struct chreNanoappInfo * info) const301 bool EventLoop::populateNanoappInfoForAppId(
302 uint64_t appId, struct chreNanoappInfo *info) const {
303 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
304 Nanoapp *app = lookupAppByAppId(appId);
305 return populateNanoappInfo(app, info);
306 }
307
populateNanoappInfoForInstanceId(uint32_t instanceId,struct chreNanoappInfo * info) const308 bool EventLoop::populateNanoappInfoForInstanceId(
309 uint32_t instanceId, struct chreNanoappInfo *info) const {
310 ConditionalLockGuard<Mutex> lock(mNanoappsLock, !inEventLoopThread());
311 Nanoapp *app = lookupAppByInstanceId(instanceId);
312 return populateNanoappInfo(app, info);
313 }
314
currentNanoappIsStopping() const315 bool EventLoop::currentNanoappIsStopping() const {
316 return (mCurrentApp == mStoppingNanoapp || !mRunning);
317 }
318
logStateToBuffer(char * buffer,size_t * bufferPos,size_t bufferSize) const319 void EventLoop::logStateToBuffer(char *buffer, size_t *bufferPos,
320 size_t bufferSize) const {
321 debugDumpPrint(buffer, bufferPos, bufferSize, "\nNanoapps:\n");
322 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
323 app->logStateToBuffer(buffer, bufferPos, bufferSize);
324 }
325
326 debugDumpPrint(buffer, bufferPos, bufferSize, "\nEvent Loop:\n");
327 debugDumpPrint(buffer, bufferPos, bufferSize,
328 " Max event pool usage: %zu/%zu\n",
329 mMaxEventPoolUsage, kMaxEventCount);
330 }
331
allocateAndPostEvent(uint16_t eventType,void * eventData,chreEventCompleteFunction * freeCallback,uint32_t senderInstanceId,uint32_t targetInstanceId)332 bool EventLoop::allocateAndPostEvent(uint16_t eventType, void *eventData,
333 chreEventCompleteFunction *freeCallback, uint32_t senderInstanceId,
334 uint32_t targetInstanceId) {
335 bool success = false;
336
337 Event *event = mEventPool.allocate(eventType, eventData, freeCallback,
338 senderInstanceId, targetInstanceId);
339 if (event != nullptr) {
340 success = mEvents.push(event);
341 }
342 return success;
343 }
344
deliverEvents()345 bool EventLoop::deliverEvents() {
346 bool havePendingEvents = false;
347
348 // Do one loop of round-robin. We might want to have some kind of priority or
349 // time sharing in the future, but this should be good enough for now.
350 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
351 if (app->hasPendingEvent()) {
352 havePendingEvents |= deliverNextEvent(app);
353 }
354 }
355
356 return havePendingEvents;
357 }
358
deliverNextEvent(const UniquePtr<Nanoapp> & app)359 bool EventLoop::deliverNextEvent(const UniquePtr<Nanoapp>& app) {
360 // TODO: cleaner way to set/clear this? RAII-style?
361 mCurrentApp = app.get();
362 Event *event = app->processNextEvent();
363 mCurrentApp = nullptr;
364
365 if (event->isUnreferenced()) {
366 freeEvent(event);
367 }
368
369 return app->hasPendingEvent();
370 }
371
distributeEvent(Event * event)372 void EventLoop::distributeEvent(Event *event) {
373 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
374 if ((event->targetInstanceId == chre::kBroadcastInstanceId
375 && app->isRegisteredForBroadcastEvent(event->eventType))
376 || event->targetInstanceId == app->getInstanceId()) {
377 app->postEvent(event);
378 }
379 }
380
381 if (event->isUnreferenced()) {
382 // Events sent to the system instance ID are processed via the free callback
383 // and are not expected to be delivered to any nanoapp, so no need to log a
384 // warning in that case
385 if (event->senderInstanceId != kSystemInstanceId) {
386 LOGW("Dropping event 0x%" PRIx16, event->eventType);
387 }
388 freeEvent(event);
389 }
390 }
391
flushInboundEventQueue()392 void EventLoop::flushInboundEventQueue() {
393 while (!mEvents.empty()) {
394 distributeEvent(mEvents.pop());
395 }
396 }
397
flushNanoappEventQueues()398 void EventLoop::flushNanoappEventQueues() {
399 while (deliverEvents());
400 }
401
freeEvent(Event * event)402 void EventLoop::freeEvent(Event *event) {
403 if (event->freeCallback != nullptr) {
404 // TODO: find a better way to set the context to the creator of the event
405 mCurrentApp = lookupAppByInstanceId(event->senderInstanceId);
406 event->freeCallback(event->eventType, event->eventData);
407 mCurrentApp = nullptr;
408 }
409
410 mEventPool.deallocate(event);
411 }
412
lookupAppByAppId(uint64_t appId) const413 Nanoapp *EventLoop::lookupAppByAppId(uint64_t appId) const {
414 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
415 if (app->getAppId() == appId) {
416 return app.get();
417 }
418 }
419
420 return nullptr;
421 }
422
lookupAppByInstanceId(uint32_t instanceId) const423 Nanoapp *EventLoop::lookupAppByInstanceId(uint32_t instanceId) const {
424 // The system instance ID always has nullptr as its Nanoapp pointer, so can
425 // skip iterating through the nanoapp list for that case
426 if (instanceId != kSystemInstanceId) {
427 for (const UniquePtr<Nanoapp>& app : mNanoapps) {
428 if (app->getInstanceId() == instanceId) {
429 return app.get();
430 }
431 }
432 }
433
434 return nullptr;
435 }
436
notifyAppStatusChange(uint16_t eventType,const Nanoapp & nanoapp)437 void EventLoop::notifyAppStatusChange(uint16_t eventType,
438 const Nanoapp& nanoapp) {
439 auto *info = memoryAlloc<chreNanoappInfo>();
440 if (info == nullptr) {
441 LOG_OOM();
442 } else {
443 info->appId = nanoapp.getAppId();
444 info->version = nanoapp.getAppVersion();
445 info->instanceId = nanoapp.getInstanceId();
446
447 postEvent(eventType, info, freeEventDataCallback);
448 }
449 }
450
unloadNanoappAtIndex(size_t index)451 void EventLoop::unloadNanoappAtIndex(size_t index) {
452 const UniquePtr<Nanoapp>& nanoapp = mNanoapps[index];
453
454 // Lock here to prevent the nanoapp instance from being accessed between the
455 // time it is ended and fully erased
456 LockGuard<Mutex> lock(mNanoappsLock);
457
458 // Let the app know it's going away
459 mCurrentApp = nanoapp.get();
460 nanoapp->end();
461 mCurrentApp = nullptr;
462
463 // Destroy the Nanoapp instance
464 mNanoapps.erase(index);
465 }
466
467 } // namespace chre
468