• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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