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 <stdint.h>
19#include <sys/types.h>
20#include <binder/Parcel.h>
21#include <media/IMediaMetadataRetriever.h>
22#include <utils/String8.h>
23
24// The binder is supposed to propagate the scheduler group across
25// the binder interface so that remote calls are executed with
26// the same priority as local calls. This is currently not working
27// so this change puts in a temporary hack to fix the issue with
28// metadata retrieval which can be a huge CPU hit if done on a
29// foreground thread.
30#ifndef DISABLE_GROUP_SCHEDULE_HACK
31
32#undef LOG_TAG
33#define LOG_TAG "IMediaMetadataRetriever"
34#include <utils/Log.h>
35#include <cutils/sched_policy.h>
36
37namespace android {
38
39static void sendSchedPolicy(Parcel& data)
40{
41    SchedPolicy policy;
42    get_sched_policy(gettid(), &policy);
43    data.writeInt32(policy);
44}
45
46static void setSchedPolicy(const Parcel& data)
47{
48    SchedPolicy policy = (SchedPolicy) data.readInt32();
49    set_sched_policy(gettid(), policy);
50}
51static void restoreSchedPolicy()
52{
53    set_sched_policy(gettid(), SP_FOREGROUND);
54}
55}; // end namespace android
56#endif
57
58namespace android {
59
60enum {
61    DISCONNECT = IBinder::FIRST_CALL_TRANSACTION,
62    SET_DATA_SOURCE_URL,
63    SET_DATA_SOURCE_FD,
64    GET_FRAME_AT_TIME,
65    EXTRACT_ALBUM_ART,
66    EXTRACT_METADATA,
67};
68
69class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
70{
71public:
72    BpMediaMetadataRetriever(const sp<IBinder>& impl)
73        : BpInterface<IMediaMetadataRetriever>(impl)
74    {
75    }
76
77    // disconnect from media metadata retriever service
78    void disconnect()
79    {
80        Parcel data, reply;
81        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
82        remote()->transact(DISCONNECT, data, &reply);
83    }
84
85    status_t setDataSource(
86            const char *srcUrl, const KeyedVector<String8, String8> *headers)
87    {
88        Parcel data, reply;
89        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
90        data.writeCString(srcUrl);
91
92        if (headers == NULL) {
93            data.writeInt32(0);
94        } else {
95            // serialize the headers
96            data.writeInt32(headers->size());
97            for (size_t i = 0; i < headers->size(); ++i) {
98                data.writeString8(headers->keyAt(i));
99                data.writeString8(headers->valueAt(i));
100            }
101        }
102
103        remote()->transact(SET_DATA_SOURCE_URL, data, &reply);
104        return reply.readInt32();
105    }
106
107    status_t setDataSource(int fd, int64_t offset, int64_t length)
108    {
109        Parcel data, reply;
110        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
111        data.writeFileDescriptor(fd);
112        data.writeInt64(offset);
113        data.writeInt64(length);
114        remote()->transact(SET_DATA_SOURCE_FD, data, &reply);
115        return reply.readInt32();
116    }
117
118    sp<IMemory> getFrameAtTime(int64_t timeUs, int option)
119    {
120        ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
121        Parcel data, reply;
122        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
123        data.writeInt64(timeUs);
124        data.writeInt32(option);
125#ifndef DISABLE_GROUP_SCHEDULE_HACK
126        sendSchedPolicy(data);
127#endif
128        remote()->transact(GET_FRAME_AT_TIME, data, &reply);
129        status_t ret = reply.readInt32();
130        if (ret != NO_ERROR) {
131            return NULL;
132        }
133        return interface_cast<IMemory>(reply.readStrongBinder());
134    }
135
136    sp<IMemory> extractAlbumArt()
137    {
138        Parcel data, reply;
139        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
140#ifndef DISABLE_GROUP_SCHEDULE_HACK
141        sendSchedPolicy(data);
142#endif
143        remote()->transact(EXTRACT_ALBUM_ART, data, &reply);
144        status_t ret = reply.readInt32();
145        if (ret != NO_ERROR) {
146            return NULL;
147        }
148        return interface_cast<IMemory>(reply.readStrongBinder());
149    }
150
151    const char* extractMetadata(int keyCode)
152    {
153        Parcel data, reply;
154        data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
155#ifndef DISABLE_GROUP_SCHEDULE_HACK
156        sendSchedPolicy(data);
157#endif
158        data.writeInt32(keyCode);
159        remote()->transact(EXTRACT_METADATA, data, &reply);
160        status_t ret = reply.readInt32();
161        if (ret != NO_ERROR) {
162            return NULL;
163        }
164        return reply.readCString();
165    }
166};
167
168IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
169
170// ----------------------------------------------------------------------
171
172status_t BnMediaMetadataRetriever::onTransact(
173    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
174{
175    switch (code) {
176        case DISCONNECT: {
177            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
178            disconnect();
179            return NO_ERROR;
180        } break;
181        case SET_DATA_SOURCE_URL: {
182            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
183            const char* srcUrl = data.readCString();
184
185            KeyedVector<String8, String8> headers;
186            int32_t numHeaders = data.readInt32();
187            for (int i = 0; i < numHeaders; ++i) {
188                String8 key = data.readString8();
189                String8 value = data.readString8();
190                headers.add(key, value);
191            }
192
193            reply->writeInt32(
194                    setDataSource(srcUrl, numHeaders > 0 ? &headers : NULL));
195
196            return NO_ERROR;
197        } break;
198        case SET_DATA_SOURCE_FD: {
199            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
200            int fd = dup(data.readFileDescriptor());
201            int64_t offset = data.readInt64();
202            int64_t length = data.readInt64();
203            reply->writeInt32(setDataSource(fd, offset, length));
204            return NO_ERROR;
205        } break;
206        case GET_FRAME_AT_TIME: {
207            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
208            int64_t timeUs = data.readInt64();
209            int option = data.readInt32();
210            ALOGV("getTimeAtTime: time(%lld us) and option(%d)", timeUs, option);
211#ifndef DISABLE_GROUP_SCHEDULE_HACK
212            setSchedPolicy(data);
213#endif
214            sp<IMemory> bitmap = getFrameAtTime(timeUs, option);
215            if (bitmap != 0) {  // Don't send NULL across the binder interface
216                reply->writeInt32(NO_ERROR);
217                reply->writeStrongBinder(bitmap->asBinder());
218            } else {
219                reply->writeInt32(UNKNOWN_ERROR);
220            }
221#ifndef DISABLE_GROUP_SCHEDULE_HACK
222            restoreSchedPolicy();
223#endif
224            return NO_ERROR;
225        } break;
226        case EXTRACT_ALBUM_ART: {
227            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
228#ifndef DISABLE_GROUP_SCHEDULE_HACK
229            setSchedPolicy(data);
230#endif
231            sp<IMemory> albumArt = extractAlbumArt();
232            if (albumArt != 0) {  // Don't send NULL across the binder interface
233                reply->writeInt32(NO_ERROR);
234                reply->writeStrongBinder(albumArt->asBinder());
235            } else {
236                reply->writeInt32(UNKNOWN_ERROR);
237            }
238#ifndef DISABLE_GROUP_SCHEDULE_HACK
239            restoreSchedPolicy();
240#endif
241            return NO_ERROR;
242        } break;
243        case EXTRACT_METADATA: {
244            CHECK_INTERFACE(IMediaMetadataRetriever, data, reply);
245#ifndef DISABLE_GROUP_SCHEDULE_HACK
246            setSchedPolicy(data);
247#endif
248            int keyCode = data.readInt32();
249            const char* value = extractMetadata(keyCode);
250            if (value != NULL) {  // Don't send NULL across the binder interface
251                reply->writeInt32(NO_ERROR);
252                reply->writeCString(value);
253            } else {
254                reply->writeInt32(UNKNOWN_ERROR);
255            }
256#ifndef DISABLE_GROUP_SCHEDULE_HACK
257            restoreSchedPolicy();
258#endif
259            return NO_ERROR;
260        } break;
261        default:
262            return BBinder::onTransact(code, data, reply, flags);
263    }
264}
265
266// ----------------------------------------------------------------------------
267
268}; // namespace android
269