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