285 lines
9.4 KiB
C++
285 lines
9.4 KiB
C++
/* //device/libs/media_jni/MediaScanner.cpp
|
|
**
|
|
** Copyright 2007, The Android Open Source Project
|
|
**
|
|
** Licensed under the Apache License, Version 2.0 (the "License");
|
|
** you may not use this file except in compliance with the License.
|
|
** You may obtain a copy of the License at
|
|
**
|
|
** http://www.apache.org/licenses/LICENSE-2.0
|
|
**
|
|
** Unless required by applicable law or agreed to in writing, software
|
|
** distributed under the License is distributed on an "AS IS" BASIS,
|
|
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
** See the License for the specific language governing permissions and
|
|
** limitations under the License.
|
|
*/
|
|
|
|
#define LOG_TAG "MediaScanner"
|
|
#include "utils/Log.h"
|
|
|
|
#include <media/mediascanner.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <utils/threads.h>
|
|
|
|
#include "jni.h"
|
|
#include "JNIHelp.h"
|
|
#include "android_runtime/AndroidRuntime.h"
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
using namespace android;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
struct fields_t {
|
|
jfieldID context;
|
|
};
|
|
static fields_t fields;
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class MyMediaScannerClient : public MediaScannerClient
|
|
{
|
|
public:
|
|
MyMediaScannerClient(JNIEnv *env, jobject client)
|
|
: mEnv(env),
|
|
mClient(env->NewGlobalRef(client)),
|
|
mScanFileMethodID(0),
|
|
mHandleStringTagMethodID(0),
|
|
mSetMimeTypeMethodID(0)
|
|
{
|
|
jclass mediaScannerClientInterface = env->FindClass("android/media/MediaScannerClient");
|
|
if (mediaScannerClientInterface == NULL) {
|
|
fprintf(stderr, "android/media/MediaScannerClient not found\n");
|
|
}
|
|
else {
|
|
mScanFileMethodID = env->GetMethodID(mediaScannerClientInterface, "scanFile",
|
|
"(Ljava/lang/String;JJ)V");
|
|
mHandleStringTagMethodID = env->GetMethodID(mediaScannerClientInterface, "handleStringTag",
|
|
"(Ljava/lang/String;Ljava/lang/String;)V");
|
|
mSetMimeTypeMethodID = env->GetMethodID(mediaScannerClientInterface, "setMimeType",
|
|
"(Ljava/lang/String;)V");
|
|
}
|
|
}
|
|
|
|
virtual ~MyMediaScannerClient()
|
|
{
|
|
mEnv->DeleteGlobalRef(mClient);
|
|
}
|
|
|
|
// returns true if it succeeded, false if an exception occured in the Java code
|
|
virtual bool scanFile(const char* path, long long lastModified, long long fileSize)
|
|
{
|
|
jstring pathStr;
|
|
if ((pathStr = mEnv->NewStringUTF(path)) == NULL) return false;
|
|
|
|
mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize);
|
|
|
|
mEnv->DeleteLocalRef(pathStr);
|
|
return (!mEnv->ExceptionCheck());
|
|
}
|
|
|
|
// returns true if it succeeded, false if an exception occured in the Java code
|
|
virtual bool handleStringTag(const char* name, const char* value)
|
|
{
|
|
jstring nameStr, valueStr;
|
|
if ((nameStr = mEnv->NewStringUTF(name)) == NULL) return false;
|
|
if ((valueStr = mEnv->NewStringUTF(value)) == NULL) return false;
|
|
|
|
mEnv->CallVoidMethod(mClient, mHandleStringTagMethodID, nameStr, valueStr);
|
|
|
|
mEnv->DeleteLocalRef(nameStr);
|
|
mEnv->DeleteLocalRef(valueStr);
|
|
return (!mEnv->ExceptionCheck());
|
|
}
|
|
|
|
// returns true if it succeeded, false if an exception occured in the Java code
|
|
virtual bool setMimeType(const char* mimeType)
|
|
{
|
|
jstring mimeTypeStr;
|
|
if ((mimeTypeStr = mEnv->NewStringUTF(mimeType)) == NULL) return false;
|
|
|
|
mEnv->CallVoidMethod(mClient, mSetMimeTypeMethodID, mimeTypeStr);
|
|
|
|
mEnv->DeleteLocalRef(mimeTypeStr);
|
|
return (!mEnv->ExceptionCheck());
|
|
}
|
|
|
|
private:
|
|
JNIEnv *mEnv;
|
|
jobject mClient;
|
|
jmethodID mScanFileMethodID;
|
|
jmethodID mHandleStringTagMethodID;
|
|
jmethodID mSetMimeTypeMethodID;
|
|
};
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
static bool ExceptionCheck(void* env)
|
|
{
|
|
return ((JNIEnv *)env)->ExceptionCheck();
|
|
}
|
|
|
|
static void
|
|
android_media_MediaScanner_processDirectory(JNIEnv *env, jobject thiz, jstring path, jstring extensions, jobject client)
|
|
{
|
|
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
|
|
|
|
if (path == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return;
|
|
}
|
|
if (extensions == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return;
|
|
}
|
|
|
|
const char *pathStr = env->GetStringUTFChars(path, NULL);
|
|
if (pathStr == NULL) { // Out of memory
|
|
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
|
|
return;
|
|
}
|
|
const char *extensionsStr = env->GetStringUTFChars(extensions, NULL);
|
|
if (extensionsStr == NULL) { // Out of memory
|
|
env->ReleaseStringUTFChars(path, pathStr);
|
|
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
|
|
return;
|
|
}
|
|
|
|
MyMediaScannerClient myClient(env, client);
|
|
mp->processDirectory(pathStr, extensionsStr, myClient, ExceptionCheck, env);
|
|
env->ReleaseStringUTFChars(path, pathStr);
|
|
env->ReleaseStringUTFChars(extensions, extensionsStr);
|
|
}
|
|
|
|
static void
|
|
android_media_MediaScanner_processFile(JNIEnv *env, jobject thiz, jstring path, jstring mimeType, jobject client)
|
|
{
|
|
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
|
|
|
|
if (path == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return;
|
|
}
|
|
|
|
const char *pathStr = env->GetStringUTFChars(path, NULL);
|
|
if (pathStr == NULL) { // Out of memory
|
|
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
|
|
return;
|
|
}
|
|
const char *mimeTypeStr = (mimeType ? env->GetStringUTFChars(mimeType, NULL) : NULL);
|
|
if (mimeType && mimeTypeStr == NULL) { // Out of memory
|
|
env->ReleaseStringUTFChars(path, pathStr);
|
|
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
|
|
return;
|
|
}
|
|
|
|
MyMediaScannerClient myClient(env, client);
|
|
mp->processFile(pathStr, mimeTypeStr, myClient);
|
|
env->ReleaseStringUTFChars(path, pathStr);
|
|
if (mimeType) {
|
|
env->ReleaseStringUTFChars(mimeType, mimeTypeStr);
|
|
}
|
|
}
|
|
|
|
static jbyteArray
|
|
android_media_MediaScanner_extractAlbumArt(JNIEnv *env, jobject thiz, jobject fileDescriptor)
|
|
{
|
|
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
|
|
|
|
if (fileDescriptor == NULL) {
|
|
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
|
|
return NULL;
|
|
}
|
|
|
|
int fd = getParcelFileDescriptorFD(env, fileDescriptor);
|
|
char* data = mp->extractAlbumArt(fd);
|
|
if (!data) {
|
|
return NULL;
|
|
}
|
|
long len = *((long*)data);
|
|
|
|
jbyteArray array = env->NewByteArray(len);
|
|
if (array != NULL) {
|
|
jbyte* bytes = env->GetByteArrayElements(array, NULL);
|
|
memcpy(bytes, data + 4, len);
|
|
env->ReleaseByteArrayElements(array, bytes, 0);
|
|
}
|
|
|
|
done:
|
|
free(data);
|
|
// if NewByteArray() returned NULL, an out-of-memory
|
|
// exception will have been raised. I just want to
|
|
// return null in that case.
|
|
env->ExceptionClear();
|
|
return array;
|
|
}
|
|
|
|
static void
|
|
android_media_MediaScanner_native_setup(JNIEnv *env, jobject thiz)
|
|
{
|
|
MediaScanner *mp = new MediaScanner();
|
|
if (mp == NULL) {
|
|
jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
|
|
return;
|
|
}
|
|
|
|
env->SetIntField(thiz, fields.context, (int)mp);
|
|
}
|
|
|
|
static void
|
|
android_media_MediaScanner_native_finalize(JNIEnv *env, jobject thiz)
|
|
{
|
|
MediaScanner *mp = (MediaScanner *)env->GetIntField(thiz, fields.context);
|
|
|
|
//printf("##### android_media_MediaScanner_native_finalize: ctx=0x%p\n", ctx);
|
|
|
|
if (mp == 0)
|
|
return;
|
|
|
|
delete mp;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
static JNINativeMethod gMethods[] = {
|
|
{"processDirectory", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
|
|
(void *)android_media_MediaScanner_processDirectory},
|
|
{"processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V",
|
|
(void *)android_media_MediaScanner_processFile},
|
|
{"extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt},
|
|
{"native_setup", "()V", (void *)android_media_MediaScanner_native_setup},
|
|
{"native_finalize", "()V", (void *)android_media_MediaScanner_native_finalize},
|
|
};
|
|
|
|
static const char* const kClassPathName = "android/media/MediaScanner";
|
|
|
|
int register_android_media_MediaScanner(JNIEnv *env)
|
|
{
|
|
jclass clazz;
|
|
|
|
clazz = env->FindClass("android/media/MediaScanner");
|
|
if (clazz == NULL) {
|
|
LOGE("Can't find android/media/MediaScanner");
|
|
return -1;
|
|
}
|
|
|
|
fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
|
|
if (fields.context == NULL) {
|
|
LOGE("Can't find MediaScanner.mNativeContext");
|
|
return -1;
|
|
}
|
|
|
|
return AndroidRuntime::registerNativeMethods(env,
|
|
"android/media/MediaScanner", gMethods, NELEM(gMethods));
|
|
}
|
|
|
|
|