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         bool wakeLocked = false;
    221 
    222         {
    223             Mutex::Autolock autoLock(mLock);
    224 
    225             if (mStopped) {
    226                 break;
    227             }
    228 
    229             while (mQueue.empty()) {
    230                 mQueueNotEmptyCondition.wait(mLock);
    231             }
    232 
    233             event_id eventID = 0;
    234             for (;;) {
    235                 if (mQueue.empty()) {
    236                     // The only event in the queue could have been cancelled
    237                     // while we were waiting for its scheduled time.
    238                     break;
    239                 }
    240 
    241                 List<QueueItem>::iterator it = mQueue.begin();
    242                 eventID = (*it).event->eventID();
    243 
    244                 now_us = ALooper::GetNowUs();
    245                 int64_t when_us = (*it).realtime_us;
    246 
    247                 int64_t delay_us;
    248                 if (when_us < 0 || when_us == INT64_MAX) {
    249                     delay_us = 0;
    250                 } else {
    251                     delay_us = when_us - now_us;
    252                 }
    253 
    254                 if (delay_us <= 0) {
    255                     break;
    256                 }
    257 
    258                 static int64_t kMaxTimeoutUs = 10000000ll;  // 10 secs
    259                 bool timeoutCapped = false;
    260                 if (delay_us > kMaxTimeoutUs) {
    261                     ALOGW("delay_us exceeds max timeout: %lld us", delay_us);
    262 
    263                     // We'll never block for more than 10 secs, instead
    264                     // we will split up the full timeout into chunks of
    265                     // 10 secs at a time. This will also avoid overflow
    266                     // when converting from us to ns.
    267                     delay_us = kMaxTimeoutUs;
    268                     timeoutCapped = true;
    269                 }
    270 
    271                 status_t err = mQueueHeadChangedCondition.waitRelative(
    272                         mLock, delay_us * 1000ll);
    273 
    274                 if (!timeoutCapped && err == -ETIMEDOUT) {
    275                     // We finally hit the time this event is supposed to
    276                     // trigger.
    277                     now_us = ALooper::GetNowUs();
    278                     break;
    279                 }
    280             }
    281 
    282             // The event w/ this id may have been cancelled while we're
    283             // waiting for its trigger-time, in that case
    284             // removeEventFromQueue_l will return NULL.
    285             // Otherwise, the QueueItem will be removed
    286             // from the queue and the referenced event returned.
    287             event = removeEventFromQueue_l(eventID, &wakeLocked);
    288         }
    289 
    290         if (event != NULL) {
    291             // Fire event with the lock NOT held.
    292             event->fire(this, now_us);
    293             if (wakeLocked) {
    294                 Mutex::Autolock autoLock(mLock);
    295                 releaseWakeLock_l();
    296             }
    297         }
    298     }
    299 }
    300 
    301 sp<TimedEventQueue::Event> TimedEventQueue::removeEventFromQueue_l(
    302         event_id id, bool *wakeLocked) {
    303     for (List<QueueItem>::iterator it = mQueue.begin();
    304          it != mQueue.end(); ++it) {
    305         if ((*it).event->eventID() == id) {
    306             sp<Event> event = (*it).event;
    307             event->setEventID(0);
    308             *wakeLocked = (*it).has_wakelock;
    309             mQueue.erase(it);
    310             return event;
    311         }
    312     }
    313 
    314     ALOGW("Event %d was not found in the queue, already cancelled?", id);
    315 
    316     return NULL;
    317 }
    318 
    319 void TimedEventQueue::acquireWakeLock_l()
    320 {
    321     if (mWakeLockCount++ == 0) {
    322         CHECK(mWakeLockToken == 0);
    323         if (mPowerManager == 0) {
    324             // use checkService() to avoid blocking if power service is not up yet
    325             sp<IBinder> binder =
    326                 defaultServiceManager()->checkService(String16("power"));
    327             if (binder == 0) {
    328                 ALOGW("cannot connect to the power manager service");
    329             } else {
    330                 mPowerManager = interface_cast<IPowerManager>(binder);
    331                 binder->linkToDeath(mDeathRecipient);
    332             }
    333         }
    334         if (mPowerManager != 0) {
    335             sp<IBinder> binder = new BBinder();
    336             int64_t token = IPCThreadState::self()->clearCallingIdentity();
    337             status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
    338                                                              binder,
    339                                                              String16("TimedEventQueue"),
    340                                                              String16("media"));
    341             IPCThreadState::self()->restoreCallingIdentity(token);
    342             if (status == NO_ERROR) {
    343                 mWakeLockToken = binder;
    344             }
    345         }
    346     }
    347 }
    348 
    349 void TimedEventQueue::releaseWakeLock_l(bool force)
    350 {
    351     if (force) {
    352         if (mWakeLockCount == 0) {
    353             return;
    354         }
    355         // Force wakelock release below by setting reference count to 1.
    356         mWakeLockCount = 1;
    357     }
    358     CHECK(mWakeLockCount != 0);
    359     if (--mWakeLockCount == 0) {
    360         CHECK(mWakeLockToken != 0);
    361         if (mPowerManager != 0) {
    362             int64_t token = IPCThreadState::self()->clearCallingIdentity();
    363             mPowerManager->releaseWakeLock(mWakeLockToken, 0);
    364             IPCThreadState::self()->restoreCallingIdentity(token);
    365         }
    366         mWakeLockToken.clear();
    367     }
    368 }
    369 
    370 void TimedEventQueue::clearPowerManager()
    371 {
    372     Mutex::Autolock _l(mLock);
    373     releaseWakeLock_l(true /*force*/);
    374     mPowerManager.clear();
    375 }
    376 
    377 void TimedEventQueue::PMDeathRecipient::binderDied(const wp<IBinder>& who)
    378 {
    379     mQueue->clearPowerManager();
    380 }
    381 
    382 }  // namespace android
    383 
    384