Merge "Exempt carrier privileged apps from flex policy." into main
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index e96d07f..ee9400f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -46,8 +46,11 @@
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccSlotMapping;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
+import android.util.IntArray;
import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
@@ -68,6 +71,8 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Set;
import java.util.function.Predicate;
/**
@@ -1620,9 +1625,21 @@
private final Object mSatLock = new Object();
private DeviceIdleInternal mDeviceIdleInternal;
+ private TelephonyManager mTelephonyManager;
+
+ private final boolean mHasFeatureTelephonySubscription;
/** Set of all apps that have been deemed special, keyed by user ID. */
private final SparseSetArray<String> mSpecialApps = new SparseSetArray<>();
+ /**
+ * Set of carrier privileged apps, keyed by the logical ID of the SIM their privileged
+ * for.
+ */
+ @GuardedBy("mSatLock")
+ private final SparseSetArray<String> mCarrierPrivilegedApps = new SparseSetArray<>();
+ @GuardedBy("mSatLock")
+ private final SparseArray<LogicalIndexCarrierPrivilegesCallback>
+ mCarrierPrivilegedCallbacks = new SparseArray<>();
@GuardedBy("mSatLock")
private final ArraySet<String> mPowerAllowlistedApps = new ArraySet<>();
@@ -1630,6 +1647,10 @@
@Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
+ case TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED:
+ updateCarrierPrivilegedCallbackRegistration();
+ break;
+
case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache);
break;
@@ -1637,6 +1658,11 @@
}
};
+ SpecialAppTracker() {
+ mHasFeatureTelephonySubscription = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION);
+ }
+
public boolean isSpecialApp(final int userId, @NonNull String packageName) {
synchronized (mSatLock) {
if (mSpecialApps.contains(UserHandle.USER_ALL, packageName)) {
@@ -1654,6 +1680,12 @@
if (mPowerAllowlistedApps.contains(packageName)) {
return true;
}
+ for (int l = mCarrierPrivilegedApps.size() - 1; l >= 0; --l) {
+ if (mCarrierPrivilegedApps.contains(
+ mCarrierPrivilegedApps.keyAt(l), packageName)) {
+ return true;
+ }
+ }
}
return false;
}
@@ -1669,9 +1701,12 @@
private void onSystemServicesReady() {
mDeviceIdleInternal = LocalServices.getService(DeviceIdleInternal.class);
+ mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
synchronized (mLock) {
if (mFlexibilityEnabled) {
+ mHandler.post(
+ SpecialAppTracker.this::updateCarrierPrivilegedCallbackRegistration);
mHandler.post(SpecialAppTracker.this::updatePowerAllowlistCache);
}
}
@@ -1686,6 +1721,13 @@
private void startTracking() {
IntentFilter filter = new IntentFilter(
PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
+
+ if (mHasFeatureTelephonySubscription) {
+ filter.addAction(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED);
+
+ updateCarrierPrivilegedCallbackRegistration();
+ }
+
mContext.registerReceiver(mBroadcastReceiver, filter);
updatePowerAllowlistCache();
@@ -1695,11 +1737,63 @@
mContext.unregisterReceiver(mBroadcastReceiver);
synchronized (mSatLock) {
+ mCarrierPrivilegedApps.clear();
mPowerAllowlistedApps.clear();
mSpecialApps.clear();
+
+ for (int i = mCarrierPrivilegedCallbacks.size() - 1; i >= 0; --i) {
+ mTelephonyManager.unregisterCarrierPrivilegesCallback(
+ mCarrierPrivilegedCallbacks.valueAt(i));
+ }
+ mCarrierPrivilegedCallbacks.clear();
}
}
+ private void updateCarrierPrivilegedCallbackRegistration() {
+ if (mTelephonyManager == null) {
+ return;
+ }
+ if (!mHasFeatureTelephonySubscription) {
+ return;
+ }
+
+ Collection<UiccSlotMapping> simSlotMapping = mTelephonyManager.getSimSlotMapping();
+ final ArraySet<String> changedPkgs = new ArraySet<>();
+ synchronized (mSatLock) {
+ final IntArray callbacksToRemove = new IntArray();
+ for (int i = mCarrierPrivilegedCallbacks.size() - 1; i >= 0; --i) {
+ callbacksToRemove.add(mCarrierPrivilegedCallbacks.keyAt(i));
+ }
+ for (UiccSlotMapping mapping : simSlotMapping) {
+ final int logicalIndex = mapping.getLogicalSlotIndex();
+ if (mCarrierPrivilegedCallbacks.contains(logicalIndex)) {
+ // Callback already exists. No need to create a new one or remove it.
+ callbacksToRemove.remove(logicalIndex);
+ continue;
+ }
+ final LogicalIndexCarrierPrivilegesCallback callback =
+ new LogicalIndexCarrierPrivilegesCallback(logicalIndex);
+ mCarrierPrivilegedCallbacks.put(logicalIndex, callback);
+ // Upon registration, the callbacks will be called with the current list of
+ // apps, so there's no need to query the app list synchronously.
+ mTelephonyManager.registerCarrierPrivilegesCallback(logicalIndex,
+ AppSchedulingModuleThread.getExecutor(), callback);
+ }
+
+ for (int i = callbacksToRemove.size() - 1; i >= 0; --i) {
+ final int logicalIndex = callbacksToRemove.get(i);
+ final LogicalIndexCarrierPrivilegesCallback callback =
+ mCarrierPrivilegedCallbacks.get(logicalIndex);
+ mTelephonyManager.unregisterCarrierPrivilegesCallback(callback);
+ mCarrierPrivilegedCallbacks.remove(logicalIndex);
+ changedPkgs.addAll(mCarrierPrivilegedApps.get(logicalIndex));
+ mCarrierPrivilegedApps.remove(logicalIndex);
+ }
+ }
+
+ updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs);
+ }
+
/**
* Update the processed special app set for the specified user ID, only looking at the
* specified set of apps. This method must <b>NEVER</b> be called while holding
@@ -1762,18 +1856,65 @@
updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs);
}
+ class LogicalIndexCarrierPrivilegesCallback implements
+ TelephonyManager.CarrierPrivilegesCallback {
+ public final int logicalIndex;
+
+ LogicalIndexCarrierPrivilegesCallback(int logicalIndex) {
+ this.logicalIndex = logicalIndex;
+ }
+
+ @Override
+ public void onCarrierPrivilegesChanged(@NonNull Set<String> privilegedPackageNames,
+ @NonNull Set<Integer> privilegedUids) {
+ final ArraySet<String> changedPkgs = new ArraySet<>();
+ synchronized (mSatLock) {
+ final ArraySet<String> oldPrivilegedSet =
+ mCarrierPrivilegedApps.get(logicalIndex);
+ if (oldPrivilegedSet != null) {
+ changedPkgs.addAll(oldPrivilegedSet);
+ mCarrierPrivilegedApps.remove(logicalIndex);
+ }
+ for (String pkgName : privilegedPackageNames) {
+ mCarrierPrivilegedApps.add(logicalIndex, pkgName);
+ if (!changedPkgs.remove(pkgName)) {
+ // The package wasn't in the previous set of privileged apps. Add it
+ // since its state has changed.
+ changedPkgs.add(pkgName);
+ }
+ }
+ }
+
+ // The carrier privileged list doesn't provide a simple userId correlation,
+ // so for now, use USER_ALL for these packages.
+ // TODO(141645789): use the UID list to narrow down to specific userIds
+ updateSpecialAppSetUnlocked(UserHandle.USER_ALL, changedPkgs);
+ }
+ }
+
public void dump(@NonNull IndentingPrintWriter pw) {
pw.println("Special apps:");
pw.increaseIndent();
synchronized (mSatLock) {
for (int u = 0; u < mSpecialApps.size(); ++u) {
+ pw.print("User ");
pw.print(mSpecialApps.keyAt(u));
pw.print(": ");
pw.println(mSpecialApps.valuesAt(u));
}
pw.println();
+ pw.println("Carrier privileged packages:");
+ pw.increaseIndent();
+ for (int i = 0; i < mCarrierPrivilegedApps.size(); ++i) {
+ pw.print(mCarrierPrivilegedApps.keyAt(i));
+ pw.print(": ");
+ pw.println(mCarrierPrivilegedApps.valuesAt(i));
+ }
+ pw.decreaseIndent();
+
+ pw.println();
pw.print("Power allowlisted packages: ");
pw.println(mPowerAllowlistedApps);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
index 6bcd778..c6a6865 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java
@@ -60,10 +60,13 @@
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AppGlobals;
import android.app.job.JobInfo;
@@ -71,12 +74,15 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.net.NetworkRequest;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.telephony.TelephonyManager;
+import android.telephony.UiccSlotMapping;
import android.util.ArraySet;
import android.util.EmptyArray;
import android.util.SparseArray;
@@ -104,6 +110,9 @@
import java.time.Instant;
import java.time.ZoneOffset;
import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.Executor;
public class FlexibilityControllerTest {
@@ -113,6 +122,9 @@
private MockitoSession mMockingSession;
private BroadcastReceiver mBroadcastReceiver;
+ private final SparseArray<ArraySet<String>> mCarrierPrivilegedApps = new SparseArray<>();
+ private final SparseArray<TelephonyManager.CarrierPrivilegesCallback>
+ mCarrierPrivilegedCallbacks = new SparseArray<>();
private FlexibilityController mFlexibilityController;
private DeviceConfig.Properties.Builder mDeviceConfigPropertiesBuilder;
private JobStore mJobStore;
@@ -130,6 +142,10 @@
@Mock
private PrefetchController mPrefetchController;
@Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private IPackageManager mIPackageManager;
+ @Mock
private PackageManager mPackageManager;
@Before
@@ -138,6 +154,7 @@
.initMocks(this)
.strictness(Strictness.LENIENT)
.spyStatic(DeviceConfig.class)
+ .mockStatic(AppGlobals.class)
.mockStatic(LocalServices.class)
.startMocking();
// Called in StateController constructor.
@@ -167,17 +184,23 @@
-> mDeviceConfigPropertiesBuilder.build())
.when(() -> DeviceConfig.getProperties(
eq(DeviceConfig.NAMESPACE_JOB_SCHEDULER), ArgumentMatchers.<String>any()));
+ // Used in FlexibilityController.SpecialAppTracker.
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION))
+ .thenReturn(true);
//used to get jobs by UID
mJobStore = JobStore.initAndGetForTesting(mContext, mContext.getFilesDir());
doReturn(mJobStore).when(mJobSchedulerService).getJobStore();
// Used in JobStatus.
- doReturn(mock(PackageManagerInternal.class))
- .when(() -> LocalServices.getService(PackageManagerInternal.class));
+ doReturn(mIPackageManager).when(AppGlobals::getPackageManager);
// Freeze the clocks at a moment in time
JobSchedulerService.sSystemClock =
Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC);
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(Instant.ofEpochMilli(FROZEN_TIME), ZoneOffset.UTC);
+ // Set empty set of privileged apps.
+ setSimSlotMappings(null);
+ setPowerWhitelistExceptIdle();
// Initialize real objects.
doReturn(Long.MAX_VALUE).when(mPrefetchController).getNextEstimatedLaunchTimeLocked(any());
ArgumentCaptor<BroadcastReceiver> receiverCaptor =
@@ -249,9 +272,13 @@
}
private JobStatus createJobStatus(String testTag, JobInfo.Builder job) {
+ return createJobStatus(testTag, job, SOURCE_PACKAGE);
+ }
+
+ private JobStatus createJobStatus(String testTag, JobInfo.Builder job, String sourcePackage) {
JobInfo jobInfo = job.build();
JobStatus js = JobStatus.createFromJobInfo(
- jobInfo, 1000, SOURCE_PACKAGE, SOURCE_USER_ID, "FCTest", testTag);
+ jobInfo, 1000, sourcePackage, SOURCE_USER_ID, "FCTest", testTag);
js.enqueueTime = FROZEN_TIME;
js.setStandbyBucket(ACTIVE_INDEX);
if (js.hasFlexibilityConstraint()) {
@@ -1084,7 +1111,6 @@
@Test
public void testAllowlistedAppBypass() {
- setPowerWhitelistExceptIdle();
mFlexibilityController.onSystemServicesReady();
JobStatus jsHigh = createJobStatus("testAllowlistedAppBypass",
@@ -1118,6 +1144,148 @@
}
@Test
+ public void testCarrierPrivilegedAppBypass() throws Exception {
+ mFlexibilityController.onSystemServicesReady();
+
+ final String carrier1Pkg1 = "com.test.carrier.1.pkg.1";
+ final String carrier1Pkg2 = "com.test.carrier.1.pkg.2";
+ final String carrier2Pkg = "com.test.carrier.2.pkg";
+ final String nonCarrierPkg = "com.test.normal.pkg";
+
+ setPackageUid(carrier1Pkg1, 1);
+ setPackageUid(carrier1Pkg2, 11);
+ setPackageUid(carrier2Pkg, 2);
+ setPackageUid(nonCarrierPkg, 3);
+
+ // Set the second carrier's privileged list before SIM configuration is sent to test
+ // initialization.
+ setCarrierPrivilegedAppList(2, carrier2Pkg);
+
+ UiccSlotMapping sim1 = mock(UiccSlotMapping.class);
+ UiccSlotMapping sim2 = mock(UiccSlotMapping.class);
+ doReturn(1).when(sim1).getLogicalSlotIndex();
+ doReturn(2).when(sim2).getLogicalSlotIndex();
+ setSimSlotMappings(List.of(sim1, sim2));
+
+ JobStatus jsHighC1P1 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_HIGH), carrier1Pkg1);
+ JobStatus jsDefaultC1P1 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), carrier1Pkg1);
+ JobStatus jsLowC1P1 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_LOW), carrier1Pkg1);
+ JobStatus jsMinC1P1 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_MIN), carrier1Pkg1);
+ JobStatus jsHighC1P2 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_HIGH), carrier1Pkg2);
+ JobStatus jsDefaultC1P2 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), carrier1Pkg2);
+ JobStatus jsLowC1P2 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_LOW), carrier1Pkg2);
+ JobStatus jsMinC1P2 = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_MIN), carrier1Pkg2);
+ JobStatus jsHighC2P = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_HIGH), carrier2Pkg);
+ JobStatus jsDefaultC2P = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), carrier2Pkg);
+ JobStatus jsLowC2P = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_LOW), carrier2Pkg);
+ JobStatus jsMinC2P = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_MIN), carrier2Pkg);
+ JobStatus jsHighNCP = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_HIGH), nonCarrierPkg);
+ JobStatus jsDefaultNCP = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT), nonCarrierPkg);
+ JobStatus jsLowNCP = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_LOW), nonCarrierPkg);
+ JobStatus jsMinNCP = createJobStatus("testCarrierPrivilegedAppBypass",
+ createJob(0).setPriority(JobInfo.PRIORITY_MIN), nonCarrierPkg);
+
+ setCarrierPrivilegedAppList(1);
+ synchronized (mFlexibilityController.mLock) {
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP));
+ }
+
+ // Only mark the first package of carrier 1 as privileged. Only that app's jobs should
+ // be exempted.
+ setCarrierPrivilegedAppList(1, carrier1Pkg1);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP));
+ }
+
+ // Add the second package of carrier 1. Both apps' jobs should be exempted.
+ setCarrierPrivilegedAppList(1, carrier1Pkg1, carrier1Pkg2);
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP));
+ }
+
+ // Remove a SIM slot. The relevant app's should no longer have exempted jobs.
+ setSimSlotMappings(List.of(sim1));
+ synchronized (mFlexibilityController.mLock) {
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P1));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P1));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC1P2));
+ assertTrue(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC1P2));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinC2P));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsHighNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsDefaultNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsLowNCP));
+ assertFalse(mFlexibilityController.isFlexibilitySatisfiedLocked(jsMinNCP));
+ }
+ }
+
+ @Test
public void testForegroundAppBypass() {
JobStatus jsHigh = createJobStatus("testAllowlistedAppBypass",
createJob(0).setPriority(JobInfo.PRIORITY_HIGH));
@@ -1753,6 +1921,24 @@
}
}
+ private void setCarrierPrivilegedAppList(int logicalIndex, String... packages) {
+ final ArraySet<String> packageSet = packages == null
+ ? new ArraySet<>() : new ArraySet<>(packages);
+ mCarrierPrivilegedApps.put(logicalIndex, packageSet);
+
+ TelephonyManager.CarrierPrivilegesCallback callback =
+ mCarrierPrivilegedCallbacks.get(logicalIndex);
+ if (callback != null) {
+ callback.onCarrierPrivilegesChanged(packageSet, Collections.emptySet());
+ waitForQuietModuleThread();
+ }
+ }
+
+ private void setPackageUid(final String pkgName, final int uid) throws Exception {
+ doReturn(uid).when(mIPackageManager)
+ .getPackageUid(eq(pkgName), anyLong(), eq(UserHandle.getUserId(uid)));
+ }
+
private void setPowerWhitelistExceptIdle(String... packages) {
doReturn(packages == null ? EmptyArray.STRING : packages)
.when(mDeviceIdleInternal).getFullPowerWhitelistExceptIdle();
@@ -1763,6 +1949,47 @@
}
}
+ private void setSimSlotMappings(@Nullable Collection<UiccSlotMapping> simSlotMapping) {
+ clearInvocations(mTelephonyManager);
+ final Collection<UiccSlotMapping> returnedMapping = simSlotMapping == null
+ ? Collections.emptyList() : simSlotMapping;
+ doReturn(returnedMapping).when(mTelephonyManager).getSimSlotMapping();
+ if (mBroadcastReceiver != null) {
+ final Intent intent = new Intent(TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED);
+ mBroadcastReceiver.onReceive(mContext, intent);
+ waitForQuietModuleThread();
+ }
+ if (returnedMapping.size() > 0) {
+ ArgumentCaptor<TelephonyManager.CarrierPrivilegesCallback> callbackCaptor =
+ ArgumentCaptor.forClass(TelephonyManager.CarrierPrivilegesCallback.class);
+ ArgumentCaptor<Integer> logicalIndexCaptor = ArgumentCaptor.forClass(Integer.class);
+
+ final int minExpectedNewRegistrations = Math.max(0,
+ returnedMapping.size() - mCarrierPrivilegedCallbacks.size());
+ verify(mTelephonyManager, atLeast(minExpectedNewRegistrations))
+ .registerCarrierPrivilegesCallback(
+ logicalIndexCaptor.capture(), any(), callbackCaptor.capture());
+
+ final List<Integer> registeredIndices = logicalIndexCaptor.getAllValues();
+ final List<TelephonyManager.CarrierPrivilegesCallback> registeredCallbacks =
+ callbackCaptor.getAllValues();
+ for (int i = 0; i < registeredIndices.size(); ++i) {
+ final int logicalIndex = registeredIndices.get(i);
+ final TelephonyManager.CarrierPrivilegesCallback callback =
+ registeredCallbacks.get(i);
+
+ mCarrierPrivilegedCallbacks.put(logicalIndex, callback);
+
+ // The API contract promises a callback upon registration with the current list.
+ final ArraySet<String> cpApps = mCarrierPrivilegedApps.get(logicalIndex);
+ callback.onCarrierPrivilegesChanged(
+ cpApps == null ? Collections.emptySet() : cpApps,
+ Collections.emptySet());
+ }
+ waitForQuietModuleThread();
+ }
+ }
+
private void setUidBias(int uid, int bias) {
int prevBias = mJobSchedulerService.getUidBias(uid);
doReturn(bias).when(mJobSchedulerService).getUidBias(uid);