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