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