android_media_MediaScanner.cpp revision c371a02e7cf504e9a926ca29c33e63b658c2cef7
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;JJZ)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 mAddNoMediaFolderMethodID = env->GetMethodID( 83 mediaScannerClientInterface, 84 "addNoMediaFolder", 85 "(Ljava/lang/String;)V"); 86 } 87 } 88 89 virtual ~MyMediaScannerClient() 90 { 91 LOGV("MyMediaScannerClient destructor"); 92 mEnv->DeleteGlobalRef(mClient); 93 } 94 95 // Returns true if it succeeded, false if an exception occured 96 // in the Java code 97 virtual bool scanFile(const char* path, long long lastModified, 98 long long fileSize, bool isDirectory) 99 { 100 LOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)", 101 path, lastModified, fileSize, isDirectory); 102 103 jstring pathStr; 104 if ((pathStr = mEnv->NewStringUTF(path)) == NULL) { 105 return false; 106 } 107 108 mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, 109 fileSize, isDirectory); 110 111 mEnv->DeleteLocalRef(pathStr); 112 return (!mEnv->ExceptionCheck()); 113 } 114 115 // Returns true if it succeeded, false if an exception occured 116 // in the Java code 117 virtual bool handleStringTag(const char* name, const char* value) 118 { 119 LOGV("handleStringTag: name(%s) and value(%s)", name, value); 120 jstring nameStr, valueStr; 121 if ((nameStr = mEnv->NewStringUTF(name)) == NULL) { 122 return false; 123 } 124 if ((valueStr = mEnv->NewStringUTF(value)) == NULL) { 125 return false; 126 } 127 128 mEnv->CallVoidMethod( 129 mClient, mHandleStringTagMethodID, nameStr, valueStr); 130 131 mEnv->DeleteLocalRef(nameStr); 132 mEnv->DeleteLocalRef(valueStr); 133 return (!mEnv->ExceptionCheck()); 134 } 135 136 // Returns true if it succeeded, false if an exception occured 137 // in the Java code 138 virtual bool setMimeType(const char* mimeType) 139 { 140 LOGV("setMimeType: %s", mimeType); 141 jstring mimeTypeStr; 142 if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) { 143 return false; 144 } 145 146 mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr); 147 148 mEnv->DeleteLocalRef(mimeTypeStr); 149 return (!mEnv->ExceptionCheck()); 150 } 151 152 // Returns true if it succeeded, false if an exception occured 153 // in the Java code 154 virtual bool addNoMediaFolder(const char* path) 155 { 156 LOGV("addNoMediaFolder: path(%s)", path); 157 jstring pathStr; 158 if ((pathStr = mEnv->NewStringUTF(path)) == NULL) { 159 return false; 160 } 161 162 mEnv->CallVoidMethod(mClient, mAddNoMediaFolderMethodID, pathStr); 163 164 mEnv->DeleteLocalRef(pathStr); 165 return (!mEnv->ExceptionCheck()); 166 } 167 168 169private: 170 JNIEnv *mEnv; 171 jobject mClient; 172 jmethodID mScanFileMethodID; 173 jmethodID mHandleStringTagMethodID; 174 jmethodID mSetMimeTypeMethodID; 175 jmethodID mAddNoMediaFolderMethodID; 176}; 177 178 179static bool ExceptionCheck(void* env) 180{ 181 LOGV("ExceptionCheck"); 182 return ((JNIEnv *)env)->ExceptionCheck(); 183} 184 185// Call this method with sLock hold 186static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz) 187{ 188 return (MediaScanner *) env->GetIntField(thiz, fields.context); 189} 190 191// Call this method with sLock hold 192static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s) 193{ 194 env->SetIntField(thiz, fields.context, (int)s); 195} 196 197static void 198android_media_MediaScanner_processDirectory( 199 JNIEnv *env, jobject thiz, jstring path, jobject client) 200{ 201 LOGV("processDirectory"); 202 Mutex::Autolock l(sLock); 203 MediaScanner *mp = getNativeScanner_l(env, thiz); 204 if (mp == NULL) { 205 jniThrowException(env, kRunTimeException, "No scanner available"); 206 return; 207 } 208 209 if (path == NULL) { 210 jniThrowException(env, kIllegalArgumentException, NULL); 211 return; 212 } 213 214 const char *pathStr = env->GetStringUTFChars(path, NULL); 215 if (pathStr == NULL) { // Out of memory 216 return; 217 } 218 219 MyMediaScannerClient myClient(env, client); 220 mp->processDirectory(pathStr, myClient, ExceptionCheck, env); 221 env->ReleaseStringUTFChars(path, pathStr); 222} 223 224static void 225android_media_MediaScanner_processFile( 226 JNIEnv *env, jobject thiz, jstring path, 227 jstring mimeType, jobject client) 228{ 229 LOGV("processFile"); 230 231 // Lock already hold by processDirectory 232 MediaScanner *mp = getNativeScanner_l(env, thiz); 233 if (mp == NULL) { 234 jniThrowException(env, kRunTimeException, "No scanner available"); 235 return; 236 } 237 238 if (path == NULL) { 239 jniThrowException(env, kIllegalArgumentException, NULL); 240 return; 241 } 242 243 const char *pathStr = env->GetStringUTFChars(path, NULL); 244 if (pathStr == NULL) { // Out of memory 245 return; 246 } 247 248 const char *mimeTypeStr = 249 (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL); 250 if (mimeType && mimeTypeStr == NULL) { // Out of memory 251 // ReleaseStringUTFChars can be called with an exception pending. 252 env->ReleaseStringUTFChars(path, pathStr); 253 return; 254 } 255 256 MyMediaScannerClient myClient(env, client); 257 mp->processFile(pathStr, mimeTypeStr, myClient); 258 env->ReleaseStringUTFChars(path, pathStr); 259 if (mimeType) { 260 env->ReleaseStringUTFChars(mimeType, mimeTypeStr); 261 } 262} 263 264static void 265android_media_MediaScanner_setLocale( 266 JNIEnv *env, jobject thiz, jstring locale) 267{ 268 LOGV("setLocale"); 269 Mutex::Autolock l(sLock); 270 MediaScanner *mp = getNativeScanner_l(env, thiz); 271 if (mp == NULL) { 272 jniThrowException(env, kRunTimeException, "No scanner available"); 273 return; 274 } 275 276 if (locale == NULL) { 277 jniThrowException(env, kIllegalArgumentException, NULL); 278 return; 279 } 280 const char *localeStr = env->GetStringUTFChars(locale, NULL); 281 if (localeStr == NULL) { // Out of memory 282 return; 283 } 284 mp->setLocale(localeStr); 285 286 env->ReleaseStringUTFChars(locale, localeStr); 287} 288 289static jbyteArray 290android_media_MediaScanner_extractAlbumArt( 291 JNIEnv *env, jobject thiz, jobject fileDescriptor) 292{ 293 LOGV("extractAlbumArt"); 294 Mutex::Autolock l(sLock); 295 MediaScanner *mp = getNativeScanner_l(env, thiz); 296 if (mp == NULL) { 297 jniThrowException(env, kRunTimeException, "No scanner available"); 298 return NULL; 299 } 300 301 if (fileDescriptor == NULL) { 302 jniThrowException(env, kIllegalArgumentException, NULL); 303 return NULL; 304 } 305 306 int fd = getParcelFileDescriptorFD(env, fileDescriptor); 307 char* data = mp->extractAlbumArt(fd); 308 if (!data) { 309 return NULL; 310 } 311 long len = *((long*)data); 312 313 jbyteArray array = env->NewByteArray(len); 314 if (array != NULL) { 315 jbyte* bytes = env->GetByteArrayElements(array, NULL); 316 memcpy(bytes, data + 4, len); 317 env->ReleaseByteArrayElements(array, bytes, 0); 318 } 319 320done: 321 free(data); 322 // if NewByteArray() returned NULL, an out-of-memory 323 // exception will have been raised. I just want to 324 // return null in that case. 325 env->ExceptionClear(); 326 return array; 327} 328 329// This function gets a field ID, which in turn causes class initialization. 330// It is called from a static block in MediaScanner, which won't run until the 331// first time an instance of this class is used. 332static void 333android_media_MediaScanner_native_init(JNIEnv *env) 334{ 335 LOGV("native_init"); 336 jclass clazz = env->FindClass(kClassMediaScanner); 337 if (clazz == NULL) { 338 const char* err = "Can't find android/media/MediaScanner"; 339 jniThrowException(env, kRunTimeException, err); 340 return; 341 } 342 343 fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 344 if (fields.context == NULL) { 345 const char* err = "Can't find MediaScanner.mNativeContext"; 346 jniThrowException(env, kRunTimeException, err); 347 return; 348 } 349} 350 351static void 352android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) 353{ 354 LOGV("native_setup"); 355 MediaScanner *mp = new StagefrightMediaScanner; 356 357 if (mp == NULL) { 358 jniThrowException(env, kRunTimeException, "Out of memory"); 359 return; 360 } 361 362 env->SetIntField(thiz, fields.context, (int)mp); 363} 364 365static void 366android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz) 367{ 368 LOGV("native_finalize"); 369 Mutex::Autolock l(sLock); 370 MediaScanner *mp = getNativeScanner_l(env, thiz); 371 if (mp == 0) { 372 return; 373 } 374 delete mp; 375 setNativeScanner_l(env, thiz, 0); 376} 377 378static JNINativeMethod gMethods[] = { 379 { 380 "processDirectory", 381 "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 382 (void *)android_media_MediaScanner_processDirectory 383 }, 384 385 { 386 "processFile", 387 "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 388 (void *)android_media_MediaScanner_processFile 389 }, 390 391 { 392 "setLocale", 393 "(Ljava/lang/String;)V", 394 (void *)android_media_MediaScanner_setLocale 395 }, 396 397 { 398 "extractAlbumArt", 399 "(Ljava/io/FileDescriptor;)[B", 400 (void *)android_media_MediaScanner_extractAlbumArt 401 }, 402 403 { 404 "native_init", 405 "()V", 406 (void *)android_media_MediaScanner_native_init 407 }, 408 409 { 410 "native_setup", 411 "()V", 412 (void *)android_media_MediaScanner_native_setup 413 }, 414 415 { 416 "native_finalize", 417 "()V", 418 (void *)android_media_MediaScanner_native_finalize 419 }, 420}; 421 422// This function only registers the native methods, and is called from 423// JNI_OnLoad in android_media_MediaPlayer.cpp 424int register_android_media_MediaScanner(JNIEnv *env) 425{ 426 return AndroidRuntime::registerNativeMethods(env, 427 kClassMediaScanner, gMethods, NELEM(gMethods)); 428} 429 430 431