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 
30 namespace android {
31 
32 enum {
33     COUNTTRACKS = IBinder::FIRST_CALL_TRANSACTION,
34     GETTRACK,
35     GETTRACKMETADATA,
36     GETMETADATA,
37     FLAGS,
38     SETMEDIACAS,
39     NAME,
40     GETMETRICS
41 };
42 
43 class BpMediaExtractor : public BpInterface<IMediaExtractor> {
44 public:
BpMediaExtractor(const sp<IBinder> & impl)45     explicit BpMediaExtractor(const sp<IBinder>& impl)
46         : BpInterface<IMediaExtractor>(impl)
47     {
48     }
49 
countTracks()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     }
getTrack(size_t index)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 
getTrackMetaData(size_t index,uint32_t flags)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 
getMetaData()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 
getMetrics(Parcel * reply)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 
flags() const108     virtual uint32_t flags() const {
109         ALOGV("flags NOT IMPLEMENTED");
110         return 0;
111     }
112 
setMediaCas(const HInterfaceToken & casToken)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 
name()127     virtual const char * name() {
128         ALOGV("name NOT IMPLEMENTED");
129         return NULL;
130     }
131 };
132 
133 IMPLEMENT_META_INTERFACE(MediaExtractor, "android.media.IMediaExtractor");
134 
135 #undef LOG_TAG
136 #define LOG_TAG "BnMediaExtractor"
137 
onTransact(uint32_t code,const Parcel & data,Parcel * reply,uint32_t flags)138 status_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 
213 typedef 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 
toString() const224 String8 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 
254 static Vector<ExtractorInstance> sExtractors;
255 static Mutex sExtractorsLock;
256 
registerMediaSource(const sp<IMediaExtractor> & ex,const sp<IMediaSource> & source)257 void 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 
registerMediaExtractor(const sp<IMediaExtractor> & extractor,const sp<DataSource> & source,const char * mime)280 void 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 
dumpExtractors(int fd,const Vector<String16> &)300 status_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