Home | History | Annotate | Download | only in mtp
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.mtp;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.content.Context;
     22 import android.hardware.usb.UsbDevice;
     23 import android.hardware.usb.UsbDeviceConnection;
     24 import android.os.CancellationSignal;
     25 import android.os.ParcelFileDescriptor;
     26 
     27 import android.os.UserManager;
     28 import com.android.internal.annotations.GuardedBy;
     29 import com.android.internal.util.Preconditions;
     30 import dalvik.system.CloseGuard;
     31 
     32 import java.io.IOException;
     33 
     34 /**
     35  * This class represents an MTP or PTP device connected on the USB host bus. An application can
     36  * instantiate an object of this type, by referencing an attached {@link
     37  * android.hardware.usb.UsbDevice} and then use methods in this class to get information about the
     38  * device and objects stored on it, as well as open the connection and transfer data.
     39  */
     40 public final class MtpDevice {
     41 
     42     private static final String TAG = "MtpDevice";
     43 
     44     private final UsbDevice mDevice;
     45 
     46     static {
     47         System.loadLibrary("media_jni");
     48     }
     49 
     50     /** Make sure that MTP device is closed properly */
     51     @GuardedBy("mLock")
     52     private CloseGuard mCloseGuard = CloseGuard.get();
     53 
     54     /** Current connection to the {@link #mDevice}, or null if device is not connected */
     55     @GuardedBy("mLock")
     56     private UsbDeviceConnection mConnection;
     57 
     58     private final Object mLock = new Object();
     59 
     60     /**
     61      * MtpClient constructor
     62      *
     63      * @param device the {@link android.hardware.usb.UsbDevice} for the MTP or PTP device
     64      */
     65     public MtpDevice(@NonNull UsbDevice device) {
     66         Preconditions.checkNotNull(device);
     67         mDevice = device;
     68     }
     69 
     70     /**
     71      * Opens the MTP device.  Once the device is open it takes ownership of the
     72      * {@link android.hardware.usb.UsbDeviceConnection}.
     73      * The connection will be closed when you call {@link #close()}
     74      * The connection will also be closed if this method fails.
     75      *
     76      * @param connection an open {@link android.hardware.usb.UsbDeviceConnection} for the device
     77      * @return true if the device was successfully opened.
     78      */
     79     public boolean open(@NonNull UsbDeviceConnection connection) {
     80         boolean result = false;
     81 
     82         Context context = connection.getContext();
     83 
     84         synchronized (mLock) {
     85             if (context != null) {
     86                 UserManager userManager = (UserManager) context
     87                         .getSystemService(Context.USER_SERVICE);
     88 
     89                 if (!userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) {
     90                     result = native_open(mDevice.getDeviceName(), connection.getFileDescriptor());
     91                 }
     92             }
     93 
     94             if (!result) {
     95                 connection.close();
     96             } else {
     97                 mConnection = connection;
     98                 mCloseGuard.open("close");
     99             }
    100         }
    101 
    102         return result;
    103     }
    104 
    105     /**
    106      * Closes all resources related to the MtpDevice object.
    107      * After this is called, the object can not be used until {@link #open} is called again
    108      * with a new {@link android.hardware.usb.UsbDeviceConnection}.
    109      */
    110     public void close() {
    111         synchronized (mLock) {
    112             if (mConnection != null) {
    113                 mCloseGuard.close();
    114 
    115                 native_close();
    116 
    117                 mConnection.close();
    118                 mConnection = null;
    119             }
    120         }
    121     }
    122 
    123     @Override
    124     protected void finalize() throws Throwable {
    125         try {
    126             if (mCloseGuard != null) {
    127                 mCloseGuard.warnIfOpen();
    128             }
    129 
    130             close();
    131         } finally {
    132             super.finalize();
    133         }
    134     }
    135 
    136     /**
    137      * Returns the name of the USB device
    138      * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceName}
    139      * for the device's {@link android.hardware.usb.UsbDevice}
    140      *
    141      * @return the device name
    142      */
    143     public @NonNull String getDeviceName() {
    144         return mDevice.getDeviceName();
    145     }
    146 
    147     /**
    148      * Returns the USB ID of the USB device.
    149      * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceId}
    150      * for the device's {@link android.hardware.usb.UsbDevice}
    151      *
    152      * @return the device ID
    153      */
    154     public int getDeviceId() {
    155         return mDevice.getDeviceId();
    156     }
    157 
    158     @Override
    159     public @NonNull String toString() {
    160         return mDevice.getDeviceName();
    161     }
    162 
    163     /**
    164      * Returns the {@link MtpDeviceInfo} for this device
    165      *
    166      * @return the device info, or null if fetching device info fails
    167      */
    168     public @Nullable MtpDeviceInfo getDeviceInfo() {
    169         return native_get_device_info();
    170     }
    171 
    172     /**
    173      * Returns the list of IDs for all storage units on this device
    174      * Information about each storage unit can be accessed via {@link #getStorageInfo}.
    175      *
    176      * @return the list of storage IDs, or null if fetching storage IDs fails
    177      */
    178     public @Nullable int[] getStorageIds() {
    179         return native_get_storage_ids();
    180     }
    181 
    182     /**
    183      * Returns the list of object handles for all objects on the given storage unit,
    184      * with the given format and parent.
    185      * Information about each object can be accessed via {@link #getObjectInfo}.
    186      *
    187      * @param storageId the storage unit to query
    188      * @param format the format of the object to return, or zero for all formats
    189      * @param objectHandle the parent object to query, -1 for the storage root,
    190      *     or zero for all objects
    191      * @return the object handles, or null if fetching object handles fails
    192      */
    193     public @Nullable int[] getObjectHandles(int storageId, int format, int objectHandle) {
    194         return native_get_object_handles(storageId, format, objectHandle);
    195     }
    196 
    197     /**
    198      * Returns the data for an object as a byte array.
    199      * This call may block for an arbitrary amount of time depending on the size
    200      * of the data and speed of the devices.
    201      *
    202      * @param objectHandle handle of the object to read
    203      * @param objectSize the size of the object (this should match
    204      *      {@link MtpObjectInfo#getCompressedSize})
    205      * @return the object's data, or null if reading fails
    206      */
    207     public @Nullable byte[] getObject(int objectHandle, int objectSize) {
    208         Preconditions.checkArgumentNonnegative(objectSize, "objectSize should not be negative");
    209         return native_get_object(objectHandle, objectSize);
    210     }
    211 
    212     /**
    213      * Obtains object bytes in the specified range and writes it to an array.
    214      * This call may block for an arbitrary amount of time depending on the size
    215      * of the data and speed of the devices.
    216      *
    217      * @param objectHandle handle of the object to read
    218      * @param offset Start index of reading range. It must be a non-negative value at most
    219      *     0xffffffff.
    220      * @param size Size of reading range. It must be a non-negative value at most Integer.MAX_VALUE
    221      *     or 0xffffffff. If 0xffffffff is specified, the method obtains the full bytes of object.
    222      * @param buffer Array to write data.
    223      * @return Size of bytes that are actually read.
    224      */
    225     public long getPartialObject(int objectHandle, long offset, long size, @NonNull byte[] buffer)
    226             throws IOException {
    227         return native_get_partial_object(objectHandle, offset, size, buffer);
    228     }
    229 
    230     /**
    231      * Obtains object bytes in the specified range and writes it to an array.
    232      * This call may block for an arbitrary amount of time depending on the size
    233      * of the data and speed of the devices.
    234      *
    235      * This is a vender-extended operation supported by Android that enables us to pass
    236      * unsigned 64-bit offset. Check if the MTP device supports the operation by using
    237      * {@link MtpDeviceInfo#getOperationsSupported()}.
    238      *
    239      * @param objectHandle handle of the object to read
    240      * @param offset Start index of reading range. It must be a non-negative value.
    241      * @param size Size of reading range. It must be a non-negative value at most Integer.MAX_VALUE.
    242      * @param buffer Array to write data.
    243      * @return Size of bytes that are actually read.
    244      * @see MtpConstants#OPERATION_GET_PARTIAL_OBJECT_64
    245      */
    246     public long getPartialObject64(int objectHandle, long offset, long size, @NonNull byte[] buffer)
    247             throws IOException {
    248         return native_get_partial_object_64(objectHandle, offset, size, buffer);
    249     }
    250 
    251     /**
    252      * Returns the thumbnail data for an object as a byte array.
    253      * The size and format of the thumbnail data can be determined via
    254      * {@link MtpObjectInfo#getThumbCompressedSize} and
    255      * {@link MtpObjectInfo#getThumbFormat}.
    256      * For typical devices the format is JPEG.
    257      *
    258      * @param objectHandle handle of the object to read
    259      * @return the object's thumbnail, or null if reading fails
    260      */
    261     public @Nullable byte[] getThumbnail(int objectHandle) {
    262         return native_get_thumbnail(objectHandle);
    263     }
    264 
    265     /**
    266      * Retrieves the {@link MtpStorageInfo} for a storage unit.
    267      *
    268      * @param storageId the ID of the storage unit
    269      * @return the MtpStorageInfo, or null if fetching storage info fails
    270      */
    271     public @Nullable MtpStorageInfo getStorageInfo(int storageId) {
    272         return native_get_storage_info(storageId);
    273     }
    274 
    275     /**
    276      * Retrieves the {@link MtpObjectInfo} for an object.
    277      *
    278      * @param objectHandle the handle of the object
    279      * @return the MtpObjectInfo, or null if fetching object info fails
    280      */
    281     public @Nullable MtpObjectInfo getObjectInfo(int objectHandle) {
    282         return native_get_object_info(objectHandle);
    283     }
    284 
    285     /**
    286      * Deletes an object on the device.  This call may block, since
    287      * deleting a directory containing many files may take a long time
    288      * on some devices.
    289      *
    290      * @param objectHandle handle of the object to delete
    291      * @return true if the deletion succeeds
    292      */
    293     public boolean deleteObject(int objectHandle) {
    294         return native_delete_object(objectHandle);
    295     }
    296 
    297     /**
    298      * Retrieves the object handle for the parent of an object on the device.
    299      *
    300      * @param objectHandle handle of the object to query
    301      * @return the parent's handle, or zero if it is in the root of the storage
    302      */
    303     public long getParent(int objectHandle) {
    304         return native_get_parent(objectHandle);
    305     }
    306 
    307     /**
    308      * Retrieves the ID of the storage unit containing the given object on the device.
    309      *
    310      * @param objectHandle handle of the object to query
    311      * @return the object's storage unit ID
    312      */
    313     public long getStorageId(int objectHandle) {
    314         return native_get_storage_id(objectHandle);
    315     }
    316 
    317     /**
    318      * Copies the data for an object to a file in external storage.
    319      * This call may block for an arbitrary amount of time depending on the size
    320      * of the data and speed of the devices.
    321      *
    322      * @param objectHandle handle of the object to read
    323      * @param destPath path to destination for the file transfer.
    324      *      This path should be in the external storage as defined by
    325      *      {@link android.os.Environment#getExternalStorageDirectory}
    326      * @return true if the file transfer succeeds
    327      */
    328     public boolean importFile(int objectHandle, @NonNull String destPath) {
    329         return native_import_file(objectHandle, destPath);
    330     }
    331 
    332     /**
    333      * Copies the data for an object to a file descriptor.
    334      * This call may block for an arbitrary amount of time depending on the size
    335      * of the data and speed of the devices. The file descriptor is not closed
    336      * on completion, and must be done by the caller.
    337      *
    338      * @param objectHandle handle of the object to read
    339      * @param descriptor file descriptor to write the data to for the file transfer.
    340      * @return true if the file transfer succeeds
    341      */
    342     public boolean importFile(int objectHandle, @NonNull ParcelFileDescriptor descriptor) {
    343         return native_import_file(objectHandle, descriptor.getFd());
    344     }
    345 
    346     /**
    347      * Copies the data for an object from a file descriptor.
    348      * This call may block for an arbitrary amount of time depending on the size
    349      * of the data and speed of the devices. The file descriptor is not closed
    350      * on completion, and must be done by the caller.
    351      *
    352      * @param objectHandle handle of the target file
    353      * @param size size of the file in bytes
    354      * @param descriptor file descriptor to read the data from.
    355      * @return true if the file transfer succeeds
    356      */
    357     public boolean sendObject(
    358             int objectHandle, long size, @NonNull ParcelFileDescriptor descriptor) {
    359         return native_send_object(objectHandle, size, descriptor.getFd());
    360     }
    361 
    362     /**
    363      * Uploads an object metadata for a new entry. The {@link MtpObjectInfo} can be
    364      * created with the {@link MtpObjectInfo.Builder} class.
    365      *
    366      * The returned {@link MtpObjectInfo} has the new object handle field filled in.
    367      *
    368      * @param info metadata of the entry
    369      * @return object info of the created entry, or null if sending object info fails
    370      */
    371     public @Nullable MtpObjectInfo sendObjectInfo(@NonNull MtpObjectInfo info) {
    372         return native_send_object_info(info);
    373     }
    374 
    375     /**
    376      * Reads an event from the device. It blocks the current thread until it gets an event.
    377      * It throws OperationCanceledException if it is cancelled by signal.
    378      *
    379      * @param signal signal for cancellation
    380      * @return obtained event
    381      * @throws IOException
    382      */
    383     public @NonNull MtpEvent readEvent(@Nullable CancellationSignal signal) throws IOException {
    384         final int handle = native_submit_event_request();
    385         Preconditions.checkState(handle >= 0, "Other thread is reading an event.");
    386 
    387         if (signal != null) {
    388             signal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
    389                 @Override
    390                 public void onCancel() {
    391                     native_discard_event_request(handle);
    392                 }
    393             });
    394         }
    395 
    396         try {
    397             return native_reap_event_request(handle);
    398         } finally {
    399             if (signal != null) {
    400                 signal.setOnCancelListener(null);
    401             }
    402         }
    403     }
    404 
    405     /**
    406      * Returns object size in 64-bit integer.
    407      *
    408      * Though MtpObjectInfo#getCompressedSize returns the object size in 32-bit unsigned integer,
    409      * this method returns the object size in 64-bit integer from the object property. Thus it can
    410      * fetch 4GB+ object size correctly. If the device does not support objectSize property, it
    411      * throws IOException.
    412      * @hide
    413      */
    414     public long getObjectSizeLong(int handle, int format) throws IOException {
    415         return native_get_object_size_long(handle, format);
    416     }
    417 
    418     // used by the JNI code
    419     private long mNativeContext;
    420 
    421     private native boolean native_open(String deviceName, int fd);
    422     private native void native_close();
    423     private native MtpDeviceInfo native_get_device_info();
    424     private native int[] native_get_storage_ids();
    425     private native MtpStorageInfo native_get_storage_info(int storageId);
    426     private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
    427     private native MtpObjectInfo native_get_object_info(int objectHandle);
    428     private native byte[] native_get_object(int objectHandle, long objectSize);
    429     private native long native_get_partial_object(
    430             int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
    431     private native int native_get_partial_object_64(
    432             int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
    433     private native byte[] native_get_thumbnail(int objectHandle);
    434     private native boolean native_delete_object(int objectHandle);
    435     private native int native_get_parent(int objectHandle);
    436     private native int native_get_storage_id(int objectHandle);
    437     private native boolean native_import_file(int objectHandle, String destPath);
    438     private native boolean native_import_file(int objectHandle, int fd);
    439     private native boolean native_send_object(int objectHandle, long size, int fd);
    440     private native MtpObjectInfo native_send_object_info(MtpObjectInfo info);
    441     private native int native_submit_event_request() throws IOException;
    442     private native MtpEvent native_reap_event_request(int handle) throws IOException;
    443     private native void native_discard_event_request(int handle);
    444     private native long native_get_object_size_long(int handle, int format) throws IOException;
    445 }
    446