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.util.Preconditions; 29 30 import java.io.IOException; 31 32 /** 33 * This class represents an MTP or PTP device connected on the USB host bus. An application can 34 * instantiate an object of this type, by referencing an attached {@link 35 * android.hardware.usb.UsbDevice} and then use methods in this class to get information about the 36 * device and objects stored on it, as well as open the connection and transfer data. 37 */ 38 public final class MtpDevice { 39 40 private static final String TAG = "MtpDevice"; 41 42 private final UsbDevice mDevice; 43 44 static { 45 System.loadLibrary("media_jni"); 46 } 47 48 /** 49 * MtpClient constructor 50 * 51 * @param device the {@link android.hardware.usb.UsbDevice} for the MTP or PTP device 52 */ MtpDevice(UsbDevice device)53 public MtpDevice(UsbDevice device) { 54 mDevice = device; 55 } 56 57 /** 58 * Opens the MTP device. Once the device is open it takes ownership of the 59 * {@link android.hardware.usb.UsbDeviceConnection}. 60 * The connection will be closed when you call {@link #close()} 61 * The connection will also be closed if this method fails. 62 * 63 * @param connection an open {@link android.hardware.usb.UsbDeviceConnection} for the device 64 * @return true if the device was successfully opened. 65 */ open(UsbDeviceConnection connection)66 public boolean open(UsbDeviceConnection connection) { 67 boolean result = false; 68 69 Context context = connection.getContext(); 70 if (context != null) { 71 UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); 72 73 if (!userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) { 74 result = native_open(mDevice.getDeviceName(), connection.getFileDescriptor()); 75 } 76 } 77 78 if (!result) { 79 connection.close(); 80 } 81 return result; 82 } 83 84 /** 85 * Closes all resources related to the MtpDevice object. 86 * After this is called, the object can not be used until {@link #open} is called again 87 * with a new {@link android.hardware.usb.UsbDeviceConnection}. 88 */ close()89 public void close() { 90 native_close(); 91 } 92 93 @Override finalize()94 protected void finalize() throws Throwable { 95 try { 96 native_close(); 97 } finally { 98 super.finalize(); 99 } 100 } 101 102 /** 103 * Returns the name of the USB device 104 * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceName} 105 * for the device's {@link android.hardware.usb.UsbDevice} 106 * 107 * @return the device name 108 */ getDeviceName()109 public String getDeviceName() { 110 return mDevice.getDeviceName(); 111 } 112 113 /** 114 * Returns the USB ID of the USB device. 115 * This returns the same value as {@link android.hardware.usb.UsbDevice#getDeviceId} 116 * for the device's {@link android.hardware.usb.UsbDevice} 117 * 118 * @return the device ID 119 */ getDeviceId()120 public int getDeviceId() { 121 return mDevice.getDeviceId(); 122 } 123 124 @Override toString()125 public String toString() { 126 return mDevice.getDeviceName(); 127 } 128 129 /** 130 * Returns the {@link MtpDeviceInfo} for this device 131 * 132 * @return the device info 133 */ getDeviceInfo()134 public MtpDeviceInfo getDeviceInfo() { 135 return native_get_device_info(); 136 } 137 138 /** 139 * Returns the list of IDs for all storage units on this device 140 * Information about each storage unit can be accessed via {@link #getStorageInfo}. 141 * 142 * @return the list of storage IDs 143 */ getStorageIds()144 public int[] getStorageIds() { 145 return native_get_storage_ids(); 146 } 147 148 /** 149 * Returns the list of object handles for all objects on the given storage unit, 150 * with the given format and parent. 151 * Information about each object can be accessed via {@link #getObjectInfo}. 152 * 153 * @param storageId the storage unit to query 154 * @param format the format of the object to return, or zero for all formats 155 * @param objectHandle the parent object to query, -1 for the storage root, 156 * or zero for all objects 157 * @return the object handles 158 */ getObjectHandles(int storageId, int format, int objectHandle)159 public int[] getObjectHandles(int storageId, int format, int objectHandle) { 160 return native_get_object_handles(storageId, format, objectHandle); 161 } 162 163 /** 164 * Returns the data for an object as a byte array. 165 * This call may block for an arbitrary amount of time depending on the size 166 * of the data and speed of the devices. 167 * 168 * @param objectHandle handle of the object to read 169 * @param objectSize the size of the object (this should match 170 * {@link MtpObjectInfo#getCompressedSize}) 171 * @return the object's data, or null if reading fails 172 */ getObject(int objectHandle, int objectSize)173 public byte[] getObject(int objectHandle, int objectSize) { 174 Preconditions.checkArgumentNonnegative(objectSize, "objectSize should not be negative"); 175 return native_get_object(objectHandle, objectSize); 176 } 177 178 /** 179 * Obtains object bytes in the specified range and writes it to an array. 180 * This call may block for an arbitrary amount of time depending on the size 181 * of the data and speed of the devices. 182 * 183 * @param objectHandle handle of the object to read 184 * @param offset Start index of reading range. It must be a non-negative value at most 185 * 0xffffffff. 186 * @param size Size of reading range. It must be a non-negative value at most Integer.MAX_VALUE 187 * or 0xffffffff. If 0xffffffff is specified, the method obtains the full bytes of object. 188 * @param buffer Array to write data. 189 * @return Size of bytes that are actually read. 190 */ getPartialObject(int objectHandle, long offset, long size, byte[] buffer)191 public long getPartialObject(int objectHandle, long offset, long size, byte[] buffer) 192 throws IOException { 193 return native_get_partial_object(objectHandle, offset, size, buffer); 194 } 195 196 /** 197 * Obtains object bytes in the specified range and writes it to an array. 198 * This call may block for an arbitrary amount of time depending on the size 199 * of the data and speed of the devices. 200 * 201 * This is a vender-extended operation supported by Android that enables us to pass 202 * unsigned 64-bit offset. Check if the MTP device supports the operation by using 203 * {@link MtpDeviceInfo#getOperationsSupported()}. 204 * 205 * @param objectHandle handle of the object to read 206 * @param offset Start index of reading range. It must be a non-negative value. 207 * @param size Size of reading range. It must be a non-negative value at most Integer.MAX_VALUE. 208 * @param buffer Array to write data. 209 * @return Size of bytes that are actually read. 210 * @see MtpConstants#OPERATION_GET_PARTIAL_OBJECT_64 211 */ getPartialObject64(int objectHandle, long offset, long size, byte[] buffer)212 public long getPartialObject64(int objectHandle, long offset, long size, byte[] buffer) 213 throws IOException { 214 return native_get_partial_object_64(objectHandle, offset, size, buffer); 215 } 216 217 /** 218 * Returns the thumbnail data for an object as a byte array. 219 * The size and format of the thumbnail data can be determined via 220 * {@link MtpObjectInfo#getThumbCompressedSize} and 221 * {@link MtpObjectInfo#getThumbFormat}. 222 * For typical devices the format is JPEG. 223 * 224 * @param objectHandle handle of the object to read 225 * @return the object's thumbnail, or null if reading fails 226 */ getThumbnail(int objectHandle)227 public byte[] getThumbnail(int objectHandle) { 228 return native_get_thumbnail(objectHandle); 229 } 230 231 /** 232 * Retrieves the {@link MtpStorageInfo} for a storage unit. 233 * 234 * @param storageId the ID of the storage unit 235 * @return the MtpStorageInfo 236 */ getStorageInfo(int storageId)237 public MtpStorageInfo getStorageInfo(int storageId) { 238 return native_get_storage_info(storageId); 239 } 240 241 /** 242 * Retrieves the {@link MtpObjectInfo} for an object. 243 * 244 * @param objectHandle the handle of the object 245 * @return the MtpObjectInfo 246 */ getObjectInfo(int objectHandle)247 public MtpObjectInfo getObjectInfo(int objectHandle) { 248 return native_get_object_info(objectHandle); 249 } 250 251 /** 252 * Deletes an object on the device. This call may block, since 253 * deleting a directory containing many files may take a long time 254 * on some devices. 255 * 256 * @param objectHandle handle of the object to delete 257 * @return true if the deletion succeeds 258 */ deleteObject(int objectHandle)259 public boolean deleteObject(int objectHandle) { 260 return native_delete_object(objectHandle); 261 } 262 263 /** 264 * Retrieves the object handle for the parent of an object on the device. 265 * 266 * @param objectHandle handle of the object to query 267 * @return the parent's handle, or zero if it is in the root of the storage 268 */ getParent(int objectHandle)269 public long getParent(int objectHandle) { 270 return native_get_parent(objectHandle); 271 } 272 273 /** 274 * Retrieves the ID of the storage unit containing the given object on the device. 275 * 276 * @param objectHandle handle of the object to query 277 * @return the object's storage unit ID 278 */ getStorageId(int objectHandle)279 public long getStorageId(int objectHandle) { 280 return native_get_storage_id(objectHandle); 281 } 282 283 /** 284 * Copies the data for an object to a file in external storage. 285 * This call may block for an arbitrary amount of time depending on the size 286 * of the data and speed of the devices. 287 * 288 * @param objectHandle handle of the object to read 289 * @param destPath path to destination for the file transfer. 290 * This path should be in the external storage as defined by 291 * {@link android.os.Environment#getExternalStorageDirectory} 292 * @return true if the file transfer succeeds 293 */ importFile(int objectHandle, String destPath)294 public boolean importFile(int objectHandle, String destPath) { 295 return native_import_file(objectHandle, destPath); 296 } 297 298 /** 299 * Copies the data for an object to a file descriptor. 300 * This call may block for an arbitrary amount of time depending on the size 301 * of the data and speed of the devices. The file descriptor is not closed 302 * on completion, and must be done by the caller. 303 * 304 * @param objectHandle handle of the object to read 305 * @param descriptor file descriptor to write the data to for the file transfer. 306 * @return true if the file transfer succeeds 307 */ importFile(int objectHandle, ParcelFileDescriptor descriptor)308 public boolean importFile(int objectHandle, ParcelFileDescriptor descriptor) { 309 return native_import_file(objectHandle, descriptor.getFd()); 310 } 311 312 /** 313 * Copies the data for an object from a file descriptor. 314 * This call may block for an arbitrary amount of time depending on the size 315 * of the data and speed of the devices. The file descriptor is not closed 316 * on completion, and must be done by the caller. 317 * 318 * @param objectHandle handle of the target file 319 * @param size size of the file in bytes 320 * @param descriptor file descriptor to read the data from. 321 * @return true if the file transfer succeeds 322 */ sendObject(int objectHandle, long size, ParcelFileDescriptor descriptor)323 public boolean sendObject(int objectHandle, long size, ParcelFileDescriptor descriptor) { 324 return native_send_object(objectHandle, size, descriptor.getFd()); 325 } 326 327 /** 328 * Uploads an object metadata for a new entry. The {@link MtpObjectInfo} can be 329 * created with the {@link MtpObjectInfo.Builder} class. 330 * 331 * The returned {@link MtpObjectInfo} has the new object handle field filled in. 332 * 333 * @param info metadata of the entry 334 * @return object info of the created entry or null if the operation failed. 335 */ sendObjectInfo(MtpObjectInfo info)336 public MtpObjectInfo sendObjectInfo(MtpObjectInfo info) { 337 return native_send_object_info(info); 338 } 339 340 /** 341 * Reads an event from the device. It blocks the current thread until it gets an event. 342 * It throws OperationCanceledException if it is cancelled by signal. 343 * 344 * @param signal signal for cancellation 345 * @return obtained event 346 * @throws IOException 347 */ readEvent(@ullable CancellationSignal signal)348 public @NonNull MtpEvent readEvent(@Nullable CancellationSignal signal) throws IOException { 349 final int handle = native_submit_event_request(); 350 Preconditions.checkState(handle >= 0, "Other thread is reading an event."); 351 352 if (signal != null) { 353 signal.setOnCancelListener(new CancellationSignal.OnCancelListener() { 354 @Override 355 public void onCancel() { 356 native_discard_event_request(handle); 357 } 358 }); 359 } 360 361 try { 362 return native_reap_event_request(handle); 363 } finally { 364 if (signal != null) { 365 signal.setOnCancelListener(null); 366 } 367 } 368 } 369 370 /** 371 * Returns object size in 64-bit integer. 372 * 373 * Though MtpObjectInfo#getCompressedSize returns the object size in 32-bit unsigned integer, 374 * this method returns the object size in 64-bit integer from the object property. Thus it can 375 * fetch 4GB+ object size correctly. If the device does not support objectSize property, it 376 * throws IOException. 377 * @hide 378 */ getObjectSizeLong(int handle, int format)379 public long getObjectSizeLong(int handle, int format) throws IOException { 380 return native_get_object_size_long(handle, format); 381 } 382 383 // used by the JNI code 384 private long mNativeContext; 385 native_open(String deviceName, int fd)386 private native boolean native_open(String deviceName, int fd); native_close()387 private native void native_close(); native_get_device_info()388 private native MtpDeviceInfo native_get_device_info(); native_get_storage_ids()389 private native int[] native_get_storage_ids(); native_get_storage_info(int storageId)390 private native MtpStorageInfo native_get_storage_info(int storageId); native_get_object_handles(int storageId, int format, int objectHandle)391 private native int[] native_get_object_handles(int storageId, int format, int objectHandle); native_get_object_info(int objectHandle)392 private native MtpObjectInfo native_get_object_info(int objectHandle); native_get_object(int objectHandle, long objectSize)393 private native byte[] native_get_object(int objectHandle, long objectSize); native_get_partial_object( int objectHandle, long offset, long objectSize, byte[] buffer)394 private native long native_get_partial_object( 395 int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException; native_get_partial_object_64( int objectHandle, long offset, long objectSize, byte[] buffer)396 private native int native_get_partial_object_64( 397 int objectHandle, long offset, long objectSize, byte[] buffer) throws IOException; native_get_thumbnail(int objectHandle)398 private native byte[] native_get_thumbnail(int objectHandle); native_delete_object(int objectHandle)399 private native boolean native_delete_object(int objectHandle); native_get_parent(int objectHandle)400 private native int native_get_parent(int objectHandle); native_get_storage_id(int objectHandle)401 private native int native_get_storage_id(int objectHandle); native_import_file(int objectHandle, String destPath)402 private native boolean native_import_file(int objectHandle, String destPath); native_import_file(int objectHandle, int fd)403 private native boolean native_import_file(int objectHandle, int fd); native_send_object(int objectHandle, long size, int fd)404 private native boolean native_send_object(int objectHandle, long size, int fd); native_send_object_info(MtpObjectInfo info)405 private native MtpObjectInfo native_send_object_info(MtpObjectInfo info); native_submit_event_request()406 private native int native_submit_event_request() throws IOException; native_reap_event_request(int handle)407 private native MtpEvent native_reap_event_request(int handle) throws IOException; native_discard_event_request(int handle)408 private native void native_discard_event_request(int handle); native_get_object_size_long(int handle, int format)409 private native long native_get_object_size_long(int handle, int format) throws IOException; 410 } 411