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    EXTRACT_ALBUM_ART,
72    EXTRACT_METADATA,
73};
74
75class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
76{
77public:
78    explicit BpMediaMetadataRetriever(const sp<IBinder>& impl)
79        : BpInterface<IMediaMetadataRetriever>(impl)
80    {
81    }
82
83    // disconnect from media metadata retriever service
84    void disconnect()
85    {
86        Parcel data, reply;
87        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
88        remote()->transact(DISCONNECT, data, &reply);
89    }
90
91    status_t setDataSource(
92            const sp<IMediaHTTPService> &httpService,
93            const char *srcUrl,
94            const KeyedVector<String8, String8> *headers)
95    {
96        Parcel data, reply;
97        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
98        data.writeInt32(httpService != NULL);
99        if (httpService != NULL) {
100            data.writeStrongBinder(IInterface::asBinder(httpService));
101        }
102        data.writeCString(srcUrl);
103
104        if (headers == NULL) {
105            data.writeInt32(0);
106        } else {
107            // serialize the headers
108            data.writeInt64(headers->size());
109            for (size_t i = 0; i < headers->size(); ++i) {
110                data.writeString8(headers->keyAt(i));
111                data.writeString8(headers->valueAt(i));
112            }
113        }
114
115        remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
116        return reply.readInt32();
117    }
118
119    status_t setDataSource(int fd, int64_t offset, int64_t length)
120    {
121        Parcel data, reply;
122        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
123        data.writeFileDescriptor(fd);
124        data.writeInt64(offset);
125        data.writeInt64(length);
126        remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
127        return reply.readInt32();
128    }
129
130    status_t setDataSource(const sp<IDataSource>& source)
131    {
132        Parcel data, reply;
133        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
134        data.writeStrongBinder(IInterface::asBinder(source));
135        remote()->transact(SET_DATA_SOURCE_CALLBACK, data, &reply);
136        return reply.readInt32();
137    }
138
139    sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
140    {
141        ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
142        Parcel data, reply;
143        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
144        data.writeInt64(timeUs);
145        data.writeInt32(option);
146#ifndef DISABLE_GROUP_SCHEDULE_HACK
147        sendSchedPolicy(data);
148#endif
149        remote()->transact(GET_FRAME_AT_TIME, data, &reply);
150        status_t ret = reply.readInt32();
151        if (ret != NO_ERROR) {
152            return NULL;
153        }
154        return interface_cast<IMemory>(reply.readStrongBinder());
155    }
156
157    sp<IMemory> extractAlbumArt()
158    {
159        Parcel data, reply;
160        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
161#ifndef DISABLE_GROUP_SCHEDULE_HACK
162        sendSchedPolicy(data);
163#endif
164        remote()->transact(EXTRACT_ALBUM_ART, data, &reply);
165        status_t ret = reply.readInt32();
166        if (ret != NO_ERROR) {
167            return NULL;
168        }
169        return interface_cast<IMemory>(reply.readStrongBinder());
170    }
171
172    const char* extractMetadata(int keyCode)
173    {
174        Parcel data, reply;
175        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
176#ifndef DISABLE_GROUP_SCHEDULE_HACK
177        sendSchedPolicy(data);
178#endif
179        data.writeInt32(keyCode);
180        remote()->transact(EXTRACT_METADATA, data, &reply);
181        status_t ret = reply.readInt32();
182        if (ret != NO_ERROR) {
183            return NULL;
184        }
185        const char* str = reply.readCString();
186        if (str != NULL) {
187            String8 value(str);
188            if (mMetadata.indexOfKey(keyCode) < 0) {
189                mMetadata.add(keyCode, value);
190            } else {
191                mMetadata.replaceValueFor(keyCode, value);
192            }
193            return mMetadata.valueFor(keyCode).string();
194        } else {
195            return NULL;
196        }
197    }
198
199private:
200    KeyedVector<int, String8> mMetadata;
201};
202
203IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
204
205// ----------------------------------------------------------------------
206
207status_t BnMediaMetadataRetriever::onTransact(
208    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
209{
210    switch (code) {
211        case DISCONNECT: {
212            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
213            disconnect();
214            return NO_ERROR;
215        } break;
216        case SET_DATA_SOURCE_URL: {
217            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
218
219            sp<IMediaHTTPService> httpService;
220            if (data.readInt32()) {
221                httpService =
222                    interface_cast<IMediaHTTPService>(data.readStrongBinder());
223            }
224
225            const char* srcUrl = data.readCString();
226
227            if (httpService == NULL || srcUrl == NULL) {
228                reply->writeInt32(BAD_VALUE);
229                return NO_ERROR;
230            }
231
232            KeyedVector<String8, String8> headers;
233            size_t numHeaders = (size_t) data.readInt64();
234            for (size_t i = 0; i < numHeaders; ++i) {
235                String8 key = data.readString8();
236                String8 value = data.readString8();
237                headers.add(key, value);
238            }
239
240            reply->writeInt32(
241                    setDataSource(
242                        httpService, srcUrl, numHeaders > 0 ? &headers : NULL));
243
244            return NO_ERROR;
245        } break;
246        case SET_DATA_SOURCE_FD: {
247            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
248            int fd = data.readFileDescriptor();
249            int64_t offset = data.readInt64();
250            int64_t length = data.readInt64();
251            reply->writeInt32(setDataSource(fd, offset, length));
252            return NO_ERROR;
253        } break;
254        case SET_DATA_SOURCE_CALLBACK: {
255            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
256            sp<IDataSource> source =
257                interface_cast<IDataSource>(data.readStrongBinder());
258            if (source == NULL) {
259                reply->writeInt32(BAD_VALUE);
260            } else {
261                reply->writeInt32(setDataSource(source));
262            }
263            return NO_ERROR;
264        } break;
265        case GET_FRAME_AT_TIME: {
266            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
267            int64_t timeUs = data.readInt64();
268            int option = data.readInt32();
269            ALOGV("getTimeAtTime: time(%" PRId64 " us) and option(%d)", timeUs, option);
270#ifndef DISABLE_GROUP_SCHEDULE_HACK
271            setSchedPolicy(data);
272#endif
273            sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
274            if (bitmap != 0) {  // Don't send NULL across the binder interface
275                reply->writeInt32(NO_ERROR);
276                reply->writeStrongBinder(IInterface::asBinder(bitmap));
277            } else {
278                reply->writeInt32(UNKNOWN_ERROR);
279            }
280#ifndef DISABLE_GROUP_SCHEDULE_HACK
281            restoreSchedPolicy();
282#endif
283            return NO_ERROR;
284        } break;
285        case EXTRACT_ALBUM_ART: {
286            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
287#ifndef DISABLE_GROUP_SCHEDULE_HACK
288            setSchedPolicy(data);
289#endif
290            sp<IMemory> albumArt = extractAlbumArt();
291            if (albumArt != 0) {  // Don't send NULL across the binder interface
292                reply->writeInt32(NO_ERROR);
293                reply->writeStrongBinder(IInterface::asBinder(albumArt));
294            } else {
295                reply->writeInt32(UNKNOWN_ERROR);
296            }
297#ifndef DISABLE_GROUP_SCHEDULE_HACK
298            restoreSchedPolicy();
299#endif
300            return NO_ERROR;
301        } break;
302        case EXTRACT_METADATA: {
303            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
304#ifndef DISABLE_GROUP_SCHEDULE_HACK
305            setSchedPolicy(data);
306#endif
307            int keyCode = data.readInt32();
308            const char* value = extractMetadata(keyCode);
309            if (value != NULL) {  // Don't send NULL across the binder interface
310                reply->writeInt32(NO_ERROR);
311                reply->writeCString(value);
312            } else {
313                reply->writeInt32(UNKNOWN_ERROR);
314            }
315#ifndef DISABLE_GROUP_SCHEDULE_HACK
316            restoreSchedPolicy();
317#endif
318            return NO_ERROR;
319        } break;
320        default:
321            return BBinder::onTransact(code, data, reply, flags);
322    }
323}
324
325// ----------------------------------------------------------------------------
326
327} // namespace android
328