1 /* 2 * Copyright (C) 2018 Google Inc. 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.compatibility.common.util; 18 19 import static android.app.AppOpsManager.MODE_ALLOWED; 20 import static android.app.AppOpsManager.MODE_DEFAULT; 21 import static android.app.AppOpsManager.MODE_ERRORED; 22 import static android.app.AppOpsManager.MODE_IGNORED; 23 24 import android.app.AppOpsManager; 25 26 import androidx.test.InstrumentationRegistry; 27 28 import java.io.IOException; 29 30 /** 31 * Utilities for controlling App Ops settings, and testing whether ops are logged. 32 */ 33 public class AppOpsUtils { 34 35 /** 36 * Resets a package's app ops configuration to the device default. See AppOpsManager for the 37 * default op settings. 38 * 39 * <p> 40 * It's recommended to call this in setUp() and tearDown() of your test so the test starts and 41 * ends with a reproducible default state, and so doesn't affect other tests. 42 * 43 * <p> 44 * Some app ops are configured to be non-resettable, which means that the state of these will 45 * not be reset even when calling this method. 46 */ reset(String packageName)47 public static String reset(String packageName) throws IOException { 48 return runCommand("appops reset " + packageName); 49 } 50 51 /** 52 * Sets the app op mode (e.g. allowed, denied) for a single package and operation. 53 */ setOpMode(String packageName, String opStr, int mode)54 public static String setOpMode(String packageName, String opStr, int mode) 55 throws IOException { 56 String modeStr; 57 switch (mode) { 58 case MODE_ALLOWED: 59 modeStr = "allow"; 60 break; 61 case MODE_ERRORED: 62 modeStr = "deny"; 63 break; 64 case MODE_IGNORED: 65 modeStr = "ignore"; 66 break; 67 case MODE_DEFAULT: 68 modeStr = "default"; 69 break; 70 default: 71 throw new IllegalArgumentException("Unexpected app op type"); 72 } 73 String command = "appops set " + packageName + " " + opStr + " " + modeStr; 74 return runCommand(command); 75 } 76 77 /** 78 * Get the app op mode (e.g. MODE_ALLOWED, MODE_DEFAULT) for a single package and operation. 79 */ getOpMode(String packageName, String opStr)80 public static int getOpMode(String packageName, String opStr) 81 throws IOException { 82 String opState = getOpState(packageName, opStr); 83 if (opState.contains(" allow")) { 84 return MODE_ALLOWED; 85 } else if (opState.contains(" deny")) { 86 return MODE_ERRORED; 87 } else if (opState.contains(" ignore")) { 88 return MODE_IGNORED; 89 } else if (opState.contains(" default")) { 90 return MODE_DEFAULT; 91 } else { 92 throw new IllegalStateException("Unexpected app op mode returned " + opState); 93 } 94 } 95 96 /** 97 * Returns whether an allowed operation has been logged by the AppOpsManager for a 98 * package. Operations are noted when the app attempts to perform them and calls e.g. 99 * {@link AppOpsManager#noteOperation}. 100 * 101 * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS). 102 */ allowedOperationLogged(String packageName, String opStr)103 public static boolean allowedOperationLogged(String packageName, String opStr) 104 throws IOException { 105 return getOpState(packageName, opStr).contains(" time="); 106 } 107 108 /** 109 * Returns whether a rejected operation has been logged by the AppOpsManager for a 110 * package. Operations are noted when the app attempts to perform them and calls e.g. 111 * {@link AppOpsManager#noteOperation}. 112 * 113 * @param opStr The public string constant of the operation (e.g. OPSTR_READ_SMS). 114 */ rejectedOperationLogged(String packageName, String opStr)115 public static boolean rejectedOperationLogged(String packageName, String opStr) 116 throws IOException { 117 return getOpState(packageName, opStr).contains(" rejectTime="); 118 } 119 120 /** 121 * Returns the app op state for a package. Includes information on when the operation was last 122 * attempted to be performed by the package. 123 * 124 * Format: "SEND_SMS: allow; time=+23h12m54s980ms ago; rejectTime=+1h10m23s180ms" 125 */ getOpState(String packageName, String opStr)126 private static String getOpState(String packageName, String opStr) throws IOException { 127 return runCommand("appops get " + packageName + " " + opStr); 128 } 129 runCommand(String command)130 private static String runCommand(String command) throws IOException { 131 return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command); 132 } 133 } 134