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 42namespace android { 43 44static void sendSchedPolicy(Parcel& data) 45{ 46 SchedPolicy policy; 47 get_sched_policy(gettid(), &policy); 48 data.writeInt32(policy); 49} 50 51static void setSchedPolicy(const Parcel& data) 52{ 53 SchedPolicy policy = (SchedPolicy) data.readInt32(); 54 set_sched_policy(gettid(), policy); 55} 56static void restoreSchedPolicy() 57{ 58 set_sched_policy(gettid(), SP_FOREGROUND); 59} 60}; // end namespace android 61#endif 62 63namespace android { 64 65enum { 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 75class BpMediaMetadataRetriever: public BpInterface<IMediaMetadataRetriever> 76{ 77public: 78 BpMediaMetadataRetriever(const sp<IBinder>& impl) 79 : BpInterface<IMediaMetadataRetriever>(impl) 80 { 81 } 82 83 // disconnect from media metadata retriever service 84 void disconnect() 85 { 86 Parcel data, reply; 87 data.writeInterfaceToken(IMediaMetadataRetriever::getInterfaceDescriptor()); 88 remote()->transact(DISCONNECT, data, &reply); 89 } 90 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 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 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 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 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 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 199private: 200 KeyedVector<int, String8> mMetadata; 201}; 202 203IMPLEMENT_META_INTERFACE(MediaMetadataRetriever, "android.media.IMediaMetadataRetriever"); 204 205// ---------------------------------------------------------------------- 206 207status_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