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