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