In the MTP spec, the object size is stored in MtpObjectInfo as unsigned 32-bit integer and fetched by the getObjectInfo operation. For the objects that are more than 4GB, the object size is provided as one of extra properties, which are fetched by different operation. The CL adds to getObjectSizeLong hidden method to Java MtpDevice class so that client code can obtain 4GB+ object size. BUG=27805369 Change-Id: I8a5b85c8db39734f00e49709c61b271eb48ff33d
398 lines
15 KiB
Java
398 lines
15 KiB
Java
/*
|
|
* 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.usb.UsbDevice;
|
|
import android.hardware.usb.UsbDeviceConnection;
|
|
import android.os.CancellationSignal;
|
|
import android.os.ParcelFileDescriptor;
|
|
|
|
import com.android.internal.util.Preconditions;
|
|
|
|
import java.io.IOException;
|
|
|
|
/**
|
|
* This class represents an MTP or PTP device connected on the USB host bus. An application can
|
|
* instantiate an object of this type, by referencing an attached {@link
|
|
* android.hardware.usb.UsbDevice} and then use methods in this class to get information about the
|
|
* device and objects stored on it, as well as open the connection and transfer data.
|
|
*/
|
|
public final class MtpDevice {
|
|
|
|
private static final String TAG = "MtpDevice";
|
|
|
|
private final UsbDevice mDevice;
|
|
|
|
static {
|
|
System.loadLibrary("media_jni");
|
|
}
|
|
|
|
/**
|
|
* MtpClient constructor
|
|
*
|
|
* @param device the {@link android.hardware.usb.UsbDevice} for the MTP or PTP device
|
|
*/
|
|
public MtpDevice(UsbDevice device) {
|
|
mDevice = device;
|
|
}
|
|
|
|
/**
|
|
* Opens the MTP device. Once the device is open it takes ownership of the
|
|
* {@link android.hardware.usb.UsbDeviceConnection}.
|
|
* The connection will be closed when you call {@link #close()}
|
|
* The connection will also be closed if this method fails.
|
|
*
|
|
* @param connection an open {@link android.hardware.usb.UsbDeviceConnection} for the device
|
|
* @return true if the device was successfully opened.
|
|
*/
|
|
public boolean open(UsbDeviceConnection connection) {
|
|
boolean result = native_open(mDevice.getDeviceName(), connection.getFileDescriptor());
|
|
if (!result) {
|
|
connection.close();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Closes all resources related to the MtpDevice object.
|
|
* After this is called, the object can not be used until {@link #open} is called again
|
|
* with a new {@link android.hardware.usb.UsbDeviceConnection}.
|
|
*/
|
|
public void close() {
|
|
native_close();
|
|
}
|
|
|
|
@Override
|
|
protected void finalize() throws Throwable {
|
|
try {
|
|
native_close();
|
|
} finally {
|
|
super.finalize();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the name of the USB device
|
|
* This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceName}
|
|
* for the device's {@link android.hardware.usb.UsbDevice}
|
|
*
|
|
* @return the device name
|
|
*/
|
|
public String getDeviceName() {
|
|
return mDevice.getDeviceName();
|
|
}
|
|
|
|
/**
|
|
* Returns the USB ID of the USB device.
|
|
* This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceId}
|
|
* for the device's {@link android.hardware.usb.UsbDevice}
|
|
*
|
|
* @return the device ID
|
|
*/
|
|
public int getDeviceId() {
|
|
return mDevice.getDeviceId();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return mDevice.getDeviceName();
|
|
}
|
|
|
|
/**
|
|
* Returns the {@link MtpDeviceInfo} for this device
|
|
*
|
|
* @return the device info
|
|
*/
|
|
public MtpDeviceInfo getDeviceInfo() {
|
|
return native_get_device_info();
|
|
}
|
|
|
|
/**
|
|
* Returns the list of IDs for all storage units on this device
|
|
* Information about each storage unit can be accessed via {@link #getStorageInfo}.
|
|
*
|
|
* @return the list of storage IDs
|
|
*/
|
|
public int[] getStorageIds() {
|
|
return native_get_storage_ids();
|
|
}
|
|
|
|
/**
|
|
* Returns the list of object handles for all objects on the given storage unit,
|
|
* with the given format and parent.
|
|
* Information about each object can be accessed via {@link #getObjectInfo}.
|
|
*
|
|
* @param storageId the storage unit to query
|
|
* @param format the format of the object to return, or zero for all formats
|
|
* @param objectHandle the parent object to query, -1 for the storage root,
|
|
* or zero for all objects
|
|
* @return the object handles
|
|
*/
|
|
public int[] getObjectHandles(int storageId, int format, int objectHandle) {
|
|
return native_get_object_handles(storageId, format, objectHandle);
|
|
}
|
|
|
|
/**
|
|
* Returns the data for an object as a byte array.
|
|
* This call may block for an arbitrary amount of time depending on the size
|
|
* of the data and speed of the devices.
|
|
*
|
|
* @param objectHandle handle of the object to read
|
|
* @param objectSize the size of the object (this should match
|
|
* {@link MtpObjectInfo#getCompressedSize})
|
|
* @return the object's data, or null if reading fails
|
|
*/
|
|
public byte[] getObject(int objectHandle, int objectSize) {
|
|
Preconditions.checkArgumentNonnegative(objectSize, "objectSize should not be negative");
|
|
return native_get_object(objectHandle, objectSize);
|
|
}
|
|
|
|
/**
|
|
* Obtains object bytes in the specified range and writes it to an array.
|
|
* This call may block for an arbitrary amount of time depending on the size
|
|
* of the data and speed of the devices.
|
|
*
|
|
* @param objectHandle handle of the object to read
|
|
* @param offset Start index of reading range. It must be a non-negative value at most
|
|
* 0xffffffff.
|
|
* @param size Size of reading range. It must be a non-negative value at most 0xffffffff. If
|
|
* 0xffffffff is specified, the method obtains the full bytes of object.
|
|
* @param buffer Array to write data.
|
|
* @return Size of bytes that are actually read.
|
|
*/
|
|
public long getPartialObject(int objectHandle, long offset, long size, byte[] buffer)
|
|
throws IOException {
|
|
return native_get_partial_object(objectHandle, offset, size, buffer);
|
|
}
|
|
|
|
/**
|
|
* Obtains object bytes in the specified range and writes it to an array.
|
|
* This call may block for an arbitrary amount of time depending on the size
|
|
* of the data and speed of the devices.
|
|
*
|
|
* This is a vender-extended operation supported by Android that enables us to pass
|
|
* unsigned 64-bit offset. Check if the MTP device supports the operation by using
|
|
* {@link MtpDeviceInfo#getOperationsSupported()}.
|
|
*
|
|
* @param objectHandle handle of the object to read
|
|
* @param offset Start index of reading range. It must be a non-negative value.
|
|
* @param size Size of reading range. It must be a non-negative value at most 0xffffffff.
|
|
* @param buffer Array to write data.
|
|
* @return Size of bytes that are actually read.
|
|
* @see MtpConstants#OPERATION_GET_PARTIAL_OBJECT_64
|
|
*/
|
|
public long getPartialObject64(int objectHandle, long offset, long size, byte[] buffer)
|
|
throws IOException {
|
|
return native_get_partial_object_64(objectHandle, offset, size, buffer);
|
|
}
|
|
|
|
/**
|
|
* Returns the thumbnail data for an object as a byte array.
|
|
* The size and format of the thumbnail data can be determined via
|
|
* {@link MtpObjectInfo#getThumbCompressedSize} and
|
|
* {@link MtpObjectInfo#getThumbFormat}.
|
|
* For typical devices the format is JPEG.
|
|
*
|
|
* @param objectHandle handle of the object to read
|
|
* @return the object's thumbnail, or null if reading fails
|
|
*/
|
|
public byte[] getThumbnail(int objectHandle) {
|
|
return native_get_thumbnail(objectHandle);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the {@link MtpStorageInfo} for a storage unit.
|
|
*
|
|
* @param storageId the ID of the storage unit
|
|
* @return the MtpStorageInfo
|
|
*/
|
|
public MtpStorageInfo getStorageInfo(int storageId) {
|
|
return native_get_storage_info(storageId);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the {@link MtpObjectInfo} for an object.
|
|
*
|
|
* @param objectHandle the handle of the object
|
|
* @return the MtpObjectInfo
|
|
*/
|
|
public MtpObjectInfo getObjectInfo(int objectHandle) {
|
|
return native_get_object_info(objectHandle);
|
|
}
|
|
|
|
/**
|
|
* Deletes an object on the device. This call may block, since
|
|
* deleting a directory containing many files may take a long time
|
|
* on some devices.
|
|
*
|
|
* @param objectHandle handle of the object to delete
|
|
* @return true if the deletion succeeds
|
|
*/
|
|
public boolean deleteObject(int objectHandle) {
|
|
return native_delete_object(objectHandle);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the object handle for the parent of an object on the device.
|
|
*
|
|
* @param objectHandle handle of the object to query
|
|
* @return the parent's handle, or zero if it is in the root of the storage
|
|
*/
|
|
public long getParent(int objectHandle) {
|
|
return native_get_parent(objectHandle);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the ID of the storage unit containing the given object on the device.
|
|
*
|
|
* @param objectHandle handle of the object to query
|
|
* @return the object's storage unit ID
|
|
*/
|
|
public long getStorageId(int objectHandle) {
|
|
return native_get_storage_id(objectHandle);
|
|
}
|
|
|
|
/**
|
|
* Copies the data for an object to a file in external storage.
|
|
* This call may block for an arbitrary amount of time depending on the size
|
|
* of the data and speed of the devices.
|
|
*
|
|
* @param objectHandle handle of the object to read
|
|
* @param destPath path to destination for the file transfer.
|
|
* This path should be in the external storage as defined by
|
|
* {@link android.os.Environment#getExternalStorageDirectory}
|
|
* @return true if the file transfer succeeds
|
|
*/
|
|
public boolean importFile(int objectHandle, String destPath) {
|
|
return native_import_file(objectHandle, destPath);
|
|
}
|
|
|
|
/**
|
|
* Copies the data for an object to a file descriptor.
|
|
* This call may block for an arbitrary amount of time depending on the size
|
|
* of the data and speed of the devices. The file descriptor is not closed
|
|
* on completion, and must be done by the caller.
|
|
*
|
|
* @param objectHandle handle of the object to read
|
|
* @param descriptor file descriptor to write the data to for the file transfer.
|
|
* @return true if the file transfer succeeds
|
|
*/
|
|
public boolean importFile(int objectHandle, ParcelFileDescriptor descriptor) {
|
|
return native_import_file(objectHandle, descriptor.getFd());
|
|
}
|
|
|
|
/**
|
|
* Copies the data for an object from a file descriptor.
|
|
* This call may block for an arbitrary amount of time depending on the size
|
|
* of the data and speed of the devices. The file descriptor is not closed
|
|
* on completion, and must be done by the caller.
|
|
*
|
|
* @param objectHandle handle of the target file
|
|
* @param size size of the file in bytes
|
|
* @param descriptor file descriptor to read the data from.
|
|
* @return true if the file transfer succeeds
|
|
*/
|
|
public boolean sendObject(int objectHandle, long size, ParcelFileDescriptor descriptor) {
|
|
return native_send_object(objectHandle, size, descriptor.getFd());
|
|
}
|
|
|
|
/**
|
|
* Uploads an object metadata for a new entry. The {@link MtpObjectInfo} can be
|
|
* created with the {@link MtpObjectInfo.Builder} class.
|
|
*
|
|
* The returned {@link MtpObjectInfo} has the new object handle field filled in.
|
|
*
|
|
* @param info metadata of the entry
|
|
* @return object info of the created entry
|
|
*/
|
|
public MtpObjectInfo sendObjectInfo(MtpObjectInfo info) {
|
|
return native_send_object_info(info);
|
|
}
|
|
|
|
/**
|
|
* Reads an event from the device. It blocks the current thread until it gets an event.
|
|
* It throws OperationCanceledException if it is cancelled by signal.
|
|
*
|
|
* @param signal signal for cancellation
|
|
* @return obtained event
|
|
*/
|
|
public MtpEvent readEvent(CancellationSignal signal) {
|
|
final int handle = native_submit_event_request();
|
|
|
|
if (handle < 0) {
|
|
throw new IllegalStateException("Other thread is reading an event.");
|
|
}
|
|
|
|
if (signal != null) {
|
|
signal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
|
|
@Override
|
|
public void onCancel() {
|
|
native_discard_event_request(handle);
|
|
}
|
|
});
|
|
}
|
|
|
|
try {
|
|
return native_reap_event_request(handle);
|
|
} finally {
|
|
if (signal != null) {
|
|
signal.setOnCancelListener(null);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns object size in 64-bit integer.
|
|
*
|
|
* The object size stored in MtpObjectInfo is unsigned 32-bit integer.
|
|
* The methods reads 64-bit object size from the object property so that it can fetch 4GB+
|
|
* object size correctly.
|
|
* @hide
|
|
*/
|
|
public long getObjectSizeLong(int handle, int format) throws IOException {
|
|
return native_get_object_size_long(handle, format);
|
|
}
|
|
|
|
// used by the JNI code
|
|
private long 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, long objectSize);
|
|
private native long native_get_partial_object(
|
|
int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
|
|
private native int native_get_partial_object_64(
|
|
int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
|
|
private native byte[] native_get_thumbnail(int objectHandle);
|
|
private native boolean native_delete_object(int objectHandle);
|
|
private native int native_get_parent(int objectHandle);
|
|
private native int native_get_storage_id(int objectHandle);
|
|
private native boolean native_import_file(int objectHandle, String destPath);
|
|
private native boolean native_import_file(int objectHandle, int fd);
|
|
private native boolean native_send_object(int objectHandle, long size, int fd);
|
|
private native MtpObjectInfo native_send_object_info(MtpObjectInfo info);
|
|
private native int native_submit_event_request();
|
|
private native MtpEvent native_reap_event_request(int handle);
|
|
private native void native_discard_event_request(int handle);
|
|
private native long native_get_object_size_long(int handle, int format) throws IOException;
|
|
}
|