diff options
Diffstat (limited to 'media/jni')
-rw-r--r-- | media/jni/Android.mk | 14 | ||||
-rw-r--r-- | media/jni/android_media_MediaPlayer.cpp | 24 | ||||
-rw-r--r-- | media/jni/android_media_MtpClient.cpp | 233 | ||||
-rw-r--r-- | media/jni/android_media_MtpCursor.cpp | 136 | ||||
-rw-r--r-- | media/jni/android_media_MtpDatabase.cpp | 463 | ||||
-rw-r--r-- | media/jni/android_media_MtpServer.cpp | 223 |
6 files changed, 1090 insertions, 3 deletions
diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 6eec215..653532c 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -12,7 +12,11 @@ LOCAL_SRC_FILES:= \ android_media_MediaMetadataRetriever.cpp \ android_media_ResampleInputStream.cpp \ android_media_MediaProfiles.cpp \ - android_media_AmrInputStream.cpp + android_media_AmrInputStream.cpp \ + android_media_MtpClient.cpp \ + android_media_MtpCursor.cpp \ + android_media_MtpDatabase.cpp \ + android_media_MtpServer.cpp \ LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ @@ -25,7 +29,8 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libsurfaceflinger_client \ libstagefright \ - libcamera_client + libcamera_client \ + libsqlite ifneq ($(BUILD_WITHOUT_PV),true) @@ -35,7 +40,9 @@ else LOCAL_CFLAGS += -DNO_OPENCORE endif -LOCAL_STATIC_LIBRARIES := +ifneq ($(TARGET_SIMULATOR),true) +LOCAL_STATIC_LIBRARIES := libmtp libusbhost +endif LOCAL_C_INCLUDES += \ external/tremor/Tremor \ @@ -44,6 +51,7 @@ LOCAL_C_INCLUDES += \ frameworks/base/media/libstagefright/codecs/amrnb/enc/src \ frameworks/base/media/libstagefright/codecs/amrnb/common \ frameworks/base/media/libstagefright/codecs/amrnb/common/include \ + frameworks/base/media/mtp \ $(PV_INCLUDES) \ $(JNI_H_INCLUDE) \ $(call include-path-for, corecg graphics) diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 474a174..6710db0 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -788,6 +788,10 @@ extern int register_android_media_MediaRecorder(JNIEnv *env); extern int register_android_media_MediaScanner(JNIEnv *env); extern int register_android_media_ResampleInputStream(JNIEnv *env); extern int register_android_media_MediaProfiles(JNIEnv *env); +extern int register_android_media_MtpClient(JNIEnv *env); +extern int register_android_media_MtpCursor(JNIEnv *env); +extern int register_android_media_MtpDatabase(JNIEnv *env); +extern int register_android_media_MtpServer(JNIEnv *env); #ifndef NO_OPENCORE extern int register_android_media_AmrInputStream(JNIEnv *env); @@ -841,6 +845,26 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) goto bail; } + if (register_android_media_MtpClient(env) < 0) { + LOGE("ERROR: MtpClient native registration failed"); + goto bail; + } + + if (register_android_media_MtpCursor(env) < 0) { + LOGE("ERROR: MtpCursor native registration failed"); + goto bail; + } + + if (register_android_media_MtpDatabase(env) < 0) { + LOGE("ERROR: MtpDatabase native registration failed"); + goto bail; + } + + if (register_android_media_MtpServer(env) < 0) { + LOGE("ERROR: MtpServer native registration failed"); + goto bail; + } + /* success -- return valid version number */ result = JNI_VERSION_1_4; diff --git a/media/jni/android_media_MtpClient.cpp b/media/jni/android_media_MtpClient.cpp new file mode 100644 index 0000000..f69053c --- /dev/null +++ b/media/jni/android_media_MtpClient.cpp @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2010 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 "MtpClientJNI" +#include "utils/Log.h" + +#include <stdio.h> +#include <assert.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include "MtpClient.h" +#include "MtpDevice.h" + +using namespace android; + +// ---------------------------------------------------------------------------- + +static jmethodID method_deviceAdded; +static jmethodID method_deviceRemoved; +static jfieldID field_context; + +#ifdef HAVE_ANDROID_OS + +static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + LOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +class MyClient : public MtpClient { +private: + virtual void deviceAdded(MtpDevice *device); + virtual void deviceRemoved(MtpDevice *device); + + jobject mClient; + MtpDevice* mEventDevice; + +public: + MyClient(JNIEnv *env, jobject client); + void cleanup(JNIEnv *env); +}; + +MtpClient* get_client_from_object(JNIEnv* env, jobject javaClient) +{ + return (MtpClient*)env->GetIntField(javaClient, field_context); +} + + +MyClient::MyClient(JNIEnv *env, jobject client) + : mClient(env->NewGlobalRef(client)) +{ +} + +void MyClient::cleanup(JNIEnv *env) { + env->DeleteGlobalRef(mClient); +} + +void MyClient::deviceAdded(MtpDevice *device) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + const char* name = device->getDeviceName(); + LOGD("MyClient::deviceAdded %s\n", name); + + env->CallVoidMethod(mClient, method_deviceAdded, device->getID()); + + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +void MyClient::deviceRemoved(MtpDevice *device) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + const char* name = device->getDeviceName(); + LOGD("MyClient::deviceRemoved %s\n", name); + + env->CallVoidMethod(mClient, method_deviceRemoved, device->getID()); + + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +#endif // HAVE_ANDROID_OS + +// ---------------------------------------------------------------------------- + +static void +android_media_MtpClient_setup(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("setup\n"); + MyClient* client = new MyClient(env, thiz); + client->start(); + env->SetIntField(thiz, field_context, (int)client); +#endif +} + +static void +android_media_MtpClient_finalize(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("finalize\n"); + MyClient *client = (MyClient *)env->GetIntField(thiz, field_context); + client->cleanup(env); + delete client; + env->SetIntField(thiz, field_context, 0); +#endif +} + +static jboolean +android_media_MtpClient_start(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("start\n"); + MyClient *client = (MyClient *)env->GetIntField(thiz, field_context); + return client->start(); +#else + return false; +#endif +} + +static void +android_media_MtpClient_stop(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("stop\n"); + MyClient *client = (MyClient *)env->GetIntField(thiz, field_context); + client->stop(); +#endif +} + +static jboolean +android_media_MtpClient_delete_object(JNIEnv *env, jobject thiz, + jint device_id, jint object_id) +{ +#ifdef HAVE_ANDROID_OS + MyClient *client = (MyClient *)env->GetIntField(thiz, field_context); + MtpDevice* device = client->getDevice(device_id); + if (device) + return device->deleteObject(object_id); + else + #endif + return NULL; +} + +static jint +android_media_MtpClient_get_parent(JNIEnv *env, jobject thiz, + jint device_id, jint object_id) +{ +#ifdef HAVE_ANDROID_OS + MyClient *client = (MyClient *)env->GetIntField(thiz, field_context); + MtpDevice* device = client->getDevice(device_id); + if (device) + return device->getParent(object_id); + else +#endif + return -1; +} + +static jint +android_media_MtpClient_get_storage_id(JNIEnv *env, jobject thiz, + jint device_id, jint object_id) +{ + #ifdef HAVE_ANDROID_OS + MyClient *client = (MyClient *)env->GetIntField(thiz, field_context); + MtpDevice* device = client->getDevice(device_id); + if (device) + return device->getStorageID(object_id); + else +#endif + return -1; +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"native_setup", "()V", (void *)android_media_MtpClient_setup}, + {"native_finalize", "()V", (void *)android_media_MtpClient_finalize}, + {"native_start", "()Z", (void *)android_media_MtpClient_start}, + {"native_stop", "()V", (void *)android_media_MtpClient_stop}, + {"native_delete_object", "(II)Z", (void *)android_media_MtpClient_delete_object}, + {"native_get_parent", "(II)I", (void *)android_media_MtpClient_get_parent}, + {"native_get_storage_id", "(II)I", (void *)android_media_MtpClient_get_storage_id}, +}; + +static const char* const kClassPathName = "android/media/MtpClient"; + +int register_android_media_MtpClient(JNIEnv *env) +{ + jclass clazz; + + LOGD("register_android_media_MtpClient\n"); + + clazz = env->FindClass("android/media/MtpClient"); + if (clazz == NULL) { + LOGE("Can't find android/media/MtpClient"); + return -1; + } + method_deviceAdded = env->GetMethodID(clazz, "deviceAdded", "(I)V"); + if (method_deviceAdded == NULL) { + LOGE("Can't find deviceAdded"); + return -1; + } + method_deviceRemoved = env->GetMethodID(clazz, "deviceRemoved", "(I)V"); + if (method_deviceRemoved == NULL) { + LOGE("Can't find deviceRemoved"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find MtpClient.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/media/MtpClient", gMethods, NELEM(gMethods)); +} diff --git a/media/jni/android_media_MtpCursor.cpp b/media/jni/android_media_MtpCursor.cpp new file mode 100644 index 0000000..6228b5d --- /dev/null +++ b/media/jni/android_media_MtpCursor.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2010 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 "MtpCursorJNI" +#include "utils/Log.h" + +#include <stdio.h> +#include <assert.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" +#include "binder/CursorWindow.h" + +#include "MtpClient.h" +#include "MtpCursor.h" + +using namespace android; + +// ---------------------------------------------------------------------------- + +static jfieldID field_context; + +// From android_media_MtpClient.cpp +MtpClient * get_client_from_object(JNIEnv * env, jobject javaClient); + +// ---------------------------------------------------------------------------- + +static bool ExceptionCheck(void* env) +{ + return ((JNIEnv *)env)->ExceptionCheck(); +} + +static void +android_media_MtpCursor_setup(JNIEnv *env, jobject thiz, jobject javaClient, + jint queryType, jint deviceID, jint storageID, jint objectID, jintArray javaColumns) +{ +#ifdef HAVE_ANDROID_OS + LOGD("android_media_MtpCursor_setup queryType: %d deviceID: %d storageID: %d objectID: %d\n", + queryType, deviceID, storageID, objectID); + + int* columns = NULL; + int columnCount = 0; + if (javaColumns) { + columns = env->GetIntArrayElements(javaColumns, 0); + columnCount = env->GetArrayLength(javaColumns); + } + + MtpClient* client = get_client_from_object(env, javaClient); + MtpCursor* cursor = new MtpCursor(client, queryType, + deviceID, storageID, objectID, columnCount, columns); + + if (columns) + env->ReleaseIntArrayElements(javaColumns, columns, 0); + env->SetIntField(thiz, field_context, (int)cursor); +#endif +} + +static void +android_media_MtpCursor_finalize(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("finalize\n"); + MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context); + delete cursor; +#endif +} + +static jint +android_media_MtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindow, jint startPos) +{ +#ifdef HAVE_ANDROID_OS + CursorWindow* window = get_window_from_object(env, javaWindow); + if (!window) { + LOGE("Invalid CursorWindow"); + jniThrowException(env, "java/lang/IllegalArgumentException", + "Bad CursorWindow"); + return 0; + } + MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context); + + return cursor->fillWindow(window, startPos); +#else + return 0; +#endif +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"native_setup", "(Landroid/media/MtpClient;IIII[I)V", + (void *)android_media_MtpCursor_setup}, + {"native_finalize", "()V", (void *)android_media_MtpCursor_finalize}, + {"native_fill_window", "(Landroid/database/CursorWindow;I)I", + (void *)android_media_MtpCursor_fill_window}, + +}; + +static const char* const kClassPathName = "android/media/MtpCursor"; + +int register_android_media_MtpCursor(JNIEnv *env) +{ + jclass clazz; + + LOGD("register_android_media_MtpCursor\n"); + + clazz = env->FindClass("android/media/MtpCursor"); + if (clazz == NULL) { + LOGE("Can't find android/media/MtpCursor"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find MtpCursor.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/media/MtpCursor", gMethods, NELEM(gMethods)); +} diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp new file mode 100644 index 0000000..be59362 --- /dev/null +++ b/media/jni/android_media_MtpDatabase.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (C) 2010 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 "MtpDatabaseJNI" +#include "utils/Log.h" + +#include <stdio.h> +#include <assert.h> +#include <limits.h> +#include <unistd.h> +#include <fcntl.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include "MtpDatabase.h" +#include "MtpDataPacket.h" +#include "MtpUtils.h" +#include "mtp.h" + +using namespace android; + +// ---------------------------------------------------------------------------- + +static jmethodID method_beginSendObject; +static jmethodID method_endSendObject; +static jmethodID method_getObjectList; +static jmethodID method_getObjectProperty; +static jmethodID method_getObjectInfo; +static jmethodID method_getObjectFilePath; +static jmethodID method_deleteFile; +static jfieldID field_context; + +MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database) { + return (MtpDatabase *)env->GetIntField(database, field_context); +} + +#ifdef HAVE_ANDROID_OS +// ---------------------------------------------------------------------------- + +class MyMtpDatabase : public MtpDatabase { +private: + jobject mDatabase; + jintArray mIntBuffer; + jlongArray mLongBuffer; + jcharArray mStringBuffer; + +public: + MyMtpDatabase(JNIEnv *env, jobject client); + virtual ~MyMtpDatabase(); + void cleanup(JNIEnv *env); + + virtual MtpObjectHandle beginSendObject(const char* path, + MtpObjectFormat format, + MtpObjectHandle parent, + MtpStorageID storage, + uint64_t size, + time_t modified); + + virtual void endSendObject(const char* path, + MtpObjectHandle handle, + MtpObjectFormat format, + bool succeeded); + + virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID, + MtpObjectFormat format, + MtpObjectHandle parent); + + virtual MtpResponseCode getObjectProperty(MtpObjectHandle handle, + MtpObjectProperty property, + MtpDataPacket& packet); + + virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle, + MtpDataPacket& packet); + + virtual bool getObjectFilePath(MtpObjectHandle handle, + MtpString& filePath, + int64_t& fileLength); + virtual bool deleteFile(MtpObjectHandle handle); + + // helper for media scanner + virtual MtpObjectHandle* getFileList(int& outCount); + + virtual void beginTransaction(); + virtual void commitTransaction(); + virtual void rollbackTransaction(); + + bool getPropertyInfo(MtpObjectProperty property, int& type); +}; + +MyMtpDatabase::MyMtpDatabase(JNIEnv *env, jobject client) + : mDatabase(env->NewGlobalRef(client)), + mIntBuffer(NULL), + mLongBuffer(NULL), + mStringBuffer(NULL) +{ + jintArray intArray; + jlongArray longArray; + jcharArray charArray; + + // create buffers for out arguments + // we don't need to be thread-safe so this is OK + intArray = env->NewIntArray(3); + if (!intArray) + goto out_of_memory; + mIntBuffer = (jintArray)env->NewGlobalRef(intArray); + longArray = env->NewLongArray(2); + if (!longArray) + goto out_of_memory; + mLongBuffer = (jlongArray)env->NewGlobalRef(longArray); + charArray = env->NewCharArray(256); + if (!charArray) + goto out_of_memory; + mStringBuffer = (jcharArray)env->NewGlobalRef(charArray); + return; + +out_of_memory: + env->ThrowNew(env->FindClass("java/lang/OutOfMemoryError"), NULL); +} + +void MyMtpDatabase::cleanup(JNIEnv *env) { + env->DeleteGlobalRef(mDatabase); + env->DeleteGlobalRef(mIntBuffer); + env->DeleteGlobalRef(mLongBuffer); + env->DeleteGlobalRef(mStringBuffer); +} + +MyMtpDatabase::~MyMtpDatabase() { +} + +MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path, + MtpObjectFormat format, + MtpObjectHandle parent, + MtpStorageID storage, + uint64_t size, + time_t modified) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + return env->CallIntMethod(mDatabase, method_beginSendObject, env->NewStringUTF(path), + (jint)format, (jint)parent, (jint)storage, (jlong)size, (jlong)modified); +} + +void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle, + MtpObjectFormat format, bool succeeded) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mDatabase, method_endSendObject, env->NewStringUTF(path), + (jint)handle, (jint)format, (jboolean)succeeded); +} + +MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID, + MtpObjectFormat format, + MtpObjectHandle parent) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jintArray array = (jintArray)env->CallObjectMethod(mDatabase, method_getObjectList, + (jint)storageID, (jint)format, (jint)parent); + if (!array) + return NULL; + MtpObjectHandleList* list = new MtpObjectHandleList(); + jint* handles = env->GetIntArrayElements(array, 0); + jsize length = env->GetArrayLength(array); +LOGD("getObjectList length: %d", length); + for (int i = 0; i < length; i++) { +LOGD("push: %d", handles[i]); + list->push(handles[i]); + } + env->ReleaseIntArrayElements(array, handles, 0); + return list; +} + +MtpResponseCode MyMtpDatabase::getObjectProperty(MtpObjectHandle handle, + MtpObjectProperty property, + MtpDataPacket& packet) { + int type; + + if (!getPropertyInfo(property, type)) + return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE; + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jint result = env->CallIntMethod(mDatabase, method_getObjectProperty, + (jint)handle, (jint)property, mLongBuffer, mStringBuffer); + if (result != MTP_RESPONSE_OK) + return result; + + jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); + jlong longValue = longValues[0]; + env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); + + switch (type) { + case MTP_TYPE_INT8: + packet.putInt8(longValue); + break; + case MTP_TYPE_UINT8: + packet.putUInt8(longValue); + break; + case MTP_TYPE_INT16: + packet.putInt16(longValue); + break; + case MTP_TYPE_UINT16: + packet.putUInt16(longValue); + break; + case MTP_TYPE_INT32: + packet.putInt32(longValue); + break; + case MTP_TYPE_UINT32: + packet.putUInt32(longValue); + break; + case MTP_TYPE_INT64: + packet.putInt64(longValue); + break; + case MTP_TYPE_UINT64: + packet.putUInt64(longValue); + break; + case MTP_TYPE_STR: + { + jchar* str = env->GetCharArrayElements(mStringBuffer, 0); + packet.putString(str); + env->ReleaseCharArrayElements(mStringBuffer, str, 0); + break; + } + default: + LOGE("unsupported object type\n"); + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + } + return MTP_RESPONSE_OK; +} + +MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, + MtpDataPacket& packet) { + char date[20]; + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectInfo, + (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer); + if (!result) + return MTP_RESPONSE_INVALID_OBJECT_HANDLE; + + jint* intValues = env->GetIntArrayElements(mIntBuffer, 0); + MtpStorageID storageID = intValues[0]; + MtpObjectFormat format = intValues[1]; + MtpObjectHandle parent = intValues[2]; + env->ReleaseIntArrayElements(mIntBuffer, intValues, 0); + + jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); + uint64_t size = longValues[0]; + uint64_t modified = longValues[1]; + env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); + + int associationType = (format == MTP_FORMAT_ASSOCIATION ? + MTP_ASSOCIATION_TYPE_GENERIC_FOLDER : + MTP_ASSOCIATION_TYPE_UNDEFINED); + + packet.putUInt32(storageID); + packet.putUInt16(format); + packet.putUInt16(0); // protection status + packet.putUInt32((size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size)); + packet.putUInt16(0); // thumb format + packet.putUInt32(0); // thumb compressed size + packet.putUInt32(0); // thumb pix width + packet.putUInt32(0); // thumb pix height + packet.putUInt32(0); // image pix width + packet.putUInt32(0); // image pix height + packet.putUInt32(0); // image bit depth + packet.putUInt32(parent); + packet.putUInt16(associationType); + packet.putUInt32(0); // association desc + packet.putUInt32(0); // sequence number + + jchar* str = env->GetCharArrayElements(mStringBuffer, 0); + packet.putString(str); // file name + env->ReleaseCharArrayElements(mStringBuffer, str, 0); + + packet.putEmptyString(); + formatDateTime(modified, date, sizeof(date)); + packet.putString(date); // date modified + packet.putEmptyString(); // keywords + + return MTP_RESPONSE_OK; +} + +bool MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, + MtpString& filePath, + int64_t& fileLength) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectFilePath, + (jint)handle, mStringBuffer, mLongBuffer); + if (!result) + return false; + + jchar* str = env->GetCharArrayElements(mStringBuffer, 0); + filePath.setTo(str, strlen16(str)); + env->ReleaseCharArrayElements(mStringBuffer, str, 0); + + jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); + fileLength = longValues[0]; + env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); + + return true; +} + +bool MyMtpDatabase::deleteFile(MtpObjectHandle handle) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + return env->CallBooleanMethod(mDatabase, method_deleteFile, (jint)handle); +} + + // helper for media scanner +MtpObjectHandle* MyMtpDatabase::getFileList(int& outCount) { + // REMOVE ME + return NULL; +} + +void MyMtpDatabase::beginTransaction() { + // REMOVE ME +} + +void MyMtpDatabase::commitTransaction() { + // REMOVE ME +} + +void MyMtpDatabase::rollbackTransaction() { + // REMOVE ME +} + +struct PropertyTableEntry { + MtpObjectProperty property; + int type; +}; + +static const PropertyTableEntry kPropertyTable[] = { + { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 }, + { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT32 }, + { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR }, + { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 }, + { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR }, +}; + +bool MyMtpDatabase::getPropertyInfo(MtpObjectProperty property, int& type) { + int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]); + const PropertyTableEntry* entry = kPropertyTable; + for (int i = 0; i < count; i++, entry++) { + if (entry->property == property) { + type = entry->type; + return true; + } + } + return false; +} + +// ---------------------------------------------------------------------------- + +static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + LOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +#endif // HAVE_ANDROID_OS + +// ---------------------------------------------------------------------------- + +static void +android_media_MtpDatabase_setup(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("setup\n"); + MyMtpDatabase* database = new MyMtpDatabase(env, thiz); + env->SetIntField(thiz, field_context, (int)database); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +#endif +} + +static void +android_media_MtpDatabase_finalize(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("finalize\n"); + MyMtpDatabase* database = (MyMtpDatabase *)env->GetIntField(thiz, field_context); + database->cleanup(env); + delete database; + env->SetIntField(thiz, field_context, 0); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +#endif +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"native_setup", "()V", (void *)android_media_MtpDatabase_setup}, + {"native_finalize", "()V", (void *)android_media_MtpDatabase_finalize}, +}; + +static const char* const kClassPathName = "android/media/MtpDatabase"; + +int register_android_media_MtpDatabase(JNIEnv *env) +{ + jclass clazz; + + LOGD("register_android_media_MtpDatabase\n"); + + clazz = env->FindClass("android/media/MtpDatabase"); + if (clazz == NULL) { + LOGE("Can't find android/media/MtpDatabase"); + return -1; + } + method_beginSendObject = env->GetMethodID(clazz, "beginSendObject", "(Ljava/lang/String;IIIJJ)I"); + if (method_beginSendObject == NULL) { + LOGE("Can't find beginSendObject"); + return -1; + } + method_endSendObject = env->GetMethodID(clazz, "endSendObject", "(Ljava/lang/String;IIZ)V"); + if (method_endSendObject == NULL) { + LOGE("Can't find endSendObject"); + return -1; + } + method_getObjectList = env->GetMethodID(clazz, "getObjectList", "(III)[I"); + if (method_getObjectList == NULL) { + LOGE("Can't find getObjectList"); + return -1; + } + method_getObjectProperty = env->GetMethodID(clazz, "getObjectProperty", "(II[J[C)I"); + if (method_getObjectProperty == NULL) { + LOGE("Can't find getObjectProperty"); + return -1; + } + method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z"); + if (method_getObjectInfo == NULL) { + LOGE("Can't find getObjectInfo"); + return -1; + } + method_getObjectFilePath = env->GetMethodID(clazz, "getObjectFilePath", "(I[C[J)Z"); + if (method_getObjectFilePath == NULL) { + LOGE("Can't find getObjectFilePath"); + return -1; + } + method_deleteFile = env->GetMethodID(clazz, "deleteFile", "(I)Z"); + if (method_deleteFile == NULL) { + LOGE("Can't find deleteFile"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find MtpDatabase.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/media/MtpDatabase", gMethods, NELEM(gMethods)); +} diff --git a/media/jni/android_media_MtpServer.cpp b/media/jni/android_media_MtpServer.cpp new file mode 100644 index 0000000..eddad57 --- /dev/null +++ b/media/jni/android_media_MtpServer.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2010 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 "MtpServerJNI" +#include "utils/Log.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" +#include "private/android_filesystem_config.h" + +#include "MtpServer.h" + +using namespace android; + +// ---------------------------------------------------------------------------- + +static jfieldID field_context; + +// in android_media_MtpDatabase.cpp +extern MtpDatabase* getMtpDatabase(JNIEnv *env, jobject database); + +// ---------------------------------------------------------------------------- + +#ifdef HAVE_ANDROID_OS + +static bool ExceptionCheck(void* env) +{ + return ((JNIEnv *)env)->ExceptionCheck(); +} + +class MtpThread : public Thread { +private: + MtpDatabase* mDatabase; + MtpServer* mServer; + String8 mStoragePath; + bool mDone; + Mutex mMutex; + +public: + MtpThread(MtpDatabase* database, const char* storagePath) + : mDatabase(database), mServer(NULL), mStoragePath(storagePath), mDone(false) + { + } + + virtual bool threadLoop() { + int fd = open("/dev/mtp_usb", O_RDWR); + printf("open returned %d\n", fd); + if (fd < 0) { + LOGE("could not open MTP driver\n"); + return false; + } + + mMutex.lock(); + mServer = new MtpServer(fd, mDatabase, AID_SDCARD_RW, 0664, 0775); + mServer->addStorage(mStoragePath); + mMutex.unlock(); + + LOGD("MtpThread mServer->run"); + mServer->run(); + close(fd); + + mMutex.lock(); + delete mServer; + mServer = NULL; + mMutex.unlock(); + + bool done = mDone; + if (done) + delete this; + LOGD("threadLoop returning %s", (done ? "false" : "true")); + return !done; + } + + void setDone() { mDone = true; } + + void sendObjectAdded(MtpObjectHandle handle) { + mMutex.lock(); + if (mServer) + mServer->sendObjectAdded(handle); + else + LOGE("sendObjectAdded called while disconnected\n"); + mMutex.unlock(); + } + + void sendObjectRemoved(MtpObjectHandle handle) { + mMutex.lock(); + if (mServer) + mServer->sendObjectRemoved(handle); + else + LOGE("sendObjectRemoved called while disconnected\n"); + mMutex.unlock(); + } +}; + +#endif // HAVE_ANDROID_OS + +static void +android_media_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase, jstring storagePath) +{ +#ifdef HAVE_ANDROID_OS + LOGD("setup\n"); + + MtpDatabase* database = getMtpDatabase(env, javaDatabase); + const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL); + + MtpThread* thread = new MtpThread(database, storagePathStr); + env->SetIntField(thiz, field_context, (int)thread); + + env->ReleaseStringUTFChars(storagePath, storagePathStr); +#endif +} + +static void +android_media_MtpServer_finalize(JNIEnv *env, jobject thiz) +{ + LOGD("finalize\n"); +} + + +static void +android_media_MtpServer_start(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("start\n"); + MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context); + thread->run("MtpThread"); +#endif // HAVE_ANDROID_OS +} + +static void +android_media_MtpServer_stop(JNIEnv *env, jobject thiz) +{ +#ifdef HAVE_ANDROID_OS + LOGD("stop\n"); + MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context); + if (thread) { + thread->setDone(); + env->SetIntField(thiz, field_context, 0); + } +#endif +} + +static void +android_media_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle) +{ +#ifdef HAVE_ANDROID_OS + LOGD("send_object_added %d\n", handle); + MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context); + if (thread) + thread->sendObjectAdded(handle); + else + LOGE("sendObjectAdded called while disconnected\n"); +#endif +} + +static void +android_media_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle) +{ +#ifdef HAVE_ANDROID_OS + LOGD("send_object_removed %d\n", handle); + MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context); + if (thread) + thread->sendObjectRemoved(handle); + else + LOGE("sendObjectRemoved called while disconnected\n"); +#endif +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"native_setup", "(Landroid/media/MtpDatabase;Ljava/lang/String;)V", + (void *)android_media_MtpServer_setup}, + {"native_finalize", "()V", (void *)android_media_MtpServer_finalize}, + {"native_start", "()V", (void *)android_media_MtpServer_start}, + {"native_stop", "()V", (void *)android_media_MtpServer_stop}, + {"native_send_object_added", "(I)V", (void *)android_media_MtpServer_send_object_added}, + {"native_send_object_removed", "(I)V", (void *)android_media_MtpServer_send_object_removed}, +}; + +static const char* const kClassPathName = "android/media/MtpServer"; + +int register_android_media_MtpServer(JNIEnv *env) +{ + jclass clazz; + + LOGD("register_android_media_MtpServer\n"); + + clazz = env->FindClass("android/media/MtpServer"); + if (clazz == NULL) { + LOGE("Can't find android/media/MtpServer"); + return -1; + } + field_context = env->GetFieldID(clazz, "mNativeContext", "I"); + if (field_context == NULL) { + LOGE("Can't find MtpServer.mNativeContext"); + return -1; + } + + return AndroidRuntime::registerNativeMethods(env, + "android/media/MtpServer", gMethods, NELEM(gMethods)); +} |