android_media_MediaScanner.cpp revision f39975285874ba80bca3e8d94323c6aad3bb83da
1/*
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_NDEBUG 0
19#define LOG_TAG "MediaScannerJNI"
20#include <utils/Log.h>
21#include <utils/threads.h>
22#include <media/mediascanner.h>
23#include <media/stagefright/StagefrightMediaScanner.h>
24
25#include "jni.h"
26#include "JNIHelp.h"
27#include "android_runtime/AndroidRuntime.h"
28
29using namespace android;
30
31
32struct fields_t {
33    jfieldID    context;
34};
35static fields_t fields;
36
37static const char* const kClassMediaScannerClient =
38        "android/media/MediaScannerClient";
39
40static const char* const kClassMediaScanner =
41        "android/media/MediaScanner";
42
43static const char* const kRunTimeException =
44        "java/lang/RuntimeException";
45
46static const char* const kIllegalArgumentException =
47        "java/lang/IllegalArgumentException";
48
49class MyMediaScannerClient : public MediaScannerClient
50{
51public:
52    MyMediaScannerClient(JNIEnv *env, jobject client)
53        :   mEnv(env),
54            mClient(env->NewGlobalRef(client)),
55            mScanFileMethodID(0),
56            mHandleStringTagMethodID(0),
57            mSetMimeTypeMethodID(0)
58    {
59        LOGV("MyMediaScannerClient constructor");
60        jclass mediaScannerClientInterface =
61                env->FindClass(kClassMediaScannerClient);
62
63        if (mediaScannerClientInterface == NULL) {
64            LOGE("Class %s not found", kClassMediaScannerClient);
65        } else {
66            mScanFileMethodID = env->GetMethodID(
67                                    mediaScannerClientInterface,
68                                    "scanFile",
69                                    "(Ljava/lang/String;JJZ)V");
70
71            mHandleStringTagMethodID = env->GetMethodID(
72                                    mediaScannerClientInterface,
73                                    "handleStringTag",
74                                    "(Ljava/lang/String;Ljava/lang/String;)V");
75
76            mSetMimeTypeMethodID = env->GetMethodID(
77                                    mediaScannerClientInterface,
78                                    "setMimeType",
79                                    "(Ljava/lang/String;)V");
80
81            mAddNoMediaFolderMethodID = env->GetMethodID(
82                                    mediaScannerClientInterface,
83                                    "addNoMediaFolder",
84                                    "(Ljava/lang/String;)V");
85        }
86    }
87
88    virtual ~MyMediaScannerClient()
89    {
90        LOGV("MyMediaScannerClient destructor");
91        mEnv->DeleteGlobalRef(mClient);
92    }
93
94    // Returns true if it succeeded, false if an exception occured
95    // in the Java code
96    virtual bool scanFile(const char* path, long long lastModified,
97            long long fileSize, bool isDirectory)
98    {
99        LOGV("scanFile: path(%s), time(%lld), size(%lld) and isDir(%d)",
100            path, lastModified, fileSize, isDirectory);
101
102        jstring pathStr;
103        if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
104            return false;
105        }
106
107        mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified,
108                fileSize, isDirectory);
109
110        mEnv->DeleteLocalRef(pathStr);
111        return (!mEnv->ExceptionCheck());
112    }
113
114    // Returns true if it succeeded, false if an exception occured
115    // in the Java code
116    virtual bool handleStringTag(const char* name, const char* value)
117    {
118        LOGV("handleStringTag: name(%s) and value(%s)", name, value);
119        jstring nameStr, valueStr;
120        if ((nameStr = mEnv->NewStringUTF(name)) == NULL) {
121            return false;
122        }
123        if ((valueStr = mEnv->NewStringUTF(value)) == NULL) {
124            return false;
125        }
126
127        mEnv->CallVoidMethod(
128            mClient, mHandleStringTagMethodID, nameStr, valueStr);
129
130        mEnv->DeleteLocalRef(nameStr);
131        mEnv->DeleteLocalRef(valueStr);
132        return (!mEnv->ExceptionCheck());
133    }
134
135    // Returns true if it succeeded, false if an exception occured
136    // in the Java code
137    virtual bool setMimeType(const char* mimeType)
138    {
139        LOGV("setMimeType: %s", mimeType);
140        jstring mimeTypeStr;
141        if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) {
142            return false;
143        }
144
145        mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
146
147        mEnv->DeleteLocalRef(mimeTypeStr);
148        return (!mEnv->ExceptionCheck());
149    }
150
151    // Returns true if it succeeded, false if an exception occured
152    // in the Java code
153    virtual bool addNoMediaFolder(const char* path)
154    {
155        LOGV("addNoMediaFolder: path(%s)", path);
156        jstring pathStr;
157        if ((pathStr = mEnv->NewStringUTF(path)) == NULL) {
158            return false;
159        }
160
161        mEnv->CallVoidMethod(mClient, mAddNoMediaFolderMethodID, pathStr);
162
163        mEnv->DeleteLocalRef(pathStr);
164        return (!mEnv->ExceptionCheck());
165    }
166
167
168private:
169    JNIEnv *mEnv;
170    jobject mClient;
171    jmethodID mScanFileMethodID;
172    jmethodID mHandleStringTagMethodID;
173    jmethodID mSetMimeTypeMethodID;
174    jmethodID mAddNoMediaFolderMethodID;
175};
176
177
178static bool ExceptionCheck(void* env)
179{
180    LOGV("ExceptionCheck");
181    return ((JNIEnv *)env)->ExceptionCheck();
182}
183
184static void
185android_media_MediaScanner_processDirectory(
186        JNIEnv *env, jobject thiz, jstring path, jobject client)
187{
188    LOGV("processDirectory");
189    MediaScanner *mp =
190        (MediaScanner *)env->GetIntField(thiz, fields.context);
191
192    if (path == NULL) {
193        jniThrowException(env, kIllegalArgumentException, NULL);
194        return;
195    }
196
197    const char *pathStr = env->GetStringUTFChars(path, NULL);
198    if (pathStr == NULL) {  // Out of memory
199        jniThrowException(env, kRunTimeException, "Out of memory");
200        return;
201    }
202
203    MyMediaScannerClient myClient(env, client);
204    mp->processDirectory(pathStr, myClient, ExceptionCheck, env);
205    env->ReleaseStringUTFChars(path, pathStr);
206}
207
208static void
209android_media_MediaScanner_processFile(
210        JNIEnv *env, jobject thiz, jstring path,
211        jstring mimeType, jobject client)
212{
213    LOGV("processFile");
214    MediaScanner *mp =
215        (MediaScanner *)env->GetIntField(thiz, fields.context);
216
217    if (path == NULL) {
218        jniThrowException(env, kIllegalArgumentException, NULL);
219        return;
220    }
221
222    const char *pathStr = env->GetStringUTFChars(path, NULL);
223    if (pathStr == NULL) {  // Out of memory
224        jniThrowException(env, kRunTimeException, "Out of memory");
225        return;
226    }
227
228    const char *mimeTypeStr =
229        (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
230    if (mimeType && mimeTypeStr == NULL) {  // Out of memory
231        env->ReleaseStringUTFChars(path, pathStr);
232        jniThrowException(env, kRunTimeException, "Out of memory");
233        return;
234    }
235
236    MyMediaScannerClient myClient(env, client);
237    mp->processFile(pathStr, mimeTypeStr, myClient);
238    env->ReleaseStringUTFChars(path, pathStr);
239    if (mimeType) {
240        env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
241    }
242}
243
244static void
245android_media_MediaScanner_setLocale(
246        JNIEnv *env, jobject thiz, jstring locale)
247{
248    LOGV("setLocale");
249    MediaScanner *mp =
250        (MediaScanner *)env->GetIntField(thiz, fields.context);
251
252    if (locale == NULL) {
253        jniThrowException(env, kIllegalArgumentException, NULL);
254        return;
255    }
256    const char *localeStr = env->GetStringUTFChars(locale, NULL);
257    if (localeStr == NULL) {  // Out of memory
258        jniThrowException(env, kRunTimeException, "Out of memory");
259        return;
260    }
261    mp->setLocale(localeStr);
262
263    env->ReleaseStringUTFChars(locale, localeStr);
264}
265
266static jbyteArray
267android_media_MediaScanner_extractAlbumArt(
268        JNIEnv *env, jobject thiz, jobject fileDescriptor)
269{
270    LOGV("extractAlbumArt");
271    MediaScanner *mp =
272        (MediaScanner *)env->GetIntField(thiz, fields.context);
273
274    if (fileDescriptor == NULL) {
275        jniThrowException(env, kIllegalArgumentException, NULL);
276        return NULL;
277    }
278
279    int fd = getParcelFileDescriptorFD(env, fileDescriptor);
280    char* data = mp->extractAlbumArt(fd);
281    if (!data) {
282        return NULL;
283    }
284    long len = *((long*)data);
285
286    jbyteArray array = env->NewByteArray(len);
287    if (array != NULL) {
288        jbyte* bytes = env->GetByteArrayElements(array, NULL);
289        memcpy(bytes, data + 4, len);
290        env->ReleaseByteArrayElements(array, bytes, 0);
291    }
292
293done:
294    free(data);
295    // if NewByteArray() returned NULL, an out-of-memory
296    // exception will have been raised. I just want to
297    // return null in that case.
298    env->ExceptionClear();
299    return array;
300}
301
302// This function gets a field ID, which in turn causes class initialization.
303// It is called from a static block in MediaScanner, which won't run until the
304// first time an instance of this class is used.
305static void
306android_media_MediaScanner_native_init(JNIEnv *env)
307{
308    LOGV("native_init");
309    jclass clazz = env->FindClass(kClassMediaScanner);
310    if (clazz == NULL) {
311        const char* err = "Can't find android/media/MediaScanner";
312        jniThrowException(env, kRunTimeException, err);
313        return;
314    }
315
316    fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
317    if (fields.context == NULL) {
318        const char* err = "Can't find MediaScanner.mNativeContext";
319        jniThrowException(env, kRunTimeException, err);
320        return;
321    }
322}
323
324static void
325android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
326{
327    LOGV("native_setup");
328    MediaScanner *mp = new StagefrightMediaScanner;
329
330    if (mp == NULL) {
331        jniThrowException(env, kRunTimeException, "Out of memory");
332        return;
333    }
334
335    env->SetIntField(thiz, fields.context, (int)mp);
336}
337
338static void
339android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
340{
341    LOGV("native_finalize");
342    MediaScanner *mp =
343        (MediaScanner *)env->GetIntField(thiz, fields.context);
344
345    if (mp == 0) {
346        return;
347    }
348
349    delete mp;
350}
351
352static JNINativeMethod gMethods[] = {
353    {
354        "processDirectory",
355        "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
356        (void *)android_media_MediaScanner_processDirectory
357    },
358
359    {
360        "processFile",
361        "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
362        (void *)android_media_MediaScanner_processFile
363    },
364
365    {
366        "setLocale",
367        "(Ljava/lang/String;)V",
368        (void *)android_media_MediaScanner_setLocale
369    },
370
371    {
372        "extractAlbumArt",
373        "(Ljava/io/FileDescriptor;)[B",
374        (void *)android_media_MediaScanner_extractAlbumArt
375    },
376
377    {
378        "native_init",
379        "()V",
380        (void *)android_media_MediaScanner_native_init
381    },
382
383    {
384        "native_setup",
385        "()V",
386        (void *)android_media_MediaScanner_native_setup
387    },
388
389    {
390        "native_finalize",
391        "()V",
392        (void *)android_media_MediaScanner_native_finalize
393    },
394};
395
396// This function only registers the native methods, and is called from
397// JNI_OnLoad in android_media_MediaPlayer.cpp
398int register_android_media_MediaScanner(JNIEnv *env)
399{
400    return AndroidRuntime::registerNativeMethods(env,
401                kClassMediaScanner, gMethods, NELEM(gMethods));
402}
403
404
405