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