summaryrefslogtreecommitdiff
path: root/apex
diff options
context:
space:
mode:
Diffstat (limited to 'apex')
-rw-r--r--apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java28
-rw-r--r--apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java7
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java6
-rw-r--r--apex/jobscheduler/service/aconfig/app_idle.aconfig11
-rw-r--r--apex/jobscheduler/service/aconfig/job.aconfig34
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java12
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java6
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java34
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java20
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java273
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java63
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java40
12 files changed, 467 insertions, 67 deletions
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index f6ae56f01758..5b3b876edd3a 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -32,11 +32,13 @@ import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import java.io.Closeable;
import java.io.IOException;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -153,6 +155,26 @@ public class BlobStoreManager {
private final Context mContext;
private final IBlobStoreManager mService;
+ // TODO: b/404309424 - Make these constants available using a test-api to avoid hardcoding
+ // them in tests.
+ /**
+ * The maximum allowed length for the package name, provided using
+ * {@link BlobStoreManager.Session#allowPackageAccess(String, byte[])}.
+ *
+ * This is the same limit that is already used for limiting the length of the package names
+ * at android.content.pm.parsing.FrameworkParsingPackageUtils#MAX_FILE_NAME_SIZE.
+ *
+ * @hide
+ */
+ public static final int MAX_PACKAGE_NAME_LENGTH = 223;
+ /**
+ * The maximum allowed length for the certificate, provided using
+ * {@link BlobStoreManager.Session#allowPackageAccess(String, byte[])}.
+ *
+ * @hide
+ */
+ public static final int MAX_CERTIFICATE_LENGTH = 32;
+
/** @hide */
public BlobStoreManager(@NonNull Context context, @NonNull IBlobStoreManager service) {
mContext = context;
@@ -786,6 +808,12 @@ public class BlobStoreManager {
*/
public void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate)
throws IOException {
+ Objects.requireNonNull(packageName);
+ Preconditions.checkArgument(packageName.length() <= MAX_PACKAGE_NAME_LENGTH,
+ "packageName is longer than " + MAX_PACKAGE_NAME_LENGTH + " chars");
+ Objects.requireNonNull(certificate);
+ Preconditions.checkArgument(certificate.length <= MAX_CERTIFICATE_LENGTH,
+ "certificate is longer than " + MAX_CERTIFICATE_LENGTH + " chars");
try {
mSession.allowPackageAccess(packageName, certificate);
} catch (ParcelableException e) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index ede29ec168c0..790d4e934317 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -16,6 +16,8 @@
package com.android.server.blob;
import static android.app.blob.BlobStoreManager.COMMIT_RESULT_ERROR;
+import static android.app.blob.BlobStoreManager.MAX_CERTIFICATE_LENGTH;
+import static android.app.blob.BlobStoreManager.MAX_PACKAGE_NAME_LENGTH;
import static android.app.blob.XmlTags.ATTR_CREATION_TIME_MS;
import static android.app.blob.XmlTags.ATTR_ID;
import static android.app.blob.XmlTags.ATTR_PACKAGE;
@@ -328,6 +330,11 @@ class BlobStoreSession extends IBlobStoreSession.Stub {
@NonNull byte[] certificate) {
assertCallerIsOwner();
Objects.requireNonNull(packageName, "packageName must not be null");
+ Preconditions.checkArgument(packageName.length() <= MAX_PACKAGE_NAME_LENGTH,
+ "packageName is longer than " + MAX_PACKAGE_NAME_LENGTH + " chars");
+ Objects.requireNonNull(certificate, "certificate must not be null");
+ Preconditions.checkArgument(certificate.length <= MAX_CERTIFICATE_LENGTH,
+ "certificate is longer than " + MAX_CERTIFICATE_LENGTH + " chars");
synchronized (mSessionLock) {
if (mState != STATE_OPENED) {
throw new IllegalStateException("Not allowed to change access type in state: "
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index cc2d104813e4..d48af2cccd59 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -810,7 +810,7 @@ public class JobInfo implements Parcelable {
/**
* <p class="caution"><strong>Note:</strong> Beginning with
- * {@link android.os.Build.VERSION_CODES#B}, this flag will be ignored and no longer
+ * {@link android.os.Build.VERSION_CODES#BAKLAVA}, this flag will be ignored and no longer
* function effectively, regardless of the calling app's target SDK version.
* Calling this method will always return {@code false}.
*
@@ -2137,9 +2137,9 @@ public class JobInfo implements Parcelable {
* Jobs marked as important-while-foreground are given {@link #PRIORITY_HIGH} by default.
*
* <p class="caution"><strong>Note:</strong> Beginning with
- * {@link android.os.Build.VERSION_CODES#B}, this flag will be ignored and no longer
+ * {@link android.os.Build.VERSION_CODES#BAKLAVA}, this flag will be ignored and no longer
* function effectively, regardless of the calling app's target SDK version.
- * {link #isImportantWhileForeground()} will always return {@code false}.
+ * {@link #isImportantWhileForeground()} will always return {@code false}.
* Apps should use {link #setExpedited(boolean)} with {@code true} to indicate
* that this job is important and needs to run as soon as possible.
*
diff --git a/apex/jobscheduler/service/aconfig/app_idle.aconfig b/apex/jobscheduler/service/aconfig/app_idle.aconfig
index 74d2a590086f..899341016148 100644
--- a/apex/jobscheduler/service/aconfig/app_idle.aconfig
+++ b/apex/jobscheduler/service/aconfig/app_idle.aconfig
@@ -28,3 +28,14 @@ flag {
description: "Adjust the default bucket evaluation parameters"
bug: "379909479"
}
+
+flag {
+ name: "persist_restore_to_rare_apps_list"
+ namespace: "backstage_power"
+ description: "Persist the list of apps which are put in the RARE bucket upon restore."
+ is_fixed_read_only: true
+ bug: "383766428"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/apex/jobscheduler/service/aconfig/job.aconfig b/apex/jobscheduler/service/aconfig/job.aconfig
index 86ed06bf4e3d..aae5bb31273b 100644
--- a/apex/jobscheduler/service/aconfig/job.aconfig
+++ b/apex/jobscheduler/service/aconfig/job.aconfig
@@ -105,4 +105,36 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
-} \ No newline at end of file
+}
+
+flag {
+ name: "include_trace_tag_in_job_name"
+ namespace: "backstage_power"
+ description: "Add the trace tag to the job name"
+ bug: "354795473"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "additional_quota_for_system_installer"
+ namespace: "backstage_power"
+ description: "Offer additional quota for system installer"
+ bug: "398264531"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "tune_quota_window_default_parameters"
+ namespace: "backstage_power"
+ description: "Tune default active/exempted bucket quota parameters"
+ bug: "401767691"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index f89b13dce307..44e4999ccf44 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -2995,6 +2995,8 @@ public class AlarmManagerService extends SystemService {
pw.print(Flags.FLAG_START_USER_BEFORE_SCHEDULED_ALARMS,
Flags.startUserBeforeScheduledAlarms());
pw.println();
+ pw.print(Flags.FLAG_ACQUIRE_WAKELOCK_BEFORE_SEND, Flags.acquireWakelockBeforeSend());
+ pw.println();
pw.decreaseIndent();
pw.println();
@@ -5367,6 +5369,11 @@ public class AlarmManagerService extends SystemService {
// to do any wakelock or stats tracking, so we have nothing
// left to do here but go on to the next thing.
mSendFinishCount++;
+ if (Flags.acquireWakelockBeforeSend() && mBroadcastRefCount == 0) {
+ // No other alarms are in-flight and this dispatch failed. We will
+ // acquire the wakelock again before the next dispatch.
+ mWakeLock.release();
+ }
return;
}
} else {
@@ -5404,6 +5411,11 @@ public class AlarmManagerService extends SystemService {
// stats management to do. It threw before we posted the delayed
// timeout message, so we're done here.
mListenerFinishCount++;
+ if (Flags.acquireWakelockBeforeSend() && mBroadcastRefCount == 0) {
+ // No other alarms are in-flight and this dispatch failed. We will
+ // acquire the wakelock again before the next dispatch.
+ mWakeLock.release();
+ }
return;
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index ebfda527001d..010006edc995 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -809,7 +809,11 @@ public final class JobServiceContext implements ServiceConnection {
if (!verifyCallerLocked(cb)) {
return;
}
-
+ if (mVerb != VERB_EXECUTING) {
+ // Any state other than executing means the
+ // job is in transient or stopped state
+ return;
+ }
executing = getRunningJobLocked();
}
if (executing != null && jobId == executing.getJobId()) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index abec170f3b7d..d52bbc245157 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -18,6 +18,7 @@ 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;
@@ -28,6 +29,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Log;
@@ -56,7 +58,10 @@ public final class DeviceIdleJobsController extends StateController {
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
- private static final long BACKGROUND_JOBS_DELAY = 3000;
+ /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
+ private static final String DIJC_CONSTANT_PREFIX = "dijc_";
+ private static final String KEY_BACKGROUND_JOBS_DELAY_MS =
+ DIJC_CONSTANT_PREFIX + "background_jobs_delay_ms";
static final int PROCESS_BACKGROUND_JOBS = 1;
@@ -78,6 +83,8 @@ public final class DeviceIdleJobsController extends StateController {
private int[] mDeviceIdleWhitelistAppIds;
private int[] mPowerSaveTempWhitelistAppIds;
+ private long mBackgroundJobsDelay;
+
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -128,6 +135,9 @@ public final class DeviceIdleJobsController extends StateController {
public DeviceIdleJobsController(JobSchedulerService service) {
super(service);
+ mBackgroundJobsDelay = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_jobSchedulerBackgroundJobsDelay);
+
mHandler = new DeviceIdleJobsDelayHandler(AppSchedulingModuleThread.get().getLooper());
// Register for device idle mode changes
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -165,7 +175,7 @@ public final class DeviceIdleJobsController extends StateController {
// When coming out of doze, process all foreground uids and EJs immediately,
// while others will be processed after a delay of 3 seconds.
mService.getJobStore().forEachJob(mShouldRushEvaluation, mDeviceIdleUpdateFunctor);
- mHandler.sendEmptyMessageDelayed(PROCESS_BACKGROUND_JOBS, BACKGROUND_JOBS_DELAY);
+ mHandler.sendEmptyMessageDelayed(PROCESS_BACKGROUND_JOBS, mBackgroundJobsDelay);
}
}
// Inform the job scheduler service about idle mode changes
@@ -237,6 +247,26 @@ public final class DeviceIdleJobsController extends StateController {
}
@Override
+ public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
+ @NonNull String key) {
+ switch (key) {
+ case KEY_BACKGROUND_JOBS_DELAY_MS:
+ mBackgroundJobsDelay = Math.max(0, properties.getLong(key, mBackgroundJobsDelay));
+ break;
+ }
+ }
+
+ @Override
+ public void dumpConstants(IndentingPrintWriter pw) {
+ pw.println();
+ pw.print(DeviceIdleJobsController.class.getSimpleName());
+ pw.println(":");
+ pw.increaseIndent();
+ pw.print(KEY_BACKGROUND_JOBS_DELAY_MS, mBackgroundJobsDelay).println();
+ pw.decreaseIndent();
+ }
+
+ @Override
public void dumpControllerStateLocked(final IndentingPrintWriter pw,
final Predicate<JobStatus> predicate) {
pw.println("Idle mode: " + mDeviceIdleMode);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 4b9d7364e27b..2d069f934d0d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -673,10 +673,22 @@ public final class JobStatus {
this.job = job;
- final String bnNamespace = namespace == null ? "" : "@" + namespace + "@";
- this.batteryName = this.sourceTag != null
- ? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName()
- : bnNamespace + job.getService().flattenToShortString();
+ StringBuilder batteryName = new StringBuilder();
+ if (com.android.server.job.Flags.includeTraceTagInJobName()) {
+ final String filteredTraceTag = this.getFilteredTraceTag();
+ if (filteredTraceTag != null) {
+ batteryName.append("#").append(filteredTraceTag).append("#");
+ }
+ }
+ if (namespace != null) {
+ batteryName.append("@").append(namespace).append("@");
+ }
+ if (sourceTag != null) {
+ batteryName.append(sourceTag).append(":").append(job.getService().getPackageName());
+ } else {
+ batteryName.append(job.getService().flattenToShortString());
+ }
+ this.batteryName = batteryName.toString();
final String componentPackage = job.getService().getPackageName();
mIsProxyJob = !this.sourcePackageName.equals(componentPackage);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 637c726a9bd1..6dd7521e4d43 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -36,7 +36,6 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.UidObserver;
-import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
@@ -54,6 +53,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -74,6 +74,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.server.AppSchedulingModuleThread;
import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
+import com.android.server.compat.PlatformCompat;
import com.android.server.job.ConstantsProto;
import com.android.server.job.Flags;
import com.android.server.job.JobSchedulerService;
@@ -157,6 +158,15 @@ public final class QuotaController extends StateController {
@Overridable // The change can be overridden in user build.
static final long OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS = 374323858L;
+ /**
+ * When enabled this change id overrides the default quota parameters adjustment.
+ */
+ @VisibleForTesting
+ @ChangeId
+ @Disabled // Disabled by default
+ @Overridable // The change can be overridden in user build.
+ static final long OVERRIDE_QUOTA_ADJUST_DEFAULT_CONSTANTS = 378129159L;
+
@VisibleForTesting
static class ExecutionStats {
/**
@@ -360,13 +370,13 @@ public final class QuotaController extends StateController {
/** How much time each app will have to run jobs within their standby bucket window. */
private final long[] mAllowedTimePerPeriodMs = new long[]{
- QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS,
0, // NEVER
QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
- QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
+ QcConstants.DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
};
/**
@@ -494,6 +504,9 @@ public final class QuotaController extends StateController {
private long mEjLimitAdditionSpecialMs = QcConstants.DEFAULT_EJ_LIMIT_ADDITION_SPECIAL_MS;
+ private long mAllowedTimePeriodAdditionaInstallerMs =
+ QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
/**
* The period of time used to calculate expedited job sessions. Apps can only have expedited job
* sessions totalling {@link #mEJLimitsMs}[bucket within this period of time (without factoring
@@ -533,6 +546,8 @@ public final class QuotaController extends StateController {
*/
private final SparseSetArray<String> mSystemInstallers = new SparseSetArray<>();
+ private final PlatformCompat mPlatformCompat;
+
/** An app has reached its quota. The message should contain a {@link UserPackage} object. */
@VisibleForTesting
static final int MSG_REACHED_TIME_QUOTA = 0;
@@ -584,6 +599,13 @@ public final class QuotaController extends StateController {
PowerAllowlistInternal pai = LocalServices.getService(PowerAllowlistInternal.class);
pai.registerTempAllowlistChangeListener(new TempAllowlistTracker());
+ mPlatformCompat = (PlatformCompat)
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
+ if (Flags.adjustQuotaDefaultConstants()) {
+ mPlatformCompat.registerListener(OVERRIDE_QUOTA_ADJUST_DEFAULT_CONSTANTS,
+ (packageName) -> handleQuotaDefaultConstantsCompatChange());
+ }
+
try {
ActivityManager.getService().registerUidObserver(new QcUidObserver(),
ActivityManager.UID_OBSERVER_PROCSTATE,
@@ -648,8 +670,9 @@ public final class QuotaController extends StateController {
final int uid = jobStatus.getSourceUid();
if ((!Flags.enforceQuotaPolicyToTopStartedJobs()
- || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- uid)) && mTopAppCache.get(uid)) {
+ || mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS, uid))
+ && mTopAppCache.get(uid)) {
if (DEBUG) {
Slog.d(TAG, jobStatus.toShortString() + " is top started job");
}
@@ -687,8 +710,8 @@ public final class QuotaController extends StateController {
}
}
if (!Flags.enforceQuotaPolicyToTopStartedJobs()
- || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- jobStatus.getSourceUid())) {
+ || mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS, jobStatus.getSourceUid())) {
mTopStartedJobs.remove(jobStatus);
}
}
@@ -802,8 +825,8 @@ public final class QuotaController extends StateController {
/** @return true if the job was started while the app was in the TOP state. */
private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) {
if (!Flags.enforceQuotaPolicyToTopStartedJobs()
- || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- jobStatus.getSourceUid())) {
+ || mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS, jobStatus.getSourceUid())) {
return mTopStartedJobs.contains(jobStatus);
}
@@ -1095,6 +1118,19 @@ public final class QuotaController extends StateController {
return baseLimitMs;
}
+ private long getAllowedTimePerPeriodMsLocked(final int userId, @NonNull final String pkgName,
+ final int standbyBucket) {
+ final long baseLimitMs = mAllowedTimePerPeriodMs[standbyBucket];
+ if (Flags.adjustQuotaDefaultConstants()
+ && !isCompatOverridedForQuotaConstantAdjustment()
+ && Flags.additionalQuotaForSystemInstaller()
+ && standbyBucket == EXEMPTED_INDEX
+ && mSystemInstallers.contains(userId, pkgName)) {
+ return baseLimitMs + mAllowedTimePeriodAdditionaInstallerMs;
+ }
+ return baseLimitMs;
+ }
+
/**
* Returns the amount of time, in milliseconds, until the package would have reached its
* duration quota, assuming it has a job counting towards its quota the entire time. This takes
@@ -1112,25 +1148,26 @@ public final class QuotaController extends StateController {
List<TimedEvent> events = mTimingEvents.get(userId, packageName);
final ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
+ final long allowedTimePerPeriodMs =
+ getAllowedTimePerPeriodMsLocked(userId, packageName, standbyBucket);
if (events == null || events.size() == 0) {
// Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
// essentially run until they reach the maximum limit.
- if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
+ if (stats.windowSizeMs == allowedTimePerPeriodMs) {
return mMaxExecutionTimeMs;
}
- return mAllowedTimePerPeriodMs[standbyBucket];
+ return allowedTimePerPeriodMs;
}
final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
- final long allowedTimePerPeriodMs = mAllowedTimePerPeriodMs[standbyBucket];
final long allowedTimeRemainingMs = allowedTimePerPeriodMs - stats.executionTimeInWindowMs;
final long maxExecutionTimeRemainingMs =
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;
// Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
// essentially run until they reach the maximum limit.
- if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
+ if (stats.windowSizeMs == allowedTimePerPeriodMs) {
return calculateTimeUntilQuotaConsumedLocked(
events, startMaxElapsed, maxExecutionTimeRemainingMs);
}
@@ -1270,7 +1307,8 @@ public final class QuotaController extends StateController {
appStats[standbyBucket] = stats;
}
if (refreshStatsIfOld) {
- final long bucketAllowedTimeMs = mAllowedTimePerPeriodMs[standbyBucket];
+ final long bucketAllowedTimeMs =
+ getAllowedTimePerPeriodMsLocked(userId, packageName, standbyBucket);
final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
final int jobCountLimit = mMaxBucketJobCounts[standbyBucket];
final int sessionCountLimit = mMaxBucketSessionCounts[standbyBucket];
@@ -1456,10 +1494,21 @@ public final class QuotaController extends StateController {
}
}
+ void handleQuotaDefaultConstantsCompatChange() {
+ synchronized (mLock) {
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+ mQcConstants.adjustDefaultBucketWindowSizes(isCompatEnabled);
+ mQcConstants.adjustDefaultEjLimits(isCompatEnabled);
+ mQcConstants.mShouldReevaluateConstraints = true;
+ onConstantsUpdatedLocked();
+ }
+ }
+
void processQuotaConstantsAdjustment() {
- if (Flags.adjustQuotaDefaultConstants()) {
- mQcConstants.adjustDefaultBucketWindowSizes();
- mQcConstants.adjustDefaultEjLimits();
+ if (Flags.adjustQuotaDefaultConstants()
+ && !isCompatOverridedForQuotaConstantAdjustment()) {
+ mQcConstants.adjustDefaultBucketWindowSizes(false);
+ mQcConstants.adjustDefaultEjLimits(false);
}
}
@@ -1488,6 +1537,11 @@ public final class QuotaController extends StateController {
}
}
+ private boolean isCompatOverridedForQuotaConstantAdjustment() {
+ return mPlatformCompat.isChangeEnabledByPackageName(
+ OVERRIDE_QUOTA_ADJUST_DEFAULT_CONSTANTS, "android", UserHandle.USER_SYSTEM);
+ }
+
private void incrementTimingSessionCountLocked(final int userId,
@NonNull final String packageName) {
final long now = sElapsedRealtimeClock.millis();
@@ -1845,9 +1899,10 @@ public final class QuotaController extends StateController {
final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats);
final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats);
final long remainingEJQuota = getRemainingEJExecutionTimeLocked(userId, packageName);
-
+ final long allowedTimePerPeriosMs =
+ getAllowedTimePerPeriodMsLocked(userId, packageName, standbyBucket);
final boolean inRegularQuota =
- stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs[standbyBucket]
+ stats.executionTimeInWindowMs < allowedTimePerPeriosMs
&& stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
&& isUnderJobCountQuota
&& isUnderTimingSessionCountQuota;
@@ -2671,7 +2726,8 @@ public final class QuotaController extends StateController {
@VisibleForTesting
int getProcessStateQuotaFreeThreshold(int uid) {
if (Flags.enforceQuotaPolicyToFgsJobs()
- && !CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS, uid)) {
+ && !mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS, uid)) {
return ActivityManager.PROCESS_STATE_BOUND_TOP;
}
@@ -3037,6 +3093,9 @@ public final class QuotaController extends StateController {
static final String KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
QC_CONSTANT_PREFIX + "allowed_time_per_period_restricted_ms";
@VisibleForTesting
+ static final String KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ QC_CONSTANT_PREFIX + "allowed_time_per_period_addition_installer_ms";
+ @VisibleForTesting
static final String KEY_IN_QUOTA_BUFFER_MS =
QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
@VisibleForTesting
@@ -3157,9 +3216,11 @@ public final class QuotaController extends StateController {
static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
- private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ // Legacy default time each app will have to run jobs within EXEMPTED bucket
+ private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
10 * 60 * 1000L; // 10 minutes
- private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ // Legacy default time each app will have to run jobs within ACTIVE bucket
+ private static final long DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
10 * 60 * 1000L; // 10 minutes
@@ -3169,14 +3230,28 @@ public final class QuotaController extends StateController {
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
10 * 60 * 1000L; // 10 minutes
+ private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ 10 * 60 * 1000L; // 10 minutes
+
+ // Current default time each app will have to run jobs within EXEMPTED bucket
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ 20 * 60 * 1000L; // 20 minutes
+ // Current default time each app will have to run jobs within ACTIVE bucket
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ 20 * 60 * 1000L; // 20 minutes
+ private static final long DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ 20 * 60 * 1000L; // 20 minutes
+
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
// Legacy default window size for EXEMPTED bucket
+ // EXEMPT apps can run jobs at any time
private static final long DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
// Legacy default window size for ACTIVE bucket
+ // ACTIVE apps can run jobs at any time
private static final long DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
// Legacy default window size for WORKING bucket
private static final long DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS =
2 * 60 * 60 * 1000L; // 2 hours
@@ -3193,6 +3268,13 @@ public final class QuotaController extends StateController {
private static final long DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS =
12 * 60 * 60 * 1000L; // 12 hours
+ // Latest default window size for EXEMPTED bucket.
+ private static final long DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS =
+ 40 * 60 * 1000L; // 40 minutes.
+ // Latest default window size for ACTIVE bucket.
+ private static final long DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS =
+ 60 * 60 * 1000L; // 60 minutes.
+
private static final long DEFAULT_WINDOW_SIZE_RARE_MS =
24 * 60 * 60 * 1000L; // 24 hours
private static final long DEFAULT_WINDOW_SIZE_RESTRICTED_MS =
@@ -3253,12 +3335,13 @@ public final class QuotaController extends StateController {
* bucket window.
*/
public long ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
/**
* How much time each app in the active bucket will have to run jobs within their standby
* bucket window.
*/
- public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
/**
* How much time each app in the working set bucket will have to run jobs within their
* standby bucket window.
@@ -3509,6 +3592,9 @@ public final class QuotaController extends StateController {
*/
public long EJ_LIMIT_ADDITION_INSTALLER_MS = DEFAULT_EJ_LIMIT_ADDITION_INSTALLER_MS;
+ public long ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
/**
* The period of time used to calculate expedited job sessions. Apps can only have expedited
* job sessions totalling EJ_LIMIT_<bucket>_MS within this period of time (without factoring
@@ -3548,11 +3634,45 @@ public final class QuotaController extends StateController {
*/
public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
- void adjustDefaultBucketWindowSizes() {
- WINDOW_SIZE_EXEMPTED_MS = DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
- WINDOW_SIZE_ACTIVE_MS = DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
- WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
- WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
+ void adjustDefaultBucketWindowSizes(boolean useLegacyQuotaConstants) {
+ if (useLegacyQuotaConstants) {
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+ WINDOW_SIZE_EXEMPTED_MS = DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS;
+ WINDOW_SIZE_WORKING_MS = DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS;
+ WINDOW_SIZE_FREQUENT_MS = DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS;
+ } else {
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+ WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+ WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
+ WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
+ }
+
+ mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = Math.min(MAX_PERIOD_MS,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
+ mAllowedTimePerPeriodMs[ACTIVE_INDEX] = Math.min(MAX_PERIOD_MS,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS));
mBucketPeriodsMs[EXEMPTED_INDEX] = Math.max(
mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
@@ -3566,12 +3686,22 @@ public final class QuotaController extends StateController {
mBucketPeriodsMs[FREQUENT_INDEX] = Math.max(
mAllowedTimePerPeriodMs[FREQUENT_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
+
+ mAllowedTimePeriodAdditionaInstallerMs =
+ Math.min(mBucketPeriodsMs[EXEMPTED_INDEX]
+ - mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
}
- void adjustDefaultEjLimits() {
- EJ_LIMIT_WORKING_MS = DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS;
- EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
- EJ_REWARD_INTERACTION_MS = DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS;
+ void adjustDefaultEjLimits(boolean useLegacyQuotaConstants) {
+ EJ_LIMIT_WORKING_MS = useLegacyQuotaConstants ? DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS
+ : DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS;
+ EJ_TOP_APP_TIME_CHUNK_SIZE_MS = useLegacyQuotaConstants
+ ? DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS :
+ DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+ EJ_REWARD_INTERACTION_MS = useLegacyQuotaConstants
+ ? DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS
+ : DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS;
// The limit must be in the range [15 minutes, active limit].
mEJLimitsMs[WORKING_INDEX] = Math.max(15 * MINUTE_IN_MILLIS,
@@ -3596,6 +3726,8 @@ public final class QuotaController extends StateController {
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+
switch (key) {
case KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS:
@@ -3603,6 +3735,7 @@ public final class QuotaController extends StateController {
case KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS:
+ case KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS:
case KEY_IN_QUOTA_BUFFER_MS:
case KEY_MAX_EXECUTION_TIME_MS:
case KEY_WINDOW_SIZE_ACTIVE_MS:
@@ -3762,7 +3895,8 @@ public final class QuotaController extends StateController {
case KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS:
// We don't need to re-evaluate execution stats or constraint status for this.
EJ_TOP_APP_TIME_CHUNK_SIZE_MS =
- properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+ properties.getLong(key,
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS :
DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS);
// Limit chunking to be in the range [1 millisecond, 15 minutes] per event.
@@ -3800,7 +3934,8 @@ public final class QuotaController extends StateController {
case KEY_EJ_REWARD_INTERACTION_MS:
// We don't need to re-evaluate execution stats or constraint status for this.
EJ_REWARD_INTERACTION_MS =
- properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+ properties.getLong(key,
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS :
DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS);
// Limit interaction reward to be in the range [5 seconds, 15 minutes] per
@@ -3841,13 +3976,15 @@ public final class QuotaController extends StateController {
}
mExecutionPeriodConstantsUpdated = true;
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
- KEY_IN_QUOTA_BUFFER_MS,
+ KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS, KEY_IN_QUOTA_BUFFER_MS,
KEY_MAX_EXECUTION_TIME_MS,
KEY_WINDOW_SIZE_EXEMPTED_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
KEY_WINDOW_SIZE_WORKING_MS,
@@ -3855,10 +3992,14 @@ public final class QuotaController extends StateController {
KEY_WINDOW_SIZE_RESTRICTED_MS);
ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
ALLOWED_TIME_PER_PERIOD_WORKING_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS);
@@ -3871,26 +4012,37 @@ public final class QuotaController extends StateController {
ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS
+ : DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
DEFAULT_MAX_EXECUTION_TIME_MS);
WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
- Flags.adjustQuotaDefaultConstants()
- ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
- DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS);
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
+ && Flags.tuneQuotaWindowDefaultParameters())
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
+ ? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS));
WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
- Flags.adjustQuotaDefaultConstants()
- ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
- DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS);
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
+ && Flags.tuneQuotaWindowDefaultParameters())
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
+ ? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS));
WINDOW_SIZE_WORKING_MS =
properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
- Flags.adjustQuotaDefaultConstants()
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS :
DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS);
WINDOW_SIZE_FREQUENT_MS =
properties.getLong(KEY_WINDOW_SIZE_FREQUENT_MS,
- Flags.adjustQuotaDefaultConstants()
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS :
DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS);
WINDOW_SIZE_RARE_MS = properties.getLong(KEY_WINDOW_SIZE_RARE_MS,
@@ -3995,6 +4147,18 @@ public final class QuotaController extends StateController {
mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
mShouldReevaluateConstraints = true;
}
+
+ if (Flags.additionalQuotaForSystemInstaller()) {
+ // The additions must be in the range
+ // [0 minutes, exempted window size - active limit].
+ long newAdditionInstallerMs = Math.max(0,
+ Math.min(mBucketPeriodsMs[EXEMPTED_INDEX] - newAllowedTimeExemptedMs,
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS));
+ if (mAllowedTimePeriodAdditionaInstallerMs != newAdditionInstallerMs) {
+ mAllowedTimePeriodAdditionaInstallerMs = newAdditionInstallerMs;
+ mShouldReevaluateConstraints = true;
+ }
+ }
}
private void updateRateLimitingConstantsLocked() {
@@ -4049,6 +4213,8 @@ public final class QuotaController extends StateController {
}
mEJLimitConstantsUpdated = true;
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
@@ -4063,7 +4229,7 @@ public final class QuotaController extends StateController {
EJ_LIMIT_ACTIVE_MS = properties.getLong(
KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS);
EJ_LIMIT_WORKING_MS = properties.getLong(
- KEY_EJ_LIMIT_WORKING_MS, Flags.adjustQuotaDefaultConstants()
+ KEY_EJ_LIMIT_WORKING_MS, Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS :
DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS);
EJ_LIMIT_FREQUENT_MS = properties.getLong(
@@ -4159,6 +4325,8 @@ public final class QuotaController extends StateController {
.println();
pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS).println();
+ pw.print(KEY_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS,
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS).println();
pw.print(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println();
pw.print(KEY_WINDOW_SIZE_EXEMPTED_MS, WINDOW_SIZE_EXEMPTED_MS).println();
pw.print(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println();
@@ -4335,6 +4503,11 @@ public final class QuotaController extends StateController {
}
@VisibleForTesting
+ long getAllowedTimePeriodAdditionInstallerMs() {
+ return mAllowedTimePeriodAdditionaInstallerMs;
+ }
+
+ @VisibleForTesting
long getEjLimitAdditionSpecialMs() {
return mEjLimitAdditionSpecialMs;
}
@@ -4435,6 +4608,8 @@ public final class QuotaController extends StateController {
+ ": " + Flags.enforceQuotaPolicyToFgsJobs());
pw.println(" " + Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS
+ ": " + Flags.enforceQuotaPolicyToTopStartedJobs());
+ pw.println(" " + Flags.FLAG_ADDITIONAL_QUOTA_FOR_SYSTEM_INSTALLER
+ + ": " + Flags.additionalQuotaForSystemInstaller());
pw.println();
pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis());
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index a8641ae43509..4acfebc536eb 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -38,6 +38,7 @@ import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageStatsManager;
import android.os.SystemClock;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -83,6 +84,9 @@ public class AppIdleHistory {
private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
private static final long ONE_MINUTE = 60 * 1000;
+ // Only keep the persisted restore-to-rare apps list for 2 days.
+ static final long RESTORE_TO_RARE_APPS_LIST_EXPIRY = ONE_MINUTE * 60 * 24 * 2;
+
static final int STANDBY_BUCKET_UNKNOWN = -1;
/**
@@ -277,6 +281,58 @@ public class AppIdleHistory {
writeScreenOnTime();
}
+ private File getRestoreToRareAppsListFile(int userId) {
+ return new File(getUserDirectory(userId), "restore_to_rare_apps_list");
+ }
+
+ public ArraySet<String> readRestoreToRareAppsList(int userId) {
+ File restoreToRareAppsListFile = getRestoreToRareAppsListFile(userId);
+ if (!restoreToRareAppsListFile.exists()) {
+ return null;
+ }
+
+ try (BufferedReader reader =
+ new BufferedReader(new FileReader(restoreToRareAppsListFile))) {
+ final ArraySet<String> appsList = new ArraySet<>();
+ final long restoreTime = Long.parseLong(reader.readLine());
+ if (System.currentTimeMillis() - restoreTime > RESTORE_TO_RARE_APPS_LIST_EXPIRY) {
+ // the apps list should only be kept around for 2 days
+ reader.close();
+ restoreToRareAppsListFile.delete();
+ return null;
+ }
+ String pkgName;
+ while ((pkgName = reader.readLine()) != null) {
+ appsList.add(pkgName);
+ }
+ return appsList;
+ } catch (IOException | NumberFormatException e) {
+ return null;
+ }
+ }
+
+ public void writeRestoreToRareAppsList(int userId, ArraySet<String> restoreAppsToRare) {
+ File fileHandle = getRestoreToRareAppsListFile(userId);
+ if (fileHandle.exists()) {
+ // don't update the persisted file - it should only be written once.
+ return;
+ }
+ AtomicFile restoreToRareAppsListFile = new AtomicFile(fileHandle);
+ FileOutputStream fos = null;
+ try {
+ fos = restoreToRareAppsListFile.startWrite();
+ final StringBuilder sb = new StringBuilder();
+ sb.append(System.currentTimeMillis()).append("\n");
+ for (String pkgName : restoreAppsToRare) {
+ sb.append(pkgName).append("\n");
+ }
+ fos.write(sb.toString().getBytes());
+ restoreToRareAppsListFile.finishWrite(fos);
+ } catch (IOException ioe) {
+ restoreToRareAppsListFile.failWrite(fos);
+ }
+ }
+
/**
* Mark the app as used and update the bucket if necessary. If there is a expiry time specified
* that's in the future, then the usage event is temporary and keeps the app in the specified
@@ -694,10 +750,13 @@ public class AppIdleHistory {
return appUsageHistory.bucketExpiryTimesMs.get(bucket, 0);
}
+ private File getUserDirectory(int userId) {
+ return new File(new File(mStorageDir, "users"), Integer.toString(userId));
+ }
+
@VisibleForTesting
File getUserFile(int userId) {
- return new File(new File(new File(mStorageDir, "users"),
- Integer.toString(userId)), APP_IDLE_FILENAME);
+ return new File(getUserDirectory(userId), APP_IDLE_FILENAME);
}
void clearLastUsedTimestamps(String packageName, int userId) {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 9871d713178e..b87b5ceebd98 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -264,6 +264,8 @@ public class AppStandbyController
@GuardedBy("mCarrierPrivilegedLock")
private boolean mHaveCarrierPrivilegedApps;
+ private final boolean mHasFeatureTelephonySubscription;
+
/** List of carrier-privileged apps that should be excluded from standby */
@GuardedBy("mCarrierPrivilegedLock")
private List<String> mCarrierPrivilegedApps;
@@ -603,6 +605,8 @@ public class AppStandbyController
mContext = mInjector.getContext();
mHandler = new AppStandbyHandler(mInjector.getLooper());
mPackageManager = mContext.getPackageManager();
+ mHasFeatureTelephonySubscription = mPackageManager.hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY_SUBSCRIPTION);
DeviceStateReceiver deviceStateReceiver = new DeviceStateReceiver();
IntentFilter deviceStates = new IntentFilter(BatteryManager.ACTION_CHARGING);
@@ -1515,7 +1519,7 @@ public class AppStandbyController
}
// Check this last, as it can be the most expensive check
- if (isCarrierApp(packageName)) {
+ if (mHasFeatureTelephonySubscription && isCarrierApp(packageName)) {
return STANDBY_BUCKET_EXEMPTED;
}
@@ -1702,10 +1706,18 @@ public class AppStandbyController
restoreAppToRare(packageName, userId, nowElapsed, reason);
}
// Clear out the list of restored apps that need to have their standby buckets adjusted
- // if they still haven't been installed eight hours after restore.
- // Note: if the device reboots within these first 8 hours, this list will be lost since it's
- // not persisted - this is the expected behavior for now and may be updated in the future.
- mHandler.postDelayed(() -> mAppsToRestoreToRare.remove(userId), 8 * ONE_HOUR);
+ // if they still haven't been installed two days after initial restore.
+ final long delayMillis = Flags.persistRestoreToRareAppsList()
+ ? AppIdleHistory.RESTORE_TO_RARE_APPS_LIST_EXPIRY : 8 * ONE_HOUR;
+ mHandler.postDelayed(() -> mAppsToRestoreToRare.remove(userId), delayMillis);
+
+ // Persist the file in case the device reboots within 2 days after the initial restore.
+ if (Flags.persistRestoreToRareAppsList()) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeRestoreToRareAppsList(
+ userId, mAppsToRestoreToRare.get(userId));
+ }
+ }
}
/** Adjust the standby bucket of the given package for the user to RARE. */
@@ -2268,6 +2280,22 @@ public class AppStandbyController
} else if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) {
clearAppIdleForPackage(pkgName, userId);
} else {
+ // Do a lazy read of the persisted list, if necessary.
+ if (Flags.persistRestoreToRareAppsList()
+ && mAppsToRestoreToRare.get(userId) == null) {
+ synchronized (mAppIdleLock) {
+ final ArraySet<String> restoredApps =
+ mAppIdleHistory.readRestoreToRareAppsList(userId);
+ if (restoredApps != null) {
+ mAppsToRestoreToRare.addAll(userId, restoredApps);
+ // Clear out the list of restored apps if they still haven't been
+ // installed in two days - at worst, we are allowing for up to
+ // 4 days for reinstallation (device reboots just before 2 days)
+ mHandler.postDelayed(() -> mAppsToRestoreToRare.remove(userId),
+ AppIdleHistory.RESTORE_TO_RARE_APPS_LIST_EXPIRY);
+ }
+ }
+ }
// Package was just added and it's not being replaced.
if (mAppsToRestoreToRare.contains(userId, pkgName)) {
restoreAppToRare(pkgName, userId, mInjector.elapsedRealtime(),
@@ -2450,6 +2478,8 @@ public class AppStandbyController
+ ": " + Flags.avoidIdleCheck());
pw.println(" " + Flags.FLAG_ADJUST_DEFAULT_BUCKET_ELEVATION_PARAMS
+ ": " + Flags.adjustDefaultBucketElevationParams());
+ pw.println(" " + Flags.FLAG_PERSIST_RESTORE_TO_RARE_APPS_LIST
+ + ": " + Flags.persistRestoreToRareAppsList());
pw.println();
synchronized (mCarrierPrivilegedLock) {