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 17 package com.android.internal.util; 18 19 import android.app.AppOpsManager; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.os.Binder; 23 import android.os.Handler; 24 import android.util.Slog; 25 26 import java.io.PrintWriter; 27 import java.io.StringWriter; 28 29 /** 30 * Helper functions for dumping the state of system services. 31 */ 32 public final class DumpUtils { 33 private static final String TAG = "DumpUtils"; 34 private static final boolean DEBUG = false; 35 DumpUtils()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 */ dumpAsync(Handler handler, final Dump dump, PrintWriter pw, final String prefix, long timeout)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 { dump(PrintWriter pw, String prefix)64 void dump(PrintWriter pw, String prefix); 65 } 66 logMessage(PrintWriter pw, String msg)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 */ checkDumpPermission(Context context, String tag, PrintWriter pw)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 */ checkUsageStatsPermission(Context context, String tag, PrintWriter pw)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 switch (appOps.checkOpNoThrow(AppOpsManager.OP_GET_USAGE_STATS, uid, pkg)) { 125 case AppOpsManager.MODE_ALLOWED: 126 if (DEBUG) Slog.v(TAG, "Found package " + pkg + " with " 127 + "android:get_usage_stats allowed"); 128 return true; 129 case AppOpsManager.MODE_DEFAULT: 130 if (DEBUG) Slog.v(TAG, "Found package " + pkg + " with " 131 + "android:get_usage_stats default"); 132 return true; 133 } 134 } 135 } 136 137 logMessage(pw, "Permission Denial: can't dump " + tag + " from from pid=" 138 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 139 + " due to android:get_usage_stats app-op not allowed"); 140 return false; 141 } 142 143 /** 144 * Verify that caller holds both {@link android.Manifest.permission#DUMP} 145 * and {@link android.Manifest.permission#PACKAGE_USAGE_STATS}, and that 146 * they have {@link AppOpsManager#OP_GET_USAGE_STATS} access. 147 * 148 * @return true if access should be granted. 149 * @hide 150 */ checkDumpAndUsageStatsPermission(Context context, String tag, PrintWriter pw)151 public static boolean checkDumpAndUsageStatsPermission(Context context, String tag, 152 PrintWriter pw) { 153 return checkDumpPermission(context, tag, pw) && checkUsageStatsPermission(context, tag, pw); 154 } 155 } 156