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