DumpUtils.java revision fe9a53bc45fd0124a876dc0a49680aaf86641d3e
1/*
2 * Copyright (C) 2012 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.internal.util;
18
19import android.app.AppOpsManager;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.os.Binder;
23import android.os.Handler;
24import android.util.Slog;
25
26import java.io.PrintWriter;
27import java.io.StringWriter;
28
29/**
30 * Helper functions for dumping the state of system services.
31 */
32public final class DumpUtils {
33    private static final String TAG = "DumpUtils";
34    private static final boolean DEBUG = true;
35
36    private DumpUtils() {
37    }
38
39    /**
40     * Helper for dumping state owned by a handler thread.
41     *
42     * Because the caller might be holding an important lock that the handler is
43     * trying to acquire, we use a short timeout to avoid deadlocks.  The process
44     * is inelegant but this function is only used for debugging purposes.
45     */
46    public static void dumpAsync(Handler handler, final Dump dump, PrintWriter pw,
47            final String prefix, long timeout) {
48        final StringWriter sw = new StringWriter();
49        if (handler.runWithScissors(new Runnable() {
50            @Override
51            public void run() {
52                PrintWriter lpw = new FastPrintWriter(sw);
53                dump.dump(lpw, prefix);
54                lpw.close();
55            }
56        }, timeout)) {
57            pw.print(sw.toString());
58        } else {
59            pw.println("... timed out");
60        }
61    }
62
63    public interface Dump {
64        void dump(PrintWriter pw, String prefix);
65    }
66
67    private static void logMessage(PrintWriter pw, String msg) {
68        if (DEBUG) Slog.v(TAG, msg);
69        pw.println(msg);
70    }
71
72    /**
73     * Verify that caller holds {@link android.Manifest.permission#DUMP}.
74     *
75     * @return true if access should be granted.
76     * @hide
77     */
78    public static boolean checkDumpPermission(Context context, String tag, PrintWriter pw) {
79        if (context.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
80                != PackageManager.PERMISSION_GRANTED) {
81            logMessage(pw, "Permission Denial: can't dump " + tag + " from from pid="
82                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
83                    + " due to missing android.permission.DUMP permission");
84            return false;
85        } else {
86            return true;
87        }
88    }
89
90    /**
91     * Verify that caller holds
92     * {@link android.Manifest.permission#PACKAGE_USAGE_STATS} and that they
93     * have {@link AppOpsManager#OP_GET_USAGE_STATS} access.
94     *
95     * @return true if access should be granted.
96     * @hide
97     */
98    public static boolean checkUsageStatsPermission(Context context, String tag, PrintWriter pw) {
99        // System internals always get access
100        final int uid = Binder.getCallingUid();
101        switch (uid) {
102            case android.os.Process.ROOT_UID:
103            case android.os.Process.SYSTEM_UID:
104            case android.os.Process.SHELL_UID:
105                return true;
106        }
107
108        // Caller always needs to hold permission
109        if (context.checkCallingOrSelfPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
110                != PackageManager.PERMISSION_GRANTED) {
111            logMessage(pw, "Permission Denial: can't dump " + tag + " from from pid="
112                    + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
113                    + " due to missing android.permission.PACKAGE_USAGE_STATS permission");
114            return false;
115        }
116
117        // And finally, caller needs to have appops access; this is totally
118        // hacky, but it's the easiest way to wire this up without retrofitting
119        // Binder.dump() to pass through package names.
120        final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
121        final String[] pkgs = context.getPackageManager().getPackagesForUid(uid);
122        if (pkgs != null) {
123            for (String pkg : pkgs) {
124                if (appOps.checkOpNoThrow(AppOpsManager.OP_GET_USAGE_STATS, uid,
125                        pkg) == AppOpsManager.MODE_ALLOWED) {
126                    appOps.noteOp(AppOpsManager.OP_GET_USAGE_STATS, uid, pkg);
127                    if (DEBUG) Slog.v(TAG, "Found package " + pkg + " with "
128                                + "android:get_usage_stats access");
129                    return true;
130                }
131            }
132        }
133
134        logMessage(pw, "Permission Denial: can't dump " + tag + " from from pid="
135                + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
136                + " due to android:get_usage_stats app-op not allowed");
137        return false;
138    }
139
140    /**
141     * Verify that caller holds both {@link android.Manifest.permission#DUMP}
142     * and {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, and that
143     * they have {@link AppOpsManager#OP_GET_USAGE_STATS} access.
144     *
145     * @return true if access should be granted.
146     * @hide
147     */
148    public static boolean checkDumpAndUsageStatsPermission(Context context, String tag,
149            PrintWriter pw) {
150        return checkDumpPermission(context, tag, pw) && checkUsageStatsPermission(context, tag, pw);
151    }
152}
153