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