• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.traceur;
17 
18 import android.annotation.SuppressLint;
19 import android.database.Cursor;
20 import android.database.MatrixCursor;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.os.FileUtils;
24 import android.os.CancellationSignal;
25 import android.os.ParcelFileDescriptor;
26 import android.provider.DocumentsContract;
27 import android.provider.DocumentsContract.Document;
28 import android.provider.DocumentsContract.Root;
29 import android.provider.DocumentsProvider;
30 import android.provider.Settings;
31 import android.util.Log;
32 import android.webkit.MimeTypeMap;
33 
34 import com.android.internal.content.FileSystemProvider;
35 
36 import java.io.File;
37 import java.io.FileNotFoundException;
38 
39 /**
40  * Adds an entry for traces in the file picker.
41  */
42 public class StorageProvider extends FileSystemProvider{
43 
44     public static final String TAG = StorageProvider.class.getName();
45     public static final String AUTHORITY = "com.android.traceur.documents";
46 
47     private static final String DOC_ID_ROOT = "traces";
48     private static final String ROOT_DIR = "/data/local/traces";
49     private static final String MIME_TYPE = "application/vnd.android.systrace";
50 
51     private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
52             Root.COLUMN_ROOT_ID,
53             Root.COLUMN_ICON,
54             Root.COLUMN_TITLE,
55             Root.COLUMN_FLAGS,
56             Root.COLUMN_DOCUMENT_ID,
57     };
58 
59     private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
60             Document.COLUMN_DOCUMENT_ID,
61             Document.COLUMN_DISPLAY_NAME,
62             Document.COLUMN_MIME_TYPE,
63             Document.COLUMN_FLAGS,
64             Document.COLUMN_SIZE,
65             Document.COLUMN_LAST_MODIFIED,
66     };
67 
68     @Override
onCreate()69     public boolean onCreate() {
70         super.onCreate(DEFAULT_DOCUMENT_PROJECTION);
71         return true;
72     }
73 
74     @Override
queryRoots(String[] projection)75     public Cursor queryRoots(String[] projection) throws FileNotFoundException {
76         final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
77 
78         boolean developerOptionsIsEnabled =
79             Settings.Global.getInt(getContext().getContentResolver(),
80                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
81 
82         // If developer options is not enabled, return an empty root cursor.
83         // This removes the provider from the list entirely.
84         if (!developerOptionsIsEnabled) {
85             return null;
86         }
87 
88         final MatrixCursor.RowBuilder row = result.newRow();
89         row.add(Root.COLUMN_ROOT_ID, DOC_ID_ROOT);
90         row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY);
91         row.add(Root.COLUMN_MIME_TYPES, MIME_TYPE);
92         row.add(Root.COLUMN_ICON, R.drawable.stat_sys_adb_green);
93         row.add(Root.COLUMN_TITLE,
94             getContext().getString(R.string.system_traces_storage_title));
95         row.add(Root.COLUMN_DOCUMENT_ID, DOC_ID_ROOT);
96         return result;
97     }
98 
99     @Override
queryDocument(String documentId, String[] projection)100     public Cursor queryDocument(String documentId, String[] projection)
101             throws FileNotFoundException {
102         final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection));
103         final MatrixCursor.RowBuilder row = result.newRow();
104         File file;
105         String mimeType;
106 
107         if (DOC_ID_ROOT.equals(documentId)) {
108             file = new File(ROOT_DIR);
109             mimeType = Document.MIME_TYPE_DIR;
110         } else {
111             file = getFileForDocId(documentId);
112             mimeType = MIME_TYPE;
113         }
114 
115         row.add(Document.COLUMN_DOCUMENT_ID, documentId);
116         row.add(Document.COLUMN_MIME_TYPE, mimeType);
117         row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
118         row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
119         row.add(Document.COLUMN_SIZE, file.length());
120         row.add(Document.COLUMN_FLAGS, Document.FLAG_DIR_PREFERS_LAST_MODIFIED | Document.FLAG_SUPPORTS_DELETE);
121         return result;
122     }
123 
124     @Override
queryChildDocuments( String parentDocumentId, String[] projection, String sortOrder)125     public Cursor queryChildDocuments(
126             String parentDocumentId, String[] projection, String sortOrder)
127             throws FileNotFoundException {
128         Cursor result = super.queryChildDocuments(parentDocumentId, projection, sortOrder);
129 
130         Bundle bundle = new Bundle();
131         bundle.putString(DocumentsContract.EXTRA_INFO,
132             getContext().getResources().getString(R.string.system_trace_sensitive_data));
133         result.setExtras(bundle);
134 
135         return result;
136     }
137 
138 
139     @Override
openDocument( String documentId, String mode, CancellationSignal signal)140     public ParcelFileDescriptor openDocument(
141             String documentId, String mode, CancellationSignal signal)
142             throws FileNotFoundException, UnsupportedOperationException {
143         if (ParcelFileDescriptor.parseMode(mode) != ParcelFileDescriptor.MODE_READ_ONLY) {
144             throw new UnsupportedOperationException(
145                 "Attempt to open read-only file " + documentId + " in mode " + mode);
146         }
147         return ParcelFileDescriptor.open(getFileForDocId(documentId),
148                 ParcelFileDescriptor.MODE_READ_ONLY);
149     }
150 
resolveRootProjection(String[] projection)151     private static String[] resolveRootProjection(String[] projection) {
152         return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
153     }
154 
resolveDocumentProjection(String[] projection)155     private static String[] resolveDocumentProjection(String[] projection) {
156         return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
157     }
158 
159     @Override
buildNotificationUri(String docId)160     protected Uri buildNotificationUri(String docId) {
161         return DocumentsContract.buildChildDocumentsUri(AUTHORITY, docId);
162     }
163 
164     @Override
getDocIdForFile(File file)165     protected String getDocIdForFile(File file) {
166         return DOC_ID_ROOT + ":" + file.getName();
167     }
168 
169     @Override
getFileForDocId(String documentId, boolean visible)170     protected File getFileForDocId(String documentId, boolean visible)
171             throws FileNotFoundException {
172         if (DOC_ID_ROOT.equals(documentId)) {
173             return new File(ROOT_DIR);
174         } else {
175             final int splitIndex = documentId.indexOf(':', 1);
176             final String name = documentId.substring(splitIndex + 1);
177             if (splitIndex == -1 || !DOC_ID_ROOT.equals(documentId.substring(0, splitIndex)) ||
178                     !FileUtils.isValidExtFilename(name)) {
179                 throw new FileNotFoundException("Invalid document ID: " + documentId);
180             }
181             final File file = new File(ROOT_DIR, name);
182             if (!file.exists()) {
183                 throw new FileNotFoundException("File not found: " + documentId);
184             }
185             return file;
186         }
187     }
188 
189 }
190