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 38 using namespace android; 39 40 // ---------------------------------------------------------------------------- 41 42 struct fields_t { 43 jfieldID context; 44 }; 45 static fields_t fields; 46 47 // ---------------------------------------------------------------------------- 48 49 class MyMediaScannerClient : public MediaScannerClient 50 { 51 public: 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 131 private: 132 JNIEnv *mEnv; 133 jobject mClient; 134 jmethodID mScanFileMethodID; 135 jmethodID mHandleStringTagMethodID; 136 jmethodID mSetMimeTypeMethodID; 137 jmethodID mAddNoMediaFolderMethodID; 138 }; 139 140 141 // ---------------------------------------------------------------------------- 142 143 static bool ExceptionCheck(void* env) 144 { 145 return ((JNIEnv *)env)->ExceptionCheck(); 146 } 147 148 static void 149 android_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 180 static void 181 android_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 210 static void 211 android_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 229 static jbyteArray 230 android_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 253 done: 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. 265 static void 266 android_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 283 static void 284 android_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 296 static void 297 android_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 311 static 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 323 static 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 327 int register_android_media_MediaScanner(JNIEnv *env) 328 { 329 return AndroidRuntime::registerNativeMethods(env, 330 "android/media/MediaScanner", gMethods, NELEM(gMethods)); 331 } 332 333 334