blob: 144fba2d5873a473d495d50b8141d5410c8dcecf [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/* //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
Andreas Huberbfb9fb12009-12-03 11:31:19 -080033#ifndef NO_OPENCORE
34#include "pvmediascanner.h"
35#else
36#include "StagefrightMediaScanner.h"
37#endif
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
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");
Marco Nelissen8b046612009-09-03 10:49:55 -070073 mAddNoMediaFolderMethodID = env->GetMethodID(mediaScannerClientInterface, "addNoMediaFolder",
74 "(Ljava/lang/String;)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 }
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
Marco Nelissen8b046612009-09-03 10:49:55 -0700121 // 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134private:
135 JNIEnv *mEnv;
136 jobject mClient;
137 jmethodID mScanFileMethodID;
138 jmethodID mHandleStringTagMethodID;
139 jmethodID mSetMimeTypeMethodID;
Marco Nelissen8b046612009-09-03 10:49:55 -0700140 jmethodID mAddNoMediaFolderMethodID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141};
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
Marco Nelissen4935d052009-08-03 11:12:58 -0700265// 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286static void
287android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
288{
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800289#ifndef NO_OPENCORE
290 MediaScanner *mp = new PVMediaScanner();
291#else
292 MediaScanner *mp = new StagefrightMediaScanner();
293#endif
294
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 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},
Marco Nelissen4935d052009-08-03 11:12:58 -0700325 {"native_init", "()V", (void *)android_media_MediaScanner_native_init},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 {"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
Marco Nelissen4935d052009-08-03 11:12:58 -0700332// This function only registers the native methods, and is called from
333// JNI_OnLoad in android_media_MediaPlayer.cpp
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334int register_android_media_MediaScanner(JNIEnv *env)
335{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 return AndroidRuntime::registerNativeMethods(env,
337 "android/media/MediaScanner", gMethods, NELEM(gMethods));
338}
339
340