blob: 043f3739c36f318776b259b03350bf1de070268b [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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216 return;
217 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218
219 MyMediaScannerClient myClient(env, client);
Mike Lockwoodc37255d2010-09-10 14:47:36 -0400220 mp->processDirectory(pathStr, myClient, ExceptionCheck, env);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 env->ReleaseStringUTFChars(path, pathStr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222}
223
224static void
James Dongf3997522011-03-11 12:02:12 -0800225android_media_MediaScanner_processFile(
226 JNIEnv *env, jobject thiz, jstring path,
227 jstring mimeType, jobject client)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228{
James Dongf3997522011-03-11 12:02:12 -0800229 LOGV("processFile");
James Dong133cf8b2011-03-11 15:18:40 -0800230
231 // Lock already hold by processDirectory
232 MediaScanner *mp = getNativeScanner_l(env, thiz);
233 if (mp == NULL) {
234 jniThrowException(env, kRunTimeException, "No scanner available");
235 return;
236 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237
238 if (path == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800239 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 return;
241 }
James Dongf3997522011-03-11 12:02:12 -0800242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 const char *pathStr = env->GetStringUTFChars(path, NULL);
244 if (pathStr == NULL) { // Out of memory
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 return;
246 }
James Dongf3997522011-03-11 12:02:12 -0800247
248 const char *mimeTypeStr =
249 (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 if (mimeType && mimeTypeStr == NULL) { // Out of memory
James Dongc371a022011-04-06 12:16:07 -0700251 // ReleaseStringUTFChars can be called with an exception pending.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 env->ReleaseStringUTFChars(path, pathStr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 return;
254 }
255
256 MyMediaScannerClient myClient(env, client);
257 mp->processFile(pathStr, mimeTypeStr, myClient);
258 env->ReleaseStringUTFChars(path, pathStr);
259 if (mimeType) {
260 env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
261 }
262}
263
264static void
James Dongf3997522011-03-11 12:02:12 -0800265android_media_MediaScanner_setLocale(
266 JNIEnv *env, jobject thiz, jstring locale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267{
James Dongf3997522011-03-11 12:02:12 -0800268 LOGV("setLocale");
James Dong133cf8b2011-03-11 15:18:40 -0800269 Mutex::Autolock l(sLock);
270 MediaScanner *mp = getNativeScanner_l(env, thiz);
271 if (mp == NULL) {
272 jniThrowException(env, kRunTimeException, "No scanner available");
273 return;
274 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275
276 if (locale == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800277 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 return;
279 }
280 const char *localeStr = env->GetStringUTFChars(locale, NULL);
281 if (localeStr == NULL) { // Out of memory
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 return;
283 }
284 mp->setLocale(localeStr);
285
286 env->ReleaseStringUTFChars(locale, localeStr);
287}
288
289static jbyteArray
James Dongf3997522011-03-11 12:02:12 -0800290android_media_MediaScanner_extractAlbumArt(
291 JNIEnv *env, jobject thiz, jobject fileDescriptor)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800292{
James Dongf3997522011-03-11 12:02:12 -0800293 LOGV("extractAlbumArt");
James Dong133cf8b2011-03-11 15:18:40 -0800294 Mutex::Autolock l(sLock);
295 MediaScanner *mp = getNativeScanner_l(env, thiz);
296 if (mp == NULL) {
297 jniThrowException(env, kRunTimeException, "No scanner available");
298 return NULL;
299 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300
301 if (fileDescriptor == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800302 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 return NULL;
304 }
305
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700306 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 char* data = mp->extractAlbumArt(fd);
308 if (!data) {
309 return NULL;
310 }
311 long len = *((long*)data);
James Dongf3997522011-03-11 12:02:12 -0800312
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 jbyteArray array = env->NewByteArray(len);
314 if (array != NULL) {
315 jbyte* bytes = env->GetByteArrayElements(array, NULL);
316 memcpy(bytes, data + 4, len);
317 env->ReleaseByteArrayElements(array, bytes, 0);
318 }
James Dongf3997522011-03-11 12:02:12 -0800319
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320done:
321 free(data);
322 // if NewByteArray() returned NULL, an out-of-memory
323 // exception will have been raised. I just want to
324 // return null in that case.
325 env->ExceptionClear();
326 return array;
327}
328
Marco Nelissen4935d052009-08-03 11:12:58 -0700329// This function gets a field ID, which in turn causes class initialization.
330// It is called from a static block in MediaScanner, which won't run until the
331// first time an instance of this class is used.
332static void
333android_media_MediaScanner_native_init(JNIEnv *env)
334{
James Dongf3997522011-03-11 12:02:12 -0800335 LOGV("native_init");
336 jclass clazz = env->FindClass(kClassMediaScanner);
Marco Nelissen4935d052009-08-03 11:12:58 -0700337 if (clazz == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800338 const char* err = "Can't find android/media/MediaScanner";
339 jniThrowException(env, kRunTimeException, err);
Marco Nelissen4935d052009-08-03 11:12:58 -0700340 return;
341 }
342
343 fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
344 if (fields.context == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800345 const char* err = "Can't find MediaScanner.mNativeContext";
346 jniThrowException(env, kRunTimeException, err);
Marco Nelissen4935d052009-08-03 11:12:58 -0700347 return;
348 }
349}
350
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351static void
352android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
353{
James Dongf3997522011-03-11 12:02:12 -0800354 LOGV("native_setup");
Andreas Huber8d65dd22010-06-23 16:40:57 -0700355 MediaScanner *mp = new StagefrightMediaScanner;
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800356
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 if (mp == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800358 jniThrowException(env, kRunTimeException, "Out of memory");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 return;
360 }
361
362 env->SetIntField(thiz, fields.context, (int)mp);
363}
364
365static void
366android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
367{
James Dongf3997522011-03-11 12:02:12 -0800368 LOGV("native_finalize");
James Dong133cf8b2011-03-11 15:18:40 -0800369 Mutex::Autolock l(sLock);
370 MediaScanner *mp = getNativeScanner_l(env, thiz);
James Dongf3997522011-03-11 12:02:12 -0800371 if (mp == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 return;
James Dongf3997522011-03-11 12:02:12 -0800373 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 delete mp;
James Dong133cf8b2011-03-11 15:18:40 -0800375 setNativeScanner_l(env, thiz, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376}
377
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378static JNINativeMethod gMethods[] = {
James Dongf3997522011-03-11 12:02:12 -0800379 {
380 "processDirectory",
381 "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
382 (void *)android_media_MediaScanner_processDirectory
383 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384
James Dongf3997522011-03-11 12:02:12 -0800385 {
386 "processFile",
387 "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
388 (void *)android_media_MediaScanner_processFile
389 },
390
391 {
392 "setLocale",
393 "(Ljava/lang/String;)V",
394 (void *)android_media_MediaScanner_setLocale
395 },
396
397 {
398 "extractAlbumArt",
399 "(Ljava/io/FileDescriptor;)[B",
400 (void *)android_media_MediaScanner_extractAlbumArt
401 },
402
403 {
404 "native_init",
405 "()V",
406 (void *)android_media_MediaScanner_native_init
407 },
408
409 {
410 "native_setup",
411 "()V",
412 (void *)android_media_MediaScanner_native_setup
413 },
414
415 {
416 "native_finalize",
417 "()V",
418 (void *)android_media_MediaScanner_native_finalize
419 },
420};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421
Marco Nelissen4935d052009-08-03 11:12:58 -0700422// This function only registers the native methods, and is called from
423// JNI_OnLoad in android_media_MediaPlayer.cpp
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424int register_android_media_MediaScanner(JNIEnv *env)
425{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 return AndroidRuntime::registerNativeMethods(env,
James Dongf3997522011-03-11 12:02:12 -0800427 kClassMediaScanner, gMethods, NELEM(gMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428}