android_media_MediaScanner.cpp revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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 } 69 } 70 71 virtual ~MyMediaScannerClient() 72 { 73 mEnv->DeleteGlobalRef(mClient); 74 } 75 76 // returns true if it succeeded, false if an exception occured in the Java code 77 virtual bool scanFile(const char* path, long long lastModified, long long fileSize) 78 { 79 jstring pathStr; 80 if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false; 81 82 mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize); 83 84 mEnv->DeleteLocalRef(pathStr); 85 return (!mEnv->ExceptionCheck()); 86 } 87 88 // returns true if it succeeded, false if an exception occured in the Java code 89 virtual bool handleStringTag(const char* name, const char* value) 90 { 91 jstring nameStr, valueStr; 92 if ((nameStr = mEnv->NewStringUTF(name)) == NULL) return false; 93 if ((valueStr = mEnv->NewStringUTF(value)) == NULL) return false; 94 95 mEnv->CallVoidMethod(mClient, mHandleStringTagMethodID, nameStr, valueStr); 96 97 mEnv->DeleteLocalRef(nameStr); 98 mEnv->DeleteLocalRef(valueStr); 99 return (!mEnv->ExceptionCheck()); 100 } 101 102 // returns true if it succeeded, false if an exception occured in the Java code 103 virtual bool setMimeType(const char* mimeType) 104 { 105 jstring mimeTypeStr; 106 if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) return false; 107 108 mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr); 109 110 mEnv->DeleteLocalRef(mimeTypeStr); 111 return (!mEnv->ExceptionCheck()); 112 } 113 114private: 115 JNIEnv *mEnv; 116 jobject mClient; 117 jmethodID mScanFileMethodID; 118 jmethodID mHandleStringTagMethodID; 119 jmethodID mSetMimeTypeMethodID; 120}; 121 122 123// ---------------------------------------------------------------------------- 124 125static bool ExceptionCheck(void* env) 126{ 127 return ((JNIEnv *)env)->ExceptionCheck(); 128} 129 130static void 131android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client) 132{ 133 MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); 134 135 if (path == NULL) { 136 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 137 return; 138 } 139 if (extensions == 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 const char *extensionsStr = env->GetStringUTFChars(extensions, NULL); 150 if (extensionsStr == NULL) { // Out of memory 151 env->ReleaseStringUTFChars(path, pathStr); 152 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 153 return; 154 } 155 156 MyMediaScannerClient myClient(env, client); 157 mp->processDirectory(pathStr, extensionsStr, myClient, ExceptionCheck, env); 158 env->ReleaseStringUTFChars(path, pathStr); 159 env->ReleaseStringUTFChars(extensions, extensionsStr); 160} 161 162static void 163android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client) 164{ 165 MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); 166 167 if (path == NULL) { 168 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 169 return; 170 } 171 172 const char *pathStr = env->GetStringUTFChars(path, NULL); 173 if (pathStr == NULL) { // Out of memory 174 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 175 return; 176 } 177 const char *mimeTypeStr = (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL); 178 if (mimeType && mimeTypeStr == NULL) { // Out of memory 179 env->ReleaseStringUTFChars(path, pathStr); 180 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 181 return; 182 } 183 184 MyMediaScannerClient myClient(env, client); 185 mp->processFile(pathStr, mimeTypeStr, myClient); 186 env->ReleaseStringUTFChars(path, pathStr); 187 if (mimeType) { 188 env->ReleaseStringUTFChars(mimeType, mimeTypeStr); 189 } 190} 191 192static jbyteArray 193android_media_MediaScanner_extractAlbumArt(JNIEnv *env, jobject thiz, jobject fileDescriptor) 194{ 195 MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); 196 197 if (fileDescriptor == NULL) { 198 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 199 return NULL; 200 } 201 202 int fd = getParcelFileDescriptorFD(env, fileDescriptor); 203 char* data = mp->extractAlbumArt(fd); 204 if (!data) { 205 return NULL; 206 } 207 long len = *((long*)data); 208 209 jbyteArray array = env->NewByteArray(len); 210 if (array != NULL) { 211 jbyte* bytes = env->GetByteArrayElements(array, NULL); 212 memcpy(bytes, data + 4, len); 213 env->ReleaseByteArrayElements(array, bytes, 0); 214 } 215 216done: 217 free(data); 218 // if NewByteArray() returned NULL, an out-of-memory 219 // exception will have been raised. I just want to 220 // return null in that case. 221 env->ExceptionClear(); 222 return array; 223} 224 225static void 226android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz) 227{ 228 MediaScanner *mp = new MediaScanner(); 229 if (mp == NULL) { 230 jniThrowException(env, "java/lang/RuntimeException", "Out of memory"); 231 return; 232 } 233 234 env->SetIntField(thiz, fields.context, (int)mp); 235} 236 237static void 238android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz) 239{ 240 MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context); 241 242 //printf("##### android_media_MediaScanner_native_finalize: ctx=0x%p\n", ctx); 243 244 if (mp == 0) 245 return; 246 247 delete mp; 248} 249 250// ---------------------------------------------------------------------------- 251 252static JNINativeMethod gMethods[] = { 253 {"processDirectory", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 254 (void *)android_media_MediaScanner_processDirectory}, 255 {"processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", 256 (void *)android_media_MediaScanner_processFile}, 257 {"extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt}, 258 {"native_setup", "()V", (void *)android_media_MediaScanner_native_setup}, 259 {"native_finalize", "()V", (void *)android_media_MediaScanner_native_finalize}, 260}; 261 262static const char* const kClassPathName = "android/media/MediaScanner"; 263 264int register_android_media_MediaScanner(JNIEnv *env) 265{ 266 jclass clazz; 267 268 clazz = env->FindClass("android/media/MediaScanner"); 269 if (clazz == NULL) { 270 LOGE("Can't find android/media/MediaScanner"); 271 return -1; 272 } 273 274 fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); 275 if (fields.context == NULL) { 276 LOGE("Can't find MediaScanner.mNativeContext"); 277 return -1; 278 } 279 280 return AndroidRuntime::registerNativeMethods(env, 281 "android/media/MediaScanner", gMethods, NELEM(gMethods)); 282} 283 284 285