summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@android.com>2010-12-30 15:38:45 -0500
committerMike Lockwood <lockwood@android.com>2011-01-22 15:56:09 -0800
commit8182e72479a8b0d832df9c392890b25bfa6f97b5 (patch)
treeb641d4bd5b2b65cb07fa0d6846b59ad80a959a60
parent97d7e4c227ca26c125c1b587c5ed04ab82d0f4e1 (diff)
downloadframeworks_base-8182e72479a8b0d832df9c392890b25bfa6f97b5.zip
frameworks_base-8182e72479a8b0d832df9c392890b25bfa6f97b5.tar.gz
frameworks_base-8182e72479a8b0d832df9c392890b25bfa6f97b5.tar.bz2
New APIs for MTP and PTP host support
This replaces the previous ContentProvider based interface Change-Id: I4cea2544854adb9fdcc04345e4d73d8ef05380f3 Signed-off-by: Mike Lockwood <lockwood@android.com>
-rw-r--r--media/java/android/mtp/MtpClient.java270
-rw-r--r--media/java/android/mtp/MtpDevice.java141
-rw-r--r--media/java/android/mtp/MtpDeviceInfo.java72
-rw-r--r--media/java/android/mtp/MtpObjectInfo.java257
-rw-r--r--media/java/android/mtp/MtpStorageInfo.java82
-rw-r--r--media/jni/Android.mk1
-rw-r--r--media/jni/android_media_MediaPlayer.cpp6
-rw-r--r--media/jni/android_mtp_MtpDevice.cpp663
-rw-r--r--media/mtp/Android.mk2
-rw-r--r--media/mtp/MtpClient.cpp251
-rw-r--r--media/mtp/MtpClient.h68
-rw-r--r--media/mtp/MtpDevice.cpp220
-rw-r--r--media/mtp/MtpDevice.h14
13 files changed, 1715 insertions, 332 deletions
diff --git a/media/java/android/mtp/MtpClient.java b/media/java/android/mtp/MtpClient.java
new file mode 100644
index 0000000..19ee92a
--- /dev/null
+++ b/media/java/android/mtp/MtpClient.java
@@ -0,0 +1,270 @@
+/*
+ * 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.mtp;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.UsbConstants;
+import android.hardware.UsbDevice;
+import android.hardware.UsbInterface;
+import android.hardware.UsbManager;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class helps an application manage a list of connected MTP devices.
+ * It listens for MTP devices being attached and removed from the USB host bus
+ * and notifies the application when the MTP device list changes.
+ * {@hide}
+ */
+public class MtpClient {
+
+ private static final String TAG = "MtpClient";
+
+ private final Context mContext;
+ private final UsbManager mUsbManager;
+ private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
+ private final ArrayList<MtpDevice> mDeviceList = new ArrayList<MtpDevice>();
+
+ private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String deviceName = intent.getStringExtra(UsbManager.EXTRA_DEVICE_NAME);
+
+ synchronized (mDeviceList) {
+ MtpDevice mtpDevice = getDeviceLocked(deviceName);
+
+ if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
+ if (mtpDevice == null) {
+ UsbDevice usbDevice =
+ (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ mtpDevice = openDevice(usbDevice);
+ }
+ if (mtpDevice != null) {
+ mDeviceList.add(mtpDevice);
+ for (Listener listener : mListeners) {
+ listener.deviceAdded(mtpDevice);
+ }
+ }
+ } else if (mtpDevice != null) {
+ mDeviceList.remove(mtpDevice);
+ for (Listener listener : mListeners) {
+ listener.deviceRemoved(mtpDevice);
+ }
+ }
+ }
+ }
+ };
+
+ public interface Listener {
+ public void deviceAdded(MtpDevice device);
+ public void deviceRemoved(MtpDevice device);
+ }
+
+ static public boolean isCamera(UsbDevice device) {
+ int count = device.getInterfaceCount();
+ for (int i = 0; i < count; i++) {
+ UsbInterface intf = device.getInterface(i);
+ if (intf.getInterfaceClass() == UsbConstants.USB_CLASS_STILL_IMAGE &&
+ intf.getInterfaceSubclass() == 1 &&
+ intf.getInterfaceProtocol() == 1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private MtpDevice openDevice(UsbDevice usbDevice) {
+ if (isCamera(usbDevice)) {
+ MtpDevice mtpDevice = new MtpDevice(usbDevice);
+ if (mtpDevice.open(mUsbManager)) {
+ return mtpDevice;
+ }
+ }
+ return null;
+ }
+
+ public MtpClient(Context context) {
+ mContext = context;
+ mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ context.registerReceiver(mUsbReceiver, filter);
+
+ for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
+ MtpDevice mtpDevice = getDeviceLocked(usbDevice.getDeviceName());
+ if (mtpDevice == null) {
+ mtpDevice = openDevice(usbDevice);
+ }
+ if (mtpDevice != null) {
+ mDeviceList.add(mtpDevice);
+ }
+ }
+ }
+
+ public void close() {
+ mContext.unregisterReceiver(mUsbReceiver);
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ public void addListener(Listener listener) {
+ synchronized (mDeviceList) {
+ if (!mListeners.contains(listener)) {
+ mListeners.add(listener);
+ }
+ }
+ }
+
+ public void removeListener(Listener listener) {
+ synchronized (mDeviceList) {
+ mListeners.remove(listener);
+ }
+ }
+
+ public MtpDevice getDevice(String deviceName) {
+ synchronized (mDeviceList) {
+ return getDeviceLocked(deviceName);
+ }
+ }
+
+ public MtpDevice getDevice(int id) {
+ synchronized (mDeviceList) {
+ return getDeviceLocked(UsbDevice.getDeviceName(id));
+ }
+ }
+
+ private MtpDevice getDeviceLocked(String deviceName) {
+ for (MtpDevice device : mDeviceList) {
+ if (device.getDeviceName().equals(deviceName)) {
+ return device;
+ }
+ }
+ return null;
+ }
+
+ public List<MtpDevice> getDeviceList() {
+ synchronized (mDeviceList) {
+ return new ArrayList<MtpDevice>(mDeviceList);
+ }
+ }
+
+ public List<MtpStorageInfo> getStorageList(String deviceName) {
+ MtpDevice device = getDevice(deviceName);
+ if (device == null) {
+ return null;
+ }
+ int[] storageIds = device.getStorageIds();
+ if (storageIds == null) {
+ return null;
+ }
+
+ int length = storageIds.length;
+ ArrayList<MtpStorageInfo> storageList = new ArrayList<MtpStorageInfo>(length);
+ for (int i = 0; i < length; i++) {
+ MtpStorageInfo info = device.getStorageInfo(storageIds[i]);
+ if (info == null) {
+ Log.w(TAG, "getStorageInfo failed");
+ } else {
+ storageList.add(info);
+ }
+ }
+ return storageList;
+ }
+
+ public MtpObjectInfo getObjectInfo(String deviceName, int objectHandle) {
+ MtpDevice device = getDevice(deviceName);
+ if (device == null) {
+ return null;
+ }
+ return device.getObjectInfo(objectHandle);
+ }
+
+ public boolean deleteObject(String deviceName, int objectHandle) {
+ MtpDevice device = getDevice(deviceName);
+ if (device == null) {
+ return false;
+ }
+ return device.deleteObject(objectHandle);
+ }
+
+ public List<MtpObjectInfo> getObjectList(String deviceName, int storageId, int objectHandle) {
+ MtpDevice device = getDevice(deviceName);
+ if (device == null) {
+ return null;
+ }
+ if (objectHandle == 0) {
+ // all objects in root of storage
+ objectHandle = 0xFFFFFFFF;
+ }
+ int[] handles = device.getObjectHandles(storageId, 0, objectHandle);
+ if (handles == null) {
+ return null;
+ }
+
+ int length = handles.length;
+ ArrayList<MtpObjectInfo> objectList = new ArrayList<MtpObjectInfo>(length);
+ for (int i = 0; i < length; i++) {
+ MtpObjectInfo info = device.getObjectInfo(handles[i]);
+ if (info == null) {
+ Log.w(TAG, "getObjectInfo failed");
+ } else {
+ objectList.add(info);
+ }
+ }
+ return objectList;
+ }
+
+ public byte[] getObject(String deviceName, int objectHandle, int objectSize) {
+ MtpDevice device = getDevice(deviceName);
+ if (device == null) {
+ return null;
+ }
+ return device.getObject(objectHandle, objectSize);
+ }
+
+ public byte[] getThumbnail(String deviceName, int objectHandle) {
+ MtpDevice device = getDevice(deviceName);
+ if (device == null) {
+ return null;
+ }
+ return device.getThumbnail(objectHandle);
+ }
+
+ public boolean importFile(String deviceName, int objectHandle, String destPath) {
+ MtpDevice device = getDevice(deviceName);
+ if (device == null) {
+ return false;
+ }
+ return device.importFile(objectHandle, destPath);
+ }
+}
diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java
new file mode 100644
index 0000000..2d726c2
--- /dev/null
+++ b/media/java/android/mtp/MtpDevice.java
@@ -0,0 +1,141 @@
+/*
+ * 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.mtp;
+
+import android.hardware.UsbDevice;
+import android.hardware.UsbManager;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+/**
+ * This class represents an MTP device connected on the USB host bus.
+ *
+ * {@hide}
+ */
+public final class MtpDevice {
+
+ private static final String TAG = "MtpDevice";
+
+ private final UsbDevice mDevice;
+
+ static {
+ System.loadLibrary("media_jni");
+ }
+
+ public MtpDevice(UsbDevice device) {
+ mDevice = device;
+ }
+
+ public boolean open(UsbManager manager) {
+ if (manager.openDevice(mDevice)) {
+ return native_open(mDevice.getDeviceName(), mDevice.getFileDescriptor());
+ } else {
+ return false;
+ }
+ }
+
+ public void close() {
+ Log.d(TAG, "close");
+ native_close();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ Log.d(TAG, "finalize");
+ try {
+ native_close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ public String getDeviceName() {
+ return mDevice.getDeviceName();
+ }
+
+ public int getDeviceId() {
+ return mDevice.getDeviceId();
+ }
+
+ @Override
+ public String toString() {
+ return mDevice.getDeviceName();
+ }
+
+ public MtpDeviceInfo getDeviceInfo() {
+ return native_get_device_info();
+ }
+
+ public int[] getStorageIds() {
+ return native_get_storage_ids();
+ }
+
+ public int[] getObjectHandles(int storageId, int format, int objectHandle) {
+ return native_get_object_handles(storageId, format, objectHandle);
+ }
+
+ public byte[] getObject(int objectHandle, int objectSize) {
+ return native_get_object(objectHandle, objectSize);
+ }
+
+ public byte[] getThumbnail(int objectHandle) {
+ return native_get_thumbnail(objectHandle);
+ }
+
+ public MtpStorageInfo getStorageInfo(int storageId) {
+ return native_get_storage_info(storageId);
+ }
+
+ public MtpObjectInfo getObjectInfo(int objectHandle) {
+ return native_get_object_info(objectHandle);
+ }
+
+ public boolean deleteObject(int objectHandle) {
+ return native_delete_object(objectHandle);
+ }
+
+ public long getParent(int objectHandle) {
+ return native_get_parent(objectHandle);
+ }
+
+ public long getStorageID(int objectHandle) {
+ return native_get_storage_id(objectHandle);
+ }
+
+ // Reads a file from device to host to the specified destination.
+ // Returns true if the transfer succeeds.
+ public boolean importFile(int objectHandle, String destPath) {
+ return native_import_file(objectHandle, destPath);
+ }
+
+ // used by the JNI code
+ private int mNativeContext;
+
+ private native boolean native_open(String deviceName, int fd);
+ private native void native_close();
+ private native MtpDeviceInfo native_get_device_info();
+ private native int[] native_get_storage_ids();
+ private native MtpStorageInfo native_get_storage_info(int storageId);
+ private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
+ private native MtpObjectInfo native_get_object_info(int objectHandle);
+ private native byte[] native_get_object(int objectHandle, int objectSize);
+ private native byte[] native_get_thumbnail(int objectHandle);
+ private native boolean native_delete_object(int objectHandle);
+ private native long native_get_parent(int objectHandle);
+ private native long native_get_storage_id(int objectHandle);
+ private native boolean native_import_file(int objectHandle, String destPath);
+}
diff --git a/media/java/android/mtp/MtpDeviceInfo.java b/media/java/android/mtp/MtpDeviceInfo.java
new file mode 100644
index 0000000..d918c20
--- /dev/null
+++ b/media/java/android/mtp/MtpDeviceInfo.java
@@ -0,0 +1,72 @@
+/*
+ * 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.mtp;
+
+/**
+ * This class encapsulates information about an MTP device.
+ * This corresponds to the DeviceInfo Dataset described in
+ * section 5.1.1 of the MTP specification.
+ *
+ * {@hide}
+ */
+public class MtpDeviceInfo {
+
+ private String mManufacturer;
+ private String mModel;
+ private String mVersion;
+ private String mSerialNumber;
+
+ // only instantiated via JNI
+ private MtpDeviceInfo() {
+ }
+
+ /**
+ * Returns the manufacturer's name for the MTP device
+ *
+ * @return the manufacturer name
+ */
+ public final String getManufacturer() {
+ return mManufacturer;
+ }
+
+ /**
+ * Returns the model name for the MTP device
+ *
+ * @return the model name
+ */
+ public final String getModel() {
+ return mModel;
+ }
+
+ /**
+ * Returns the version string the MTP device
+ *
+ * @return the device version
+ */
+ public final String getVersion() {
+ return mVersion;
+ }
+
+ /**
+ * Returns the unique serial number for the MTP device
+ *
+ * @return the serial number
+ */
+ public final String getSerialNumber() {
+ return mSerialNumber;
+ }
+} \ No newline at end of file
diff --git a/media/java/android/mtp/MtpObjectInfo.java b/media/java/android/mtp/MtpObjectInfo.java
new file mode 100644
index 0000000..309d524
--- /dev/null
+++ b/media/java/android/mtp/MtpObjectInfo.java
@@ -0,0 +1,257 @@
+/*
+ * 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.mtp;
+
+/**
+ * This class encapsulates information about an object on an MTP device.
+ * This corresponds to the ObjectInfo Dataset described in
+ * section 5.3.1 of the MTP specification.
+ *
+ * {@hide}
+ */
+public final class MtpObjectInfo {
+ private int mHandle;
+ private int mStorageId;
+ private int mFormat;
+ private int mProtectionStatus;
+ private int mCompressedSize;
+ private int mThumbFormat;
+ private int mThumbCompressedSize;
+ private int mThumbPixWidth;
+ private int mThumbPixHeight;
+ private int mImagePixWidth;
+ private int mImagePixHeight;
+ private int mImagePixDepth;
+ private int mParent;
+ private int mAssociationType;
+ private int mAssociationDesc;
+ private int mSequenceNumber;
+ private String mName;
+ private long mDateCreated;
+ private long mDateModified;
+ private String mKeywords;
+
+ // only instantiated via JNI
+ private MtpObjectInfo() {
+ }
+
+ /**
+ * Returns the object handle for the MTP object
+ *
+ * @return the object handle
+ */
+ public final int getObjectHandle() {
+ return mHandle;
+ }
+
+ /**
+ * Returns the storage ID for the MTP object's storage unit
+ *
+ * @return the storage ID
+ */
+ public final int getStorageId() {
+ return mStorageId;
+ }
+
+ /**
+ * Returns the format code for the MTP object
+ *
+ * @return the format code
+ */
+ public final int getFormat() {
+ return mFormat;
+ }
+
+ /**
+ * Returns the protection status for the MTP object
+ * Possible values are:
+ *
+ * <ul>
+ * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_NONE}
+ * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_READ_ONLY}
+ * <li> {@link android.mtp.MtpConstants#PROTECTION_STATUS_NON_TRANSFERABLE_DATA}
+ * </ul>
+ *
+ * @return the protection status
+ */
+ public final int getProtectionStatus() {
+ return mProtectionStatus;
+ }
+
+ /**
+ * Returns the size of the MTP object
+ *
+ * @return the object size
+ */
+ public final int getCompressedSize() {
+ return mCompressedSize;
+ }
+
+ /**
+ * Returns the format code for the MTP object's thumbnail
+ * Will be zero for objects with no thumbnail
+ *
+ * @return the thumbnail format code
+ */
+ public final int getThumbFormat() {
+ return mThumbFormat;
+ }
+
+ /**
+ * Returns the size of the MTP object's thumbnail
+ * Will be zero for objects with no thumbnail
+ *
+ * @return the thumbnail size
+ */
+ public final int getThumbCompressedSize() {
+ return mThumbCompressedSize;
+ }
+
+ /**
+ * Returns the width of the MTP object's thumbnail in pixels
+ * Will be zero for objects with no thumbnail
+ *
+ * @return the thumbnail width
+ */
+ public final int getThumbPixWidth() {
+ return mThumbPixWidth;
+ }
+
+ /**
+ * Returns the height of the MTP object's thumbnail in pixels
+ * Will be zero for objects with no thumbnail
+ *
+ * @return the thumbnail height
+ */
+ public final int getThumbPixHeight() {
+ return mThumbPixHeight;
+ }
+
+ /**
+ * Returns the width of the MTP object in pixels
+ * Will be zero for non-image objects
+ *
+ * @return the image width
+ */
+ public final int getImagePixWidth() {
+ return mImagePixWidth;
+ }
+
+ /**
+ * Returns the height of the MTP object in pixels
+ * Will be zero for non-image objects
+ *
+ * @return the image height
+ */
+ public final int getImagePixHeight() {
+ return mImagePixHeight;
+ }
+
+ /**
+ * Returns the depth of the MTP object in bits per pixel
+ * Will be zero for non-image objects
+ *
+ * @return the image depth
+ */
+ public final int getImagePixDepth() {
+ return mImagePixDepth;
+ }
+
+ /**
+ * Returns the object handle for the object's parent
+ * Will be zero for the root directory of a storage unit
+ *
+ * @return the object's parent
+ */
+ public final int getParent() {
+ return mParent;
+ }
+
+ /**
+ * Returns the association type for the MTP object
+ * Will be zero objects that are not of format
+ * {@link android.mtp.MtpConstants#FORMAT_ASSOCIATION}
+ * For directories the association type is typically
+ * {@link android.mtp.MtpConstants#ASSOCIATION_TYPE_GENERIC_FOLDER}
+ *
+ * @return the object's association type
+ */
+ public final int getAssociationType() {
+ return mAssociationType;
+ }
+
+ /**
+ * Returns the association description for the MTP object
+ * Will be zero objects that are not of format
+ * {@link android.mtp.MtpConstants#FORMAT_ASSOCIATION}
+ *
+ * @return the object's association description
+ */
+ public final int getAssociationDesc() {
+ return mAssociationDesc;
+ }
+
+ /**
+ * Returns the sequence number for the MTP object
+ * This field is typically not used for MTP devices,
+ * but is sometimes used to define a sequence of photos
+ * on PTP cameras.
+ *
+ * @return the object's sequence number
+ */
+ public final int getSequenceNumber() {
+ return mSequenceNumber;
+ }
+
+ /**
+ * Returns the name of the MTP object
+ *
+ * @return the object's name
+ */
+ public final String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns the creation date of the MTP object
+ * The value is represented as milliseconds since January 1, 1970
+ *
+ * @return the object's creation date
+ */
+ public final long getDateCreated() {
+ return mDateCreated;
+ }
+
+ /**
+ * Returns the modification date of the MTP object
+ * The value is represented as milliseconds since January 1, 1970
+ *
+ * @return the object's modification date
+ */
+ public final long getDateModified() {
+ return mDateModified;
+ }
+
+ /**
+ * Returns a comma separated list of keywords for the MTP object
+ *
+ * @return the object's keyword list
+ */
+ public final String getKeywords() {
+ return mKeywords;
+ }
+}
diff --git a/media/java/android/mtp/MtpStorageInfo.java b/media/java/android/mtp/MtpStorageInfo.java
new file mode 100644
index 0000000..811455a
--- /dev/null
+++ b/media/java/android/mtp/MtpStorageInfo.java
@@ -0,0 +1,82 @@
+/*
+ * 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.mtp;
+
+/**
+ * This class encapsulates information about a storage unit on an MTP device.
+ * This corresponds to the StorageInfo Dataset described in
+ * section 5.2.2 of the MTP specification.
+ *
+ * {@hide}
+ */
+public final class MtpStorageInfo {
+
+ private int mStorageId;
+ private long mMaxCapacity;
+ private long mFreeSpace;
+ private String mDescription;
+ private String mVolumeIdentifier;
+
+ // only instantiated via JNI
+ private MtpStorageInfo() {
+ }
+
+ /**
+ * Returns the storage ID for the storage unit
+ *
+ * @return the storage ID
+ */
+ public final int getStorageId() {
+ return mStorageId;
+ }
+
+ /**
+ * Returns the maximum storage capacity for the storage unit in bytes
+ *
+ * @return the maximum capacity
+ */
+ public final long getMaxCapacity() {
+ return mMaxCapacity;
+ }
+
+ /**
+ * Returns the amount of free space in the storage unit in bytes
+ *
+ * @return the amount of free space
+ */
+ public final long getFreeSpace() {
+ return mFreeSpace;
+ }
+
+ /**
+ * Returns the description string for the storage unit
+ *
+ * @return the storage unit description
+ */
+ public final String getDescription() {
+ return mDescription;
+ }
+
+ /**
+ * Returns the volume identifier for the storage unit
+ *
+ * @return the storage volume identifier
+ */
+ public final String getVolumeIdentifier() {
+ return mVolumeIdentifier;
+ }
+}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index ab6e512..2a89a2a 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -10,6 +10,7 @@ LOCAL_SRC_FILES:= \
android_media_MediaProfiles.cpp \
android_media_AmrInputStream.cpp \
android_mtp_MtpDatabase.cpp \
+ android_mtp_MtpDevice.cpp \
android_mtp_MtpServer.cpp \
LOCAL_SHARED_LIBRARIES := \
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index f0609b2..0884e35 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -776,6 +776,7 @@ extern int register_android_media_ResampleInputStream(JNIEnv *env);
extern int register_android_media_MediaProfiles(JNIEnv *env);
extern int register_android_media_AmrInputStream(JNIEnv *env);
extern int register_android_mtp_MtpDatabase(JNIEnv *env);
+extern int register_android_mtp_MtpDevice(JNIEnv *env);
extern int register_android_mtp_MtpServer(JNIEnv *env);
jint JNI_OnLoad(JavaVM* vm, void* reserved)
@@ -829,6 +830,11 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
goto bail;
}
+ if (register_android_mtp_MtpDevice(env) < 0) {
+ LOGE("ERROR: MtpDevice native registration failed");
+ goto bail;
+ }
+
if (register_android_mtp_MtpServer(env) < 0) {
LOGE("ERROR: MtpServer native registration failed");
goto bail;
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
new file mode 100644
index 0000000..9e67985
--- /dev/null
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -0,0 +1,663 @@
+/*
+ * 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_NDEBUG 0
+
+#define LOG_TAG "MtpDeviceJNI"
+#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 "private/android_filesystem_config.h"
+
+#include "MtpTypes.h"
+#include "MtpDevice.h"
+#include "MtpDeviceInfo.h"
+#include "MtpStorageInfo.h"
+#include "MtpObjectInfo.h"
+
+using namespace android;
+
+// ----------------------------------------------------------------------------
+
+static jfieldID field_context;
+
+jclass clazz_deviceInfo;
+jclass clazz_storageInfo;
+jclass clazz_objectInfo;
+
+jmethodID constructor_deviceInfo;
+jmethodID constructor_storageInfo;
+jmethodID constructor_objectInfo;
+
+// MtpDeviceInfo fields
+static jfieldID field_deviceInfo_manufacturer;
+static jfieldID field_deviceInfo_model;
+static jfieldID field_deviceInfo_version;
+static jfieldID field_deviceInfo_serialNumber;
+
+// MtpStorageInfo fields
+static jfieldID field_storageInfo_storageId;
+static jfieldID field_storageInfo_maxCapacity;
+static jfieldID field_storageInfo_freeSpace;
+static jfieldID field_storageInfo_description;
+static jfieldID field_storageInfo_volumeIdentifier;
+
+// MtpObjectInfo fields
+static jfieldID field_objectInfo_handle;
+static jfieldID field_objectInfo_storageId;
+static jfieldID field_objectInfo_format;
+static jfieldID field_objectInfo_protectionStatus;
+static jfieldID field_objectInfo_compressedSize;
+static jfieldID field_objectInfo_thumbFormat;
+static jfieldID field_objectInfo_thumbCompressedSize;
+static jfieldID field_objectInfo_thumbPixWidth;
+static jfieldID field_objectInfo_thumbPixHeight;
+static jfieldID field_objectInfo_imagePixWidth;
+static jfieldID field_objectInfo_imagePixHeight;
+static jfieldID field_objectInfo_imagePixDepth;
+static jfieldID field_objectInfo_parent;
+static jfieldID field_objectInfo_associationType;
+static jfieldID field_objectInfo_associationDesc;
+static jfieldID field_objectInfo_sequenceNumber;
+static jfieldID field_objectInfo_name;
+static jfieldID field_objectInfo_dateCreated;
+static jfieldID field_objectInfo_dateModified;
+static jfieldID field_objectInfo_keywords;
+
+#ifdef HAVE_ANDROID_OS
+
+MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice)
+{
+ return (MtpDevice*)env->GetIntField(javaDevice, field_context);
+}
+
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ LOGE("An exception was thrown by callback '%s'.", methodName);
+ LOGE_EX(env);
+ env->ExceptionClear();
+ }
+}
+
+#endif // HAVE_ANDROID_OS
+
+// ----------------------------------------------------------------------------
+
+static jboolean
+android_mtp_MtpDevice_open(JNIEnv *env, jobject thiz, jstring deviceName, jint fd)
+{
+#ifdef HAVE_ANDROID_OS
+ LOGD("open\n");
+ const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL);
+ MtpDevice* device = MtpDevice::open(deviceNameStr, fd);
+ env->ReleaseStringUTFChars(deviceName, deviceNameStr);
+
+ if (device)
+ env->SetIntField(thiz, field_context, (int)device);
+ return (device != NULL);
+#endif
+}
+
+static void
+android_mtp_MtpDevice_close(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+ LOGD("close\n");
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (device) {
+ device->close();
+ delete device;
+ env->SetIntField(thiz, field_context, 0);
+ }
+#endif
+}
+
+static jobject
+android_mtp_MtpDevice_get_device_info(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (!device) {
+ LOGD("android_mtp_MtpDevice_get_device_info device is null");
+ return NULL;
+ }
+ MtpDeviceInfo* deviceInfo = device->getDeviceInfo();
+ if (!deviceInfo) {
+ LOGD("android_mtp_MtpDevice_get_device_info deviceInfo is null");
+ return NULL;
+ }
+ jobject info = env->NewObject(clazz_deviceInfo, constructor_deviceInfo);
+ if (info == NULL) {
+ LOGE("Could not create a MtpDeviceInfo object");
+ delete deviceInfo;
+ return NULL;
+ }
+
+ if (deviceInfo->mManufacturer)
+ env->SetObjectField(info, field_deviceInfo_manufacturer,
+ env->NewStringUTF(deviceInfo->mManufacturer));
+ if (deviceInfo->mModel)
+ env->SetObjectField(info, field_deviceInfo_model,
+ env->NewStringUTF(deviceInfo->mModel));
+ if (deviceInfo->mVersion)
+ env->SetObjectField(info, field_deviceInfo_version,
+ env->NewStringUTF(deviceInfo->mVersion));
+ if (deviceInfo->mSerial)
+ env->SetObjectField(info, field_deviceInfo_serialNumber,
+ env->NewStringUTF(deviceInfo->mSerial));
+
+ delete deviceInfo;
+ return info;
+#else
+ return NULL;
+#endif
+}
+
+static jintArray
+android_mtp_MtpDevice_get_storage_ids(JNIEnv *env, jobject thiz)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (!device)
+ return NULL;
+ MtpStorageIDList* storageIDs = device->getStorageIDs();
+ if (!storageIDs)
+ return NULL;
+
+ int length = storageIDs->size();
+ jintArray array = env->NewIntArray(length);
+ // FIXME is this cast safe?
+ env->SetIntArrayRegion(array, 0, length, (const jint *)storageIDs->array());
+
+ delete storageIDs;
+ return array;
+#else
+ return NULL;
+#endif
+}
+
+static jobject
+android_mtp_MtpDevice_get_storage_info(JNIEnv *env, jobject thiz, jint storageID)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (!device)
+ return NULL;
+ MtpStorageInfo* storageInfo = device->getStorageInfo(storageID);
+ if (!storageInfo)
+ return NULL;
+
+ jobject info = env->NewObject(clazz_storageInfo, constructor_storageInfo);
+ if (info == NULL) {
+ LOGE("Could not create a MtpStorageInfo object");
+ delete storageInfo;
+ return NULL;
+ }
+
+ if (storageInfo->mStorageID)
+ env->SetIntField(info, field_storageInfo_storageId, storageInfo->mStorageID);
+ if (storageInfo->mMaxCapacity)
+ env->SetLongField(info, field_storageInfo_maxCapacity, storageInfo->mMaxCapacity);
+ if (storageInfo->mFreeSpaceBytes)
+ env->SetLongField(info, field_storageInfo_freeSpace, storageInfo->mFreeSpaceBytes);
+ if (storageInfo->mStorageDescription)
+ env->SetObjectField(info, field_storageInfo_description,
+ env->NewStringUTF(storageInfo->mStorageDescription));
+ if (storageInfo->mVolumeIdentifier)
+ env->SetObjectField(info, field_storageInfo_volumeIdentifier,
+ env->NewStringUTF(storageInfo->mVolumeIdentifier));
+
+ delete storageInfo;
+ return info;
+#else
+ return NULL;
+#endif
+}
+
+static jintArray
+android_mtp_MtpDevice_get_object_handles(JNIEnv *env, jobject thiz,
+ jint storageID, jint format, jint objectID)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (!device)
+ return NULL;
+ MtpObjectHandleList* handles = device->getObjectHandles(storageID, format, objectID);
+ if (!handles)
+ return NULL;
+
+ int length = handles->size();
+ jintArray array = env->NewIntArray(length);
+ // FIXME is this cast safe?
+ env->SetIntArrayRegion(array, 0, length, (const jint *)handles->array());
+
+ delete handles;
+ return array;
+#else
+ return NULL;
+#endif
+}
+
+static jobject
+android_mtp_MtpDevice_get_object_info(JNIEnv *env, jobject thiz, jint objectID)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (!device)
+ return NULL;
+ MtpObjectInfo* objectInfo = device->getObjectInfo(objectID);
+ if (!objectInfo)
+ return NULL;
+ jobject info = env->NewObject(clazz_objectInfo, constructor_objectInfo);
+ if (info == NULL) {
+ LOGE("Could not create a MtpObjectInfo object");
+ delete objectInfo;
+ return NULL;
+ }
+
+ if (objectInfo->mHandle)
+ env->SetIntField(info, field_objectInfo_handle, objectInfo->mHandle);
+ if (objectInfo->mStorageID)
+ env->SetIntField(info, field_objectInfo_storageId, objectInfo->mStorageID);
+ if (objectInfo->mFormat)
+ env->SetIntField(info, field_objectInfo_format, objectInfo->mFormat);
+ if (objectInfo->mProtectionStatus)
+ env->SetIntField(info, field_objectInfo_protectionStatus, objectInfo->mProtectionStatus);
+ if (objectInfo->mCompressedSize)
+ env->SetIntField(info, field_objectInfo_compressedSize, objectInfo->mCompressedSize);
+ if (objectInfo->mThumbFormat)
+ env->SetIntField(info, field_objectInfo_thumbFormat, objectInfo->mThumbFormat);
+ if (objectInfo->mThumbCompressedSize)
+ env->SetIntField(info, field_objectInfo_thumbCompressedSize, objectInfo->mThumbCompressedSize);
+ if (objectInfo->mThumbPixWidth)
+ env->SetIntField(info, field_objectInfo_thumbPixWidth, objectInfo->mThumbPixWidth);
+ if (objectInfo->mThumbPixHeight)
+ env->SetIntField(info, field_objectInfo_thumbPixHeight, objectInfo->mThumbPixHeight);
+ if (objectInfo->mImagePixWidth)
+ env->SetIntField(info, field_objectInfo_imagePixWidth, objectInfo->mImagePixWidth);
+ if (objectInfo->mImagePixHeight)
+ env->SetIntField(info, field_objectInfo_imagePixHeight, objectInfo->mImagePixHeight);
+ if (objectInfo->mImagePixDepth)
+ env->SetIntField(info, field_objectInfo_imagePixDepth, objectInfo->mImagePixDepth);
+ if (objectInfo->mParent)
+ env->SetIntField(info, field_objectInfo_parent, objectInfo->mParent);
+ if (objectInfo->mAssociationType)
+ env->SetIntField(info, field_objectInfo_associationType, objectInfo->mAssociationType);
+ if (objectInfo->mAssociationDesc)
+ env->SetIntField(info, field_objectInfo_associationDesc, objectInfo->mAssociationDesc);
+ if (objectInfo->mSequenceNumber)
+ env->SetIntField(info, field_objectInfo_sequenceNumber, objectInfo->mSequenceNumber);
+ if (objectInfo->mName)
+ env->SetObjectField(info, field_objectInfo_name, env->NewStringUTF(objectInfo->mName));
+ if (objectInfo->mDateCreated)
+ env->SetLongField(info, field_objectInfo_dateCreated, objectInfo->mDateCreated);
+ if (objectInfo->mDateModified)
+ env->SetLongField(info, field_objectInfo_dateModified, objectInfo->mDateModified);
+ if (objectInfo->mKeywords)
+ env->SetObjectField(info, field_objectInfo_keywords,
+ env->NewStringUTF(objectInfo->mKeywords));
+
+ delete objectInfo;
+ return info;
+#else
+ return NULL;
+#endif
+}
+
+struct get_object_callback_data {
+ JNIEnv *env;
+ jbyteArray array;
+};
+
+static bool get_object_callback(void* data, int offset, int length, void* clientData)
+{
+ get_object_callback_data* cbData = (get_object_callback_data *)clientData;
+ cbData->env->SetByteArrayRegion(cbData->array, offset, length, (jbyte *)data);
+ return true;
+}
+
+static jbyteArray
+android_mtp_MtpDevice_get_object(JNIEnv *env, jobject thiz, jint objectID, jint objectSize)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (!device)
+ return NULL;
+
+ jbyteArray array = env->NewByteArray(objectSize);
+ if (!array) {
+ jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
+ return NULL;
+ }
+
+ get_object_callback_data data;
+ data.env = env;
+ data.array = array;
+
+ if (device->readObject(objectID, get_object_callback, objectSize, &data))
+ return array;
+#endif
+ return NULL;
+}
+
+static jbyteArray
+android_mtp_MtpDevice_get_thumbnail(JNIEnv *env, jobject thiz, jint objectID)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (!device)
+ return NULL;
+
+ int length;
+ void* thumbnail = device->getThumbnail(objectID, length);
+ if (! thumbnail)
+ return NULL;
+ jbyteArray array = env->NewByteArray(length);
+ env->SetByteArrayRegion(array, 0, length, (const jbyte *)thumbnail);
+
+ free(thumbnail);
+ return array;
+#else
+ return NULL;
+#endif
+}
+
+static jboolean
+android_mtp_MtpDevice_delete_object(JNIEnv *env, jobject thiz, jint object_id)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (device)
+ return device->deleteObject(object_id);
+ else
+ #endif
+ return NULL;
+}
+
+static jlong
+android_mtp_MtpDevice_get_parent(JNIEnv *env, jobject thiz, jint object_id)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (device)
+ return device->getParent(object_id);
+ else
+#endif
+ return -1;
+}
+
+static jlong
+android_mtp_MtpDevice_get_storage_id(JNIEnv *env, jobject thiz, jint object_id)
+{
+ #ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (device)
+ return device->getStorageID(object_id);
+ else
+#endif
+ return -1;
+}
+
+static jboolean
+android_mtp_MtpDevice_import_file(JNIEnv *env, jobject thiz, jint object_id, jstring dest_path)
+{
+#ifdef HAVE_ANDROID_OS
+ MtpDevice* device = get_device_from_object(env, thiz);
+ if (device) {
+ const char *destPathStr = env->GetStringUTFChars(dest_path, NULL);
+ bool result = device->readObject(object_id, destPathStr, AID_SDCARD_RW, 0664);
+ env->ReleaseStringUTFChars(dest_path, destPathStr);
+ return result;
+ }
+#endif
+ return NULL;
+}
+
+// ----------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+ {"native_open", "(Ljava/lang/String;I)Z",
+ (void *)android_mtp_MtpDevice_open},
+ {"native_close", "()V", (void *)android_mtp_MtpDevice_close},
+ {"native_get_device_info", "()Landroid/mtp/MtpDeviceInfo;",
+ (void *)android_mtp_MtpDevice_get_device_info},
+ {"native_get_storage_ids", "()[I", (void *)android_mtp_MtpDevice_get_storage_ids},
+ {"native_get_storage_info", "(I)Landroid/mtp/MtpStorageInfo;",
+ (void *)android_mtp_MtpDevice_get_storage_info},
+ {"native_get_object_handles","(III)[I",
+ (void *)android_mtp_MtpDevice_get_object_handles},
+ {"native_get_object_info", "(I)Landroid/mtp/MtpObjectInfo;",
+ (void *)android_mtp_MtpDevice_get_object_info},
+ {"native_get_object", "(II)[B",(void *)android_mtp_MtpDevice_get_object},
+ {"native_get_thumbnail", "(I)[B",(void *)android_mtp_MtpDevice_get_thumbnail},
+ {"native_delete_object", "(I)Z", (void *)android_mtp_MtpDevice_delete_object},
+ {"native_get_parent", "(I)J", (void *)android_mtp_MtpDevice_get_parent},
+ {"native_get_storage_id", "(I)J", (void *)android_mtp_MtpDevice_get_storage_id},
+ {"native_import_file", "(ILjava/lang/String;)Z",
+ (void *)android_mtp_MtpDevice_import_file},
+};
+
+static const char* const kClassPathName = "android/mtp/MtpDevice";
+
+int register_android_mtp_MtpDevice(JNIEnv *env)
+{
+ jclass clazz;
+
+ LOGD("register_android_mtp_MtpDevice\n");
+
+ clazz = env->FindClass("android/mtp/MtpDeviceInfo");
+ if (clazz == NULL) {
+ LOGE("Can't find android/mtp/MtpDeviceInfo");
+ return -1;
+ }
+ constructor_deviceInfo = env->GetMethodID(clazz, "<init>", "()V");
+ if (constructor_deviceInfo == NULL) {
+ LOGE("Can't find android/mtp/MtpDeviceInfo constructor");
+ return -1;
+ }
+ field_deviceInfo_manufacturer = env->GetFieldID(clazz, "mManufacturer", "Ljava/lang/String;");
+ if (field_deviceInfo_manufacturer == NULL) {
+ LOGE("Can't find MtpDeviceInfo.mManufacturer");
+ return -1;
+ }
+ field_deviceInfo_model = env->GetFieldID(clazz, "mModel", "Ljava/lang/String;");
+ if (field_deviceInfo_model == NULL) {
+ LOGE("Can't find MtpDeviceInfo.mModel");
+ return -1;
+ }
+ field_deviceInfo_version = env->GetFieldID(clazz, "mVersion", "Ljava/lang/String;");
+ if (field_deviceInfo_version == NULL) {
+ LOGE("Can't find MtpDeviceInfo.mVersion");
+ return -1;
+ }
+ field_deviceInfo_serialNumber = env->GetFieldID(clazz, "mSerialNumber", "Ljava/lang/String;");
+ if (field_deviceInfo_serialNumber == NULL) {
+ LOGE("Can't find MtpDeviceInfo.mSerialNumber");
+ return -1;
+ }
+ clazz_deviceInfo = clazz;
+
+ clazz = env->FindClass("android/mtp/MtpStorageInfo");
+ if (clazz == NULL) {
+ LOGE("Can't find android/mtp/MtpStorageInfo");
+ return -1;
+ }
+ constructor_storageInfo = env->GetMethodID(clazz, "<init>", "()V");
+ if (constructor_storageInfo == NULL) {
+ LOGE("Can't find android/mtp/MtpStorageInfo constructor");
+ return -1;
+ }
+ field_storageInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I");
+ if (field_storageInfo_storageId == NULL) {
+ LOGE("Can't find MtpStorageInfo.mStorageId");
+ return -1;
+ }
+ field_storageInfo_maxCapacity = env->GetFieldID(clazz, "mMaxCapacity", "J");
+ if (field_storageInfo_maxCapacity == NULL) {
+ LOGE("Can't find MtpStorageInfo.mMaxCapacity");
+ return -1;
+ }
+ field_storageInfo_freeSpace = env->GetFieldID(clazz, "mFreeSpace", "J");
+ if (field_storageInfo_freeSpace == NULL) {
+ LOGE("Can't find MtpStorageInfo.mFreeSpace");
+ return -1;
+ }
+ field_storageInfo_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
+ if (field_storageInfo_description == NULL) {
+ LOGE("Can't find MtpStorageInfo.mDescription");
+ return -1;
+ }
+ field_storageInfo_volumeIdentifier = env->GetFieldID(clazz, "mVolumeIdentifier", "Ljava/lang/String;");
+ if (field_storageInfo_volumeIdentifier == NULL) {
+ LOGE("Can't find MtpStorageInfo.mVolumeIdentifier");
+ return -1;
+ }
+ clazz_storageInfo = clazz;
+
+ clazz = env->FindClass("android/mtp/MtpObjectInfo");
+ if (clazz == NULL) {
+ LOGE("Can't find android/mtp/MtpObjectInfo");
+ return -1;
+ }
+ constructor_objectInfo = env->GetMethodID(clazz, "<init>", "()V");
+ if (constructor_objectInfo == NULL) {
+ LOGE("Can't find android/mtp/MtpObjectInfo constructor");
+ return -1;
+ }
+ field_objectInfo_handle = env->GetFieldID(clazz, "mHandle", "I");
+ if (field_objectInfo_handle == NULL) {
+ LOGE("Can't find MtpObjectInfo.mHandle");
+ return -1;
+ }
+ field_objectInfo_storageId = env->GetFieldID(clazz, "mStorageId", "I");
+ if (field_objectInfo_storageId == NULL) {
+ LOGE("Can't find MtpObjectInfo.mStorageId");
+ return -1;
+ }
+ field_objectInfo_format = env->GetFieldID(clazz, "mFormat", "I");
+ if (field_objectInfo_format == NULL) {
+ LOGE("Can't find MtpObjectInfo.mFormat");
+ return -1;
+ }
+ field_objectInfo_protectionStatus = env->GetFieldID(clazz, "mProtectionStatus", "I");
+ if (field_objectInfo_protectionStatus == NULL) {
+ LOGE("Can't find MtpObjectInfo.mProtectionStatus");
+ return -1;
+ }
+ field_objectInfo_compressedSize = env->GetFieldID(clazz, "mCompressedSize", "I");
+ if (field_objectInfo_compressedSize == NULL) {
+ LOGE("Can't find MtpObjectInfo.mCompressedSize");
+ return -1;
+ }
+ field_objectInfo_thumbFormat = env->GetFieldID(clazz, "mThumbFormat", "I");
+ if (field_objectInfo_thumbFormat == NULL) {
+ LOGE("Can't find MtpObjectInfo.mThumbFormat");
+ return -1;
+ }
+ field_objectInfo_thumbCompressedSize = env->GetFieldID(clazz, "mThumbCompressedSize", "I");
+ if (field_objectInfo_thumbCompressedSize == NULL) {
+ LOGE("Can't find MtpObjectInfo.mThumbCompressedSize");
+ return -1;
+ }
+ field_objectInfo_thumbPixWidth = env->GetFieldID(clazz, "mThumbPixWidth", "I");
+ if (field_objectInfo_thumbPixWidth == NULL) {
+ LOGE("Can't find MtpObjectInfo.mThumbPixWidth");
+ return -1;
+ }
+ field_objectInfo_thumbPixHeight = env->GetFieldID(clazz, "mThumbPixHeight", "I");
+ if (field_objectInfo_thumbPixHeight == NULL) {
+ LOGE("Can't find MtpObjectInfo.mThumbPixHeight");
+ return -1;
+ }
+ field_objectInfo_imagePixWidth = env->GetFieldID(clazz, "mImagePixWidth", "I");
+ if (field_objectInfo_imagePixWidth == NULL) {
+ LOGE("Can't find MtpObjectInfo.mImagePixWidth");
+ return -1;
+ }
+ field_objectInfo_imagePixHeight = env->GetFieldID(clazz, "mImagePixHeight", "I");
+ if (field_objectInfo_imagePixHeight == NULL) {
+ LOGE("Can't find MtpObjectInfo.mImagePixHeight");
+ return -1;
+ }
+ field_objectInfo_imagePixDepth = env->GetFieldID(clazz, "mImagePixDepth", "I");
+ if (field_objectInfo_imagePixDepth == NULL) {
+ LOGE("Can't find MtpObjectInfo.mImagePixDepth");
+ return -1;
+ }
+ field_objectInfo_parent = env->GetFieldID(clazz, "mParent", "I");
+ if (field_objectInfo_parent == NULL) {
+ LOGE("Can't find MtpObjectInfo.mParent");
+ return -1;
+ }
+ field_objectInfo_associationType = env->GetFieldID(clazz, "mAssociationType", "I");
+ if (field_objectInfo_associationType == NULL) {
+ LOGE("Can't find MtpObjectInfo.mAssociationType");
+ return -1;
+ }
+ field_objectInfo_associationDesc = env->GetFieldID(clazz, "mAssociationDesc", "I");
+ if (field_objectInfo_associationDesc == NULL) {
+ LOGE("Can't find MtpObjectInfo.mAssociationDesc");
+ return -1;
+ }
+ field_objectInfo_sequenceNumber = env->GetFieldID(clazz, "mSequenceNumber", "I");
+ if (field_objectInfo_sequenceNumber == NULL) {
+ LOGE("Can't find MtpObjectInfo.mSequenceNumber");
+ return -1;
+ }
+ field_objectInfo_name = env->GetFieldID(clazz, "mName", "Ljava/lang/String;");
+ if (field_objectInfo_name == NULL) {
+ LOGE("Can't find MtpObjectInfo.mName");
+ return -1;
+ }
+ field_objectInfo_dateCreated = env->GetFieldID(clazz, "mDateCreated", "J");
+ if (field_objectInfo_dateCreated == NULL) {
+ LOGE("Can't find MtpObjectInfo.mDateCreated");
+ return -1;
+ }
+ field_objectInfo_dateModified = env->GetFieldID(clazz, "mDateModified", "J");
+ if (field_objectInfo_dateModified == NULL) {
+ LOGE("Can't find MtpObjectInfo.mDateModified");
+ return -1;
+ }
+ field_objectInfo_keywords = env->GetFieldID(clazz, "mKeywords", "Ljava/lang/String;");
+ if (field_objectInfo_keywords == NULL) {
+ LOGE("Can't find MtpObjectInfo.mKeywords");
+ return -1;
+ }
+ clazz_objectInfo = clazz;
+
+ clazz = env->FindClass("android/mtp/MtpDevice");
+ if (clazz == NULL) {
+ LOGE("Can't find android/mtp/MtpDevice");
+ return -1;
+ }
+ field_context = env->GetFieldID(clazz, "mNativeContext", "I");
+ if (field_context == NULL) {
+ LOGE("Can't find MtpDevice.mNativeContext");
+ return -1;
+ }
+
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/mtp/MtpDevice", gMethods, NELEM(gMethods));
+}
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index 70dc340..c25285e 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -21,7 +21,6 @@ ifneq ($(TARGET_SIMULATOR),true)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- MtpClient.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
MtpDevice.cpp \
@@ -53,7 +52,6 @@ ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- MtpClient.cpp \
MtpDataPacket.cpp \
MtpDebug.cpp \
MtpDevice.cpp \
diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp
deleted file mode 100644
index c830540..0000000
--- a/media/mtp/MtpClient.cpp
+++ /dev/null
@@ -1,251 +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.
- */
-
-#define LOG_TAG "MtpClient"
-
-#include "MtpDebug.h"
-#include "MtpClient.h"
-#include "MtpDevice.h"
-
-#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>
-
-struct usb_device;
-
-namespace android {
-
-static bool isMtpDevice(uint16_t vendor, uint16_t product) {
- // Sandisk Sansa Fuze
- if (vendor == 0x0781 && product == 0x74c2)
- return true;
- // Samsung YP-Z5
- if (vendor == 0x04e8 && product == 0x503c)
- return true;
- return false;
-}
-
-class MtpClientThread : public Thread {
-private:
- MtpClient* mClient;
-
-public:
- MtpClientThread(MtpClient* client)
- : mClient(client)
- {
- }
-
- virtual bool threadLoop() {
- return mClient->threadLoop();
- }
-};
-
-
-MtpClient::MtpClient()
- : mThread(NULL),
- mUsbHostContext(NULL),
- mDone(false)
-{
-}
-
-MtpClient::~MtpClient() {
- usb_host_cleanup(mUsbHostContext);
-}
-
-bool MtpClient::start() {
- Mutex::Autolock autoLock(mMutex);
-
- if (mThread)
- return true;
-
- mUsbHostContext = usb_host_init();
- if (!mUsbHostContext)
- return false;
-
- mThread = new MtpClientThread(this);
- mThread->run("MtpClientThread");
- // wait for the thread to do initial device discovery before returning
- mThreadStartCondition.wait(mMutex);
-
- return true;
-}
-
-void MtpClient::stop() {
- mDone = true;
-}
-
-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;
-}
-
-bool 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 mDone;
- }
-
- 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));
- } else if (interface->bInterfaceClass == 0xFF &&
- interface->bInterfaceSubClass == 0xFF &&
- interface->bInterfaceProtocol == 0) {
- char* interfaceName = usb_device_get_string(device, interface->iInterface);
- if (!interfaceName || strcmp(interfaceName, "MTP"))
- continue;
- // Looks like an android style MTP device
- LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
- usb_device_get_product_name(device));
- } else {
- // look for special cased devices based on vendor/product ID
- // we are doing this mainly for testing purposes
- uint16_t vendor = usb_device_get_vendor_id(device);
- uint16_t product = usb_device_get_product_id(device);
- if (!isMtpDevice(vendor, product)) {
- // not an MTP or PTP device
- continue;
- }
- // request MTP OS string and descriptor
- // some music players need to see this before entering MTP mode.
- char buffer[256];
- memset(buffer, 0, sizeof(buffer));
- int ret = usb_device_send_control(device,
- USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
- USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
- 0, sizeof(buffer), buffer);
- printf("usb_device_send_control returned %d errno: %d\n", ret, errno);
- if (ret > 0) {
- printf("got MTP string %s\n", buffer);
- ret = usb_device_send_control(device,
- USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
- 0, 4, sizeof(buffer), buffer);
- printf("OS descriptor got %d\n", ret);
- } else {
- printf("no MTP string\n");
- }
- }
-
- // if we got here, then we have a likely MTP or PTP 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 mDone;
- }
- 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 mDone;
- }
-
- if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
- LOGE("usb_device_claim_interface failed errno: %d\n", errno);
- return mDone;
- }
-
- MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
- ep_in_desc, ep_out_desc, ep_intr_desc);
- mDeviceList.add(mtpDevice);
- mtpDevice->initialize();
- deviceAdded(mtpDevice);
- return mDone;
- }
- }
-
- usb_device_close(device);
- return mDone;
-}
-
-bool MtpClient::usbDeviceRemoved(const char *devname) {
- for (int i = 0; i < mDeviceList.size(); i++) {
- MtpDevice* device = mDeviceList[i];
- if (!strcmp(devname, device->getDeviceName())) {
- deviceRemoved(device);
- mDeviceList.removeAt(i);
- delete device;
- LOGD("Camera removed!\n");
- break;
- }
- }
- return mDone;
-}
-
-bool MtpClient::usbDiscoveryDone() {
- Mutex::Autolock autoLock(mMutex);
- mThreadStartCondition.signal();
- return mDone;
-}
-
-bool MtpClient::threadLoop() {
- usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, usb_discovery_done, this);
- return false;
-}
-
-int MtpClient::usb_device_added(const char *devname, void* client_data) {
- LOGD("usb_device_added %s\n", devname);
- return ((MtpClient *)client_data)->usbDeviceAdded(devname);
-}
-
-int MtpClient::usb_device_removed(const char *devname, void* client_data) {
- LOGD("usb_device_removed %s\n", devname);
- return ((MtpClient *)client_data)->usbDeviceRemoved(devname);
-}
-
-int MtpClient::usb_discovery_done(void* client_data) {
- LOGD("usb_discovery_done\n");
- return ((MtpClient *)client_data)->usbDiscoveryDone();
-}
-
-} // namespace android
diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h
deleted file mode 100644
index fa5c527..0000000
--- a/media/mtp/MtpClient.h
+++ /dev/null
@@ -1,68 +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.
- */
-
-#ifndef _MTP_CLIENT_H
-#define _MTP_CLIENT_H
-
-#include "MtpTypes.h"
-
-#include <utils/threads.h>
-
-struct usb_host_context;
-
-namespace android {
-
-class MtpClientThread;
-
-class MtpClient {
-private:
- MtpDeviceList mDeviceList;
- MtpClientThread* mThread;
- Condition mThreadStartCondition;
- Mutex mMutex;
- struct usb_host_context* mUsbHostContext;
- bool mDone;
-
-public:
- MtpClient();
- virtual ~MtpClient();
-
- bool start();
- void stop();
-
- inline MtpDeviceList& getDeviceList() { return mDeviceList; }
- MtpDevice* getDevice(int id);
-
-
- virtual void deviceAdded(MtpDevice *device) = 0;
- virtual void deviceRemoved(MtpDevice *device) = 0;
-
-private:
- // these return true if we should stop monitoring USB and clean up
- bool usbDeviceAdded(const char *devname);
- bool usbDeviceRemoved(const char *devname);
- bool usbDiscoveryDone();
-
- friend class MtpClientThread;
- bool threadLoop();
- static int usb_device_added(const char *devname, void* client_data);
- static int usb_device_removed(const char *devname, void* client_data);
- static int usb_discovery_done(void* client_data);
-};
-
-}; // namespace android
-
-#endif // _MTP_CLIENT_H
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index d22c72f..d02ed90 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -38,6 +38,121 @@
namespace android {
+static bool isMtpDevice(uint16_t vendor, uint16_t product) {
+ // Sandisk Sansa Fuze
+ if (vendor == 0x0781 && product == 0x74c2)
+ return true;
+ // Samsung YP-Z5
+ if (vendor == 0x04e8 && product == 0x503c)
+ return true;
+ return false;
+}
+
+MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
+ struct usb_device *device = usb_device_new(deviceName, fd);
+ if (!device) {
+ LOGE("usb_device_new failed for %s", deviceName);
+ return NULL;
+ }
+
+ struct usb_descriptor_header* desc;
+ struct usb_descriptor_iter iter;
+
+ 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));
+ } else if (interface->bInterfaceClass == 0xFF &&
+ interface->bInterfaceSubClass == 0xFF &&
+ interface->bInterfaceProtocol == 0) {
+ char* interfaceName = usb_device_get_string(device, interface->iInterface);
+ if (!interfaceName || strcmp(interfaceName, "MTP"))
+ continue;
+ // Looks like an android style MTP device
+ LOGD("Found MTP device: \"%s\" \"%s\"\n", usb_device_get_manufacturer_name(device),
+ usb_device_get_product_name(device));
+ } else {
+ // look for special cased devices based on vendor/product ID
+ // we are doing this mainly for testing purposes
+ uint16_t vendor = usb_device_get_vendor_id(device);
+ uint16_t product = usb_device_get_product_id(device);
+ if (!isMtpDevice(vendor, product)) {
+ // not an MTP or PTP device
+ continue;
+ }
+ // request MTP OS string and descriptor
+ // some music players need to see this before entering MTP mode.
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ int ret = usb_device_send_control(device,
+ USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
+ USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
+ 0, sizeof(buffer), buffer);
+ printf("usb_device_send_control returned %d errno: %d\n", ret, errno);
+ if (ret > 0) {
+ printf("got MTP string %s\n", buffer);
+ ret = usb_device_send_control(device,
+ USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
+ 0, 4, sizeof(buffer), buffer);
+ printf("OS descriptor got %d\n", ret);
+ } else {
+ printf("no MTP string\n");
+ }
+ }
+
+ // if we got here, then we have a likely MTP or PTP 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 NULL;
+ }
+ 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 NULL;
+ }
+
+ if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
+ LOGE("usb_device_claim_interface failed errno: %d\n", errno);
+ return NULL;
+ }
+
+ MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
+ ep_in_desc, ep_out_desc, ep_intr_desc);
+ mtpDevice->initialize();
+ return mtpDevice;
+ }
+ }
+
+ usb_device_close(device);
+ LOGE("device not found");
+ return NULL;
+}
+
MtpDevice::MtpDevice(struct usb_device* device, int interface,
const struct usb_endpoint_descriptor *ep_in,
const struct usb_endpoint_descriptor *ep_out,
@@ -49,7 +164,6 @@ MtpDevice::MtpDevice(struct usb_device* device, int interface,
mRequestOut(NULL),
mRequestIntr(NULL),
mDeviceInfo(NULL),
- mID(usb_device_get_unique_id(device)),
mSessionID(0),
mTransactionID(0),
mReceivedResponse(false)
@@ -430,6 +544,98 @@ MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectForma
return NULL;
}
+bool MtpDevice::readObject(MtpObjectHandle handle,
+ bool (* callback)(void* data, int offset, int length, void* clientData),
+ int objectSize, void* clientData) {
+ Mutex::Autolock autoLock(mMutex);
+ bool result = false;
+
+ mRequest.reset();
+ mRequest.setParameter(1, handle);
+ if (sendRequest(MTP_OPERATION_GET_OBJECT)
+ && mData.readDataHeader(mRequestIn1)) {
+ uint32_t length = mData.getContainerLength();
+ if (length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
+ LOGE("readObject error objectSize: %d, length: %d",
+ objectSize, length);
+ goto fail;
+ }
+ length -= MTP_CONTAINER_HEADER_SIZE;
+ uint32_t remaining = length;
+ int offset = 0;
+
+ int initialDataLength = 0;
+ void* initialData = mData.getData(initialDataLength);
+ if (initialData) {
+ if (initialDataLength > 0) {
+ if (!callback(initialData, 0, initialDataLength, clientData))
+ goto fail;
+ remaining -= initialDataLength;
+ offset += initialDataLength;
+ }
+ free(initialData);
+ }
+
+ // USB reads greater than 16K don't work
+ char buffer1[16384], buffer2[16384];
+ mRequestIn1->buffer = buffer1;
+ mRequestIn2->buffer = buffer2;
+ struct usb_request* req = mRequestIn1;
+ void* writeBuffer = NULL;
+ int writeLength = 0;
+
+ while (remaining > 0 || writeBuffer) {
+ if (remaining > 0) {
+ // queue up a read request
+ req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
+ if (mData.readDataAsync(req)) {
+ LOGE("readDataAsync failed");
+ goto fail;
+ }
+ } else {
+ req = NULL;
+ }
+
+ if (writeBuffer) {
+ // write previous buffer
+ if (!callback(writeBuffer, offset, writeLength, clientData)) {
+ LOGE("write failed");
+ // wait for pending read before failing
+ if (req)
+ mData.readDataWait(mDevice);
+ goto fail;
+ }
+ offset += writeLength;
+ writeBuffer = NULL;
+ }
+
+ // wait for read to complete
+ if (req) {
+ int read = mData.readDataWait(mDevice);
+ if (read < 0)
+ goto fail;
+
+ if (read > 0) {
+ writeBuffer = req->buffer;
+ writeLength = read;
+ remaining -= read;
+ req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+ } else {
+ writeBuffer = NULL;
+ }
+ }
+ }
+
+ MtpResponseCode response = readResponse();
+ if (response == MTP_RESPONSE_OK)
+ result = true;
+ }
+
+fail:
+ return result;
+}
+
+
// reads the object's data and writes it to the specified file path
bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
LOGD("readObject: %s", destPath);
@@ -507,10 +713,14 @@ bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int gro
if (read < 0)
goto fail;
- writeBuffer = req->buffer;
- writeLength = read;
- remaining -= read;
- req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+ if (read > 0) {
+ writeBuffer = req->buffer;
+ writeLength = read;
+ remaining -= read;
+ req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+ } else {
+ writeBuffer = NULL;
+ }
}
}
diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h
index d0a0fb3..b69203e 100644
--- a/media/mtp/MtpDevice.h
+++ b/media/mtp/MtpDevice.h
@@ -45,9 +45,6 @@ private:
MtpDeviceInfo* mDeviceInfo;
MtpPropertyList mDeviceProperties;
- // a unique ID for the device
- int mID;
-
// current session ID
MtpSessionID mSessionID;
// current transaction ID
@@ -67,9 +64,10 @@ public:
const struct usb_endpoint_descriptor *ep_in,
const struct usb_endpoint_descriptor *ep_out,
const struct usb_endpoint_descriptor *ep_intr);
- virtual ~MtpDevice();
- inline int getID() const { return mID; }
+ static MtpDevice* open(const char* deviceName, int fd);
+
+ virtual ~MtpDevice();
void initialize();
void close();
@@ -97,7 +95,11 @@ public:
MtpProperty* getDevicePropDesc(MtpDeviceProperty code);
MtpProperty* getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format);
- bool readObject(MtpObjectHandle handle, const char* destPath, int group,
+ bool readObject(MtpObjectHandle handle,
+ bool (* callback)(void* data, int offset,
+ int length, void* clientData),
+ int objectSize, void* clientData);
+ bool readObject(MtpObjectHandle handle, const char* destPath, int group,
int perm);
private: