Merge "Satisfy charging constraint earlier for top apps."

This commit is contained in:
Kweku Adams
2022-01-14 20:35:34 +00:00
committed by Android (Google) Code Review
7 changed files with 479 additions and 25 deletions

View File

@@ -1556,7 +1556,7 @@ public class JobSchedulerService extends com.android.server.SystemService
Slog.d(TAG, "UID " + uid + " bias changed from " + prevBias + " to " + newBias);
}
for (int c = 0; c < mControllers.size(); ++c) {
mControllers.get(c).onUidBiasChangedLocked(uid, newBias);
mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias);
}
}
}

View File

@@ -18,6 +18,14 @@ package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.annotation.NonNull;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -27,6 +35,7 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -42,10 +51,26 @@ public final class BatteryController extends RestrictingController {
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
@GuardedBy("mLock")
private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
/**
* List of jobs that started while the UID was in the TOP state.
*/
@GuardedBy("mLock")
private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();
private final PowerTracker mPowerTracker;
/**
* Helper set to avoid too much GC churn from frequent calls to
* {@link #maybeReportNewChargingStateLocked()}.
*/
private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
public BatteryController(JobSchedulerService service) {
super(service);
mPowerTracker = new PowerTracker();
mPowerTracker.startTracking();
}
@Override
@@ -54,8 +79,15 @@ public final class BatteryController extends RestrictingController {
final long nowElapsed = sElapsedRealtimeClock.millis();
mTrackedTasks.add(taskStatus);
taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY);
taskStatus.setChargingConstraintSatisfied(nowElapsed,
mService.isBatteryCharging() && mService.isBatteryNotLow());
if (taskStatus.hasChargingConstraint()) {
if (hasTopExemptionLocked(taskStatus)) {
taskStatus.setChargingConstraintSatisfied(nowElapsed,
mPowerTracker.isPowerConnected());
} else {
taskStatus.setChargingConstraintSatisfied(nowElapsed,
mService.isBatteryCharging() && mService.isBatteryNotLow());
}
}
taskStatus.setBatteryNotLowConstraintSatisfied(nowElapsed, mService.isBatteryNotLow());
}
}
@@ -65,10 +97,33 @@ public final class BatteryController extends RestrictingController {
maybeStartTrackingJobLocked(jobStatus, null);
}
@Override
@GuardedBy("mLock")
public void prepareForExecutionLocked(JobStatus jobStatus) {
if (DEBUG) {
Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
}
final int uid = jobStatus.getSourceUid();
if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) {
if (DEBUG) {
Slog.d(TAG, jobStatus.toShortString() + " is top started job");
}
mTopStartedJobs.add(jobStatus);
}
}
@Override
@GuardedBy("mLock")
public void unprepareFromExecutionLocked(JobStatus jobStatus) {
mTopStartedJobs.remove(jobStatus);
}
@Override
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
mTrackedTasks.remove(taskStatus);
mTopStartedJobs.remove(taskStatus);
}
}
@@ -90,33 +145,124 @@ public final class BatteryController extends RestrictingController {
});
}
@Override
@GuardedBy("mLock")
public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
if (prevBias == JobInfo.BIAS_TOP_APP || newBias == JobInfo.BIAS_TOP_APP) {
maybeReportNewChargingStateLocked();
}
}
@GuardedBy("mLock")
private boolean hasTopExemptionLocked(@NonNull JobStatus taskStatus) {
return mService.getUidBias(taskStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
|| mTopStartedJobs.contains(taskStatus);
}
@GuardedBy("mLock")
private void maybeReportNewChargingStateLocked() {
final boolean powerConnected = mPowerTracker.isPowerConnected();
final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow();
final boolean batteryNotLow = mService.isBatteryNotLow();
if (DEBUG) {
Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower);
Slog.d(TAG, "maybeReportNewChargingStateLocked: "
+ powerConnected + "/" + stablePower + "/" + batteryNotLow);
}
final long nowElapsed = sElapsedRealtimeClock.millis();
boolean reportChange = false;
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
reportChange |= ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
reportChange |= ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
if (ts.hasChargingConstraint()) {
if (hasTopExemptionLocked(ts)
&& ts.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
// If the job started while the app was on top or the app is currently on top,
// let the job run as long as there's power connected, even if the device isn't
// officially charging.
// For user requested/initiated jobs, users may be confused when the task stops
// running even though the device is plugged in.
// Low priority jobs don't need to be exempted.
if (ts.setChargingConstraintSatisfied(nowElapsed, powerConnected)) {
mChangedJobs.add(ts);
}
} else if (ts.setChargingConstraintSatisfied(nowElapsed, stablePower)) {
mChangedJobs.add(ts);
}
}
if (ts.hasBatteryNotLowConstraint()
&& ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow)) {
mChangedJobs.add(ts);
}
}
if (stablePower || batteryNotLow) {
// If one of our conditions has been satisfied, always schedule any newly ready jobs.
mStateChangedListener.onRunJobNow(null);
} else if (reportChange) {
} else if (mChangedJobs.size() > 0) {
// Otherwise, just let the job scheduler know the state has changed and take care of it
// as it thinks is best.
mStateChangedListener.onControllerStateChanged(mTrackedTasks);
mStateChangedListener.onControllerStateChanged(mChangedJobs);
}
mChangedJobs.clear();
}
private final class PowerTracker extends BroadcastReceiver {
/**
* Track whether there is power connected. It doesn't mean the device is charging.
* Use {@link JobSchedulerService#isBatteryCharging()} to determine if the device is
* charging.
*/
private boolean mPowerConnected;
PowerTracker() {
}
void startTracking() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
mContext.registerReceiver(this, filter);
// Initialize tracker state.
BatteryManagerInternal batteryManagerInternal =
LocalServices.getService(BatteryManagerInternal.class);
mPowerConnected = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
}
boolean isPowerConnected() {
return mPowerConnected;
}
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
final String action = intent.getAction();
if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
if (DEBUG) {
Slog.d(TAG, "Power connected @ " + sElapsedRealtimeClock.millis());
}
if (mPowerConnected) {
return;
}
mPowerConnected = true;
} else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
if (DEBUG) {
Slog.d(TAG, "Power disconnected @ " + sElapsedRealtimeClock.millis());
}
if (!mPowerConnected) {
return;
}
mPowerConnected = false;
}
maybeReportNewChargingStateLocked();
}
}
}
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
pw.println("Power connected: " + mPowerTracker.isPowerConnected());
pw.println("Stable power: " + (mService.isBatteryCharging() && mService.isBatteryNotLow()));
pw.println("Not low: " + mService.isBatteryNotLow());

View File

@@ -517,7 +517,7 @@ public final class ConnectivityController extends RestrictingController implemen
@GuardedBy("mLock")
@Override
public void onUidBiasChangedLocked(int uid, int newBias) {
public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
UidStats uidStats = mUidStats.get(uid);
if (uidStats != null && uidStats.baseBias != newBias) {
uidStats.baseBias = newBias;

View File

@@ -40,7 +40,6 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -81,9 +80,6 @@ public class PrefetchController extends StateController {
*/
@GuardedBy("mLock")
private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>();
/** Cached list of UIDs in the TOP state. */
@GuardedBy("mLock")
private final SparseBooleanArray mTopUids = new SparseBooleanArray();
private final ThresholdAlarmListener mThresholdAlarmListener;
/**
@@ -186,15 +182,9 @@ public class PrefetchController extends StateController {
@GuardedBy("mLock")
@Override
public void onUidBiasChangedLocked(int uid, int newBias) {
public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
final boolean isNowTop = newBias == JobInfo.BIAS_TOP_APP;
final boolean wasTop = mTopUids.get(uid);
if (isNowTop) {
mTopUids.put(uid, true);
} else {
// Delete entries of non-top apps so the set doesn't get too large.
mTopUids.delete(uid);
}
final boolean wasTop = prevBias == JobInfo.BIAS_TOP_APP;
if (isNowTop != wasTop) {
mHandler.obtainMessage(MSG_PROCESS_TOP_STATE_CHANGE, uid, 0).sendToTarget();
}
@@ -314,7 +304,8 @@ public class PrefetchController extends StateController {
// 3. The app is not open but has an active widget (we can't tell if a widget displays
// status/data, so this assumes the prefetch job is to update the data displayed on
// the widget).
final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid());
final boolean appIsOpen =
mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP;
final boolean satisfied;
if (!appIsOpen) {
final int userId = jobStatus.getSourceUserId();

View File

@@ -144,7 +144,7 @@ public abstract class StateController {
* important the UID is.
*/
@GuardedBy("mLock")
public void onUidBiasChangedLocked(int uid, int newBias) {
public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
}
protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) {

View File

@@ -0,0 +1,315 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.job.controllers;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.verify;
import android.app.AppGlobals;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ServiceInfo;
import android.os.BatteryManagerInternal;
import android.os.RemoteException;
import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@RunWith(AndroidJUnit4.class)
public class BatteryControllerTest {
private static final int CALLING_UID = 1000;
private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
private static final int SOURCE_USER_ID = 0;
private BatteryController mBatteryController;
private BroadcastReceiver mPowerReceiver;
private JobSchedulerService.Constants mConstants = new JobSchedulerService.Constants();
private int mSourceUid;
private MockitoSession mMockingSession;
@Mock
private Context mContext;
@Mock
private BatteryManagerInternal mBatteryManagerInternal;
@Mock
private JobSchedulerService mJobSchedulerService;
@Mock
private PackageManagerInternal mPackageManagerInternal;
@Before
public void setUp() {
mMockingSession = mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
.mockStatic(LocalServices.class)
.startMocking();
// Called in StateController constructor.
when(mJobSchedulerService.getTestableContext()).thenReturn(mContext);
when(mJobSchedulerService.getLock()).thenReturn(mJobSchedulerService);
when(mJobSchedulerService.getConstants()).thenReturn(mConstants);
// Called in BatteryController constructor.
doReturn(mBatteryManagerInternal)
.when(() -> LocalServices.getService(BatteryManagerInternal.class));
// Used in JobStatus.
doReturn(mPackageManagerInternal)
.when(() -> LocalServices.getService(PackageManagerInternal.class));
// Initialize real objects.
// Capture the listeners.
ArgumentCaptor<BroadcastReceiver> receiverCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
mBatteryController = new BatteryController(mJobSchedulerService);
verify(mContext).registerReceiver(receiverCaptor.capture(),
ArgumentMatchers.argThat(filter ->
filter.hasAction(Intent.ACTION_POWER_CONNECTED)
&& filter.hasAction(Intent.ACTION_POWER_DISCONNECTED)));
mPowerReceiver = receiverCaptor.getValue();
try {
mSourceUid = AppGlobals.getPackageManager().getPackageUid(SOURCE_PACKAGE, 0, 0);
// Need to do this since we're using a mock JS and not a real object.
doReturn(new ArraySet<>(new String[]{SOURCE_PACKAGE}))
.when(mJobSchedulerService).getPackagesForUidLocked(mSourceUid);
} catch (RemoteException e) {
fail(e.getMessage());
}
setPowerConnected(false);
}
@After
public void tearDown() {
if (mMockingSession != null) {
mMockingSession.finishMocking();
}
}
private void setBatteryNotLow(boolean notLow) {
doReturn(notLow).when(mJobSchedulerService).isBatteryNotLow();
synchronized (mBatteryController.mLock) {
mBatteryController.onBatteryStateChangedLocked();
}
waitForNonDelayedMessagesProcessed();
}
private void setCharging() {
doReturn(true).when(mJobSchedulerService).isBatteryCharging();
synchronized (mBatteryController.mLock) {
mBatteryController.onBatteryStateChangedLocked();
}
waitForNonDelayedMessagesProcessed();
}
private void setDischarging() {
doReturn(false).when(mJobSchedulerService).isBatteryCharging();
synchronized (mBatteryController.mLock) {
mBatteryController.onBatteryStateChangedLocked();
}
waitForNonDelayedMessagesProcessed();
}
private void setPowerConnected(boolean connected) {
Intent intent = new Intent(
connected ? Intent.ACTION_POWER_CONNECTED : Intent.ACTION_POWER_DISCONNECTED);
mPowerReceiver.onReceive(mContext, intent);
}
private void setUidBias(int uid, int bias) {
int prevBias = mJobSchedulerService.getUidBias(uid);
doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
synchronized (mBatteryController.mLock) {
mBatteryController.onUidBiasChangedLocked(uid, prevBias, bias);
}
}
private void trackJobs(JobStatus... jobs) {
for (JobStatus job : jobs) {
synchronized (mBatteryController.mLock) {
mBatteryController.maybeStartTrackingJobLocked(job, null);
}
}
}
private void waitForNonDelayedMessagesProcessed() {
JobSchedulerBackgroundThread.getHandler().runWithScissors(() -> {}, 15_000);
}
private JobInfo.Builder createBaseJobInfoBuilder(int jobId) {
return new JobInfo.Builder(jobId, new ComponentName(mContext, "TestBatteryJobService"));
}
private JobInfo.Builder createBaseJobInfoBuilder(int jobId, String pkgName) {
return new JobInfo.Builder(jobId, new ComponentName(pkgName, "TestBatteryJobService"));
}
private JobStatus createJobStatus(String testTag, String packageName, int callingUid,
JobInfo jobInfo) {
JobStatus js = JobStatus.createFromJobInfo(
jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
js.serviceInfo = mock(ServiceInfo.class);
// Make sure tests aren't passing just because the default bucket is likely ACTIVE.
js.setStandbyBucket(FREQUENT_INDEX);
return js;
}
@Test
public void testBatteryNotLow() {
JobStatus job1 = createJobStatus("testBatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
createBaseJobInfoBuilder(1).setRequiresBatteryNotLow(true).build());
JobStatus job2 = createJobStatus("testBatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
createBaseJobInfoBuilder(2).setRequiresBatteryNotLow(true).build());
setBatteryNotLow(false);
trackJobs(job1);
assertFalse(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
setBatteryNotLow(true);
assertTrue(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
trackJobs(job2);
assertTrue(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_BATTERY_NOT_LOW));
}
@Test
public void testCharging_BatteryNotLow() {
JobStatus job1 = createJobStatus("testCharging_BatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
createBaseJobInfoBuilder(1)
.setRequiresCharging(true)
.setRequiresBatteryNotLow(true).build());
JobStatus job2 = createJobStatus("testCharging_BatteryNotLow", SOURCE_PACKAGE, CALLING_UID,
createBaseJobInfoBuilder(2)
.setRequiresCharging(true)
.setRequiresBatteryNotLow(false).build());
setBatteryNotLow(true);
setDischarging();
trackJobs(job1, job2);
assertFalse(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
setCharging();
assertTrue(job1.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertTrue(job2.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
}
@Test
public void testTopPowerConnectedExemption() {
final int uid1 = mSourceUid;
final int uid2 = mSourceUid + 1;
final int uid3 = mSourceUid + 2;
JobStatus jobFg = createJobStatus("testTopPowerConnectedExemption", SOURCE_PACKAGE, uid1,
createBaseJobInfoBuilder(1).setRequiresCharging(true).build());
JobStatus jobFgRunner = createJobStatus("testTopPowerConnectedExemption",
SOURCE_PACKAGE, uid1,
createBaseJobInfoBuilder(2).setRequiresCharging(true).build());
JobStatus jobFgLow = createJobStatus("testTopPowerConnectedExemption", SOURCE_PACKAGE, uid1,
createBaseJobInfoBuilder(3)
.setRequiresCharging(true)
.setPriority(JobInfo.PRIORITY_LOW)
.build());
JobStatus jobBg = createJobStatus("testTopPowerConnectedExemption",
"some.background.app", uid2,
createBaseJobInfoBuilder(4, "some.background.app")
.setRequiresCharging(true)
.build());
JobStatus jobLateFg = createJobStatus("testTopPowerConnectedExemption",
"switch.to.fg", uid3,
createBaseJobInfoBuilder(5, "switch.to.fg").setRequiresCharging(true).build());
JobStatus jobLateFgLow = createJobStatus("testTopPowerConnectedExemption",
"switch.to.fg", uid3,
createBaseJobInfoBuilder(6, "switch.to.fg")
.setRequiresCharging(true)
.setPriority(JobInfo.PRIORITY_MIN)
.build());
setBatteryNotLow(false);
setDischarging();
setUidBias(uid1, JobInfo.BIAS_TOP_APP);
setUidBias(uid2, JobInfo.BIAS_DEFAULT);
setUidBias(uid3, JobInfo.BIAS_DEFAULT);
// Jobs are scheduled when power isn't connected.
setPowerConnected(false);
trackJobs(jobFg, jobFgLow, jobBg, jobLateFg, jobLateFgLow);
assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
// Power is connected. TOP app should be allowed to start job DEFAULT+ jobs.
setPowerConnected(true);
assertTrue(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
// Test that newly scheduled job of TOP app is correctly allowed to run.
trackJobs(jobFgRunner);
assertTrue(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
// Switch top app. New TOP app should be allowed to run job and the running job of
// previously TOP app should be allowed to continue to run.
synchronized (mBatteryController.mLock) {
mBatteryController.prepareForExecutionLocked(jobFgRunner);
}
setUidBias(uid1, JobInfo.BIAS_DEFAULT);
setUidBias(uid2, JobInfo.BIAS_DEFAULT);
setUidBias(uid3, JobInfo.BIAS_TOP_APP);
assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertTrue(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertTrue(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
setPowerConnected(false);
assertFalse(jobFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobFgRunner.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobBg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobLateFg.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
assertFalse(jobLateFgLow.isConstraintSatisfied(JobStatus.CONSTRAINT_CHARGING));
}
}

View File

@@ -200,8 +200,10 @@ public class PrefetchControllerTest {
}
private void setUidBias(int uid, int bias) {
int prevBias = mJobSchedulerService.getUidBias(uid);
doReturn(bias).when(mJobSchedulerService).getUidBias(uid);
synchronized (mPrefetchController.mLock) {
mPrefetchController.onUidBiasChangedLocked(uid, bias);
mPrefetchController.onUidBiasChangedLocked(uid, prevBias, bias);
}
}