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