Add openDevice and closeDevice methods to MtpDocumentsProvider.
These methods will be called in the following CL from the service that handles
USB attach and detach intent.
BUG=20274999
Change-Id: I7b3c658afc5750d6a2713c07f40c59b26dcd1460
diff --git a/packages/MtpDocumentsProvider/Android.mk b/packages/MtpDocumentsProvider/Android.mk
index ec18463..5f76a9a 100644
--- a/packages/MtpDocumentsProvider/Android.mk
+++ b/packages/MtpDocumentsProvider/Android.mk
@@ -5,6 +5,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := MtpDocumentsProvider
LOCAL_CERTIFICATE := media
+LOCAL_PROGUARD_FLAGS := '-keepclassmembers class * {' \
+ ' @com.android.internal.annotations.VisibleForTesting *; }'
include $(BUILD_PACKAGE)
include $(LOCAL_PATH)/tests/Android.mk
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index ba3067e..0cfa749 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -16,14 +16,24 @@
package com.android.mtp;
+import android.content.ContentResolver;
import android.database.Cursor;
import android.os.CancellationSignal;
import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
-import java.io.FileNotFoundException;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * DocumentsProvider for MTP devices.
+ */
public class MtpDocumentsProvider extends DocumentsProvider {
private static final String TAG = "MtpDocumentsProvider";
public static final String AUTHORITY = "com.android.mtp.documents";
@@ -40,32 +50,89 @@
Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
};
+ private MtpManager mDeviceModel;
+ private ContentResolver mResolver;
+
@Override
public boolean onCreate() {
+ mDeviceModel = new MtpManager(getContext());
+ mResolver = getContext().getContentResolver();
return true;
}
+ @VisibleForTesting
+ void onCreateForTesting(MtpManager deviceModel, ContentResolver resolver) {
+ this.mDeviceModel = deviceModel;
+ this.mResolver = resolver;
+ }
+
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
- return null;
+ throw new FileNotFoundException();
}
@Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
- return null;
+ throw new FileNotFoundException();
}
@Override
public Cursor queryChildDocuments(String parentDocumentId,
String[] projection, String sortOrder)
throws FileNotFoundException {
- return null;
+ throw new FileNotFoundException();
}
@Override
public ParcelFileDescriptor openDocument(String documentId, String mode,
CancellationSignal signal) throws FileNotFoundException {
- return null;
+ throw new FileNotFoundException();
+ }
+
+ // TODO: Remove annotation when the method starts to be used.
+ @VisibleForTesting
+ void openDevice(int deviceId) {
+ try {
+ mDeviceModel.openDevice(deviceId);
+ notifyRootsUpdate();
+ } catch (IOException error) {
+ Log.d(TAG, "Failed to open the MTP device: " + deviceId);
+ }
+ }
+
+ // TODO: Remove annotation when the method starts to be used.
+ @VisibleForTesting
+ void closeDevice(int deviceId) {
+ try {
+ mDeviceModel.closeDevice(deviceId);
+ notifyRootsUpdate();
+ } catch (IOException error) {
+ Log.d(TAG, "Failed to close the MTP device: " + deviceId);
+ }
+ }
+
+ // TODO: Remove annotation when the method starts to be used.
+ @VisibleForTesting
+ void closeAllDevices() {
+ boolean closed = false;
+ for (int deviceId : mDeviceModel.getOpenedDeviceIds()) {
+ try {
+ mDeviceModel.closeDevice(deviceId);
+ closed = true;
+ } catch (IOException d) {
+ Log.d(TAG, "Failed to close the MTP device: " + deviceId);
+ }
+ }
+ if (closed) {
+ notifyRootsUpdate();
+ }
+ }
+
+ private void notifyRootsUpdate() {
+ mResolver.notifyChange(
+ DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY),
+ null,
+ false);
}
}
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
new file mode 100644
index 0000000..eea38b1
--- /dev/null
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.android.mtp;
+
+import android.content.Context;
+
+import java.io.IOException;
+
+/**
+ * The model wrapping android.mtp API.
+ */
+class MtpManager {
+ MtpManager(Context context) {
+ }
+
+ void openDevice(int deviceId) throws IOException {
+ // TODO: Implement this method.
+ }
+
+ void closeDevice(int deviceId) throws IOException {
+ // TODO: Implement this method.
+ }
+
+ int[] getOpenedDeviceIds() {
+ // TODO: Implement this method.
+ return null;
+ }
+}
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 990d6ae..7e91cc7 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -16,13 +16,92 @@
package com.android.mtp;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
+import java.io.IOException;
+
@SmallTest
public class MtpDocumentsProviderTest extends AndroidTestCase {
- public void testBasic() {
+ public void testOpenAndCloseDevice() throws Exception {
+ final ContentResolver resolver = new ContentResolver();
final MtpDocumentsProvider provider = new MtpDocumentsProvider();
- assertNotNull(provider);
+ provider.onCreateForTesting(new MtpManagerMock(getContext()), resolver);
+
+ provider.openDevice(MtpManagerMock.SUCCESS_DEVICE_ID);
+ assertEquals(1, resolver.changeCount);
+ provider.closeDevice(MtpManagerMock.SUCCESS_DEVICE_ID);
+ assertEquals(2, resolver.changeCount);
+
+ provider.openDevice(MtpManagerMock.FAILURE_DEVICE_ID);
+ assertEquals(2, resolver.changeCount);
+ provider.closeDevice(MtpManagerMock.FAILURE_DEVICE_ID);
+ assertEquals(2, resolver.changeCount);
+ }
+
+ public void testCloseAllDevices() {
+ final ContentResolver resolver = new ContentResolver();
+ final MtpDocumentsProvider provider = new MtpDocumentsProvider();
+ provider.onCreateForTesting(new MtpManagerMock(getContext()), resolver);
+
+ provider.closeAllDevices();
+ assertEquals(0, resolver.changeCount);
+
+ provider.openDevice(MtpManagerMock.SUCCESS_DEVICE_ID);
+ assertEquals(1, resolver.changeCount);
+
+ provider.closeAllDevices();
+ assertEquals(2, resolver.changeCount);
+ }
+
+ private static class MtpManagerMock extends MtpManager {
+ final static int SUCCESS_DEVICE_ID = 1;
+ final static int FAILURE_DEVICE_ID = 2;
+
+ private boolean opened = false;
+
+ MtpManagerMock(Context context) {
+ super(context);
+ }
+
+ @Override
+ void openDevice(int deviceId) throws IOException {
+ if (deviceId == SUCCESS_DEVICE_ID) {
+ opened = true;
+ } else {
+ throw new IOException();
+ }
+ }
+
+ @Override
+ void closeDevice(int deviceId) throws IOException {
+ if (opened && deviceId == SUCCESS_DEVICE_ID) {
+ opened = false;
+ } else {
+ throw new IOException();
+ }
+ }
+
+ @Override
+ int[] getOpenedDeviceIds() {
+ if (opened) {
+ return new int[] { SUCCESS_DEVICE_ID };
+ } else {
+ return new int[0];
+ }
+ }
+ }
+
+ private static class ContentResolver extends MockContentResolver {
+ int changeCount = 0;
+
+ @Override
+ public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
+ changeCount++;
+ }
}
}