android_media_MediaScanner.cpp revision dd66bcbf9d6ef0c50a18d9c4b1b39ce7ef7afcc4
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 = jniGetFDFromFileDescriptor(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 return; 339 } 340 341 fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 342 if (fields.context == NULL) { 343 return; 344 } 345} 346 347static void 348android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) 349{ 350 LOGV("native_setup"); 351 MediaScanner *mp = new StagefrightMediaScanner; 352 353 if (mp == NULL) { 354 jniThrowException(env, kRunTimeException, "Out of memory"); 355 return; 356 } 357 358 env->SetIntField(thiz, fields.context, (int)mp); 359} 360 361static void 362android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz) 363{ 364 LOGV("native_finalize"); 365 Mutex::Autolock l(sLock); 366 MediaScanner *mp = getNativeScanner_l(env, thiz); 367 if (mp == 0) { 368 return; 369 } 370 delete mp; 371 setNativeScanner_l(env, thiz, 0); 372} 373 374static JNINativeMethod gMethods[] = { 375 { 376 "processDirectory", 377 "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 378 (void *)android_media_MediaScanner_processDirectory 379 }, 380 381 { 382 "processFile", 383 "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 384 (void *)android_media_MediaScanner_processFile 385 }, 386 387 { 388 "setLocale", 389 "(Ljava/lang/String;)V", 390 (void *)android_media_MediaScanner_setLocale 391 }, 392 393 { 394 "extractAlbumArt", 395 "(Ljava/io/FileDescriptor;)[B", 396 (void *)android_media_MediaScanner_extractAlbumArt 397 }, 398 399 { 400 "native_init", 401 "()V", 402 (void *)android_media_MediaScanner_native_init 403 }, 404 405 { 406 "native_setup", 407 "()V", 408 (void *)android_media_MediaScanner_native_setup 409 }, 410 411 { 412 "native_finalize", 413 "()V", 414 (void *)android_media_MediaScanner_native_finalize 415 }, 416}; 417 418// This function only registers the native methods, and is called from 419// JNI_OnLoad in android_media_MediaPlayer.cpp 420int register_android_media_MediaScanner(JNIEnv *env) 421{ 422 return AndroidRuntime::registerNativeMethods(env, 423 kClassMediaScanner, gMethods, NELEM(gMethods)); 424} 425