diff options
author | Mike Lockwood <lockwood@android.com> | 2010-12-30 15:38:45 -0500 |
---|---|---|
committer | Mike Lockwood <lockwood@android.com> | 2011-01-22 15:56:09 -0800 |
commit | 8182e72479a8b0d832df9c392890b25bfa6f97b5 (patch) | |
tree | b641d4bd5b2b65cb07fa0d6846b59ad80a959a60 | |
parent | 97d7e4c227ca26c125c1b587c5ed04ab82d0f4e1 (diff) | |
download | frameworks_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.java | 270 | ||||
-rw-r--r-- | media/java/android/mtp/MtpDevice.java | 141 | ||||
-rw-r--r-- | media/java/android/mtp/MtpDeviceInfo.java | 72 | ||||
-rw-r--r-- | media/java/android/mtp/MtpObjectInfo.java | 257 | ||||
-rw-r--r-- | media/java/android/mtp/MtpStorageInfo.java | 82 | ||||
-rw-r--r-- | media/jni/Android.mk | 1 | ||||
-rw-r--r-- | media/jni/android_media_MediaPlayer.cpp | 6 | ||||
-rw-r--r-- | media/jni/android_mtp_MtpDevice.cpp | 663 | ||||
-rw-r--r-- | media/mtp/Android.mk | 2 | ||||
-rw-r--r-- | media/mtp/MtpClient.cpp | 251 | ||||
-rw-r--r-- | media/mtp/MtpClient.h | 68 | ||||
-rw-r--r-- | media/mtp/MtpDevice.cpp | 220 | ||||
-rw-r--r-- | media/mtp/MtpDevice.h | 14 |
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: |