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 package android.app.cts; 17 18 import static android.Manifest.permission.ACCESS_COARSE_LOCATION; 19 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; 20 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; 21 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; 22 import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; 23 import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; 24 import static android.app.AppOpsManager.MODE_ALLOWED; 25 import static android.app.AppOpsManager.MODE_FOREGROUND; 26 import static android.app.AppOpsManager.MODE_IGNORED; 27 import static android.app.AppOpsManager.OPSTR_CAMERA; 28 import static android.app.AppOpsManager.OPSTR_COARSE_LOCATION; 29 import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; 30 import static android.app.AppOpsManager.OP_FLAGS_ALL; 31 import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE; 32 import static android.app.AppOpsManager.UID_STATE_TOP; 33 34 import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity; 35 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 36 37 import static org.junit.Assert.assertEquals; 38 39 import android.app.AppOpsManager; 40 import android.app.AppOpsManager.HistoricalOp; 41 import android.app.AppOpsManager.HistoricalOps; 42 import android.app.AppOpsManager.HistoricalOpsRequest; 43 import android.app.Instrumentation; 44 import android.app.cts.android.app.cts.tools.WaitForBroadcast; 45 import android.app.cts.android.app.cts.tools.WatchUidRunner; 46 import android.content.Context; 47 import android.content.Intent; 48 import android.content.pm.PackageManager.NameNotFoundException; 49 import android.permission.cts.PermissionUtils; 50 import android.provider.Settings; 51 52 import androidx.test.InstrumentationRegistry; 53 import androidx.test.runner.AndroidJUnit4; 54 import androidx.test.uiautomator.UiDevice; 55 56 import org.junit.After; 57 import org.junit.Before; 58 import org.junit.Ignore; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 62 import java.time.Instant; 63 import java.time.temporal.ChronoUnit; 64 import java.util.Arrays; 65 import java.util.concurrent.CompletableFuture; 66 import java.util.concurrent.TimeUnit; 67 68 /** 69 * AppOpsManager.MODE_FOREGROUND is introduced in API level 29. This test class specifically tests 70 * ActivityManagerService's interaction with AppOpsService regarding MODE_FOREGROUND operation. 71 * If an operation's mode is MODE_FOREGROUND, this operation is allowed only when the process is in 72 * one of the foreground state (including foreground_service state), this operation will be denied 73 * when the process is in background state. 74 */ 75 @RunWith(AndroidJUnit4.class) 76 public class ActivityManagerApi29Test { 77 private static final String PACKAGE_NAME = "android.app.cts.activitymanager.api29"; 78 private static final String SIMPLE_ACTIVITY = ".SimpleActivity"; 79 private static final String ACTION_SIMPLE_ACTIVITY_START_RESULT = 80 "android.app.cts.activitymanager.api29.SimpleActivity.RESULT"; 81 private static final String ACTION_SERVICE_START_RESULT = 82 "android.app.cts.activitymanager.api29.LocationForegroundService.RESULT"; 83 private static final String SERVICE_NAME = ".LocationForegroundService"; 84 private static final int WAITFOR_MSEC = 10000; 85 private static final int NOTEOP_COUNT = 5; 86 87 private static final int PROCESS_CAPABILITY_ALL = PROCESS_CAPABILITY_FOREGROUND_LOCATION 88 | PROCESS_CAPABILITY_FOREGROUND_CAMERA 89 | PROCESS_CAPABILITY_FOREGROUND_MICROPHONE 90 | PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; 91 92 private static final Instrumentation sInstrumentation = 93 InstrumentationRegistry.getInstrumentation(); 94 private static final Context sContext = sInstrumentation.getContext(); 95 private static final Context sTargetContext = sInstrumentation.getTargetContext(); 96 private static final AppOpsManager sAppOps = 97 (AppOpsManager) sContext.getSystemService(AppOpsManager.class); 98 private static Intent sActivityIntent; 99 private static Intent sServiceIntent = new Intent().setClassName( 100 PACKAGE_NAME, PACKAGE_NAME + SERVICE_NAME); 101 private static int sUid; 102 static { 103 try { 104 sUid = sContext.getPackageManager().getApplicationInfo(PACKAGE_NAME, 0).uid; 105 } catch (NameNotFoundException e) { 106 throw new RuntimeException("NameNotFoundException:" + e); 107 } 108 } 109 110 private String mOldAppOpsSettings; 111 private WatchUidRunner mUidWatcher; 112 113 @Before setUp()114 public void setUp() throws Exception { 115 CtsAppTestUtils.turnScreenOn(sInstrumentation, sContext); 116 CtsAppTestUtils.makeUidIdle(sInstrumentation, PACKAGE_NAME); 117 // PACKAGE_NAME's targetSdkVersion is 29, when ACCESS_COARSE_LOCATION is granted, appOp is 118 // MODE_FOREGROUND (In API level lower than 29, appOp is MODE_ALLOWED). 119 assertEquals(MODE_FOREGROUND, 120 PermissionUtils.getAppOp(PACKAGE_NAME, ACCESS_COARSE_LOCATION)); 121 runWithShellPermissionIdentity(()-> { 122 mOldAppOpsSettings = Settings.Global.getString(sContext.getContentResolver(), 123 Settings.Global.APP_OPS_CONSTANTS); 124 Settings.Global.putString(sContext.getContentResolver(), 125 Settings.Global.APP_OPS_CONSTANTS, 126 "top_state_settle_time=0,fg_service_state_settle_time=0," 127 + "bg_state_settle_time=0"); 128 sAppOps.clearHistory(); 129 sAppOps.resetHistoryParameters(); } 130 ); 131 mUidWatcher = new WatchUidRunner(sInstrumentation, sUid, WAITFOR_MSEC); 132 // Press home key to ensure stopAppSwitches is called so the grace period of 133 // the background start will be ignored if there's any. 134 UiDevice.getInstance(sInstrumentation).pressHome(); 135 } 136 137 @After tearDown()138 public void tearDown() throws Exception { 139 CtsAppTestUtils.makeUidIdle(sInstrumentation, PACKAGE_NAME); 140 runWithShellPermissionIdentity(() -> { 141 // restore old AppOps settings. 142 Settings.Global.putString(sContext.getContentResolver(), 143 Settings.Global.APP_OPS_CONSTANTS, mOldAppOpsSettings); 144 sAppOps.clearHistory(); 145 sAppOps.resetHistoryParameters(); } 146 ); 147 mUidWatcher.finish(); 148 } 149 startSimpleActivity()150 private void startSimpleActivity() { 151 sActivityIntent = new Intent(Intent.ACTION_MAIN) 152 .setClassName(PACKAGE_NAME, PACKAGE_NAME + SIMPLE_ACTIVITY) 153 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 154 sTargetContext.startActivity(sActivityIntent); 155 } 156 stopSimpleActivity()157 private void stopSimpleActivity() { 158 sActivityIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) 159 .putExtra("finish", true); 160 sTargetContext.startActivity(sActivityIntent); 161 } 162 startSimpleService()163 private void startSimpleService() { 164 sTargetContext.startForegroundService(sServiceIntent); 165 } 166 stopSimpleService()167 private void stopSimpleService() { 168 sTargetContext.stopService(sServiceIntent); 169 } 170 171 /** 172 * This tests app in PROCESS_STATE_TOP state can have location access. 173 * The app's permission is AppOpsManager.MODE_FOREGROUND. If the process is in PROCESS_STATE_TOP 174 * , even its capability is zero, it still has location access. 175 * @throws Exception 176 */ 177 @Test 178 @Ignore("because ag/13230961, FGS started in instrumentation are not subject to while-in-use " 179 + "restriction") testTopActivityWithAppOps()180 public void testTopActivityWithAppOps() throws Exception { 181 startSimpleActivity(); 182 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP, 183 new Integer(PROCESS_CAPABILITY_ALL)); 184 185 // AppOps location access should be allowed. 186 assertEquals(MODE_ALLOWED, noteOp(OPSTR_COARSE_LOCATION)); 187 188 stopSimpleActivity(); 189 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY, 190 new Integer(PROCESS_CAPABILITY_NONE)); 191 192 // AppOps location access should be denied. 193 assertEquals(MODE_IGNORED, noteOp(OPSTR_COARSE_LOCATION)); 194 } 195 196 /** 197 * When ActivityManagerService process states and capability changes, it updates AppOpsService. 198 * This test starts a foreground service with location type, it updates AppOpsService with 199 * PROCESS_STATE_FOREGROUND_SERVICE and PROCESS_CAPABILITY_FOREGROUND_LOCATION, then check if 200 * AppOpsManager allow ACCESS_COARSE_LOCATION of MODE_FOREGROUND. 201 * 202 * The "android.app.cts.activitymanager.api29" package's targetSdkVersion is 29. 203 * @throws Exception 204 */ 205 @Test 206 @Ignore("because ag/13230961, FGS started in instrumentation are not subject to while-in-use " 207 + "restriction") testFgsLocationWithAppOps()208 public void testFgsLocationWithAppOps() throws Exception { 209 // Start a foreground service with location 210 startSimpleService(); 211 // Wait for state and capability change. 212 // BG started FGS does not have location capability. 213 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE, 214 new Integer(PROCESS_CAPABILITY_NONE)); 215 216 startSimpleActivity(); 217 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP, 218 new Integer(PROCESS_CAPABILITY_ALL)); 219 220 // AppOps location access should be allowed. 221 assertEquals(MODE_ALLOWED, noteOp(OPSTR_COARSE_LOCATION)); 222 223 stopSimpleActivity(); 224 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE, 225 new Integer(PROCESS_CAPABILITY_NONE)); 226 227 // AppOps location access should be denied. 228 assertEquals(MODE_IGNORED, noteOp(OPSTR_COARSE_LOCATION)); 229 230 stopSimpleService(); 231 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY, 232 new Integer(PROCESS_CAPABILITY_NONE)); 233 234 // AppOps location access should be denied. 235 assertEquals(MODE_IGNORED, noteOp(OPSTR_COARSE_LOCATION)); 236 } 237 238 /** 239 * After calling AppOpsManager.noteOp() interface multiple times in different process states, 240 * this test calls AppOpsManager.getHistoricalOps() and check the access count and reject count 241 * in HistoricalOps. 242 * 243 * @throws Exception 244 */ 245 @Test 246 @Ignore("because ag/13230961, FGS started in instrumentation are not subject to while-in-use " 247 + "restriction") testAppOpsHistoricalOps()248 public void testAppOpsHistoricalOps() throws Exception { 249 runWithShellPermissionIdentity( 250 () -> sAppOps.setHistoryParameters(AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE, 251 1000, 10) 252 ); 253 WaitForBroadcast waiter = new WaitForBroadcast(sInstrumentation.getTargetContext()); 254 waiter.prepare(ACTION_SIMPLE_ACTIVITY_START_RESULT); 255 startSimpleActivity(); 256 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP, 257 new Integer(PROCESS_CAPABILITY_ALL)); 258 waiter.doWait(WAITFOR_MSEC); 259 260 waiter = new WaitForBroadcast(sInstrumentation.getTargetContext()); 261 waiter.prepare(ACTION_SERVICE_START_RESULT); 262 startSimpleService(); 263 waiter.doWait(WAITFOR_MSEC); 264 265 for (int i = 0; i < NOTEOP_COUNT; i++) { 266 noteOp(OPSTR_COARSE_LOCATION); 267 } 268 269 stopSimpleActivity(); 270 // The callingPackage to start FGS is in background. 271 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE, 272 new Integer(PROCESS_CAPABILITY_NONE)); 273 for (int i = 0; i < NOTEOP_COUNT; i++) { 274 noteOp(OPSTR_COARSE_LOCATION); 275 } 276 stopSimpleService(); 277 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY, 278 new Integer(PROCESS_CAPABILITY_NONE)); 279 280 for (int i = 0; i < NOTEOP_COUNT; i++) { 281 noteOp(OPSTR_COARSE_LOCATION); 282 } 283 runWithShellPermissionIdentity(() -> { 284 CompletableFuture<HistoricalOps> ops = new CompletableFuture<>(); 285 HistoricalOpsRequest histOpsRequest = new HistoricalOpsRequest.Builder( 286 Instant.now().minus(1, ChronoUnit.HOURS).toEpochMilli(), 287 Long.MAX_VALUE) 288 .setUid(sUid) 289 .setPackageName(PACKAGE_NAME) 290 .setOpNames(Arrays.asList(OPSTR_COARSE_LOCATION)) 291 .setFlags(OP_FLAGS_ALL) 292 .build(); 293 sAppOps.getHistoricalOps(histOpsRequest, sContext.getMainExecutor(), ops::complete); 294 HistoricalOp hOp = ops.get(5000, TimeUnit.MILLISECONDS) 295 .getUidOps(sUid).getPackageOps(PACKAGE_NAME) 296 .getOp(OPSTR_COARSE_LOCATION); 297 assertEquals(NOTEOP_COUNT, hOp.getAccessCount(UID_STATE_TOP, 298 UID_STATE_FOREGROUND_SERVICE, AppOpsManager.OP_FLAGS_ALL)); 299 assertEquals(NOTEOP_COUNT, hOp.getForegroundAccessCount(OP_FLAGS_ALL)); 300 assertEquals(NOTEOP_COUNT, hOp.getForegroundRejectCount(OP_FLAGS_ALL)); 301 assertEquals(0, hOp.getBackgroundAccessCount(OP_FLAGS_ALL)); 302 // denied access one time in background. 303 assertEquals(NOTEOP_COUNT, hOp.getBackgroundRejectCount(OP_FLAGS_ALL)); } 304 ); 305 } 306 307 /** 308 * Only FGS started by TOP app can have OP_CAMERA and OP_RECORD_AUDIO. 309 * @throws Exception 310 */ 311 @Test 312 @Ignore("because ag/13230961, FGS started in instrumentation are not subject to while-in-use " 313 + "restriction") testCameraWithAppOps()314 public void testCameraWithAppOps() throws Exception { 315 startSimpleService(); 316 // Wait for state and capability change. 317 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE, 318 new Integer(PROCESS_CAPABILITY_NONE)); 319 320 // Non-Top started FGS do not have while-in-use permission, camera/microphone access is 321 // denied. 322 assertEquals(MODE_IGNORED, noteOp(OPSTR_CAMERA)); 323 assertEquals(MODE_IGNORED, noteOp(OPSTR_RECORD_AUDIO)); 324 325 // Start an activity, put app in TOP. 326 startSimpleActivity(); 327 // TOP process has all capabilities. 328 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP, 329 new Integer(PROCESS_CAPABILITY_ALL)); 330 331 // Camera/microphone access is allowed because the app is TOP. 332 assertEquals(MODE_ALLOWED, noteOp(OPSTR_CAMERA)); 333 assertEquals(MODE_ALLOWED, noteOp(OPSTR_RECORD_AUDIO)); 334 335 // Tell the activity to finalize. 336 stopSimpleActivity(); 337 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE, 338 new Integer(PROCESS_CAPABILITY_NONE)); 339 340 // App not in Top, camera/microphone access should be denied. 341 assertEquals(MODE_IGNORED, noteOp(OPSTR_CAMERA)); 342 assertEquals(MODE_IGNORED, noteOp(OPSTR_RECORD_AUDIO)); 343 344 // Stop the foreground service. 345 stopSimpleService(); 346 mUidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY, 347 new Integer(PROCESS_CAPABILITY_NONE)); 348 349 assertEquals(MODE_IGNORED, noteOp(OPSTR_CAMERA)); 350 assertEquals(MODE_IGNORED, noteOp(OPSTR_RECORD_AUDIO)); 351 } 352 noteOp(String opStr)353 private int noteOp(String opStr) throws Exception { 354 return callWithShellPermissionIdentity( 355 () -> sAppOps.noteOp(opStr, sUid, PACKAGE_NAME, 356 opStr, "")); 357 } 358 } 359