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