blob: 9d7bf2c89aee1e68c0c22514f7f47eab40f1e2eb [file] [log] [blame]
James Dongf3997522011-03-11 12:02:12 -08001/*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002**
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
James Dongf3997522011-03-11 12:02:12 -080018//#define LOG_NDEBUG 0
19#define LOG_TAG "MediaScannerJNI"
20#include <utils/Log.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021#include <utils/threads.h>
James Dongf3997522011-03-11 12:02:12 -080022#include <media/mediascanner.h>
23#include <media/stagefright/StagefrightMediaScanner.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024
25#include "jni.h"
26#include "JNIHelp.h"
27#include "android_runtime/AndroidRuntime.h"
28
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029using namespace android;
30
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031
James Dongf3997522011-03-11 12:02:12 -080032static const char* const kClassMediaScannerClient =
33 "android/media/MediaScannerClient";
34
35static const char* const kClassMediaScanner =
36 "android/media/MediaScanner";
37
38static const char* const kRunTimeException =
39 "java/lang/RuntimeException";
40
41static const char* const kIllegalArgumentException =
42 "java/lang/IllegalArgumentException";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
James Dong133cf8b2011-03-11 15:18:40 -080044struct fields_t {
45 jfieldID context;
46};
47static fields_t fields;
48static Mutex sLock;
49
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050class MyMediaScannerClient : public MediaScannerClient
51{
52public:
53 MyMediaScannerClient(JNIEnv *env, jobject client)
54 : mEnv(env),
55 mClient(env->NewGlobalRef(client)),
56 mScanFileMethodID(0),
57 mHandleStringTagMethodID(0),
58 mSetMimeTypeMethodID(0)
59 {
James Dongf3997522011-03-11 12:02:12 -080060 LOGV("MyMediaScannerClient constructor");
61 jclass mediaScannerClientInterface =
62 env->FindClass(kClassMediaScannerClient);
63
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 if (mediaScannerClientInterface == NULL) {
James Dongf3997522011-03-11 12:02:12 -080065 LOGE("Class %s not found", kClassMediaScannerClient);
66 } else {
67 mScanFileMethodID = env->GetMethodID(
68 mediaScannerClientInterface,
69 "scanFile",
70 "(Ljava/lang/String;JJZ)V");
71
72 mHandleStringTagMethodID = env->GetMethodID(
73 mediaScannerClientInterface,
74 "handleStringTag",
75 "(Ljava/lang/String;Ljava/lang/String;)V");
76
77 mSetMimeTypeMethodID = env->GetMethodID(
78 mediaScannerClientInterface,
79 "setMimeType",
80 "(Ljava/lang/String;)V");
81
82 mAddNoMediaFolderMethodID = env->GetMethodID(
83 mediaScannerClientInterface,
84 "addNoMediaFolder",
85 "(Ljava/lang/String;)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 }
87 }
James Dongf3997522011-03-11 12:02:12 -080088
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 virtual ~MyMediaScannerClient()
90 {
James Dongf3997522011-03-11 12:02:12 -080091 LOGV("MyMediaScannerClient destructor");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 mEnv->DeleteGlobalRef(mClient);
93 }
James Dongf3997522011-03-11 12:02:12 -080094
95 // Returns true if it succeeded, false if an exception occured
96 // in the Java code
Mike Lockwood076e05b2010-12-16 12:54:24 -080097 virtual bool scanFile(const char* path, long long lastModified,
98 long long fileSize, bool isDirectory)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 {
James Dongf3997522011-03-11 12:02:12 -0800100 LOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
101 path, lastModified, fileSize, isDirectory);
102
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 jstring pathStr;
James Dongf3997522011-03-11 12:02:12 -0800104 if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
105 return false;
106 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107
Mike Lockwood076e05b2010-12-16 12:54:24 -0800108 mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
109 fileSize, isDirectory);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110
111 mEnv->DeleteLocalRef(pathStr);
112 return (!mEnv->ExceptionCheck());
113 }
114
James Dongf3997522011-03-11 12:02:12 -0800115 // Returns true if it succeeded, false if an exception occured
116 // in the Java code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 virtual bool handleStringTag(const char* name, const char* value)
118 {
James Dongf3997522011-03-11 12:02:12 -0800119 LOGV("handleStringTag: name(%s) and value(%s)", name, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 jstring nameStr, valueStr;
James Dongf3997522011-03-11 12:02:12 -0800121 if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {
122 return false;
123 }
124 if ((valueStr = mEnv->NewStringUTF(value)) == NULL) {
125 return false;
126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127
James Dongf3997522011-03-11 12:02:12 -0800128 mEnv->CallVoidMethod(
129 mClient, mHandleStringTagMethodID, nameStr, valueStr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130
131 mEnv->DeleteLocalRef(nameStr);
132 mEnv->DeleteLocalRef(valueStr);
133 return (!mEnv->ExceptionCheck());
134 }
135
James Dongf3997522011-03-11 12:02:12 -0800136 // Returns true if it succeeded, false if an exception occured
137 // in the Java code
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 virtual bool setMimeType(const char* mimeType)
139 {
James Dongf3997522011-03-11 12:02:12 -0800140 LOGV("setMimeType: %s", mimeType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 jstring mimeTypeStr;
James Dongf3997522011-03-11 12:02:12 -0800142 if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) {
143 return false;
144 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145
146 mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
147
148 mEnv->DeleteLocalRef(mimeTypeStr);
149 return (!mEnv->ExceptionCheck());
150 }
151
James Dongf3997522011-03-11 12:02:12 -0800152 // Returns true if it succeeded, false if an exception occured
153 // in the Java code
Marco Nelissen8b046612009-09-03 10:49:55 -0700154 virtual bool addNoMediaFolder(const char* path)
155 {
James Dongf3997522011-03-11 12:02:12 -0800156 LOGV("addNoMediaFolder: path(%s)", path);
Marco Nelissen8b046612009-09-03 10:49:55 -0700157 jstring pathStr;
James Dongf3997522011-03-11 12:02:12 -0800158 if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
159 return false;
160 }
Marco Nelissen8b046612009-09-03 10:49:55 -0700161
162 mEnv->CallVoidMethod(mClient, mAddNoMediaFolderMethodID, pathStr);
163
164 mEnv->DeleteLocalRef(pathStr);
165 return (!mEnv->ExceptionCheck());
166 }
167
168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169private:
170 JNIEnv *mEnv;
171 jobject mClient;
James Dongf3997522011-03-11 12:02:12 -0800172 jmethodID mScanFileMethodID;
173 jmethodID mHandleStringTagMethodID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 jmethodID mSetMimeTypeMethodID;
Marco Nelissen8b046612009-09-03 10:49:55 -0700175 jmethodID mAddNoMediaFolderMethodID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176};
177
178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179static bool ExceptionCheck(void* env)
180{
James Dongf3997522011-03-11 12:02:12 -0800181 LOGV("ExceptionCheck");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 return ((JNIEnv *)env)->ExceptionCheck();
183}
184
James Dong133cf8b2011-03-11 15:18:40 -0800185// Call this method with sLock hold
186static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz)
187{
188 return (MediaScanner *) env->GetIntField(thiz, fields.context);
189}
190
191// Call this method with sLock hold
192static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s)
193{
194 env->SetIntField(thiz, fields.context, (int)s);
195}
196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197static void
James Dongf3997522011-03-11 12:02:12 -0800198android_media_MediaScanner_processDirectory(
199 JNIEnv *env, jobject thiz, jstring path, jobject client)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200{
James Dongf3997522011-03-11 12:02:12 -0800201 LOGV("processDirectory");
James Dong133cf8b2011-03-11 15:18:40 -0800202 Mutex::Autolock l(sLock);
203 MediaScanner *mp = getNativeScanner_l(env, thiz);
204 if (mp == NULL) {
205 jniThrowException(env, kRunTimeException, "No scanner available");
206 return;
207 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208
209 if (path == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800210 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 return;
212 }
Mike Lockwoodc37255d2010-09-10 14:47:36 -0400213
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 const char *pathStr = env->GetStringUTFChars(path, NULL);
215 if (pathStr == NULL) { // Out of memory
James Dongf3997522011-03-11 12:02:12 -0800216 jniThrowException(env, kRunTimeException, "Out of memory");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 return;
218 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219
220 MyMediaScannerClient myClient(env, client);
Mike Lockwoodc37255d2010-09-10 14:47:36 -0400221 mp->processDirectory(pathStr, myClient, ExceptionCheck, env);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 env->ReleaseStringUTFChars(path, pathStr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223}
224
225static void
James Dongf3997522011-03-11 12:02:12 -0800226android_media_MediaScanner_processFile(
227 JNIEnv *env, jobject thiz, jstring path,
228 jstring mimeType, jobject client)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229{
James Dongf3997522011-03-11 12:02:12 -0800230 LOGV("processFile");
James Dong133cf8b2011-03-11 15:18:40 -0800231
232 // Lock already hold by processDirectory
233 MediaScanner *mp = getNativeScanner_l(env, thiz);
234 if (mp == NULL) {
235 jniThrowException(env, kRunTimeException, "No scanner available");
236 return;
237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238
239 if (path == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800240 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 return;
242 }
James Dongf3997522011-03-11 12:02:12 -0800243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 const char *pathStr = env->GetStringUTFChars(path, NULL);
245 if (pathStr == NULL) { // Out of memory
James Dongf3997522011-03-11 12:02:12 -0800246 jniThrowException(env, kRunTimeException, "Out of memory");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 return;
248 }
James Dongf3997522011-03-11 12:02:12 -0800249
250 const char *mimeTypeStr =
251 (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 if (mimeType && mimeTypeStr == NULL) { // Out of memory
253 env->ReleaseStringUTFChars(path, pathStr);
James Dongf3997522011-03-11 12:02:12 -0800254 jniThrowException(env, kRunTimeException, "Out of memory");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 return;
256 }
257
258 MyMediaScannerClient myClient(env, client);
259 mp->processFile(pathStr, mimeTypeStr, myClient);
260 env->ReleaseStringUTFChars(path, pathStr);
261 if (mimeType) {
262 env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
263 }
264}
265
266static void
James Dongf3997522011-03-11 12:02:12 -0800267android_media_MediaScanner_setLocale(
268 JNIEnv *env, jobject thiz, jstring locale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269{
James Dongf3997522011-03-11 12:02:12 -0800270 LOGV("setLocale");
James Dong133cf8b2011-03-11 15:18:40 -0800271 Mutex::Autolock l(sLock);
272 MediaScanner *mp = getNativeScanner_l(env, thiz);
273 if (mp == NULL) {
274 jniThrowException(env, kRunTimeException, "No scanner available");
275 return;
276 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277
278 if (locale == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800279 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 return;
281 }
282 const char *localeStr = env->GetStringUTFChars(locale, NULL);
283 if (localeStr == NULL) { // Out of memory
James Dongf3997522011-03-11 12:02:12 -0800284 jniThrowException(env, kRunTimeException, "Out of memory");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 return;
286 }
287 mp->setLocale(localeStr);
288
289 env->ReleaseStringUTFChars(locale, localeStr);
290}
291
292static jbyteArray
James Dongf3997522011-03-11 12:02:12 -0800293android_media_MediaScanner_extractAlbumArt(
294 JNIEnv *env, jobject thiz, jobject fileDescriptor)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295{
James Dongf3997522011-03-11 12:02:12 -0800296 LOGV("extractAlbumArt");
James Dong133cf8b2011-03-11 15:18:40 -0800297 Mutex::Autolock l(sLock);
298 MediaScanner *mp = getNativeScanner_l(env, thiz);
299 if (mp == NULL) {
300 jniThrowException(env, kRunTimeException, "No scanner available");
301 return NULL;
302 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303
304 if (fileDescriptor == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800305 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800306 return NULL;
307 }
308
309 int fd = getParcelFileDescriptorFD(env, fileDescriptor);
310 char* data = mp->extractAlbumArt(fd);
311 if (!data) {
312 return NULL;
313 }
314 long len = *((long*)data);
James Dongf3997522011-03-11 12:02:12 -0800315
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 jbyteArray array = env->NewByteArray(len);
317 if (array != NULL) {
318 jbyte* bytes = env->GetByteArrayElements(array, NULL);
319 memcpy(bytes, data + 4, len);
320 env->ReleaseByteArrayElements(array, bytes, 0);
321 }
James Dongf3997522011-03-11 12:02:12 -0800322
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323done:
324 free(data);
325 // if NewByteArray() returned NULL, an out-of-memory
326 // exception will have been raised. I just want to
327 // return null in that case.
328 env->ExceptionClear();
329 return array;
330}
331
Marco Nelissen4935d052009-08-03 11:12:58 -0700332// This function gets a field ID, which in turn causes class initialization.
333// It is called from a static block in MediaScanner, which won't run until the
334// first time an instance of this class is used.
335static void
336android_media_MediaScanner_native_init(JNIEnv *env)
337{
James Dongf3997522011-03-11 12:02:12 -0800338 LOGV("native_init");
339 jclass clazz = env->FindClass(kClassMediaScanner);
Marco Nelissen4935d052009-08-03 11:12:58 -0700340 if (clazz == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800341 const char* err = "Can't find android/media/MediaScanner";
342 jniThrowException(env, kRunTimeException, err);
Marco Nelissen4935d052009-08-03 11:12:58 -0700343 return;
344 }
345
346 fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
347 if (fields.context == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800348 const char* err = "Can't find MediaScanner.mNativeContext";
349 jniThrowException(env, kRunTimeException, err);
Marco Nelissen4935d052009-08-03 11:12:58 -0700350 return;
351 }
352}
353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354static void
355android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
356{
James Dongf3997522011-03-11 12:02:12 -0800357 LOGV("native_setup");
Andreas Huber8d65dd22010-06-23 16:40:57 -0700358 MediaScanner *mp = new StagefrightMediaScanner;
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 if (mp == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800361 jniThrowException(env, kRunTimeException, "Out of memory");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 return;
363 }
364
365 env->SetIntField(thiz, fields.context, (int)mp);
366}
367
368static void
369android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
370{
James Dongf3997522011-03-11 12:02:12 -0800371 LOGV("native_finalize");
James Dong133cf8b2011-03-11 15:18:40 -0800372 Mutex::Autolock l(sLock);
373 MediaScanner *mp = getNativeScanner_l(env, thiz);
James Dongf3997522011-03-11 12:02:12 -0800374 if (mp == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 return;
James Dongf3997522011-03-11 12:02:12 -0800376 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 delete mp;
James Dong133cf8b2011-03-11 15:18:40 -0800378 setNativeScanner_l(env, thiz, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379}
380
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381static JNINativeMethod gMethods[] = {
James Dongf3997522011-03-11 12:02:12 -0800382 {
383 "processDirectory",
384 "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
385 (void *)android_media_MediaScanner_processDirectory
386 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387
James Dongf3997522011-03-11 12:02:12 -0800388 {
389 "processFile",
390 "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
391 (void *)android_media_MediaScanner_processFile
392 },
393
394 {
395 "setLocale",
396 "(Ljava/lang/String;)V",
397 (void *)android_media_MediaScanner_setLocale
398 },
399
400 {
401 "extractAlbumArt",
402 "(Ljava/io/FileDescriptor;)[B",
403 (void *)android_media_MediaScanner_extractAlbumArt
404 },
405
406 {
407 "native_init",
408 "()V",
409 (void *)android_media_MediaScanner_native_init
410 },
411
412 {
413 "native_setup",
414 "()V",
415 (void *)android_media_MediaScanner_native_setup
416 },
417
418 {
419 "native_finalize",
420 "()V",
421 (void *)android_media_MediaScanner_native_finalize
422 },
423};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424
Marco Nelissen4935d052009-08-03 11:12:58 -0700425// This function only registers the native methods, and is called from
426// JNI_OnLoad in android_media_MediaPlayer.cpp
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427int register_android_media_MediaScanner(JNIEnv *env)
428{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 return AndroidRuntime::registerNativeMethods(env,
James Dongf3997522011-03-11 12:02:12 -0800430 kClassMediaScanner, gMethods, NELEM(gMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431}
432
433