Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +33 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.res.Resources; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; Loading @@ -34,6 +35,7 @@ import android.provider.DocumentsContract.Root; import com.android.internal.annotations.VisibleForTesting; import java.io.File; import java.io.FileNotFoundException; import java.util.Objects; Loading Loading @@ -305,6 +307,32 @@ class MtpDatabase { } } /** * Returns the set of device ID stored in the database. */ int[] getDeviceIds() { final Cursor cursor = mDatabase.query( true, TABLE_DOCUMENTS, strings(COLUMN_DEVICE_ID), null, null, null, null, null, null); try { final int[] ids = new int[cursor.getCount()]; for (int i = 0; i < ids.length; i++) { cursor.moveToNext(); ids[i] = cursor.getInt(0); } return ids; } finally { cursor.close(); } } private boolean deleteDocumentsAndRoots(String selection, String[] args) { mDatabase.beginTransaction(); try { Loading Loading @@ -351,6 +379,11 @@ class MtpDatabase { } } @VisibleForTesting static void deleteDatabase(Context context) { context.deleteDatabase(DATABASE_NAME); } /** * Gets {@link ContentValues} for the given root. * @param values {@link ContentValues} that receives values. Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import android.provider.DocumentsContract.Root; */ class MtpDatabaseConstants { static final int DATABASE_VERSION = 1; static final String DATABASE_NAME = null; static final String DATABASE_NAME = "database"; static final int FLAG_DATABASE_IN_MEMORY = 1; static final int FLAG_DATABASE_IN_FILE = 0; Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +54 −41 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.content.ContentResolver; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.database.Cursor; import android.database.MatrixCursor; import android.graphics.Point; import android.media.MediaFile; import android.mtp.MtpConstants; Loading Loading @@ -58,11 +57,13 @@ public class MtpDocumentsProvider extends DocumentsProvider { Document.COLUMN_FLAGS, Document.COLUMN_SIZE, }; private final Object mDeviceListLock = new Object(); private static MtpDocumentsProvider sSingleton; private MtpManager mMtpManager; private ContentResolver mResolver; @GuardedBy("mDeviceToolkits") @GuardedBy("mDeviceListLock") private Map<Integer, DeviceToolkit> mDeviceToolkits; private RootScanner mRootScanner; private Resources mResources; Loading @@ -84,6 +85,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE); mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); resume(); return true; } Loading @@ -99,6 +101,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); mDatabase = database; mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); resume(); } @Override Loading Loading @@ -196,7 +199,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { @Override public void onTrimMemory(int level) { synchronized (mDeviceToolkits) { synchronized (mDeviceListLock) { for (final DeviceToolkit toolkit : mDeviceToolkits.values()) { toolkit.mDocumentLoader.clearCompletedTasks(); } Loading Loading @@ -234,64 +237,47 @@ public class MtpDocumentsProvider extends DocumentsProvider { } void openDevice(int deviceId) throws IOException { synchronized (mDeviceToolkits) { synchronized (mDeviceListLock) { mMtpManager.openDevice(deviceId); mDeviceToolkits.put(deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase)); mDeviceToolkits.put( deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase)); } mRootScanner.resume(); } void closeDevice(int deviceId) throws IOException, InterruptedException { // TODO: Flush the device before closing (if not closed externally). synchronized (mDeviceToolkits) { getDeviceToolkit(deviceId).mDocumentLoader.clearTasks(); mDeviceToolkits.remove(deviceId); mDatabase.removeDeviceRows(deviceId); mMtpManager.closeDevice(deviceId); } mRootScanner.notifyChange(); if (!hasOpenedDevices()) { mRootScanner.pause(); } } synchronized void closeAllDevices() throws InterruptedException { boolean closed = false; for (int deviceId : mMtpManager.getOpenedDeviceIds()) { try { synchronized (mDeviceListLock) { closeDeviceInternal(deviceId); mDatabase.removeDeviceRows(deviceId); mMtpManager.closeDevice(deviceId); getDeviceToolkit(deviceId).mDocumentLoader.clearTasks(); closed = true; } catch (IOException d) { Log.d(TAG, "Failed to close the MTP device: " + deviceId); } } if (closed) { mRootScanner.notifyChange(); mRootScanner.pause(); } } boolean hasOpenedDevices() { synchronized (mDeviceListLock) { return mMtpManager.getOpenedDeviceIds().length != 0; } } /** * Finalize the content provider for unit tests. */ @Override public void shutdown() { synchronized (mDeviceListLock) { try { closeAllDevices(); } catch (InterruptedException e) { for (final int id : mMtpManager.getOpenedDeviceIds()) { closeDeviceInternal(id); } } catch (InterruptedException|IOException e) { // It should fail unit tests by throwing runtime exception. throw new RuntimeException(e.getMessage()); throw new RuntimeException(e); } finally { mDatabase.close(); super.shutdown(); } } } private void notifyChildDocumentsChange(String parentDocumentId) { mResolver.notifyChange( Loading @@ -300,8 +286,35 @@ public class MtpDocumentsProvider extends DocumentsProvider { false); } /** * Reopens MTP devices based on database state. */ private void resume() { synchronized (mDeviceListLock) { mDatabase.getMapper().clearMapping(); final int[] ids = mDatabase.getDeviceIds(); for (final int id : ids) { try { openDevice(id); } catch (IOException exception) { mDatabase.removeDeviceRows(id); } } } } private void closeDeviceInternal(int deviceId) throws IOException, InterruptedException { // TODO: Flush the device before closing (if not closed externally). getDeviceToolkit(deviceId).mDocumentLoader.clearTasks(); mDeviceToolkits.remove(deviceId); mMtpManager.closeDevice(deviceId); if (!hasOpenedDevices()) { mRootScanner.pause(); } } private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException { synchronized (mDeviceToolkits) { synchronized (mDeviceListLock) { final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId); if (toolkit == null) { throw new FileNotFoundException(); Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java +13 −21 Original line number Diff line number Diff line Loading @@ -55,11 +55,8 @@ public class MtpDocumentsService extends Service { @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 != null) { if (intent.getAction().equals(ACTION_OPEN_DEVICE)) { final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE); try { Loading @@ -70,7 +67,8 @@ public class MtpDocumentsService extends Service { Log.e(MtpDocumentsProvider.TAG, error.getMessage()); } } else { Log.w(MtpDocumentsProvider.TAG, "Received unknown intent action."); Log.e(MtpDocumentsProvider.TAG, "Received unknown intent action."); } } stopSelfIfNeeded(); return Service.START_NOT_STICKY; Loading @@ -78,14 +76,8 @@ public class MtpDocumentsService extends Service { @Override public void onDestroy() { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); unregisterReceiver(mReceiver); mReceiver = null; try { provider.closeAllDevices(); } catch (InterruptedException e) { Log.e(MtpDocumentsProvider.TAG, e.getMessage()); } super.onDestroy(); } Loading packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +1 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; /** * The model wrapping android.mtp API. Loading Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java +33 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.res.Resources; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; Loading @@ -34,6 +35,7 @@ import android.provider.DocumentsContract.Root; import com.android.internal.annotations.VisibleForTesting; import java.io.File; import java.io.FileNotFoundException; import java.util.Objects; Loading Loading @@ -305,6 +307,32 @@ class MtpDatabase { } } /** * Returns the set of device ID stored in the database. */ int[] getDeviceIds() { final Cursor cursor = mDatabase.query( true, TABLE_DOCUMENTS, strings(COLUMN_DEVICE_ID), null, null, null, null, null, null); try { final int[] ids = new int[cursor.getCount()]; for (int i = 0; i < ids.length; i++) { cursor.moveToNext(); ids[i] = cursor.getInt(0); } return ids; } finally { cursor.close(); } } private boolean deleteDocumentsAndRoots(String selection, String[] args) { mDatabase.beginTransaction(); try { Loading Loading @@ -351,6 +379,11 @@ class MtpDatabase { } } @VisibleForTesting static void deleteDatabase(Context context) { context.deleteDatabase(DATABASE_NAME); } /** * Gets {@link ContentValues} for the given root. * @param values {@link ContentValues} that receives values. Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabaseConstants.java +1 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import android.provider.DocumentsContract.Root; */ class MtpDatabaseConstants { static final int DATABASE_VERSION = 1; static final String DATABASE_NAME = null; static final String DATABASE_NAME = "database"; static final int FLAG_DATABASE_IN_MEMORY = 1; static final int FLAG_DATABASE_IN_FILE = 0; Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java +54 −41 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ import android.content.ContentResolver; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.database.Cursor; import android.database.MatrixCursor; import android.graphics.Point; import android.media.MediaFile; import android.mtp.MtpConstants; Loading Loading @@ -58,11 +57,13 @@ public class MtpDocumentsProvider extends DocumentsProvider { Document.COLUMN_FLAGS, Document.COLUMN_SIZE, }; private final Object mDeviceListLock = new Object(); private static MtpDocumentsProvider sSingleton; private MtpManager mMtpManager; private ContentResolver mResolver; @GuardedBy("mDeviceToolkits") @GuardedBy("mDeviceListLock") private Map<Integer, DeviceToolkit> mDeviceToolkits; private RootScanner mRootScanner; private Resources mResources; Loading @@ -84,6 +85,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE); mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); resume(); return true; } Loading @@ -99,6 +101,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); mDatabase = database; mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); resume(); } @Override Loading Loading @@ -196,7 +199,7 @@ public class MtpDocumentsProvider extends DocumentsProvider { @Override public void onTrimMemory(int level) { synchronized (mDeviceToolkits) { synchronized (mDeviceListLock) { for (final DeviceToolkit toolkit : mDeviceToolkits.values()) { toolkit.mDocumentLoader.clearCompletedTasks(); } Loading Loading @@ -234,64 +237,47 @@ public class MtpDocumentsProvider extends DocumentsProvider { } void openDevice(int deviceId) throws IOException { synchronized (mDeviceToolkits) { synchronized (mDeviceListLock) { mMtpManager.openDevice(deviceId); mDeviceToolkits.put(deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase)); mDeviceToolkits.put( deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase)); } mRootScanner.resume(); } void closeDevice(int deviceId) throws IOException, InterruptedException { // TODO: Flush the device before closing (if not closed externally). synchronized (mDeviceToolkits) { getDeviceToolkit(deviceId).mDocumentLoader.clearTasks(); mDeviceToolkits.remove(deviceId); mDatabase.removeDeviceRows(deviceId); mMtpManager.closeDevice(deviceId); } mRootScanner.notifyChange(); if (!hasOpenedDevices()) { mRootScanner.pause(); } } synchronized void closeAllDevices() throws InterruptedException { boolean closed = false; for (int deviceId : mMtpManager.getOpenedDeviceIds()) { try { synchronized (mDeviceListLock) { closeDeviceInternal(deviceId); mDatabase.removeDeviceRows(deviceId); mMtpManager.closeDevice(deviceId); getDeviceToolkit(deviceId).mDocumentLoader.clearTasks(); closed = true; } catch (IOException d) { Log.d(TAG, "Failed to close the MTP device: " + deviceId); } } if (closed) { mRootScanner.notifyChange(); mRootScanner.pause(); } } boolean hasOpenedDevices() { synchronized (mDeviceListLock) { return mMtpManager.getOpenedDeviceIds().length != 0; } } /** * Finalize the content provider for unit tests. */ @Override public void shutdown() { synchronized (mDeviceListLock) { try { closeAllDevices(); } catch (InterruptedException e) { for (final int id : mMtpManager.getOpenedDeviceIds()) { closeDeviceInternal(id); } } catch (InterruptedException|IOException e) { // It should fail unit tests by throwing runtime exception. throw new RuntimeException(e.getMessage()); throw new RuntimeException(e); } finally { mDatabase.close(); super.shutdown(); } } } private void notifyChildDocumentsChange(String parentDocumentId) { mResolver.notifyChange( Loading @@ -300,8 +286,35 @@ public class MtpDocumentsProvider extends DocumentsProvider { false); } /** * Reopens MTP devices based on database state. */ private void resume() { synchronized (mDeviceListLock) { mDatabase.getMapper().clearMapping(); final int[] ids = mDatabase.getDeviceIds(); for (final int id : ids) { try { openDevice(id); } catch (IOException exception) { mDatabase.removeDeviceRows(id); } } } } private void closeDeviceInternal(int deviceId) throws IOException, InterruptedException { // TODO: Flush the device before closing (if not closed externally). getDeviceToolkit(deviceId).mDocumentLoader.clearTasks(); mDeviceToolkits.remove(deviceId); mMtpManager.closeDevice(deviceId); if (!hasOpenedDevices()) { mRootScanner.pause(); } } private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException { synchronized (mDeviceToolkits) { synchronized (mDeviceListLock) { final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId); if (toolkit == null) { throw new FileNotFoundException(); Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsService.java +13 −21 Original line number Diff line number Diff line Loading @@ -55,11 +55,8 @@ public class MtpDocumentsService extends Service { @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 != null) { if (intent.getAction().equals(ACTION_OPEN_DEVICE)) { final UsbDevice device = intent.<UsbDevice>getParcelableExtra(EXTRA_DEVICE); try { Loading @@ -70,7 +67,8 @@ public class MtpDocumentsService extends Service { Log.e(MtpDocumentsProvider.TAG, error.getMessage()); } } else { Log.w(MtpDocumentsProvider.TAG, "Received unknown intent action."); Log.e(MtpDocumentsProvider.TAG, "Received unknown intent action."); } } stopSelfIfNeeded(); return Service.START_NOT_STICKY; Loading @@ -78,14 +76,8 @@ public class MtpDocumentsService extends Service { @Override public void onDestroy() { final MtpDocumentsProvider provider = MtpDocumentsProvider.getInstance(); unregisterReceiver(mReceiver); mReceiver = null; try { provider.closeAllDevices(); } catch (InterruptedException e) { Log.e(MtpDocumentsProvider.TAG, e.getMessage()); } super.onDestroy(); } Loading
packages/MtpDocumentsProvider/src/com/android/mtp/MtpManager.java +1 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; /** * The model wrapping android.mtp API. Loading