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             mCloseGuard.warnIfOpen();
    127             close();
    128         } finally {
    129             super.finalize();
    130         }
    131     }
    132 
    133     /**
    134      * Returns the name of the USB device
    135      * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceName}
    136      * for the device's {@link android.hardware.usb.UsbDevice}
    137      *
    138      * @return the device name
    139      */
    140     public @NonNull String getDeviceName() {
    141         return mDevice.getDeviceName();
    142     }
    143 
    144     /**
    145      * Returns the USB ID of the USB device.
    146      * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceId}
    147      * for the device's {@link android.hardware.usb.UsbDevice}
    148      *
    149      * @return the device ID
    150      */
    151     public int getDeviceId() {
    152         return mDevice.getDeviceId();
    153     }
    154 
    155     @Override
    156     public @NonNull String toString() {
    157         return mDevice.getDeviceName();
    158     }
    159 
    160     /**
    161      * Returns the {@link MtpDeviceInfo} for this device
    162      *
    163      * @return the device info, or null if fetching device info fails
    164      */
    165     public @Nullable MtpDeviceInfo getDeviceInfo() {
    166         return native_get_device_info();
    167     }
    168 
    169     /**
    170      * Returns the list of IDs for all storage units on this device
    171      * Information about each storage unit can be accessed via {@link #getStorageInfo}.
    172      *
    173      * @return the list of storage IDs, or null if fetching storage IDs fails
    174      */
    175     public @Nullable int[] getStorageIds() {
    176         return native_get_storage_ids();
    177     }
    178 
    179     /**
    180      * Returns the list of object handles for all objects on the given storage unit,
    181      * with the given format and parent.
    182      * Information about each object can be accessed via {@link #getObjectInfo}.
    183      *
    184      * @param storageId the storage unit to query
    185      * @param format the format of the object to return, or zero for all formats
    186      * @param objectHandle the parent object to query, -1 for the storage root,
    187      *     or zero for all objects
    188      * @return the object handles, or null if fetching object handles fails
    189      */
    190     public @Nullable int[] getObjectHandles(int storageId, int format, int objectHandle) {
    191         return native_get_object_handles(storageId, format, objectHandle);
    192     }
    193 
    194     /**
    195      * Returns the data for an object as a byte array.
    196      * This call may block for an arbitrary amount of time depending on the size
    197      * of the data and speed of the devices.
    198      *
    199      * @param objectHandle handle of the object to read
    200      * @param objectSize the size of the object (this should match
    201      *      {@link MtpObjectInfo#getCompressedSize})
    202      * @return the object's data, or null if reading fails
    203      */
    204     public @Nullable byte[] getObject(int objectHandle, int objectSize) {
    205         Preconditions.checkArgumentNonnegative(objectSize, "objectSize should not be negative");
    206         return native_get_object(objectHandle, objectSize);
    207     }
    208 
    209     /**
    210      * Obtains object bytes in the specified range and writes it to an array.
    211      * This call may block for an arbitrary amount of time depending on the size
    212      * of the data and speed of the devices.
    213      *
    214      * @param objectHandle handle of the object to read
    215      * @param offset Start index of reading range. It must be a non-negative value at most
    216      *     0xffffffff.
    217      * @param size Size of reading range. It must be a non-negative value at most Integer.MAX_VALUE
    218      *     or 0xffffffff. If 0xffffffff is specified, the method obtains the full bytes of object.
    219      * @param buffer Array to write data.
    220      * @return Size of bytes that are actually read.
    221      */
    222     public long getPartialObject(int objectHandle, long offset, long size, @NonNull byte[] buffer)
    223             throws IOException {
    224         return native_get_partial_object(objectHandle, offset, size, buffer);
    225     }
    226 
    227     /**
    228      * Obtains object bytes in the specified range and writes it to an array.
    229      * This call may block for an arbitrary amount of time depending on the size
    230      * of the data and speed of the devices.
    231      *
    232      * This is a vender-extended operation supported by Android that enables us to pass
    233      * unsigned 64-bit offset. Check if the MTP device supports the operation by using
    234      * {@link MtpDeviceInfo#getOperationsSupported()}.
    235      *
    236      * @param objectHandle handle of the object to read
    237      * @param offset Start index of reading range. It must be a non-negative value.
    238      * @param size Size of reading range. It must be a non-negative value at most Integer.MAX_VALUE.
    239      * @param buffer Array to write data.
    240      * @return Size of bytes that are actually read.
    241      * @see MtpConstants#OPERATION_GET_PARTIAL_OBJECT_64
    242      */
    243     public long getPartialObject64(int objectHandle, long offset, long size, @NonNull byte[] buffer)
    244             throws IOException {
    245         return native_get_partial_object_64(objectHandle, offset, size, buffer);
    246     }
    247 
    248     /**
    249      * Returns the thumbnail data for an object as a byte array.
    250      * The size and format of the thumbnail data can be determined via
    251      * {@link MtpObjectInfo#getThumbCompressedSize} and
    252      * {@link MtpObjectInfo#getThumbFormat}.
    253      * For typical devices the format is JPEG.
    254      *
    255      * @param objectHandle handle of the object to read
    256      * @return the object's thumbnail, or null if reading fails
    257      */
    258     public @Nullable byte[] getThumbnail(int objectHandle) {
    259         return native_get_thumbnail(objectHandle);
    260     }
    261 
    262     /**
    263      * Retrieves the {@link MtpStorageInfo} for a storage unit.
    264      *
    265      * @param storageId the ID of the storage unit
    266      * @return the MtpStorageInfo, or null if fetching storage info fails
    267      */
    268     public @Nullable MtpStorageInfo getStorageInfo(int storageId) {
    269         return native_get_storage_info(storageId);
    270     }
    271 
    272     /**
    273      * Retrieves the {@link MtpObjectInfo} for an object.
    274      *
    275      * @param objectHandle the handle of the object
    276      * @return the MtpObjectInfo, or null if fetching object info fails
    277      */
    278     public @Nullable MtpObjectInfo getObjectInfo(int objectHandle) {
    279         return native_get_object_info(objectHandle);
    280     }
    281 
    282     /**
    283      * Deletes an object on the device.  This call may block, since
    284      * deleting a directory containing many files may take a long time
    285      * on some devices.
    286      *
    287      * @param objectHandle handle of the object to delete
    288      * @return true if the deletion succeeds
    289      */
    290     public boolean deleteObject(int objectHandle) {
    291         return native_delete_object(objectHandle);
    292     }
    293 
    294     /**
    295      * Retrieves the object handle for the parent of an object on the device.
    296      *
    297      * @param objectHandle handle of the object to query
    298      * @return the parent's handle, or zero if it is in the root of the storage
    299      */
    300     public long getParent(int objectHandle) {
    301         return native_get_parent(objectHandle);
    302     }
    303 
    304     /**
    305      * Retrieves the ID of the storage unit containing the given object on the device.
    306      *
    307      * @param objectHandle handle of the object to query
    308      * @return the object's storage unit ID
    309      */
    310     public long getStorageId(int objectHandle) {
    311         return native_get_storage_id(objectHandle);
    312     }
    313 
    314     /**
    315      * Copies the data for an object to a file in external storage.
    316      * This call may block for an arbitrary amount of time depending on the size
    317      * of the data and speed of the devices.
    318      *
    319      * @param objectHandle handle of the object to read
    320      * @param destPath path to destination for the file transfer.
    321      *      This path should be in the external storage as defined by
    322      *      {@link android.os.Environment#getExternalStorageDirectory}
    323      * @return true if the file transfer succeeds
    324      */
    325     public boolean importFile(int objectHandle, @NonNull String destPath) {
    326         return native_import_file(objectHandle, destPath);
    327     }
    328 
    329     /**
    330      * Copies the data for an object to a file descriptor.
    331      * This call may block for an arbitrary amount of time depending on the size
    332      * of the data and speed of the devices. The file descriptor is not closed
    333      * on completion, and must be done by the caller.
    334      *
    335      * @param objectHandle handle of the object to read
    336      * @param descriptor file descriptor to write the data to for the file transfer.
    337      * @return true if the file transfer succeeds
    338      */
    339     public boolean importFile(int objectHandle, @NonNull ParcelFileDescriptor descriptor) {
    340         return native_import_file(objectHandle, descriptor.getFd());
    341     }
    342 
    343     /**
    344      * Copies the data for an object from a file descriptor.
    345      * This call may block for an arbitrary amount of time depending on the size
    346      * of the data and speed of the devices. The file descriptor is not closed
    347      * on completion, and must be done by the caller.
    348      *
    349      * @param objectHandle handle of the target file
    350      * @param size size of the file in bytes
    351      * @param descriptor file descriptor to read the data from.
    352      * @return true if the file transfer succeeds
    353      */
    354     public boolean sendObject(
    355             int objectHandle, long size, @NonNull ParcelFileDescriptor descriptor) {
    356         return native_send_object(objectHandle, size, descriptor.getFd());
    357     }
    358 
    359     /**
    360      * Uploads an object metadata for a new entry. The {@link MtpObjectInfo} can be
    361      * created with the {@link MtpObjectInfo.Builder} class.
    362      *
    363      * The returned {@link MtpObjectInfo} has the new object handle field filled in.
    364      *
    365      * @param info metadata of the entry
    366      * @return object info of the created entry, or null if sending object info fails
    367      */
    368     public @Nullable MtpObjectInfo sendObjectInfo(@NonNull MtpObjectInfo info) {
    369         return native_send_object_info(info);
    370     }
    371 
    372     /**
    373      * Reads an event from the device. It blocks the current thread until it gets an event.
    374      * It throws OperationCanceledException if it is cancelled by signal.
    375      *
    376      * @param signal signal for cancellation
    377      * @return obtained event
    378      * @throws IOException
    379      */
    380     public @NonNull MtpEvent readEvent(@Nullable CancellationSignal signal) throws IOException {
    381         final int handle = native_submit_event_request();
    382         Preconditions.checkState(handle >= 0, "Other thread is reading an event.");
    383 
    384         if (signal != null) {
    385             signal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
    386                 @Override
    387                 public void onCancel() {
    388                     native_discard_event_request(handle);
    389                 }
    390             });
    391         }
    392 
    393         try {
    394             return native_reap_event_request(handle);
    395         } finally {
    396             if (signal != null) {
    397                 signal.setOnCancelListener(null);
    398             }
    399         }
    400     }
    401 
    402     /**
    403      * Returns object size in 64-bit integer.
    404      *
    405      * Though MtpObjectInfo#getCompressedSize returns the object size in 32-bit unsigned integer,
    406      * this method returns the object size in 64-bit integer from the object property. Thus it can
    407      * fetch 4GB+ object size correctly. If the device does not support objectSize property, it
    408      * throws IOException.
    409      * @hide
    410      */
    411     public long getObjectSizeLong(int handle, int format) throws IOException {
    412         return native_get_object_size_long(handle, format);
    413     }
    414 
    415     // used by the JNI code
    416     private long mNativeContext;
    417 
    418     private native boolean native_open(String deviceName, int fd);
    419     private native void native_close();
    420     private native MtpDeviceInfo native_get_device_info();
    421     private native int[] native_get_storage_ids();
    422     private native MtpStorageInfo native_get_storage_info(int storageId);
    423     private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
    424     private native MtpObjectInfo native_get_object_info(int objectHandle);
    425     private native byte[] native_get_object(int objectHandle, long objectSize);
    426     private native long native_get_partial_object(
    427             int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
    428     private native int native_get_partial_object_64(
    429             int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
    430     private native byte[] native_get_thumbnail(int objectHandle);
    431     private native boolean native_delete_object(int objectHandle);
    432     private native int native_get_parent(int objectHandle);
    433     private native int native_get_storage_id(int objectHandle);
    434     private native boolean native_import_file(int objectHandle, String destPath);
    435     private native boolean native_import_file(int objectHandle, int fd);
    436     private native boolean native_send_object(int objectHandle, long size, int fd);
    437     private native MtpObjectInfo native_send_object_info(MtpObjectInfo info);
    438     private native int native_submit_event_request() throws IOException;
    439     private native MtpEvent native_reap_event_request(int handle) throws IOException;
    440     private native void native_discard_event_request(int handle);
    441     private native long native_get_object_size_long(int handle, int format) throws IOException;
    442 }
    443