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