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
42namespace android {
43
44static void sendSchedPolicy(Parcel& data)
45{
46    SchedPolicy policy;
47    get_sched_policy(gettid(), &policy);
48    data.writeInt32(policy);
49}
50
51static void setSchedPolicy(const Parcel& data)
52{
53    SchedPolicy policy = (SchedPolicy) data.readInt32();
54    set_sched_policy(gettid(), policy);
55}
56static void restoreSchedPolicy()
57{
58    set_sched_policy(gettid(), SP_FOREGROUND);
59}
60}; // end namespace android
61#endif
62
63namespace android {
64
65enum {
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
78class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
79{
80public:
81    explicit BpMediaMetadataRetriever(const sp<IBinder>& impl)
82        : BpInterface<IMediaMetadataRetriever>(impl)
83    {
84    }
85
86    // disconnect from media metadata retriever service
87    void disconnect()
88    {
89        Parcel data, reply;
90        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
91        remote()->transact(DISCONNECT, data, &reply);
92    }
93
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
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
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
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
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
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
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
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
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
286private:
287    KeyedVector<int, String8> mMetadata;
288};
289
290IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
291
292// ----------------------------------------------------------------------
293
294status_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