MtpDocumentsProvider.java revision 8ba419119d50a031160cab54bef6899bd0051ea9
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;
20c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.database.Cursor;
2150d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hironoimport android.database.MatrixCursor;
22c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.os.CancellationSignal;
23c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.os.ParcelFileDescriptor;
24d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport android.provider.DocumentsContract;
25c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.provider.DocumentsContract.Document;
26c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.provider.DocumentsContract.Root;
27c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport android.provider.DocumentsProvider;
28d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport android.util.Log;
29d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
30d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport com.android.internal.annotations.VisibleForTesting;
31d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
32c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironoimport java.io.FileNotFoundException;
33d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hironoimport java.io.IOException;
34c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
35d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono/**
36d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono * DocumentsProvider for MTP devices.
37d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono */
38c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hironopublic class MtpDocumentsProvider extends DocumentsProvider {
392efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    static final String AUTHORITY = "com.android.mtp.documents";
402efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    static final String TAG = "MtpDocumentsProvider";
41c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
42c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON,
43c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
44c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Root.COLUMN_AVAILABLE_BYTES,
45c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    };
46c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
47c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE,
48c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED,
49c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
50c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    };
51c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
522efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    private static MtpDocumentsProvider sSingleton;
532efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono
542efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    private MtpManager mMtpManager;
55d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    private ContentResolver mResolver;
568ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono    private PipeManager mPipeManager;
57d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
582efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    /**
592efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono     * Provides singleton instance to MtpDocumentsService.
602efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono     */
612efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    static MtpDocumentsProvider getInstance() {
622efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        return sSingleton;
632efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    }
642efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono
65c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
66c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    public boolean onCreate() {
672efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        sSingleton = this;
682efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        mMtpManager = new MtpManager(getContext());
69d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        mResolver = getContext().getContentResolver();
708ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        mPipeManager = new PipeManager();
718ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono
72c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono        return true;
73c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    }
74c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
75d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    @VisibleForTesting
762efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    void onCreateForTesting(MtpManager mtpManager, ContentResolver resolver) {
772efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        this.mMtpManager = mtpManager;
78d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        this.mResolver = resolver;
79d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
80d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
81c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
82c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
8350d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        if (projection == null) {
8450d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono            projection = MtpDocumentsProvider.DEFAULT_ROOT_PROJECTION;
8550d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        }
8650d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        final MatrixCursor cursor = new MatrixCursor(projection);
8750d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        for (final int deviceId : mMtpManager.getOpenedDeviceIds()) {
8850d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono            try {
8950d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                final MtpRoot[] roots = mMtpManager.getRoots(deviceId);
9050d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                // TODO: Add retry logic here.
9150d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono
9250d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                for (final MtpRoot root : roots) {
93e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                    final Identifier rootIdentifier = new Identifier(deviceId, root.mStorageId);
9450d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                    final MatrixCursor.RowBuilder builder = cursor.newRow();
95e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                    builder.add(Root.COLUMN_ROOT_ID, rootIdentifier.toRootId());
9650d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                    builder.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_IS_CHILD);
9750d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                    builder.add(Root.COLUMN_TITLE, root.mDescription);
9850d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                    builder.add(
9950d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                            Root.COLUMN_DOCUMENT_ID,
100e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                            rootIdentifier.toDocumentId());
10150d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                    builder.add(Root.COLUMN_AVAILABLE_BYTES , root.mFreeSpace);
10250d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                }
10350d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono            } catch (IOException error) {
10450d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                Log.d(TAG, error.getMessage());
10550d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono            }
10650d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        }
10750d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        cursor.setNotificationUri(
10850d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono                mResolver, DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY));
10950d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        return cursor;
110c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    }
111c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
112c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
113c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    public Cursor queryDocument(String documentId, String[] projection)
114c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono            throws FileNotFoundException {
115e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        if (projection == null) {
116e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            projection = MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION;
117e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        }
118e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        final Identifier identifier = Identifier.createFromDocumentId(documentId);
119e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono
120e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        MtpDocument document = null;
121e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        if (identifier.mObjectHandle != MtpDocument.DUMMY_HANDLE_FOR_ROOT) {
122e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            try {
123e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                document = mMtpManager.getDocument(identifier.mDeviceId, identifier.mObjectHandle);
124e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            } catch (IOException e) {
125e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                throw new FileNotFoundException(e.getMessage());
126e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            }
127e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        } else {
128e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            MtpRoot[] roots;
129e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            try {
130e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                roots = mMtpManager.getRoots(identifier.mDeviceId);
131e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                if (roots != null) {
132e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                    for (final MtpRoot root : roots) {
133e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                        if (identifier.mStorageId == root.mStorageId) {
134e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                            document = new MtpDocument(root);
135e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                            break;
136e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                        }
137e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                    }
138e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                }
139e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                if (document == null) {
140e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                    throw new FileNotFoundException();
141e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                }
142e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            } catch (IOException e) {
143e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                throw new FileNotFoundException(e.getMessage());
144e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            }
145e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        }
146e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono
147e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        final MatrixCursor cursor = new MatrixCursor(projection);
148e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        cursor.addRow(document.getRow(
149e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                new Identifier(identifier.mDeviceId, identifier.mStorageId),
150e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                projection));
151e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono        return cursor;
152c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    }
153c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
154c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
155e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono    public Cursor queryChildDocuments(
156e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono            String parentDocumentId, String[] projection, String sortOrder)
157e5323b7493f2bc1537d7e6b2d4595d69fd01d72eDaichi Hirono                    throws FileNotFoundException {
158d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        throw new FileNotFoundException();
159c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    }
160c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono
161c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    @Override
1628ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono    public ParcelFileDescriptor openDocument(
1638ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono            String documentId, String mode, CancellationSignal signal)
1648ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono                    throws FileNotFoundException {
1658ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        if (!"r".equals(mode) && !"w".equals(mode)) {
1668ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono            // TODO: Support seekable file.
1678ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono            throw new UnsupportedOperationException("The provider does not support seekable file.");
1688ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        }
1698ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        final Identifier identifier = Identifier.createFromDocumentId(documentId);
1708ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        try {
1718ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono            final MtpDocument document =
1728ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono                    mMtpManager.getDocument(identifier.mDeviceId, identifier.mObjectHandle);
1738ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono            return mPipeManager.readDocument(mMtpManager, identifier, document.getSize());
1748ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        } catch (IOException error) {
1758ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono            throw new FileNotFoundException(error.getMessage());
1768ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono        }
177d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
178d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
1792efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    void openDevice(int deviceId) throws IOException {
1802efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        mMtpManager.openDevice(deviceId);
18150d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        notifyRootsChange();
182d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
183d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
1842efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono    void closeDevice(int deviceId) throws IOException {
1852efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        mMtpManager.closeDevice(deviceId);
18650d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        notifyRootsChange();
187d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
188d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
189d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    void closeAllDevices() {
190d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        boolean closed = false;
1912efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono        for (int deviceId : mMtpManager.getOpenedDeviceIds()) {
192d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono            try {
1932efe4cac49efd87c5f14f48051a5556f437cedc9Daichi Hirono                mMtpManager.closeDevice(deviceId);
194d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono                closed = true;
195d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono            } catch (IOException d) {
196d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono                Log.d(TAG, "Failed to close the MTP device: " + deviceId);
197d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono            }
198d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        }
199d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        if (closed) {
20050d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono            notifyRootsChange();
201d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        }
202d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono    }
203d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono
20450d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono    boolean hasOpenedDevices() {
20550d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono        return mMtpManager.getOpenedDeviceIds().length != 0;
20650d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono    }
20750d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono
20850d17aa871d9ca645a8e7af64df8866b85aee245Daichi Hirono    private void notifyRootsChange() {
209d5152429e11d3a831a7993dc67ddf69f69bd3912Daichi Hirono        mResolver.notifyChange(
2108ba419119d50a031160cab54bef6899bd0051ea9Daichi Hirono                DocumentsContract.buildRootsUri(MtpDocumentsProvider.AUTHORITY), null, false);
211c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono    }
212c00d5d4d82620beba271e63875b93ad9cc39523fDaichi Hirono}
213