MtpDocumentsProvider.java revision fda7474c5faae1e36a9274d8a5fe83e42ec6503b
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 61e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono private final Object mDeviceListLock = new Object(); 62e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono 632efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono private static MtpDocumentsProvider sSingleton; 642efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono 652efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono private MtpManager mMtpManager; 66d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono private ContentResolver mResolver; 67e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono @GuardedBy("mDeviceListLock") 684c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private Map<Integer, DeviceToolkit> mDeviceToolkits; 698b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono private RootScanner mRootScanner; 7017c8d8bcdae4b31d295431fc089db81bf3721c54Daichi Hirono private Resources mResources; 71dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono private MtpDatabase mDatabase; 72f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono private AppFuse mAppFuse; 73fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono private ServiceIntentSender mIntentSender; 74d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 752efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono /** 762efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono * Provides singleton instance to MtpDocumentsService. 772efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono */ 782efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono static MtpDocumentsProvider getInstance() { 792efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono return sSingleton; 802efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono } 812efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono 82c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 83c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono public boolean onCreate() { 842efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono sSingleton = this; 8517c8d8bcdae4b31d295431fc089db81bf3721c54Daichi Hirono mResources = getContext().getResources(); 862efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono mMtpManager = new MtpManager(getContext()); 87d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono mResolver = getContext().getContentResolver(); 884c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); 8947eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE); 90dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); 91f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono mAppFuse = new AppFuse(TAG, new AppFuseCallback()); 92fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mIntentSender = new ServiceIntentSender(getContext()); 93f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono // TODO: Mount AppFuse on demands. 94e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono try { 95e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono mAppFuse.mount(getContext().getSystemService(StorageManager.class)); 96e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono } catch (IOException e) { 97e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono Log.e(TAG, "Failed to start app fuse.", e); 98e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono return false; 99e6054c0ff07005c73bd10a362f039933dc00f8bdDaichi Hirono } 100e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono resume(); 101c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono return true; 102c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono } 103c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 104d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono @VisibleForTesting 105b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono boolean onCreateForTesting( 106dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono Resources resources, 107dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono MtpManager mtpManager, 108dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono ContentResolver resolver, 109b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono MtpDatabase database, 110fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono StorageManager storageManager, 111fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono ServiceIntentSender intentSender) { 11217c8d8bcdae4b31d295431fc089db81bf3721c54Daichi Hirono mResources = resources; 1136baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono mMtpManager = mtpManager; 1146baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono mResolver = resolver; 1154c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski mDeviceToolkits = new HashMap<Integer, DeviceToolkit>(); 116dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono mDatabase = database; 117dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono mRootScanner = new RootScanner(mResolver, mResources, mMtpManager, mDatabase); 118b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono mAppFuse = new AppFuse(TAG, new AppFuseCallback()); 119fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mIntentSender = intentSender; 120b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono // TODO: Mount AppFuse on demands. 121b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono try { 122b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono mAppFuse.mount(storageManager); 123b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono } catch (IOException e) { 124b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono Log.e(TAG, "Failed to start app fuse.", e); 125b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono return false; 126b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono } 127e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono resume(); 128b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono return true; 129d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono } 130d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 131c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 132c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono public Cursor queryRoots(String[] projection) throws FileNotFoundException { 13350d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono if (projection == null) { 13450d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono projection = MtpDocumentsProvider.DEFAULT_ROOT_PROJECTION; 13550d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono } 136dc47344660035b27a564ab6d9c9a9b58ec340347Daichi Hirono final Cursor cursor = mDatabase.queryRoots(projection); 13750d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono cursor.setNotificationUri( 13850d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono mResolver, DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY)); 13950d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono return cursor; 140c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono } 141c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 142c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 143c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono public Cursor queryDocument(String documentId, String[] projection) 144c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono throws FileNotFoundException { 145e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono if (projection == null) { 146e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION; 147e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono } 1489e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono return mDatabase.queryDocument(documentId, projection); 149c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono } 150c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 151c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 152124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono public Cursor queryChildDocuments(String parentDocumentId, 153124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono String[] projection, String sortOrder) throws FileNotFoundException { 154124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono if (projection == null) { 155124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION; 156124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono } 1576a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Identifier parentIdentifier = mDatabase.createIdentifier(parentDocumentId); 158124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono try { 159fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(parentIdentifier.mDeviceId); 1606a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_DEVICE) { 1616a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono final Identifier singleStorageIdentifier = 1626a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono mDatabase.getSingleStorageIdentifier(parentDocumentId); 1636a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono if (singleStorageIdentifier == null) { 1646a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono // Returns storage list from database. 1656a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono return mDatabase.queryChildDocuments(projection, parentDocumentId); 1666a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono } 1676a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono parentIdentifier = singleStorageIdentifier; 1686a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono } 1696a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono // Returns object list from document loader. 1704c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski return getDocumentLoader(parentIdentifier).queryChildDocuments( 1714c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski projection, parentIdentifier); 172124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono } catch (IOException exception) { 1736a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(MtpDocumentsProvider.TAG, "queryChildDocuments", exception); 174124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono throw new FileNotFoundException(exception.getMessage()); 175124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono } 176c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono } 177c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono 178c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono @Override 1798ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono public ParcelFileDescriptor openDocument( 1808ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono String documentId, String mode, CancellationSignal signal) 1818ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono throws FileNotFoundException { 1829e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final Identifier identifier = mDatabase.createIdentifier(documentId); 1838ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono try { 184fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(identifier.mDeviceId); 185b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski switch (mode) { 186b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski case "r": 187f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono final long fileSize = getFileSize(documentId); 188fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono // MTP getPartialObject operation does not support files that are larger than 189fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono // 4GB. Fallback to non-seekable file descriptor. 190f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono // TODO: Use getPartialObject64 for MTP devices that support Android vendor 191f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono // extension. 192b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono if (fileSize <= 0xffffffffl) { 193f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono return mAppFuse.openFile(Integer.parseInt(documentId)); 194f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } else { 195f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono return getPipeManager(identifier).readDocument(mMtpManager, identifier); 196f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 197b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski case "w": 19881d74743107b372424fb8f7439357bdd608f8cafTomasz Mikolajewski // TODO: Clear the parent document loader task (if exists) and call notify 19981d74743107b372424fb8f7439357bdd608f8cafTomasz Mikolajewski // when writing is completed. 2004c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski return getPipeManager(identifier).writeDocument( 2014c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski getContext(), mMtpManager, identifier); 202f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono case "rw": 203f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono // TODO: Add support for "rw" mode. 204b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski throw new UnsupportedOperationException( 205f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono "The provider does not support 'rw' mode."); 206f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono default: 207f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono throw new IllegalArgumentException("Unknown mode for openDocument: " + mode); 208b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski } 2098ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono } catch (IOException error) { 2106a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(MtpDocumentsProvider.TAG, "openDocument", error); 2118ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono throw new FileNotFoundException(error.getMessage()); 2128ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono } 213d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono } 214d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 2153faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono @Override 2163faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono public AssetFileDescriptor openDocumentThumbnail( 2173faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono String documentId, 2183faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono Point sizeHint, 2193faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono CancellationSignal signal) throws FileNotFoundException { 2209e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final Identifier identifier = mDatabase.createIdentifier(documentId); 2213faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono try { 222fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(identifier.mDeviceId); 2233faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono return new AssetFileDescriptor( 2244c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski getPipeManager(identifier).readThumbnail(mMtpManager, identifier), 225573c1fbc5f98f033681e378ec965136bce49c899Daichi Hirono 0, // Start offset. 2263faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono AssetFileDescriptor.UNKNOWN_LENGTH); 2273faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } catch (IOException error) { 2286a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(MtpDocumentsProvider.TAG, "openDocumentThumbnail", error); 2293faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono throw new FileNotFoundException(error.getMessage()); 2303faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } 2313faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } 2323faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono 2333faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono @Override 2343faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono public void deleteDocument(String documentId) throws FileNotFoundException { 2353faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono try { 2369e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final Identifier identifier = mDatabase.createIdentifier(documentId); 237fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(identifier.mDeviceId); 2386a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono final Identifier parentIdentifier = mDatabase.getParentIdentifier(documentId); 2393faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle); 2409e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono mDatabase.deleteDocument(documentId); 2414c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski getDocumentLoader(parentIdentifier).clearTask(parentIdentifier); 2429e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono notifyChildDocumentsChange(parentIdentifier.mDocumentId); 2436a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono if (parentIdentifier.mDocumentType == MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE) { 2446a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono // If the parent is storage, the object might be appeared as child of device because 2456a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono // we skip storage when the device has only one storage. 2466a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono final Identifier deviceIdentifier = mDatabase.getParentIdentifier( 2476a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono parentIdentifier.mDocumentId); 2486a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono notifyChildDocumentsChange(deviceIdentifier.mDocumentId); 2496a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono } 2503faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } catch (IOException error) { 2516a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(MtpDocumentsProvider.TAG, "deleteDocument", error); 2523faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono throw new FileNotFoundException(error.getMessage()); 2533faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } 2543faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono } 2553faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono 2566baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono @Override 2576baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono public void onTrimMemory(int level) { 258e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 259e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono for (final DeviceToolkit toolkit : mDeviceToolkits.values()) { 260e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono toolkit.mDocumentLoader.clearCompletedTasks(); 261e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 262e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 2636baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono } 2646baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono 26587763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski @Override 26687763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski public String createDocument(String parentDocumentId, String mimeType, String displayName) 26787763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski throws FileNotFoundException { 26887763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski try { 2699e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final Identifier parentId = mDatabase.createIdentifier(parentDocumentId); 270fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono openDevice(parentId.mDeviceId); 271df544176b10f536969de1ed143b0ba57123fcb93Tomasz Mikolajewski final ParcelFileDescriptor pipe[] = ParcelFileDescriptor.createReliablePipe(); 272df544176b10f536969de1ed143b0ba57123fcb93Tomasz Mikolajewski pipe[0].close(); // 0 bytes for a new document. 2739e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final int formatCode = Document.MIME_TYPE_DIR.equals(mimeType) ? 2749e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono MtpConstants.FORMAT_ASSOCIATION : 2759e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono MediaFile.getFormatCode(displayName, mimeType); 2769e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final MtpObjectInfo info = new MtpObjectInfo.Builder() 2779e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .setStorageId(parentId.mStorageId) 2789e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .setParent(parentId.mObjectHandle) 2799e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .setFormat(formatCode) 2809e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .setName(displayName) 2819e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono .build(); 2829e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final int objectHandle = mMtpManager.createDocument(parentId.mDeviceId, info, pipe[1]); 2839e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final MtpObjectInfo infoWithHandle = 2849e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono new MtpObjectInfo.Builder(info).setObjectHandle(objectHandle).build(); 2859e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono final String documentId = mDatabase.putNewDocument( 2869e8a4fa78f5b9e3964dca84ad4047210d35c4013Daichi Hirono parentId.mDeviceId, parentDocumentId, infoWithHandle); 2874c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski getDocumentLoader(parentId).clearTask(parentId); 28887763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski notifyChildDocumentsChange(parentDocumentId); 28987763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski return documentId; 29087763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski } catch (IOException error) { 2916a5ea7eae8a70bced97ceef051c965c27cb642caDaichi Hirono Log.e(TAG, "createDocument", error); 29287763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski throw new FileNotFoundException(error.getMessage()); 29387763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski } 29487763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski } 29587763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski 2962efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono void openDevice(int deviceId) throws IOException { 297e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 298fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono if (mDeviceToolkits.containsKey(deviceId)) { 299fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono return; 300fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } 301e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono mMtpManager.openDevice(deviceId); 302e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mDeviceToolkits.put( 303e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono deviceId, new DeviceToolkit(mMtpManager, mResolver, mDatabase)); 304fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mIntentSender.sendUpdateNotificationIntent(); 305fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono try { 306fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mRootScanner.resume().await(); 307fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } catch (InterruptedException error) { 308fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono Log.e(TAG, "openDevice", error); 309fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } 310e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 311d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono } 312d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 313e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono void closeDevice(int deviceId) throws IOException, InterruptedException { 314e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 315e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono closeDeviceInternal(deviceId); 316e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 31720754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono mRootScanner.resume(); 318fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mIntentSender.sendUpdateNotificationIntent(); 319d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono } 320d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono 321a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono int[] getOpenedDeviceIds() { 322e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 323a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono return mMtpManager.getOpenedDeviceIds(); 324a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono } 325a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono } 326a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono 327a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono String getDeviceName(int deviceId) throws IOException { 328a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono synchronized (mDeviceListLock) { 32920754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono for (final MtpDeviceRecord device : mMtpManager.getDevices()) { 33020754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono if (device.deviceId == deviceId) { 33120754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono return device.name; 33220754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono } 33320754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono } 33420754c5a112e418c11cc88176283db2c4bf2efd6Daichi Hirono throw new IOException("Not found the device: " + Integer.toString(deviceId)); 335e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 33650d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono } 33750d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono 338e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono /** 339fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono * Resumes root scanner to handle the update of device list. 340fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono */ 341fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono void resumeRootScanner() { 342fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono mRootScanner.resume(); 343fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } 344fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono 345fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono /** 346e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono * Finalize the content provider for unit tests. 347e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono */ 348e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono @Override 349e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono public void shutdown() { 350e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 351e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono try { 352e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono for (final int id : mMtpManager.getOpenedDeviceIds()) { 353e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono closeDeviceInternal(id); 354e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 355e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } catch (InterruptedException|IOException e) { 356e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono // It should fail unit tests by throwing runtime exception. 357e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono throw new RuntimeException(e); 358e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } finally { 359e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mDatabase.close(); 360b36b15586a5d3d0de590773ce4392fa3a82af66aDaichi Hirono mAppFuse.close(); 361e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono super.shutdown(); 362e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 363e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 364e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 365e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono 3665fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono private void notifyChildDocumentsChange(String parentDocumentId) { 3675fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono mResolver.notifyChange( 3685fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono DocumentsContract.buildChildDocumentsUri(AUTHORITY, parentDocumentId), 3695fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono null, 3705fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono false); 3715fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono } 3724c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 373e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono /** 374be38848969c91ba9bc3ec8eee31017a34905acfcDaichi Hirono * Clears MTP identifier in the database. 375e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono */ 376e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono private void resume() { 377e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 378e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mDatabase.getMapper().clearMapping(); 379e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 380e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 381e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono 382e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono private void closeDeviceInternal(int deviceId) throws IOException, InterruptedException { 383e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono // TODO: Flush the device before closing (if not closed externally). 384fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono if (!mDeviceToolkits.containsKey(deviceId)) { 385fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono return; 386fda7474c5faae1e36a9274d8a5fe83e42ec6503bDaichi Hirono } 387e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono getDeviceToolkit(deviceId).mDocumentLoader.clearTasks(); 388e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mDeviceToolkits.remove(deviceId); 389e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mMtpManager.closeDevice(deviceId); 390a57d9ed09003acd8b2beb0494a2bd32f7030cc11Daichi Hirono if (getOpenedDeviceIds().length == 0) { 391e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono mRootScanner.pause(); 392e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 393e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono } 394e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono 3954c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private DeviceToolkit getDeviceToolkit(int deviceId) throws FileNotFoundException { 396e0282dd7d409dce7738173162e766ce9317ef804Daichi Hirono synchronized (mDeviceListLock) { 397e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono final DeviceToolkit toolkit = mDeviceToolkits.get(deviceId); 398e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono if (toolkit == null) { 399e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono throw new FileNotFoundException(); 400e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono } 401e1d57710fb38dae2747aadca0e5b6f98a84a0514Daichi Hirono return toolkit; 4024c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4034c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4044c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 4054c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private PipeManager getPipeManager(Identifier identifier) throws FileNotFoundException { 4064c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski return getDeviceToolkit(identifier.mDeviceId).mPipeManager; 4074c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4084c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 4094c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private DocumentLoader getDocumentLoader(Identifier identifier) throws FileNotFoundException { 4104c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski return getDeviceToolkit(identifier.mDeviceId).mDocumentLoader; 4114c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4124c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 413f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono private long getFileSize(String documentId) throws FileNotFoundException { 414f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono final Cursor cursor = mDatabase.queryDocument( 415f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono documentId, 416f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono MtpDatabase.strings(Document.COLUMN_SIZE, Document.COLUMN_DISPLAY_NAME)); 417f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono try { 418f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono if (cursor.moveToNext()) { 419f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono return cursor.getLong(0); 420f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } else { 421f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono throw new FileNotFoundException(); 422f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 423f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } finally { 424f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono cursor.close(); 425f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 426f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 427f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono 4284c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski private static class DeviceToolkit { 4294c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski public final PipeManager mPipeManager; 4304c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski public final DocumentLoader mDocumentLoader; 4314c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski 43247eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono public DeviceToolkit(MtpManager manager, ContentResolver resolver, MtpDatabase database) { 4334c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski mPipeManager = new PipeManager(); 43447eb192b2704e27272ca94a95680cac40b6bff3fDaichi Hirono mDocumentLoader = new DocumentLoader(manager, resolver, database); 4354c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 4364c1d3dde05308cb10187269dd9824c9bfdbb27deTomasz Mikolajewski } 437f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono 438f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono private class AppFuseCallback implements AppFuse.Callback { 439f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono @Override 4402f310f6d5d352817f42384394b50a660ad6e0bf8Daichi Hirono public long readObjectBytes( 4412f310f6d5d352817f42384394b50a660ad6e0bf8Daichi Hirono int inode, long offset, long size, byte[] buffer) throws IOException { 442f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono final Identifier identifier = mDatabase.createIdentifier(Integer.toString(inode)); 4432f310f6d5d352817f42384394b50a660ad6e0bf8Daichi Hirono return mMtpManager.getPartialObject( 4442f310f6d5d352817f42384394b50a660ad6e0bf8Daichi Hirono identifier.mDeviceId, identifier.mObjectHandle, offset, size, buffer); 445f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 446f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono 447f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono @Override 448f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono public long getFileSize(int inode) throws FileNotFoundException { 449f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono return MtpDocumentsProvider.this.getFileSize(String.valueOf(inode)); 450f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 451f52ef008c76566f7118a80bf28f599ba48d7c578Daichi Hirono } 452c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono} 453