diff options
| -rw-r--r-- | services/core/java/com/android/server/am/AppRestrictionController.java | 97 | ||||
| -rw-r--r-- | services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java | 165 |
2 files changed, 230 insertions, 32 deletions
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index 6928bd307af0..81a2fc5277a0 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<String> mCarrierPrivilegedApps; + private final SparseArray<Set<String>> mCarrierPrivilegedApps = new SparseArray<>(); + + /** + * Holding the callbacks to the carrier privileged app changes. + * + * it's lock free. + */ + private volatile ArrayList<PhoneCarrierPrivilegesCallback> 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<PhoneCarrierPrivilegesCallback> 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; } - @GuardedBy("mCarrierPrivilegedLock") - private void fetchCarrierPrivilegedAppsCPL() { + private void unregisterCarrierPrivilegesCallbacks() { final TelephonyManager telephonyManager = mInjector.getTelephonyManager(); - mCarrierPrivilegedApps = - telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions(); - if (DEBUG_BG_RESTRICTION_CONTROLLER) { - Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps); + if (telephonyManager == null) { + return; + } + final ArrayList<PhoneCarrierPrivilegesCallback> callbacks = mCarrierPrivilegesCallbacks; + if (callbacks != null) { + for (int i = callbacks.size() - 1; i >= 0; i--) { + telephonyManager.unregisterCarrierPrivilegesCallback(callbacks.get(i)); + } + mCarrierPrivilegesCallbacks = null; + } + } + + private class PhoneCarrierPrivilegesCallback implements CarrierPrivilegesCallback { + private final int mPhoneId; + + PhoneCarrierPrivilegesCallback(int phoneId) { + mPhoneId = phoneId; + } + + @Override + public void onCarrierPrivilegesChanged(@NonNull Set<String> privilegedPackageNames, + @NonNull Set<Integer> 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 3042edec6aad..bad04dc020c7 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<AppStateTracker.BackgroundRestrictedAppListener> 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<Pair<String[], int[]>> mPackages = new SparseArray<>(); + private final SparseArray<Pair<Executor, CarrierPrivilegesCallback>> 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<String[], int[]> pkgs = mPackages.get(phoneId); + final Set<String> pkgNames = pkgs != null + ? Arrays.stream(pkgs.first).collect(Collectors.toUnmodifiableSet()) + : Collections.emptySet(); + final Set<Integer> 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<Executor, CarrierPrivilegesCallback> 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; |