Loading packages/MtpDocumentsProvider/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.mtp" android:sharedUserId="android.media"> <uses-feature android:name="android.hardware.usb.host" /> <application android:label="@string/app_label"> <provider android:name=".MtpDocumentsProvider" Loading @@ -14,5 +15,6 @@ <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> </intent-filter> </provider> <service android:name=".MtpDocumentsService"></service> </application> </manifest> packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +28 −30 Original line number Diff line number Diff line Loading @@ -35,34 +35,42 @@ 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"; static final String AUTHORITY = "com.android.mtp.documents"; static final String TAG = "MtpDocumentsProvider"; private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES, }; private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] { Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE, }; private MtpManager mDeviceModel; private static MtpDocumentsProvider sSingleton; private MtpManager mMtpManager; private ContentResolver mResolver; /** * Provides singleton instance to MtpDocumentsService. */ static MtpDocumentsProvider getInstance() { return sSingleton; } @Override public boolean onCreate() { mDeviceModel = new MtpManager(getContext()); sSingleton = this; mMtpManager = new MtpManager(getContext()); mResolver = getContext().getContentResolver(); return true; } @VisibleForTesting void onCreateForTesting(MtpManager deviceModel, ContentResolver resolver) { this.mDeviceModel = deviceModel; void onCreateForTesting(MtpManager mtpManager, ContentResolver resolver) { this.mMtpManager = mtpManager; this.mResolver = resolver; } Loading Loading @@ -90,35 +98,21 @@ public class MtpDocumentsProvider extends DocumentsProvider { throw new FileNotFoundException(); } // TODO: Remove annotation when the method starts to be used. @VisibleForTesting void openDevice(int deviceId) { try { mDeviceModel.openDevice(deviceId); void openDevice(int deviceId) throws IOException { mMtpManager.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); void closeDevice(int deviceId) throws IOException { mMtpManager.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()) { for (int deviceId : mMtpManager.getOpenedDeviceIds()) { try { mDeviceModel.closeDevice(deviceId); mMtpManager.closeDevice(deviceId); closed = true; } catch (IOException d) { Log.d(TAG, "Failed to close the MTP device: " + deviceId); Loading @@ -135,4 +129,8 @@ public class MtpDocumentsProvider extends DocumentsProvider { null, false); } boolean hasOpenedDevices() { return mMtpManager.getOpenedDeviceIds().length != 0; } } packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java 0 → 100644 +111 −0 Original line number Diff line number Diff line /* * 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.app.Service; 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.UsbManager; import android.os.IBinder; import android.util.Log; import java.io.IOException; /** * Service to manage lifetime of DocumentsProvider's process. * The service prevents the system from killing the process that holds USB connections. The service * starts to run when the first MTP device is opened, and stops when the last MTP device is closed. */ public class MtpDocumentsService extends Service { static final String ACTION_OPEN_DEVICE = "com.android.mtp.action.ACTION_OPEN_DEVICE"; static final String EXTRA_DEVICE = "device"; Receiver mReceiver; @Override public IBinder onBind(Intent intent) { // The service is used via intents. return null; } @Override public void onCreate() { super.onCreate(); final IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED); mReceiver = new Receiver(); registerReceiver(mReceiver, filter); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent == null) { // If intent is null, the service was restarted. // TODO: Recover opened devices here. return START_STICKY; } if (intent.getAction().equals(ACTION_OPEN_DEVICE)) { final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE); try { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); provider.openDevice(device.getDeviceId()); return START_STICKY; } catch (IOException error) { Log.d(MtpDocumentsProvider.TAG, error.getMessage()); } } else { Log.d(MtpDocumentsProvider.TAG, "Received unknown intent action."); } stopSelfIfNeeded(); return Service.START_NOT_STICKY; } @Override public void onDestroy() { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); provider.closeAllDevices(); unregisterReceiver(mReceiver); mReceiver = null; super.onDestroy(); } private void stopSelfIfNeeded() { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); if (!provider.hasOpenedDevices()) { stopSelf(); } } private class Receiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { final UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); try { provider.closeDevice(device.getDeviceId()); } catch (IOException error) { Log.d(MtpDocumentsProvider.TAG, error.getMessage()); } stopSelfIfNeeded(); } } } } packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +13 −3 Original line number Diff line number Diff line Loading @@ -37,13 +37,23 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { provider.closeDevice(MtpManagerMock.SUCCESS_DEVICE_ID); assertEquals(2, resolver.changeCount); int exceptionCounter = 0; try { provider.openDevice(MtpManagerMock.FAILURE_DEVICE_ID); } catch (IOException error) { exceptionCounter++; } assertEquals(2, resolver.changeCount); try { provider.closeDevice(MtpManagerMock.FAILURE_DEVICE_ID); } catch (IOException error) { exceptionCounter++; } assertEquals(2, resolver.changeCount); assertEquals(2, exceptionCounter); } public void testCloseAllDevices() { public void testCloseAllDevices() throws IOException { final ContentResolver resolver = new ContentResolver(); final MtpDocumentsProvider provider = new MtpDocumentsProvider(); provider.onCreateForTesting(new MtpManagerMock(getContext()), resolver); Loading Loading
packages/MtpDocumentsProvider/AndroidManifest.xml +2 −0 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.mtp" android:sharedUserId="android.media"> <uses-feature android:name="android.hardware.usb.host" /> <application android:label="@string/app_label"> <provider android:name=".MtpDocumentsProvider" Loading @@ -14,5 +15,6 @@ <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> </intent-filter> </provider> <service android:name=".MtpDocumentsService"></service> </application> </manifest>
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +28 −30 Original line number Diff line number Diff line Loading @@ -35,34 +35,42 @@ 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"; static final String AUTHORITY = "com.android.mtp.documents"; static final String TAG = "MtpDocumentsProvider"; private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES, }; private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] { Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE, }; private MtpManager mDeviceModel; private static MtpDocumentsProvider sSingleton; private MtpManager mMtpManager; private ContentResolver mResolver; /** * Provides singleton instance to MtpDocumentsService. */ static MtpDocumentsProvider getInstance() { return sSingleton; } @Override public boolean onCreate() { mDeviceModel = new MtpManager(getContext()); sSingleton = this; mMtpManager = new MtpManager(getContext()); mResolver = getContext().getContentResolver(); return true; } @VisibleForTesting void onCreateForTesting(MtpManager deviceModel, ContentResolver resolver) { this.mDeviceModel = deviceModel; void onCreateForTesting(MtpManager mtpManager, ContentResolver resolver) { this.mMtpManager = mtpManager; this.mResolver = resolver; } Loading Loading @@ -90,35 +98,21 @@ public class MtpDocumentsProvider extends DocumentsProvider { throw new FileNotFoundException(); } // TODO: Remove annotation when the method starts to be used. @VisibleForTesting void openDevice(int deviceId) { try { mDeviceModel.openDevice(deviceId); void openDevice(int deviceId) throws IOException { mMtpManager.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); void closeDevice(int deviceId) throws IOException { mMtpManager.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()) { for (int deviceId : mMtpManager.getOpenedDeviceIds()) { try { mDeviceModel.closeDevice(deviceId); mMtpManager.closeDevice(deviceId); closed = true; } catch (IOException d) { Log.d(TAG, "Failed to close the MTP device: " + deviceId); Loading @@ -135,4 +129,8 @@ public class MtpDocumentsProvider extends DocumentsProvider { null, false); } boolean hasOpenedDevices() { return mMtpManager.getOpenedDeviceIds().length != 0; } }
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java 0 → 100644 +111 −0 Original line number Diff line number Diff line /* * 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.app.Service; 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.UsbManager; import android.os.IBinder; import android.util.Log; import java.io.IOException; /** * Service to manage lifetime of DocumentsProvider's process. * The service prevents the system from killing the process that holds USB connections. The service * starts to run when the first MTP device is opened, and stops when the last MTP device is closed. */ public class MtpDocumentsService extends Service { static final String ACTION_OPEN_DEVICE = "com.android.mtp.action.ACTION_OPEN_DEVICE"; static final String EXTRA_DEVICE = "device"; Receiver mReceiver; @Override public IBinder onBind(Intent intent) { // The service is used via intents. return null; } @Override public void onCreate() { super.onCreate(); final IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED); mReceiver = new Receiver(); registerReceiver(mReceiver, filter); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent == null) { // If intent is null, the service was restarted. // TODO: Recover opened devices here. return START_STICKY; } if (intent.getAction().equals(ACTION_OPEN_DEVICE)) { final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE); try { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); provider.openDevice(device.getDeviceId()); return START_STICKY; } catch (IOException error) { Log.d(MtpDocumentsProvider.TAG, error.getMessage()); } } else { Log.d(MtpDocumentsProvider.TAG, "Received unknown intent action."); } stopSelfIfNeeded(); return Service.START_NOT_STICKY; } @Override public void onDestroy() { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); provider.closeAllDevices(); unregisterReceiver(mReceiver); mReceiver = null; super.onDestroy(); } private void stopSelfIfNeeded() { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); if (!provider.hasOpenedDevices()) { stopSelf(); } } private class Receiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(intent.getAction())) { final UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); try { provider.closeDevice(device.getDeviceId()); } catch (IOException error) { Log.d(MtpDocumentsProvider.TAG, error.getMessage()); } stopSelfIfNeeded(); } } } }
packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java +13 −3 Original line number Diff line number Diff line Loading @@ -37,13 +37,23 @@ public class MtpDocumentsProviderTest extends AndroidTestCase { provider.closeDevice(MtpManagerMock.SUCCESS_DEVICE_ID); assertEquals(2, resolver.changeCount); int exceptionCounter = 0; try { provider.openDevice(MtpManagerMock.FAILURE_DEVICE_ID); } catch (IOException error) { exceptionCounter++; } assertEquals(2, resolver.changeCount); try { provider.closeDevice(MtpManagerMock.FAILURE_DEVICE_ID); } catch (IOException error) { exceptionCounter++; } assertEquals(2, resolver.changeCount); assertEquals(2, exceptionCounter); } public void testCloseAllDevices() { public void testCloseAllDevices() throws IOException { final ContentResolver resolver = new ContentResolver(); final MtpDocumentsProvider provider = new MtpDocumentsProvider(); provider.onCreateForTesting(new MtpManagerMock(getContext()), resolver); Loading