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