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