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