From e39d8cc89c93798c55a7f43e647c759beaa21dcb Mon Sep 17 00:00:00 2001 From: Jing Ji Date: Thu, 30 Mar 2023 23:50:17 -0700 Subject: [PATCH] Add listener on carrier privileged app changes for exempted FGS type In case the carrier privileged app is changed, i.e. sim swap, the new carrier privileged apps could start FGS with the systemExempted type. MUST_SLEEP Bug: 275718387 Test: atest BackgroundRestrictionTest Change-Id: Ia938da9669e8b447c9c9d93a7145c0d0f050eb08 --- .../server/am/AppRestrictionController.java | 99 +++++++---- .../server/am/BackgroundRestrictionTest.java | 165 ++++++++++++++++++ 2 files changed, 231 insertions(+), 33 deletions(-) diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index 6928bd307af05..81a2fc5277a08 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -136,6 +136,7 @@ import android.provider.DeviceConfig.OnPropertiesChangedListener; import android.provider.DeviceConfig.Properties; import android.provider.Settings; import android.telephony.TelephonyManager; +import android.telephony.TelephonyManager.CarrierPrivilegesCallback; import android.text.TextUtils; import android.util.ArraySet; import android.util.AtomicFile; @@ -311,10 +312,18 @@ public final class AppRestrictionController { private final Object mCarrierPrivilegedLock = new Object(); /** - * List of carrier-privileged apps that should be excluded from standby. + * List of carrier-privileged apps that should be excluded from standby, + * the key of this array here is the phone id. */ @GuardedBy("mCarrierPrivilegedLock") - private List mCarrierPrivilegedApps; + private final SparseArray> mCarrierPrivilegedApps = new SparseArray<>(); + + /** + * Holding the callbacks to the carrier privileged app changes. + * + * it's lock free. + */ + private volatile ArrayList mCarrierPrivilegesCallbacks; /** * Whether or not we've loaded the restriction settings from the persistent storage. @@ -357,19 +366,6 @@ public final class AppRestrictionController { onUidAdded(uid); } } - } - // fall through. - case Intent.ACTION_PACKAGE_CHANGED: { - final String pkgName = intent.getData().getSchemeSpecificPart(); - final String[] cmpList = intent.getStringArrayExtra( - Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST); - // If this is PACKAGE_ADDED (cmpList == null), or if it's a whole-package - // enable/disable event (cmpList is just the package name itself), drop - // our carrier privileged app & system-app caches and let them refresh - if (cmpList == null - || (cmpList.length == 1 && pkgName.equals(cmpList[0]))) { - clearCarrierPrivilegedApps(); - } } break; case Intent.ACTION_PACKAGE_FULLY_REMOVED: { final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); @@ -412,6 +408,10 @@ public final class AppRestrictionController { onUserRemoved(userId); } } break; + case TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED: { + unregisterCarrierPrivilegesCallbacks(); + registerCarrierPrivilegesCallbacks(); + } break; } } }; @@ -1508,6 +1508,7 @@ public final class AppRestrictionController { initRolesInInterest(); registerForUidObservers(); registerForSystemBroadcasts(); + registerCarrierPrivilegesCallbacks(); mNotificationHelper.onSystemReady(); mInjector.getAppStateTracker().addBackgroundRestrictedAppListener( mBackgroundRestrictionListener); @@ -2823,6 +2824,7 @@ public final class AppRestrictionController { final PackageManagerInternal pm = mInjector.getPackageManagerInternal(); final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal(); final AppOpsManager appOpsManager = mInjector.getAppOpsManager(); + final ActivityManagerService activityManagerService = mInjector.getActivityManagerService(); final int userId = UserHandle.getUserId(uid); if (isSystemModule(pkg)) { return REASON_SYSTEM_MODULE; @@ -2836,7 +2838,7 @@ public final class AppRestrictionController { return REASON_DPO_PROTECTED_APP; } else if (appStandbyInternal.isActiveDeviceAdmin(pkg, userId)) { return REASON_ACTIVE_DEVICE_ADMIN; - } else if (mActivityManagerService.mConstants.mFlagSystemExemptPowerRestrictionsEnabled + } else if (activityManagerService.mConstants.mFlagSystemExemptPowerRestrictionsEnabled && appOpsManager.checkOpNoThrow( AppOpsManager.OP_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS, uid, pkg) == AppOpsManager.MODE_ALLOWED) { @@ -2879,32 +2881,61 @@ public final class AppRestrictionController { private boolean isCarrierApp(String packageName) { synchronized (mCarrierPrivilegedLock) { - if (mCarrierPrivilegedApps == null) { - fetchCarrierPrivilegedAppsCPL(); - } if (mCarrierPrivilegedApps != null) { - return mCarrierPrivilegedApps.contains(packageName); + for (int i = mCarrierPrivilegedApps.size() - 1; i >= 0; i--) { + if (mCarrierPrivilegedApps.valueAt(i).contains(packageName)) { + return true; + } + } } return false; } } - private void clearCarrierPrivilegedApps() { - if (DEBUG_BG_RESTRICTION_CONTROLLER) { - Slog.i(TAG, "Clearing carrier privileged apps list"); + private void registerCarrierPrivilegesCallbacks() { + final TelephonyManager telephonyManager = mInjector.getTelephonyManager(); + if (telephonyManager == null) { + return; } - synchronized (mCarrierPrivilegedLock) { - mCarrierPrivilegedApps = null; // Need to be refetched. + + final int numPhones = telephonyManager.getActiveModemCount(); + final ArrayList callbacks = new ArrayList<>(); + for (int i = 0; i < numPhones; i++) { + final PhoneCarrierPrivilegesCallback callback = new PhoneCarrierPrivilegesCallback(i); + callbacks.add(callback); + telephonyManager.registerCarrierPrivilegesCallback(i, mBgExecutor, callback); + } + mCarrierPrivilegesCallbacks = callbacks; + } + + private void unregisterCarrierPrivilegesCallbacks() { + final TelephonyManager telephonyManager = mInjector.getTelephonyManager(); + if (telephonyManager == null) { + return; + } + final ArrayList callbacks = mCarrierPrivilegesCallbacks; + if (callbacks != null) { + for (int i = callbacks.size() - 1; i >= 0; i--) { + telephonyManager.unregisterCarrierPrivilegesCallback(callbacks.get(i)); + } + mCarrierPrivilegesCallbacks = null; } } - @GuardedBy("mCarrierPrivilegedLock") - private void fetchCarrierPrivilegedAppsCPL() { - final TelephonyManager telephonyManager = mInjector.getTelephonyManager(); - mCarrierPrivilegedApps = - telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions(); - if (DEBUG_BG_RESTRICTION_CONTROLLER) { - Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps); + private class PhoneCarrierPrivilegesCallback implements CarrierPrivilegesCallback { + private final int mPhoneId; + + PhoneCarrierPrivilegesCallback(int phoneId) { + mPhoneId = phoneId; + } + + @Override + public void onCarrierPrivilegesChanged(@NonNull Set privilegedPackageNames, + @NonNull Set privilegedUids) { + synchronized (mCarrierPrivilegedLock) { + mCarrierPrivilegedApps.put(mPhoneId, + Collections.unmodifiableSet(privilegedPackageNames)); + } } } @@ -3272,7 +3303,6 @@ public final class AppRestrictionController { private void registerForSystemBroadcasts() { final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); - packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); packageFilter.addDataScheme("package"); mContext.registerReceiverForAllUsers(mBroadcastReceiver, packageFilter, null, mBgHandler); @@ -3285,6 +3315,9 @@ public final class AppRestrictionController { bootFilter.addAction(Intent.ACTION_LOCKED_BOOT_COMPLETED); mContext.registerReceiverAsUser(mBootReceiver, UserHandle.SYSTEM, bootFilter, null, mBgHandler); + final IntentFilter telFilter = new IntentFilter( + TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED); + mContext.registerReceiverForAllUsers(mBroadcastReceiver, telFilter, null, mBgHandler); } private void unregisterForSystemBroadcasts() { diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index 3042edec6aad9..bad04dc020c78 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -60,6 +60,8 @@ import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; +import static android.os.PowerExemptionManager.REASON_CARRIER_PRIVILEGED_APP; +import static android.os.PowerExemptionManager.REASON_DENIED; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -90,6 +92,7 @@ import static org.mockito.Mockito.anyObject; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -132,8 +135,10 @@ import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.service.notification.StatusBarNotification; import android.telephony.TelephonyManager; +import android.telephony.TelephonyManager.CarrierPrivilegesCallback; import android.util.Log; import android.util.Pair; +import android.util.SparseArray; import androidx.test.runner.AndroidJUnit4; @@ -176,9 +181,12 @@ import java.io.File; import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; @@ -233,8 +241,42 @@ public final class BackgroundRestrictionTest { private static final int BATTERY_FULL_CHARGE_MAH = 5_000; + private static final String[] MOCK_PRIVILEGED_PACKAGES_0 = new String[] { + TEST_PACKAGE_BASE + 0, + TEST_PACKAGE_BASE + 1, + }; + private static final String[] MOCK_PRIVILEGED_PACKAGES_1 = new String[] { + TEST_PACKAGE_BASE + 2, + TEST_PACKAGE_BASE + 3, + }; + private static final String[] MOCK_PRIVILEGED_PACKAGES_2 = new String[] { + TEST_PACKAGE_BASE + 4, + TEST_PACKAGE_BASE + 5, + }; + private static final int[] MOCK_PRIVILEGED_UIDS_0 = new int[] { + UserHandle.getUid(TEST_USER0, TEST_PACKAGE_APPID_BASE + 0), + UserHandle.getUid(TEST_USER0, TEST_PACKAGE_APPID_BASE + 1), + }; + private static final int[] MOCK_PRIVILEGED_UIDS_1 = new int[] { + UserHandle.getUid(TEST_USER0, TEST_PACKAGE_APPID_BASE + 2), + UserHandle.getUid(TEST_USER0, TEST_PACKAGE_APPID_BASE + 3), + }; + private static final int[] MOCK_PRIVILEGED_UIDS_2 = new int[] { + UserHandle.getUid(TEST_USER0, TEST_PACKAGE_APPID_BASE + 4), + UserHandle.getUid(TEST_USER0, TEST_PACKAGE_APPID_BASE + 5), + }; + private static final String[][] MOCK_PRIVILEGED_PACKAGES = new String[][] { + MOCK_PRIVILEGED_PACKAGES_0, + MOCK_PRIVILEGED_PACKAGES_1, + }; + private static final int[][] MOCK_PRIVILEGED_UIDS = new int[][] { + MOCK_PRIVILEGED_UIDS_0, + MOCK_PRIVILEGED_UIDS_1, + }; + @Mock private ActivityManagerInternal mActivityManagerInternal; @Mock private ActivityManagerService mActivityManagerService; + @Mock private ActivityManagerConstants mActivityManagerConstants; @Mock private AppOpsManager mAppOpsManager; @Mock private AppStandbyInternal mAppStandbyInternal; @Mock private AppHibernationManagerInternal mAppHibernationInternal; @@ -255,6 +297,8 @@ public final class BackgroundRestrictionTest { @Mock private TelephonyManager mTelephonyManager; @Mock private IAppOpsService mIAppOpsService; + private PhoneCarrierPrivileges mPhoneCarrierPrivileges; + private long mCurrentTimeMillis; @Captor private ArgumentCaptor mFasListenerCap; @@ -298,6 +342,14 @@ public final class BackgroundRestrictionTest { mBgRestrictionController = spy(new AppRestrictionController(mInjector, mActivityManagerService)); + mActivityManagerService.mConstants = mActivityManagerConstants; + mPhoneCarrierPrivileges = new PhoneCarrierPrivileges( + mInjector.getTelephonyManager(), MOCK_PRIVILEGED_PACKAGES.length); + for (int i = 0; i < MOCK_PRIVILEGED_PACKAGES.length; i++) { + mPhoneCarrierPrivileges.addNewPrivilegePackages(i, + MOCK_PRIVILEGED_PACKAGES[i], MOCK_PRIVILEGED_UIDS[i]); + } + doReturn(PROCESS_STATE_FOREGROUND_SERVICE).when(mActivityManagerInternal) .getUidProcessState(anyInt()); doReturn(TEST_USERS).when(mUserManagerInternal).getUserIds(); @@ -2984,6 +3036,78 @@ public final class BackgroundRestrictionTest { verifyLoadedSettings(settings); } + @Test + public void testCarrierPrivilegedAppListener() throws Exception { + final long shortMs = 1_000L; + for (int i = 0; i < MOCK_PRIVILEGED_PACKAGES.length; i++) { + verifyPotentialSystemExemptionReason(REASON_CARRIER_PRIVILEGED_APP, + MOCK_PRIVILEGED_PACKAGES[i], + MOCK_PRIVILEGED_UIDS[i]); + } + verifyPotentialSystemExemptionReason(REASON_DENIED, + MOCK_PRIVILEGED_PACKAGES_2, + MOCK_PRIVILEGED_UIDS_2); + + mPhoneCarrierPrivileges.addNewPrivilegePackages(0, + MOCK_PRIVILEGED_PACKAGES_2, + MOCK_PRIVILEGED_UIDS_2); + Thread.sleep(shortMs); + + verifyPotentialSystemExemptionReason(REASON_CARRIER_PRIVILEGED_APP, + MOCK_PRIVILEGED_PACKAGES_2, + MOCK_PRIVILEGED_UIDS_2); + + verifyPotentialSystemExemptionReason(REASON_DENIED, + MOCK_PRIVILEGED_PACKAGES_0, + MOCK_PRIVILEGED_UIDS_0); + + verifyPotentialSystemExemptionReason(REASON_CARRIER_PRIVILEGED_APP, + MOCK_PRIVILEGED_PACKAGES_1, + MOCK_PRIVILEGED_UIDS_1); + + mPhoneCarrierPrivileges.addNewPrivilegePackages(1, + new String[0], new int[0]); + Thread.sleep(shortMs); + + verifyPotentialSystemExemptionReason(REASON_CARRIER_PRIVILEGED_APP, + MOCK_PRIVILEGED_PACKAGES_2, + MOCK_PRIVILEGED_UIDS_2); + + verifyPotentialSystemExemptionReason(REASON_DENIED, + MOCK_PRIVILEGED_PACKAGES_0, + MOCK_PRIVILEGED_UIDS_0); + + verifyPotentialSystemExemptionReason(REASON_DENIED, + MOCK_PRIVILEGED_PACKAGES_1, + MOCK_PRIVILEGED_UIDS_1); + + mPhoneCarrierPrivileges.addNewPrivilegePackages(0, + MOCK_PRIVILEGED_PACKAGES_0, + MOCK_PRIVILEGED_UIDS_0); + Thread.sleep(shortMs); + + verifyPotentialSystemExemptionReason(REASON_DENIED, + MOCK_PRIVILEGED_PACKAGES_2, + MOCK_PRIVILEGED_UIDS_2); + + verifyPotentialSystemExemptionReason(REASON_CARRIER_PRIVILEGED_APP, + MOCK_PRIVILEGED_PACKAGES_0, + MOCK_PRIVILEGED_UIDS_0); + + verifyPotentialSystemExemptionReason(REASON_DENIED, + MOCK_PRIVILEGED_PACKAGES_1, + MOCK_PRIVILEGED_UIDS_1); + } + + private void verifyPotentialSystemExemptionReason(int expectedReason, + String[] packages, int[] uids) throws Exception { + for (int i = 0; i < packages.length; i++) { + assertEquals(expectedReason, + mBgRestrictionController.getPotentialSystemExemptionReason( + uids[i], packages[i])); + } + } + private void verifyLoadedSettings(RestrictionSettings settings) throws Exception { // Make a new copy and reset it. RestrictionSettings test = (RestrictionSettings) settings.clone(); @@ -3019,6 +3143,47 @@ public final class BackgroundRestrictionTest { return result; } + private class PhoneCarrierPrivileges { + private final SparseArray> mPackages = new SparseArray<>(); + private final SparseArray> mListeners = + new SparseArray<>(); + + PhoneCarrierPrivileges(TelephonyManager telephonyManager, int phoneIds) { + doReturn(phoneIds).when(telephonyManager).getActiveModemCount(); + doAnswer(inv -> { + registerCarrierPrivilegesCallback( + inv.getArgument(0), + inv.getArgument(1), + inv.getArgument(2)); + return null; + }).when(telephonyManager).registerCarrierPrivilegesCallback( + anyInt(), anyObject(), anyObject()); + } + + public void registerCarrierPrivilegesCallback(int phoneId, Executor executor, + CarrierPrivilegesCallback callback) { + mListeners.put(phoneId, Pair.create(executor, callback)); + final Pair pkgs = mPackages.get(phoneId); + final Set pkgNames = pkgs != null + ? Arrays.stream(pkgs.first).collect(Collectors.toUnmodifiableSet()) + : Collections.emptySet(); + final Set uids = pkgs != null + ? Arrays.stream(pkgs.second).boxed().collect(Collectors.toUnmodifiableSet()) + : Collections.emptySet(); + executor.execute(() -> callback.onCarrierPrivilegesChanged(pkgNames, uids)); + } + + public void addNewPrivilegePackages(int phoneId, String[] pkgNames, int[] uids) { + mPackages.put(phoneId, Pair.create(pkgNames, uids)); + final Pair callback = mListeners.get(phoneId); + if (callback != null) { + callback.first.execute(() -> callback.second.onCarrierPrivilegesChanged( + Arrays.stream(pkgNames).collect(Collectors.toUnmodifiableSet()), + Arrays.stream(uids).boxed().collect(Collectors.toUnmodifiableSet()))); + } + } + } + private class TestBgRestrictionInjector extends AppRestrictionController.Injector { private Context mContext;