MyHandler.h revision cf7b9c7aae758ac0b99833915053c63c2ac46e09
1cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber/*
2cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber * Copyright (C) 2010 The Android Open Source Project
3cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber *
4cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber * Licensed under the Apache License, Version 2.0 (the "License");
5cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber * you may not use this file except in compliance with the License.
6cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber * You may obtain a copy of the License at
7cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber *
8cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber *      http://www.apache.org/licenses/LICENSE-2.0
9cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber *
10cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber * Unless required by applicable law or agreed to in writing, software
11cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber * distributed under the License is distributed on an "AS IS" BASIS,
12cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber * See the License for the specific language governing permissions and
14cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber * limitations under the License.
15cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber */
16cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
17cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#ifndef MY_HANDLER_H_
18cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
19cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#define MY_HANDLER_H_
20cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
21cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#include "APacketSource.h"
22cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#include "ARTPConnection.h"
23cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#include "ARTSPConnection.h"
24cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#include "ASessionDescription.h"
25cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
26cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#include <media/stagefright/foundation/ABuffer.h>
27cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#include <media/stagefright/foundation/ADebug.h>
28cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#include <media/stagefright/foundation/ALooper.h>
29cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#include <media/stagefright/foundation/AMessage.h>
30cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#include <media/stagefright/MediaErrors.h>
31cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
32cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Hubernamespace android {
33cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
34cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huberstruct MyHandler : public AHandler {
35cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    MyHandler(const char *url, const sp<ALooper> &looper)
36cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        : mLooper(looper),
37cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber          mConn(new ARTSPConnection),
38cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber          mRTPConn(new ARTPConnection),
39cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber          mSessionURL(url),
40cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber          mSetupTracksSuccessful(false),
41cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber          mFirstAccessUnit(true),
42cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber          mFirstAccessUnitNTP(-1) {
43cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        mLooper->registerHandler(this);
44cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        mLooper->registerHandler(mConn);
45cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        mLooper->registerHandler(mRTPConn);
46cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        sp<AMessage> reply = new AMessage('conn', id());
47cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        mConn->connect(mSessionURL.c_str(), reply);
48cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    }
49cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
50cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    virtual void onMessageReceived(const sp<AMessage> &msg) {
51cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        switch (msg->what()) {
52cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            case 'conn':
53cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            {
54cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                int32_t result;
55cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(msg->findInt32("result", &result));
56cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
57cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                LOG(INFO) << "connection request completed with result "
58cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                     << result << " (" << strerror(-result) << ")";
59cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
60cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                if (result == OK) {
61cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    AString request;
62cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request = "DESCRIBE ";
63cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append(mSessionURL);
64cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append(" RTSP/1.0\r\n");
65cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append("Accept: application/sdp\r\n");
66cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append("\r\n");
67cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
68cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<AMessage> reply = new AMessage('desc', id());
69cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mConn->sendRequest(request.c_str(), reply);
70cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                }
71cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
72cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
73cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
74cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            case 'disc':
75cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            {
76cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                LOG(INFO) << "disconnect completed";
77cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
78cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                (new AMessage('quit', id()))->post();
79cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
80cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
81cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
82cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            case 'desc':
83cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            {
84cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                int32_t result;
85cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(msg->findInt32("result", &result));
86cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
87cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                LOG(INFO) << "DESCRIBE completed with result "
88cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                     << result << " (" << strerror(-result) << ")";
89cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
90cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                if (result == OK) {
91cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<RefBase> obj;
92cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    CHECK(msg->findObject("response", &obj));
93cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<ARTSPResponse> response =
94cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        static_cast<ARTSPResponse *>(obj.get());
95cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
96cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    if (response->mStatusCode == 302) {
97cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        ssize_t i = response->mHeaders.indexOfKey("location");
98cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        CHECK_GE(i, 0);
99cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
100cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        mSessionURL = response->mHeaders.valueAt(i);
101cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
102cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        AString request;
103cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        request = "DESCRIBE ";
104cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        request.append(mSessionURL);
105cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        request.append(" RTSP/1.0\r\n");
106cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        request.append("Accept: application/sdp\r\n");
107cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        request.append("\r\n");
108cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
109cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        sp<AMessage> reply = new AMessage('desc', id());
110cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        mConn->sendRequest(request.c_str(), reply);
111cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        break;
112cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    }
113cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
114cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    CHECK_EQ(response->mStatusCode, 200u);
115cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
116cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mSessionDesc = new ASessionDescription;
117cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
118cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mSessionDesc->setTo(
119cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            response->mContent->data(),
120cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            response->mContent->size());
121cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
122cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    CHECK(mSessionDesc->isValid());
123cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
124cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    ssize_t i = response->mHeaders.indexOfKey("content-base");
125cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    if (i >= 0) {
126cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        mBaseURL = response->mHeaders.valueAt(i);
127cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    } else {
128cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        i = response->mHeaders.indexOfKey("content-location");
129cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        if (i >= 0) {
130cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            mBaseURL = response->mHeaders.valueAt(i);
131cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        } else {
132cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            mBaseURL = mSessionURL;
133cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        }
134cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    }
135cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
136cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    CHECK_GT(mSessionDesc->countTracks(), 1u);
137cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    setupTrack(1);
138cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                } else {
139cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<AMessage> reply = new AMessage('disc', id());
140cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mConn->disconnect(reply);
141cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                }
142cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
143cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
144cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
145cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            case 'setu':
146cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            {
147cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                size_t index;
148cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(msg->findSize("index", &index));
149cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
150cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                size_t trackIndex;
151cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(msg->findSize("track-index", &trackIndex));
152cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
153cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                int32_t result;
154cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(msg->findInt32("result", &result));
155cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
156cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                LOG(INFO) << "SETUP(" << index << ") completed with result "
157cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                     << result << " (" << strerror(-result) << ")";
158cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
159cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                TrackInfo *track = &mTracks.editItemAt(trackIndex);
160cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
161cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                if (result == OK) {
162cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<RefBase> obj;
163cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    CHECK(msg->findObject("response", &obj));
164cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<ARTSPResponse> response =
165cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        static_cast<ARTSPResponse *>(obj.get());
166cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
167cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    CHECK_EQ(response->mStatusCode, 200u);
168cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
169cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    ssize_t i = response->mHeaders.indexOfKey("session");
170cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    CHECK_GE(i, 0);
171cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
172cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    if (index == 1) {
173cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        mSessionID = response->mHeaders.valueAt(i);
174cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        i = mSessionID.find(";");
175cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        if (i >= 0) {
176cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            // Remove options, i.e. ";timeout=90"
177cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            mSessionID.erase(i, mSessionID.size() - i);
178cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        }
179cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    }
180cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
181cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<AMessage> notify = new AMessage('accu', id());
182cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    notify->setSize("track-index", trackIndex);
183cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
184cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mRTPConn->addStream(
185cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            track->mRTPSocket, track->mRTCPSocket,
186cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            mSessionDesc, index,
187cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            notify);
188cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
189cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    track->mPacketSource =
190cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        new APacketSource(mSessionDesc, index);
191cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
192cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mSetupTracksSuccessful = true;
193cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
194cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    ++index;
195cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    if (index < mSessionDesc->countTracks()) {
196cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        setupTrack(index);
197cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        break;
198cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    }
199cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                } else {
200cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    close(track->mRTPSocket);
201cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    close(track->mRTCPSocket);
202cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
203cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mTracks.removeItemsAt(mTracks.size() - 1);
204cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                }
205cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
206cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                if (mSetupTracksSuccessful) {
207cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    AString request = "PLAY ";
208cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append(mSessionURL);
209cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append(" RTSP/1.0\r\n");
210cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
211cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append("Session: ");
212cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append(mSessionID);
213cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append("\r\n");
214cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
215cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    request.append("\r\n");
216cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
217cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<AMessage> reply = new AMessage('play', id());
218cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mConn->sendRequest(request.c_str(), reply);
219cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                } else {
220cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<AMessage> reply = new AMessage('disc', id());
221cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mConn->disconnect(reply);
222cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                }
223cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
224cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
225cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
226cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            case 'play':
227cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            {
228cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                int32_t result;
229cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(msg->findInt32("result", &result));
230cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
231cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                LOG(INFO) << "PLAY completed with result "
232cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                     << result << " (" << strerror(-result) << ")";
233cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
234cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                if (result == OK) {
235cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<RefBase> obj;
236cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    CHECK(msg->findObject("response", &obj));
237cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<ARTSPResponse> response =
238cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                        static_cast<ARTSPResponse *>(obj.get());
239cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
240cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    CHECK_EQ(response->mStatusCode, 200u);
241cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
242cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<AMessage> msg = new AMessage('abor', id());
243cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    msg->post(60000000ll);
244cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                } else {
245cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    sp<AMessage> reply = new AMessage('disc', id());
246cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mConn->disconnect(reply);
247cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                }
248cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
249cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
250cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
251cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
252cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            case 'abor':
253cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            {
254cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                for (size_t i = 0; i < mTracks.size(); ++i) {
255cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mTracks.editItemAt(i).mPacketSource->signalEOS(
256cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            ERROR_END_OF_STREAM);
257cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                }
258cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
259cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                sp<AMessage> reply = new AMessage('tear', id());
260cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
261cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                AString request;
262cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                request = "TEARDOWN ";
263cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
264cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                // XXX should use aggregate url from SDP here...
265cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                request.append(mSessionURL);
266cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                request.append(" RTSP/1.0\r\n");
267cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
268cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                request.append("Session: ");
269cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                request.append(mSessionID);
270cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                request.append("\r\n");
271cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
272cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                request.append("\r\n");
273cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
274cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                mConn->sendRequest(request.c_str(), reply);
275cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
276cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
277cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
278cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            case 'tear':
279cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            {
280cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                int32_t result;
281cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(msg->findInt32("result", &result));
282cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
283cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                LOG(INFO) << "TEARDOWN completed with result "
284cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                     << result << " (" << strerror(-result) << ")";
285cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
286cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                sp<AMessage> reply = new AMessage('disc', id());
287cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                mConn->disconnect(reply);
288cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
289cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
290cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
291cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            case 'quit':
292cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            {
293cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                mLooper->stop();
294cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
295cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
296cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
297cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            case 'accu':
298cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            {
299cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                size_t trackIndex;
300cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(msg->findSize("track-index", &trackIndex));
301cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
302cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                sp<RefBase> obj;
303cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(msg->findObject("access-unit", &obj));
304cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
305cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
306cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
307cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                uint64_t ntpTime;
308cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                CHECK(accessUnit->meta()->findInt64(
309cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                            "ntp-time", (int64_t *)&ntpTime));
310cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
311cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                if (mFirstAccessUnit) {
312cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mFirstAccessUnit = false;
313cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    mFirstAccessUnitNTP = ntpTime;
314cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                }
315cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                if (ntpTime > mFirstAccessUnitNTP) {
316cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    ntpTime -= mFirstAccessUnitNTP;
317cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                } else {
318cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                    ntpTime = 0;
319cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                }
320cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
321cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                accessUnit->meta()->setInt64("ntp-time", ntpTime);
322cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
323cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                TrackInfo *track = &mTracks.editItemAt(trackIndex);
324cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                track->mPacketSource->queueAccessUnit(accessUnit);
325cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
326cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
327cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
328cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            default:
329cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                TRESPASS();
330cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                break;
331cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        }
332cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    }
333cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
334cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    sp<APacketSource> getPacketSource(size_t index) {
335cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        CHECK_GE(index, 0u);
336cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        CHECK_LT(index, mTracks.size());
337cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
338cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        return mTracks.editItemAt(index).mPacketSource;
339cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    }
340cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
341cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    size_t countTracks() const {
342cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        return mTracks.size();
343cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    }
344cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
345cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huberprivate:
346cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    sp<ALooper> mLooper;
347cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    sp<ARTSPConnection> mConn;
348cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    sp<ARTPConnection> mRTPConn;
349cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    sp<ASessionDescription> mSessionDesc;
350cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    AString mSessionURL;
351cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    AString mBaseURL;
352cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    AString mSessionID;
353cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    bool mSetupTracksSuccessful;
354cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    bool mFirstAccessUnit;
355cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    uint64_t mFirstAccessUnitNTP;
356cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
357cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    struct TrackInfo {
358cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        int mRTPSocket;
359cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        int mRTCPSocket;
360cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
361cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        sp<APacketSource> mPacketSource;
362cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    };
363cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    Vector<TrackInfo> mTracks;
364cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
365cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    void setupTrack(size_t index) {
366cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        AString url;
367cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        CHECK(mSessionDesc->findAttribute(index, "a=control", &url));
368cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
369cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        AString trackURL;
370cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        CHECK(MakeURL(mBaseURL.c_str(), url.c_str(), &trackURL));
371cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
372cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        mTracks.push(TrackInfo());
373cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
374cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
375cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        unsigned rtpPort;
376cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        ARTPConnection::MakePortPair(
377cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                &info->mRTPSocket, &info->mRTCPSocket, &rtpPort);
378cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
379cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        AString request = "SETUP ";
380cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        request.append(trackURL);
381cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        request.append(" RTSP/1.0\r\n");
382cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
383cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        request.append("Transport: RTP/AVP/UDP;unicast;client_port=");
384cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        request.append(rtpPort);
385cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        request.append("-");
386cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        request.append(rtpPort + 1);
387cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        request.append("\r\n");
388cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
389cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        if (index > 1) {
390cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            request.append("Session: ");
391cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            request.append(mSessionID);
392cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            request.append("\r\n");
393cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        }
394cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
395cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        request.append("\r\n");
396cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
397cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        sp<AMessage> reply = new AMessage('setu', id());
398cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        reply->setSize("index", index);
399cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        reply->setSize("track-index", mTracks.size() - 1);
400cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        mConn->sendRequest(request.c_str(), reply);
401cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    }
402cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
403cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    static bool MakeURL(const char *baseURL, const char *url, AString *out) {
404cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        out->clear();
405cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
406cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        if (strncasecmp("rtsp://", baseURL, 7)) {
407cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            // Base URL must be absolute
408cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            return false;
409cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        }
410cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
411cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        if (!strncasecmp("rtsp://", url, 7)) {
412cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            // "url" is already an absolute URL, ignore base URL.
413cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            out->setTo(url);
414cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            return true;
415cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        }
416cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
417cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        size_t n = strlen(baseURL);
418cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        if (baseURL[n - 1] == '/') {
419cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            out->setTo(baseURL);
420cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            out->append(url);
421cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        } else {
422cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            char *slashPos = strrchr(baseURL, '/');
423cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
424cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            if (slashPos > &baseURL[6]) {
425cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                out->setTo(baseURL, slashPos - baseURL);
426cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            } else {
427cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber                out->setTo(baseURL);
428cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            }
429cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
430cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            out->append("/");
431cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber            out->append(url);
432cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        }
433cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
434cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber        return true;
435cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    }
436cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
437cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber    DISALLOW_EVIL_CONSTRUCTORS(MyHandler);
438cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber};
439cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
440cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber}  // namespace android
441cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber
442cf7b9c7aae758ac0b99833915053c63c2ac46e09Andreas Huber#endif  // MY_HANDLER_H_
443