1 /*
2  * Copyright (C) 2019 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.shell;
18 
19 import android.annotation.NonNull;
20 import android.content.ContentProvider;
21 import android.content.ContentValues;
22 import android.database.Cursor;
23 import android.net.Uri;
24 import android.os.Binder;
25 import android.os.ParcelFileDescriptor;
26 import android.os.Process;
27 
28 import java.io.File;
29 import java.io.FileNotFoundException;
30 
31 /** ContentProvider to write and access heap dumps. */
32 public class HeapDumpProvider extends ContentProvider {
33     private static final String FILENAME_SUFFIX = "_javaheap.bin";
34     private static final Object sLock = new Object();
35 
36     private File mRoot;
37 
38     @Override
onCreate()39     public boolean onCreate() {
40         synchronized (sLock) {
41             mRoot = new File(getContext().createCredentialProtectedStorageContext().getFilesDir(),
42                     "heapdumps");
43             return mRoot.mkdir();
44         }
45     }
46 
47     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)48     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
49             String sortOrder) {
50         return null;
51     }
52 
53     @Override
getType(Uri uri)54     public String getType(Uri uri) {
55         return "application/octet-stream";
56     }
57 
58     @Override
insert(Uri uri, ContentValues values)59     public Uri insert(Uri uri, ContentValues values) {
60         throw new UnsupportedOperationException("Insert not allowed.");
61     }
62 
63     @Override
delete(Uri uri, String selection, String[] selectionArgs)64     public int delete(Uri uri, String selection, String[] selectionArgs) {
65         String path = sanitizePath(uri.getEncodedPath());
66         String tag = Uri.decode(path);
67         return (new File(mRoot, tag)).delete() ? 1 : 0;
68     }
69 
70     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)71     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
72         throw new UnsupportedOperationException("Update not allowed.");
73     }
74 
75     @Override
openFile(Uri uri, String mode)76     public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
77         String path = sanitizePath(uri.getEncodedPath());
78         String tag = Uri.decode(path);
79         final int pMode;
80         if (Binder.getCallingUid() == Process.SYSTEM_UID) {
81             pMode = ParcelFileDescriptor.MODE_CREATE
82                     | ParcelFileDescriptor.MODE_TRUNCATE
83                     | ParcelFileDescriptor.MODE_WRITE_ONLY;
84         } else {
85             pMode = ParcelFileDescriptor.MODE_READ_ONLY;
86         }
87 
88         synchronized (sLock) {
89             return ParcelFileDescriptor.open(new File(mRoot, tag), pMode);
90         }
91     }
92 
93     @NonNull
makeUri(@onNull String procName)94     static Uri makeUri(@NonNull String procName) {
95         return Uri.parse("content://com.android.shell.heapdump/" + procName + FILENAME_SUFFIX);
96     }
97 
sanitizePath(String path)98     private String sanitizePath(String path) {
99         return path.replaceAll("[^a-zA-Z0-9_.]", "");
100     }
101 }
102