MtpDocumentsProvider.java revision b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2
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;
21c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.database.Cursor;
2250d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hironoimport android.database.MatrixCursor;
233faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hironoimport android.graphics.Point;
24c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.os.CancellationSignal;
25c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.os.ParcelFileDescriptor;
26d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport android.provider.DocumentsContract;
27c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.provider.DocumentsContract.Document;
28c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.provider.DocumentsContract.Root;
29c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.provider.DocumentsProvider;
30d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport android.util.Log;
31d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
32d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport com.android.internal.annotations.VisibleForTesting;
33d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
34c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport java.io.FileNotFoundException;
35d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport java.io.IOException;
36c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
37d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono/**
38d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono * DocumentsProvider for MTP devices.
39d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono */
40c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironopublic class MtpDocumentsProvider extends DocumentsProvider {
412efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    static final String AUTHORITY = "com.android.mtp.documents";
422efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    static final String TAG = "MtpDocumentsProvider";
436baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono    static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
44c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
45c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
46c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Root.COLUMN_AVAILABLE_BYTES,
47c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    };
486baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono    static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
49c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
50c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
51c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
52c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    };
53c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
542efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    private static MtpDocumentsProvider sSingleton;
552efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono
562efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    private MtpManager mMtpManager;
57d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    private ContentResolver mResolver;
588ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono    private PipeManager mPipeManager;
596baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono    private DocumentLoader mDocumentLoader;
608b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono    private RootScanner mRootScanner;
61d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
622efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    /**
632efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono     * Provides singleton instance to MtpDocumentsService.
642efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono     */
652efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    static MtpDocumentsProvider getInstance() {
662efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        return sSingleton;
672efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    }
682efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono
69c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
70c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    public boolean onCreate() {
712efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        sSingleton = this;
722efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        mMtpManager = new MtpManager(getContext());
73d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        mResolver = getContext().getContentResolver();
748ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        mPipeManager = new PipeManager();
756baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono        mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
768b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono        mRootScanner = new RootScanner(mResolver, mMtpManager);
77c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono        return true;
78c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    }
79c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
80d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    @VisibleForTesting
812efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    void onCreateForTesting(MtpManager mtpManager, ContentResolver resolver) {
826baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono        mMtpManager = mtpManager;
836baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono        mResolver = resolver;
846baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono        mDocumentLoader = new DocumentLoader(mMtpManager, mResolver);
858b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono        mRootScanner = new RootScanner(mResolver, mMtpManager);
86d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
87d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
88c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
89c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
9050d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        if (projection == null) {
9150d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono            projection = MtpDocumentsProvider.DEFAULT_ROOT_PROJECTION;
9250d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        }
9350d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        final MatrixCursor cursor = new MatrixCursor(projection);
948b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono        final MtpRoot[] roots = mRootScanner.getRoots();
958b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono        for (final MtpRoot root : roots) {
968b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono            final Identifier rootIdentifier = new Identifier(root.mDeviceId, root.mStorageId);
978b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono            final MatrixCursor.RowBuilder builder = cursor.newRow();
988b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono            builder.add(Root.COLUMN_ROOT_ID, rootIdentifier.toRootId());
9987763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski            builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD | Root.FLAG_SUPPORTS_CREATE);
1008b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono            builder.add(Root.COLUMN_TITLE, root.mDescription);
1018b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono            builder.add(
1028b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono                    Root.COLUMN_DOCUMENT_ID,
1038b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono                    rootIdentifier.toDocumentId());
1048b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono            builder.add(Root.COLUMN_AVAILABLE_BYTES , root.mFreeSpace);
10550d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        }
10650d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        cursor.setNotificationUri(
10750d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                mResolver, DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY));
10850d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        return cursor;
109c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    }
110c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
111c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
112c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    public Cursor queryDocument(String documentId, String[] projection)
113c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            throws FileNotFoundException {
114e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        if (projection == null) {
115e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION;
116e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        }
117e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        final Identifier identifier = Identifier.createFromDocumentId(documentId);
118e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono
119e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        MtpDocument document = null;
120e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        if (identifier.mObjectHandle != MtpDocument.DUMMY_HANDLE_FOR_ROOT) {
121e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            try {
122e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                document = mMtpManager.getDocument(identifier.mDeviceId, identifier.mObjectHandle);
123e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            } catch (IOException e) {
124e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                throw new FileNotFoundException(e.getMessage());
125e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            }
126e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        } else {
127e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            MtpRoot[] roots;
128e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            try {
129e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                roots = mMtpManager.getRoots(identifier.mDeviceId);
130e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                if (roots != null) {
131e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                    for (final MtpRoot root : roots) {
132e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                        if (identifier.mStorageId == root.mStorageId) {
133e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                            document = new MtpDocument(root);
134e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                            break;
135e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                        }
136e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                    }
137e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                }
138e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                if (document == null) {
139e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                    throw new FileNotFoundException();
140e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                }
141e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            } catch (IOException e) {
142e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                throw new FileNotFoundException(e.getMessage());
143e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            }
144e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        }
145e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono
146e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        final MatrixCursor cursor = new MatrixCursor(projection);
147124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono        document.addToCursor(
148124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono                new Identifier(identifier.mDeviceId, identifier.mStorageId), cursor.newRow());
149e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        return cursor;
150c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    }
151c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
152c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
153124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono    public Cursor queryChildDocuments(String parentDocumentId,
154124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono            String[] projection, String sortOrder) throws FileNotFoundException {
155124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono        if (projection == null) {
156124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono            projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION;
157124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono        }
158124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono        final Identifier parentIdentifier = Identifier.createFromDocumentId(parentDocumentId);
159124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono        try {
1606baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono            return mDocumentLoader.queryChildDocuments(projection, parentIdentifier);
161124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono        } catch (IOException exception) {
162124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono            throw new FileNotFoundException(exception.getMessage());
163124d060bc980c7555616ff9d07a4dc3b8f3cd341Daichi Hirono        }
164c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    }
165c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
166c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
1678ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono    public ParcelFileDescriptor openDocument(
1688ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono            String documentId, String mode, CancellationSignal signal)
1698ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono                    throws FileNotFoundException {
1708ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        final Identifier identifier = Identifier.createFromDocumentId(documentId);
1718ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        try {
172b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski            switch (mode) {
173b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski                case "r":
174b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski                    return mPipeManager.readDocument(mMtpManager, identifier);
175b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski                case "w":
176b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski                    return mPipeManager.writeDocument(getContext(), mMtpManager, identifier);
177b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski                default:
178b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski                    // TODO: Add support for seekable files.
179b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski                    throw new UnsupportedOperationException(
180b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski                            "The provider does not support seekable file.");
181b80a3cfd05fc7492dd59b7f8d4337eb5e29088c2Tomasz Mikolajewski            }
1828ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        } catch (IOException error) {
1838ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono            throw new FileNotFoundException(error.getMessage());
1848ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        }
185d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
186d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
1873faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono    @Override
1883faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono    public AssetFileDescriptor openDocumentThumbnail(
1893faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            String documentId,
1903faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            Point sizeHint,
1913faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            CancellationSignal signal) throws FileNotFoundException {
1923faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono        final Identifier identifier = Identifier.createFromDocumentId(documentId);
1933faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono        try {
1943faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            return new AssetFileDescriptor(
1953faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono                    mPipeManager.readThumbnail(mMtpManager, identifier),
196573c1fbc5f98f033681e378ec965136bce49c899Daichi Hirono                    0,  // Start offset.
1973faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono                    AssetFileDescriptor.UNKNOWN_LENGTH);
1983faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono        } catch (IOException error) {
1993faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            throw new FileNotFoundException(error.getMessage());
2003faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono        }
2013faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono    }
2023faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono
2033faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono    @Override
2043faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono    public void deleteDocument(String documentId) throws FileNotFoundException {
2053faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono        try {
2063faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            final Identifier identifier = Identifier.createFromDocumentId(documentId);
2073faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            final int parentHandle =
2083faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono                    mMtpManager.getParent(identifier.mDeviceId, identifier.mObjectHandle);
2093faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            mMtpManager.deleteDocument(identifier.mDeviceId, identifier.mObjectHandle);
2103faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            notifyChildDocumentsChange(new Identifier(
2113faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono                    identifier.mDeviceId, identifier.mStorageId, parentHandle).toDocumentId());
2123faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono        } catch (IOException error) {
2133faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono            throw new FileNotFoundException(error.getMessage());
2143faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono        }
2153faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono    }
2163faa43a4a6f270e2e1e2ec55b77508084af16757Daichi Hirono
2176baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono    @Override
2186baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono    public void onTrimMemory(int level) {
2196baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono        mDocumentLoader.clearCache();
2206baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono    }
2216baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono
22287763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski    @Override
22387763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski    public String createDocument(String parentDocumentId, String mimeType, String displayName)
22487763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski            throws FileNotFoundException {
22587763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski        try {
22687763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski            final Identifier parentId = Identifier.createFromDocumentId(parentDocumentId);
22787763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski            final int objectHandle = mMtpManager.createDocument(
22887763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski                    parentId.mDeviceId, parentId.mStorageId, parentId.mObjectHandle, mimeType,
22987763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski                    displayName);
23087763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski            final String documentId =  new Identifier(parentId.mDeviceId, parentId.mStorageId,
23187763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski                   objectHandle).toDocumentId();
23287763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski            notifyChildDocumentsChange(parentDocumentId);
23387763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski            return documentId;
23487763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski        } catch (IOException error) {
23587763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski            Log.e(TAG, error.getMessage());
23687763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski            throw new FileNotFoundException(error.getMessage());
23787763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski        }
23887763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski    }
23987763e6a91a54e7995cfda9b7e80162f02ac4cbcTomasz Mikolajewski
2402efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    void openDevice(int deviceId) throws IOException {
2412efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        mMtpManager.openDevice(deviceId);
2428b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono        mRootScanner.scanNow();
243d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
244d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
2452efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    void closeDevice(int deviceId) throws IOException {
2462efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        mMtpManager.closeDevice(deviceId);
2476baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono        mDocumentLoader.clearCache(deviceId);
2488b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono        mRootScanner.scanNow();
249d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
250d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
251d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    void closeAllDevices() {
252d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        boolean closed = false;
2532efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
254d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono            try {
2552efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono                mMtpManager.closeDevice(deviceId);
2566baa16e9109046661fef8dcc25b8754ac68bcdaeDaichi Hirono                mDocumentLoader.clearCache(deviceId);
257d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono                closed = true;
258d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono            } catch (IOException d) {
259d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono                Log.d(TAG, "Failed to close the MTP device: " + deviceId);
260d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono            }
261d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        }
262d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        if (closed) {
2638b9024f0c20b1b79df1f2d0bc2f1a82f726b1176Daichi Hirono            mRootScanner.scanNow();
264d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        }
265d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
266d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
26750d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono    boolean hasOpenedDevices() {
26850d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        return mMtpManager.getOpenedDeviceIds().length != 0;
26950d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono    }
27050d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono
2715fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono    private void notifyChildDocumentsChange(String parentDocumentId) {
2725fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono        mResolver.notifyChange(
2735fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono                DocumentsContract.buildChildDocumentsUri(AUTHORITY, parentDocumentId),
2745fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono                null,
2755fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono                false);
2765fecc6cf032bbbc2616dd2342a50656bf2857832Daichi Hirono    }
277c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono}
278