android_media_MediaScanner.cpp revision 997354e4b4a9666cedd62282471e97822affced9
1/* 2** 3** Copyright 2007, 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//#define LOG_NDEBUG 0 19#define LOG_TAG "MediaScannerJNI" 20#include <utils/Log.h> 21#include <utils/threads.h> 22#include <media/mediascanner.h> 23#include <media/stagefright/StagefrightMediaScanner.h> 24 25#include "jni.h" 26#include "JNIHelp.h" 27#include "android_runtime/AndroidRuntime.h" 28 29using namespace android; 30 31 32static const char* const kClassMediaScannerClient = 33 "android/media/MediaScannerClient"; 34 35static const char* const kClassMediaScanner = 36 "android/media/MediaScanner"; 37 38static const char* const kRunTimeException = 39 "java/lang/RuntimeException"; 40 41static const char* const kIllegalArgumentException = 42 "java/lang/IllegalArgumentException"; 43 44struct fields_t { 45 jfieldID context; 46}; 47static fields_t fields; 48static Mutex sLock; 49 50class MyMediaScannerClient : public MediaScannerClient 51{ 52public: 53 MyMediaScannerClient(JNIEnv *env, jobject client) 54 : mEnv(env), 55 mClient(env->NewGlobalRef(client)), 56 mScanFileMethodID(0), 57 mHandleStringTagMethodID(0), 58 mSetMimeTypeMethodID(0) 59 { 60 LOGV("MyMediaScannerClient constructor"); 61 jclass mediaScannerClientInterface = 62 env->FindClass(kClassMediaScannerClient); 63 64 if (mediaScannerClientInterface == NULL) { 65 LOGE("Class %s not found", kClassMediaScannerClient); 66 } else { 67 mScanFileMethodID = env->GetMethodID( 68 mediaScannerClientInterface, 69 "scanFile", 70 "(Ljava/lang/String;JJZZ)V"); 71 72 mHandleStringTagMethodID = env->GetMethodID( 73 mediaScannerClientInterface, 74 "handleStringTag", 75 "(Ljava/lang/String;Ljava/lang/String;)V"); 76 77 mSetMimeTypeMethodID = env->GetMethodID( 78 mediaScannerClientInterface, 79 "setMimeType", 80 "(Ljava/lang/String;)V"); 81 } 82 } 83 84 virtual ~MyMediaScannerClient() 85 { 86 LOGV("MyMediaScannerClient destructor"); 87 mEnv->DeleteGlobalRef(mClient); 88 } 89 90 // Returns true if it succeeded, false if an exception occured 91 // in the Java code 92 virtual bool scanFile(const char* path, long long lastModified, 93 long long fileSize, bool isDirectory, bool noMedia) 94 { 95 LOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)", 96 path, lastModified, fileSize, isDirectory); 97 98 jstring pathStr; 99 if ((pathStr = mEnv->NewStringUTF(path)) == NULL) { 100 return false; 101 } 102 103 mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, 104 fileSize, isDirectory, noMedia); 105 106 mEnv->DeleteLocalRef(pathStr); 107 return (!mEnv->ExceptionCheck()); 108 } 109 110 // Returns true if it succeeded, false if an exception occured 111 // in the Java code 112 virtual bool handleStringTag(const char* name, const char* value) 113 { 114 LOGV("handleStringTag: name(%s) and value(%s)", name, value); 115 jstring nameStr, valueStr; 116 if ((nameStr = mEnv->NewStringUTF(name)) == NULL) { 117 return false; 118 } 119 if ((valueStr = mEnv->NewStringUTF(value)) == NULL) { 120 return false; 121 } 122 123 mEnv->CallVoidMethod( 124 mClient, mHandleStringTagMethodID, nameStr, valueStr); 125 126 mEnv->DeleteLocalRef(nameStr); 127 mEnv->DeleteLocalRef(valueStr); 128 return (!mEnv->ExceptionCheck()); 129 } 130 131 // Returns true if it succeeded, false if an exception occured 132 // in the Java code 133 virtual bool setMimeType(const char* mimeType) 134 { 135 LOGV("setMimeType: %s", mimeType); 136 jstring mimeTypeStr; 137 if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) { 138 return false; 139 } 140 141 mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr); 142 143 mEnv->DeleteLocalRef(mimeTypeStr); 144 return (!mEnv->ExceptionCheck()); 145 } 146 147private: 148 JNIEnv *mEnv; 149 jobject mClient; 150 jmethodID mScanFileMethodID; 151 jmethodID mHandleStringTagMethodID; 152 jmethodID mSetMimeTypeMethodID; 153}; 154 155 156static bool ExceptionCheck(void* env) 157{ 158 LOGV("ExceptionCheck"); 159 return ((JNIEnv *)env)->ExceptionCheck(); 160} 161 162// Call this method with sLock hold 163static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz) 164{ 165 return (MediaScanner *) env->GetIntField(thiz, fields.context); 166} 167 168// Call this method with sLock hold 169static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s) 170{ 171 env->SetIntField(thiz, fields.context, (int)s); 172} 173 174static void 175android_media_MediaScanner_processDirectory( 176 JNIEnv *env, jobject thiz, jstring path, jobject client) 177{ 178 LOGV("processDirectory"); 179 Mutex::Autolock l(sLock); 180 MediaScanner *mp = getNativeScanner_l(env, thiz); 181 if (mp == NULL) { 182 jniThrowException(env, kRunTimeException, "No scanner available"); 183 return; 184 } 185 186 if (path == NULL) { 187 jniThrowException(env, kIllegalArgumentException, NULL); 188 return; 189 } 190 191 const char *pathStr = env->GetStringUTFChars(path, NULL); 192 if (pathStr == NULL) { // Out of memory 193 return; 194 } 195 196 MyMediaScannerClient myClient(env, client); 197 mp->processDirectory(pathStr, myClient, ExceptionCheck, env); 198 env->ReleaseStringUTFChars(path, pathStr); 199} 200 201static void 202android_media_MediaScanner_processFile( 203 JNIEnv *env, jobject thiz, jstring path, 204 jstring mimeType, jobject client) 205{ 206 LOGV("processFile"); 207 208 // Lock already hold by processDirectory 209 MediaScanner *mp = getNativeScanner_l(env, thiz); 210 if (mp == NULL) { 211 jniThrowException(env, kRunTimeException, "No scanner available"); 212 return; 213 } 214 215 if (path == NULL) { 216 jniThrowException(env, kIllegalArgumentException, NULL); 217 return; 218 } 219 220 const char *pathStr = env->GetStringUTFChars(path, NULL); 221 if (pathStr == NULL) { // Out of memory 222 return; 223 } 224 225 const char *mimeTypeStr = 226 (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL); 227 if (mimeType && mimeTypeStr == NULL) { // Out of memory 228 // ReleaseStringUTFChars can be called with an exception pending. 229 env->ReleaseStringUTFChars(path, pathStr); 230 return; 231 } 232 233 MyMediaScannerClient myClient(env, client); 234 mp->processFile(pathStr, mimeTypeStr, myClient); 235 env->ReleaseStringUTFChars(path, pathStr); 236 if (mimeType) { 237 env->ReleaseStringUTFChars(mimeType, mimeTypeStr); 238 } 239} 240 241static void 242android_media_MediaScanner_setLocale( 243 JNIEnv *env, jobject thiz, jstring locale) 244{ 245 LOGV("setLocale"); 246 Mutex::Autolock l(sLock); 247 MediaScanner *mp = getNativeScanner_l(env, thiz); 248 if (mp == NULL) { 249 jniThrowException(env, kRunTimeException, "No scanner available"); 250 return; 251 } 252 253 if (locale == NULL) { 254 jniThrowException(env, kIllegalArgumentException, NULL); 255 return; 256 } 257 const char *localeStr = env->GetStringUTFChars(locale, NULL); 258 if (localeStr == NULL) { // Out of memory 259 return; 260 } 261 mp->setLocale(localeStr); 262 263 env->ReleaseStringUTFChars(locale, localeStr); 264} 265 266static jbyteArray 267android_media_MediaScanner_extractAlbumArt( 268 JNIEnv *env, jobject thiz, jobject fileDescriptor) 269{ 270 LOGV("extractAlbumArt"); 271 Mutex::Autolock l(sLock); 272 MediaScanner *mp = getNativeScanner_l(env, thiz); 273 if (mp == NULL) { 274 jniThrowException(env, kRunTimeException, "No scanner available"); 275 return NULL; 276 } 277 278 if (fileDescriptor == NULL) { 279 jniThrowException(env, kIllegalArgumentException, NULL); 280 return NULL; 281 } 282 283 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); 284 char* data = mp->extractAlbumArt(fd); 285 if (!data) { 286 return NULL; 287 } 288 long len = *((long*)data); 289 290 jbyteArray array = env->NewByteArray(len); 291 if (array != NULL) { 292 jbyte* bytes = env->GetByteArrayElements(array, NULL); 293 memcpy(bytes, data + 4, len); 294 env->ReleaseByteArrayElements(array, bytes, 0); 295 } 296 297done: 298 free(data); 299 // if NewByteArray() returned NULL, an out-of-memory 300 // exception will have been raised. I just want to 301 // return null in that case. 302 env->ExceptionClear(); 303 return array; 304} 305 306// This function gets a field ID, which in turn causes class initialization. 307// It is called from a static block in MediaScanner, which won't run until the 308// first time an instance of this class is used. 309static void 310android_media_MediaScanner_native_init(JNIEnv *env) 311{ 312 LOGV("native_init"); 313 jclass clazz = env->FindClass(kClassMediaScanner); 314 if (clazz == NULL) { 315 return; 316 } 317 318 fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 319 if (fields.context == NULL) { 320 return; 321 } 322} 323 324static void 325android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) 326{ 327 LOGV("native_setup"); 328 MediaScanner *mp = new StagefrightMediaScanner; 329 330 if (mp == NULL) { 331 jniThrowException(env, kRunTimeException, "Out of memory"); 332 return; 333 } 334 335 env->SetIntField(thiz, fields.context, (int)mp); 336} 337 338static void 339android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz) 340{ 341 LOGV("native_finalize"); 342 Mutex::Autolock l(sLock); 343 MediaScanner *mp = getNativeScanner_l(env, thiz); 344 if (mp == 0) { 345 return; 346 } 347 delete mp; 348 setNativeScanner_l(env, thiz, 0); 349} 350 351static JNINativeMethod gMethods[] = { 352 { 353 "processDirectory", 354 "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 355 (void *)android_media_MediaScanner_processDirectory 356 }, 357 358 { 359 "processFile", 360 "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 361 (void *)android_media_MediaScanner_processFile 362 }, 363 364 { 365 "setLocale", 366 "(Ljava/lang/String;)V", 367 (void *)android_media_MediaScanner_setLocale 368 }, 369 370 { 371 "extractAlbumArt", 372 "(Ljava/io/FileDescriptor;)[B", 373 (void *)android_media_MediaScanner_extractAlbumArt 374 }, 375 376 { 377 "native_init", 378 "()V", 379 (void *)android_media_MediaScanner_native_init 380 }, 381 382 { 383 "native_setup", 384 "()V", 385 (void *)android_media_MediaScanner_native_setup 386 }, 387 388 { 389 "native_finalize", 390 "()V", 391 (void *)android_media_MediaScanner_native_finalize 392 }, 393}; 394 395// This function only registers the native methods, and is called from 396// JNI_OnLoad in android_media_MediaPlayer.cpp 397int register_android_media_MediaScanner(JNIEnv *env) 398{ 399 return AndroidRuntime::registerNativeMethods(env, 400 kClassMediaScanner, gMethods, NELEM(gMethods)); 401} 402