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