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