1dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto/*
2dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto * Copyright (C) 2015 The Android Open Source Project
3dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto *
4dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto * Licensed under the Apache License, Version 2.0 (the "License");
5dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto * you may not use this file except in compliance with the License.
6dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto * You may obtain a copy of the License at
7dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto *
8dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto *      http://www.apache.org/licenses/LICENSE-2.0
9dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto *
10dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto * Unless required by applicable law or agreed to in writing, software
11dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto * distributed under the License is distributed on an "AS IS" BASIS,
12dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto * See the License for the specific language governing permissions and
14dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto * limitations under the License.
15dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto */
16dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
17dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotopackage com.android.shell;
18dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
19dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport android.database.Cursor;
20dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport android.database.MatrixCursor;
21dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport android.database.MatrixCursor.RowBuilder;
22c6905cfb1133627dfd500491c60b6528a3e593e0Ben Linimport android.net.Uri;
23dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport android.os.CancellationSignal;
24dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport android.os.FileUtils;
25dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport android.os.ParcelFileDescriptor;
26c6905cfb1133627dfd500491c60b6528a3e593e0Ben Linimport android.provider.DocumentsContract;
27dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport android.provider.DocumentsContract.Document;
28dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport android.provider.DocumentsContract.Root;
29d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin
30d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Linimport com.android.internal.content.FileSystemProvider;
31dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
32dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport java.io.File;
33dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimotoimport java.io.FileNotFoundException;
34dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
35d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Linpublic class BugreportStorageProvider extends FileSystemProvider {
36c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin    private static final String AUTHORITY = "com.android.shell.documents";
37dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    private static final String DOC_ID_ROOT = "bugreport";
38dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
39d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    private static final String[] DEFAULT_ROOT_PROJECTION = new String[]{
40dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto            Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE,
41dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto            Root.COLUMN_DOCUMENT_ID,
42dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    };
43dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
44d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[]{
45dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto            Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME,
46dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto            Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,
47dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    };
48dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
49dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    private File mRoot;
50dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
51dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    @Override
52dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    public boolean onCreate() {
53d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        super.onCreate(DEFAULT_DOCUMENT_PROJECTION);
54dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        mRoot = new File(getContext().getFilesDir(), "bugreports");
55dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        return true;
56dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    }
57dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
58dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    @Override
59dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    public Cursor queryRoots(String[] projection) throws FileNotFoundException {
60dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
61dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        final RowBuilder row = result.newRow();
62dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        row.add(Root.COLUMN_ROOT_ID, DOC_ID_ROOT);
63774cc932fc29705c9580775abf7c1e7a80c2bbe3Aga Wronska        row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED);
64dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        row.add(Root.COLUMN_ICON, android.R.mipmap.sym_def_app_icon);
65dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        row.add(Root.COLUMN_TITLE, getContext().getString(R.string.bugreport_storage_title));
66dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        row.add(Root.COLUMN_DOCUMENT_ID, DOC_ID_ROOT);
67dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        return result;
68dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    }
69dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
70dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    @Override
71dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    public Cursor queryDocument(String documentId, String[] projection)
72dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto            throws FileNotFoundException {
73dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        if (DOC_ID_ROOT.equals(documentId)) {
74d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
75d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            includeDefaultDocument(result);
76d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            return result;
77dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        } else {
78d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            return super.queryDocument(documentId, projection);
79dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        }
80dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    }
81dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
82dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    @Override
83dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    public ParcelFileDescriptor openDocument(
84dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto            String documentId, String mode, CancellationSignal signal)
85dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto            throws FileNotFoundException {
86dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        if (ParcelFileDescriptor.parseMode(mode) != ParcelFileDescriptor.MODE_READ_ONLY) {
87dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto            throw new FileNotFoundException("Failed to open: " + documentId + ", mode = " + mode);
88dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        }
89dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        return ParcelFileDescriptor.open(getFileForDocId(documentId),
90dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto                ParcelFileDescriptor.MODE_READ_ONLY);
91dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    }
92dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
93dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    @Override
94d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    protected Uri buildNotificationUri(String docId) {
95d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        return DocumentsContract.buildChildDocumentsUri(AUTHORITY, docId);
96c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin    }
97c6905cfb1133627dfd500491c60b6528a3e593e0Ben Lin
98dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    private static String[] resolveRootProjection(String[] projection) {
99dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
100dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    }
101dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
102dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    private static String[] resolveDocumentProjection(String[] projection) {
103dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
104dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    }
105dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
106d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    @Override
107d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    protected String getDocIdForFile(File file) {
108dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        return DOC_ID_ROOT + ":" + file.getName();
109dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    }
110dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
111d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    @Override
112d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    protected File getFileForDocId(String documentId, boolean visible)
113d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            throws FileNotFoundException {
114d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        if (DOC_ID_ROOT.equals(documentId)) {
115d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            return mRoot;
116d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        } else {
117d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            final int splitIndex = documentId.indexOf(':', 1);
118d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            final String name = documentId.substring(splitIndex + 1);
119d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            if (splitIndex == -1 || !DOC_ID_ROOT.equals(documentId.substring(0, splitIndex)) ||
120d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin                    !FileUtils.isValidExtFilename(name)) {
121d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin                throw new FileNotFoundException("Invalid document ID: " + documentId);
122d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            }
123d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            final File file = new File(mRoot, name);
124d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            if (!file.exists()) {
125d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin                throw new FileNotFoundException("File not found: " + documentId);
126d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            }
127d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            return file;
128dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        }
129dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    }
130dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto
131d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    @Override
132d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    protected RowBuilder includeFile(MatrixCursor result, String docId, File file)
133d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin            throws FileNotFoundException {
134d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        RowBuilder row = super.includeFile(result, docId, file);
135d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_DELETE);
136d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        return row;
137d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    }
138d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin
139d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin    private void includeDefaultDocument(MatrixCursor result) {
140dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto        final RowBuilder row = result.newRow();
141d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        row.add(Document.COLUMN_DOCUMENT_ID, DOC_ID_ROOT);
142d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR);
143d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        row.add(Document.COLUMN_DISPLAY_NAME, mRoot.getName());
144d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        row.add(Document.COLUMN_LAST_MODIFIED, mRoot.lastModified());
145d8235e2647a1d7fbe3c1717cb235cf2518478cbdBen Lin        row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_PREFERS_LAST_MODIFIED);
146dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto    }
147dd98034edce7ebbfefadf3f8a351c1b7ee7c2a73Ryo Hashimoto}
148