1 /* 2 * Copyright (C) 2022 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 android.app.cts.fgstest; 18 19 import static android.app.fgstesthelper.LocalForegroundServiceBase.RESULT_INVALID_TYPE_EXCEPTION; 20 import static android.app.fgstesthelper.LocalForegroundServiceBase.RESULT_MISSING_TYPE_EXCEPTION; 21 import static android.app.fgstesthelper.LocalForegroundServiceBase.RESULT_OK; 22 import static android.app.fgstesthelper.LocalForegroundServiceBase.RESULT_SECURITY_EXCEPTION; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.fail; 26 import static org.junit.Assume.assumeTrue; 27 28 import android.app.ActivityManager; 29 import android.app.AppOpsManager; 30 import android.app.ForegroundServiceTypePolicy; 31 import android.app.ForegroundServiceTypePolicy.ForegroundServiceTypePolicyInfo; 32 import android.app.Instrumentation; 33 import android.app.cts.android.app.cts.tools.WatchUidRunner; 34 import android.app.fgstesthelper.LocalForegroundServiceBase; 35 import android.app.role.RoleManager; 36 import android.content.BroadcastReceiver; 37 import android.content.ComponentName; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.IntentFilter; 41 import android.content.pm.ApplicationInfo; 42 import android.content.pm.PackageManager; 43 import android.content.pm.PermissionInfo; 44 import android.content.pm.ServiceInfo; 45 import android.location.LocationManager; 46 import android.os.Process; 47 import android.os.UserHandle; 48 import android.platform.test.annotations.Presubmit; 49 import android.util.ArrayMap; 50 51 import androidx.test.InstrumentationRegistry; 52 import androidx.test.runner.AndroidJUnit4; 53 import androidx.test.uiautomator.UiDevice; 54 55 import com.android.compatibility.common.util.ApiTest; 56 import com.android.compatibility.common.util.DeviceConfigStateHelper; 57 import com.android.compatibility.common.util.SystemUtil; 58 import com.android.internal.util.ArrayUtils; 59 60 import org.junit.After; 61 import org.junit.Before; 62 import org.junit.Ignore; 63 import org.junit.Test; 64 import org.junit.runner.RunWith; 65 66 import java.util.ArrayList; 67 import java.util.Arrays; 68 import java.util.concurrent.CountDownLatch; 69 import java.util.concurrent.TimeUnit; 70 71 @RunWith(AndroidJUnit4.class) 72 @Presubmit 73 public final class ActivityManagerForegroundServiceTypeTest { 74 private static final String TAG = ActivityManagerForegroundServiceTypeTest.class.getName(); 75 76 private static final String TEST_PKG_NAME_TARGET = "android.app.fgstesthelper"; 77 private static final String TEST_PKG_NAME_CURRENT = "android.app.fgstesthelpercurrent"; 78 private static final String TEST_PKG_NAME_API33 = "android.app.fgstesthelper33"; 79 private static final String SHELL_PKG_NAME = "com.android.shell"; 80 81 private static final String TEST_CLS_NAME_NO_TYPE = 82 "android.app.fgstesthelper.LocalForegroundServiceNoType"; 83 private static final String TEST_CLS_NAME_ALL_TYPE = 84 "android.app.fgstesthelper.LocalForegroundServiceAllTypes"; 85 private static final String FGS_TYPE_PERMISSION_CHANGE_ID = "FGS_TYPE_PERMISSION_CHANGE_ID"; 86 87 private static final long WAITFOR_MSEC = 5000; 88 89 private static final ComponentName TEST_COMP_TARGET_FGS_NO_TYPE = new ComponentName( 90 TEST_PKG_NAME_TARGET, TEST_CLS_NAME_NO_TYPE); 91 private static final ComponentName TEST_COMP_TARGET_FGS_ALL_TYPE = new ComponentName( 92 TEST_PKG_NAME_TARGET, TEST_CLS_NAME_ALL_TYPE); 93 private static final ComponentName TEST_COMP_CURRENT_FGS_NO_TYPE = new ComponentName( 94 TEST_PKG_NAME_CURRENT, TEST_CLS_NAME_NO_TYPE); 95 private static final ComponentName TEST_COMP_CURRENT_FGS_ALL_TYPE = new ComponentName( 96 TEST_PKG_NAME_CURRENT, TEST_CLS_NAME_ALL_TYPE); 97 private static final ComponentName TEST_COMP_API33_FGS_NO_TYPE = new ComponentName( 98 TEST_PKG_NAME_API33, TEST_CLS_NAME_NO_TYPE); 99 private static final ComponentName TEST_COMP_API33_FGS_ALL_TYPE = new ComponentName( 100 TEST_PKG_NAME_API33, TEST_CLS_NAME_ALL_TYPE); 101 102 private static final String SPECIAL_PERMISSION_OP_ALLOWLISTED = "SPECIAL_PERM_ALLOWLISTED"; 103 private static final ArrayMap<String, SpecialPermissionOp> sSpecialPermissionOps = 104 new ArrayMap<>(); 105 106 private Context mContext; 107 private Context mTargetContext; 108 private Instrumentation mInstrumentation; 109 private ActivityManager mActivityManager; 110 private PackageManager mPackageManager; 111 112 @Before setUp()113 public void setUp() { 114 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 115 mContext = mInstrumentation.getContext(); 116 mTargetContext = mInstrumentation.getTargetContext(); 117 mActivityManager = mInstrumentation.getContext().getSystemService(ActivityManager.class); 118 mPackageManager = mInstrumentation.getContext().getPackageManager(); 119 if (sSpecialPermissionOps.isEmpty()) { 120 sSpecialPermissionOps.put(SPECIAL_PERMISSION_OP_ALLOWLISTED, 121 new DeviceAllowlistPermissionOp()); 122 } 123 } 124 125 @After tearDown()126 public void tearDown() throws Exception { 127 SystemUtil.runWithShellPermissionIdentity(() -> { 128 mActivityManager.forceStopPackage(TEST_PKG_NAME_CURRENT); 129 mActivityManager.forceStopPackage(TEST_PKG_NAME_API33); 130 }); 131 } 132 133 @ApiTest(apis = {"android.app.Service#startForeground"}) 134 @Test testForegroundServiceTypeMissing()135 public void testForegroundServiceTypeMissing() throws Exception { 136 try { 137 enablePermissionEnforcement(false, TEST_PKG_NAME_CURRENT); 138 enablePermissionEnforcement(false, TEST_PKG_NAME_API33); 139 testForegroundServiceTypeDisabledCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST, 140 RESULT_MISSING_TYPE_EXCEPTION, 141 TEST_COMP_API33_FGS_NO_TYPE, TEST_COMP_CURRENT_FGS_NO_TYPE); 142 } finally { 143 clearPermissionEnforcement(TEST_PKG_NAME_CURRENT); 144 clearPermissionEnforcement(TEST_PKG_NAME_API33); 145 } 146 } 147 148 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE"}) 149 @Test testForegroundServiceTypeNone()150 public void testForegroundServiceTypeNone() throws Exception { 151 try { 152 enablePermissionEnforcement(false, TEST_PKG_NAME_CURRENT); 153 enablePermissionEnforcement(false, TEST_PKG_NAME_API33); 154 testForegroundServiceTypeDisabledCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE, 155 TEST_COMP_API33_FGS_NO_TYPE, TEST_COMP_CURRENT_FGS_NO_TYPE); 156 } finally { 157 clearPermissionEnforcement(TEST_PKG_NAME_CURRENT); 158 clearPermissionEnforcement(TEST_PKG_NAME_API33); 159 } 160 } 161 162 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC"}) 163 @Test testForegroundServiceTypeDataSync()164 public void testForegroundServiceTypeDataSync() throws Exception { 165 try { 166 enablePermissionEnforcement(false, TEST_PKG_NAME_CURRENT); 167 enablePermissionEnforcement(false, TEST_PKG_NAME_API33); 168 testForegroundServiceTypeDisabledCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC, 169 TEST_COMP_API33_FGS_ALL_TYPE, TEST_COMP_CURRENT_FGS_ALL_TYPE); 170 } finally { 171 clearPermissionEnforcement(TEST_PKG_NAME_CURRENT); 172 clearPermissionEnforcement(TEST_PKG_NAME_API33); 173 } 174 } 175 176 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_DATA_SYNC"}) 177 @Test testForegroundServiceTypeDataSyncPermission()178 public void testForegroundServiceTypeDataSyncPermission() throws Exception { 179 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC); 180 } 181 182 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK"}) 183 @Test testForegroundServiceTypeMediaPlaybackPermission()184 public void testForegroundServiceTypeMediaPlaybackPermission() throws Exception { 185 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK); 186 } 187 188 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_PHONE_CALL"}) 189 @Test testForegroundServiceTypePhoneCallPermission()190 public void testForegroundServiceTypePhoneCallPermission() throws Exception { 191 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL); 192 } 193 194 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_LOCATION"}) 195 @Test testForegroundServiceTypeLocationPermission()196 public void testForegroundServiceTypeLocationPermission() throws Exception { 197 final LocationManager lm = mContext.getSystemService(LocationManager.class); 198 final UserHandle user = Process.myUserHandle(); 199 final boolean wasEnabled = lm.isLocationEnabledForUser(user); 200 try { 201 SystemUtil.runWithShellPermissionIdentity(() -> { 202 lm.setLocationEnabledForUser(true, user); 203 }); 204 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION); 205 } finally { 206 SystemUtil.runWithShellPermissionIdentity(() -> { 207 lm.setLocationEnabledForUser(wasEnabled, user); 208 }); 209 } 210 } 211 212 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE"}) 213 @Test testForegroundServiceTypeConnectedDevicePermission()214 public void testForegroundServiceTypeConnectedDevicePermission() throws Exception { 215 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE); 216 } 217 218 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION"}) 219 @Test testForegroundServiceTypeMediaProjectionPermission()220 public void testForegroundServiceTypeMediaProjectionPermission() throws Exception { 221 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION); 222 } 223 224 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_CAMERA"}) 225 @Test testForegroundServiceTypeCameraPermission()226 public void testForegroundServiceTypeCameraPermission() throws Exception { 227 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA); 228 } 229 230 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MICROPHONE"}) 231 @Test testForegroundServiceTypeMicrophonePermission()232 public void testForegroundServiceTypeMicrophonePermission() throws Exception { 233 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE); 234 } 235 236 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_HEALTH"}) 237 @Test testForegroundServiceTypeHealthPermission()238 public void testForegroundServiceTypeHealthPermission() throws Exception { 239 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH); 240 } 241 242 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING"}) 243 @Test testForegroundServiceTypeRemoteMessagingPermission()244 public void testForegroundServiceTypeRemoteMessagingPermission() throws Exception { 245 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING); 246 } 247 248 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED"}) 249 @Test testForegroundServiceTypeSystemExemptedPermission()250 public void testForegroundServiceTypeSystemExemptedPermission() throws Exception { 251 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED, 252 new String[] {SPECIAL_PERMISSION_OP_ALLOWLISTED}); 253 } 254 255 @Ignore("b/265347862") 256 @Test testForegroundServiceTypeFileManagementPermission()257 public void testForegroundServiceTypeFileManagementPermission() throws Exception { 258 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_FILE_MANAGEMENT); 259 } 260 261 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING"}) 262 @Test testForegroundServiceTypeMediaProcessingPermission()263 public void testForegroundServiceTypeMediaProcessingPermission() throws Exception { 264 assumeTrue(android.content.pm.Flags.introduceMediaProcessingType()); 265 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROCESSING); 266 } 267 268 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE"}) 269 @Test testForegroundServiceTypeSpecialUsePermission()270 public void testForegroundServiceTypeSpecialUsePermission() throws Exception { 271 testPermissionEnforcementCommon(ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE); 272 } 273 274 @ApiTest(apis = {"android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_SPECIAL_USE"}) 275 @Test testForegroundServiceTypeSpecialUseProperty()276 public void testForegroundServiceTypeSpecialUseProperty() throws Exception { 277 final String expectedPropertyValue = "foo"; 278 try { 279 final PackageManager.Property prop = mTargetContext.getPackageManager() 280 .getProperty(PackageManager.PROPERTY_SPECIAL_USE_FGS_SUBTYPE, 281 TEST_COMP_TARGET_FGS_NO_TYPE); 282 fail("Property " + PackageManager.PROPERTY_SPECIAL_USE_FGS_SUBTYPE + " not expected."); 283 } catch (PackageManager.NameNotFoundException e) { 284 // expected. 285 } 286 final PackageManager.Property prop = mTargetContext.getPackageManager() 287 .getProperty(PackageManager.PROPERTY_SPECIAL_USE_FGS_SUBTYPE, 288 TEST_COMP_TARGET_FGS_ALL_TYPE); 289 assertEquals(expectedPropertyValue, prop.getString()); 290 } 291 testForegroundServiceTypeDisabledCommon(int type, ComponentName api33Comp, ComponentName apiCurComp)292 private void testForegroundServiceTypeDisabledCommon(int type, 293 ComponentName api33Comp, ComponentName apiCurComp) throws Exception { 294 testForegroundServiceTypeDisabledCommon(type, RESULT_INVALID_TYPE_EXCEPTION, 295 api33Comp, apiCurComp); 296 } 297 testForegroundServiceTypeDisabledCommon(int type, int exceptionType, ComponentName api33Comp, ComponentName apiCurComp)298 private void testForegroundServiceTypeDisabledCommon(int type, int exceptionType, 299 ComponentName api33Comp, ComponentName apiCurComp) throws Exception { 300 final ApplicationInfo appCurInfo = mTargetContext.getPackageManager().getApplicationInfo( 301 TEST_PKG_NAME_CURRENT, 0); 302 final ApplicationInfo app33Info = mTargetContext.getPackageManager().getApplicationInfo( 303 TEST_PKG_NAME_API33, 0); 304 final WatchUidRunner uidCurWatcher = new WatchUidRunner(mInstrumentation, appCurInfo.uid, 305 WAITFOR_MSEC); 306 final WatchUidRunner uid33Watcher = new WatchUidRunner(mInstrumentation, app33Info.uid, 307 WAITFOR_MSEC); 308 309 final ForegroundServiceTypePolicy policy = ForegroundServiceTypePolicy.getDefaultPolicy(); 310 final ForegroundServiceTypePolicyInfo info = policy.getForegroundServiceTypePolicyInfo( 311 type, ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); 312 try { 313 SystemUtil.runWithShellPermissionIdentity(() -> { 314 info.setTypeDisabledForTest(false, TEST_PKG_NAME_CURRENT); 315 info.setTypeDisabledForTest(false, TEST_PKG_NAME_API33); 316 }); 317 startAndStopFgsType(api33Comp, type, uid33Watcher); 318 startAndStopFgsType(apiCurComp, type, uidCurWatcher); 319 320 SystemUtil.runWithShellPermissionIdentity(() -> { 321 info.setTypeDisabledForTest(true, TEST_PKG_NAME_CURRENT); 322 }); 323 324 assertEquals(exceptionType, startForegroundServiceWithType(apiCurComp, type)); 325 326 stopService(apiCurComp, null); 327 startAndStopFgsType(api33Comp, type, uid33Watcher); 328 } finally { 329 SystemUtil.runWithShellPermissionIdentity(() -> { 330 info.clearTypeDisabledForTest(TEST_PKG_NAME_CURRENT); 331 info.clearTypeDisabledForTest(TEST_PKG_NAME_API33); 332 }); 333 } 334 } 335 startAndStopFgsType(ComponentName compName, int type, WatchUidRunner uidWatcher)336 private void startAndStopFgsType(ComponentName compName, int type, WatchUidRunner uidWatcher) 337 throws Exception { 338 assertEquals(RESULT_OK, startForegroundServiceWithType(compName, type)); 339 if (uidWatcher != null) { 340 uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE, null); 341 } 342 stopService(compName, uidWatcher); 343 } 344 startForegroundServiceWithType(ComponentName compName, int type)345 private int startForegroundServiceWithType(ComponentName compName, int type) throws Exception { 346 final CountDownLatch latch = new CountDownLatch(1); 347 final int[] result = new int[1]; 348 final BroadcastReceiver receiver = new BroadcastReceiver() { 349 @Override 350 public void onReceive(Context context, Intent intent) { 351 result[0] = intent.getIntExtra(LocalForegroundServiceBase.EXTRA_RESULT_CODE, 352 RESULT_OK); 353 latch.countDown(); 354 } 355 }; 356 final Intent intent = new Intent(); 357 intent.setComponent(compName); 358 intent.putExtra(LocalForegroundServiceBase.EXTRA_COMMAND, 359 LocalForegroundServiceBase.COMMAND_START_FOREGROUND); 360 intent.putExtra(LocalForegroundServiceBase.EXTRA_FGS_TYPE, type); 361 final IntentFilter filter = 362 new IntentFilter(LocalForegroundServiceBase.ACTION_START_FGS_RESULT); 363 364 try { 365 mTargetContext.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED); 366 mTargetContext.startForegroundService(intent); 367 latch.await(WAITFOR_MSEC, TimeUnit.MILLISECONDS); 368 return result[0]; 369 } finally { 370 mTargetContext.unregisterReceiver(receiver); 371 } 372 } 373 stopService(ComponentName compName, WatchUidRunner uidWatcher)374 private void stopService(ComponentName compName, WatchUidRunner uidWatcher) throws Exception { 375 final Intent intent = new Intent(); 376 intent.setComponent(compName); 377 intent.putExtra(LocalForegroundServiceBase.EXTRA_COMMAND, 378 LocalForegroundServiceBase.COMMAND_STOP_SELF); 379 380 mTargetContext.startService(intent); 381 382 if (uidWatcher != null) { 383 uidWatcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY, 384 null); 385 } 386 } 387 testPermissionEnforcementCommon(int type)388 private void testPermissionEnforcementCommon(int type) throws Exception { 389 testPermissionEnforcementCommon(type, null); 390 } 391 testPermissionEnforcementCommon(int type, String[] specialOps)392 private void testPermissionEnforcementCommon(int type, String[] specialOps) throws Exception { 393 final String testPackageName = TEST_PKG_NAME_TARGET; 394 TestPermissionInfo[] allOfPermissions = null; 395 TestPermissionInfo[] anyOfPermissions = null; 396 final ForegroundServiceTypePolicy policy = 397 ForegroundServiceTypePolicy.getDefaultPolicy(); 398 final ForegroundServiceTypePolicyInfo info = policy.getForegroundServiceTypePolicyInfo( 399 type, ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE); 400 final String permFlag = info.getPermissionEnforcementFlagForTest(); 401 try (DeviceConfigStateHelper helper = new DeviceConfigStateHelper("activity_manager")) { 402 // Enable the permission check. 403 enablePermissionEnforcement(true, testPackageName); 404 if (permFlag != null) { 405 helper.set(permFlag, "true"); 406 } 407 408 assertEquals(type, info.getForegroundServiceType()); 409 allOfPermissions = triagePermissions( 410 info.getRequiredAllOfPermissionsForTest(mTargetContext).orElse(null)); 411 anyOfPermissions = ArrayUtils.concat(TestPermissionInfo.class, 412 triagePermissions(info.getRequiredAnyOfPermissionsForTest( 413 mTargetContext).orElse(null)), 414 triagePermissions(specialOps)); 415 416 // If we grant all of the permissions, the foreground service start will succeed. 417 grantPermissions(ArrayUtils.concat(TestPermissionInfo.class, 418 allOfPermissions, anyOfPermissions), testPackageName); 419 420 startAndStopFgsType(TEST_COMP_TARGET_FGS_ALL_TYPE, type, null); 421 422 resetPermissions(anyOfPermissions, testPackageName); 423 424 // If we grant all of the "allOf" permission, but none of the "anyOf" permission, it 425 // should fail to start a foreground service. 426 if (!ArrayUtils.isEmpty(anyOfPermissions)) { 427 grantPermissions(allOfPermissions, testPackageName); 428 assertEquals(RESULT_SECURITY_EXCEPTION, 429 startForegroundServiceWithType(TEST_COMP_TARGET_FGS_ALL_TYPE, type)); 430 stopService(TEST_COMP_TARGET_FGS_ALL_TYPE, null); 431 resetPermissions(anyOfPermissions, testPackageName); 432 433 // If there is a feature flag to turn the permission check off, it should succeed. 434 if (permFlag != null) { 435 helper.set(permFlag, "false"); 436 Thread.sleep(1000); 437 grantPermissions(allOfPermissions, testPackageName); 438 startAndStopFgsType(TEST_COMP_TARGET_FGS_ALL_TYPE, type, null); 439 resetPermissions(anyOfPermissions, testPackageName); 440 helper.set(permFlag, "true"); 441 } 442 443 // If we grant any of them, it should succeed. 444 for (TestPermissionInfo perm: anyOfPermissions) { 445 grantPermissions(ArrayUtils.concat(TestPermissionInfo.class, 446 allOfPermissions, new TestPermissionInfo[] {perm}), 447 testPackageName); 448 startAndStopFgsType(TEST_COMP_TARGET_FGS_ALL_TYPE, type, null); 449 resetPermissions(anyOfPermissions, testPackageName); 450 } 451 } 452 453 // If we skip one of the "allOf" permissions, it should fail. 454 if (!ArrayUtils.isEmpty(allOfPermissions)) { 455 for (int i = 0; i < allOfPermissions.length; i++) { 456 final TestPermissionInfo[] perms = getListExceptIndex(allOfPermissions, i); 457 grantPermissions(ArrayUtils.concat(TestPermissionInfo.class, 458 perms, anyOfPermissions), testPackageName); 459 assertEquals(RESULT_SECURITY_EXCEPTION, 460 startForegroundServiceWithType(TEST_COMP_TARGET_FGS_ALL_TYPE, type)); 461 stopService(TEST_COMP_TARGET_FGS_ALL_TYPE, null); 462 resetPermissions(anyOfPermissions, testPackageName); 463 } 464 } 465 } finally { 466 resetPermissions(anyOfPermissions, testPackageName); 467 enablePermissionEnforcement(false, testPackageName); 468 } 469 } 470 regularPermissionToAppOpIfPossible(TestPermissionInfo perm)471 private static int regularPermissionToAppOpIfPossible(TestPermissionInfo perm) { 472 return !perm.mIsAppOps && perm.mSpecialOp == null 473 ? AppOpsManager.permissionToOpCode(perm.mName) 474 : AppOpsManager.OP_NONE; 475 } 476 getListExceptIndex(TestPermissionInfo[] list, int exceptIndex)477 private TestPermissionInfo[] getListExceptIndex(TestPermissionInfo[] list, int exceptIndex) { 478 final ArrayList<TestPermissionInfo> ret = new ArrayList<>(); 479 for (int i = 0; i < list.length; i++) { 480 if (i == exceptIndex) { 481 continue; 482 } 483 ret.add(list[i]); 484 } 485 if (ret.size() > 0) { 486 return ret.toArray(new TestPermissionInfo[ret.size()]); 487 } else { 488 return null; 489 } 490 } 491 enablePermissionEnforcement(boolean enable, String packageName)492 private void enablePermissionEnforcement(boolean enable, String packageName) throws Exception { 493 if (enable) { 494 executeShellCommand("am compat enable --no-kill FGS_TYPE_PERMISSION_CHANGE_ID " 495 + packageName); 496 } else { 497 executeShellCommand("am compat disable --no-kill FGS_TYPE_PERMISSION_CHANGE_ID " 498 + packageName); 499 } 500 } 501 clearPermissionEnforcement(String packageName)502 private void clearPermissionEnforcement(String packageName) throws Exception { 503 executeShellCommand("am compat reset --no-kill FGS_TYPE_PERMISSION_CHANGE_ID " 504 + packageName); 505 } 506 executeShellCommand(String cmd)507 private String executeShellCommand(String cmd) throws Exception { 508 final UiDevice uiDevice = UiDevice.getInstance(mInstrumentation); 509 return uiDevice.executeShellCommand(cmd).trim(); 510 } 511 512 private class TestPermissionInfo { 513 final String mName; 514 final boolean mIsAppOps; 515 final SpecialPermissionOp mSpecialOp; 516 final boolean mIsRole; 517 TestPermissionInfo(String name, boolean isAppOps, SpecialPermissionOp specialOp, boolean isRole)518 TestPermissionInfo(String name, boolean isAppOps, SpecialPermissionOp specialOp, 519 boolean isRole) { 520 mName = name; 521 mIsAppOps = isAppOps; 522 mSpecialOp = specialOp; 523 mIsRole = isRole; 524 } 525 } 526 527 private interface SpecialPermissionOp { grantPermission(String packageName)528 void grantPermission(String packageName) throws Exception; revokePermission(String packageName)529 void revokePermission(String packageName) throws Exception; 530 } 531 532 private class DeviceAllowlistPermissionOp implements SpecialPermissionOp { 533 @Override grantPermission(String packageName)534 public void grantPermission(String packageName) throws Exception { 535 executeShellCommand("cmd deviceidle whitelist +" + packageName); 536 } 537 538 @Override revokePermission(String packageName)539 public void revokePermission(String packageName) throws Exception { 540 executeShellCommand("cmd deviceidle whitelist -" + packageName); 541 } 542 } 543 triagePermissions(String[] permissions)544 private TestPermissionInfo[] triagePermissions(String[] permissions) { 545 final ArrayList<TestPermissionInfo> perms = new ArrayList<>(); 546 if (permissions != null) { 547 final RoleManager rm = mTargetContext.getSystemService(RoleManager.class); 548 for (String perm : permissions) { 549 PermissionInfo pi = null; 550 try { 551 pi = mPackageManager.getPermissionInfo(perm, 0); 552 } catch (PackageManager.NameNotFoundException e) { 553 // It could be an appop. 554 } 555 if (pi != null) { 556 perms.add(new TestPermissionInfo(perm, false, null, false)); 557 } else if (sSpecialPermissionOps.containsKey(perm)) { 558 perms.add(new TestPermissionInfo(perm, false, sSpecialPermissionOps.get(perm), 559 true)); 560 } else if (rm.isRoleAvailable(perm)) { 561 perms.add(new TestPermissionInfo(perm, false, null, true)); 562 } else { 563 try { 564 AppOpsManager.strOpToOp(perm); 565 perms.add(new TestPermissionInfo(perm, true, null, false)); 566 } catch (IllegalArgumentException e) { 567 // We don't support other type of permissions in CTS tests here. 568 } 569 } 570 } 571 } 572 return perms.toArray(new TestPermissionInfo[perms.size()]); 573 } 574 grantPermissions(TestPermissionInfo[] permissions, String packageName)575 private void grantPermissions(TestPermissionInfo[] permissions, String packageName) 576 throws Exception { 577 if (ArrayUtils.isEmpty(permissions)) { 578 return; 579 } 580 final String[] regularPermissions = Arrays.stream(permissions) 581 .filter(p -> !p.mIsAppOps && p.mSpecialOp == null && !p.mIsRole) 582 .map(p -> p.mName) 583 .toArray(String[]::new); 584 final String[] appops = ArrayUtils.concat(String.class, Arrays.stream(permissions) 585 .filter(p -> p.mIsAppOps && p.mSpecialOp == null && !p.mIsRole) 586 .map(p -> p.mName) 587 .toArray(String[]::new), 588 Arrays.stream(permissions) 589 .filter(p -> regularPermissionToAppOpIfPossible(p) != AppOpsManager.OP_NONE) 590 .map(p-> AppOpsManager.opToPublicName(regularPermissionToAppOpIfPossible(p))) 591 .toArray(String[]::new)); 592 final SpecialPermissionOp[] specialOps = Arrays.stream(permissions) 593 .filter(p-> p.mSpecialOp != null) 594 .map(p -> p.mSpecialOp) 595 .toArray(SpecialPermissionOp[]::new); 596 final String[] roles = Arrays.stream(permissions) 597 .filter(p-> p.mIsRole && p.mSpecialOp == null && !p.mIsAppOps) 598 .map(p -> p.mName) 599 .toArray(String[]::new); 600 if (!ArrayUtils.isEmpty(regularPermissions)) { 601 mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(regularPermissions); 602 } 603 if (!ArrayUtils.isEmpty(appops)) { 604 for (String appop : appops) { 605 // Because we're adopting the shell identity, we have to set the appop to shell here 606 executeShellCommand("appops set --user " + UserHandle.myUserId() 607 + " --uid " + SHELL_PKG_NAME + " " + appop + " allow"); 608 } 609 } 610 if (!ArrayUtils.isEmpty(specialOps)) { 611 for (SpecialPermissionOp op : specialOps) { 612 op.grantPermission(packageName); 613 } 614 } 615 if (!ArrayUtils.isEmpty(roles)) { 616 for (String role: roles) { 617 executeShellCommand("cmd role add-role-holder --user " + UserHandle.myUserId() 618 + " " + role + " " + packageName); 619 } 620 } 621 } 622 resetPermissions(TestPermissionInfo[] permissions, String packageName)623 private void resetPermissions(TestPermissionInfo[] permissions, String packageName) 624 throws Exception { 625 mInstrumentation.getUiAutomation().dropShellPermissionIdentity(); 626 executeShellCommand("appops reset --user " + UserHandle.myUserId() 627 + " " + SHELL_PKG_NAME); 628 if (permissions != null) { 629 final SpecialPermissionOp[] specialOps = Arrays.stream(permissions) 630 .filter(p-> p.mSpecialOp != null) 631 .map(p -> p.mSpecialOp) 632 .toArray(SpecialPermissionOp[]::new); 633 final String[] roles = Arrays.stream(permissions) 634 .filter(p-> p.mIsRole && p.mSpecialOp == null && !p.mIsAppOps) 635 .map(p -> p.mName) 636 .toArray(String[]::new); 637 if (!ArrayUtils.isEmpty(specialOps)) { 638 for (SpecialPermissionOp op : specialOps) { 639 op.revokePermission(packageName); 640 } 641 } 642 if (!ArrayUtils.isEmpty(roles)) { 643 for (String role: roles) { 644 executeShellCommand("cmd role remove-role-holder --user " 645 + UserHandle.myUserId() + " " + role + " " + packageName); 646 } 647 } 648 } 649 } 650 } 651