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