1/*
2 * Copyright (C) 2010 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//#define LOG_NDEBUG 0
18#define LOG_TAG "ALooper"
19
20#include <media/stagefright/foundation/ADebug.h>
21
22#include <utils/Log.h>
23
24#include <sys/time.h>
25
26#include "ALooper.h"
27
28#include "AHandler.h"
29#include "ALooperRoster.h"
30#include "AMessage.h"
31
32namespace android {
33
34ALooperRoster gLooperRoster;
35
36struct ALooper::LooperThread : public Thread {
37    LooperThread(ALooper *looper, bool canCallJava)
38        : Thread(canCallJava),
39          mLooper(looper),
40          mThreadId(NULL) {
41    }
42
43    virtual status_t readyToRun() {
44        mThreadId = androidGetThreadId();
45
46        return Thread::readyToRun();
47    }
48
49    virtual bool threadLoop() {
50        return mLooper->loop();
51    }
52
53    bool isCurrentThread() const {
54        return mThreadId == androidGetThreadId();
55    }
56
57protected:
58    virtual ~LooperThread() {}
59
60private:
61    ALooper *mLooper;
62    android_thread_id_t mThreadId;
63
64    DISALLOW_EVIL_CONSTRUCTORS(LooperThread);
65};
66
67// static
68int64_t ALooper::GetNowUs() {
69    return systemTime(SYSTEM_TIME_MONOTONIC) / 1000ll;
70}
71
72ALooper::ALooper()
73    : mRunningLocally(false) {
74    // clean up stale AHandlers. Doing it here instead of in the destructor avoids
75    // the side effect of objects being deleted from the unregister function recursively.
76    gLooperRoster.unregisterStaleHandlers();
77}
78
79ALooper::~ALooper() {
80    stop();
81    // stale AHandlers are now cleaned up in the constructor of the next ALooper to come along
82}
83
84void ALooper::setName(const char *name) {
85    mName = name;
86}
87
88ALooper::handler_id ALooper::registerHandler(const sp<AHandler> &handler) {
89    return gLooperRoster.registerHandler(this, handler);
90}
91
92void ALooper::unregisterHandler(handler_id handlerID) {
93    gLooperRoster.unregisterHandler(handlerID);
94}
95
96status_t ALooper::start(
97        bool runOnCallingThread, bool canCallJava, int32_t priority) {
98    if (runOnCallingThread) {
99        {
100            Mutex::Autolock autoLock(mLock);
101
102            if (mThread != NULL || mRunningLocally) {
103                return INVALID_OPERATION;
104            }
105
106            mRunningLocally = true;
107        }
108
109        do {
110        } while (loop());
111
112        return OK;
113    }
114
115    Mutex::Autolock autoLock(mLock);
116
117    if (mThread != NULL || mRunningLocally) {
118        return INVALID_OPERATION;
119    }
120
121    mThread = new LooperThread(this, canCallJava);
122
123    status_t err = mThread->run(
124            mName.empty() ? "ALooper" : mName.c_str(), priority);
125    if (err != OK) {
126        mThread.clear();
127    }
128
129    return err;
130}
131
132status_t ALooper::stop() {
133    sp<LooperThread> thread;
134    bool runningLocally;
135
136    {
137        Mutex::Autolock autoLock(mLock);
138
139        thread = mThread;
140        runningLocally = mRunningLocally;
141        mThread.clear();
142        mRunningLocally = false;
143    }
144
145    if (thread == NULL && !runningLocally) {
146        return INVALID_OPERATION;
147    }
148
149    if (thread != NULL) {
150        thread->requestExit();
151    }
152
153    mQueueChangedCondition.signal();
154
155    if (!runningLocally && !thread->isCurrentThread()) {
156        // If not running locally and this thread _is_ the looper thread,
157        // the loop() function will return and never be called again.
158        thread->requestExitAndWait();
159    }
160
161    return OK;
162}
163
164void ALooper::post(const sp<AMessage> &msg, int64_t delayUs) {
165    Mutex::Autolock autoLock(mLock);
166
167    int64_t whenUs;
168    if (delayUs > 0) {
169        whenUs = GetNowUs() + delayUs;
170    } else {
171        whenUs = GetNowUs();
172    }
173
174    List<Event>::iterator it = mEventQueue.begin();
175    while (it != mEventQueue.end() && (*it).mWhenUs <= whenUs) {
176        ++it;
177    }
178
179    Event event;
180    event.mWhenUs = whenUs;
181    event.mMessage = msg;
182
183    if (it == mEventQueue.begin()) {
184        mQueueChangedCondition.signal();
185    }
186
187    mEventQueue.insert(it, event);
188}
189
190bool ALooper::loop() {
191    Event event;
192
193    {
194        Mutex::Autolock autoLock(mLock);
195        if (mThread == NULL && !mRunningLocally) {
196            return false;
197        }
198        if (mEventQueue.empty()) {
199            mQueueChangedCondition.wait(mLock);
200            return true;
201        }
202        int64_t whenUs = (*mEventQueue.begin()).mWhenUs;
203        int64_t nowUs = GetNowUs();
204
205        if (whenUs > nowUs) {
206            int64_t delayUs = whenUs - nowUs;
207            mQueueChangedCondition.waitRelative(mLock, delayUs * 1000ll);
208
209            return true;
210        }
211
212        event = *mEventQueue.begin();
213        mEventQueue.erase(mEventQueue.begin());
214    }
215
216    event.mMessage->deliver();
217
218    // NOTE: It's important to note that at this point our "ALooper" object
219    // may no longer exist (its final reference may have gone away while
220    // delivering the message). We have made sure, however, that loop()
221    // won't be called again.
222
223    return true;
224}
225
226// to be called by AMessage::postAndAwaitResponse only
227sp<AReplyToken> ALooper::createReplyToken() {
228    return new AReplyToken(this);
229}
230
231// to be called by AMessage::postAndAwaitResponse only
232status_t ALooper::awaitResponse(const sp<AReplyToken> &replyToken, sp<AMessage> *response) {
233    // return status in case we want to handle an interrupted wait
234    Mutex::Autolock autoLock(mRepliesLock);
235    CHECK(replyToken != NULL);
236    while (!replyToken->retrieveReply(response)) {
237        mRepliesCondition.wait(mRepliesLock);
238    }
239    return OK;
240}
241
242status_t ALooper::postReply(const sp<AReplyToken> &replyToken, const sp<AMessage> &reply) {
243    Mutex::Autolock autoLock(mRepliesLock);
244    status_t err = replyToken->setReply(reply);
245    if (err == OK) {
246        mRepliesCondition.broadcast();
247    }
248    return err;
249}
250
251}  // namespace android
252