Home | History | Annotate | Download | only in libstagefright
      1 /*
      2  * Copyright (C) 2009 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 #undef __STRICT_ANSI__
     18 #define __STDINT_LIMITS
     19 #define __STDC_LIMIT_MACROS
     20 #include <stdint.h>
     21 
     22 //#define LOG_NDEBUG 0
     23 #define LOG_TAG "TimedEventQueue"
     24 #include <utils/Log.h>
     25 #include <utils/threads.h>
     26 
     27 #include "include/TimedEventQueue.h"
     28 
     29 #include <sys/prctl.h>
     30 #include <sys/time.h>
     31 
     32 #include <media/stagefright/foundation/ADebug.h>
     33 #include <media/stagefright/foundation/ALooper.h>
     34 #include <binder/IServiceManager.h>
     35 #include <powermanager/PowerManager.h>
     36 #include <binder/IPCThreadState.h>
     37 #include <utils/CallStack.h>
     38 
     39 namespace android {
     40 
     41 static int64_t kWakelockMinDelay = 100000ll;  // 100ms
     42 
     43 TimedEventQueue::TimedEventQueue()
     44     : mNextEventID(1),
     45       mRunning(false),
     46       mStopped(false),
     47       mDeathRecipient(new PMDeathRecipient(this)),
     48       mWakeLockCount(0) {
     49 }
     50 
     51 TimedEventQueue::~TimedEventQueue() {
     52     stop();
     53     if (mPowerManager != 0) {
     54         sp<IBinder> binder = mPowerManager->asBinder();
     55         binder->unlinkToDeath(mDeathRecipient);
     56     }
     57 }
     58 
     59 void TimedEventQueue::start() {
     60     if (mRunning) {
     61         return;
     62     }
     63 
     64     mStopped = false;
     65 
     66     pthread_attr_t attr;
     67     pthread_attr_init(&attr);
     68     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
     69 
     70     pthread_create(&mThread, &attr, ThreadWrapper, this);
     71 
     72     pthread_attr_destroy(&attr);
     73 
     74     mRunning = true;
     75 }
     76 
     77 void TimedEventQueue::stop(bool flush) {
     78     if (!mRunning) {
     79         return;
     80     }
     81 
     82     if (flush) {
     83         postEventToBack(new StopEvent);
     84     } else {
     85         postTimedEvent(new StopEvent, INT64_MIN);
     86     }
     87 
     88     void *dummy;
     89     pthread_join(mThread, &dummy);
     90 
     91     // some events may be left in the queue if we did not flush and the wake lock
     92     // must be released.
     93     releaseWakeLock_l(true /*force*/);
     94     mQueue.clear();
     95 
     96     mRunning = false;
     97 }
     98 
     99 TimedEventQueue::event_id TimedEventQueue::postEvent(const sp<Event> &event) {
    100     // Reserve an earlier timeslot an INT64_MIN to be able to post
    101     // the StopEvent to the absolute head of the queue.
    102     return postTimedEvent(event, INT64_MIN + 1);
    103 }
    104 
    105 TimedEventQueue::event_id TimedEventQueue::postEventToBack(
    106         const sp<Event> &event) {
    107     return postTimedEvent(event, INT64_MAX);
    108 }
    109 
    110 TimedEventQueue::event_id TimedEventQueue::postEventWithDelay(
    111         const sp<Event> &event, int64_t delay_us) {
    112     CHECK(delay_us >= 0);
    113     return postTimedEvent(event, ALooper::GetNowUs() + delay_us);
    114 }
    115 
    116 TimedEventQueue::event_id TimedEventQueue::postTimedEvent(
    117         const sp<Event> &event, int64_t realtime_us) {
    118     Mutex::Autolock autoLock(mLock);
    119 
    120     event->setEventID(mNextEventID++);
    121 
    122     List<QueueItem>::iterator it = mQueue.begin();
    123     while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
    124         ++it;
    125     }
    126 
    127     QueueItem item;
    128     item.event = event;
    129     item.realtime_us = realtime_us;
    130     item.has_wakelock = false;
    131 
    132     if (it == mQueue.begin()) {
    133         mQueueHeadChangedCondition.signal();
    134     }
    135 
    136     if (realtime_us > ALooper::GetNowUs() + kWakelockMinDelay) {
    137         acquireWakeLock_l();
    138         item.has_wakelock = true;
    139     }
    140     mQueue.insert(it, item);
    141 
    142     mQueueNotEmptyCondition.signal();
    143 
    144     return event->eventID();
    145 }
    146 
    147 static bool MatchesEventID(
    148         void *cookie, const sp<TimedEventQueue::Event> &event) {
    149     TimedEventQueue::event_id *id =
    150         static_cast<TimedEventQueue::event_id *>(cookie);
    151 
    152     if (event->eventID() != *id) {
    153         return false;
    154     }
    155 
    156     *id = 0;
    157 
    158     return true;
    159 }
    160 
    161 bool TimedEventQueue::cancelEvent(event_id id) {
    162     if (id == 0) {
    163         return false;
    164     }
    165 
    166     cancelEvents(&MatchesEventID, &id, true /* stopAfterFirstMatch */);
    167 
    168     // if MatchesEventID found a match, it will have set id to 0
    169     // (which is not a valid event_id).
    170 
    171     return id == 0;
    172 }
    173 
    174 void TimedEventQueue::cancelEvents(
    175         bool (*predicate)(void *cookie, const sp<Event> &event),
    176         void *cookie,
    177         bool stopAfterFirstMatch) {
    178     Mutex::Autolock autoLock(mLock);
    179 
    180     List<QueueItem>::iterator it = mQueue.begin();
    181     while (it != mQueue.end()) {
    182         if (!(*predicate)(cookie, (*it).event)) {
    183             ++it;
    184             continue;
    185         }
    186 
    187         if (it == mQueue.begin()) {
    188             mQueueHeadChangedCondition.signal();
    189         }
    190 
    191         ALOGV("cancelling event %d", (*it).event->eventID());
    192 
    193         (*it).event->setEventID(0);
    194         if ((*it).has_wakelock) {
    195             releaseWakeLock_l();
    196         }
    197         it = mQueue.erase(it);
    198         if (stopAfterFirstMatch) {
    199             return;
    200         }
    201     }
    202 }
    203 
    204 // static
    205 void *TimedEventQueue::ThreadWrapper(void *me) {
    206 
    207     androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND);
    208 
    209     static_cast<TimedEventQueue *>(me)->threadEntry();
    210 
    211     return NULL;
    212 }
    213 
    214 void TimedEventQueue::threadEntry() {
    215     prctl(PR_SET_NAME, (unsigned long)"TimedEventQueue", 0, 0, 0);
    216 
    217     for (;;) {
    218         int64_t now_us = 0;
    219         sp<Event> event;
    220 
    221         {
    222             Mutex::Autolock autoLock(mLock);
    223 
    224             if (mStopped) {
    225                 break;
    226             }
    227 
    228             while (mQueue.empty()) {
    229                 mQueueNotEmptyCondition.wait(mLock);
    230             }
    231 
    232             event_id eventID = 0;
    233             for (;;) {
    234                 if (mQueue.empty()) {
    235                     // The only event in the queue could have been cancelled
    236                     // while we were waiting for its scheduled time.
    237                     break;
    238                 }
    239 
    240                 List<QueueItem>::iterator it = mQueue.begin();
    241                 eventID = (*it).event->eventID();
    242 
    243                 now_us = ALooper::GetNowUs();
    244                 int64_t when_us = (*it).realtime_us;
    245 
    246                 int64_t delay_us;
    247                 if (when_us < 0 || when_us == INT64_MAX) {
    248                     delay_us = 0;
    249                 } else {
    250                     delay_us = when_us - now_us;
    251                 }
    252 
    253                 if (delay_us <= 0) {
    254                     break;
    255                 }
    256 
    257                 static int64_t kMaxTimeoutUs = 10000000ll;  // 10 secs
    258                 bool timeoutCapped = false;
    259                 if (delay_us > kMaxTimeoutUs) {
    260                     ALOGW("delay_us exceeds max timeout: %lld us", delay_us);
    261 
    262                     // We'll never block for more than 10 secs, instead
    263                     // we will split up the full timeout into chunks of
    264                     // 10 secs at a time. This will also avoid overflow
    265                     // when converting from us to ns.
    266                     delay_us = kMaxTimeoutUs;
    267                     timeoutCapped = true;
    268                 }
    269 
    270                 status_t err = mQueueHeadChangedCondition.waitRelative(
    271                         mLock, delay_us * 1000ll);
    272 
    273                 if (!timeoutCapped && err == -ETIMEDOUT) {
    274                     // We finally hit the time this event is supposed to
    275                     // trigger.
    276                     now_us = ALooper::GetNowUs();
    277                     break;
    278                 }
    279             }
    280 
    281             // The event w/ this id may have been cancelled while we're
    282             // waiting for its trigger-time, in that case
    283             // removeEventFromQueue_l will return NULL.
    284             // Otherwise, the QueueItem will be removed
    285             // from the queue and the referenced event returned.
    286             event = removeEventFromQueue_l(eventID);
    287         }
    288 
    289         if (event != NULL) {
    290             // Fire event with the lock NOT held.
    291             event->fire(this, now_us);
    292         }
    293     }
    294 }
    295 
    296 sp<TimedEventQueue::Event> TimedEventQueue::removeEventFromQueue_l(
    297         event_id id) {
    298     for (List<QueueItem>::iterator it = mQueue.begin();
    299          it != mQueue.end(); ++it) {
    300         if ((*it).event->eventID() == id) {
    301             sp<Event> event = (*it).event;
    302             event->setEventID(0);
    303             if ((*it).has_wakelock) {
    304                 releaseWakeLock_l();
    305             }
    306             mQueue.erase(it);
    307             return event;
    308         }
    309     }
    310 
    311     ALOGW("Event %d was not found in the queue, already cancelled?", id);
    312 
    313     return NULL;
    314 }
    315 
    316 void TimedEventQueue::acquireWakeLock_l()
    317 {
    318     if (mWakeLockCount++ == 0) {
    319         CHECK(mWakeLockToken == 0);
    320         if (mPowerManager == 0) {
    321             // use checkService() to avoid blocking if power service is not up yet
    322             sp<IBinder> binder =
    323                 defaultServiceManager()->checkService(String16("power"));
    324             if (binder == 0) {
    325                 ALOGW("cannot connect to the power manager service");
    326             } else {
    327                 mPowerManager = interface_cast<IPowerManager>(binder);
    328                 binder->linkToDeath(mDeathRecipient);
    329             }
    330         }
    331         if (mPowerManager != 0) {
    332             sp<IBinder> binder = new BBinder();
    333             int64_t token = IPCThreadState::self()->clearCallingIdentity();
    334             status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
    335                                                              binder,
    336                                                              String16("TimedEventQueue"),
    337                                                              String16("media"));
    338             IPCThreadState::self()->restoreCallingIdentity(token);
    339             if (status == NO_ERROR) {
    340                 mWakeLockToken = binder;
    341             }
    342         }
    343     }
    344 }
    345 
    346 void TimedEventQueue::releaseWakeLock_l(bool force)
    347 {
    348     if (force) {
    349         if (mWakeLockCount == 0) {
    350             return;
    351         }
    352         // Force wakelock release below by setting reference count to 1.
    353         mWakeLockCount = 1;
    354     }
    355     CHECK(mWakeLockCount != 0);
    356     if (--mWakeLockCount == 0) {
    357         CHECK(mWakeLockToken != 0);
    358         if (mPowerManager != 0) {
    359             int64_t token = IPCThreadState::self()->clearCallingIdentity();
    360             mPowerManager->releaseWakeLock(mWakeLockToken, 0);
    361             IPCThreadState::self()->restoreCallingIdentity(token);
    362         }
    363         mWakeLockToken.clear();
    364     }
    365 }
    366 
    367 void TimedEventQueue::clearPowerManager()
    368 {
    369     Mutex::Autolock _l(mLock);
    370     releaseWakeLock_l(true /*force*/);
    371     mPowerManager.clear();
    372 }
    373 
    374 void TimedEventQueue::PMDeathRecipient::binderDied(const wp<IBinder>& who)
    375 {
    376     mQueue->clearPowerManager();
    377 }
    378 
    379 }  // namespace android
    380 
    381