Merge "Satisfy charging constraint earlier for top apps."
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user