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>
This commit is contained in:
Mike Lockwood
2010-05-25 19:08:48 -04:00
parent a393a85d2b
commit 755fd61725
16 changed files with 1591 additions and 378 deletions

View File

@@ -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";
}
}

View File

@@ -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 \

View File

@@ -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),

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
mSessionID = newSession;
mTransactionID = 1;
return true;
}
bool MtpClient::closeSession() {
// FIXME
return true;
}
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;
}
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();
}
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;
}
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();
}
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;
}
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);
}
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();
bool MtpClient::start() {
if (mStarted)
return true;
}
else {
printf("readResponse failed\n");
if (usb_host_init(usb_device_added, usb_device_removed, this)) {
LOGE("MtpClient::start failed\n");
return false;
}
mStarted = true;
return true;
}
void MtpClient::usbDeviceAdded(const char *devname) {
struct usb_descriptor_header* desc;
struct usb_descriptor_iter iter;
struct usb_device *device = usb_device_open(devname);
if (!device) {
LOGE("usb_device_open failed\n");
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)
{
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;
}
}
}
usb_device_close(device);
}
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;
}
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;
}
}
}
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_added(const char *devname, void* client_data) {
LOGD("usb_device_added %s\n", devname);
((MtpClient *)client_data)->usbDeviceAdded(devname);
}
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

View File

@@ -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);
virtual void deviceAdded(MtpDevice *device) = 0;
virtual void deviceRemoved(MtpDevice *device) = 0;
private:
bool sendRequest(MtpOperationCode operation);
bool sendData(MtpOperationCode operation);
bool readData();
MtpResponseCode readResponse();
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

332
media/mtp/MtpCursor.cpp Normal file
View File

@@ -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

75
media/mtp/MtpCursor.h Normal file
View File

@@ -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

230
media/mtp/MtpDevice.cpp Normal file
View File

@@ -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

83
media/mtp/MtpDevice.h Normal file
View File

@@ -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

View File

@@ -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

View File

@@ -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;
}