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