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      */
MtpDevice(@onNull UsbDevice device)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      */
open(@onNull UsbDeviceConnection connection)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      */
close()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
finalize()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      */
getDeviceName()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      */
getDeviceId()151     public int getDeviceId() {
152         return mDevice.getDeviceId();
153     }
154 
155     @Override
toString()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      */
getDeviceInfo()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      */
getStorageIds()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      */
getObjectHandles(int storageId, int format, int objectHandle)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      */
getObject(int objectHandle, int objectSize)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      */
getPartialObject(int objectHandle, long offset, long size, @NonNull byte[] buffer)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      */
getPartialObject64(int objectHandle, long offset, long size, @NonNull byte[] buffer)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      */
getThumbnail(int objectHandle)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      */
getStorageInfo(int storageId)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      */
getObjectInfo(int objectHandle)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      */
deleteObject(int objectHandle)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      */
getParent(int objectHandle)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      */
getStorageId(int objectHandle)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      */
importFile(int objectHandle, @NonNull String destPath)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      */
importFile(int objectHandle, @NonNull ParcelFileDescriptor descriptor)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      */
sendObject( int objectHandle, long size, @NonNull ParcelFileDescriptor descriptor)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      */
sendObjectInfo(@onNull MtpObjectInfo info)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      */
readEvent(@ullable CancellationSignal signal)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      */
getObjectSizeLong(int handle, int format)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 
native_open(String deviceName, int fd)418     private native boolean native_open(String deviceName, int fd);
native_close()419     private native void native_close();
native_get_device_info()420     private native MtpDeviceInfo native_get_device_info();
native_get_storage_ids()421     private native int[] native_get_storage_ids();
native_get_storage_info(int storageId)422     private native MtpStorageInfo native_get_storage_info(int storageId);
native_get_object_handles(int storageId, int format, int objectHandle)423     private native int[] native_get_object_handles(int storageId, int format, int objectHandle);
native_get_object_info(int objectHandle)424     private native MtpObjectInfo native_get_object_info(int objectHandle);
native_get_object(int objectHandle, long objectSize)425     private native byte[] native_get_object(int objectHandle, long objectSize);
native_get_partial_object( int objectHandle, long offset, long objectSize, byte[] buffer)426     private native long native_get_partial_object(
427             int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
native_get_partial_object_64( int objectHandle, long offset, long objectSize, byte[] buffer)428     private native int native_get_partial_object_64(
429             int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException;
native_get_thumbnail(int objectHandle)430     private native byte[] native_get_thumbnail(int objectHandle);
native_delete_object(int objectHandle)431     private native boolean native_delete_object(int objectHandle);
native_get_parent(int objectHandle)432     private native int native_get_parent(int objectHandle);
native_get_storage_id(int objectHandle)433     private native int native_get_storage_id(int objectHandle);
native_import_file(int objectHandle, String destPath)434     private native boolean native_import_file(int objectHandle, String destPath);
native_import_file(int objectHandle, int fd)435     private native boolean native_import_file(int objectHandle, int fd);
native_send_object(int objectHandle, long size, int fd)436     private native boolean native_send_object(int objectHandle, long size, int fd);
native_send_object_info(MtpObjectInfo info)437     private native MtpObjectInfo native_send_object_info(MtpObjectInfo info);
native_submit_event_request()438     private native int native_submit_event_request() throws IOException;
native_reap_event_request(int handle)439     private native MtpEvent native_reap_event_request(int handle) throws IOException;
native_discard_event_request(int handle)440     private native void native_discard_event_request(int handle);
native_get_object_size_long(int handle, int format)441     private native long native_get_object_size_long(int handle, int format) throws IOException;
442 }
443