android_media_MediaScanner.cpp revision 8d65dd26530c7559082bd738cf930e2806158ab2
1/* //device/libs/media_jni/MediaScanner.cpp 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_TAG "MediaScanner" 19#include "utils/Log.h" 20 21#include <media/mediascanner.h> 22#include <stdio.h> 23#include <assert.h> 24#include <limits.h> 25#include <unistd.h> 26#include <fcntl.h> 27#include <cutils/properties.h> 28#include <utils/threads.h> 29 30#include "jni.h" 31#include "JNIHelp.h" 32#include "android_runtime/AndroidRuntime.h" 33 34#include <media/stagefright/StagefrightMediaScanner.h> 35 36// ---------------------------------------------------------------------------- 37 38using namespace android; 39 40// ---------------------------------------------------------------------------- 41 42struct fields_t { 43 jfieldID context; 44}; 45static fields_t fields; 46 47// ---------------------------------------------------------------------------- 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 jclass mediaScannerClientInterface = env->FindClass("android/media/MediaScannerClient"); 60 if (mediaScannerClientInterface == NULL) { 61 fprintf(stderr, "android/media/MediaScannerClient not found\n"); 62 } 63 else { 64 mScanFileMethodID = env->GetMethodID(mediaScannerClientInterface, "scanFile", 65 "(Ljava/lang/String;JJ)V"); 66 mHandleStringTagMethodID = env->GetMethodID(mediaScannerClientInterface, "handleStringTag", 67 "(Ljava/lang/String;Ljava/lang/String;)V"); 68 mSetMimeTypeMethodID = env->GetMethodID(mediaScannerClientInterface, "setMimeType", 69 "(Ljava/lang/String;)V"); 70 mAddNoMediaFolderMethodID = env->GetMethodID(mediaScannerClientInterface, "addNoMediaFolder", 71 "(Ljava/lang/String;)V"); 72 } 73 } 74 75 virtual ~MyMediaScannerClient() 76 { 77 mEnv->DeleteGlobalRef(mClient); 78 } 79 80 // returns true if it succeeded, false if an exception occured in the Java code 81 virtual bool scanFile(const char* path, long long lastModified, long long fileSize) 82 { 83 jstring pathStr; 84 if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false; 85 86 mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize); 87 88 mEnv->DeleteLocalRef(pathStr); 89 return (!mEnv->ExceptionCheck()); 90 } 91 92 // returns true if it succeeded, false if an exception occured in the Java code 93 virtual bool handleStringTag(const char* name, const char* value) 94 { 95 jstring nameStr, valueStr; 96 if ((nameStr = mEnv->NewStringUTF(name)) == NULL) return false; 97 if ((valueStr = mEnv->NewStringUTF(value)) == NULL) return false; 98 99 mEnv->CallVoidMethod(mClient, mHandleStringTagMethodID, nameStr, valueStr); 100 101 mEnv->DeleteLocalRef(nameStr); 102 mEnv->DeleteLocalRef(valueStr); 103 return (!mEnv->ExceptionCheck()); 104 } 105 106 // returns true if it succeeded, false if an exception occured in the Java code 107 virtual bool setMimeType(const char* mimeType) 108 { 109 jstring mimeTypeStr; 110 if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) return false; 111 112 mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr); 113 114 mEnv->DeleteLocalRef(mimeTypeStr); 115 return (!mEnv->ExceptionCheck()); 116 } 117 118 // returns true if it succeeded, false if an exception occured in the Java code 119 virtual bool addNoMediaFolder(const char* path) 120 { 121 jstring pathStr; 122 if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false; 123 124 mEnv->CallVoidMethod(mClient, mAddNoMediaFolderMethodID, pathStr); 125 126 mEnv->DeleteLocalRef(pathStr); 127 return (!mEnv->ExceptionCheck()); 128 } 129 130 131private: 132 JNIEnv *mEnv; 133 jobject mClient; 134 jmethodID mScanFileMethodID; 135 jmethodID mHandleStringTagMethodID; 136 jmethodID mSetMimeTypeMethodID; 137 jmethodID mAddNoMediaFolderMethodID; 138}; 139 140 141// ---------------------------------------------------------------------------- 142 143static bool ExceptionCheck(void* env) 144{ 145 return ((JNIEnv *)env)->ExceptionCheck(); 146} 147 148static void 149android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client) 150{ 151 MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); 152 153 if (path == NULL) { 154 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 155 return; 156 } 157 if (extensions == NULL) { 158 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 159 return; 160 } 161 162 const char *pathStr = env->GetStringUTFChars(path, NULL); 163 if (pathStr == NULL) { // Out of memory 164 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 165 return; 166 } 167 const char *extensionsStr = env->GetStringUTFChars(extensions, NULL); 168 if (extensionsStr == NULL) { // Out of memory 169 env->ReleaseStringUTFChars(path, pathStr); 170 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 171 return; 172 } 173 174 MyMediaScannerClient myClient(env, client); 175 mp->processDirectory(pathStr, extensionsStr, myClient, ExceptionCheck, env); 176 env->ReleaseStringUTFChars(path, pathStr); 177 env->ReleaseStringUTFChars(extensions, extensionsStr); 178} 179 180static void 181android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client) 182{ 183 MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); 184 185 if (path == NULL) { 186 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 187 return; 188 } 189 190 const char *pathStr = env->GetStringUTFChars(path, NULL); 191 if (pathStr == NULL) { // Out of memory 192 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 193 return; 194 } 195 const char *mimeTypeStr = (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL); 196 if (mimeType && mimeTypeStr == NULL) { // Out of memory 197 env->ReleaseStringUTFChars(path, pathStr); 198 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 199 return; 200 } 201 202 MyMediaScannerClient myClient(env, client); 203 mp->processFile(pathStr, mimeTypeStr, myClient); 204 env->ReleaseStringUTFChars(path, pathStr); 205 if (mimeType) { 206 env->ReleaseStringUTFChars(mimeType, mimeTypeStr); 207 } 208} 209 210static void 211android_media_MediaScanner_setLocale(JNIEnv *env, jobject thiz, jstring locale) 212{ 213 MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); 214 215 if (locale == NULL) { 216 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 217 return; 218 } 219 const char *localeStr = env->GetStringUTFChars(locale, NULL); 220 if (localeStr == NULL) { // Out of memory 221 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 222 return; 223 } 224 mp->setLocale(localeStr); 225 226 env->ReleaseStringUTFChars(locale, localeStr); 227} 228 229static jbyteArray 230android_media_MediaScanner_extractAlbumArt(JNIEnv *env, jobject thiz, jobject fileDescriptor) 231{ 232 MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); 233 234 if (fileDescriptor == NULL) { 235 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 236 return NULL; 237 } 238 239 int fd = getParcelFileDescriptorFD(env, fileDescriptor); 240 char* data = mp->extractAlbumArt(fd); 241 if (!data) { 242 return NULL; 243 } 244 long len = *((long*)data); 245 246 jbyteArray array = env->NewByteArray(len); 247 if (array != NULL) { 248 jbyte* bytes = env->GetByteArrayElements(array, NULL); 249 memcpy(bytes, data + 4, len); 250 env->ReleaseByteArrayElements(array, bytes, 0); 251 } 252 253done: 254 free(data); 255 // if NewByteArray() returned NULL, an out-of-memory 256 // exception will have been raised. I just want to 257 // return null in that case. 258 env->ExceptionClear(); 259 return array; 260} 261 262// This function gets a field ID, which in turn causes class initialization. 263// It is called from a static block in MediaScanner, which won't run until the 264// first time an instance of this class is used. 265static void 266android_media_MediaScanner_native_init(JNIEnv *env) 267{ 268 jclass clazz; 269 270 clazz = env->FindClass("android/media/MediaScanner"); 271 if (clazz == NULL) { 272 jniThrowException(env, "java/lang/RuntimeException", "Can't find android/media/MediaScanner"); 273 return; 274 } 275 276 fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 277 if (fields.context == NULL) { 278 jniThrowException(env, "java/lang/RuntimeException", "Can't find MediaScanner.mNativeContext"); 279 return; 280 } 281} 282 283static void 284android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) 285{ 286 MediaScanner *mp = new StagefrightMediaScanner; 287 288 if (mp == NULL) { 289 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 290 return; 291 } 292 293 env->SetIntField(thiz, fields.context, (int)mp); 294} 295 296static void 297android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz) 298{ 299 MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); 300 301 //printf("##### android_media_MediaScanner_native_finalize: ctx=0x%p\n", ctx); 302 303 if (mp == 0) 304 return; 305 306 delete mp; 307} 308 309// ---------------------------------------------------------------------------- 310 311static JNINativeMethod gMethods[] = { 312 {"processDirectory", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 313 (void *)android_media_MediaScanner_processDirectory}, 314 {"processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 315 (void *)android_media_MediaScanner_processFile}, 316 {"setLocale", "(Ljava/lang/String;)V", (void *)android_media_MediaScanner_setLocale}, 317 {"extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt}, 318 {"native_init", "()V", (void *)android_media_MediaScanner_native_init}, 319 {"native_setup", "()V", (void *)android_media_MediaScanner_native_setup}, 320 {"native_finalize", "()V", (void *)android_media_MediaScanner_native_finalize}, 321}; 322 323static const char* const kClassPathName = "android/media/MediaScanner"; 324 325// This function only registers the native methods, and is called from 326// JNI_OnLoad in android_media_MediaPlayer.cpp 327int register_android_media_MediaScanner(JNIEnv *env) 328{ 329 return AndroidRuntime::registerNativeMethods(env, 330 "android/media/MediaScanner", gMethods, NELEM(gMethods)); 331} 332 333 334