Add concurrency restriction at the USER level
Add a new layer of concurrency limitation at the USER level. Jobs that
are from the background users will be restricted at a maximum
concurrency.
Bug: 175643251
Test: atest JobSchedulerServiceTest
Test: atest WorkCountTrackerTest
Test: atest WorkTypeConfigTest
Test: atest JobCurrencyManagerTest
Test: atest GracePeriodObserverTest
Test: atest JobConcurrencyManagerTest
Test: atest CtsJobSchedulerTestCases
Test: atest FrameworksServicesTests:PrioritySchedulingTest
Change-Id: I82932355d30ac48dd40adb420524483908f21a3e
(cherry picked from commit e30d7fc004)
This commit is contained in:
@@ -16,33 +16,43 @@
|
||||
|
||||
package com.android.server.job;
|
||||
|
||||
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.UserSwitchObserver;
|
||||
import android.app.job.JobInfo;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.util.ArraySet;
|
||||
import android.util.IndentingPrintWriter;
|
||||
import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseIntArray;
|
||||
import android.util.SparseLongArray;
|
||||
import android.util.TimeUtils;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.app.procstats.ProcessStats;
|
||||
import com.android.internal.util.StatLogger;
|
||||
import com.android.server.JobSchedulerBackgroundThread;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.job.controllers.JobStatus;
|
||||
import com.android.server.job.controllers.StateController;
|
||||
import com.android.server.pm.UserManagerInternal;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -66,12 +76,14 @@ class JobConcurrencyManager {
|
||||
static final int WORK_TYPE_NONE = 0;
|
||||
static final int WORK_TYPE_TOP = 1 << 0;
|
||||
static final int WORK_TYPE_BG = 1 << 1;
|
||||
private static final int NUM_WORK_TYPES = 2;
|
||||
static final int WORK_TYPE_BGUSER = 1 << 2;
|
||||
private static final int NUM_WORK_TYPES = 3;
|
||||
|
||||
@IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
|
||||
WORK_TYPE_NONE,
|
||||
WORK_TYPE_TOP,
|
||||
WORK_TYPE_BG
|
||||
WORK_TYPE_BG,
|
||||
WORK_TYPE_BGUSER
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface WorkType {
|
||||
@@ -98,22 +110,26 @@ class JobConcurrencyManager {
|
||||
// defaultMin
|
||||
List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)),
|
||||
// defaultMax
|
||||
List.of(Pair.create(WORK_TYPE_BG, 6))),
|
||||
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
|
||||
),
|
||||
new WorkTypeConfig("screen_on_moderate", 8,
|
||||
// defaultMin
|
||||
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
|
||||
// defaultMax
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4))),
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
|
||||
),
|
||||
new WorkTypeConfig("screen_on_low", 5,
|
||||
// defaultMin
|
||||
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
|
||||
// defaultMax
|
||||
List.of(Pair.create(WORK_TYPE_BG, 1))),
|
||||
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
|
||||
),
|
||||
new WorkTypeConfig("screen_on_critical", 5,
|
||||
// defaultMin
|
||||
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
|
||||
// defaultMax
|
||||
List.of(Pair.create(WORK_TYPE_BG, 1)))
|
||||
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
|
||||
)
|
||||
);
|
||||
private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
|
||||
new WorkConfigLimitsPerMemoryTrimLevel(
|
||||
@@ -121,22 +137,26 @@ class JobConcurrencyManager {
|
||||
// defaultMin
|
||||
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
|
||||
// defaultMax
|
||||
List.of(Pair.create(WORK_TYPE_BG, 6))),
|
||||
List.of(Pair.create(WORK_TYPE_BG, 6), Pair.create(WORK_TYPE_BGUSER, 4))
|
||||
),
|
||||
new WorkTypeConfig("screen_off_moderate", 10,
|
||||
// defaultMin
|
||||
List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
|
||||
// defaultMax
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4))),
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2))
|
||||
),
|
||||
new WorkTypeConfig("screen_off_low", 5,
|
||||
// defaultMin
|
||||
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
|
||||
// defaultMax
|
||||
List.of(Pair.create(WORK_TYPE_BG, 1))),
|
||||
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
|
||||
),
|
||||
new WorkTypeConfig("screen_off_critical", 5,
|
||||
// defaultMin
|
||||
List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
|
||||
// defaultMax
|
||||
List.of(Pair.create(WORK_TYPE_BG, 1)))
|
||||
List.of(Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1))
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -171,6 +191,10 @@ class JobConcurrencyManager {
|
||||
"assignJobsToContexts",
|
||||
"refreshSystemState",
|
||||
});
|
||||
@VisibleForTesting
|
||||
GracePeriodObserver mGracePeriodObserver;
|
||||
@VisibleForTesting
|
||||
boolean mShouldRestrictBgUser;
|
||||
|
||||
interface Stats {
|
||||
int ASSIGN_JOBS_TO_CONTEXTS = 0;
|
||||
@@ -182,9 +206,13 @@ class JobConcurrencyManager {
|
||||
JobConcurrencyManager(JobSchedulerService service) {
|
||||
mService = service;
|
||||
mLock = mService.mLock;
|
||||
mContext = service.getContext();
|
||||
mContext = service.getTestableContext();
|
||||
|
||||
mHandler = JobSchedulerBackgroundThread.getHandler();
|
||||
|
||||
mGracePeriodObserver = new GracePeriodObserver(mContext);
|
||||
mShouldRestrictBgUser = mContext.getResources().getBoolean(
|
||||
R.bool.config_jobSchedulerRestrictBackgroundUser);
|
||||
}
|
||||
|
||||
public void onSystemReady() {
|
||||
@@ -193,10 +221,18 @@ class JobConcurrencyManager {
|
||||
final IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
mContext.registerReceiver(mReceiver, filter);
|
||||
try {
|
||||
ActivityManager.getService().registerUserSwitchObserver(mGracePeriodObserver, TAG);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
|
||||
onInteractiveStateChanged(mPowerManager.isInteractive());
|
||||
}
|
||||
|
||||
void onUserRemoved(int userId) {
|
||||
mGracePeriodObserver.onUserRemoved(userId);
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -224,7 +260,7 @@ class JobConcurrencyManager {
|
||||
Slog.d(TAG, "Interactive: " + interactive);
|
||||
}
|
||||
|
||||
final long nowRealtime = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
final long nowRealtime = sElapsedRealtimeClock.millis();
|
||||
if (interactive) {
|
||||
mLastScreenOnRealtime = nowRealtime;
|
||||
mEffectiveInteractiveState = true;
|
||||
@@ -261,7 +297,7 @@ class JobConcurrencyManager {
|
||||
if (mLastScreenOnRealtime > mLastScreenOffRealtime) {
|
||||
return;
|
||||
}
|
||||
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
|
||||
final long now = sElapsedRealtimeClock.millis();
|
||||
if ((mLastScreenOffRealtime + mScreenOffAdjustmentDelayMs) > now) {
|
||||
return;
|
||||
}
|
||||
@@ -723,6 +759,10 @@ class JobConcurrencyManager {
|
||||
pw.print(mLastMemoryTrimLevel);
|
||||
pw.println();
|
||||
|
||||
pw.print("User Grace Period: ");
|
||||
pw.print(mGracePeriodObserver.mGracePeriodExpiration);
|
||||
pw.println();
|
||||
|
||||
mStatLogger.dump(pw);
|
||||
} finally {
|
||||
pw.decreaseIndent();
|
||||
@@ -748,14 +788,44 @@ class JobConcurrencyManager {
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides whether a job is from the current foreground user or the equivalent.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean shouldRunAsFgUserJob(JobStatus job) {
|
||||
if (!mShouldRestrictBgUser) return true;
|
||||
int userId = job.getSourceUserId();
|
||||
UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
|
||||
UserInfo userInfo = um.getUserInfo(userId);
|
||||
|
||||
// If the user has a parent user (e.g. a work profile of another user), the user should be
|
||||
// treated equivalent as its parent user.
|
||||
if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
|
||||
&& userInfo.profileGroupId != userId) {
|
||||
userId = userInfo.profileGroupId;
|
||||
userInfo = um.getUserInfo(userId);
|
||||
}
|
||||
|
||||
int currentUser = LocalServices.getService(ActivityManagerInternal.class)
|
||||
.getCurrentUserId();
|
||||
// A user is treated as foreground user if any of the followings is true:
|
||||
// 1. The user is current user
|
||||
// 2. The user is primary user
|
||||
// 3. The user's grace period has not expired
|
||||
return currentUser == userId || userInfo.isPrimary()
|
||||
|| mGracePeriodObserver.isWithinGracePeriodForUser(userId);
|
||||
}
|
||||
|
||||
int getJobWorkTypes(@NonNull JobStatus js) {
|
||||
int classification = 0;
|
||||
// TODO(171305774): create dedicated work type for EJ and FGS
|
||||
if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
|
||||
|| js.shouldTreatAsExpeditedJob()) {
|
||||
classification |= WORK_TYPE_TOP;
|
||||
} else {
|
||||
} else if (shouldRunAsFgUserJob(js)) {
|
||||
classification |= WORK_TYPE_BG;
|
||||
} else {
|
||||
classification |= WORK_TYPE_BGUSER;
|
||||
}
|
||||
return classification;
|
||||
}
|
||||
@@ -766,8 +836,12 @@ class JobConcurrencyManager {
|
||||
CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_";
|
||||
private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_";
|
||||
private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
|
||||
private static final String KEY_PREFIX_MAX_BGUSER =
|
||||
CONFIG_KEY_PREFIX_CONCURRENCY + "max_bguser_";
|
||||
private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
|
||||
private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
|
||||
private static final String KEY_PREFIX_MIN_BGUSER =
|
||||
CONFIG_KEY_PREFIX_CONCURRENCY + "min_bguser_";
|
||||
private final String mConfigIdentifier;
|
||||
|
||||
private int mMaxTotal;
|
||||
@@ -815,6 +889,10 @@ class JobConcurrencyManager {
|
||||
properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
|
||||
mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
|
||||
mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
|
||||
final int maxBgUser = Math.max(1, Math.min(mMaxTotal,
|
||||
properties.getInt(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
|
||||
mDefaultMaxAllowedSlots.get(WORK_TYPE_BGUSER, mMaxTotal))));
|
||||
mMaxAllowedSlots.put(WORK_TYPE_BGUSER, maxBgUser);
|
||||
|
||||
int remaining = mMaxTotal;
|
||||
mMinReservedSlots.clear();
|
||||
@@ -829,6 +907,12 @@ class JobConcurrencyManager {
|
||||
properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier,
|
||||
mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
|
||||
mMinReservedSlots.put(WORK_TYPE_BG, minBg);
|
||||
remaining -= minBg;
|
||||
// Ensure bg user is in the range [0, min(maxBgUser, remaining)]
|
||||
final int minBgUser = Math.max(0, Math.min(Math.min(maxBgUser, remaining),
|
||||
properties.getInt(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
|
||||
mDefaultMinReservedSlots.get(WORK_TYPE_BGUSER, 0))));
|
||||
mMinReservedSlots.put(WORK_TYPE_BGUSER, minBgUser);
|
||||
}
|
||||
|
||||
int getMaxTotal() {
|
||||
@@ -853,6 +937,10 @@ class JobConcurrencyManager {
|
||||
.println();
|
||||
pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
|
||||
.println();
|
||||
pw.print(KEY_PREFIX_MIN_BGUSER + mConfigIdentifier,
|
||||
mMinReservedSlots.get(WORK_TYPE_BGUSER)).println();
|
||||
pw.print(KEY_PREFIX_MAX_BGUSER + mConfigIdentifier,
|
||||
mMaxAllowedSlots.get(WORK_TYPE_BGUSER)).println();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,6 +960,58 @@ class JobConcurrencyManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class keeps the track of when a user's grace period expires.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static class GracePeriodObserver extends UserSwitchObserver {
|
||||
// Key is UserId and Value is the time when grace period expires
|
||||
@VisibleForTesting
|
||||
final SparseLongArray mGracePeriodExpiration = new SparseLongArray();
|
||||
private int mCurrentUserId;
|
||||
@VisibleForTesting
|
||||
int mGracePeriod;
|
||||
private final UserManagerInternal mUserManagerInternal;
|
||||
final Object mLock = new Object();
|
||||
|
||||
|
||||
GracePeriodObserver(Context context) {
|
||||
mCurrentUserId = LocalServices.getService(ActivityManagerInternal.class)
|
||||
.getCurrentUserId();
|
||||
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
|
||||
mGracePeriod = Math.max(0, context.getResources().getInteger(
|
||||
R.integer.config_jobSchedulerUserGracePeriod));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserSwitchComplete(int newUserId) {
|
||||
final long expiration = sElapsedRealtimeClock.millis() + mGracePeriod;
|
||||
synchronized (mLock) {
|
||||
if (mCurrentUserId != UserHandle.USER_NULL
|
||||
&& mUserManagerInternal.exists(mCurrentUserId)) {
|
||||
mGracePeriodExpiration.append(mCurrentUserId, expiration);
|
||||
}
|
||||
mGracePeriodExpiration.delete(newUserId);
|
||||
mCurrentUserId = newUserId;
|
||||
}
|
||||
}
|
||||
|
||||
void onUserRemoved(int userId) {
|
||||
synchronized (mLock) {
|
||||
mGracePeriodExpiration.delete(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public boolean isWithinGracePeriodForUser(int userId) {
|
||||
synchronized (mLock) {
|
||||
return userId == mCurrentUserId
|
||||
|| sElapsedRealtimeClock.millis()
|
||||
< mGracePeriodExpiration.get(userId, Long.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class decides, taking into account the current {@link WorkTypeConfig} and how many jobs
|
||||
* are running/pending, how many more job can start.
|
||||
@@ -900,12 +1040,16 @@ class JobConcurrencyManager {
|
||||
mConfigNumReservedSlots.put(WORK_TYPE_TOP,
|
||||
workTypeConfig.getMinReserved(WORK_TYPE_TOP));
|
||||
mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
|
||||
mConfigNumReservedSlots.put(WORK_TYPE_BGUSER,
|
||||
workTypeConfig.getMinReserved(WORK_TYPE_BGUSER));
|
||||
mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
|
||||
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
|
||||
mConfigAbsoluteMaxSlots.put(WORK_TYPE_BGUSER, workTypeConfig.getMax(WORK_TYPE_BGUSER));
|
||||
|
||||
mNumUnspecialized = mConfigMaxTotal;
|
||||
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
|
||||
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
|
||||
mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BGUSER);
|
||||
mNumUnspecializedRemaining = mConfigMaxTotal;
|
||||
for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
|
||||
mNumUnspecializedRemaining -= Math.max(mNumRunningJobs.valueAt(i),
|
||||
@@ -937,6 +1081,9 @@ class JobConcurrencyManager {
|
||||
if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
|
||||
mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1);
|
||||
}
|
||||
if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
|
||||
mNumPendingJobs.put(WORK_TYPE_BGUSER, mNumPendingJobs.get(WORK_TYPE_BGUSER) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void stageJob(@WorkType int workType) {
|
||||
@@ -1029,6 +1176,11 @@ class JobConcurrencyManager {
|
||||
int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
|
||||
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
|
||||
mNumUnspecialized -= resBg;
|
||||
final int numBgUser = mNumRunningJobs.get(WORK_TYPE_BGUSER)
|
||||
+ mNumPendingJobs.get(WORK_TYPE_BGUSER);
|
||||
int resBgUser = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BGUSER), numBgUser);
|
||||
mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser);
|
||||
mNumUnspecialized -= resBgUser;
|
||||
|
||||
mNumUnspecializedRemaining = mNumUnspecialized;
|
||||
// Account for already running jobs after we've assigned the minimum number of slots.
|
||||
@@ -1048,6 +1200,14 @@ class JobConcurrencyManager {
|
||||
resBg += unspecializedAssigned;
|
||||
mNumUnspecializedRemaining -= extraRunning;
|
||||
}
|
||||
extraRunning = (mNumRunningJobs.get(WORK_TYPE_BGUSER) - resBgUser);
|
||||
if (extraRunning > 0) {
|
||||
unspecializedAssigned = Math.max(0,
|
||||
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER) - resBgUser,
|
||||
extraRunning));
|
||||
resBgUser += unspecializedAssigned;
|
||||
mNumUnspecializedRemaining -= extraRunning;
|
||||
}
|
||||
|
||||
// Assign remaining unspecialized based on ranking.
|
||||
unspecializedAssigned = Math.max(0,
|
||||
@@ -1060,6 +1220,12 @@ class JobConcurrencyManager {
|
||||
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG), numBg) - resBg));
|
||||
mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
|
||||
mNumUnspecializedRemaining -= unspecializedAssigned;
|
||||
unspecializedAssigned = Math.max(0,
|
||||
Math.min(mNumUnspecializedRemaining,
|
||||
Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER), numBgUser)
|
||||
- resBgUser));
|
||||
mNumActuallyReservedSlots.put(WORK_TYPE_BGUSER, resBgUser + unspecializedAssigned);
|
||||
mNumUnspecializedRemaining -= unspecializedAssigned;
|
||||
}
|
||||
|
||||
int canJobStart(int workTypes) {
|
||||
@@ -1081,6 +1247,16 @@ class JobConcurrencyManager {
|
||||
return WORK_TYPE_BG;
|
||||
}
|
||||
}
|
||||
if ((workTypes & WORK_TYPE_BGUSER) == WORK_TYPE_BGUSER) {
|
||||
final int maxAllowed = Math.min(
|
||||
mConfigAbsoluteMaxSlots.get(WORK_TYPE_BGUSER),
|
||||
mNumActuallyReservedSlots.get(WORK_TYPE_BGUSER)
|
||||
+ mNumUnspecializedRemaining);
|
||||
if (mNumRunningJobs.get(WORK_TYPE_BGUSER) + mNumStartingJobs.get(WORK_TYPE_BGUSER)
|
||||
< maxAllowed) {
|
||||
return WORK_TYPE_BGUSER;
|
||||
}
|
||||
}
|
||||
return WORK_TYPE_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -743,6 +743,7 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
mControllers.get(c).onUserRemovedLocked(userId);
|
||||
}
|
||||
}
|
||||
mConcurrencyManager.onUserRemoved(userId);
|
||||
} else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
|
||||
// Has this package scheduled any jobs, such that we will take action
|
||||
// if it were to be force-stopped?
|
||||
|
||||
@@ -3477,6 +3477,11 @@
|
||||
<!-- The alarm window (in milliseconds) that JobScheduler uses to enter the idle state -->
|
||||
<integer name="config_jobSchedulerIdleWindowSlop">300000</integer>
|
||||
|
||||
<!-- If true, jobs from background user will be restricted -->
|
||||
<bool name="config_jobSchedulerRestrictBackgroundUser">false</bool>
|
||||
<!-- The length of grace period after user becomes background user -->
|
||||
<integer name="config_jobSchedulerUserGracePeriod">60000</integer>
|
||||
|
||||
<!-- If true, all guest users created on the device will be ephemeral. -->
|
||||
<bool name="config_guestUserEphemeral">false</bool>
|
||||
|
||||
|
||||
@@ -2680,6 +2680,8 @@
|
||||
|
||||
<java-symbol type="integer" name="config_jobSchedulerInactivityIdleThreshold" />
|
||||
<java-symbol type="integer" name="config_jobSchedulerIdleWindowSlop" />
|
||||
<java-symbol type="bool" name="config_jobSchedulerRestrictBackgroundUser" />
|
||||
<java-symbol type="integer" name="config_jobSchedulerUserGracePeriod" />
|
||||
|
||||
<java-symbol type="style" name="Animation.ImmersiveModeConfirmation" />
|
||||
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.job.JobInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
|
||||
import com.android.server.job.controllers.JobStatus;
|
||||
import com.android.server.pm.UserManagerInternal;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public final class JobConcurrencyManagerTest {
|
||||
private static final int UNAVAILABLE_USER = 0;
|
||||
private JobConcurrencyManager mJobConcurrencyManager;
|
||||
private UserManagerInternal mUserManagerInternal;
|
||||
private ActivityManagerInternal mActivityManagerInternal;
|
||||
private int mNextUserId;
|
||||
private GracePeriodObserver mGracePeriodObserver;
|
||||
private Context mContext;
|
||||
private Resources mResources;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpOnce() {
|
||||
LocalServices.addService(UserManagerInternal.class, mock(UserManagerInternal.class));
|
||||
LocalServices.addService(
|
||||
ActivityManagerInternal.class, mock(ActivityManagerInternal.class));
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownOnce() {
|
||||
LocalServices.removeServiceForTest(UserManagerInternal.class);
|
||||
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
final JobSchedulerService jobSchedulerService = mock(JobSchedulerService.class);
|
||||
mContext = mock(Context.class);
|
||||
mResources = mock(Resources.class);
|
||||
doReturn(true).when(mResources).getBoolean(
|
||||
R.bool.config_jobSchedulerRestrictBackgroundUser);
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
doReturn(mContext).when(jobSchedulerService).getTestableContext();
|
||||
mJobConcurrencyManager = new JobConcurrencyManager(jobSchedulerService);
|
||||
mGracePeriodObserver = mock(GracePeriodObserver.class);
|
||||
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
|
||||
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
|
||||
mNextUserId = 10;
|
||||
mJobConcurrencyManager.mGracePeriodObserver = mGracePeriodObserver;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldRunAsFgUserJob_currentUser() {
|
||||
assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
|
||||
createJob(createCurrentUser(false))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldRunAsFgUserJob_currentProfile() {
|
||||
assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
|
||||
createJob(createCurrentUser(true))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldRunAsFgUserJob_primaryUser() {
|
||||
assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
|
||||
createJob(createPrimaryUser(false))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldRunAsFgUserJob_primaryProfile() {
|
||||
assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
|
||||
createJob(createPrimaryUser(true))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldRunAsFgUserJob_UnexpiredUser() {
|
||||
assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
|
||||
createJob(createUnexpiredUser(false))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldRunAsFgUserJob_UnexpiredProfile() {
|
||||
assertTrue(mJobConcurrencyManager.shouldRunAsFgUserJob(
|
||||
createJob(createUnexpiredUser(true))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldRunAsFgUserJob_restrictedUser() {
|
||||
assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
|
||||
createJob(createRestrictedUser(false))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldRunAsFgUserJob_restrictedProfile() {
|
||||
assertFalse(mJobConcurrencyManager.shouldRunAsFgUserJob(
|
||||
createJob(createRestrictedUser(true))));
|
||||
}
|
||||
|
||||
private UserInfo createCurrentUser(boolean isProfile) {
|
||||
final UserInfo ui = createNewUser();
|
||||
doReturn(ui.id).when(mActivityManagerInternal).getCurrentUserId();
|
||||
return isProfile ? createNewProfile(ui) : ui;
|
||||
}
|
||||
|
||||
private UserInfo createPrimaryUser(boolean isProfile) {
|
||||
final UserInfo ui = createNewUser();
|
||||
doReturn(true).when(ui).isPrimary();
|
||||
return isProfile ? createNewProfile(ui) : ui;
|
||||
}
|
||||
|
||||
private UserInfo createUnexpiredUser(boolean isProfile) {
|
||||
final UserInfo ui = createNewUser();
|
||||
doReturn(true).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
|
||||
return isProfile ? createNewProfile(ui) : ui;
|
||||
}
|
||||
|
||||
private UserInfo createRestrictedUser(boolean isProfile) {
|
||||
final UserInfo ui = createNewUser();
|
||||
doReturn(UNAVAILABLE_USER).when(mActivityManagerInternal).getCurrentUserId();
|
||||
doReturn(false).when(ui).isPrimary();
|
||||
doReturn(false).when(mGracePeriodObserver).isWithinGracePeriodForUser(ui.id);
|
||||
return isProfile ? createNewProfile(ui) : ui;
|
||||
}
|
||||
|
||||
private UserInfo createNewProfile(UserInfo parent) {
|
||||
final UserInfo ui = createNewUser();
|
||||
parent.profileGroupId = parent.id;
|
||||
ui.profileGroupId = parent.id;
|
||||
doReturn(true).when(ui).isProfile();
|
||||
return ui;
|
||||
}
|
||||
|
||||
private UserInfo createNewUser() {
|
||||
final UserInfo ui = mock(UserInfo.class);
|
||||
ui.id = mNextUserId++;
|
||||
doReturn(ui).when(mUserManagerInternal).getUserInfo(ui.id);
|
||||
ui.profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
|
||||
return ui;
|
||||
}
|
||||
|
||||
private static JobStatus createJob(UserInfo userInfo) {
|
||||
JobStatus jobStatus = JobStatus.createFromJobInfo(
|
||||
new JobInfo.Builder(1, new ComponentName("foo", "bar")).build(),
|
||||
userInfo.id * UserHandle.PER_USER_RANGE,
|
||||
null, userInfo.id, "JobConcurrencyManagerTest");
|
||||
return jobStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.content.Context;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.job.JobConcurrencyManager.GracePeriodObserver;
|
||||
import com.android.server.pm.UserManagerInternal;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public final class GracePeriodObserverTest {
|
||||
private GracePeriodObserver mGracePeriodObserver;
|
||||
private UserManagerInternal mUserManagerInternal;
|
||||
private static final int FIRST_USER = 0;
|
||||
|
||||
@BeforeClass
|
||||
public static void setUpOnce() {
|
||||
UserManagerInternal userManagerInternal = mock(UserManagerInternal.class);
|
||||
LocalServices.addService(UserManagerInternal.class, userManagerInternal);
|
||||
ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
|
||||
LocalServices.addService(ActivityManagerInternal.class, activityManagerInternal);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownOnce() {
|
||||
LocalServices.removeServiceForTest(UserManagerInternal.class);
|
||||
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
final Context context = ApplicationProvider.getApplicationContext();
|
||||
JobSchedulerService.sElapsedRealtimeClock =
|
||||
Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
|
||||
doReturn(FIRST_USER)
|
||||
.when(LocalServices.getService(ActivityManagerInternal.class)).getCurrentUserId();
|
||||
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
|
||||
doReturn(true).when(mUserManagerInternal).exists(FIRST_USER);
|
||||
mGracePeriodObserver = new GracePeriodObserver(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGracePeriod() throws RemoteException {
|
||||
final int oldUser = FIRST_USER;
|
||||
final int newUser = 10;
|
||||
doReturn(true).when(mUserManagerInternal).exists(newUser);
|
||||
mGracePeriodObserver.onUserSwitchComplete(newUser);
|
||||
assertTrue(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
|
||||
JobSchedulerService.sElapsedRealtimeClock =
|
||||
Clock.offset(JobSchedulerService.sElapsedRealtimeClock,
|
||||
Duration.ofMillis(mGracePeriodObserver.mGracePeriod));
|
||||
assertFalse(mGracePeriodObserver.isWithinGracePeriodForUser(oldUser));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCleanUp() throws RemoteException {
|
||||
final int removedUser = FIRST_USER;
|
||||
final int newUser = 10;
|
||||
mGracePeriodObserver.onUserSwitchComplete(newUser);
|
||||
|
||||
final int sizeBefore = mGracePeriodObserver.mGracePeriodExpiration.size();
|
||||
doReturn(false).when(mUserManagerInternal).exists(removedUser);
|
||||
|
||||
mGracePeriodObserver.onUserRemoved(removedUser);
|
||||
assertEquals(sizeBefore - 1, mGracePeriodObserver.mGracePeriodExpiration.size());
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.job;
|
||||
|
||||
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
|
||||
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
|
||||
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
|
||||
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
|
||||
|
||||
@@ -63,17 +64,27 @@ public class WorkCountTrackerTest {
|
||||
public final SparseIntArray running = new SparseIntArray();
|
||||
public final SparseIntArray pending = new SparseIntArray();
|
||||
|
||||
public void maybeEnqueueJobs(double startRatio, double fgJobRatio) {
|
||||
public void maybeEnqueueJobs(double startRatio, double fgJobRatio, double fgUserJobRatio) {
|
||||
// fgUserJobRatio should always be at least fgJobRatio, otherwise no WORK_TYPE_BG will
|
||||
// be enqueued.
|
||||
while (mRandom.nextDouble() < startRatio) {
|
||||
if (mRandom.nextDouble() < fgJobRatio) {
|
||||
final double random = mRandom.nextDouble();
|
||||
if (random < fgJobRatio) {
|
||||
pending.put(WORK_TYPE_TOP, pending.get(WORK_TYPE_TOP) + 1);
|
||||
} else {
|
||||
} else if (random < fgUserJobRatio) {
|
||||
pending.put(WORK_TYPE_BG, pending.get(WORK_TYPE_BG) + 1);
|
||||
} else {
|
||||
pending.put(WORK_TYPE_BGUSER, pending.get(WORK_TYPE_BGUSER) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void maybeFinishJobs(double stopRatio) {
|
||||
for (int i = running.get(WORK_TYPE_BGUSER); i > 0; i--) {
|
||||
if (mRandom.nextDouble() < stopRatio) {
|
||||
running.put(WORK_TYPE_BGUSER, running.get(WORK_TYPE_BGUSER) - 1);
|
||||
}
|
||||
}
|
||||
for (int i = running.get(WORK_TYPE_BG); i > 0; i--) {
|
||||
if (mRandom.nextDouble() < stopRatio) {
|
||||
running.put(WORK_TYPE_BG, running.get(WORK_TYPE_BG) - 1);
|
||||
@@ -120,8 +131,11 @@ public class WorkCountTrackerTest {
|
||||
while ((jobs.pending.get(WORK_TYPE_TOP) > 0
|
||||
&& mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
|
||||
|| (jobs.pending.get(WORK_TYPE_BG) > 0
|
||||
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) {
|
||||
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)
|
||||
|| (jobs.pending.get(WORK_TYPE_BGUSER) > 0
|
||||
&& mWorkCountTracker.canJobStart(WORK_TYPE_BGUSER) != WORK_TYPE_NONE)) {
|
||||
final boolean isStartingFg = mRandom.nextBoolean();
|
||||
final boolean isStartingFgUser = mRandom.nextBoolean();
|
||||
|
||||
if (isStartingFg) {
|
||||
if (jobs.pending.get(WORK_TYPE_TOP) > 0
|
||||
@@ -131,7 +145,7 @@ public class WorkCountTrackerTest {
|
||||
mWorkCountTracker.stageJob(WORK_TYPE_TOP);
|
||||
mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
|
||||
}
|
||||
} else {
|
||||
} else if (isStartingFgUser) {
|
||||
if (jobs.pending.get(WORK_TYPE_BG) > 0
|
||||
&& mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
|
||||
jobs.pending.put(WORK_TYPE_BG, jobs.pending.get(WORK_TYPE_BG) - 1);
|
||||
@@ -139,6 +153,14 @@ public class WorkCountTrackerTest {
|
||||
mWorkCountTracker.stageJob(WORK_TYPE_BG);
|
||||
mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
|
||||
}
|
||||
} else {
|
||||
if (jobs.pending.get(WORK_TYPE_BGUSER) > 0
|
||||
&& mWorkCountTracker.canJobStart(WORK_TYPE_BGUSER) != WORK_TYPE_NONE) {
|
||||
jobs.pending.put(WORK_TYPE_BGUSER, jobs.pending.get(WORK_TYPE_BGUSER) - 1);
|
||||
jobs.running.put(WORK_TYPE_BGUSER, jobs.running.get(WORK_TYPE_BGUSER) + 1);
|
||||
mWorkCountTracker.stageJob(WORK_TYPE_BGUSER);
|
||||
mWorkCountTracker.onJobStarted(WORK_TYPE_BGUSER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,10 +171,10 @@ public class WorkCountTrackerTest {
|
||||
private void checkRandom(Jobs jobs, int numTests, int totalMax,
|
||||
@NonNull List<Pair<Integer, Integer>> minLimits,
|
||||
@NonNull List<Pair<Integer, Integer>> maxLimits,
|
||||
double startRatio, double fgJobRatio, double stopRatio) {
|
||||
double startRatio, double fgJobRatio, double fgUserJobRatio, double stopRatio) {
|
||||
for (int i = 0; i < numTests; i++) {
|
||||
jobs.maybeFinishJobs(stopRatio);
|
||||
jobs.maybeEnqueueJobs(startRatio, fgJobRatio);
|
||||
jobs.maybeEnqueueJobs(startRatio, fgJobRatio, fgUserJobRatio);
|
||||
|
||||
recount(jobs, totalMax, minLimits, maxLimits);
|
||||
startPendingJobs(jobs);
|
||||
@@ -182,14 +204,21 @@ public class WorkCountTrackerTest {
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 6;
|
||||
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
|
||||
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
|
||||
final List<Pair<Integer, Integer>> minLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
|
||||
final double stopRatio = 0.1;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 50%
|
||||
// WORK_TYPE_BG -- 50%
|
||||
// WORK_TYPE_BGUSER -- 0%
|
||||
final double fgJobRatio = 0.5;
|
||||
final double fgUserJobRatio = 1;
|
||||
final double startRatio = 0.1;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, stopRatio);
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -198,14 +227,20 @@ public class WorkCountTrackerTest {
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 2;
|
||||
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
|
||||
final List<Pair<Integer, Integer>> minLimits = List.of();
|
||||
final double stopRatio = 0.5;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 50%
|
||||
// WORK_TYPE_BG -- 50%
|
||||
// WORK_TYPE_BGUSER -- 0%
|
||||
final double fgJobRatio = 0.5;
|
||||
final double fgUserJobRatio = 1;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, stopRatio);
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -214,14 +249,20 @@ public class WorkCountTrackerTest {
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 2;
|
||||
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
|
||||
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
|
||||
final double stopRatio = 0.5;
|
||||
final double fgJobRatio = 0.5;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 33%
|
||||
// WORK_TYPE_BG -- 33%
|
||||
// WORK_TYPE_BGUSER -- 33%
|
||||
final double fgJobRatio = 1 / 3.0;
|
||||
final double fgUserJobRatio = 2 / 3.0;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, stopRatio);
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -230,14 +271,20 @@ public class WorkCountTrackerTest {
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 10;
|
||||
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
|
||||
final List<Pair<Integer, Integer>> minLimits = List.of();
|
||||
final double stopRatio = 0.5;
|
||||
final double fgJobRatio = 0.5;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 33%
|
||||
// WORK_TYPE_BG -- 33%
|
||||
// WORK_TYPE_BGUSER -- 33%
|
||||
final double fgJobRatio = 1 / 3.0;
|
||||
final double fgUserJobRatio = 2 / 3.0;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, stopRatio);
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -246,14 +293,20 @@ public class WorkCountTrackerTest {
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 6;
|
||||
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
|
||||
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
|
||||
final double stopRatio = 0.5;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 10%
|
||||
// WORK_TYPE_BG -- 80%
|
||||
// WORK_TYPE_BGUSER -- 10%
|
||||
final double fgJobRatio = 0.1;
|
||||
final double fgUserJobRatio = 0.9;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, stopRatio);
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -262,14 +315,20 @@ public class WorkCountTrackerTest {
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 6;
|
||||
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
|
||||
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
|
||||
final double stopRatio = 0.5;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 90%
|
||||
// WORK_TYPE_BG -- 10%
|
||||
// WORK_TYPE_BGUSER -- 0%
|
||||
final double fgJobRatio = 0.9;
|
||||
final double fgUserJobRatio = 1;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, stopRatio);
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -278,14 +337,20 @@ public class WorkCountTrackerTest {
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 6;
|
||||
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
|
||||
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
|
||||
final double stopRatio = 0.4;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 10%
|
||||
// WORK_TYPE_BG -- 10%
|
||||
// WORK_TYPE_BGUSER -- 80%
|
||||
final double fgJobRatio = 0.1;
|
||||
final double fgUserJobRatio = 0.2;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, stopRatio);
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -294,14 +359,90 @@ public class WorkCountTrackerTest {
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 6;
|
||||
final List<Pair<Integer, Integer>> maxLimits = List.of(Pair.create(WORK_TYPE_BG, 4));
|
||||
final List<Pair<Integer, Integer>> minLimits = List.of(Pair.create(WORK_TYPE_BG, 2));
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
|
||||
final List<Pair<Integer, Integer>> minLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
|
||||
final double stopRatio = 0.4;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 90%
|
||||
// WORK_TYPE_BG -- 5%
|
||||
// WORK_TYPE_BGUSER -- 5%
|
||||
final double fgJobRatio = 0.9;
|
||||
final double fgUserJobRatio = 0.95;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, stopRatio);
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandom9() {
|
||||
final Jobs jobs = new Jobs();
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 6;
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
|
||||
final List<Pair<Integer, Integer>> minLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
|
||||
final double stopRatio = 0.5;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 0%
|
||||
// WORK_TYPE_BG -- 50%
|
||||
// WORK_TYPE_BGUSER -- 50%
|
||||
final double fgJobRatio = 0;
|
||||
final double fgUserJobRatio = 0.5;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandom10() {
|
||||
final Jobs jobs = new Jobs();
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 6;
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
|
||||
final List<Pair<Integer, Integer>> minLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
|
||||
final double stopRatio = 0.5;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 0%
|
||||
// WORK_TYPE_BG -- 10%
|
||||
// WORK_TYPE_BGUSER -- 90%
|
||||
final double fgJobRatio = 0;
|
||||
final double fgUserJobRatio = 0.1;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRandom11() {
|
||||
final Jobs jobs = new Jobs();
|
||||
|
||||
final int numTests = 5000;
|
||||
final int totalMax = 6;
|
||||
final List<Pair<Integer, Integer>> maxLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2));
|
||||
final List<Pair<Integer, Integer>> minLimits =
|
||||
List.of(Pair.create(WORK_TYPE_BG, 2), Pair.create(WORK_TYPE_BGUSER, 1));
|
||||
final double stopRatio = 0.5;
|
||||
// WorkType probabilities:
|
||||
// WORK_TYPE_TOP -- 0%
|
||||
// WORK_TYPE_BG -- 90%
|
||||
// WORK_TYPE_BGUSER -- 10%
|
||||
final double fgJobRatio = 0;
|
||||
final double fgUserJobRatio = 0.9;
|
||||
final double startRatio = 0.5;
|
||||
|
||||
checkRandom(jobs, numTests, totalMax, minLimits, maxLimits,
|
||||
startRatio, fgJobRatio, fgUserJobRatio, stopRatio);
|
||||
}
|
||||
|
||||
/** Used by the following tests */
|
||||
@@ -337,7 +478,7 @@ public class WorkCountTrackerTest {
|
||||
public void testBasic() {
|
||||
checkSimple(6,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
|
||||
/* run */ List.of(),
|
||||
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
|
||||
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 1)),
|
||||
@@ -345,7 +486,7 @@ public class WorkCountTrackerTest {
|
||||
|
||||
checkSimple(6,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
|
||||
/* run */ List.of(),
|
||||
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10)),
|
||||
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 6)),
|
||||
@@ -354,7 +495,7 @@ public class WorkCountTrackerTest {
|
||||
// When there are BG jobs pending, 2 (min-BG) jobs should run.
|
||||
checkSimple(6,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
|
||||
/* run */ List.of(),
|
||||
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 1)),
|
||||
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 5), Pair.create(WORK_TYPE_BG, 1)),
|
||||
@@ -383,6 +524,16 @@ public class WorkCountTrackerTest {
|
||||
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
|
||||
/* resPen */ List.of(Pair.create(WORK_TYPE_BG, 47)));
|
||||
|
||||
checkSimple(8,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 6)),
|
||||
/* run */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 4)),
|
||||
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 49), Pair.create(WORK_TYPE_BG, 49)),
|
||||
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 4)),
|
||||
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 47), Pair.create(WORK_TYPE_BG, 49))
|
||||
);
|
||||
|
||||
|
||||
checkSimple(8,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 6)),
|
||||
@@ -407,6 +558,14 @@ public class WorkCountTrackerTest {
|
||||
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
|
||||
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
|
||||
|
||||
checkSimple(8,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 1)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* run */ List.of(Pair.create(WORK_TYPE_BG, 6)),
|
||||
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 49)),
|
||||
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 6)),
|
||||
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 8), Pair.create(WORK_TYPE_BG, 49)));
|
||||
|
||||
checkSimple(6,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4)),
|
||||
@@ -425,6 +584,38 @@ public class WorkCountTrackerTest {
|
||||
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)),
|
||||
/* resRun */ List.of(Pair.create(WORK_TYPE_BG, 6)),
|
||||
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 10), Pair.create(WORK_TYPE_BG, 3)));
|
||||
|
||||
checkSimple(6,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
|
||||
/* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* pen */ List.of(Pair.create(WORK_TYPE_TOP, 10),
|
||||
Pair.create(WORK_TYPE_BG, 3),
|
||||
Pair.create(WORK_TYPE_BGUSER, 3)),
|
||||
/* resRun */ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* resPen */ List.of(Pair.create(WORK_TYPE_TOP, 6),
|
||||
Pair.create(WORK_TYPE_BG, 3),
|
||||
Pair.create(WORK_TYPE_BGUSER, 3)));
|
||||
|
||||
checkSimple(6,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 3)),
|
||||
/* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
|
||||
/* resRun */ List.of(
|
||||
Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 2)),
|
||||
/* resPen */ List.of(
|
||||
Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 1)));
|
||||
|
||||
checkSimple(6,
|
||||
/* min */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* max */ List.of(Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
|
||||
/* run */ List.of(Pair.create(WORK_TYPE_BG, 2)),
|
||||
/* pen */ List.of(Pair.create(WORK_TYPE_BG, 3), Pair.create(WORK_TYPE_BGUSER, 3)),
|
||||
/* resRun */ List.of(
|
||||
Pair.create(WORK_TYPE_BG, 4), Pair.create(WORK_TYPE_BGUSER, 1)),
|
||||
/* resPen */ List.of(
|
||||
Pair.create(WORK_TYPE_BG, 1), Pair.create(WORK_TYPE_BGUSER, 2)));
|
||||
}
|
||||
|
||||
/** Tests that the counter updates properly when jobs are stopped. */
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
package com.android.server.job;
|
||||
|
||||
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
|
||||
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BGUSER;
|
||||
import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
@@ -31,7 +33,6 @@ import androidx.test.runner.AndroidJUnit4;
|
||||
import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -44,8 +45,10 @@ public class WorkTypeConfigTest {
|
||||
private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
|
||||
private static final String KEY_MAX_TOP = "concurrency_max_top_test";
|
||||
private static final String KEY_MAX_BG = "concurrency_max_bg_test";
|
||||
private static final String KEY_MAX_BGUSER = "concurrency_max_bguser_test";
|
||||
private static final String KEY_MIN_TOP = "concurrency_min_top_test";
|
||||
private static final String KEY_MIN_BG = "concurrency_min_bg_test";
|
||||
private static final String KEY_MIN_BGUSER = "concurrency_min_bguser_test";
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
@@ -57,17 +60,21 @@ public class WorkTypeConfigTest {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BGUSER, "", false);
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BGUSER, "", false);
|
||||
}
|
||||
|
||||
private void check(@Nullable DeviceConfig.Properties config,
|
||||
int defaultTotal,
|
||||
@Nullable Pair<Integer, Integer> defaultTopLimits,
|
||||
@Nullable Pair<Integer, Integer> defaultBgLimits,
|
||||
@Nullable Pair<Integer, Integer> defaultBgUserLimits,
|
||||
boolean expectedValid, int expectedTotal,
|
||||
@NonNull Pair<Integer, Integer> expectedTopLimits,
|
||||
@NonNull Pair<Integer, Integer> expectedBgLimits) throws Exception {
|
||||
@NonNull Pair<Integer, Integer> expectedBgLimits,
|
||||
@NonNull Pair<Integer, Integer> expectedBgUserLimits) throws Exception {
|
||||
resetConfig();
|
||||
if (config != null) {
|
||||
DeviceConfig.setProperties(config);
|
||||
@@ -92,6 +99,14 @@ public class WorkTypeConfigTest {
|
||||
defaultMax.add(Pair.create(WORK_TYPE_BG, val));
|
||||
}
|
||||
}
|
||||
if (defaultBgUserLimits != null) {
|
||||
if ((val = defaultBgUserLimits.first) != null) {
|
||||
defaultMin.add(Pair.create(WORK_TYPE_BGUSER, val));
|
||||
}
|
||||
if ((val = defaultBgUserLimits.second) != null) {
|
||||
defaultMax.add(Pair.create(WORK_TYPE_BGUSER, val));
|
||||
}
|
||||
}
|
||||
|
||||
final WorkTypeConfig counts;
|
||||
try {
|
||||
@@ -112,40 +127,45 @@ public class WorkTypeConfigTest {
|
||||
|
||||
counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
|
||||
|
||||
Assert.assertEquals(expectedTotal, counts.getMaxTotal());
|
||||
Assert.assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP));
|
||||
Assert.assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP));
|
||||
Assert.assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG));
|
||||
Assert.assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG));
|
||||
assertEquals(expectedTotal, counts.getMaxTotal());
|
||||
assertEquals((int) expectedTopLimits.first, counts.getMinReserved(WORK_TYPE_TOP));
|
||||
assertEquals((int) expectedTopLimits.second, counts.getMax(WORK_TYPE_TOP));
|
||||
assertEquals((int) expectedBgLimits.first, counts.getMinReserved(WORK_TYPE_BG));
|
||||
assertEquals((int) expectedBgLimits.second, counts.getMax(WORK_TYPE_BG));
|
||||
assertEquals((int) expectedBgUserLimits.first, counts.getMinReserved(WORK_TYPE_BGUSER));
|
||||
assertEquals((int) expectedBgUserLimits.second, counts.getMax(WORK_TYPE_BGUSER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
// Tests with various combinations.
|
||||
check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1),
|
||||
/*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1));
|
||||
check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0),
|
||||
/*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1));
|
||||
check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0),
|
||||
/*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
|
||||
check(null, /*default*/ -1, null, Pair.create(-1, -1),
|
||||
/*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1));
|
||||
check(null, /*default*/ 5, null, Pair.create(5, 5),
|
||||
/*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
|
||||
check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5),
|
||||
/*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5));
|
||||
check(null, /*default*/ 4, null, Pair.create(6, 5),
|
||||
/*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4));
|
||||
check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1),
|
||||
/*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1));
|
||||
check(null, /*default*/ 15, null, Pair.create(15, 15),
|
||||
/*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15));
|
||||
check(null, /*default*/ 16, null, Pair.create(16, 16),
|
||||
/*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
|
||||
check(null, /*default*/ 20, null, Pair.create(20, 20),
|
||||
/*expected*/ false, 16, Pair.create(1, 16), Pair.create(15, 16));
|
||||
check(null, /*default*/ 20, null, Pair.create(16, 16),
|
||||
/*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16));
|
||||
check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1), Pair.create(0, 1),
|
||||
/*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1), Pair.create(0, 1));
|
||||
check(null, /*default*/ 5, Pair.create(5, null), Pair.create(0, 0), Pair.create(0, 0),
|
||||
/*expected*/ true, 5, Pair.create(5, 5), Pair.create(0, 1), Pair.create(0, 1));
|
||||
check(null, /*default*/ 0, Pair.create(5, null), Pair.create(0, 0), Pair.create(0, 0),
|
||||
/*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1), Pair.create(0, 1));
|
||||
check(null, /*default*/ -1, null, Pair.create(-1, -1), Pair.create(-1, -1),
|
||||
/*expected*/ false, 1, Pair.create(1, 1), Pair.create(0, 1), Pair.create(0, 1));
|
||||
check(null, /*default*/ 5, null, Pair.create(5, 5), Pair.create(0, 5),
|
||||
/*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5), Pair.create(0, 5));
|
||||
check(null, /*default*/ 6, Pair.create(1, null), Pair.create(6, 5), Pair.create(2, 1),
|
||||
/*expected*/ false, 6, Pair.create(1, 6), Pair.create(5, 5), Pair.create(0, 1));
|
||||
check(null, /*default*/ 4, null, Pair.create(6, 5), Pair.create(6, 5),
|
||||
/*expected*/ false, 4, Pair.create(1, 4), Pair.create(3, 4), Pair.create(0, 4));
|
||||
check(null, /*default*/ 5, Pair.create(4, null), Pair.create(1, 1), Pair.create(0, 5),
|
||||
/*expected*/ true, 5, Pair.create(4, 5), Pair.create(1, 1), Pair.create(0, 5));
|
||||
check(null, /*default*/ 5, Pair.create(4, null), Pair.create(0, 1), Pair.create(1, 5),
|
||||
/*expected*/ true, 5, Pair.create(4, 5), Pair.create(0, 1), Pair.create(1, 5));
|
||||
check(null, /*default*/ 15, null, Pair.create(15, 15), Pair.create(0, 15),
|
||||
/*expected*/ true, 15, Pair.create(1, 15), Pair.create(14, 15), Pair.create(0, 15));
|
||||
check(null, /*default*/ 16, null, Pair.create(16, 16), Pair.create(0, 16),
|
||||
/*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16), Pair.create(0, 16));
|
||||
check(null, /*default*/ 20, null, Pair.create(20, 20), Pair.create(10, 20),
|
||||
/*expected*/ false, 16,
|
||||
Pair.create(1, 16), Pair.create(15, 16), Pair.create(0, 16));
|
||||
check(null, /*default*/ 20, null, Pair.create(16, 16), Pair.create(0, 16),
|
||||
/*expected*/ true, 16, Pair.create(1, 16), Pair.create(15, 16), Pair.create(0, 16));
|
||||
|
||||
// Test for overriding with a setting string.
|
||||
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
||||
@@ -153,26 +173,26 @@ public class WorkTypeConfigTest {
|
||||
.setInt(KEY_MAX_BG, 4)
|
||||
.setInt(KEY_MIN_BG, 3)
|
||||
.build(),
|
||||
/*default*/ 9, null, Pair.create(9, 9),
|
||||
/*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4));
|
||||
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 2),
|
||||
/*expected*/ true, 5, Pair.create(1, 5), Pair.create(3, 4), Pair.create(0, 2));
|
||||
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
||||
.setInt(KEY_MAX_TOTAL, 5).build(),
|
||||
/*default*/ 9, null, Pair.create(9, 9),
|
||||
/*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5));
|
||||
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 0),
|
||||
/*expected*/ true, 5, Pair.create(1, 5), Pair.create(4, 5), Pair.create(0, 1));
|
||||
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
||||
.setInt(KEY_MAX_BG, 4).build(),
|
||||
/*default*/ 9, null, Pair.create(9, 9),
|
||||
/*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4));
|
||||
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 9),
|
||||
/*expected*/ true, 9, Pair.create(1, 9), Pair.create(4, 4), Pair.create(0, 9));
|
||||
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
||||
.setInt(KEY_MIN_BG, 3).build(),
|
||||
/*default*/ 9, null, Pair.create(9, 9),
|
||||
/*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9));
|
||||
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 6),
|
||||
/*expected*/ true, 9, Pair.create(1, 9), Pair.create(3, 9), Pair.create(0, 6));
|
||||
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
|
||||
.setInt(KEY_MAX_TOTAL, 20)
|
||||
.setInt(KEY_MAX_BG, 20)
|
||||
.setInt(KEY_MIN_BG, 8)
|
||||
.build(),
|
||||
/*default*/ 9, null, Pair.create(9, 9),
|
||||
/*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16));
|
||||
/*default*/ 9, null, Pair.create(9, 9), Pair.create(0, 8),
|
||||
/*expected*/ true, 16, Pair.create(1, 16), Pair.create(8, 16), Pair.create(0, 8));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user