• 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 
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