1 /*
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 
17 package com.android.crashreportprovider;
18 
19 import android.content.ContentProvider;
20 import android.content.ContentValues;
21 import android.content.Intent;
22 import android.database.Cursor;
23 import android.database.MatrixCursor;
24 import android.net.Uri;
25 import android.os.FileObserver;
26 import android.os.FileUtils;
27 import android.os.ParcelFileDescriptor;
28 import android.os.UserHandle;
29 
30 import java.io.File;
31 import java.io.FileNotFoundException;
32 import java.util.List;
33 
34 public class CrashReportProvider extends ContentProvider {
35     private static final File sCrashReportDirectory = new File("/data/system/crash_reports/");
36     private static FileObserver sFileObserver;
37 
38     public static final String ACTION_CRASH_REPORT_ADDED =
39             "com.android.crashreportprovider.action.CRASH_REPORT_ADDED";
40 
41     public static final Uri URI = Uri.parse("content://com.android.crashreportprovider/");
42 
43     public final String COLUMN_NAME = "name";
44     private final String[] DEFAULT_PROJECTION = { COLUMN_NAME };
45 
46     @Override
onCreate()47     public boolean onCreate() {
48         if (sFileObserver == null) {
49             sFileObserver = new FileObserver(sCrashReportDirectory.getPath(),
50                     FileObserver.CLOSE_WRITE) {
51                     @Override
52                     public void onEvent(int event, String path) {
53                         if (path.endsWith(".meta")) {
54                             final Intent intent = new Intent(ACTION_CRASH_REPORT_ADDED);
55                             getContext().sendBroadcastAsUser(intent, UserHandle.OWNER,
56                                     android.Manifest.permission.READ_LOGS);
57                         }
58                     }
59                 };
60             sFileObserver.startWatching();
61         }
62         return true;
63     }
64 
65     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)66     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
67             String sortOrder) {
68         final MatrixCursor cursor =
69                 new MatrixCursor(projection == null ? DEFAULT_PROJECTION : projection);
70         final File[] files = sCrashReportDirectory.listFiles();
71         if (files != null) {
72             for (File file : files) {
73                 final MatrixCursor.RowBuilder row = cursor.newRow();
74                 row.add(COLUMN_NAME, file.getName());
75             }
76         }
77         return cursor;
78     }
79 
80     @Override
openFile(Uri uri, String mode)81     public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
82         if (ParcelFileDescriptor.parseMode(mode) != ParcelFileDescriptor.MODE_READ_ONLY) {
83             throw new FileNotFoundException("Cannot to open: " + uri + ", mode = " + mode);
84         }
85         List<String> segments = uri.getPathSegments();
86         if (segments.size() != 1 || !FileUtils.isValidExtFilename(segments.get(0))) {
87             throw new FileNotFoundException("Invalid path.");
88         }
89         return ParcelFileDescriptor.open(new File(sCrashReportDirectory, segments.get(0)),
90                 ParcelFileDescriptor.MODE_READ_ONLY);
91     }
92 
93     @Override
getType(Uri uri)94     public String getType(Uri uri) {
95         return null;
96     }
97 
98     @Override
delete(Uri uri, String selection, String[] selectionArgs)99     public int delete(Uri uri, String selection, String[] selectionArgs) {
100         throw new UnsupportedOperationException("Cannot delete: " + uri);
101     }
102 
103     @Override
insert(Uri uri, ContentValues values)104     public Uri insert(Uri uri, ContentValues values) {
105         throw new UnsupportedOperationException("Cannot insert: " + uri);
106     }
107 
108     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)109     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
110         throw new UnsupportedOperationException("Cannot update: " + uri);
111     }
112 }
113