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//#define LOG_NDEBUG 0
18#define LOG_TAG "BpMediaExtractor"
19#include <utils/Log.h>
20
21#include <stdint.h>
22#include <sys/types.h>
23
24#include <binder/IPCThreadState.h>
25#include <binder/Parcel.h>
26#include <binder/PermissionCache.h>
27#include <media/IMediaExtractor.h>
28#include <media/stagefright/MetaData.h>
29
30namespace android {
31
32enum {
33    COUNTTRACKS = IBinder::FIRST_CALL_TRANSACTION,
34    GETTRACK,
35    GETTRACKMETADATA,
36    GETMETADATA,
37    FLAGS,
38    SETMEDIACAS,
39    NAME,
40    GETMETRICS
41};
42
43class BpMediaExtractor : public BpInterface<IMediaExtractor> {
44public:
45    explicit BpMediaExtractor(const sp<IBinder>& impl)
46        : BpInterface<IMediaExtractor>(impl)
47    {
48    }
49
50    virtual size_t countTracks() {
51        ALOGV("countTracks");
52        Parcel data, reply;
53        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
54        status_t ret = remote()->transact(COUNTTRACKS, data, &reply);
55        size_t numTracks = 0;
56        if (ret == NO_ERROR) {
57            numTracks = reply.readUint32();
58        }
59        return numTracks;
60    }
61    virtual sp<IMediaSource> getTrack(size_t index) {
62        ALOGV("getTrack(%zu)", index);
63        Parcel data, reply;
64        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
65        data.writeUint32(index);
66        status_t ret = remote()->transact(GETTRACK, data, &reply);
67        if (ret == NO_ERROR) {
68            return interface_cast<IMediaSource>(reply.readStrongBinder());
69        }
70        return NULL;
71    }
72
73    virtual sp<MetaData> getTrackMetaData(
74            size_t index, uint32_t flags) {
75        ALOGV("getTrackMetaData(%zu, %u)", index, flags);
76        Parcel data, reply;
77        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
78        data.writeUint32(index);
79        data.writeUint32(flags);
80        status_t ret = remote()->transact(GETTRACKMETADATA, data, &reply);
81        if (ret == NO_ERROR) {
82            return MetaData::createFromParcel(reply);
83        }
84        return NULL;
85    }
86
87    virtual sp<MetaData> getMetaData() {
88        ALOGV("getMetaData");
89        Parcel data, reply;
90        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
91        status_t ret = remote()->transact(GETMETADATA, data, &reply);
92        if (ret == NO_ERROR) {
93            return MetaData::createFromParcel(reply);
94        }
95        return NULL;
96    }
97
98    virtual status_t getMetrics(Parcel * reply) {
99        Parcel data;
100        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
101        status_t ret = remote()->transact(GETMETRICS, data, reply);
102        if (ret == NO_ERROR) {
103            return OK;
104        }
105        return UNKNOWN_ERROR;
106    }
107
108    virtual uint32_t flags() const {
109        ALOGV("flags NOT IMPLEMENTED");
110        return 0;
111    }
112
113    virtual status_t setMediaCas(const HInterfaceToken &casToken) {
114        ALOGV("setMediaCas");
115
116        Parcel data, reply;
117        data.writeInterfaceToken(BpMediaExtractor::getInterfaceDescriptor());
118        data.writeByteVector(casToken);
119
120        status_t err = remote()->transact(SETMEDIACAS, data, &reply);
121        if (err != NO_ERROR) {
122            return err;
123        }
124        return reply.readInt32();
125    }
126
127    virtual const char * name() {
128        ALOGV("name NOT IMPLEMENTED");
129        return NULL;
130    }
131};
132
133IMPLEMENT_META_INTERFACE(MediaExtractor, "android.media.IMediaExtractor");
134
135#undef LOG_TAG
136#define LOG_TAG "BnMediaExtractor"
137
138status_t BnMediaExtractor::onTransact(
139    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
140{
141    switch (code) {
142        case COUNTTRACKS: {
143            ALOGV("countTracks");
144            CHECK_INTERFACE(IMediaExtractor, data, reply);
145            size_t numTracks = countTracks();
146            if (numTracks > INT32_MAX) {
147                numTracks = 0;
148            }
149            reply->writeUint32(uint32_t(numTracks));
150            return NO_ERROR;
151        }
152        case GETTRACK: {
153            ALOGV("getTrack()");
154            CHECK_INTERFACE(IMediaExtractor, data, reply);
155            uint32_t idx;
156            if (data.readUint32(&idx) == NO_ERROR) {
157                const sp<IMediaSource> track = getTrack(size_t(idx));
158                registerMediaSource(this, track);
159                return reply->writeStrongBinder(IInterface::asBinder(track));
160            }
161            return UNKNOWN_ERROR;
162        }
163        case GETTRACKMETADATA: {
164            ALOGV("getTrackMetaData");
165            CHECK_INTERFACE(IMediaExtractor, data, reply);
166            uint32_t idx;
167            uint32_t flags;
168            if (data.readUint32(&idx) == NO_ERROR &&
169                    data.readUint32(&flags) == NO_ERROR) {
170                sp<MetaData> meta = getTrackMetaData(idx, flags);
171                if (meta == NULL) {
172                    return UNKNOWN_ERROR;
173                }
174                meta->writeToParcel(*reply);
175                return NO_ERROR;
176            }
177            return UNKNOWN_ERROR;
178        }
179        case GETMETADATA: {
180            ALOGV("getMetaData");
181            CHECK_INTERFACE(IMediaExtractor, data, reply);
182            sp<MetaData> meta = getMetaData();
183            if (meta != NULL) {
184                meta->writeToParcel(*reply);
185                return NO_ERROR;
186            }
187            return UNKNOWN_ERROR;
188        }
189        case GETMETRICS: {
190            CHECK_INTERFACE(IMediaExtractor, data, reply);
191            status_t ret = getMetrics(reply);
192            return ret;
193        }
194        case SETMEDIACAS: {
195            ALOGV("setMediaCas");
196            CHECK_INTERFACE(IMediaExtractor, data, reply);
197
198            HInterfaceToken casToken;
199            status_t err = data.readByteVector(&casToken);
200            if (err != NO_ERROR) {
201                ALOGE("Error reading casToken from parcel");
202                return err;
203            }
204
205            reply->writeInt32(setMediaCas(casToken));
206            return OK;
207        }
208        default:
209            return BBinder::onTransact(code, data, reply, flags);
210    }
211}
212
213typedef struct {
214    String8 mime;
215    String8 name;
216    String8 sourceDescription;
217    pid_t owner;
218    wp<IMediaExtractor> extractor;
219    Vector<wp<IMediaSource>> tracks;
220    Vector<String8> trackDescriptions;
221    String8 toString() const;
222} ExtractorInstance;
223
224String8 ExtractorInstance::toString() const {
225    String8 str = name;
226    str.append(" for mime ");
227    str.append(mime);
228    str.append(", source ");
229    str.append(sourceDescription);
230    str.append(String8::format(", pid %d: ", owner));
231    if (extractor.promote() == NULL) {
232        str.append("deleted\n");
233    } else {
234        str.append("active\n");
235    }
236    for (size_t i = 0; i < tracks.size(); i++) {
237        const String8 desc = trackDescriptions.itemAt(i);
238        str.appendFormat("    track {%s} ", desc.string());
239        wp<IMediaSource> wSource = tracks.itemAt(i);
240        if (wSource == NULL) {
241            str.append(": null\n");
242        } else {
243            const sp<IMediaSource> source = wSource.promote();
244            if (source == NULL) {
245                str.append(": deleted\n");
246            } else {
247                str.appendFormat(": active\n");
248            }
249        }
250    }
251    return str;
252}
253
254static Vector<ExtractorInstance> sExtractors;
255static Mutex sExtractorsLock;
256
257void registerMediaSource(
258        const sp<IMediaExtractor> &ex,
259        const sp<IMediaSource> &source) {
260    Mutex::Autolock lock(sExtractorsLock);
261    for (size_t i = 0; i < sExtractors.size(); i++) {
262        ExtractorInstance &instance = sExtractors.editItemAt(i);
263        sp<IMediaExtractor> extractor = instance.extractor.promote();
264        if (extractor != NULL && extractor == ex) {
265            if (instance.tracks.size() > 5) {
266                instance.tracks.resize(5);
267                instance.trackDescriptions.resize(5);
268            }
269            instance.tracks.push_front(source);
270            if (source != NULL) {
271                instance.trackDescriptions.push_front(source->getFormat()->toString());
272            } else {
273                instance.trackDescriptions.push_front(String8::empty());
274            }
275            break;
276        }
277    }
278}
279
280void registerMediaExtractor(
281        const sp<IMediaExtractor> &extractor,
282        const sp<DataSource> &source,
283        const char *mime) {
284    ExtractorInstance ex;
285    ex.mime = mime == NULL ? "NULL" : mime;
286    ex.name = extractor->name();
287    ex.sourceDescription = source->toString();
288    ex.owner = IPCThreadState::self()->getCallingPid();
289    ex.extractor = extractor;
290
291    {
292        Mutex::Autolock lock(sExtractorsLock);
293        if (sExtractors.size() > 10) {
294            sExtractors.resize(10);
295        }
296        sExtractors.push_front(ex);
297    }
298}
299
300status_t dumpExtractors(int fd, const Vector<String16>&) {
301    String8 out;
302    const IPCThreadState* ipc = IPCThreadState::self();
303    const int pid = ipc->getCallingPid();
304    const int uid = ipc->getCallingUid();
305    if (!PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
306        out.appendFormat("Permission Denial: "
307                "can't dump MediaExtractor from pid=%d, uid=%d\n", pid, uid);
308    } else {
309        out.append("Recent extractors, most recent first:\n");
310        {
311            Mutex::Autolock lock(sExtractorsLock);
312            for (size_t i = 0; i < sExtractors.size(); i++) {
313                const ExtractorInstance &instance = sExtractors.itemAt(i);
314                out.append("  ");
315                out.append(instance.toString());
316            }
317        }
318    }
319    write(fd, out.string(), out.size());
320    return OK;
321}
322
323
324}  // namespace android
325
326