diff options
| author | 2021-08-12 04:35:54 +0000 | |
|---|---|---|
| committer | 2021-08-12 04:35:54 +0000 | |
| commit | 7e5c35b51aa25b7942fa2a8412c2c0c8ffaa7a34 (patch) | |
| tree | 2da00dde37471ad9babbc1565926fac21bda0838 | |
| parent | 6b656aaef7f572f7bbca1656e7e9d60918b77870 (diff) | |
| parent | fc9bdda8161899d798b4024d17f42aba8ad92b46 (diff) | |
Merge "Integrate JobScheduler with TARE."
8 files changed, 170 insertions, 10 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index f0745f3bad00..26237c41f5dc 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -40,6 +40,7 @@ import android.app.usage.UsageStatsManager; import android.app.usage.UsageStatsManagerInternal; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -50,6 +51,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ServiceInfo; +import android.database.ContentObserver; import android.net.Uri; import android.os.BatteryStats; import android.os.BatteryStatsInternal; @@ -66,6 +68,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.WorkSource; import android.provider.DeviceConfig; +import android.provider.Settings; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -106,6 +109,7 @@ import com.android.server.job.controllers.QuotaController; import com.android.server.job.controllers.RestrictingController; import com.android.server.job.controllers.StateController; import com.android.server.job.controllers.StorageController; +import com.android.server.job.controllers.TareController; import com.android.server.job.controllers.TimeController; import com.android.server.job.restrictions.JobRestriction; import com.android.server.job.restrictions.ThermalStatusRestriction; @@ -250,6 +254,8 @@ public class JobSchedulerService extends com.android.server.SystemService private final DeviceIdleJobsController mDeviceIdleJobsController; /** Needed to get remaining quota time. */ private final QuotaController mQuotaController; + /** Needed to get max execution time and expedited-job allowance. */ + private final TareController mTareController; /** * List of restrictions. * Note: do not add to or remove from this list at runtime except in the constructor, because we @@ -344,15 +350,41 @@ public class JobSchedulerService extends com.android.server.SystemService // (ScheduledJobStateChanged and JobStatusDumpProto). public static final int RESTRICTED_INDEX = 5; - private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener { + private class ConstantsObserver extends ContentObserver + implements DeviceConfig.OnPropertiesChangedListener { + private final ContentResolver mContentResolver; + + ConstantsObserver(Handler handler, Context context) { + super(handler); + mContentResolver = context.getContentResolver(); + } + public void start() { DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER, JobSchedulerBackgroundThread.getExecutor(), this); + mContentResolver.registerContentObserver( + Settings.Global.getUriFor(Settings.Global.ENABLE_TARE), false, this); // Load all the constants. + synchronized (mLock) { + mConstants.updateSettingsConstantsLocked(mContentResolver); + } onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER)); } @Override + public void onChange(boolean selfChange) { + synchronized (mLock) { + if (mConstants.updateSettingsConstantsLocked(mContentResolver)) { + for (int controller = 0; controller < mControllers.size(); controller++) { + final StateController sc = mControllers.get(controller); + sc.onConstantsUpdatedLocked(); + } + onControllerStateChanged(null); + } + } + } + + @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { boolean apiQuotaScheduleUpdated = false; boolean concurrencyUpdated = false; @@ -482,6 +514,7 @@ public class JobSchedulerService extends com.android.server.SystemService public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS; @VisibleForTesting public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS; + private static final boolean DEFAULT_USE_TARE_POLICY = false; /** * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early. @@ -559,6 +592,11 @@ public class JobSchedulerService extends com.android.server.SystemService */ public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS; + /** + * If true, use TARE policy for job limiting. If false, use quotas. + */ + public boolean USE_TARE_POLICY = DEFAULT_USE_TARE_POLICY; + private void updateBatchingConstantsLocked() { MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt( DeviceConfig.NAMESPACE_JOB_SCHEDULER, @@ -637,6 +675,17 @@ public class JobSchedulerService extends com.android.server.SystemService DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)); } + private boolean updateSettingsConstantsLocked(ContentResolver contentResolver) { + boolean changed = false; + final boolean isTareEnabled = Settings.Global.getInt(contentResolver, + Settings.Global.ENABLE_TARE, Settings.Global.DEFAULT_ENABLE_TARE) == 1; + if (USE_TARE_POLICY != isTareEnabled) { + USE_TARE_POLICY = isTareEnabled; + changed = true; + } + return changed; + } + void dump(IndentingPrintWriter pw) { pw.println("Settings:"); pw.increaseIndent(); @@ -665,6 +714,8 @@ public class JobSchedulerService extends com.android.server.SystemService pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS) .println(); + pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY).println(); + pw.decreaseIndent(); } @@ -1130,9 +1181,12 @@ public class JobSchedulerService extends com.android.server.SystemService JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); // Return failure early if expedited job quota used up. - if (jobStatus.isRequestedExpeditedJob() - && !mQuotaController.isWithinEJQuotaLocked(jobStatus)) { - return JobScheduler.RESULT_FAILURE; + if (jobStatus.isRequestedExpeditedJob()) { + if ((mConstants.USE_TARE_POLICY && !mTareController.canScheduleEJ(jobStatus)) + || (!mConstants.USE_TARE_POLICY + && !mQuotaController.isWithinEJQuotaLocked(jobStatus))) { + return JobScheduler.RESULT_FAILURE; + } } // Give exemption if the source is in the foreground just now. @@ -1474,7 +1528,7 @@ public class JobSchedulerService extends com.android.server.SystemService mHandler = new JobHandler(context.getMainLooper()); mConstants = new Constants(); - mConstantsObserver = new ConstantsObserver(); + mConstantsObserver = new ConstantsObserver(mHandler, context); mJobSchedulerStub = new JobSchedulerStub(); mConcurrencyManager = new JobConcurrencyManager(this); @@ -1519,6 +1573,9 @@ public class JobSchedulerService extends com.android.server.SystemService new QuotaController(this, backgroundJobsController, connectivityController); mControllers.add(mQuotaController); mControllers.add(new ComponentController(this)); + mTareController = + new TareController(this, backgroundJobsController, connectivityController); + mControllers.add(mTareController); mRestrictiveControllers = new ArrayList<>(); mRestrictiveControllers.add(mBatteryController); @@ -2560,7 +2617,9 @@ public class JobSchedulerService extends com.android.server.SystemService public long getMaxJobExecutionTimeMs(JobStatus job) { synchronized (mLock) { return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, - mQuotaController.getMaxJobExecutionTimeMsLocked(job)); + mConstants.USE_TARE_POLICY + ? mTareController.getMaxJobExecutionTimeMsLocked(job) + : mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } } 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 c61a414c4859..5bdee5e636a9 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -54,6 +54,9 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.android.server.job.controllers.JobStatus; +import com.android.server.tare.EconomicPolicy; +import com.android.server.tare.EconomyManagerInternal; +import com.android.server.tare.JobSchedulerEconomicPolicy; /** * Handles client binding and lifecycle of a job. Jobs execute one at a time on an instance of this @@ -107,6 +110,7 @@ public final class JobServiceContext implements ServiceConnection { private final Context mContext; private final Object mLock; private final IBatteryStats mBatteryStats; + private final EconomyManagerInternal mEconomyManagerInternal; private final JobPackageTracker mJobPackageTracker; private final PowerManager mPowerManager; private PowerManager.WakeLock mWakeLock; @@ -211,6 +215,7 @@ public final class JobServiceContext implements ServiceConnection { mLock = service.getLock(); mService = service; mBatteryStats = batteryStats; + mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class); mJobPackageTracker = tracker; mCallbackHandler = new JobServiceHandler(looper); mJobConcurrencyManager = concurrencyManager; @@ -288,6 +293,11 @@ public final class JobServiceContext implements ServiceConnection { mWakeLock.setReferenceCounted(false); mWakeLock.acquire(); + // Note the start when we try to bind so that the app is charged for some processing + // even if binding fails. + mEconomyManagerInternal.noteInstantaneousEvent( + job.getSourceUserId(), job.getSourcePackageName(), + getStartActionId(job), String.valueOf(job.getJobId())); mVerb = VERB_BINDING; scheduleOpTimeOutLocked(); final Intent intent = new Intent().setComponent(job.getServiceComponent()); @@ -350,6 +360,9 @@ public final class JobServiceContext implements ServiceConnection { } catch (RemoteException e) { // Whatever. } + mEconomyManagerInternal.noteOngoingEventStarted( + job.getSourceUserId(), job.getSourcePackageName(), + getRunningActionId(job), String.valueOf(job.getJobId())); final String jobPackage = job.getSourcePackageName(); final int jobUserId = job.getSourceUserId(); UsageStatsManagerInternal usageStats = @@ -363,6 +376,22 @@ public final class JobServiceContext implements ServiceConnection { } } + @EconomicPolicy.AppAction + private static int getStartActionId(@NonNull JobStatus job) { + if (job.startedAsExpeditedJob || job.shouldTreatAsExpeditedJob()) { + return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_START; + } + return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_START; + } + + @EconomicPolicy.AppAction + private static int getRunningActionId(@NonNull JobStatus job) { + if (job.startedAsExpeditedJob || job.shouldTreatAsExpeditedJob()) { + return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING; + } + return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING; + } + /** * Used externally to query the running job. Will return null if there is no job running. */ @@ -982,6 +1011,15 @@ public final class JobServiceContext implements ServiceConnection { } catch (RemoteException e) { // Whatever. } + mEconomyManagerInternal.noteOngoingEventStopped( + mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(), + getRunningActionId(mRunningJob), String.valueOf(mRunningJob.getJobId())); + if (mParams.getStopReason() == JobParameters.STOP_REASON_TIMEOUT) { + mEconomyManagerInternal.noteInstantaneousEvent( + mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(), + JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT, + String.valueOf(mRunningJob.getJobId())); + } if (mWakeLock != null) { mWakeLock.release(); } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 321c0b334a54..607ed3cd6a35 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -661,7 +661,8 @@ public final class ConnectivityController extends RestrictingController implemen NetworkCapabilities capabilities, Constants constants) { // A restricted job that's out of quota MUST use an unmetered network. if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX - && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) { + && (!jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA) + || !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_TARE_WEALTH))) { final NetworkCapabilities.Builder builder = copyCapabilities(jobStatus.getJob().getRequiredNetwork()); builder.addCapability(NET_CAPABILITY_NOT_METERED); 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 844a480529af..3801885c9781 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 @@ -1141,7 +1141,7 @@ public final class JobStatus { * treated as an expedited job. */ public boolean shouldTreatAsExpeditedJob() { - return mExpeditedQuotaApproved && isRequestedExpeditedJob(); + return mExpeditedQuotaApproved && mExpeditedTareApproved && isRequestedExpeditedJob(); } /** @@ -1564,7 +1564,8 @@ public final class JobStatus { // sessions (exempt from dynamic restrictions), we need the additional check to ensure // that NEVER jobs don't run. // TODO: cleanup quota and standby bucket management so we don't need the additional checks - if ((!mReadyWithinQuota && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob()) + if (((!mReadyWithinQuota || !mReadyTareWealth) + && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob()) || getEffectiveStandbyBucket() == NEVER_INDEX) { return false; } 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 36afac8fec78..4ea46ebb752d 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 @@ -372,6 +372,9 @@ public final class QuotaController extends StateController { private final BackgroundJobsController mBackgroundJobsController; private final ConnectivityController mConnectivityController; + @GuardedBy("mLock") + private boolean mIsEnabled; + /** How much time each app will have to run jobs within their standby bucket window. */ private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; @@ -590,6 +593,7 @@ public final class QuotaController extends StateController { mQcConstants = new QcConstants(); mBackgroundJobsController = backgroundJobsController; mConnectivityController = connectivityController; + mIsEnabled = !mConstants.USE_TARE_POLICY; // Set up the app standby bucketing tracker AppStandbyInternal appStandby = LocalServices.getService(AppStandbyInternal.class); @@ -837,6 +841,9 @@ public final class QuotaController extends StateController { /** @return true if the job is within expedited job quota. */ @GuardedBy("mLock") public boolean isWithinEJQuotaLocked(@NonNull final JobStatus jobStatus) { + if (!mIsEnabled) { + return true; + } if (isQuotaFreeLocked(jobStatus.getEffectiveStandbyBucket())) { return true; } @@ -884,6 +891,9 @@ public final class QuotaController extends StateController { @VisibleForTesting boolean isWithinQuotaLocked(@NonNull final JobStatus jobStatus) { + if (!mIsEnabled) { + return true; + } final int standbyBucket = jobStatus.getEffectiveStandbyBucket(); // A job is within quota if one of the following is true: // 1. it was started while the app was in the TOP state @@ -910,6 +920,9 @@ public final class QuotaController extends StateController { @GuardedBy("mLock") boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName, final int standbyBucket) { + if (!mIsEnabled) { + return true; + } if (standbyBucket == NEVER_INDEX) return false; if (isQuotaFreeLocked(standbyBucket)) return true; @@ -2980,7 +2993,8 @@ public final class QuotaController extends StateController { @Override public void onConstantsUpdatedLocked() { - if (mQcConstants.mShouldReevaluateConstraints) { + if (mQcConstants.mShouldReevaluateConstraints || mIsEnabled == mConstants.USE_TARE_POLICY) { + mIsEnabled = !mConstants.USE_TARE_POLICY; // Update job bookkeeping out of band. JobSchedulerBackgroundThread.getHandler().post(() -> { synchronized (mLock) { @@ -4120,6 +4134,7 @@ public final class QuotaController extends StateController { @Override public void dumpControllerStateLocked(final IndentingPrintWriter pw, final Predicate<JobStatus> predicate) { + pw.println("Is enabled: " + mIsEnabled); pw.println("Is charging: " + mChargeTracker.isChargingLocked()); pw.println("Current elapsed time: " + sElapsedRealtimeClock.millis()); pw.println(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java index 401646d0aac2..be3a3ee2921b 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java @@ -27,6 +27,7 @@ import android.util.Slog; import android.util.SparseArrayMap; import com.android.internal.annotations.GuardedBy; +import com.android.server.JobSchedulerBackgroundThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.tare.EconomyManagerInternal; @@ -170,6 +171,7 @@ public class TareController extends StateController { mBackgroundJobsController = backgroundJobsController; mConnectivityController = connectivityController; mEconomyManagerInternal = LocalServices.getService(EconomyManagerInternal.class); + mIsEnabled = mConstants.USE_TARE_POLICY; } @Override @@ -237,6 +239,32 @@ public class TareController extends StateController { } } + @Override + @GuardedBy("mLock") + public void onConstantsUpdatedLocked() { + if (mIsEnabled != mConstants.USE_TARE_POLICY) { + mIsEnabled = mConstants.USE_TARE_POLICY; + // Update job bookkeeping out of band. + JobSchedulerBackgroundThread.getHandler().post(() -> { + synchronized (mLock) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + mService.getJobStore().forEachJob((jobStatus) -> { + if (!mIsEnabled) { + jobStatus.setTareWealthConstraintSatisfied(nowElapsed, true); + setExpeditedTareApproved(jobStatus, nowElapsed, true); + } else { + jobStatus.setTareWealthConstraintSatisfied( + nowElapsed, hasEnoughWealthLocked(jobStatus)); + setExpeditedTareApproved(jobStatus, nowElapsed, + jobStatus.isRequestedExpeditedJob() + && canAffordExpeditedBillLocked(jobStatus)); + } + }); + } + }); + } + } + @GuardedBy("mLock") public boolean canScheduleEJ(@NonNull JobStatus jobStatus) { if (!mIsEnabled) { @@ -246,6 +274,21 @@ public class TareController extends StateController { } @GuardedBy("mLock") + public long getMaxJobExecutionTimeMsLocked(@NonNull JobStatus jobStatus) { + if (!mIsEnabled) { + return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS; + } + if (jobStatus.shouldTreatAsExpeditedJob()) { + return mEconomyManagerInternal.getMaxDurationMs( + jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), + BILL_JOB_RUNNING_EXPEDITED); + } + return mEconomyManagerInternal.getMaxDurationMs( + jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), + BILL_JOB_RUNNING_DEFAULT); + } + + @GuardedBy("mLock") private void addJobToBillList(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) { final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index 0fcda819d83c..6a2556035470 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -678,6 +678,7 @@ public class JobStatusTest { private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) { job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied); + job.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied); job.setDeviceNotDozingConstraintSatisfied( sElapsedRealtimeClock.millis(), isSatisfied, false); job.setBackgroundNotRestrictedConstraintSatisfied( diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index dd70c874eb86..473815923056 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -389,6 +389,8 @@ public class QuotaControllerTest { /* state */ true, /* allowlisted */false); js.setBackgroundNotRestrictedConstraintSatisfied( sElapsedRealtimeClock.millis(), true, false); + js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true); return js; } |