TimedEventQueue.cpp revision e8332ee38cc9778aa7898bbd75858561ed1e0ba3
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
39namespace android {
40
41static int64_t kWakelockMinDelay = 100000ll;  // 100ms
42
43TimedEventQueue::TimedEventQueue()
44    : mNextEventID(1),
45      mRunning(false),
46      mStopped(false),
47      mDeathRecipient(new PMDeathRecipient(this)),
48      mWakeLockCount(0) {
49}
50
51TimedEventQueue::~TimedEventQueue() {
52    stop();
53    if (mPowerManager != 0) {
54        sp<IBinder> binder = mPowerManager->asBinder();
55        binder->unlinkToDeath(mDeathRecipient);
56    }
57}
58
59void 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
77void 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
99TimedEventQueue::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
105TimedEventQueue::event_id TimedEventQueue::postEventToBack(
106        const sp<Event> &event) {
107    return postTimedEvent(event, INT64_MAX);
108}
109
110TimedEventQueue::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
116TimedEventQueue::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
147static 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
161bool 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
174void 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
205void *TimedEventQueue::ThreadWrapper(void *me) {
206
207    androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND);
208
209    static_cast<TimedEventQueue *>(me)->threadEntry();
210
211    return NULL;
212}
213
214void 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
296sp<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
316void 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
346void 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
367void TimedEventQueue::clearPowerManager()
368{
369    Mutex::Autolock _l(mLock);
370    releaseWakeLock_l(true /*force*/);
371    mPowerManager.clear();
372}
373
374void TimedEventQueue::PMDeathRecipient::binderDied(const wp<IBinder>& who)
375{
376    mQueue->clearPowerManager();
377}
378
379}  // namespace android
380
381