1 /*
2 **
3 ** Copyright (C) 2008 The Android Open Source Project
4 **
5 ** Licensed under the Apache License, Version 2.0 (the "License");
6 ** you may not use this file except in compliance with the License.
7 ** You may obtain a copy of the License at
8 **
9 **     http://www.apache.org/licenses/LICENSE-2.0
10 **
11 ** Unless required by applicable law or agreed to in writing, software
12 ** distributed under the License is distributed on an "AS IS" BASIS,
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 ** See the License for the specific language governing permissions and
15 ** limitations under the License.
16 */
17 
18 #include <inttypes.h>
19 #include <stdint.h>
20 #include <sys/types.h>
21 
22 #include <binder/Parcel.h>
23 #include <media/IDataSource.h>
24 #include <media/IMediaHTTPService.h>
25 #include <media/IMediaMetadataRetriever.h>
26 #include <utils/String8.h>
27 #include <utils/KeyedVector.h>
28 
29 // The binder is supposed to propagate the scheduler group across
30 // the binder interface so that remote calls are executed with
31 // the same priority as local calls. This is currently not working
32 // so this change puts in a temporary hack to fix the issue with
33 // metadata retrieval which can be a huge CPU hit if done on a
34 // foreground thread.
35 #ifndef DISABLE_GROUP_SCHEDULE_HACK
36 
37 #undef LOG_TAG
38 #define LOG_TAG "IMediaMetadataRetriever"
39 #include <utils/Log.h>
40 #include <cutils/sched_policy.h>
41 
42 namespace android {
43 
sendSchedPolicy(Parcel & data)44 static void sendSchedPolicy(Parcel& data)
45 {
46     SchedPolicy policy;
47     get_sched_policy(gettid(), &policy);
48     data.writeInt32(policy);
49 }
50 
setSchedPolicy(const Parcel & data)51 static void setSchedPolicy(const Parcel& data)
52 {
53     SchedPolicy policy = (SchedPolicy) data.readInt32();
54     set_sched_policy(gettid(), policy);
55 }
restoreSchedPolicy()56 static void restoreSchedPolicy()
57 {
58     set_sched_policy(gettid(), SP_FOREGROUND);
59 }
60 }; // end namespace android
61 #endif
62 
63 namespace android {
64 
65 enum {
66     DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
67     SET_DATA_SOURCE_URL,
68     SET_DATA_SOURCE_FD,
69     SET_DATA_SOURCE_CALLBACK,
70     GET_FRAME_AT_TIME,
71     GET_IMAGE_AT_INDEX,
72     GET_IMAGE_RECT_AT_INDEX,
73     GET_FRAME_AT_INDEX,
74     EXTRACT_ALBUM_ART,
75     EXTRACT_METADATA,
76 };
77 
78 class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
79 {
80 public:
BpMediaMetadataRetriever(const sp<IBinder> & impl)81     explicit BpMediaMetadataRetriever(const sp<IBinder>& impl)
82         : BpInterface<IMediaMetadataRetriever>(impl)
83     {
84     }
85 
86     // disconnect from media metadata retriever service
disconnect()87     void disconnect()
88     {
89         Parcel data, reply;
90         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
91         remote()->transact(DISCONNECT, data, &reply);
92     }
93 
setDataSource(const sp<IMediaHTTPService> & httpService,const char * srcUrl,const KeyedVector<String8,String8> * headers)94     status_t setDataSource(
95             const sp<IMediaHTTPService> &httpService,
96             const char *srcUrl,
97             const KeyedVector<String8, String8> *headers)
98     {
99         Parcel data, reply;
100         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
101         data.writeInt32(httpService != NULL);
102         if (httpService != NULL) {
103             data.writeStrongBinder(IInterface::asBinder(httpService));
104         }
105         data.writeCString(srcUrl);
106 
107         if (headers == NULL) {
108             data.writeInt32(0);
109         } else {
110             // serialize the headers
111             data.writeInt64(headers->size());
112             for (size_t i = 0; i < headers->size(); ++i) {
113                 data.writeString8(headers->keyAt(i));
114                 data.writeString8(headers->valueAt(i));
115             }
116         }
117 
118         remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
119         return reply.readInt32();
120     }
121 
setDataSource(int fd,int64_t offset,int64_t length)122     status_t setDataSource(int fd, int64_t offset, int64_t length)
123     {
124         Parcel data, reply;
125         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
126         data.writeFileDescriptor(fd);
127         data.writeInt64(offset);
128         data.writeInt64(length);
129         remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
130         return reply.readInt32();
131     }
132 
setDataSource(const sp<IDataSource> & source,const char * mime)133     status_t setDataSource(const sp<IDataSource>& source, const char *mime)
134     {
135         Parcel data, reply;
136         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
137         data.writeStrongBinder(IInterface::asBinder(source));
138 
139         if (mime != NULL) {
140             data.writeInt32(1);
141             data.writeCString(mime);
142         } else {
143             data.writeInt32(0);
144         }
145         remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply);
146         return reply.readInt32();
147     }
148 
getFrameAtTime(int64_t timeUs,int option,int colorFormat,bool metaOnly)149     sp<IMemory> getFrameAtTime(int64_t timeUs, int option, int colorFormat, bool metaOnly)
150     {
151         ALOGV("getTimeAtTime: time(%" PRId64 " us), option(%d), colorFormat(%d) metaOnly(%d)",
152                 timeUs, option, colorFormat, metaOnly);
153         Parcel data, reply;
154         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
155         data.writeInt64(timeUs);
156         data.writeInt32(option);
157         data.writeInt32(colorFormat);
158         data.writeInt32(metaOnly);
159 #ifndef DISABLE_GROUP_SCHEDULE_HACK
160         sendSchedPolicy(data);
161 #endif
162         remote()->transact(GET_FRAME_AT_TIME, data, &reply);
163         status_t ret = reply.readInt32();
164         if (ret != NO_ERROR) {
165             return NULL;
166         }
167         return interface_cast<IMemory>(reply.readStrongBinder());
168     }
169 
getImageAtIndex(int index,int colorFormat,bool metaOnly,bool thumbnail)170     sp<IMemory> getImageAtIndex(int index, int colorFormat, bool metaOnly, bool thumbnail)
171     {
172         ALOGV("getImageAtIndex: index %d, colorFormat(%d) metaOnly(%d) thumbnail(%d)",
173                 index, colorFormat, metaOnly, thumbnail);
174         Parcel data, reply;
175         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
176         data.writeInt32(index);
177         data.writeInt32(colorFormat);
178         data.writeInt32(metaOnly);
179         data.writeInt32(thumbnail);
180 #ifndef DISABLE_GROUP_SCHEDULE_HACK
181         sendSchedPolicy(data);
182 #endif
183         remote()->transact(GET_IMAGE_AT_INDEX, data, &reply);
184         status_t ret = reply.readInt32();
185         if (ret != NO_ERROR) {
186             return NULL;
187         }
188         return interface_cast<IMemory>(reply.readStrongBinder());
189     }
190 
getImageRectAtIndex(int index,int colorFormat,int left,int top,int right,int bottom)191     sp<IMemory> getImageRectAtIndex(
192             int index, int colorFormat, int left, int top, int right, int bottom)
193     {
194         ALOGV("getImageRectAtIndex: index %d, colorFormat(%d) rect {%d, %d, %d, %d}",
195                 index, colorFormat, left, top, right, bottom);
196         Parcel data, reply;
197         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
198         data.writeInt32(index);
199         data.writeInt32(colorFormat);
200         data.writeInt32(left);
201         data.writeInt32(top);
202         data.writeInt32(right);
203         data.writeInt32(bottom);
204 #ifndef DISABLE_GROUP_SCHEDULE_HACK
205         sendSchedPolicy(data);
206 #endif
207         remote()->transact(GET_IMAGE_RECT_AT_INDEX, data, &reply);
208         status_t ret = reply.readInt32();
209         if (ret != NO_ERROR) {
210             return NULL;
211         }
212         return interface_cast<IMemory>(reply.readStrongBinder());
213     }
214 
getFrameAtIndex(std::vector<sp<IMemory>> * frames,int frameIndex,int numFrames,int colorFormat,bool metaOnly)215     status_t getFrameAtIndex(std::vector<sp<IMemory> > *frames,
216             int frameIndex, int numFrames, int colorFormat, bool metaOnly)
217     {
218         ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d) metaOnly(%d)",
219                 frameIndex, numFrames, colorFormat, metaOnly);
220         Parcel data, reply;
221         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
222         data.writeInt32(frameIndex);
223         data.writeInt32(numFrames);
224         data.writeInt32(colorFormat);
225         data.writeInt32(metaOnly);
226 #ifndef DISABLE_GROUP_SCHEDULE_HACK
227         sendSchedPolicy(data);
228 #endif
229         remote()->transact(GET_FRAME_AT_INDEX, data, &reply);
230         status_t ret = reply.readInt32();
231         if (ret != NO_ERROR) {
232             return ret;
233         }
234         int retNumFrames = reply.readInt32();
235         if (retNumFrames < numFrames) {
236             numFrames = retNumFrames;
237         }
238         for (int i = 0; i < numFrames; i++) {
239             frames->push_back(interface_cast<IMemory>(reply.readStrongBinder()));
240         }
241         return OK;
242     }
243 
extractAlbumArt()244     sp<IMemory> extractAlbumArt()
245     {
246         Parcel data, reply;
247         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
248 #ifndef DISABLE_GROUP_SCHEDULE_HACK
249         sendSchedPolicy(data);
250 #endif
251         remote()->transact(EXTRACT_ALBUM_ART, data, &reply);
252         status_t ret = reply.readInt32();
253         if (ret != NO_ERROR) {
254             return NULL;
255         }
256         return interface_cast<IMemory>(reply.readStrongBinder());
257     }
258 
extractMetadata(int keyCode)259     const char* extractMetadata(int keyCode)
260     {
261         Parcel data, reply;
262         data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
263 #ifndef DISABLE_GROUP_SCHEDULE_HACK
264         sendSchedPolicy(data);
265 #endif
266         data.writeInt32(keyCode);
267         remote()->transact(EXTRACT_METADATA, data, &reply);
268         status_t ret = reply.readInt32();
269         if (ret != NO_ERROR) {
270             return NULL;
271         }
272         const char* str = reply.readCString();
273         if (str != NULL) {
274             String8 value(str);
275             if (mMetadata.indexOfKey(keyCode) < 0) {
276                 mMetadata.add(keyCode, value);
277             } else {
278                 mMetadata.replaceValueFor(keyCode, value);
279             }
280             return mMetadata.valueFor(keyCode).string();
281         } else {
282             return NULL;
283         }
284     }
285 
286 private:
287     KeyedVector<int, String8> mMetadata;
288 };
289 
290 IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
291 
292 // ----------------------------------------------------------------------
293 
onTransact(uint32_t code,const Parcel & data,Parcel * reply,uint32_t flags)294 status_t BnMediaMetadataRetriever::onTransact(
295     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
296 {
297     switch (code) {
298         case DISCONNECT: {
299             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
300             disconnect();
301             return NO_ERROR;
302         } break;
303         case SET_DATA_SOURCE_URL: {
304             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
305 
306             sp<IMediaHTTPService> httpService;
307             if (data.readInt32()) {
308                 httpService =
309                     interface_cast<IMediaHTTPService>(data.readStrongBinder());
310             }
311 
312             const char* srcUrl = data.readCString();
313 
314             if (httpService == NULL || srcUrl == NULL) {
315                 reply->writeInt32(BAD_VALUE);
316                 return NO_ERROR;
317             }
318 
319             KeyedVector<String8, String8> headers;
320             size_t numHeaders = (size_t) data.readInt64();
321             for (size_t i = 0; i < numHeaders; ++i) {
322                 String8 key = data.readString8();
323                 String8 value = data.readString8();
324                 headers.add(key, value);
325             }
326 
327             reply->writeInt32(
328                     setDataSource(
329                         httpService, srcUrl, numHeaders > 0 ? &headers : NULL));
330 
331             return NO_ERROR;
332         } break;
333         case SET_DATA_SOURCE_FD: {
334             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
335             int fd = data.readFileDescriptor();
336             int64_t offset = data.readInt64();
337             int64_t length = data.readInt64();
338             reply->writeInt32(setDataSource(fd, offset, length));
339             return NO_ERROR;
340         } break;
341         case SET_DATA_SOURCE_CALLBACK: {
342             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
343             sp<IDataSource> source =
344                 interface_cast<IDataSource>(data.readStrongBinder());
345             if (source == NULL) {
346                 reply->writeInt32(BAD_VALUE);
347             } else {
348                 int32_t hasMime = data.readInt32();
349                 const char *mime = NULL;
350                 if (hasMime) {
351                     mime = data.readCString();
352                 }
353                 reply->writeInt32(setDataSource(source, mime));
354             }
355             return NO_ERROR;
356         } break;
357         case GET_FRAME_AT_TIME: {
358             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
359             int64_t timeUs = data.readInt64();
360             int option = data.readInt32();
361             int colorFormat = data.readInt32();
362             bool metaOnly = (data.readInt32() != 0);
363             ALOGV("getTimeAtTime: time(%" PRId64 " us), option(%d), colorFormat(%d), metaOnly(%d)",
364                     timeUs, option, colorFormat, metaOnly);
365 #ifndef DISABLE_GROUP_SCHEDULE_HACK
366             setSchedPolicy(data);
367 #endif
368             sp<IMemory> bitmap = getFrameAtTime(timeUs, option, colorFormat, metaOnly);
369             if (bitmap != 0) {  // Don't send NULL across the binder interface
370                 reply->writeInt32(NO_ERROR);
371                 reply->writeStrongBinder(IInterface::asBinder(bitmap));
372             } else {
373                 reply->writeInt32(UNKNOWN_ERROR);
374             }
375 #ifndef DISABLE_GROUP_SCHEDULE_HACK
376             restoreSchedPolicy();
377 #endif
378             return NO_ERROR;
379         } break;
380         case GET_IMAGE_AT_INDEX: {
381             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
382             int index = data.readInt32();
383             int colorFormat = data.readInt32();
384             bool metaOnly = (data.readInt32() != 0);
385             bool thumbnail = (data.readInt32() != 0);
386             ALOGV("getImageAtIndex: index(%d), colorFormat(%d), metaOnly(%d), thumbnail(%d)",
387                     index, colorFormat, metaOnly, thumbnail);
388 #ifndef DISABLE_GROUP_SCHEDULE_HACK
389             setSchedPolicy(data);
390 #endif
391             sp<IMemory> bitmap = getImageAtIndex(index, colorFormat, metaOnly, thumbnail);
392             if (bitmap != 0) {  // Don't send NULL across the binder interface
393                 reply->writeInt32(NO_ERROR);
394                 reply->writeStrongBinder(IInterface::asBinder(bitmap));
395             } else {
396                 reply->writeInt32(UNKNOWN_ERROR);
397             }
398 #ifndef DISABLE_GROUP_SCHEDULE_HACK
399             restoreSchedPolicy();
400 #endif
401             return NO_ERROR;
402         } break;
403 
404         case GET_IMAGE_RECT_AT_INDEX: {
405             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
406             int index = data.readInt32();
407             int colorFormat = data.readInt32();
408             int left = data.readInt32();
409             int top = data.readInt32();
410             int right = data.readInt32();
411             int bottom = data.readInt32();
412             ALOGV("getImageRectAtIndex: index(%d), colorFormat(%d), rect {%d, %d, %d, %d}",
413                     index, colorFormat, left, top, right, bottom);
414 #ifndef DISABLE_GROUP_SCHEDULE_HACK
415             setSchedPolicy(data);
416 #endif
417             sp<IMemory> bitmap = getImageRectAtIndex(
418                     index, colorFormat, left, top, right, bottom);
419             if (bitmap != 0) {  // Don't send NULL across the binder interface
420                 reply->writeInt32(NO_ERROR);
421                 reply->writeStrongBinder(IInterface::asBinder(bitmap));
422             } else {
423                 reply->writeInt32(UNKNOWN_ERROR);
424             }
425 #ifndef DISABLE_GROUP_SCHEDULE_HACK
426             restoreSchedPolicy();
427 #endif
428             return NO_ERROR;
429         } break;
430 
431         case GET_FRAME_AT_INDEX: {
432             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
433             int frameIndex = data.readInt32();
434             int numFrames = data.readInt32();
435             int colorFormat = data.readInt32();
436             bool metaOnly = (data.readInt32() != 0);
437             ALOGV("getFrameAtIndex: frameIndex(%d), numFrames(%d), colorFormat(%d), metaOnly(%d)",
438                     frameIndex, numFrames, colorFormat, metaOnly);
439 #ifndef DISABLE_GROUP_SCHEDULE_HACK
440             setSchedPolicy(data);
441 #endif
442             std::vector<sp<IMemory> > frames;
443             status_t err = getFrameAtIndex(
444                     &frames, frameIndex, numFrames, colorFormat, metaOnly);
445             reply->writeInt32(err);
446             if (OK == err) {
447                 reply->writeInt32(frames.size());
448                 for (size_t i = 0; i < frames.size(); i++) {
449                     reply->writeStrongBinder(IInterface::asBinder(frames[i]));
450                 }
451             }
452 #ifndef DISABLE_GROUP_SCHEDULE_HACK
453             restoreSchedPolicy();
454 #endif
455             return NO_ERROR;
456         } break;
457         case EXTRACT_ALBUM_ART: {
458             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
459 #ifndef DISABLE_GROUP_SCHEDULE_HACK
460             setSchedPolicy(data);
461 #endif
462             sp<IMemory> albumArt = extractAlbumArt();
463             if (albumArt != 0) {  // Don't send NULL across the binder interface
464                 reply->writeInt32(NO_ERROR);
465                 reply->writeStrongBinder(IInterface::asBinder(albumArt));
466             } else {
467                 reply->writeInt32(UNKNOWN_ERROR);
468             }
469 #ifndef DISABLE_GROUP_SCHEDULE_HACK
470             restoreSchedPolicy();
471 #endif
472             return NO_ERROR;
473         } break;
474         case EXTRACT_METADATA: {
475             CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
476 #ifndef DISABLE_GROUP_SCHEDULE_HACK
477             setSchedPolicy(data);
478 #endif
479             int keyCode = data.readInt32();
480             const char* value = extractMetadata(keyCode);
481             if (value != NULL) {  // Don't send NULL across the binder interface
482                 reply->writeInt32(NO_ERROR);
483                 reply->writeCString(value);
484             } else {
485                 reply->writeInt32(UNKNOWN_ERROR);
486             }
487 #ifndef DISABLE_GROUP_SCHEDULE_HACK
488             restoreSchedPolicy();
489 #endif
490             return NO_ERROR;
491         } break;
492         default:
493             return BBinder::onTransact(code, data, reply, flags);
494     }
495 }
496 
497 // ----------------------------------------------------------------------------
498 
499 } // namespace android
500