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:
100
core/java/android/provider/Mtp.java
Normal file
100
core/java/android/provider/Mtp.java
Normal 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";
|
||||
}
|
||||
}
|
||||
@@ -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 \
|
||||
|
||||
@@ -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),
|
||||
|
||||
227
core/jni/android_media_MtpClient.cpp
Normal file
227
core/jni/android_media_MtpClient.cpp
Normal 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
|
||||
131
core/jni/android_media_MtpCursor.cpp
Normal file
131
core/jni/android_media_MtpCursor.cpp
Normal 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
|
||||
80
media/java/android/media/MtpClient.java
Normal file
80
media/java/android/media/MtpClient.java
Normal 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();
|
||||
}
|
||||
170
media/java/android/media/MtpCursor.java
Normal file
170
media/java/android/media/MtpCursor.java
Normal 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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
332
media/mtp/MtpCursor.cpp
Normal 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
75
media/mtp/MtpCursor.h
Normal 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
230
media/mtp/MtpDevice.cpp
Normal 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
83
media/mtp/MtpDevice.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user