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