diff options
author | 2015-09-10 20:38:15 +0900 | |
---|---|---|
committer | 2015-09-25 14:54:17 +0900 | |
commit | 0b494663a4cb177fc6f05988c9bda2ef6277333d (patch) | |
tree | 9b8c9a014894318347f67e6a182c5e00850b307e | |
parent | 52b290f73f1cd1a6ab791f21e78245becfe0b29d (diff) |
Add readEvent method to MtpDevice.
BUG=23368533
Change-Id: Ibefff559fa7dd0bee17e2812bd7cdd129108d804
-rw-r--r-- | api/current.txt | 6 | ||||
-rw-r--r-- | api/system-current.txt | 6 | ||||
-rw-r--r-- | media/java/android/mtp/MtpDevice.java | 39 | ||||
-rw-r--r-- | media/java/android/mtp/MtpEvent.java | 31 | ||||
-rw-r--r-- | media/jni/android_mtp_MtpDevice.cpp | 78 | ||||
-rw-r--r-- | packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java | 14 | ||||
-rw-r--r-- | packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java | 86 | ||||
-rw-r--r-- | packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java | 5 | ||||
-rw-r--r-- | packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java | 7 |
9 files changed, 254 insertions, 18 deletions
diff --git a/api/current.txt b/api/current.txt index 7e6f445e6ff4..adcc99ac5580 100644 --- a/api/current.txt +++ b/api/current.txt @@ -18122,6 +18122,7 @@ package android.mtp { method public boolean importFile(int, java.lang.String); method public boolean importFile(int, android.os.ParcelFileDescriptor); method public boolean open(android.hardware.usb.UsbDeviceConnection); + method public android.mtp.MtpEvent readEvent(android.os.CancellationSignal); method public boolean sendObject(int, int, android.os.ParcelFileDescriptor); method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo); } @@ -18133,6 +18134,11 @@ package android.mtp { method public final java.lang.String getVersion(); } + public class MtpEvent { + ctor public MtpEvent(); + method public int getEventCode(); + } + public final class MtpObjectInfo { method public final int getAssociationDesc(); method public final int getAssociationType(); diff --git a/api/system-current.txt b/api/system-current.txt index 0a2a5212532f..f09b2bb914e0 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -19634,6 +19634,7 @@ package android.mtp { method public boolean importFile(int, java.lang.String); method public boolean importFile(int, android.os.ParcelFileDescriptor); method public boolean open(android.hardware.usb.UsbDeviceConnection); + method public android.mtp.MtpEvent readEvent(android.os.CancellationSignal); method public boolean sendObject(int, int, android.os.ParcelFileDescriptor); method public android.mtp.MtpObjectInfo sendObjectInfo(android.mtp.MtpObjectInfo); } @@ -19645,6 +19646,11 @@ package android.mtp { method public final java.lang.String getVersion(); } + public class MtpEvent { + ctor public MtpEvent(); + method public int getEventCode(); + } + public final class MtpObjectInfo { method public final int getAssociationDesc(); method public final int getAssociationType(); diff --git a/media/java/android/mtp/MtpDevice.java b/media/java/android/mtp/MtpDevice.java index 3cd157e2b74f..95cb5206b9b5 100644 --- a/media/java/android/mtp/MtpDevice.java +++ b/media/java/android/mtp/MtpDevice.java @@ -18,6 +18,8 @@ package android.mtp; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; +import android.os.CancellationSignal; +import android.os.OperationCanceledException; import android.os.ParcelFileDescriptor; /** @@ -47,7 +49,7 @@ public final class MtpDevice { /** * Opens the MTP device. Once the device is open it takes ownership of the - * {@link android.hardware.usb.UsbDeviceConnection}. + * {@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. * @@ -278,6 +280,38 @@ public final class MtpDevice { 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 + */ + public MtpEvent readEvent(CancellationSignal signal) { + final int handle = native_submit_event_request(); + + if (handle < 0) { + throw new IllegalStateException("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); + } + } + } + // used by the JNI code private long mNativeContext; @@ -297,4 +331,7 @@ public final class MtpDevice { private native boolean native_import_file(int objectHandle, int fd); private native boolean native_send_object(int objectHandle, int size, int fd); private native MtpObjectInfo native_send_object_info(MtpObjectInfo info); + private native int native_submit_event_request(); + private native MtpEvent native_reap_event_request(int handle); + private native void native_discard_event_request(int handle); } diff --git a/media/java/android/mtp/MtpEvent.java b/media/java/android/mtp/MtpEvent.java new file mode 100644 index 000000000000..0fa7d93e12ca --- /dev/null +++ b/media/java/android/mtp/MtpEvent.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2015 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; + +/** + * This class encapsulates information about a MTP event. + */ +public class MtpEvent { + private int mEventCode; + + /** + * Returns event code of MTP event. + * + * @return event code + */ + public int getEventCode() { return mEventCode; } +} diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp index 2a46ee72e50c..85e3891cefc5 100644 --- a/media/jni/android_mtp_MtpDevice.cpp +++ b/media/jni/android_mtp_MtpDevice.cpp @@ -46,10 +46,14 @@ static jfieldID field_context; jclass clazz_deviceInfo; jclass clazz_storageInfo; jclass clazz_objectInfo; +jclass clazz_event; +jclass clazz_io_exception; +jclass clazz_operation_canceled_exception; jmethodID constructor_deviceInfo; jmethodID constructor_storageInfo; jmethodID constructor_objectInfo; +jmethodID constructor_event; // MtpDeviceInfo fields static jfieldID field_deviceInfo_manufacturer; @@ -86,6 +90,9 @@ static jfieldID field_objectInfo_dateCreated; static jfieldID field_objectInfo_dateModified; static jfieldID field_objectInfo_keywords; +// MtpEvent fields +static jfieldID field_event_eventCode; + MtpDevice* get_device_from_object(JNIEnv* env, jobject javaDevice) { return (MtpDevice*)env->GetLongField(javaDevice, field_context); @@ -488,6 +495,42 @@ android_mtp_MtpDevice_send_object_info(JNIEnv *env, jobject thiz, jobject info) return result; } +static jint android_mtp_MtpDevice_submit_event_request(JNIEnv *env, jobject thiz) +{ + MtpDevice* const device = get_device_from_object(env, thiz); + if (!device) { + env->ThrowNew(clazz_io_exception, ""); + return -1; + } + return device->submitEventRequest(); +} + +static jobject android_mtp_MtpDevice_reap_event_request(JNIEnv *env, jobject thiz, jint seq) +{ + MtpDevice* const device = get_device_from_object(env, thiz); + if (!device) { + env->ThrowNew(clazz_io_exception, ""); + return NULL; + } + const int eventCode = device->reapEventRequest(seq); + if (eventCode <= 0) { + env->ThrowNew(clazz_operation_canceled_exception, ""); + return NULL; + } + jobject result = env->NewObject(clazz_event, constructor_event); + env->SetIntField(result, field_event_eventCode, eventCode); + return result; +} + +static void android_mtp_MtpDevice_discard_event_request(JNIEnv *env, jobject thiz, jint seq) +{ + MtpDevice* const device = get_device_from_object(env, thiz); + if (!device) { + return; + } + device->discardEventRequest(seq); +} + // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -513,7 +556,11 @@ static JNINativeMethod gMethods[] = { {"native_import_file", "(II)Z",(void *)android_mtp_MtpDevice_import_file_to_fd}, {"native_send_object", "(III)Z",(void *)android_mtp_MtpDevice_send_object}, {"native_send_object_info", "(Landroid/mtp/MtpObjectInfo;)Landroid/mtp/MtpObjectInfo;", - (void *)android_mtp_MtpDevice_send_object_info} + (void *)android_mtp_MtpDevice_send_object_info}, + {"native_submit_event_request", "()I", (void *)android_mtp_MtpDevice_submit_event_request}, + {"native_reap_event_request", "(I)Landroid/mtp/MtpEvent;", + (void *)android_mtp_MtpDevice_reap_event_request}, + {"native_discard_event_request", "(I)V", (void *)android_mtp_MtpDevice_discard_event_request}, }; int register_android_mtp_MtpDevice(JNIEnv *env) @@ -703,6 +750,23 @@ int register_android_mtp_MtpDevice(JNIEnv *env) } clazz_objectInfo = (jclass)env->NewGlobalRef(clazz); + clazz = env->FindClass("android/mtp/MtpEvent"); + if (clazz == NULL) { + ALOGE("Can't find android/mtp/MtpEvent"); + return -1; + } + constructor_event = env->GetMethodID(clazz, "<init>", "()V"); + if (constructor_event == NULL) { + ALOGE("Can't find android/mtp/MtpEvent constructor"); + return -1; + } + field_event_eventCode = env->GetFieldID(clazz, "mEventCode", "I"); + if (field_event_eventCode == NULL) { + ALOGE("Can't find MtpObjectInfo.mEventCode"); + return -1; + } + clazz_event = (jclass)env->NewGlobalRef(clazz); + clazz = env->FindClass("android/mtp/MtpDevice"); if (clazz == NULL) { ALOGE("Can't find android/mtp/MtpDevice"); @@ -713,6 +777,18 @@ int register_android_mtp_MtpDevice(JNIEnv *env) ALOGE("Can't find MtpDevice.mNativeContext"); return -1; } + clazz = env->FindClass("java/io/IOException"); + if (clazz == NULL) { + ALOGE("Can't find java.io.IOException"); + return -1; + } + clazz_io_exception = (jclass)env->NewGlobalRef(clazz); + clazz = env->FindClass("android/os/OperationCanceledException"); + if (clazz == NULL) { + ALOGE("Can't find android.os.OperationCanceledException"); + return -1; + } + clazz_operation_canceled_exception = (jclass)env->NewGlobalRef(clazz); return AndroidRuntime::registerNativeMethods(env, "android/mtp/MtpDevice", gMethods, NELEM(gMethods)); diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java index 7cc7413bab3d..af7f691bdd64 100644 --- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java @@ -22,12 +22,14 @@ import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; import android.mtp.MtpConstants; import android.mtp.MtpDevice; +import android.mtp.MtpEvent; import android.mtp.MtpObjectInfo; +import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; -import android.provider.DocumentsContract.Document; -import android.provider.DocumentsContract; import android.util.SparseArray; +import com.android.internal.annotations.VisibleForTesting; + import java.io.FileNotFoundException; import java.io.IOException; @@ -188,7 +190,13 @@ class MtpManager { } } - private MtpDevice getDevice(int deviceId) throws IOException { + @VisibleForTesting + MtpEvent readEvent(int deviceId, CancellationSignal signal) throws IOException { + final MtpDevice device = getDevice(deviceId); + return device.readEvent(signal); + } + + private synchronized MtpDevice getDevice(int deviceId) throws IOException { final MtpDevice device = mDevices.get(deviceId); if (device == null) { throw new IOException("USB device " + deviceId + " is not opened."); diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java index 2c1f115ec92b..554777160792 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpManagerTest.java @@ -16,35 +16,101 @@ package com.android.mtp; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbManager; +import android.os.CancellationSignal; +import android.os.OperationCanceledException; import android.test.InstrumentationTestCase; +import java.io.IOException; import java.util.HashMap; +import java.util.concurrent.CountDownLatch; +@RealDeviceTest public class MtpManagerTest extends InstrumentationTestCase { - @RealDeviceTest - public void testBasic() throws Exception { - final UsbDevice usbDevice = findDevice(); - final MtpManager manager = new MtpManager(getContext()); - manager.openDevice(usbDevice.getDeviceId()); - waitForStorages(manager, usbDevice.getDeviceId()); - manager.closeDevice(usbDevice.getDeviceId()); + private static final String ACTION_USB_PERMISSION = + "com.android.mtp.USB_PERMISSION"; + private static final int TIMEOUT_MS = 1000; + UsbManager mUsbManager; + MtpManager mManager; + UsbDevice mUsbDevice; + int mRequest; + + @Override + public void setUp() throws Exception { + mUsbManager = getContext().getSystemService(UsbManager.class); + mUsbDevice = findDevice(); + mManager = new MtpManager(getContext()); + mManager.openDevice(mUsbDevice.getDeviceId()); + waitForStorages(mManager, mUsbDevice.getDeviceId()); + } + + @Override + public void tearDown() throws IOException { + mManager.closeDevice(mUsbDevice.getDeviceId()); + } + + public void testCancelEvent() throws Exception { + final CancellationSignal signal = new CancellationSignal(); + final Thread thread = new Thread() { + @Override + public void run() { + try { + mManager.readEvent(mUsbDevice.getDeviceId(), signal); + } catch (OperationCanceledException | IOException e) { + show(e.getMessage()); + } + } + }; + thread.start(); + Thread.sleep(TIMEOUT_MS); + signal.cancel(); + thread.join(TIMEOUT_MS); + } + + private void requestPermission(UsbDevice device) throws InterruptedException { + if (mUsbManager.hasPermission(device)) { + return; + } + final CountDownLatch latch = new CountDownLatch(1); + final BroadcastReceiver receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + latch.countDown(); + getInstrumentation().getTargetContext().unregisterReceiver(this); + } + }; + getInstrumentation().getTargetContext().registerReceiver( + receiver, new IntentFilter(ACTION_USB_PERMISSION)); + mUsbManager.requestPermission(device, PendingIntent.getBroadcast( + getInstrumentation().getTargetContext(), + 0 /* requstCode */, + new Intent(ACTION_USB_PERMISSION), + 0 /* flags */)); + latch.await(); + assertTrue(mUsbManager.hasPermission(device)); } private UsbDevice findDevice() throws InterruptedException { - final UsbManager usbManager = getContext().getSystemService(UsbManager.class); while (true) { - final HashMap<String,UsbDevice> devices = usbManager.getDeviceList(); + final HashMap<String,UsbDevice> devices = mUsbManager.getDeviceList(); if (devices.size() == 0) { show("Wait for devices."); Thread.sleep(1000); continue; } final UsbDevice device = devices.values().iterator().next(); - final UsbDeviceConnection connection = usbManager.openDevice(device); + requestPermission(device); + final UsbDeviceConnection connection = mUsbManager.openDevice(device); + if (connection == null) { + fail("Cannot open USB connection."); + } for (int i = 0; i < device.getInterfaceCount(); i++) { // Since the test runs real environment, we need to call claim interface with // force = true to rob interfaces from other applications. diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java index 9641ad764fae..22daaf2971e6 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/RealDeviceTest.java @@ -17,7 +17,10 @@ package com.android.mtp; import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) @interface RealDeviceTest {} diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java index 9824d28e1fe4..a24337534e5b 100644 --- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java +++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/TestResultInstrumentation.java @@ -45,8 +45,11 @@ public class TestResultInstrumentation extends InstrumentationTestRunner impleme } private void show(String tag, Test test, Throwable t) { + String message = ""; + if (t != null && t.getMessage() != null) { + message = t.getMessage(); + } TestResultActivity.show( - getContext(), - String.format("[%s] %s %s", tag, test.toString(), t != null ? t.getMessage() : "")); + getContext(), String.format("[%s] %s %s", tag, test.toString(), message)); } } |