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