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