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