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 EXTRACT_ALBUM_ART,
72 EXTRACT_METADATA,
73 };
74
75 class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever>
76 {
77 public:
BpMediaMetadataRetriever(const sp<IBinder> & impl)78 explicit BpMediaMetadataRetriever(const sp<IBinder>& impl)
79 : BpInterface<IMediaMetadataRetriever>(impl)
80 {
81 }
82
83 // disconnect from media metadata retriever service
disconnect()84 void disconnect()
85 {
86 Parcel data, reply;
87 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor());
88 remote()->transact(DISCONNECT, data, &reply);
89 }
90
setDataSource(const sp<IMediaHTTPService> & httpService,const char * srcUrl,const KeyedVector<String8,String8> * headers)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
setDataSource(int fd,int64_t offset,int64_t length)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
setDataSource(const sp<IDataSource> & source)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
getFrameAtTime(int64_t timeUs,int option)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
extractAlbumArt()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
extractMetadata(int keyCode)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
199 private:
200 KeyedVector<int, String8> mMetadata;
201 };
202
203 IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever");
204
205 // ----------------------------------------------------------------------
206
onTransact(uint32_t code,const Parcel & data,Parcel * reply,uint32_t flags)207 status_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