MtpDocumentsProvider.java revision 19aa93249edc5dac01025456ce3bb1881f1b11d1
1c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono/* 2c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * Copyright (C) 2015 The Android Open Source Project 3c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * 4c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * Licensed under the Apache License, Version 2.0 (the "License"); 5c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * you may not use this file except in compliance with the License. 6c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * You may obtain a copy of the License at 7c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * 8c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * http://www.apache.org/licenses/LICENSE-2.0 9c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * 10c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * Unless required by applicable law or agreed to in writing, software 11c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * distributed under the License is distributed on an "AS IS" BASIS, 12c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * See the License for the specific language governing permissions and 14c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono * limitations under the License. 15c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono */ 16c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 17c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironopackage com.android.mtp; 18c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 19d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport android.content.ContentResolver; 203faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hironoimport android.content.res.AssetFileDescriptor; 2117c8d8bcdae4b31d295431fc089db81bf3721c54Daichi Hironoimport android.content.res.Resources; 22c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.database.Cursor; 233faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hironoimport android.graphics.Point; 249e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hironoimport android.media.MediaFile; 259e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hironoimport android.mtp.MtpConstants; 26bb430fa930fa0d0700e46e7b4881de2a252223ddTomasz Mikolajewskiimport android.mtp.MtpObjectInfo; 27c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.os.CancellationSignal; 28c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.os.ParcelFileDescriptor; 29f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hironoimport android.os.storage.StorageManager; 30c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.provider.DocumentsContract.Document; 31c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.provider.DocumentsContract.Root; 32bb430fa930fa0d0700e46e7b4881de2a252223ddTomasz Mikolajewskiimport android.provider.DocumentsContract; 33c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.provider.DocumentsProvider; 34d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport android.util.Log; 35d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 36e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hironoimport com.android.internal.annotations.GuardedBy; 37d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport com.android.internal.annotations.VisibleForTesting; 38d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 39c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport java.io.FileNotFoundException; 40d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport java.io.IOException; 414c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewskiimport java.util.HashMap; 424c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewskiimport java.util.Map; 43c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 44d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono/** 45d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono * DocumentsProvider for MTP devices. 46d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono */ 47c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironopublic class MtpDocumentsProvider extends DocumentsProvider { 482efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono static final String AUTHORITY = "com.android.mtp.documents"; 492efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono static final String TAG = "MtpDocumentsProvider"; 506baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono static final String[] DEFAULT_ROOT_PROJECTION = new String[] { 51c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, 52c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, 53c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono Root.COLUMN_AVAILABLE_BYTES, 54c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono }; 556baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] { 56c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, 57c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED, 58c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono Document.COLUMN_FLAGS, Document.COLUMN_SIZE, 59c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono }; 60c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 6119aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono static final boolean DEBUG = true; 6219aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono 63e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono private final Object mDeviceListLock = new Object(); 64e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono 652efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono private static MtpDocumentsProvider sSingleton; 662efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono 672efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono private MtpManager mMtpManager; 68d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono private ContentResolver mResolver; 69e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono @GuardedBy("mDeviceListLock") 704c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private Map<Integer, DeviceToolkit> mDeviceToolkits; 718b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono private RootScanner mRootScanner; 7217c8d8bcdae4b31d295431fc089db81bf3721c54Daichi Hirono private Resources mResources; 73dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono private MtpDatabase mDatabase; 74f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono private AppFuse mAppFuse; 75fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono private ServiceIntentSender mIntentSender; 76d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 772efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono /** 782efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono * Provides singleton instance to MtpDocumentsService. 792efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono */ 802efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono static MtpDocumentsProvider getInstance() { 812efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono return sSingleton; 822efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono } 832efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono 84c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 85c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono public boolean onCreate() { 862efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono sSingleton = this; 8717c8d8bcdae4b31d295431fc089db81bf3721c54Daichi Hirono mResources = getContext().getResources(); 882efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono mMtpManager = new MtpManager(getContext()); 89d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono mResolver = getContext().getContentResolver(); 904c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); 9147eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE); 92dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); 93f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono mAppFuse = new AppFuse(TAG, new AppFuseCallback()); 94fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mIntentSender = new ServiceIntentSender(getContext()); 95f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono // TODO: Mount AppFuse on demands. 96e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono try { 97e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono mAppFuse.mount(getContext().getSystemService(StorageManager.class)); 98e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono } catch (IOException e) { 99e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono Log.e(TAG, "Failed to start app fuse.", e); 100e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono return false; 101e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono } 102e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono resume(); 103c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono return true; 104c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono } 105c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 106d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono @VisibleForTesting 107b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono boolean onCreateForTesting( 108dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono Resources resources, 109dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono MtpManager mtpManager, 110dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono ContentResolver resolver, 111b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono MtpDatabase database, 112fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono StorageManager storageManager, 113fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono ServiceIntentSender intentSender) { 11417c8d8bcdae4b31d295431fc089db81bf3721c54Daichi Hirono mResources = resources; 1156baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono mMtpManager = mtpManager; 1166baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono mResolver = resolver; 1174c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); 118dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono mDatabase = database; 119dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); 120b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono mAppFuse = new AppFuse(TAG, new AppFuseCallback()); 121fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mIntentSender = intentSender; 122b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono // TODO: Mount AppFuse on demands. 123b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono try { 124b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono mAppFuse.mount(storageManager); 125b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono } catch (IOException e) { 126b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono Log.e(TAG, "Failed to start app fuse.", e); 127b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono return false; 128b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono } 129e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono resume(); 130b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono return true; 131d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono } 132d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 133c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 134c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono public Cursor queryRoots(String[] projection) throws FileNotFoundException { 13550d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono if (projection == null) { 13650d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono projection = MtpDocumentsProvider.DEFAULT_ROOT_PROJECTION; 13750d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono } 138dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono final Cursor cursor = mDatabase.queryRoots(projection); 13950d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono cursor.setNotificationUri( 14050d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono mResolver, DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY)); 14150d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono return cursor; 142c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono } 143c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 144c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 145c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono public Cursor queryDocument(String documentId, String[] projection) 146c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono throws FileNotFoundException { 147e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono if (projection == null) { 148e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION; 149e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono } 1509e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono return mDatabase.queryDocument(documentId, projection); 151c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono } 152c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 153c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 154124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono public Cursor queryChildDocuments(String parentDocumentId, 155124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono String[] projection, String sortOrder) throws FileNotFoundException { 15619aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono if (DEBUG) { 15719aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono Log.d(TAG, "queryChildDocuments: " + parentDocumentId); 15819aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono } 159124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono if (projection == null) { 160124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION; 161124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono } 1626a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Identifier parentIdentifier = mDatabase.createIdentifier(parentDocumentId); 163124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono try { 164fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(parentIdentifier.mDeviceId); 1656a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) { 1666a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono final Identifier singleStorageIdentifier = 1676a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono mDatabase.getSingleStorageIdentifier(parentDocumentId); 1686a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono if (singleStorageIdentifier == null) { 1696a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono // Returns storage list from database. 1706a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono return mDatabase.queryChildDocuments(projection, parentDocumentId); 1716a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono } 1726a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono parentIdentifier = singleStorageIdentifier; 1736a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono } 1746a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono // Returns object list from document loader. 1754c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski return getDocumentLoader(parentIdentifier).queryChildDocuments( 1764c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski projection, parentIdentifier); 177124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono } catch (IOException exception) { 1786a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(MtpDocumentsProvider.TAG, "queryChildDocuments", exception); 179124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono throw new FileNotFoundException(exception.getMessage()); 180124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono } 181c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono } 182c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 183c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 1848ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono public ParcelFileDescriptor openDocument( 1858ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono String documentId, String mode, CancellationSignal signal) 1868ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono throws FileNotFoundException { 1879e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final Identifier identifier = mDatabase.createIdentifier(documentId); 1888ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono try { 189fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(identifier.mDeviceId); 190b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski switch (mode) { 191b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski case "r": 192f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono final long fileSize = getFileSize(documentId); 193fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono // MTP getPartialObject operation does not support files that are larger than 194fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono // 4GB. Fallback to non-seekable file descriptor. 195f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono // TODO: Use getPartialObject64 for MTP devices that support Android vendor 196f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono // extension. 197b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono if (fileSize <= 0xffffffffl) { 198f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono return mAppFuse.openFile(Integer.parseInt(documentId)); 199f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } else { 200f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono return getPipeManager(identifier).readDocument(mMtpManager, identifier); 201f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 202b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski case "w": 20381d74743107b372424fb8f7439357bdd608f8cafTomasz Mikolajewski // TODO: Clear the parent document loader task (if exists) and call notify 20481d74743107b372424fb8f7439357bdd608f8cafTomasz Mikolajewski // when writing is completed. 2054c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski return getPipeManager(identifier).writeDocument( 2064c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski getContext(), mMtpManager, identifier); 207f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono case "rw": 208f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono // TODO: Add support for "rw" mode. 209b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski throw new UnsupportedOperationException( 210f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono "The provider does not support 'rw' mode."); 211f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono default: 212f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono throw new IllegalArgumentException("Unknown mode for openDocument: " + mode); 213b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski } 2148ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono } catch (IOException error) { 2156a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(MtpDocumentsProvider.TAG, "openDocument", error); 2168ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono throw new FileNotFoundException(error.getMessage()); 2178ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono } 218d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono } 219d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 2203faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono @Override 2213faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono public AssetFileDescriptor openDocumentThumbnail( 2223faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono String documentId, 2233faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono Point sizeHint, 2243faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono CancellationSignal signal) throws FileNotFoundException { 2259e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final Identifier identifier = mDatabase.createIdentifier(documentId); 2263faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono try { 227fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(identifier.mDeviceId); 2283faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono return new AssetFileDescriptor( 2294c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski getPipeManager(identifier).readThumbnail(mMtpManager, identifier), 230573c1fbc5f98f033681e378ec965136bce49c899Daichi Hirono 0, // Start offset. 2313faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono AssetFileDescriptor.UNKNOWN_LENGTH); 2323faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } catch (IOException error) { 2336a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(MtpDocumentsProvider.TAG, "openDocumentThumbnail", error); 2343faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono throw new FileNotFoundException(error.getMessage()); 2353faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } 2363faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } 2373faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono 2383faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono @Override 2393faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono public void deleteDocument(String documentId) throws FileNotFoundException { 2403faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono try { 2419e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final Identifier identifier = mDatabase.createIdentifier(documentId); 242fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(identifier.mDeviceId); 2436a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono final Identifier parentIdentifier = mDatabase.getParentIdentifier(documentId); 2443faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle); 2459e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono mDatabase.deleteDocument(documentId); 2464c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski getDocumentLoader(parentIdentifier).clearTask(parentIdentifier); 2479e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono notifyChildDocumentsChange(parentIdentifier.mDocumentId); 2486a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) { 2496a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono // If the parent is storage, the object might be appeared as child of device because 2506a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono // we skip storage when the device has only one storage. 2516a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono final Identifier deviceIdentifier = mDatabase.getParentIdentifier( 2526a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono parentIdentifier.mDocumentId); 2536a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono notifyChildDocumentsChange(deviceIdentifier.mDocumentId); 2546a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono } 2553faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } catch (IOException error) { 2566a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(MtpDocumentsProvider.TAG, "deleteDocument", error); 2573faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono throw new FileNotFoundException(error.getMessage()); 2583faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } 2593faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } 2603faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono 2616baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono @Override 2626baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono public void onTrimMemory(int level) { 263e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 264e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono for (final DeviceToolkit toolkit : mDeviceToolkits.values()) { 265e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono toolkit.mDocumentLoader.clearCompletedTasks(); 266e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 267e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 2686baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 2696baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 27087763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski @Override 27187763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski public String createDocument(String parentDocumentId, String mimeType, String displayName) 27287763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski throws FileNotFoundException { 27387763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski try { 2749e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final Identifier parentId = mDatabase.createIdentifier(parentDocumentId); 275fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(parentId.mDeviceId); 276df544176b10f536969de1ed143b0ba57123fcb93Tomasz Mikolajewski final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe(); 277df544176b10f536969de1ed143b0ba57123fcb93Tomasz Mikolajewski pipe[0].close(); // 0 bytes for a new document. 2789e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ? 2799e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono MtpConstants.FORMAT_ASSOCIATION : 2809e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono MediaFile.getFormatCode(displayName, mimeType); 2819e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final MtpObjectInfo info = new MtpObjectInfo.Builder() 2829e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .setStorageId(parentId.mStorageId) 2839e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .setParent(parentId.mObjectHandle) 2849e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .setFormat(formatCode) 2859e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .setName(displayName) 2869e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .build(); 2879e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final int objectHandle = mMtpManager.createDocument(parentId.mDeviceId, info, pipe[1]); 2889e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final MtpObjectInfo infoWithHandle = 2899e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono new MtpObjectInfo.Builder(info).setObjectHandle(objectHandle).build(); 2909e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final String documentId = mDatabase.putNewDocument( 2919e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono parentId.mDeviceId, parentDocumentId, infoWithHandle); 2924c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski getDocumentLoader(parentId).clearTask(parentId); 29387763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski notifyChildDocumentsChange(parentDocumentId); 29487763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski return documentId; 29587763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski } catch (IOException error) { 2966a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(TAG, "createDocument", error); 29787763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski throw new FileNotFoundException(error.getMessage()); 29887763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski } 29987763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski } 30087763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski 3012efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono void openDevice(int deviceId) throws IOException { 302e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 303fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono if (mDeviceToolkits.containsKey(deviceId)) { 304fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono return; 305fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } 30619aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono if (DEBUG) { 30719aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono Log.d(TAG, "Open device " + deviceId); 30819aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono } 309e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono mMtpManager.openDevice(deviceId); 310e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mDeviceToolkits.put( 311e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase)); 312fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mIntentSender.sendUpdateNotificationIntent(); 313fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono try { 314fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mRootScanner.resume().await(); 315fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } catch (InterruptedException error) { 316fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono Log.e(TAG, "openDevice", error); 317fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } 318e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 319d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono } 320d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 321e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono void closeDevice(int deviceId) throws IOException, InterruptedException { 322e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 323e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono closeDeviceInternal(deviceId); 324e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 32520754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono mRootScanner.resume(); 326fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mIntentSender.sendUpdateNotificationIntent(); 327d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono } 328d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 329a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono int[] getOpenedDeviceIds() { 330e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 331a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono return mMtpManager.getOpenedDeviceIds(); 332a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono } 333a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono } 334a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono 335a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono String getDeviceName(int deviceId) throws IOException { 336a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono synchronized (mDeviceListLock) { 33720754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono for (final MtpDeviceRecord device : mMtpManager.getDevices()) { 33820754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono if (device.deviceId == deviceId) { 33920754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono return device.name; 34020754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono } 34120754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono } 34220754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono throw new IOException("Not found the device: " + Integer.toString(deviceId)); 343e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 34450d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono } 34550d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono 346e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono /** 347fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono * Resumes root scanner to handle the update of device list. 348fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono */ 349fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono void resumeRootScanner() { 350fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mRootScanner.resume(); 351fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } 352fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono 353fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono /** 354e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono * Finalize the content provider for unit tests. 355e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono */ 356e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono @Override 357e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono public void shutdown() { 358e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 359e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono try { 360e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono for (final int id : mMtpManager.getOpenedDeviceIds()) { 361e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono closeDeviceInternal(id); 362e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 363e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } catch (InterruptedException|IOException e) { 364e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono // It should fail unit tests by throwing runtime exception. 365e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono throw new RuntimeException(e); 366e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } finally { 367e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mDatabase.close(); 368b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono mAppFuse.close(); 369e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono super.shutdown(); 370e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 371e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 372e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 373e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono 3745fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono private void notifyChildDocumentsChange(String parentDocumentId) { 3755fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono mResolver.notifyChange( 3765fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono DocumentsContract.buildChildDocumentsUri(AUTHORITY, parentDocumentId), 3775fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono null, 3785fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono false); 3795fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono } 3804c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 381e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono /** 382be38848969c91ba9bc3ec8eee31017a34905acfcDaichi Hirono * Clears MTP identifier in the database. 383e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono */ 384e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono private void resume() { 385e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 386e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mDatabase.getMapper().clearMapping(); 387e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 388e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 389e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono 390e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono private void closeDeviceInternal(int deviceId) throws IOException, InterruptedException { 391e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono // TODO: Flush the device before closing (if not closed externally). 392fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono if (!mDeviceToolkits.containsKey(deviceId)) { 393fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono return; 394fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } 39519aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono if (DEBUG) { 39619aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono Log.d(TAG, "Close device " + deviceId); 39719aa93249edc5dac01025456ce3bb1881f1b11d1Daichi Hirono } 398e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono getDeviceToolkit(deviceId).mDocumentLoader.clearTasks(); 399e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mDeviceToolkits.remove(deviceId); 400e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mMtpManager.closeDevice(deviceId); 401a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono if (getOpenedDeviceIds().length == 0) { 402e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mRootScanner.pause(); 403e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 404e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 405e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono 4064c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException { 407e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 408e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId); 409e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono if (toolkit == null) { 410e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono throw new FileNotFoundException(); 411e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 412e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono return toolkit; 4134c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4144c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4154c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 4164c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private PipeManager getPipeManager(Identifier identifier) throws FileNotFoundException { 4174c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski return getDeviceToolkit(identifier.mDeviceId).mPipeManager; 4184c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4194c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 4204c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private DocumentLoader getDocumentLoader(Identifier identifier) throws FileNotFoundException { 4214c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski return getDeviceToolkit(identifier.mDeviceId).mDocumentLoader; 4224c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4234c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 424f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono private long getFileSize(String documentId) throws FileNotFoundException { 425f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono final Cursor cursor = mDatabase.queryDocument( 426f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono documentId, 427f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono MtpDatabase.strings(Document.COLUMN_SIZE, Document.COLUMN_DISPLAY_NAME)); 428f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono try { 429f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono if (cursor.moveToNext()) { 430f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono return cursor.getLong(0); 431f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } else { 432f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono throw new FileNotFoundException(); 433f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 434f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } finally { 435f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono cursor.close(); 436f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 437f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 438f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono 4394c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private static class DeviceToolkit { 4404c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski public final PipeManager mPipeManager; 4414c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski public final DocumentLoader mDocumentLoader; 4424c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 44347eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono public DeviceToolkit(MtpManager manager, ContentResolver resolver, MtpDatabase database) { 4444c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski mPipeManager = new PipeManager(); 44547eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono mDocumentLoader = new DocumentLoader(manager, resolver, database); 4464c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4474c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 448f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono 449f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono private class AppFuseCallback implements AppFuse.Callback { 450f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono @Override 4512f310f6d5d352817f42384394b50a660ad6e0bf8Daichi Hirono public long readObjectBytes( 4522f310f6d5d352817f42384394b50a660ad6e0bf8Daichi Hirono int inode, long offset, long size, byte[] buffer) throws IOException { 453f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode)); 4542f310f6d5d352817f42384394b50a660ad6e0bf8Daichi Hirono return mMtpManager.getPartialObject( 4552f310f6d5d352817f42384394b50a660ad6e0bf8Daichi Hirono identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer); 456f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 457f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono 458f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono @Override 459f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono public long getFileSize(int inode) throws FileNotFoundException { 460f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono return MtpDocumentsProvider.this.getFileSize(String.valueOf(inode)); 461f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 462f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 463c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono} 464