diff options
author | Mike Lockwood <lockwood@android.com> | 2010-05-25 19:08:48 -0400 |
---|---|---|
committer | Mike Lockwood <lockwood@android.com> | 2010-06-01 22:12:44 -0400 |
commit | 755fd617258d3f1731b2829d681cab680db0fdd5 (patch) | |
tree | 386dd00b808f81308404f47347f31d73914825e9 | |
parent | a393a85d2b2f319a29c52c354a8b5e44fc1621ed (diff) | |
download | frameworks_base-755fd617258d3f1731b2829d681cab680db0fdd5.zip frameworks_base-755fd617258d3f1731b2829d681cab680db0fdd5.tar.gz frameworks_base-755fd617258d3f1731b2829d681cab680db0fdd5.tar.bz2 |
Prototype Content Provider support for MTP/PTP devices.
At this point much of the plumbing is in place, but only a few simple queries
are supported.
This is enough to support a proof of concept sample program that navigates
the file hierarchy of a digital camera connected via USB.
Also removed obsolete ptptest host test program.
Change-Id: I17644344b9f0ce1ecc302bc0478c1f3d44a1647f
Signed-off-by: Mike Lockwood <lockwood@android.com>
-rw-r--r-- | core/java/android/provider/Mtp.java | 100 | ||||
-rw-r--r-- | core/jni/Android.mk | 5 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 4 | ||||
-rw-r--r-- | core/jni/android_media_MtpClient.cpp | 227 | ||||
-rw-r--r-- | core/jni/android_media_MtpCursor.cpp | 131 | ||||
-rw-r--r-- | media/java/android/media/MtpClient.java | 80 | ||||
-rw-r--r-- | media/java/android/media/MtpCursor.java | 170 | ||||
-rw-r--r-- | media/mtp/Android.mk | 17 | ||||
-rw-r--r-- | media/mtp/MtpClient.cpp | 250 | ||||
-rw-r--r-- | media/mtp/MtpClient.h | 47 | ||||
-rw-r--r-- | media/mtp/MtpCursor.cpp | 332 | ||||
-rw-r--r-- | media/mtp/MtpCursor.h | 75 | ||||
-rw-r--r-- | media/mtp/MtpDevice.cpp | 230 | ||||
-rw-r--r-- | media/mtp/MtpDevice.h | 83 | ||||
-rw-r--r-- | media/mtp/MtpTypes.h | 22 | ||||
-rw-r--r-- | media/mtp/ptptest.cpp | 166 |
16 files changed, 1576 insertions, 363 deletions
diff --git a/core/java/android/provider/Mtp.java b/core/java/android/provider/Mtp.java new file mode 100644 index 0000000..ce2aa8d --- /dev/null +++ b/core/java/android/provider/Mtp.java @@ -0,0 +1,100 @@ +/* + * 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. + */ + +package android.provider; + +import android.content.ContentUris; +import android.net.Uri; +import android.util.Log; + + +/** + * The MTP provider supports accessing content on MTP and PTP devices. + * @hide + */ +public final class Mtp +{ + private final static String TAG = "Mtp"; + + public static final String AUTHORITY = "mtp"; + + private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/"; + private static final String CONTENT_AUTHORITY_DEVICE_SLASH = "content://" + AUTHORITY + "/device/"; + + /** + * Contains list of all MTP/PTP devices + */ + public static final class Device implements BaseColumns { + + public static final Uri CONTENT_URI = Uri.parse(CONTENT_AUTHORITY_SLASH + "device"); + + /** + * The manufacturer of the device + * <P>Type: TEXT</P> + */ + public static final String MANUFACTURER = "manufacturer"; + + /** + * The model name of the device + * <P>Type: TEXT</P> + */ + public static final String MODEL = "model"; + } + + /** + * Contains list of storage units for an MTP/PTP device + */ + public static final class Storage implements BaseColumns { + + public static Uri getContentUri(int deviceID) { + return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID + "/storage"); + } + + /** + * Storage unit identifier + * <P>Type: TEXT</P> + */ + public static final String IDENTIFIER = "identifier"; + + /** + * Storage unit description + * <P>Type: TEXT</P> + */ + public static final String DESCRIPTION = "description"; + } + + /** + * Contains list of objects on an MTP/PTP device + */ + public static final class Object implements BaseColumns { + + public static Uri getContentUriForObjectChildren(int deviceID, int objectID) { + return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID + + "/object/" + objectID + "/child"); + } + + public static Uri getContentUriForStorageChildren(int deviceID, int storageID) { + return Uri.parse(CONTENT_AUTHORITY_DEVICE_SLASH + deviceID + + "/storage/" + storageID + "/child"); + } + + /** + * Name of the object + * <P>Type: TEXT</P> + */ + public static final String NAME = "name"; + } +} diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 170a32f..2239bf4 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -106,6 +106,8 @@ LOCAL_SRC_FILES:= \ android_media_AudioSystem.cpp \ android_media_AudioTrack.cpp \ android_media_JetPlayer.cpp \ + android_media_MtpClient.cpp \ + android_media_MtpCursor.cpp \ android_media_ToneGenerator.cpp \ android_hardware_Camera.cpp \ android_hardware_SensorManager.cpp \ @@ -138,6 +140,7 @@ LOCAL_C_INCLUDES += \ $(call include-path-for, libhardware_legacy)/hardware_legacy \ $(LOCAL_PATH)/../../include/ui \ $(LOCAL_PATH)/../../include/utils \ + $(LOCAL_PATH)/../../media/mtp \ external/skia/include/core \ external/skia/include/effects \ external/skia/include/images \ @@ -183,6 +186,8 @@ LOCAL_SHARED_LIBRARIES := \ libwpa_client \ libjpeg +LOCAL_STATIC_LIBRARIES := libmtphost libusbhost + ifeq ($(BOARD_HAVE_BLUETOOTH),true) LOCAL_C_INCLUDES += \ external/dbus \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 76df9db..54505dc 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -124,6 +124,8 @@ extern int register_android_database_SQLiteProgram(JNIEnv* env); extern int register_android_database_SQLiteQuery(JNIEnv* env); extern int register_android_database_SQLiteStatement(JNIEnv* env); extern int register_android_debug_JNITest(JNIEnv* env); +extern int register_android_media_MtpClient(JNIEnv *env); +extern int register_android_media_MtpCursor(JNIEnv *env); extern int register_android_nio_utils(JNIEnv* env); extern int register_android_pim_EventRecurrence(JNIEnv* env); extern int register_android_text_format_Time(JNIEnv* env); @@ -1268,6 +1270,8 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_media_AudioSystem), REG_JNI(register_android_media_AudioTrack), REG_JNI(register_android_media_JetPlayer), + REG_JNI(register_android_media_MtpClient), + REG_JNI(register_android_media_MtpCursor), REG_JNI(register_android_media_ToneGenerator), REG_JNI(register_android_opengl_classes), diff --git a/core/jni/android_media_MtpClient.cpp b/core/jni/android_media_MtpClient.cpp new file mode 100644 index 0000000..0c04255 --- /dev/null +++ b/core/jni/android_media_MtpClient.cpp @@ -0,0 +1,227 @@ +/* + * 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 <utils/threads.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include "MtpClient.h" +#include "MtpDevice.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static jmethodID method_deviceAdded; +static jmethodID method_deviceRemoved; +static jfieldID field_context; + +class MyClient : public MtpClient { +private: + + enum { + kDeviceAdded = 1, + kDeviceRemoved, + }; + + virtual void deviceAdded(MtpDevice *device); + virtual void deviceRemoved(MtpDevice *device); + + bool reportDeviceAdded(JNIEnv *env, MtpDevice *device); + bool reportDeviceRemoved(JNIEnv *env, MtpDevice *device); + + JNIEnv* mEnv; + jobject mClient; + Mutex mEventLock; + Condition mEventCondition; + Mutex mAckLock; + Condition mAckCondition; + int mEvent; + MtpDevice* mEventDevice; + +public: + MyClient(JNIEnv *env, jobject client); + virtual ~MyClient(); + void waitForEvent(JNIEnv *env); + +}; + +MtpClient* get_client_from_object(JNIEnv* env, jobject javaClient) +{ + return (MtpClient*)env->GetIntField(javaClient, field_context); +} + + +MyClient::MyClient(JNIEnv *env, jobject client) + : mEnv(env), + mClient(env->NewGlobalRef(client)), + mEvent(0), + mEventDevice(NULL) +{ +} + +MyClient::~MyClient() { + mEnv->DeleteGlobalRef(mClient); +} + + +void MyClient::deviceAdded(MtpDevice *device) { + LOGD("MyClient::deviceAdded\n"); + mAckLock.lock(); + mEventLock.lock(); + mEvent = kDeviceAdded; + mEventDevice = device; + mEventCondition.signal(); + mEventLock.unlock(); + mAckCondition.wait(mAckLock); + mAckLock.unlock(); +} + +void MyClient::deviceRemoved(MtpDevice *device) { + LOGD("MyClient::deviceRemoved\n"); + mAckLock.lock(); + mEventLock.lock(); + mEvent = kDeviceRemoved; + mEventDevice = device; + mEventCondition.signal(); + mEventLock.unlock(); + mAckCondition.wait(mAckLock); + mAckLock.unlock(); +} + +bool MyClient::reportDeviceAdded(JNIEnv *env, MtpDevice *device) { + const char* name = device->getDeviceName(); + LOGD("MyClient::reportDeviceAdded %s\n", name); + + env->CallVoidMethod(mClient, method_deviceAdded, device->getID()); + + return (!env->ExceptionCheck()); +} + +bool MyClient::reportDeviceRemoved(JNIEnv *env, MtpDevice *device) { + const char* name = device->getDeviceName(); + LOGD("MyClient::reportDeviceRemoved %s\n", name); + + env->CallVoidMethod(mClient, method_deviceRemoved, device->getID()); + + return (!env->ExceptionCheck()); +} + +void MyClient::waitForEvent(JNIEnv *env) { + mEventLock.lock(); + mEventCondition.wait(mEventLock); + mEventLock.unlock(); + + switch (mEvent) { + case kDeviceAdded: + reportDeviceAdded(env, mEventDevice); + break; + case kDeviceRemoved: + reportDeviceRemoved(env, mEventDevice); + break; + } + + mAckLock.lock(); + mAckCondition.signal(); + mAckLock.unlock(); +} + + +// ---------------------------------------------------------------------------- + +static bool ExceptionCheck(void* env) +{ + return ((JNIEnv *)env)->ExceptionCheck(); +} + +static void +android_media_MtpClient_setup(JNIEnv *env, jobject thiz) +{ + LOGD("setup\n"); + MyClient* client = new MyClient(env, thiz); + client->start(); + env->SetIntField(thiz, field_context, (int)client); +} + +static void +android_media_MtpClient_finalize(JNIEnv *env, jobject thiz) +{ + LOGD("finalize\n"); + MyClient *client = (MyClient *)env->GetIntField(thiz, field_context); + delete client; +} + +static void +android_media_MtpClient_wait_for_event(JNIEnv *env, jobject thiz) +{ + LOGD("wait_for_event\n"); + MyClient *client = (MyClient *)env->GetIntField(thiz, field_context); + client->waitForEvent(env); +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gMethods[] = { + {"native_setup", "()V", (void *)android_media_MtpClient_setup}, + {"native_finalize", "()V", (void *)android_media_MtpClient_finalize}, + {"native_wait_for_event", "()V", (void *)android_media_MtpClient_wait_for_event}, + +}; + +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)); +} + +} // namespace android diff --git a/core/jni/android_media_MtpCursor.cpp b/core/jni/android_media_MtpCursor.cpp new file mode 100644 index 0000000..7b0b7b7 --- /dev/null +++ b/core/jni/android_media_MtpCursor.cpp @@ -0,0 +1,131 @@ +/* + * 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" + +namespace android { + +static jfieldID field_context; + +// From android_database_CursorWindow.cpp +CursorWindow * get_window_from_object(JNIEnv * env, jobject javaWindow); + +// 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) +{ + 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); +} + +static void +android_media_MtpCursor_finalize(JNIEnv *env, jobject thiz) +{ + LOGD("finalize\n"); + MtpCursor *cursor = (MtpCursor *)env->GetIntField(thiz, field_context); + delete cursor; +} + +static jint +android_media_MtpCursor_fill_window(JNIEnv *env, jobject thiz, jobject javaWindow, jint startPos) +{ + 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); +} + +// ---------------------------------------------------------------------------- + +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)); +} + +} // namespace android diff --git a/media/java/android/media/MtpClient.java b/media/java/android/media/MtpClient.java new file mode 100644 index 0000000..946a400 --- /dev/null +++ b/media/java/android/media/MtpClient.java @@ -0,0 +1,80 @@ +/* + * 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. + */ + +package android.media; + +import android.util.Log; + +/** + * {@hide} + */ +public class MtpClient { + + private static final String TAG = "MtpClient"; + + static { + System.loadLibrary("media_jni"); + } + + public MtpClient() { + native_setup(); + } + + @Override + protected void finalize() { + native_finalize(); + } + + public void start() { + mEventThread = new MtpEventThread(); + mEventThread.start(); + } + + private class MtpEventThread extends Thread { + + private boolean mDone; + + public MtpEventThread() { + super("MtpEventThread"); + } + + public void run() { + Log.d(TAG, "MtpEventThread starting"); + while (!mDone) { + // this will wait for an event from an MTP device + native_wait_for_event(); + } + Log.d(TAG, "MtpEventThread exiting"); + } + } + + private void deviceAdded(int id) { + Log.d(TAG, "deviceAdded " + id); + } + + private void deviceRemoved(int id) { + Log.d(TAG, "deviceRemoved " + id); + } + + private MtpEventThread mEventThread; + + // used by the JNI code + private int mNativeContext; + + private native final void native_setup(); + private native final void native_finalize(); + private native void native_wait_for_event(); +}
\ No newline at end of file diff --git a/media/java/android/media/MtpCursor.java b/media/java/android/media/MtpCursor.java new file mode 100644 index 0000000..67ca31b --- /dev/null +++ b/media/java/android/media/MtpCursor.java @@ -0,0 +1,170 @@ +/* + * 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. + */ + +package android.media; + +import android.database.AbstractWindowedCursor; +import android.database.CursorWindow; +import android.provider.Mtp; +import android.util.Log; + +import java.util.HashMap; + +/** + * Cursor class for MTP content provider + * @hide + */ +public final class MtpCursor extends AbstractWindowedCursor { + static final String TAG = "MtpCursor"; + static final int NO_COUNT = -1; + + /* constants for mQueryType */ + public static final int DEVICE = 1; + public static final int DEVICE_ID = 2; + public static final int STORAGE = 3; + public static final int STORAGE_ID = 4; + public static final int OBJECT = 5; + public static final int OBJECT_ID = 6; + public static final int STORAGE_CHILDREN = 7; + public static final int OBJECT_CHILDREN = 8; + + private int mQueryType; + private int mDeviceID; + private int mStorageID; + private int mQbjectID; + + /** The names of the columns in the projection */ + private String[] mColumns; + + /** The number of rows in the cursor */ + private int mCount = NO_COUNT; + + private final MtpClient mClient; + + public MtpCursor(MtpClient client, int queryType, int deviceID, int storageID, int objectID, + String[] projection) { + + mClient = client; + mQueryType = queryType; + mDeviceID = deviceID; + mStorageID = storageID; + mQbjectID = objectID; + mColumns = projection; + + HashMap<String, Integer> map; + switch (queryType) { + case DEVICE: + case DEVICE_ID: + map = sDeviceProjectionMap; + break; + case STORAGE: + case STORAGE_ID: + map = sStorageProjectionMap; + break; + case OBJECT: + case OBJECT_ID: + case STORAGE_CHILDREN: + case OBJECT_CHILDREN: + map = sObjectProjectionMap; + break; + default: + throw new IllegalArgumentException("unknown query type " + queryType); + } + + int[] columns = new int[projection.length]; + for (int i = 0; i < projection.length; i++) { + Integer id = map.get(projection[i]); + if (id == null) { + throw new IllegalArgumentException("unknown column " + projection[i]); + } + columns[i] = id.intValue(); + } + native_setup(client, queryType, deviceID, storageID, objectID, columns); + } + + @Override + protected void finalize() { + native_finalize(); + } + + @Override + public int getCount() { + if (mCount == NO_COUNT) { + fillWindow(0); + } + return mCount; + } + + private void fillWindow(int startPos) { + if (mWindow == null) { + // If there isn't a window set already it will only be accessed locally + mWindow = new CursorWindow(true /* the window is local only */); + } else { + mWindow.clear(); + } + mWindow.setStartPosition(startPos); + mCount = native_fill_window(mWindow, startPos); + } + + @Override + public String[] getColumnNames() { + Log.d(TAG, "getColumnNames returning " + mColumns); + return mColumns; + } + + /* Device Column IDs */ + private static final int DEVICE_ROW_ID = 1; + private static final int DEVICE_MANUFACTURER = 2; + private static final int DEVICE_MODEL = 3; + + /* Storage Column IDs */ + private static final int STORAGE_ROW_ID = 101; + private static final int STORAGE_IDENTIFIER = 102; + private static final int STORAGE_DESCRIPTION = 103; + + /* Object Column IDs */ + private static final int OBJECT_ROW_ID = 201; + private static final int OBJECT_NAME = 202; + + private static HashMap<String, Integer> sDeviceProjectionMap; + private static HashMap<String, Integer> sStorageProjectionMap; + private static HashMap<String, Integer> sObjectProjectionMap; + + static { + sDeviceProjectionMap = new HashMap<String, Integer>(); + sDeviceProjectionMap.put(Mtp.Device._ID, new Integer(DEVICE_ROW_ID)); + sDeviceProjectionMap.put(Mtp.Device.MANUFACTURER, new Integer(DEVICE_MANUFACTURER)); + sDeviceProjectionMap.put(Mtp.Device.MODEL, new Integer(DEVICE_MODEL)); + + sStorageProjectionMap = new HashMap<String, Integer>(); + sStorageProjectionMap.put(Mtp.Storage._ID, new Integer(STORAGE_ROW_ID)); + sStorageProjectionMap.put(Mtp.Storage.IDENTIFIER, new Integer(STORAGE_IDENTIFIER)); + sStorageProjectionMap.put(Mtp.Storage.DESCRIPTION, new Integer(STORAGE_DESCRIPTION)); + + sObjectProjectionMap = new HashMap<String, Integer>(); + sObjectProjectionMap.put(Mtp.Object._ID, new Integer(OBJECT_ROW_ID)); + sObjectProjectionMap.put(Mtp.Object.NAME, new Integer(OBJECT_NAME)); + } + + // used by the JNI code + private int mNativeContext; + + private native final void native_setup(MtpClient client, int queryType, + int deviceID, int storageID, int objectID, int[] columns); + private native final void native_finalize(); + private native void native_wait_for_event(); + private native int native_fill_window(CursorWindow window, int startPos); +} diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk index d9c69a4..9e9ae2f 100644 --- a/media/mtp/Android.mk +++ b/media/mtp/Android.mk @@ -47,16 +47,16 @@ include $(BUILD_EXECUTABLE) endif -ifeq ($(HOST_OS),linux) - include $(CLEAR_VARS) -LOCAL_MODULE := ptptest +LOCAL_MODULE := libmtphost + LOCAL_SRC_FILES:= \ - ptptest.cpp \ MtpClient.cpp \ + MtpCursor.cpp \ MtpDataPacket.cpp \ MtpDebug.cpp \ + MtpDevice.cpp \ MtpDeviceInfo.cpp \ MtpObjectInfo.cpp \ MtpPacket.cpp \ @@ -65,17 +65,12 @@ LOCAL_SRC_FILES:= \ MtpStorageInfo.cpp \ MtpStringBuffer.cpp \ MtpUtils.cpp \ - ../../libs/utils/VectorImpl.cpp \ - ../../libs/utils/SharedBuffer.cpp \ -LOCAL_STATIC_LIBRARIES := libusbhost libcutils -LOCAL_LDLIBS := -lpthread - LOCAL_CFLAGS := -g -DMTP_HOST LOCAL_LDFLAGS := -g -include $(BUILD_HOST_EXECUTABLE) +include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) @@ -103,5 +98,3 @@ LOCAL_CFLAGS := -g LOCAL_LDFLAGS := -g include $(BUILD_EXECUTABLE) - -endif
\ No newline at end of file diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp index de3c199..31874e9 100644 --- a/media/mtp/MtpClient.cpp +++ b/media/mtp/MtpClient.cpp @@ -14,6 +14,9 @@ * limitations under the License. */ +#define LOG_TAG "MtpClient" +#include "utils/Log.h" + #include <stdio.h> #include <stdlib.h> #include <sys/types.h> @@ -23,180 +26,143 @@ #include <errno.h> #include <usbhost/usbhost.h> +#include <linux/version.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20) +#include <linux/usb/ch9.h> +#else +#include <linux/usb_ch9.h> +#endif #include "MtpClient.h" +#include "MtpDevice.h" #include "MtpDebug.h" -#include "MtpDeviceInfo.h" -#include "MtpObjectInfo.h" -#include "MtpStorageInfo.h" -#include "MtpStringBuffer.h" namespace android { -MtpClient::MtpClient(struct usb_endpoint *ep_in, struct usb_endpoint *ep_out, - struct usb_endpoint *ep_intr) - : mEndpointIn(ep_in), - mEndpointOut(ep_out), - mEndpointIntr(ep_intr), - mSessionID(0), - mTransactionID(0) +MtpClient::MtpClient() + : mStarted(false) { - } MtpClient::~MtpClient() { } -bool MtpClient::openSession() { -printf("openSession\n"); - mSessionID = 0; - mTransactionID = 0; - MtpSessionID newSession = 1; - mRequest.reset(); - mRequest.setParameter(1, newSession); - if (!sendRequest(MTP_OPERATION_OPEN_SESSION)) - return false; - MtpResponseCode ret = readResponse(); - if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN) - newSession = mResponse.getParameter(1); - else if (ret != MTP_RESPONSE_OK) - return false; +bool MtpClient::start() { + if (mStarted) + return true; - mSessionID = newSession; - mTransactionID = 1; + if (usb_host_init(usb_device_added, usb_device_removed, this)) { + LOGE("MtpClient::start failed\n"); + return false; + } + mStarted = true; return true; } -bool MtpClient::closeSession() { - // FIXME - return true; -} +void MtpClient::usbDeviceAdded(const char *devname) { + struct usb_descriptor_header* desc; + struct usb_descriptor_iter iter; -MtpDeviceInfo* MtpClient::getDeviceInfo() { - mRequest.reset(); - if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO)) - return NULL; - if (!readData()) - return NULL; - MtpResponseCode ret = readResponse(); -printf("getDeviceInfo returned %04X\n", ret); - if (ret == MTP_RESPONSE_OK) { - MtpDeviceInfo* info = new MtpDeviceInfo; - info->read(mData); - return info; + struct usb_device *device = usb_device_open(devname); + if (!device) { + LOGE("usb_device_open failed\n"); + return; } - return NULL; -} -MtpStorageIDList* MtpClient::getStorageIDs() { - mRequest.reset(); - if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS)) - return NULL; - if (!readData()) - return NULL; - MtpResponseCode ret = readResponse(); - if (ret == MTP_RESPONSE_OK) { - return mData.getAUInt32(); + usb_descriptor_iter_init(device, &iter); + + while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { + if (desc->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; + + if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE && + interface->bInterfaceSubClass == 1 && // Still Image Capture + interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470) + { + LOGD("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), + usb_device_get_product_name(device)); + + // interface should be followed by three endpoints + struct usb_endpoint_descriptor *ep; + struct usb_endpoint_descriptor *ep_in_desc = NULL; + struct usb_endpoint_descriptor *ep_out_desc = NULL; + struct usb_endpoint_descriptor *ep_intr_desc = NULL; + for (int i = 0; i < 3; i++) { + ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter); + if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) { + LOGE("endpoints not found\n"); + return; + } + if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { + if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + ep_in_desc = ep; + else + ep_out_desc = ep; + } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT && + ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { + ep_intr_desc = ep; + } + } + if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) { + LOGE("endpoints not found\n"); + return; + } + + struct usb_endpoint *ep_in = usb_endpoint_open(device, ep_in_desc); + struct usb_endpoint *ep_out = usb_endpoint_open(device, ep_out_desc); + struct usb_endpoint *ep_intr = usb_endpoint_open(device, ep_intr_desc); + + if (usb_device_claim_interface(device, interface->bInterfaceNumber)) { + LOGE("usb_device_claim_interface failed\n"); + usb_endpoint_close(ep_in); + usb_endpoint_close(ep_out); + usb_endpoint_close(ep_intr); + return; + } + + MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber, + ep_in, ep_out, ep_intr); + mDeviceList.add(mtpDevice); + mtpDevice->initialize(); + deviceAdded(mtpDevice); + return; + } + } } - return NULL; -} -MtpStorageInfo* MtpClient::getStorageInfo(MtpStorageID storageID) { - mRequest.reset(); - mRequest.setParameter(1, storageID); - if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO)) - return NULL; - if (!readData()) - return NULL; - MtpResponseCode ret = readResponse(); -printf("getStorageInfo returned %04X\n", ret); - if (ret == MTP_RESPONSE_OK) { - MtpStorageInfo* info = new MtpStorageInfo(storageID); - info->read(mData); - return info; - } - return NULL; + usb_device_close(device); } -MtpObjectHandleList* MtpClient::getObjectHandles(MtpStorageID storageID, - MtpObjectFormat format, MtpObjectHandle parent) { - mRequest.reset(); - mRequest.setParameter(1, storageID); - mRequest.setParameter(2, format); - mRequest.setParameter(3, parent); - if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES)) - return NULL; - if (!readData()) - return NULL; - MtpResponseCode ret = readResponse(); -printf("getObjectHandles returned %04X\n", ret); - if (ret == MTP_RESPONSE_OK) { - return mData.getAUInt32(); +MtpDevice* MtpClient::getDevice(int id) { + for (int i = 0; i < mDeviceList.size(); i++) { + MtpDevice* device = mDeviceList[i]; + if (device->getID() == id) + return device; } return NULL; } -MtpObjectInfo* MtpClient::getObjectInfo(MtpObjectHandle handle) { - mRequest.reset(); - mRequest.setParameter(1, handle); - if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO)) - return NULL; - if (!readData()) - return NULL; - MtpResponseCode ret = readResponse(); -printf("getObjectInfo returned %04X\n", ret); - if (ret == MTP_RESPONSE_OK) { - MtpObjectInfo* info = new MtpObjectInfo(handle); - info->read(mData); - return info; +void MtpClient::usbDeviceRemoved(const char *devname) { + for (int i = 0; i < mDeviceList.size(); i++) { + MtpDevice* device = mDeviceList[i]; + if (!strcmp(devname, device->getDeviceName())) { + deviceRemoved(device); + mDeviceList.removeAt(i); + delete device; + LOGD("Camera removed!\n"); + break; + } } - return NULL; } -bool MtpClient::sendRequest(MtpOperationCode operation) { - printf("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation)); - mRequest.setOperationCode(operation); - if (mTransactionID > 0) - mRequest.setTransactionID(mTransactionID++); - int ret = mRequest.write(mEndpointOut); - mRequest.dump(); - return (ret > 0); +void MtpClient::usb_device_added(const char *devname, void* client_data) { + LOGD("usb_device_added %s\n", devname); + ((MtpClient *)client_data)->usbDeviceAdded(devname); } -bool MtpClient::sendData(MtpOperationCode operation) { - printf("sendData\n"); - mData.setOperationCode(mRequest.getOperationCode()); - mData.setTransactionID(mRequest.getTransactionID()); - int ret = mData.write(mEndpointOut); - mData.dump(); - return (ret > 0); -} - -bool MtpClient::readData() { - mData.reset(); - int ret = mData.read(mEndpointIn); - printf("readData returned %d\n", ret); - if (ret >= MTP_CONTAINER_HEADER_SIZE) { - mData.dump(); - return true; - } - else { - printf("readResponse failed\n"); - return false; - } -} - -MtpResponseCode MtpClient::readResponse() { - printf("readResponse\n"); - int ret = mResponse.read(mEndpointIn); - if (ret >= MTP_CONTAINER_HEADER_SIZE) { - mResponse.dump(); - return mResponse.getResponseCode(); - } - else { - printf("readResponse failed\n"); - return -1; - } +void MtpClient::usb_device_removed(const char *devname, void* client_data) { + LOGD("usb_device_removed %s\n", devname); + ((MtpClient *)client_data)->usbDeviceRemoved(devname); } } // namespace android diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h index 76d9648..d87c226 100644 --- a/media/mtp/MtpClient.h +++ b/media/mtp/MtpClient.h @@ -17,52 +17,33 @@ #ifndef _MTP_CLIENT_H #define _MTP_CLIENT_H -#include "MtpRequestPacket.h" -#include "MtpDataPacket.h" -#include "MtpResponsePacket.h" #include "MtpTypes.h" namespace android { -class MtpDeviceInfo; -class MtpObjectInfo; -class MtpStorageInfo; - class MtpClient { private: - struct usb_endpoint* mEndpointIn; - struct usb_endpoint* mEndpointOut; - struct usb_endpoint* mEndpointIntr; - - // current session ID - MtpSessionID mSessionID; - // current transaction ID - MtpTransactionID mTransactionID; - - MtpRequestPacket mRequest; - MtpDataPacket mData; - MtpResponsePacket mResponse; + MtpDeviceList mDeviceList; + bool mStarted; public: - MtpClient(struct usb_endpoint *ep_in, struct usb_endpoint *ep_out, - struct usb_endpoint *ep_intr); + MtpClient(); virtual ~MtpClient(); - bool openSession(); - bool closeSession(); + bool start(); - MtpDeviceInfo* getDeviceInfo(); - MtpStorageIDList* getStorageIDs(); - MtpStorageInfo* getStorageInfo(MtpStorageID storageID); - MtpObjectHandleList* getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent); - MtpObjectInfo* getObjectInfo(MtpObjectHandle handle); + inline MtpDeviceList& getDeviceList() { return mDeviceList; } + MtpDevice* getDevice(int id); -private: - bool sendRequest(MtpOperationCode operation); - bool sendData(MtpOperationCode operation); - bool readData(); - MtpResponseCode readResponse(); + virtual void deviceAdded(MtpDevice *device) = 0; + virtual void deviceRemoved(MtpDevice *device) = 0; + +private: + void usbDeviceAdded(const char *devname); + void usbDeviceRemoved(const char *devname); + static void usb_device_added(const char *devname, void* client_data); + static void usb_device_removed(const char *devname, void* client_data); }; }; // namespace android diff --git a/media/mtp/MtpCursor.cpp b/media/mtp/MtpCursor.cpp new file mode 100644 index 0000000..9c9ce64 --- /dev/null +++ b/media/mtp/MtpCursor.cpp @@ -0,0 +1,332 @@ +/* + * 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 "MtpCursor" +#include "utils/Log.h" + +#include "MtpClient.h" +#include "MtpCursor.h" +#include "MtpDevice.h" +#include "MtpDeviceInfo.h" +#include "MtpObjectInfo.h" +#include "MtpStorageInfo.h" + +#include "binder/CursorWindow.h" + +namespace android { + +/* Device Column IDs */ +#define DEVICE_ROW_ID 1 +#define DEVICE_MANUFACTURER 2 +#define DEVICE_MODEL 3 + +/* Storage Column IDs */ +#define STORAGE_ROW_ID 101 +#define STORAGE_IDENTIFIER 102 +#define STORAGE_DESCRIPTION 103 + +/* Object Column IDs */ +#define OBJECT_ROW_ID 201 +#define OBJECT_NAME 202 + +MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID, + int storageID, int objectID, int columnCount, int* columns) + : mClient(client), + mQueryType(queryType), + mDeviceID(deviceID), + mStorageID(storageID), + mQbjectID(objectID), + mColumnCount(columnCount), + mColumns(NULL) +{ + if (columns) { + mColumns = new int[columnCount]; + memcpy(mColumns, columns, columnCount * sizeof(int)); + } +} + +MtpCursor::~MtpCursor() { + delete[] mColumns; +} + +int MtpCursor::fillWindow(CursorWindow* window, int startPos) { + LOGD("MtpCursor::fillWindow mQueryType: %d\n", mQueryType); + + switch (mQueryType) { + case DEVICE: + return fillDevices(window, startPos); + case DEVICE_ID: + return fillDevice(window, startPos); + case STORAGE: + return fillStorages(window, startPos); + case STORAGE_ID: + return fillStorage(window, startPos); + case OBJECT: + return fillObjects(window, 0, startPos); + case OBJECT_ID: + return fillObject(window, startPos); + case STORAGE_CHILDREN: + return fillObjects(window, -1, startPos); + case OBJECT_CHILDREN: + return fillObjects(window, mQbjectID, startPos); + default: + LOGE("MtpCursor::fillWindow: unknown query type %d\n", mQueryType); + return 0; + } +} + +int MtpCursor::fillDevices(CursorWindow* window, int startPos) { + int count = 0; + MtpDeviceList& deviceList = mClient->getDeviceList(); + for (int i = 0; i < deviceList.size(); i++) { + MtpDevice* device = deviceList[i]; + if (fillDevice(window, device, startPos)) { + count++; + startPos++; + } else { + break; + } + } + return count; +} + +int MtpCursor::fillDevice(CursorWindow* window, int startPos) { + MtpDevice* device = mClient->getDevice(mDeviceID); + if (device && fillDevice(window, device, startPos)) + return 1; + else + return 0; +} + +int MtpCursor::fillStorages(CursorWindow* window, int startPos) { + int count = 0; + MtpDevice* device = mClient->getDevice(mDeviceID); + if (!device) + return 0; + MtpStorageIDList* storageIDs = device->getStorageIDs(); + if (!storageIDs) + return 0; + + for (int i = 0; i < storageIDs->size(); i++) { + MtpStorageID storageID = (*storageIDs)[i]; + if (fillStorage(window, device, storageID, startPos)) { + count++; + startPos++; + } else { + break; + } + } + delete storageIDs; + return count; +} + +int MtpCursor::fillStorage(CursorWindow* window, int startPos) { + MtpDevice* device = mClient->getDevice(mDeviceID); + if (device && fillStorage(window, device, mStorageID, startPos)) + return 1; + else + return 0; +} + +int MtpCursor::fillObjects(CursorWindow* window, int parent, int startPos) { + int count = 0; + MtpDevice* device = mClient->getDevice(mDeviceID); + if (!device) + return 0; + MtpObjectHandleList* handles = device->getObjectHandles(mStorageID, 0, parent); + if (!handles) + return 0; + + for (int i = 0; i < handles->size(); i++) { + MtpObjectHandle handle = (*handles)[i]; + if (fillObject(window, device, handle, startPos)) { + count++; + startPos++; + } else { + break; + } + } + delete handles; + return count; +} + +int MtpCursor::fillObject(CursorWindow* window, int startPos) { + MtpDevice* device = mClient->getDevice(mDeviceID); + if (device && fillObject(window, device, mQbjectID, startPos)) + return 1; + else + return 0; +} + +bool MtpCursor::fillDevice(CursorWindow* window, MtpDevice* device, int row) { + MtpDeviceInfo* deviceInfo = device->getDeviceInfo(); + if (!deviceInfo) + return false; + if (!prepareRow(window)) + return false; + + for (int i = 0; i < mColumnCount; i++) { + switch (mColumns[i]) { + case DEVICE_ROW_ID: + if (!putLong(window, device->getID(), row, i)) + return false; + break; + case DEVICE_MANUFACTURER: + if (!putString(window, deviceInfo->mManufacturer, row, i)) + return false; + break; + case DEVICE_MODEL: + if (!putString(window, deviceInfo->mModel, row, i)) + return false; + break; + default: + LOGE("fillDevice: unknown column %d\n", mColumns[i]); + return false; + } + } + + return true; +} + +bool MtpCursor::fillStorage(CursorWindow* window, MtpDevice* device, + MtpStorageID storageID, int row) { + +LOGD("fillStorage %d\n", storageID); + + MtpStorageInfo* storageInfo = device->getStorageInfo(storageID); + if (!storageInfo) + return false; + if (!prepareRow(window)) { + delete storageInfo; + return false; + } + + const char* text; + for (int i = 0; i < mColumnCount; i++) { + switch (mColumns[i]) { + case STORAGE_ROW_ID: + if (!putLong(window, storageID, row, i)) + goto fail; + break; + case STORAGE_IDENTIFIER: + text = storageInfo->mVolumeIdentifier; + if (!text || !text[0]) + text = "Camera Storage"; + if (!putString(window, text, row, i)) + goto fail; + break; + case STORAGE_DESCRIPTION: + text = storageInfo->mStorageDescription; + if (!text || !text[0]) + text = "Storage Description"; + if (!putString(window, text, row, i)) + goto fail; + break; + default: + LOGE("fillStorage: unknown column %d\n", mColumns[i]); + goto fail; + } + } + + delete storageInfo; + return true; + +fail: + delete storageInfo; + return false; +} + +bool MtpCursor::fillObject(CursorWindow* window, MtpDevice* device, + MtpObjectHandle objectID, int row) { + +LOGD("fillObject %d\n", objectID); + + MtpObjectInfo* objectInfo = device->getObjectInfo(objectID); + if (!objectInfo) + return false; + if (!prepareRow(window)) { + delete objectInfo; + return false; + } + + for (int i = 0; i < mColumnCount; i++) { + switch (mColumns[i]) { + case OBJECT_ROW_ID: + if (!putLong(window, objectID, row, i)) + goto fail; + break; + case OBJECT_NAME: + if (!putString(window, objectInfo->mName, row, i)) + goto fail; + break; + default: + LOGE("fillStorage: unknown column %d\n", mColumns[i]); + goto fail; + } + } + + delete objectInfo; + return true; + +fail: + delete objectInfo; + return false; +} + +bool MtpCursor::prepareRow(CursorWindow* window) { + if (!window->setNumColumns(mColumnCount)) { + LOGE("Failed to change column count from %d to %d", window->getNumColumns(), mColumnCount); + return false; + } + field_slot_t * fieldDir = window->allocRow(); + if (!fieldDir) { + LOGE("Failed allocating fieldDir"); + return false; + } + return true; +} + + +bool MtpCursor::putLong(CursorWindow* window, int value, int row, int column) { + + if (!window->putLong(row, column, value)) { + window->freeLastRow(); + LOGE("Failed allocating space for a long in column %d", column); + return false; + } + return true; +} + +bool MtpCursor::putString(CursorWindow* window, const char* text, int row, int column) { + int size = strlen(text) + 1; + int offset = window->alloc(size); + if (!offset) { + window->freeLastRow(); + LOGE("Failed allocating %u bytes for text/blob %s", size, text); + return false; + } + window->copyIn(offset, (const uint8_t*)text, size); + + // This must be updated after the call to alloc(), since that + // may move the field around in the window + field_slot_t * fieldSlot = window->getFieldSlot(row, column); + fieldSlot->type = FIELD_TYPE_STRING; + fieldSlot->data.buffer.offset = offset; + fieldSlot->data.buffer.size = size; + return true; +} + +} // namespace android diff --git a/media/mtp/MtpCursor.h b/media/mtp/MtpCursor.h new file mode 100644 index 0000000..422f0c9 --- /dev/null +++ b/media/mtp/MtpCursor.h @@ -0,0 +1,75 @@ +/* + * 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. + */ + +#ifndef _MTP_CURSOR_H +#define _MTP_CURSOR_H + +#include "MtpTypes.h" + +namespace android { + +class CursorWindow; + +class MtpCursor { +private: + enum { + DEVICE = 1, + DEVICE_ID = 2, + STORAGE = 3, + STORAGE_ID = 4, + OBJECT = 5, + OBJECT_ID = 6, + STORAGE_CHILDREN = 7, + OBJECT_CHILDREN = 8, + }; + + MtpClient* mClient; + int mQueryType; + int mDeviceID; + int mStorageID; + int mQbjectID; + int mColumnCount; + int* mColumns; + +public: + MtpCursor(MtpClient* client, int queryType, int deviceID, + int storageID, int objectID, int columnCount, int* columns); + virtual ~MtpCursor(); + + int fillWindow(CursorWindow* window, int startPos); + +private: + int fillDevices(CursorWindow* window, int startPos); + int fillDevice(CursorWindow* window, int startPos); + int fillStorages(CursorWindow* window, int startPos); + int fillStorage(CursorWindow* window, int startPos); + int fillObjects(CursorWindow* window, int parent, int startPos); + int fillObject(CursorWindow* window, int startPos); + + bool fillDevice(CursorWindow* window, MtpDevice* device, int startPos); + bool fillStorage(CursorWindow* window, MtpDevice* device, + MtpStorageID storageID, int row); + bool fillObject(CursorWindow* window, MtpDevice* device, + MtpObjectHandle objectID, int row); + + bool prepareRow(CursorWindow* window); + bool putLong(CursorWindow* window, int value, int row, int column); + bool putString(CursorWindow* window, const char* text, int row, int column); +}; + +}; // namespace android + +#endif // _MTP_CURSOR_H diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp new file mode 100644 index 0000000..0282086 --- /dev/null +++ b/media/mtp/MtpDevice.cpp @@ -0,0 +1,230 @@ +/* + * 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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> + +#include <usbhost/usbhost.h> + +#include "MtpDevice.h" +#include "MtpDebug.h" +#include "MtpDeviceInfo.h" +#include "MtpObjectInfo.h" +#include "MtpStorageInfo.h" +#include "MtpStringBuffer.h" + +namespace android { + +MtpDevice::MtpDevice(struct usb_device* device, int interface, + struct usb_endpoint *ep_in, struct usb_endpoint *ep_out, + struct usb_endpoint *ep_intr) + : mDevice(device), + mInterface(interface), + mEndpointIn(ep_in), + mEndpointOut(ep_out), + mEndpointIntr(ep_intr), + mDeviceInfo(NULL), + mID(usb_device_get_unique_id(device)), + mSessionID(0), + mTransactionID(0) +{ +} + +MtpDevice::~MtpDevice() { + close(); +} + +void MtpDevice::initialize() { + openSession(); + mDeviceInfo = getDeviceInfo(); + if (mDeviceInfo) { + mDeviceInfo->print(); + } +} + +void MtpDevice::close() { + if (mDevice) { + usb_device_release_interface(mDevice, mInterface); + usb_device_close(mDevice); + mDevice = NULL; + } +} + +const char* MtpDevice::getDeviceName() { + if (mDevice) + return usb_device_get_name(mDevice); + else + return "???"; +} + +bool MtpDevice::openSession() { +printf("openSession\n"); + mSessionID = 0; + mTransactionID = 0; + MtpSessionID newSession = 1; + mRequest.reset(); + mRequest.setParameter(1, newSession); + if (!sendRequest(MTP_OPERATION_OPEN_SESSION)) + return false; + MtpResponseCode ret = readResponse(); + if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN) + newSession = mResponse.getParameter(1); + else if (ret != MTP_RESPONSE_OK) + return false; + + mSessionID = newSession; + mTransactionID = 1; + return true; +} + +bool MtpDevice::closeSession() { + // FIXME + return true; +} + +MtpDeviceInfo* MtpDevice::getDeviceInfo() { + mRequest.reset(); + if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO)) + return NULL; + if (!readData()) + return NULL; + MtpResponseCode ret = readResponse(); +printf("getDeviceInfo returned %04X\n", ret); + if (ret == MTP_RESPONSE_OK) { + MtpDeviceInfo* info = new MtpDeviceInfo; + info->read(mData); + return info; + } + return NULL; +} + +MtpStorageIDList* MtpDevice::getStorageIDs() { + mRequest.reset(); + if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS)) + return NULL; + if (!readData()) + return NULL; + MtpResponseCode ret = readResponse(); + if (ret == MTP_RESPONSE_OK) { + return mData.getAUInt32(); + } + return NULL; +} + +MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) { + mRequest.reset(); + mRequest.setParameter(1, storageID); + if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO)) + return NULL; + if (!readData()) + return NULL; + MtpResponseCode ret = readResponse(); +printf("getStorageInfo returned %04X\n", ret); + if (ret == MTP_RESPONSE_OK) { + MtpStorageInfo* info = new MtpStorageInfo(storageID); + info->read(mData); + return info; + } + return NULL; +} + +MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID, + MtpObjectFormat format, MtpObjectHandle parent) { + mRequest.reset(); + mRequest.setParameter(1, storageID); + mRequest.setParameter(2, format); + mRequest.setParameter(3, parent); + if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES)) + return NULL; + if (!readData()) + return NULL; + MtpResponseCode ret = readResponse(); +printf("getObjectHandles returned %04X\n", ret); + if (ret == MTP_RESPONSE_OK) { + return mData.getAUInt32(); + } + return NULL; +} + +MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) { + mRequest.reset(); + mRequest.setParameter(1, handle); + if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO)) + return NULL; + if (!readData()) + return NULL; + MtpResponseCode ret = readResponse(); +printf("getObjectInfo returned %04X\n", ret); + if (ret == MTP_RESPONSE_OK) { + MtpObjectInfo* info = new MtpObjectInfo(handle); + info->read(mData); + return info; + } + return NULL; +} + +bool MtpDevice::sendRequest(MtpOperationCode operation) { + printf("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation)); + mRequest.setOperationCode(operation); + if (mTransactionID > 0) + mRequest.setTransactionID(mTransactionID++); + int ret = mRequest.write(mEndpointOut); + mRequest.dump(); + return (ret > 0); +} + +bool MtpDevice::sendData(MtpOperationCode operation) { + printf("sendData\n"); + mData.setOperationCode(mRequest.getOperationCode()); + mData.setTransactionID(mRequest.getTransactionID()); + int ret = mData.write(mEndpointOut); + mData.dump(); + return (ret > 0); +} + +bool MtpDevice::readData() { + mData.reset(); + int ret = mData.read(mEndpointIn); + printf("readData returned %d\n", ret); + if (ret >= MTP_CONTAINER_HEADER_SIZE) { + mData.dump(); + return true; + } + else { + printf("readResponse failed\n"); + return false; + } +} + +MtpResponseCode MtpDevice::readResponse() { + printf("readResponse\n"); + int ret = mResponse.read(mEndpointIn); + if (ret >= MTP_CONTAINER_HEADER_SIZE) { + mResponse.dump(); + return mResponse.getResponseCode(); + } + else { + printf("readResponse failed\n"); + return -1; + } +} + +} // namespace android diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h new file mode 100644 index 0000000..fe4f1bd --- /dev/null +++ b/media/mtp/MtpDevice.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef _MTP_DEVICE_H +#define _MTP_DEVICE_H + +#include "MtpRequestPacket.h" +#include "MtpDataPacket.h" +#include "MtpResponsePacket.h" +#include "MtpTypes.h" + +namespace android { + +class MtpDeviceInfo; +class MtpObjectInfo; +class MtpStorageInfo; + +class MtpDevice { +private: + struct usb_device* mDevice; + int mInterface; + struct usb_endpoint* mEndpointIn; + struct usb_endpoint* mEndpointOut; + struct usb_endpoint* mEndpointIntr; + MtpDeviceInfo* mDeviceInfo; + + // a unique ID for the device + int mID; + + // current session ID + MtpSessionID mSessionID; + // current transaction ID + MtpTransactionID mTransactionID; + + MtpRequestPacket mRequest; + MtpDataPacket mData; + MtpResponsePacket mResponse; + +public: + MtpDevice(struct usb_device* device, int interface, + struct usb_endpoint *ep_in, struct usb_endpoint *ep_out, + struct usb_endpoint *ep_intr); + virtual ~MtpDevice(); + + inline int getID() const { return mID; } + + void initialize(); + void close(); + const char* getDeviceName(); + + bool openSession(); + bool closeSession(); + + MtpDeviceInfo* getDeviceInfo(); + MtpStorageIDList* getStorageIDs(); + MtpStorageInfo* getStorageInfo(MtpStorageID storageID); + MtpObjectHandleList* getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent); + MtpObjectInfo* getObjectInfo(MtpObjectHandle handle); + +private: + bool sendRequest(MtpOperationCode operation); + bool sendData(MtpOperationCode operation); + bool readData(); + MtpResponseCode readResponse(); + +}; + +}; // namespace android + +#endif // _MTP_DEVICE_H diff --git a/media/mtp/MtpTypes.h b/media/mtp/MtpTypes.h index 3ec844f..e3389c0 100644 --- a/media/mtp/MtpTypes.h +++ b/media/mtp/MtpTypes.h @@ -52,17 +52,19 @@ typedef uint32_t MtpObjectHandle; #define kObjectHandleIndexMask 0x0FFFFFFF // mask for object index in file table class MtpStorage; +class MtpDevice; -typedef android::Vector<MtpStorage *> MtpStorageList; +typedef Vector<MtpStorage *> MtpStorageList; +typedef Vector<MtpDevice*> MtpDeviceList; -typedef android::Vector<uint8_t> UInt8List; -typedef android::Vector<uint32_t> UInt16List; -typedef android::Vector<uint32_t> UInt32List; -typedef android::Vector<uint64_t> UInt64List; -typedef android::Vector<int8_t> Int8List; -typedef android::Vector<int32_t> Int16List; -typedef android::Vector<int32_t> Int32List; -typedef android::Vector<int64_t> Int64List; +typedef Vector<uint8_t> UInt8List; +typedef Vector<uint32_t> UInt16List; +typedef Vector<uint32_t> UInt32List; +typedef Vector<uint64_t> UInt64List; +typedef Vector<int8_t> Int8List; +typedef Vector<int32_t> Int16List; +typedef Vector<int32_t> Int32List; +typedef Vector<int64_t> Int64List; typedef UInt16List MtpDevicePropertyList; typedef UInt16List MtpObjectFormatList; @@ -70,7 +72,7 @@ typedef UInt32List MtpObjectHandleList; typedef UInt16List MtpObjectPropertyList; typedef UInt32List MtpStorageIDList; -typedef android::String8 MtpString; +typedef String8 MtpString; }; // namespace android diff --git a/media/mtp/ptptest.cpp b/media/mtp/ptptest.cpp deleted file mode 100644 index 3b09070..0000000 --- a/media/mtp/ptptest.cpp +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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. - */ - -#include <unistd.h> -#include <stdio.h> -#include <string.h> - -#include <usbhost/usbhost.h> -#include <linux/usb/ch9.h> - -#include "MtpClient.h" -#include "MtpDeviceInfo.h" -#include "MtpObjectInfo.h" -#include "MtpStorageInfo.h" - -using namespace android; - -static struct usb_device *sCameraDevice = NULL; -static int sCameraInterface = 0; -static MtpClient *sClient = NULL; - - -static void start_session(struct usb_endpoint *ep_in, struct usb_endpoint *ep_out, - struct usb_endpoint *ep_intr) -{ - if (sClient) - delete sClient; - sClient = new MtpClient(ep_in, ep_out, ep_intr); - sClient->openSession(); - MtpDeviceInfo* info = sClient->getDeviceInfo(); - if (info) { - info->print(); - delete info; - } - MtpStorageIDList* storageIDs = sClient->getStorageIDs(); - if (storageIDs) { - for (int i = 0; i < storageIDs->size(); i++) { - MtpStorageID storageID = (*storageIDs)[i]; - MtpStorageInfo* info = sClient->getStorageInfo(storageID); - if (info) { - info->print(); - delete info; - } - MtpObjectHandleList* objects = sClient->getObjectHandles(storageID, 0, MTP_PARENT_ROOT); - if (objects) { - for (int j = 0; j < objects->size(); j++) { - MtpObjectHandle handle = (*objects)[j]; - MtpObjectInfo* info = sClient->getObjectInfo(handle); - if (info) { - info->print(); - delete info; - } - } - delete objects; - } - } - } -} - -static void usb_device_added(const char *devname, void *client_data) -{ - struct usb_descriptor_header* desc; - struct usb_descriptor_iter iter; - - struct usb_device *device = usb_device_open(devname); - if (!device) return; - - usb_descriptor_iter_init(device, &iter); - - while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { - if (desc->bDescriptorType == USB_DT_INTERFACE) { - struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; - - if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE && - interface->bInterfaceSubClass == 1 && // Still Image Capture - interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470) - { - printf("Found camera: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device), - usb_device_get_product_name(device)); - - // interface should be followed by three endpoints - struct usb_endpoint_descriptor *ep, *ep_in_desc = NULL, *ep_out_desc = NULL, *ep_intr_desc = NULL; - for (int i = 0; i < 3; i++) { - ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter); - if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) { - fprintf(stderr, "endpoints not found\n"); - return; - } - if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { - if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - ep_in_desc = ep; - else - ep_out_desc = ep; - } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT && - ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) { - ep_intr_desc = ep; - } - } - if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) { - fprintf(stderr, "endpoints not found\n"); - return; - } - - struct usb_endpoint *ep_in = usb_endpoint_open(device, ep_in_desc); - struct usb_endpoint *ep_out = usb_endpoint_open(device, ep_out_desc); - struct usb_endpoint *ep_intr = usb_endpoint_open(device, ep_intr_desc); - - if (usb_device_claim_interface(device, interface->bInterfaceNumber)) { - fprintf(stderr, "usb_device_claim_interface failed\n"); - usb_endpoint_close(ep_in); - usb_endpoint_close(ep_out); - usb_endpoint_close(ep_intr); - return; - } - - if (sCameraDevice) { - usb_device_release_interface(sCameraDevice, sCameraInterface); - usb_device_close(sCameraDevice); - } - sCameraDevice = device; - start_session(ep_in, ep_out, ep_intr); - } - } - } - - if (device != sCameraDevice) - usb_device_close(device); -} - -static void usb_device_removed(const char *devname, void *client_data) -{ - if (sCameraDevice && !strcmp(devname, usb_device_get_name(sCameraDevice))) { - delete sClient; - printf("Camera removed!\n"); - usb_device_release_interface(sCameraDevice, sCameraInterface); - usb_device_close(sCameraDevice); - sCameraDevice = NULL; - } -} - -int main(int argc, char* argv[]) -{ - if (usb_host_init(usb_device_added, usb_device_removed, NULL)) { - fprintf(stderr, "usb_host_init failed\n"); - return -1; - } - - while (1) { - sleep(1); - } - - return 0; -} |