blob: b88296fb4b72b647cc99405dea86df5ffd545c99 [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;
James Dong133cf8b2011-03-11 15:18:40 -080048
Jeff Brown2c70d4a2011-07-20 16:38:43 -070049static status_t checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
50 if (env->ExceptionCheck()) {
51 LOGE("An exception was thrown by callback '%s'.", methodName);
52 LOGE_EX(env);
53 env->ExceptionClear();
54 return UNKNOWN_ERROR;
55 }
56 return OK;
57}
58
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059class MyMediaScannerClient : public MediaScannerClient
60{
61public:
62 MyMediaScannerClient(JNIEnv *env, jobject client)
63 : mEnv(env),
64 mClient(env->NewGlobalRef(client)),
65 mScanFileMethodID(0),
66 mHandleStringTagMethodID(0),
67 mSetMimeTypeMethodID(0)
68 {
James Dongf3997522011-03-11 12:02:12 -080069 LOGV("MyMediaScannerClient constructor");
70 jclass mediaScannerClientInterface =
71 env->FindClass(kClassMediaScannerClient);
72
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 if (mediaScannerClientInterface == NULL) {
James Dongf3997522011-03-11 12:02:12 -080074 LOGE("Class %s not found", kClassMediaScannerClient);
75 } else {
76 mScanFileMethodID = env->GetMethodID(
77 mediaScannerClientInterface,
78 "scanFile",
Mike Lockwood997354e2011-04-24 11:15:09 -070079 "(Ljava/lang/String;JJZZ)V");
James Dongf3997522011-03-11 12:02:12 -080080
81 mHandleStringTagMethodID = env->GetMethodID(
82 mediaScannerClientInterface,
83 "handleStringTag",
84 "(Ljava/lang/String;Ljava/lang/String;)V");
85
86 mSetMimeTypeMethodID = env->GetMethodID(
87 mediaScannerClientInterface,
88 "setMimeType",
89 "(Ljava/lang/String;)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 }
91 }
James Dongf3997522011-03-11 12:02:12 -080092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 virtual ~MyMediaScannerClient()
94 {
James Dongf3997522011-03-11 12:02:12 -080095 LOGV("MyMediaScannerClient destructor");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 mEnv->DeleteGlobalRef(mClient);
97 }
James Dongf3997522011-03-11 12:02:12 -080098
Jeff Brown2c70d4a2011-07-20 16:38:43 -070099 virtual status_t scanFile(const char* path, long long lastModified,
Mike Lockwood997354e2011-04-24 11:15:09 -0700100 long long fileSize, bool isDirectory, bool noMedia)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 {
James Dongf3997522011-03-11 12:02:12 -0800102 LOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
103 path, lastModified, fileSize, isDirectory);
104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 jstring pathStr;
James Dongf3997522011-03-11 12:02:12 -0800106 if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700107 mEnv->ExceptionClear();
108 return NO_MEMORY;
James Dongf3997522011-03-11 12:02:12 -0800109 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110
Mike Lockwood076e05b2010-12-16 12:54:24 -0800111 mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
Mike Lockwood997354e2011-04-24 11:15:09 -0700112 fileSize, isDirectory, noMedia);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113
114 mEnv->DeleteLocalRef(pathStr);
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700115 return checkAndClearExceptionFromCallback(mEnv, "scanFile");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 }
117
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700118 virtual status_t handleStringTag(const char* name, const char* value)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 {
James Dongf3997522011-03-11 12:02:12 -0800120 LOGV("handleStringTag: name(%s) and value(%s)", name, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 jstring nameStr, valueStr;
James Dongf3997522011-03-11 12:02:12 -0800122 if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700123 mEnv->ExceptionClear();
124 return NO_MEMORY;
James Dongf3997522011-03-11 12:02:12 -0800125 }
126 if ((valueStr = mEnv->NewStringUTF(value)) == NULL) {
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700127 mEnv->DeleteLocalRef(nameStr);
128 mEnv->ExceptionClear();
129 return NO_MEMORY;
James Dongf3997522011-03-11 12:02:12 -0800130 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131
James Dongf3997522011-03-11 12:02:12 -0800132 mEnv->CallVoidMethod(
133 mClient, mHandleStringTagMethodID, nameStr, valueStr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
135 mEnv->DeleteLocalRef(nameStr);
136 mEnv->DeleteLocalRef(valueStr);
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700137 return checkAndClearExceptionFromCallback(mEnv, "handleStringTag");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 }
139
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700140 virtual status_t setMimeType(const char* mimeType)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 {
James Dongf3997522011-03-11 12:02:12 -0800142 LOGV("setMimeType: %s", mimeType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 jstring mimeTypeStr;
James Dongf3997522011-03-11 12:02:12 -0800144 if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) {
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700145 mEnv->ExceptionClear();
146 return NO_MEMORY;
James Dongf3997522011-03-11 12:02:12 -0800147 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148
149 mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
150
151 mEnv->DeleteLocalRef(mimeTypeStr);
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700152 return checkAndClearExceptionFromCallback(mEnv, "setMimeType");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 }
154
155private:
156 JNIEnv *mEnv;
157 jobject mClient;
James Dongf3997522011-03-11 12:02:12 -0800158 jmethodID mScanFileMethodID;
159 jmethodID mHandleStringTagMethodID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 jmethodID mSetMimeTypeMethodID;
161};
162
163
James Dong133cf8b2011-03-11 15:18:40 -0800164static MediaScanner *getNativeScanner_l(JNIEnv* env, jobject thiz)
165{
166 return (MediaScanner *) env->GetIntField(thiz, fields.context);
167}
168
James Dong133cf8b2011-03-11 15:18:40 -0800169static void setNativeScanner_l(JNIEnv* env, jobject thiz, MediaScanner *s)
170{
171 env->SetIntField(thiz, fields.context, (int)s);
172}
173
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174static void
James Dongf3997522011-03-11 12:02:12 -0800175android_media_MediaScanner_processDirectory(
176 JNIEnv *env, jobject thiz, jstring path, jobject client)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177{
James Dongf3997522011-03-11 12:02:12 -0800178 LOGV("processDirectory");
James Dong133cf8b2011-03-11 15:18:40 -0800179 MediaScanner *mp = getNativeScanner_l(env, thiz);
180 if (mp == NULL) {
181 jniThrowException(env, kRunTimeException, "No scanner available");
182 return;
183 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184
185 if (path == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800186 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 return;
188 }
Mike Lockwoodc37255d2010-09-10 14:47:36 -0400189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 const char *pathStr = env->GetStringUTFChars(path, NULL);
191 if (pathStr == NULL) { // Out of memory
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 return;
193 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194
195 MyMediaScannerClient myClient(env, client);
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700196 MediaScanResult result = mp->processDirectory(pathStr, myClient);
197 if (result == MEDIA_SCAN_RESULT_ERROR) {
198 LOGE("An error occurred while scanning directory '%s'.", pathStr);
199 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 env->ReleaseStringUTFChars(path, pathStr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201}
202
203static void
James Dongf3997522011-03-11 12:02:12 -0800204android_media_MediaScanner_processFile(
205 JNIEnv *env, jobject thiz, jstring path,
206 jstring mimeType, jobject client)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207{
James Dongf3997522011-03-11 12:02:12 -0800208 LOGV("processFile");
James Dong133cf8b2011-03-11 15:18:40 -0800209
210 // Lock already hold by processDirectory
211 MediaScanner *mp = getNativeScanner_l(env, thiz);
212 if (mp == NULL) {
213 jniThrowException(env, kRunTimeException, "No scanner available");
214 return;
215 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216
217 if (path == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800218 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 return;
220 }
James Dongf3997522011-03-11 12:02:12 -0800221
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 const char *pathStr = env->GetStringUTFChars(path, NULL);
223 if (pathStr == NULL) { // Out of memory
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 return;
225 }
James Dongf3997522011-03-11 12:02:12 -0800226
227 const char *mimeTypeStr =
228 (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229 if (mimeType && mimeTypeStr == NULL) { // Out of memory
James Dongc371a022011-04-06 12:16:07 -0700230 // ReleaseStringUTFChars can be called with an exception pending.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 env->ReleaseStringUTFChars(path, pathStr);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800232 return;
233 }
234
235 MyMediaScannerClient myClient(env, client);
Jeff Brown2c70d4a2011-07-20 16:38:43 -0700236 MediaScanResult result = mp->processFile(pathStr, mimeTypeStr, myClient);
237 if (result == MEDIA_SCAN_RESULT_ERROR) {
238 LOGE("An error occurred while scanning file '%s'.", pathStr);
239 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 env->ReleaseStringUTFChars(path, pathStr);
241 if (mimeType) {
242 env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
243 }
244}
245
246static void
James Dongf3997522011-03-11 12:02:12 -0800247android_media_MediaScanner_setLocale(
248 JNIEnv *env, jobject thiz, jstring locale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249{
James Dongf3997522011-03-11 12:02:12 -0800250 LOGV("setLocale");
James Dong133cf8b2011-03-11 15:18:40 -0800251 MediaScanner *mp = getNativeScanner_l(env, thiz);
252 if (mp == NULL) {
253 jniThrowException(env, kRunTimeException, "No scanner available");
254 return;
255 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256
257 if (locale == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800258 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 return;
260 }
261 const char *localeStr = env->GetStringUTFChars(locale, NULL);
262 if (localeStr == NULL) { // Out of memory
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 return;
264 }
265 mp->setLocale(localeStr);
266
267 env->ReleaseStringUTFChars(locale, localeStr);
268}
269
270static jbyteArray
James Dongf3997522011-03-11 12:02:12 -0800271android_media_MediaScanner_extractAlbumArt(
272 JNIEnv *env, jobject thiz, jobject fileDescriptor)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273{
James Dongf3997522011-03-11 12:02:12 -0800274 LOGV("extractAlbumArt");
James Dong133cf8b2011-03-11 15:18:40 -0800275 MediaScanner *mp = getNativeScanner_l(env, thiz);
276 if (mp == NULL) {
277 jniThrowException(env, kRunTimeException, "No scanner available");
278 return NULL;
279 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280
281 if (fileDescriptor == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800282 jniThrowException(env, kIllegalArgumentException, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 return NULL;
284 }
285
Elliott Hughesa3804cf2011-04-11 16:50:19 -0700286 int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 char* data = mp->extractAlbumArt(fd);
288 if (!data) {
289 return NULL;
290 }
291 long len = *((long*)data);
James Dongf3997522011-03-11 12:02:12 -0800292
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 jbyteArray array = env->NewByteArray(len);
294 if (array != NULL) {
295 jbyte* bytes = env->GetByteArrayElements(array, NULL);
296 memcpy(bytes, data + 4, len);
297 env->ReleaseByteArrayElements(array, bytes, 0);
298 }
James Dongf3997522011-03-11 12:02:12 -0800299
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300done:
301 free(data);
302 // if NewByteArray() returned NULL, an out-of-memory
303 // exception will have been raised. I just want to
304 // return null in that case.
305 env->ExceptionClear();
306 return array;
307}
308
Marco Nelissen4935d052009-08-03 11:12:58 -0700309// This function gets a field ID, which in turn causes class initialization.
310// It is called from a static block in MediaScanner, which won't run until the
311// first time an instance of this class is used.
312static void
313android_media_MediaScanner_native_init(JNIEnv *env)
314{
James Dongf3997522011-03-11 12:02:12 -0800315 LOGV("native_init");
316 jclass clazz = env->FindClass(kClassMediaScanner);
Marco Nelissen4935d052009-08-03 11:12:58 -0700317 if (clazz == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700318 return;
319 }
320
321 fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
322 if (fields.context == NULL) {
Marco Nelissen4935d052009-08-03 11:12:58 -0700323 return;
324 }
325}
326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327static void
328android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
329{
James Dongf3997522011-03-11 12:02:12 -0800330 LOGV("native_setup");
Andreas Huber8d65dd22010-06-23 16:40:57 -0700331 MediaScanner *mp = new StagefrightMediaScanner;
Andreas Huberbfb9fb12009-12-03 11:31:19 -0800332
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 if (mp == NULL) {
James Dongf3997522011-03-11 12:02:12 -0800334 jniThrowException(env, kRunTimeException, "Out of memory");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 return;
336 }
337
338 env->SetIntField(thiz, fields.context, (int)mp);
339}
340
341static void
342android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
343{
James Dongf3997522011-03-11 12:02:12 -0800344 LOGV("native_finalize");
James Dong133cf8b2011-03-11 15:18:40 -0800345 MediaScanner *mp = getNativeScanner_l(env, thiz);
James Dongf3997522011-03-11 12:02:12 -0800346 if (mp == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 return;
James Dongf3997522011-03-11 12:02:12 -0800348 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 delete mp;
James Dong133cf8b2011-03-11 15:18:40 -0800350 setNativeScanner_l(env, thiz, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351}
352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353static JNINativeMethod gMethods[] = {
James Dongf3997522011-03-11 12:02:12 -0800354 {
355 "processDirectory",
356 "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
357 (void *)android_media_MediaScanner_processDirectory
358 },
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359
James Dongf3997522011-03-11 12:02:12 -0800360 {
361 "processFile",
362 "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
363 (void *)android_media_MediaScanner_processFile
364 },
365
366 {
367 "setLocale",
368 "(Ljava/lang/String;)V",
369 (void *)android_media_MediaScanner_setLocale
370 },
371
372 {
373 "extractAlbumArt",
374 "(Ljava/io/FileDescriptor;)[B",
375 (void *)android_media_MediaScanner_extractAlbumArt
376 },
377
378 {
379 "native_init",
380 "()V",
381 (void *)android_media_MediaScanner_native_init
382 },
383
384 {
385 "native_setup",
386 "()V",
387 (void *)android_media_MediaScanner_native_setup
388 },
389
390 {
391 "native_finalize",
392 "()V",
393 (void *)android_media_MediaScanner_native_finalize
394 },
395};
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396
Marco Nelissen4935d052009-08-03 11:12:58 -0700397// This function only registers the native methods, and is called from
398// JNI_OnLoad in android_media_MediaPlayer.cpp
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399int register_android_media_MediaScanner(JNIEnv *env)
400{
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 return AndroidRuntime::registerNativeMethods(env,
James Dongf3997522011-03-11 12:02:12 -0800402 kClassMediaScanner, gMethods, NELEM(gMethods));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403}