From fee8c7b42aeebf3a11b978ee657803da0dfa8147 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 21 Feb 2018 22:18:45 -0700 Subject: Mechanical refactoring to improve job dumping. First, JobStatusFunctor was really Consumer before we had the java.util.function APIs, so switch everyone over. Replace most usages with lambdas; no additional runtime cost, since existing code was already creating classes. Move dump() to accept Predicate for their filtering behavior, enabling more future advanced filtering using any attributes of JobStatus. Also move dump() to IndentingPrintWriter to avoid passing around tedious prefix information. Makes it much easier to print sane-looking output. Add IndentingPrintWriter support for initial prefix values. Test: manual dumpsys output looks sane Bug: 73019091 Change-Id: I4c2398443b42dfb48135ab900d4331ff6d2bb5c4 --- .../internal/util/IndentingPrintWriter.java | 30 ++++++-- .../java/com/android/server/AppStateTracker.java | 34 ++++---- .../core/java/com/android/server/StatLogger.java | 11 ++- .../android/server/job/JobSchedulerService.java | 61 ++++++++------- .../core/java/com/android/server/job/JobStore.java | 44 ++++++----- .../server/job/controllers/AppIdleController.java | 89 ++++++++++----------- .../job/controllers/BackgroundJobsController.java | 33 ++++---- .../server/job/controllers/BatteryController.java | 27 +++---- .../job/controllers/ConnectivityController.java | 23 +++--- .../job/controllers/ContentObserverController.java | 43 +++++++---- .../job/controllers/DeviceIdleJobsController.java | 90 ++++++++++------------ .../server/job/controllers/IdleController.java | 23 +++--- .../android/server/job/controllers/JobStatus.java | 5 -- .../server/job/controllers/StateController.java | 9 ++- .../server/job/controllers/StorageController.java | 26 +++---- .../server/job/controllers/TimeController.java | 25 +++--- 16 files changed, 297 insertions(+), 276 deletions(-) diff --git a/core/java/com/android/internal/util/IndentingPrintWriter.java b/core/java/com/android/internal/util/IndentingPrintWriter.java index 696667c6202b..e453866b05af 100644 --- a/core/java/com/android/internal/util/IndentingPrintWriter.java +++ b/core/java/com/android/internal/util/IndentingPrintWriter.java @@ -57,26 +57,46 @@ public class IndentingPrintWriter extends PrintWriter { mWrapLength = wrapLength; } - public void increaseIndent() { + public IndentingPrintWriter setIndent(String indent) { + mIndentBuilder.setLength(0); + mIndentBuilder.append(indent); + mCurrentIndent = null; + return this; + } + + public IndentingPrintWriter setIndent(int indent) { + mIndentBuilder.setLength(0); + for (int i = 0; i < indent; i++) { + increaseIndent(); + } + return this; + } + + public IndentingPrintWriter increaseIndent() { mIndentBuilder.append(mSingleIndent); mCurrentIndent = null; + return this; } - public void decreaseIndent() { + public IndentingPrintWriter decreaseIndent() { mIndentBuilder.delete(0, mSingleIndent.length()); mCurrentIndent = null; + return this; } - public void printPair(String key, Object value) { + public IndentingPrintWriter printPair(String key, Object value) { print(key + "=" + String.valueOf(value) + " "); + return this; } - public void printPair(String key, Object[] value) { + public IndentingPrintWriter printPair(String key, Object[] value) { print(key + "=" + Arrays.toString(value) + " "); + return this; } - public void printHexPair(String key, int value) { + public IndentingPrintWriter printHexPair(String key, int value) { print(key + "=0x" + Integer.toHexString(value) + " "); + return this; } @Override diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java index 9b29b3212b44..fc4d4638491f 100644 --- a/services/core/java/com/android/server/AppStateTracker.java +++ b/services/core/java/com/android/server/AppStateTracker.java @@ -53,6 +53,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.ForceAppStandbyTrackerProto.ExemptedPackage; import com.android.server.ForceAppStandbyTrackerProto.RunAnyInBackgroundRestrictedPackages; @@ -1182,72 +1183,67 @@ public class AppStateTracker { } } - public void dump(PrintWriter pw, String indent) { + @Deprecated + public void dump(PrintWriter pw, String prefix) { + dump(new IndentingPrintWriter(pw, " ").setIndent(prefix)); + } + + public void dump(IndentingPrintWriter pw) { synchronized (mLock) { - pw.print(indent); pw.println("Forced App Standby Feature enabled: " + mForcedAppStandbyEnabled); - pw.print(indent); pw.print("Force all apps standby: "); pw.println(isForceAllAppsStandbyEnabled()); - pw.print(indent); pw.print("Small Battery Device: "); pw.println(isSmallBatteryDevice()); - pw.print(indent); pw.print("Force all apps standby for small battery device: "); pw.println(mForceAllAppStandbyForSmallBattery); - pw.print(indent); pw.print("Plugged In: "); pw.println(mIsPluggedIn); - pw.print(indent); pw.print("Active uids: "); dumpUids(pw, mActiveUids); - pw.print(indent); pw.print("Foreground uids: "); dumpUids(pw, mForegroundUids); - pw.print(indent); pw.print("Whitelist appids: "); pw.println(Arrays.toString(mPowerWhitelistedAllAppIds)); - pw.print(indent); pw.print("Temp whitelist appids: "); pw.println(Arrays.toString(mTempWhitelistedAppIds)); - pw.print(indent); pw.println("Exempted packages:"); + pw.increaseIndent(); for (int i = 0; i < mExemptedPackages.size(); i++) { - pw.print(indent); - pw.print(" User "); + pw.print("User "); pw.print(mExemptedPackages.keyAt(i)); pw.println(); + pw.increaseIndent(); for (int j = 0; j < mExemptedPackages.sizeAt(i); j++) { - pw.print(indent); - pw.print(" "); pw.print(mExemptedPackages.valueAt(i, j)); pw.println(); } + pw.decreaseIndent(); } + pw.decreaseIndent(); pw.println(); - pw.print(indent); pw.println("Restricted packages:"); + pw.increaseIndent(); for (Pair uidAndPackage : mRunAnyRestrictedPackages) { - pw.print(indent); - pw.print(" "); pw.print(UserHandle.formatUid(uidAndPackage.first)); pw.print(" "); pw.print(uidAndPackage.second); pw.println(); } + pw.decreaseIndent(); - mStatLogger.dump(pw, indent); + mStatLogger.dump(pw); } } diff --git a/services/core/java/com/android/server/StatLogger.java b/services/core/java/com/android/server/StatLogger.java index 0e6f5e2338c7..d85810d31e73 100644 --- a/services/core/java/com/android/server/StatLogger.java +++ b/services/core/java/com/android/server/StatLogger.java @@ -21,6 +21,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.StatLoggerProto.Event; import java.io.PrintWriter; @@ -78,19 +79,23 @@ public class StatLogger { } } + @Deprecated public void dump(PrintWriter pw, String prefix) { + dump(new IndentingPrintWriter(pw, " ").setIndent(prefix)); + } + + public void dump(IndentingPrintWriter pw) { synchronized (mLock) { - pw.print(prefix); pw.println("Stats:"); + pw.increaseIndent(); for (int i = 0; i < SIZE; i++) { - pw.print(prefix); - pw.print(" "); final int count = mCountStats[i]; final double durationMs = mDurationStats[i] / 1000.0; pw.println(String.format("%s: count=%d, total=%.1fms, avg=%.3fms", mLabels[i], count, durationMs, (count == 0 ? 0 : ((double) durationMs) / count))); } + pw.decreaseIndent(); } } diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index f3f4dfda82c8..ff4e1e26b489 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -79,6 +79,7 @@ import com.android.internal.app.procstats.ProcessStats; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.AppStateTracker; import com.android.server.DeviceIdleController; @@ -86,7 +87,6 @@ import com.android.server.FgThread; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob; import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob; -import com.android.server.job.JobStore.JobStatusFunctor; import com.android.server.job.controllers.AppIdleController; import com.android.server.job.controllers.BackgroundJobsController; import com.android.server.job.controllers.BatteryController; @@ -110,6 +110,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -1227,13 +1228,10 @@ public final class JobSchedulerService extends com.android.server.SystemService getContext().getMainLooper())); } // Attach jobs to their controllers. - mJobs.forEachJob(new JobStatusFunctor() { - @Override - public void process(JobStatus job) { - for (int controller = 0; controller < mControllers.size(); controller++) { - final StateController sc = mControllers.get(controller); - sc.maybeStartTrackingJobLocked(job, null); - } + mJobs.forEachJob((job) -> { + for (int controller = 0; controller < mControllers.size(); controller++) { + final StateController sc = mControllers.get(controller); + sc.maybeStartTrackingJobLocked(job, null); } }); // GO GO GO! @@ -1602,11 +1600,11 @@ public final class JobSchedulerService extends com.android.server.SystemService } } - final class ReadyJobQueueFunctor implements JobStatusFunctor { + final class ReadyJobQueueFunctor implements Consumer { ArrayList newReadyJobs; @Override - public void process(JobStatus job) { + public void accept(JobStatus job) { if (isReadyToBeExecutedLocked(job)) { if (DEBUG) { Slog.d(TAG, " queued " + job.toShortString()); @@ -1640,7 +1638,7 @@ public final class JobSchedulerService extends com.android.server.SystemService * If more than 4 jobs total are ready we send them all off. * TODO: It would be nice to consolidate these sort of high-level policies somewhere. */ - final class MaybeReadyJobQueueFunctor implements JobStatusFunctor { + final class MaybeReadyJobQueueFunctor implements Consumer { int chargingCount; int batteryNotLowCount; int storageNotLowCount; @@ -1656,7 +1654,7 @@ public final class JobSchedulerService extends com.android.server.SystemService // Functor method invoked for each job via JobStore.forEachJob() @Override - public void process(JobStatus job) { + public void accept(JobStatus job) { if (isReadyToBeExecutedLocked(job)) { try { if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(), @@ -2173,12 +2171,9 @@ public final class JobSchedulerService extends com.android.server.SystemService public List getSystemScheduledPendingJobs() { synchronized (mLock) { final List pendingJobs = new ArrayList(); - mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() { - @Override - public void process(JobStatus job) { - if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) { - pendingJobs.add(job.getJob()); - } + mJobs.forEachJob(Process.SYSTEM_UID, (job) -> { + if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) { + pendingJobs.add(job.getJob()); } }); return pendingJobs; @@ -2307,7 +2302,7 @@ public final class JobSchedulerService extends com.android.server.SystemService } } - static class DeferredJobCounter implements JobStatusFunctor { + static class DeferredJobCounter implements Consumer { private int mDeferred = 0; public int numDeferred() { @@ -2315,7 +2310,7 @@ public final class JobSchedulerService extends com.android.server.SystemService } @Override - public void process(JobStatus job) { + public void accept(JobStatus job) { if (job.getWhenStandbyDeferred() > 0) { mDeferred++; } @@ -2596,12 +2591,13 @@ public final class JobSchedulerService extends com.android.server.SystemService } } - long identityToken = Binder.clearCallingIdentity(); + final long identityToken = Binder.clearCallingIdentity(); try { if (proto) { JobSchedulerService.this.dumpInternalProto(fd, filterUid); } else { - JobSchedulerService.this.dumpInternal(pw, filterUid); + JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "), + filterUid); } } finally { Binder.restoreCallingIdentity(identityToken); @@ -2900,10 +2896,14 @@ public final class JobSchedulerService extends com.android.server.SystemService }); } - void dumpInternal(final PrintWriter pw, int filterUid) { + void dumpInternal(final IndentingPrintWriter pw, int filterUid) { final int filterUidFinal = UserHandle.getAppId(filterUid); final long nowElapsed = sElapsedRealtimeClock.millis(); final long nowUptime = sUptimeMillisClock.millis(); + final Predicate predicate = (js) -> { + return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal + || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal; + }; synchronized (mLock) { mConstants.dump(pw); pw.println(); @@ -2919,7 +2919,7 @@ public final class JobSchedulerService extends com.android.server.SystemService pw.println(job.toShortStringExceptUniqueId()); // Skip printing details if the caller requested a filter - if (!job.shouldDump(filterUidFinal)) { + if (!predicate.test(job)) { continue; } @@ -2953,7 +2953,10 @@ public final class JobSchedulerService extends com.android.server.SystemService } for (int i=0; i predicate = (js) -> { + return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal + || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal; + }; synchronized (mLock) { mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS); @@ -3070,7 +3077,7 @@ public final class JobSchedulerService extends com.android.server.SystemService job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO); // Skip printing details if the caller requested a filter - if (!job.shouldDump(filterUidFinal)) { + if (!predicate.test(job)) { continue; } @@ -3103,7 +3110,7 @@ public final class JobSchedulerService extends com.android.server.SystemService } for (StateController controller : mControllers) { controller.dumpControllerStateLocked( - proto, JobSchedulerServiceDumpProto.CONTROLLERS, filterUidFinal); + proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate); } for (int i=0; i< mUidPriorityOverride.size(); i++) { int uid = mUidPriorityOverride.keyAt(i); diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index cf2788255843..7235faa6ad50 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -19,6 +19,7 @@ package com.android.server.job; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.IActivityManager; import android.app.job.JobInfo; @@ -62,6 +63,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Set; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -286,20 +288,21 @@ public final class JobStore { * transient unified collections for them to iterate over and then discard, or creating * iterators every time a client needs to perform a sweep. */ - public void forEachJob(JobStatusFunctor functor) { - mJobSet.forEachJob(functor); + public void forEachJob(Consumer functor) { + mJobSet.forEachJob(null, functor); } - public void forEachJob(int uid, JobStatusFunctor functor) { - mJobSet.forEachJob(uid, functor); + public void forEachJob(@Nullable Predicate filterPredicate, + Consumer functor) { + mJobSet.forEachJob(filterPredicate, functor); } - public void forEachJobForSourceUid(int sourceUid, JobStatusFunctor functor) { - mJobSet.forEachJobForSourceUid(sourceUid, functor); + public void forEachJob(int uid, Consumer functor) { + mJobSet.forEachJob(uid, functor); } - public interface JobStatusFunctor { - public void process(JobStatus jobStatus); + public void forEachJobForSourceUid(int sourceUid, Consumer functor) { + mJobSet.forEachJobForSourceUid(sourceUid, functor); } /** Version of the db schema. */ @@ -342,12 +345,9 @@ public final class JobStore { final List storeCopy = new ArrayList(); synchronized (mLock) { // Clone the jobs so we can release the lock before writing. - mJobSet.forEachJob(new JobStatusFunctor() { - @Override - public void process(JobStatus job) { - if (job.isPersisted()) { - storeCopy.add(new JobStatus(job)); - } + mJobSet.forEachJob(null, (job) -> { + if (job.isPersisted()) { + storeCopy.add(new JobStatus(job)); } }); } @@ -1184,31 +1184,35 @@ public final class JobStore { return total; } - public void forEachJob(JobStatusFunctor functor) { + public void forEachJob(@Nullable Predicate filterPredicate, + Consumer functor) { for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) { ArraySet jobs = mJobs.valueAt(uidIndex); if (jobs != null) { for (int i = jobs.size() - 1; i >= 0; i--) { - functor.process(jobs.valueAt(i)); + final JobStatus jobStatus = jobs.valueAt(i); + if ((filterPredicate == null) || filterPredicate.test(jobStatus)) { + functor.accept(jobStatus); + } } } } } - public void forEachJob(int callingUid, JobStatusFunctor functor) { + public void forEachJob(int callingUid, Consumer functor) { ArraySet jobs = mJobs.get(callingUid); if (jobs != null) { for (int i = jobs.size() - 1; i >= 0; i--) { - functor.process(jobs.valueAt(i)); + functor.accept(jobs.valueAt(i)); } } } - public void forEachJobForSourceUid(int sourceUid, JobStatusFunctor functor) { + public void forEachJobForSourceUid(int sourceUid, Consumer functor) { final ArraySet jobs = mJobsPerSourceUid.get(sourceUid); if (jobs != null) { for (int i = jobs.size() - 1; i >= 0; i--) { - functor.process(jobs.valueAt(i)); + functor.accept(jobs.valueAt(i)); } } } diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java index 021eccd6d1cf..4b4882499c3a 100644 --- a/services/core/java/com/android/server/job/controllers/AppIdleController.java +++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java @@ -23,12 +23,13 @@ import android.util.Log; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; -import com.android.server.job.JobStore; import com.android.server.job.StateControllerProto; -import java.io.PrintWriter; +import java.util.function.Consumer; +import java.util.function.Predicate; /** * Controls when apps are considered idle and if jobs pertaining to those apps should @@ -49,10 +50,11 @@ public final class AppIdleController extends StateController { private boolean mInitializedParoleOn; boolean mAppIdleParoleOn; - final class GlobalUpdateFunc implements JobStore.JobStatusFunctor { + final class GlobalUpdateFunc implements Consumer { boolean mChanged; - @Override public void process(JobStatus jobStatus) { + @Override + public void accept(JobStatus jobStatus) { String packageName = jobStatus.getSourcePackageName(); final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, jobStatus.getSourceUid(), jobStatus.getSourceUserId()); @@ -63,9 +65,9 @@ public final class AppIdleController extends StateController { mChanged = true; } } - }; + } - final static class PackageUpdateFunc implements JobStore.JobStatusFunctor { + final static class PackageUpdateFunc implements Consumer { final int mUserId; final String mPackage; final boolean mIdle; @@ -77,7 +79,8 @@ public final class AppIdleController extends StateController { mIdle = idle; } - @Override public void process(JobStatus jobStatus) { + @Override + public void accept(JobStatus jobStatus) { if (jobStatus.getSourcePackageName().equals(mPackage) && jobStatus.getSourceUserId() == mUserId) { if (jobStatus.setAppNotIdleConstraintSatisfied(!mIdle)) { @@ -89,7 +92,7 @@ public final class AppIdleController extends StateController { } } } - }; + } public static AppIdleController get(JobSchedulerService service) { synchronized (sCreationLock) { @@ -131,56 +134,46 @@ public final class AppIdleController extends StateController { } @Override - public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) { - pw.print("AppIdle: parole on = "); - pw.println(mAppIdleParoleOn); - mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() { - @Override public void process(JobStatus jobStatus) { - // Skip printing details if the caller requested a filter - if (!jobStatus.shouldDump(filterUid)) { - return; - } - pw.print(" #"); - jobStatus.printUniqueId(pw); - pw.print(" from "); - UserHandle.formatUid(pw, jobStatus.getSourceUid()); - pw.print(": "); - pw.print(jobStatus.getSourcePackageName()); - if ((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0) { - pw.println(" RUNNABLE"); - } else { - pw.println(" WAITING"); - } + public void dumpControllerStateLocked(final IndentingPrintWriter pw, + final Predicate predicate) { + pw.println("Parole on: " + mAppIdleParoleOn); + pw.println(); + + mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { + pw.print("#"); + jobStatus.printUniqueId(pw); + pw.print(" from "); + UserHandle.formatUid(pw, jobStatus.getSourceUid()); + pw.print(": "); + pw.print(jobStatus.getSourcePackageName()); + if ((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0) { + pw.println(" RUNNABLE"); + } else { + pw.println(" WAITING"); } }); } @Override - public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate predicate) { final long token = proto.start(fieldId); final long mToken = proto.start(StateControllerProto.APP_IDLE); proto.write(StateControllerProto.AppIdleController.IS_PAROLE_ON, mAppIdleParoleOn); - mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() { - @Override public void process(JobStatus js) { - // Skip printing details if the caller requested a filter - if (!js.shouldDump(filterUid)) { - return; - } - - final long jsToken = - proto.start(StateControllerProto.AppIdleController.TRACKED_JOBS); - js.writeToShortProto(proto, StateControllerProto.AppIdleController.TrackedJob.INFO); - proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_UID, - js.getSourceUid()); - proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_PACKAGE_NAME, - js.getSourcePackageName()); - proto.write( - StateControllerProto.AppIdleController.TrackedJob.ARE_CONSTRAINTS_SATISFIED, - (js.satisfiedConstraints & JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0); - proto.end(jsToken); - } + mJobSchedulerService.getJobStore().forEachJob(predicate, (js) -> { + final long jsToken = + proto.start(StateControllerProto.AppIdleController.TRACKED_JOBS); + js.writeToShortProto(proto, StateControllerProto.AppIdleController.TrackedJob.INFO); + proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_UID, + js.getSourceUid()); + proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_PACKAGE_NAME, + js.getSourcePackageName()); + proto.write( + StateControllerProto.AppIdleController.TrackedJob.ARE_CONSTRAINTS_SATISFIED, + (js.satisfiedConstraints & JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0); + proto.end(jsToken); }); proto.end(mToken); diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java index 46ec5e5d1e1d..7a09fc4cb2af 100644 --- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -23,16 +23,17 @@ import android.util.Log; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.server.AppStateTracker; import com.android.server.AppStateTracker.Listener; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; -import com.android.server.job.JobStore; import com.android.server.job.StateControllerProto; import com.android.server.job.StateControllerProto.BackgroundJobsController.TrackedJob; -import java.io.PrintWriter; +import java.util.function.Consumer; +import java.util.function.Predicate; public final class BackgroundJobsController extends StateController { private static final String TAG = "JobScheduler.Background"; @@ -77,19 +78,15 @@ public final class BackgroundJobsController extends StateController { } @Override - public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) { - pw.println("BackgroundJobsController"); + public void dumpControllerStateLocked(final IndentingPrintWriter pw, + final Predicate predicate) { + mAppStateTracker.dump(pw); + pw.println(); - mAppStateTracker.dump(pw, ""); - - pw.println("Job state:"); - mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> { - if (!jobStatus.shouldDump(filterUid)) { - return; - } + mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { final int uid = jobStatus.getSourceUid(); final String sourcePkg = jobStatus.getSourcePackageName(); - pw.print(" #"); + pw.print("#"); jobStatus.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, uid); @@ -115,17 +112,15 @@ public final class BackgroundJobsController extends StateController { } @Override - public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate predicate) { final long token = proto.start(fieldId); final long mToken = proto.start(StateControllerProto.BACKGROUND); mAppStateTracker.dumpProto(proto, StateControllerProto.BackgroundJobsController.FORCE_APP_STANDBY_TRACKER); - mJobSchedulerService.getJobStore().forEachJob((jobStatus) -> { - if (!jobStatus.shouldDump(filterUid)) { - return; - } + mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { final long jsToken = proto.start(StateControllerProto.BackgroundJobsController.TRACKED_JOBS); @@ -205,7 +200,7 @@ public final class BackgroundJobsController extends StateController { return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun); } - private final class UpdateJobFunctor implements JobStore.JobStatusFunctor { + private final class UpdateJobFunctor implements Consumer { private final int mFilterUid; boolean mChanged = false; @@ -217,7 +212,7 @@ public final class BackgroundJobsController extends StateController { } @Override - public void process(JobStatus jobStatus) { + public void accept(JobStatus jobStatus) { mTotalCount++; if ((mFilterUid > 0) && (mFilterUid != jobStatus.getSourceUid())) { return; diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java index 263d99b06f2c..24491b0b7dbc 100644 --- a/services/core/java/com/android/server/job/controllers/BatteryController.java +++ b/services/core/java/com/android/server/job/controllers/BatteryController.java @@ -31,12 +31,13 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; -import java.io.PrintWriter; +import java.util.function.Predicate; /** * Simple controller that tracks whether the phone is charging or not. The phone is considered to @@ -244,24 +245,23 @@ public final class BatteryController extends StateController { } @Override - public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { - pw.print("Battery: stable power = "); - pw.print(mChargeTracker.isOnStablePower()); - pw.print(", not low = "); - pw.println(mChargeTracker.isBatteryNotLow()); + public void dumpControllerStateLocked(IndentingPrintWriter pw, + Predicate predicate) { + pw.println("Stable power: " + mChargeTracker.isOnStablePower()); + pw.println("Not low: " + mChargeTracker.isBatteryNotLow()); + if (mChargeTracker.isMonitoring()) { pw.print("MONITORING: seq="); pw.println(mChargeTracker.getSeq()); } - pw.print("Tracking "); - pw.print(mTrackedTasks.size()); - pw.println(":"); + pw.println(); + for (int i = 0; i < mTrackedTasks.size(); i++) { final JobStatus js = mTrackedTasks.valueAt(i); - if (!js.shouldDump(filterUid)) { + if (!predicate.test(js)) { continue; } - pw.print(" #"); + pw.print("#"); js.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, js.getSourceUid()); @@ -270,7 +270,8 @@ public final class BatteryController extends StateController { } @Override - public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate predicate) { final long token = proto.start(fieldId); final long mToken = proto.start(StateControllerProto.BATTERY); @@ -286,7 +287,7 @@ public final class BatteryController extends StateController { for (int i = 0; i < mTrackedTasks.size(); i++) { final JobStatus js = mTrackedTasks.valueAt(i); - if (!js.shouldDump(filterUid)) { + if (!predicate.test(js)) { continue; } final long jsToken = proto.start(StateControllerProto.BatteryController.TRACKED_JOBS); diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index d7ef124c640d..dd07bc6eb7cc 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -41,12 +41,13 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobServiceContext; import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; -import java.io.PrintWriter; +import java.util.function.Predicate; /** * Handles changes in connectivity. @@ -331,18 +332,15 @@ public final class ConnectivityController extends StateController implements @GuardedBy("mLock") @Override - public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { - pw.print("Connectivity: connected="); - pw.println(mConnected); - - pw.print("Tracking "); - pw.print(mTrackedJobs.size()); - pw.println(" jobs"); + public void dumpControllerStateLocked(IndentingPrintWriter pw, + Predicate predicate) { + pw.println("System connected: " + mConnected); + pw.println(); for (int i = 0; i < mTrackedJobs.size(); i++) { final JobStatus js = mTrackedJobs.valueAt(i); - if (js.shouldDump(filterUid)) { - pw.print(" #"); + if (predicate.test(js)) { + pw.print("#"); js.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, js.getSourceUid()); @@ -355,7 +353,8 @@ public final class ConnectivityController extends StateController implements @GuardedBy("mLock") @Override - public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate predicate) { final long token = proto.start(fieldId); final long mToken = proto.start(StateControllerProto.CONNECTIVITY); @@ -363,7 +362,7 @@ public final class ConnectivityController extends StateController implements for (int i = 0; i < mTrackedJobs.size(); i++) { final JobStatus js = mTrackedJobs.valueAt(i); - if (!js.shouldDump(filterUid)) { + if (!predicate.test(js)) { continue; } final long jsToken = proto.start(StateControllerProto.ConnectivityController.TRACKED_JOBS); diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java index 90edde907f6d..27f6c1e6f87c 100644 --- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java +++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java @@ -32,13 +32,14 @@ import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; import com.android.server.job.StateControllerProto.ContentObserverController.Observer.TriggerContentData; -import java.io.PrintWriter; import java.util.ArrayList; +import java.util.function.Predicate; /** * Controller for monitoring changes to content URIs through a ContentObserver. @@ -375,22 +376,25 @@ public final class ContentObserverController extends StateController { } @Override - public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { - pw.println("Content:"); + public void dumpControllerStateLocked(IndentingPrintWriter pw, + Predicate predicate) { for (int i = 0; i < mTrackedTasks.size(); i++) { JobStatus js = mTrackedTasks.valueAt(i); - if (!js.shouldDump(filterUid)) { + if (!predicate.test(js)) { continue; } - pw.print(" #"); + pw.print("#"); js.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, js.getSourceUid()); pw.println(); } + pw.println(); + int N = mObservers.size(); if (N > 0) { - pw.println(" Observers:"); + pw.println("Observers:"); + pw.increaseIndent(); for (int userIdx = 0; userIdx < N; userIdx++) { final int userId = mObservers.keyAt(userIdx); ArrayMap observersOfUser = @@ -402,7 +406,7 @@ public final class ContentObserverController extends StateController { boolean shouldDump = false; for (int j = 0; j < M; j++) { JobInstance inst = obs.mJobs.valueAt(j); - if (inst.mJobStatus.shouldDump(filterUid)) { + if (predicate.test(inst.mJobStatus)) { shouldDump = true; break; } @@ -410,7 +414,6 @@ public final class ContentObserverController extends StateController { if (!shouldDump) { continue; } - pw.print(" "); JobInfo.TriggerContentUri trigger = observersOfUser.keyAt(observerIdx); pw.print(trigger.getUri()); pw.print(" 0x"); @@ -418,17 +421,20 @@ public final class ContentObserverController extends StateController { pw.print(" ("); pw.print(System.identityHashCode(obs)); pw.println("):"); - pw.println(" Jobs:"); + pw.increaseIndent(); + pw.println("Jobs:"); + pw.increaseIndent(); for (int j = 0; j < M; j++) { JobInstance inst = obs.mJobs.valueAt(j); - pw.print(" #"); + pw.print("#"); inst.mJobStatus.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, inst.mJobStatus.getSourceUid()); if (inst.mChangedAuthorities != null) { pw.println(":"); + pw.increaseIndent(); if (inst.mTriggerPending) { - pw.print(" Trigger pending: update="); + pw.print("Trigger pending: update="); TimeUtils.formatDuration( inst.mJobStatus.getTriggerContentUpdateDelay(), pw); pw.print(", max="); @@ -436,35 +442,38 @@ public final class ContentObserverController extends StateController { inst.mJobStatus.getTriggerContentMaxDelay(), pw); pw.println(); } - pw.println(" Changed Authorities:"); + pw.println("Changed Authorities:"); for (int k = 0; k < inst.mChangedAuthorities.size(); k++) { - pw.print(" "); pw.println(inst.mChangedAuthorities.valueAt(k)); } if (inst.mChangedUris != null) { pw.println(" Changed URIs:"); for (int k = 0; k < inst.mChangedUris.size(); k++) { - pw.print(" "); pw.println(inst.mChangedUris.valueAt(k)); } } + pw.decreaseIndent(); } else { pw.println(); } } + pw.decreaseIndent(); + pw.decreaseIndent(); } } + pw.decreaseIndent(); } } @Override - public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate predicate) { final long token = proto.start(fieldId); final long mToken = proto.start(StateControllerProto.CONTENT_OBSERVER); for (int i = 0; i < mTrackedTasks.size(); i++) { JobStatus js = mTrackedTasks.valueAt(i); - if (!js.shouldDump(filterUid)) { + if (!predicate.test(js)) { continue; } final long jsToken = @@ -493,7 +502,7 @@ public final class ContentObserverController extends StateController { boolean shouldDump = false; for (int j = 0; j < m; j++) { JobInstance inst = obs.mJobs.valueAt(j); - if (inst.mJobStatus.shouldDump(filterUid)) { + if (predicate.test(inst.mJobStatus)) { shouldDump = true; break; } diff --git a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java index 323a12634b39..aa60350134eb 100644 --- a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java +++ b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java @@ -33,15 +33,16 @@ import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; import com.android.internal.util.ArrayUtils; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.DeviceIdleController; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; -import com.android.server.job.JobStore; import com.android.server.job.StateControllerProto; import com.android.server.job.StateControllerProto.DeviceIdleJobsController.TrackedJob; -import java.io.PrintWriter; import java.util.Arrays; +import java.util.function.Consumer; +import java.util.function.Predicate; /** * When device is dozing, set constraint for all jobs, except whitelisted apps, as not satisfied. @@ -247,71 +248,64 @@ public final class DeviceIdleJobsController extends StateController { } @Override - public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) { - pw.println("DeviceIdleJobsController"); - pw.println("mDeviceIdleMode=" + mDeviceIdleMode); - mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() { - @Override public void process(JobStatus jobStatus) { - if (!jobStatus.shouldDump(filterUid)) { - return; - } - pw.print(" #"); - jobStatus.printUniqueId(pw); - pw.print(" from "); - UserHandle.formatUid(pw, jobStatus.getSourceUid()); - pw.print(": "); - pw.print(jobStatus.getSourcePackageName()); - pw.print((jobStatus.satisfiedConstraints - & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0 - ? " RUNNABLE" : " WAITING"); - if (jobStatus.dozeWhitelisted) { - pw.print(" WHITELISTED"); - } - if (mAllowInIdleJobs.contains(jobStatus)) { - pw.print(" ALLOWED_IN_DOZE"); - } - pw.println(); + public void dumpControllerStateLocked(final IndentingPrintWriter pw, + final Predicate predicate) { + pw.println("Idle mode: " + mDeviceIdleMode); + pw.println(); + + mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { + pw.print("#"); + jobStatus.printUniqueId(pw); + pw.print(" from "); + UserHandle.formatUid(pw, jobStatus.getSourceUid()); + pw.print(": "); + pw.print(jobStatus.getSourcePackageName()); + pw.print((jobStatus.satisfiedConstraints + & JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0 + ? " RUNNABLE" : " WAITING"); + if (jobStatus.dozeWhitelisted) { + pw.print(" WHITELISTED"); + } + if (mAllowInIdleJobs.contains(jobStatus)) { + pw.print(" ALLOWED_IN_DOZE"); } + pw.println(); }); } @Override - public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate predicate) { final long token = proto.start(fieldId); final long mToken = proto.start(StateControllerProto.DEVICE_IDLE); proto.write(StateControllerProto.DeviceIdleJobsController.IS_DEVICE_IDLE_MODE, mDeviceIdleMode); - mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() { - @Override public void process(JobStatus jobStatus) { - if (!jobStatus.shouldDump(filterUid)) { - return; - } - final long jsToken = - proto.start(StateControllerProto.DeviceIdleJobsController.TRACKED_JOBS); - - jobStatus.writeToShortProto(proto, TrackedJob.INFO); - proto.write(TrackedJob.SOURCE_UID, jobStatus.getSourceUid()); - proto.write(TrackedJob.SOURCE_PACKAGE_NAME, jobStatus.getSourcePackageName()); - proto.write(TrackedJob.ARE_CONSTRAINTS_SATISFIED, - (jobStatus.satisfiedConstraints & - JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0); - proto.write(TrackedJob.IS_DOZE_WHITELISTED, jobStatus.dozeWhitelisted); - proto.write(TrackedJob.IS_ALLOWED_IN_DOZE, mAllowInIdleJobs.contains(jobStatus)); - - proto.end(jsToken); - } + mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { + final long jsToken = + proto.start(StateControllerProto.DeviceIdleJobsController.TRACKED_JOBS); + + jobStatus.writeToShortProto(proto, TrackedJob.INFO); + proto.write(TrackedJob.SOURCE_UID, jobStatus.getSourceUid()); + proto.write(TrackedJob.SOURCE_PACKAGE_NAME, jobStatus.getSourcePackageName()); + proto.write(TrackedJob.ARE_CONSTRAINTS_SATISFIED, + (jobStatus.satisfiedConstraints & + JobStatus.CONSTRAINT_DEVICE_NOT_DOZING) != 0); + proto.write(TrackedJob.IS_DOZE_WHITELISTED, jobStatus.dozeWhitelisted); + proto.write(TrackedJob.IS_ALLOWED_IN_DOZE, mAllowInIdleJobs.contains(jobStatus)); + + proto.end(jsToken); }); proto.end(mToken); proto.end(token); } - final class DeviceIdleUpdateFunctor implements JobStore.JobStatusFunctor { + final class DeviceIdleUpdateFunctor implements Consumer { boolean mChanged; @Override - public void process(JobStatus jobStatus) { + public void accept(JobStatus jobStatus) { mChanged |= updateTaskStateLocked(jobStatus); } } diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index 78284e57f42d..2cfa2c517c5a 100644 --- a/services/core/java/com/android/server/job/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -30,12 +30,13 @@ import android.util.Log; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.am.ActivityManagerService; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; -import java.io.PrintWriter; +import java.util.function.Predicate; public final class IdleController extends StateController { private static final String TAG = "JobScheduler.Idle"; @@ -203,18 +204,17 @@ public final class IdleController extends StateController { } @Override - public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { - pw.print("Idle: "); - pw.println(mIdleTracker.isIdle()); - pw.print("Tracking "); - pw.print(mTrackedTasks.size()); - pw.println(":"); + public void dumpControllerStateLocked(IndentingPrintWriter pw, + Predicate predicate) { + pw.println("Currently idle: " + mIdleTracker.isIdle()); + pw.println(); + for (int i = 0; i < mTrackedTasks.size(); i++) { final JobStatus js = mTrackedTasks.valueAt(i); - if (!js.shouldDump(filterUid)) { + if (!predicate.test(js)) { continue; } - pw.print(" #"); + pw.print("#"); js.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, js.getSourceUid()); @@ -223,7 +223,8 @@ public final class IdleController extends StateController { } @Override - public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate predicate) { final long token = proto.start(fieldId); final long mToken = proto.start(StateControllerProto.IDLE); @@ -231,7 +232,7 @@ public final class IdleController extends StateController { for (int i = 0; i < mTrackedTasks.size(); i++) { final JobStatus js = mTrackedTasks.valueAt(i); - if (!js.shouldDump(filterUid)) { + if (!predicate.test(js)) { continue; } final long jsToken = proto.start(StateControllerProto.IdleController.TRACKED_JOBS); diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 3867306ee521..9333e22ee574 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -882,11 +882,6 @@ public final class JobStatus { return mLastFailedRunTime; } - public boolean shouldDump(int filterUid) { - return filterUid == -1 || UserHandle.getAppId(getUid()) == filterUid - || UserHandle.getAppId(getSourceUid()) == filterUid; - } - /** * @return Whether or not this job is ready to run, based on its requirements. This is true if * the constraints are satisfied or the deadline on the job has expired. diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java index 88d6bea97e43..2b97bca6fc5f 100644 --- a/services/core/java/com/android/server/job/controllers/StateController.java +++ b/services/core/java/com/android/server/job/controllers/StateController.java @@ -19,10 +19,10 @@ package com.android.server.job.controllers; import android.content.Context; import android.util.proto.ProtoOutputStream; -import com.android.server.job.JobSchedulerService; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.job.StateChangedListener; -import java.io.PrintWriter; +import java.util.function.Predicate; /** * Incorporates shared controller logic between the various controllers of the JobManager. @@ -64,7 +64,8 @@ public abstract class StateController { public void rescheduleForFailureLocked(JobStatus newJob, JobStatus failureToReschedule) { } - public abstract void dumpControllerStateLocked(PrintWriter pw, int filterUid); + public abstract void dumpControllerStateLocked(IndentingPrintWriter pw, + Predicate predicate); public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, - int filterUid); + Predicate predicate); } diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java index 5b79f397b281..e36f02d9c74a 100644 --- a/services/core/java/com/android/server/job/controllers/StorageController.java +++ b/services/core/java/com/android/server/job/controllers/StorageController.java @@ -29,12 +29,13 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; import com.android.server.storage.DeviceStorageMonitorService; -import java.io.PrintWriter; +import java.util.function.Predicate; /** * Simple controller that tracks the status of the device's storage. @@ -175,20 +176,18 @@ public final class StorageController extends StateController { } @Override - public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { - pw.print("Storage: not low = "); - pw.print(mStorageTracker.isStorageNotLow()); - pw.print(", seq="); - pw.println(mStorageTracker.getSeq()); - pw.print("Tracking "); - pw.print(mTrackedTasks.size()); - pw.println(":"); + public void dumpControllerStateLocked(IndentingPrintWriter pw, + Predicate predicate) { + pw.println("Not low: " + mStorageTracker.isStorageNotLow()); + pw.println("Sequence: " + mStorageTracker.getSeq()); + pw.println(); + for (int i = 0; i < mTrackedTasks.size(); i++) { final JobStatus js = mTrackedTasks.valueAt(i); - if (!js.shouldDump(filterUid)) { + if (!predicate.test(js)) { continue; } - pw.print(" #"); + pw.print("#"); js.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, js.getSourceUid()); @@ -197,7 +196,8 @@ public final class StorageController extends StateController { } @Override - public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate predicate) { final long token = proto.start(fieldId); final long mToken = proto.start(StateControllerProto.STORAGE); @@ -208,7 +208,7 @@ public final class StorageController extends StateController { for (int i = 0; i < mTrackedTasks.size(); i++) { final JobStatus js = mTrackedTasks.valueAt(i); - if (!js.shouldDump(filterUid)) { + if (!predicate.test(js)) { continue; } final long jsToken = proto.start(StateControllerProto.StorageController.TRACKED_JOBS); diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index cdafc3b7873f..1b2292db8c00 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -30,15 +30,16 @@ import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; -import java.io.PrintWriter; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.function.Predicate; /** * This class sets an alarm for the next expiring job, and determines whether a job's minimum @@ -348,25 +349,24 @@ public final class TimeController extends StateController { }; @Override - public void dumpControllerStateLocked(PrintWriter pw, int filterUid) { + public void dumpControllerStateLocked(IndentingPrintWriter pw, + Predicate predicate) { final long nowElapsed = sElapsedRealtimeClock.millis(); - pw.print("Alarms: now="); - pw.print(nowElapsed); - pw.println(); + pw.println("Elapsed clock: " + nowElapsed); + pw.print("Next delay alarm in "); TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw); pw.println(); pw.print("Next deadline alarm in "); TimeUtils.formatDuration(mNextJobExpiredElapsedMillis, nowElapsed, pw); pw.println(); - pw.print("Tracking "); - pw.print(mTrackedJobs.size()); - pw.println(":"); + pw.println(); + for (JobStatus ts : mTrackedJobs) { - if (!ts.shouldDump(filterUid)) { + if (!predicate.test(ts)) { continue; } - pw.print(" #"); + pw.print("#"); ts.printUniqueId(pw); pw.print(" from "); UserHandle.formatUid(pw, ts.getSourceUid()); @@ -387,7 +387,8 @@ public final class TimeController extends StateController { } @Override - public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, int filterUid) { + public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId, + Predicate predicate) { final long token = proto.start(fieldId); final long mToken = proto.start(StateControllerProto.TIME); @@ -399,7 +400,7 @@ public final class TimeController extends StateController { mNextJobExpiredElapsedMillis - nowElapsed); for (JobStatus ts : mTrackedJobs) { - if (!ts.shouldDump(filterUid)) { + if (!predicate.test(ts)) { continue; } final long tsToken = proto.start(StateControllerProto.TimeController.TRACKED_JOBS); -- cgit v1.2.3-59-g8ed1b From ac2e8efa4395d30ebeda5885dcb7cb679f793d4c Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Thu, 22 Feb 2018 16:06:44 -0700 Subject: Knobs for connectivity experiments. We keep the sane default values of 50% through a job window for delaying on congested networks and promoting prefetch jobs, but now allow experiments to override these values. There's only ever one of each controller, so just create it tied to the JobSchedulerService that owns it. (People that need hooks for testing can provide a mock JSS, instead of doing extra constructor overloads.) Use IndentingPrintWriter for dumping constants. Test: bit FrameworksServicesTests:com.android.server.job. Bug: 72353440, 73019091 Change-Id: Icdb40ee3b0bb22a20ed821888b42b87f33eb49ec --- core/proto/android/server/jobscheduler.proto | 6 + .../android/server/job/JobSchedulerService.java | 345 +++++++++++---------- .../server/job/controllers/AppIdleController.java | 28 +- .../job/controllers/BackgroundJobsController.java | 28 +- .../server/job/controllers/BatteryController.java | 25 +- .../job/controllers/ConnectivityController.java | 45 +-- .../job/controllers/ContentObserverController.java | 29 +- .../job/controllers/DeviceIdleJobsController.java | 38 +-- .../server/job/controllers/IdleController.java | 19 +- .../server/job/controllers/StateController.java | 17 +- .../server/job/controllers/StorageController.java | 27 +- .../server/job/controllers/TimeController.java | 17 +- .../controllers/ConnectivityControllerTest.java | 57 ++-- 13 files changed, 270 insertions(+), 411 deletions(-) diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto index 304e63f28151..2d31c5ad6696 100644 --- a/core/proto/android/server/jobscheduler.proto +++ b/core/proto/android/server/jobscheduler.proto @@ -201,6 +201,12 @@ message ConstantsProto { // be indices into this array, rather than the raw constants used by // AppIdleHistory. repeated int32 standby_beats = 20; + // The fraction of a job's running window that must pass before we + // consider running it when the network is congested. + optional double conn_congestion_delay_frac = 21; + // The fraction of a prefetch job's running window that must pass before + // we consider matching it against a metered network. + optional double conn_prefetch_relax_frac = 22; } message StateControllerProto { diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index ff4e1e26b489..740866c9aec3 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -164,15 +164,16 @@ public final class JobSchedulerService extends com.android.server.SystemService * {@link JobStatus#getServiceToken()} */ final List mActiveServices = new ArrayList<>(); + /** List of controllers that will notify this service of updates to jobs. */ - List mControllers; + private final List mControllers; /** Need direct access to this for testing. */ - BatteryController mBatteryController; + private final BatteryController mBatteryController; /** Need direct access to this for testing. */ - StorageController mStorageController; + private final StorageController mStorageController; /** Need directly for sending uid state changes */ - private BackgroundJobsController mBackgroundJobsController; - private DeviceIdleJobsController mDeviceIdleJobsController; + private final DeviceIdleJobsController mDeviceIdleJobsController; + /** * Queue of pending jobs. The JobServiceContext class will receive jobs from this list * when ready to execute them. @@ -254,12 +255,48 @@ public final class JobSchedulerService extends com.android.server.SystemService */ int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT]; + private class ConstantsObserver extends ContentObserver { + private ContentResolver mResolver; + + public ConstantsObserver(Handler handler) { + super(handler); + } + + public void start(ContentResolver resolver) { + mResolver = resolver; + mResolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this); + updateConstants(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + updateConstants(); + } + + private void updateConstants() { + synchronized (mLock) { + try { + mConstants.updateConstantsLocked(Settings.Global.getString(mResolver, + Settings.Global.JOB_SCHEDULER_CONSTANTS)); + } catch (IllegalArgumentException e) { + // Failed to parse the settings string, log this and move on + // with defaults. + Slog.e(TAG, "Bad jobscheduler settings", e); + } + } + + // Reset the heartbeat alarm based on the new heartbeat duration + setNextHeartbeatAlarm(); + } + } + /** * All times are in milliseconds. These constants are kept synchronized with the system * global Settings. Any access to this class or its fields should be done while * holding the JobSchedulerService.mLock lock. */ - private final class Constants extends ContentObserver { + public static class Constants { // Key names stored in the settings value. private static final String KEY_MIN_IDLE_COUNT = "min_idle_count"; private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count"; @@ -284,6 +321,8 @@ public final class JobSchedulerService extends com.android.server.SystemService private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats"; private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats"; private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats"; + private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac"; + private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac"; private static final int DEFAULT_MIN_IDLE_COUNT = 1; private static final int DEFAULT_MIN_CHARGING_COUNT = 1; @@ -307,6 +346,8 @@ public final class JobSchedulerService extends com.android.server.SystemService private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours + private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f; + private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f; /** * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things @@ -401,7 +442,6 @@ public final class JobSchedulerService extends com.android.server.SystemService * hour or day, so that the heartbeat drifts relative to wall-clock milestones. */ long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME; - /** * Mapping: standby bucket -> number of heartbeats between each sweep of that * bucket's jobs. @@ -416,171 +456,126 @@ public final class JobSchedulerService extends com.android.server.SystemService DEFAULT_STANDBY_FREQUENT_BEATS, DEFAULT_STANDBY_RARE_BEATS }; + /** + * The fraction of a job's running window that must pass before we + * consider running it when the network is congested. + */ + public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC; + /** + * The fraction of a prefetch job's running window that must pass before + * we consider matching it against a metered network. + */ + public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC; - private ContentResolver mResolver; private final KeyValueListParser mParser = new KeyValueListParser(','); - public Constants(Handler handler) { - super(handler); - } - - public void start(ContentResolver resolver) { - mResolver = resolver; - mResolver.registerContentObserver(Settings.Global.getUriFor( - Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this); - updateConstants(); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - updateConstants(); - } - - private void updateConstants() { - synchronized (mLock) { - try { - mParser.setString(Settings.Global.getString(mResolver, - Settings.Global.JOB_SCHEDULER_CONSTANTS)); - } catch (IllegalArgumentException e) { - // Failed to parse the settings string, log this and move on - // with defaults. - Slog.e(TAG, "Bad jobscheduler settings", e); - } - - MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT, - DEFAULT_MIN_IDLE_COUNT); - MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT, - DEFAULT_MIN_CHARGING_COUNT); - MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT, - DEFAULT_MIN_BATTERY_NOT_LOW_COUNT); - MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT, - DEFAULT_MIN_STORAGE_NOT_LOW_COUNT); - MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT, - DEFAULT_MIN_CONNECTIVITY_COUNT); - MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT, - DEFAULT_MIN_CONTENT_COUNT); - MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT, - DEFAULT_MIN_READY_JOBS_COUNT); - HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR, - DEFAULT_HEAVY_USE_FACTOR); - MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR, - DEFAULT_MODERATE_USE_FACTOR); - FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT, - DEFAULT_FG_JOB_COUNT); - BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT, - DEFAULT_BG_NORMAL_JOB_COUNT); - if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { - BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; - } - BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT, - DEFAULT_BG_MODERATE_JOB_COUNT); - if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { - BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; - } - BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT, - DEFAULT_BG_LOW_JOB_COUNT); - if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { - BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; - } - BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT, - DEFAULT_BG_CRITICAL_JOB_COUNT); - if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { - BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; - } - MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT, - DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT); - MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT, - DEFAULT_MAX_WORK_RESCHEDULE_COUNT); - MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME, - DEFAULT_MIN_LINEAR_BACKOFF_TIME); - MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME, - DEFAULT_MIN_EXP_BACKOFF_TIME); - STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME, - DEFAULT_STANDBY_HEARTBEAT_TIME); - STANDBY_BEATS[1] = mParser.getInt(KEY_STANDBY_WORKING_BEATS, - DEFAULT_STANDBY_WORKING_BEATS); - STANDBY_BEATS[2] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS, - DEFAULT_STANDBY_FREQUENT_BEATS); - STANDBY_BEATS[3] = mParser.getInt(KEY_STANDBY_RARE_BEATS, - DEFAULT_STANDBY_RARE_BEATS); - } - - // Reset the heartbeat alarm based on the new heartbeat duration - setNextHeartbeatAlarm(); - } - - void dump(PrintWriter pw) { - pw.println(" Settings:"); - - pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("="); - pw.print(MIN_IDLE_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("="); - pw.print(MIN_CHARGING_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("="); - pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("="); - pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("="); - pw.print(MIN_CONNECTIVITY_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("="); - pw.print(MIN_CONTENT_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("="); - pw.print(MIN_READY_JOBS_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("="); - pw.print(HEAVY_USE_FACTOR); pw.println(); - - pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("="); - pw.print(MODERATE_USE_FACTOR); pw.println(); - - pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("="); - pw.print(FG_JOB_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("="); - pw.print(BG_NORMAL_JOB_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("="); - pw.print(BG_MODERATE_JOB_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("="); - pw.print(BG_LOW_JOB_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("="); - pw.print(BG_CRITICAL_JOB_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("="); - pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("="); - pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println(); - - pw.print(" "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("="); - pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println(); - - pw.print(" "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("="); - pw.print(MIN_EXP_BACKOFF_TIME); pw.println(); - - pw.print(" "); pw.print(KEY_STANDBY_HEARTBEAT_TIME); pw.print("="); - pw.print(STANDBY_HEARTBEAT_TIME); pw.println(); - - pw.print(" standby_beats={"); + void updateConstantsLocked(String value) { + try { + mParser.setString(value); + } catch (Exception e) { + // Failed to parse the settings string, log this and move on + // with defaults. + Slog.e(TAG, "Bad jobscheduler settings", e); + } + + MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT, + DEFAULT_MIN_IDLE_COUNT); + MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT, + DEFAULT_MIN_CHARGING_COUNT); + MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT, + DEFAULT_MIN_BATTERY_NOT_LOW_COUNT); + MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT, + DEFAULT_MIN_STORAGE_NOT_LOW_COUNT); + MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT, + DEFAULT_MIN_CONNECTIVITY_COUNT); + MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT, + DEFAULT_MIN_CONTENT_COUNT); + MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT, + DEFAULT_MIN_READY_JOBS_COUNT); + HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR, + DEFAULT_HEAVY_USE_FACTOR); + MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR, + DEFAULT_MODERATE_USE_FACTOR); + FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT, + DEFAULT_FG_JOB_COUNT); + BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT, + DEFAULT_BG_NORMAL_JOB_COUNT); + if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { + BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; + } + BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT, + DEFAULT_BG_MODERATE_JOB_COUNT); + if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { + BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; + } + BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT, + DEFAULT_BG_LOW_JOB_COUNT); + if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { + BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; + } + BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT, + DEFAULT_BG_CRITICAL_JOB_COUNT); + if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) { + BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT; + } + MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT, + DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT); + MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT, + DEFAULT_MAX_WORK_RESCHEDULE_COUNT); + MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME, + DEFAULT_MIN_LINEAR_BACKOFF_TIME); + MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME, + DEFAULT_MIN_EXP_BACKOFF_TIME); + STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME, + DEFAULT_STANDBY_HEARTBEAT_TIME); + STANDBY_BEATS[1] = mParser.getInt(KEY_STANDBY_WORKING_BEATS, + DEFAULT_STANDBY_WORKING_BEATS); + STANDBY_BEATS[2] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS, + DEFAULT_STANDBY_FREQUENT_BEATS); + STANDBY_BEATS[3] = mParser.getInt(KEY_STANDBY_RARE_BEATS, + DEFAULT_STANDBY_RARE_BEATS); + CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC, + DEFAULT_CONN_CONGESTION_DELAY_FRAC); + CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC, + DEFAULT_CONN_PREFETCH_RELAX_FRAC); + } + + void dump(IndentingPrintWriter pw) { + pw.println("Settings:"); + pw.increaseIndent(); + pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println(); + pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println(); + pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println(); + pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println(); + pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println(); + pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println(); + pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println(); + pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println(); + pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println(); + pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println(); + pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println(); + pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println(); + pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println(); + pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println(); + pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println(); + pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println(); + pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println(); + pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println(); + pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println(); + pw.print("standby_beats={"); pw.print(STANDBY_BEATS[0]); for (int i = 1; i < STANDBY_BEATS.length; i++) { pw.print(", "); pw.print(STANDBY_BEATS[i]); } pw.println('}'); + pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println(); + pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println(); + pw.decreaseIndent(); } void dump(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); - proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT); proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT); proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT); @@ -600,16 +595,17 @@ public final class JobSchedulerService extends com.android.server.SystemService proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME); proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME); proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME); - for (int period : STANDBY_BEATS) { proto.write(ConstantsProto.STANDBY_BEATS, period); } - + proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC); + proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC); proto.end(token); } } final Constants mConstants; + final ConstantsObserver mConstantsObserver; static final Comparator mEnqueueTimeComparator = (o1, o2) -> { if (o1.enqueueTime < o2.enqueueTime) { @@ -779,6 +775,10 @@ public final class JobSchedulerService extends com.android.server.SystemService return mJobs; } + public Constants getConstants() { + return mConstants; + } + @Override public void onStartUser(int userHandle) { mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle); @@ -1098,7 +1098,8 @@ public final class JobSchedulerService extends com.android.server.SystemService LocalServices.getService(ActivityManagerInternal.class)); mHandler = new JobHandler(context.getMainLooper()); - mConstants = new Constants(mHandler); + mConstants = new Constants(); + mConstantsObserver = new ConstantsObserver(mHandler); mJobSchedulerStub = new JobSchedulerStub(); // Set up the app standby bucketing tracker @@ -1114,17 +1115,17 @@ public final class JobSchedulerService extends com.android.server.SystemService // Create the controllers. mControllers = new ArrayList(); - mControllers.add(ConnectivityController.get(this)); - mControllers.add(TimeController.get(this)); - mControllers.add(IdleController.get(this)); - mBatteryController = BatteryController.get(this); + mControllers.add(new ConnectivityController(this)); + mControllers.add(new TimeController(this)); + mControllers.add(new IdleController(this)); + mBatteryController = new BatteryController(this); mControllers.add(mBatteryController); - mStorageController = StorageController.get(this); + mStorageController = new StorageController(this); mControllers.add(mStorageController); - mControllers.add(BackgroundJobsController.get(this)); - mControllers.add(AppIdleController.get(this)); - mControllers.add(ContentObserverController.get(this)); - mDeviceIdleJobsController = DeviceIdleJobsController.get(this); + mControllers.add(new BackgroundJobsController(this)); + mControllers.add(new AppIdleController(this)); + mControllers.add(new ContentObserverController(this)); + mDeviceIdleJobsController = new DeviceIdleJobsController(this); mControllers.add(mDeviceIdleJobsController); // If the job store determined that it can't yet reschedule persisted jobs, @@ -1185,7 +1186,7 @@ public final class JobSchedulerService extends com.android.server.SystemService @Override public void onBootPhase(int phase) { if (PHASE_SYSTEM_SERVICES_READY == phase) { - mConstants.start(getContext().getContentResolver()); + mConstantsObserver.start(getContext().getContentResolver()); mAppStateTracker = Preconditions.checkNotNull( LocalServices.getService(AppStateTracker.class)); diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java index 4b4882499c3a..bd8fe289ddad 100644 --- a/services/core/java/com/android/server/job/controllers/AppIdleController.java +++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java @@ -17,7 +17,6 @@ package com.android.server.job.controllers; import android.app.usage.UsageStatsManagerInternal; -import android.content.Context; import android.os.UserHandle; import android.util.Log; import android.util.Slog; @@ -42,10 +41,6 @@ public final class AppIdleController extends StateController { private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); - // Singleton factory - private static Object sCreationLock = new Object(); - private static volatile AppIdleController sController; - private final JobSchedulerService mJobSchedulerService; private final UsageStatsManagerInternal mUsageStatsInternal; private boolean mInitializedParoleOn; boolean mAppIdleParoleOn; @@ -94,19 +89,8 @@ public final class AppIdleController extends StateController { } } - public static AppIdleController get(JobSchedulerService service) { - synchronized (sCreationLock) { - if (sController == null) { - sController = new AppIdleController(service, service.getContext(), - service.getLock()); - } - return sController; - } - } - - private AppIdleController(JobSchedulerService service, Context context, Object lock) { - super(service, context, lock); - mJobSchedulerService = service; + public AppIdleController(JobSchedulerService service) { + super(service); mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class); mAppIdleParoleOn = true; mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener()); @@ -139,7 +123,7 @@ public final class AppIdleController extends StateController { pw.println("Parole on: " + mAppIdleParoleOn); pw.println(); - mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { + mService.getJobStore().forEachJob(predicate, (jobStatus) -> { pw.print("#"); jobStatus.printUniqueId(pw); pw.print(" from "); @@ -162,7 +146,7 @@ public final class AppIdleController extends StateController { proto.write(StateControllerProto.AppIdleController.IS_PAROLE_ON, mAppIdleParoleOn); - mJobSchedulerService.getJobStore().forEachJob(predicate, (js) -> { + mService.getJobStore().forEachJob(predicate, (js) -> { final long jsToken = proto.start(StateControllerProto.AppIdleController.TRACKED_JOBS); js.writeToShortProto(proto, StateControllerProto.AppIdleController.TrackedJob.INFO); @@ -189,7 +173,7 @@ public final class AppIdleController extends StateController { } mAppIdleParoleOn = isAppIdleParoleOn; GlobalUpdateFunc update = new GlobalUpdateFunc(); - mJobSchedulerService.getJobStore().forEachJob(update); + mService.getJobStore().forEachJob(update); if (update.mChanged) { changed = true; } @@ -210,7 +194,7 @@ public final class AppIdleController extends StateController { } PackageUpdateFunc update = new PackageUpdateFunc(userId, packageName, idle); - mJobSchedulerService.getJobStore().forEachJob(update); + mService.getJobStore().forEachJob(update); if (update.mChanged) { changed = true; } diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java index 7a09fc4cb2af..36e75115712c 100644 --- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -16,7 +16,6 @@ package com.android.server.job.controllers; -import android.content.Context; import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; @@ -40,27 +39,10 @@ public final class BackgroundJobsController extends StateController { private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); - // Singleton factory - private static final Object sCreationLock = new Object(); - private static volatile BackgroundJobsController sController; - - private final JobSchedulerService mJobSchedulerService; - private final AppStateTracker mAppStateTracker; - public static BackgroundJobsController get(JobSchedulerService service) { - synchronized (sCreationLock) { - if (sController == null) { - sController = new BackgroundJobsController(service, service.getContext(), - service.getLock()); - } - return sController; - } - } - - private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) { - super(service, context, lock); - mJobSchedulerService = service; + public BackgroundJobsController(JobSchedulerService service) { + super(service); mAppStateTracker = Preconditions.checkNotNull( LocalServices.getService(AppStateTracker.class)); @@ -83,7 +65,7 @@ public final class BackgroundJobsController extends StateController { mAppStateTracker.dump(pw); pw.println(); - mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { + mService.getJobStore().forEachJob(predicate, (jobStatus) -> { final int uid = jobStatus.getSourceUid(); final String sourcePkg = jobStatus.getSourcePackageName(); pw.print("#"); @@ -120,7 +102,7 @@ public final class BackgroundJobsController extends StateController { mAppStateTracker.dumpProto(proto, StateControllerProto.BackgroundJobsController.FORCE_APP_STANDBY_TRACKER); - mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { + mService.getJobStore().forEachJob(predicate, (jobStatus) -> { final long jsToken = proto.start(StateControllerProto.BackgroundJobsController.TRACKED_JOBS); @@ -171,7 +153,7 @@ public final class BackgroundJobsController extends StateController { final long start = DEBUG ? SystemClock.elapsedRealtimeNanos() : 0; - mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs); + mService.getJobStore().forEachJob(updateTrackedJobs); final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0; if (DEBUG) { diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java index 24491b0b7dbc..46658ad33b85 100644 --- a/services/core/java/com/android/server/job/controllers/BatteryController.java +++ b/services/core/java/com/android/server/job/controllers/BatteryController.java @@ -34,7 +34,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; -import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; import java.util.function.Predicate; @@ -49,36 +48,16 @@ public final class BatteryController extends StateController { private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); - private static final Object sCreationLock = new Object(); - private static volatile BatteryController sController; - private final ArraySet mTrackedTasks = new ArraySet<>(); private ChargingTracker mChargeTracker; - public static BatteryController get(JobSchedulerService taskManagerService) { - synchronized (sCreationLock) { - if (sController == null) { - sController = new BatteryController(taskManagerService, - taskManagerService.getContext(), taskManagerService.getLock()); - } - } - return sController; - } - @VisibleForTesting public ChargingTracker getTracker() { return mChargeTracker; } - @VisibleForTesting - public static BatteryController getForTesting(StateChangedListener stateChangedListener, - Context context) { - return new BatteryController(stateChangedListener, context, new Object()); - } - - private BatteryController(StateChangedListener stateChangedListener, Context context, - Object lock) { - super(stateChangedListener, context, lock); + public BatteryController(JobSchedulerService service) { + super(service); mChargeTracker = new ChargingTracker(); mChargeTracker.startTracking(); } diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java index dd07bc6eb7cc..abe55bbfb729 100644 --- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java +++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java @@ -21,7 +21,6 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import android.app.job.JobInfo; -import android.content.Context; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.INetworkPolicyListener; @@ -43,8 +42,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.job.JobSchedulerService; +import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.JobServiceContext; -import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; import java.util.function.Predicate; @@ -69,22 +68,8 @@ public final class ConnectivityController extends StateController implements @GuardedBy("mLock") private final ArraySet mTrackedJobs = new ArraySet<>(); - /** Singleton. */ - private static ConnectivityController sSingleton; - private static Object sCreationLock = new Object(); - - public static ConnectivityController get(JobSchedulerService jms) { - synchronized (sCreationLock) { - if (sSingleton == null) { - sSingleton = new ConnectivityController(jms, jms.getContext(), jms.getLock()); - } - return sSingleton; - } - } - - private ConnectivityController(StateChangedListener stateChangedListener, Context context, - Object lock) { - super(stateChangedListener, context, lock); + public ConnectivityController(JobSchedulerService service) { + super(service); mConnManager = mContext.getSystemService(ConnectivityManager.class); mNetPolicyManager = mContext.getSystemService(NetworkPolicyManager.class); @@ -123,7 +108,7 @@ public final class ConnectivityController extends StateController implements */ @SuppressWarnings("unused") private static boolean isInsane(JobStatus jobStatus, Network network, - NetworkCapabilities capabilities) { + NetworkCapabilities capabilities, Constants constants) { final long estimatedBytes = jobStatus.getEstimatedNetworkBytes(); if (estimatedBytes == JobInfo.NETWORK_BYTES_UNKNOWN) { // We don't know how large the job is; cross our fingers! @@ -154,11 +139,11 @@ public final class ConnectivityController extends StateController implements @SuppressWarnings("unused") private static boolean isCongestionDelayed(JobStatus jobStatus, Network network, - NetworkCapabilities capabilities) { + NetworkCapabilities capabilities, Constants constants) { // If network is congested, and job is less than 50% through the // developer-requested window, then we're okay delaying the job. if (!capabilities.hasCapability(NET_CAPABILITY_NOT_CONGESTED)) { - return jobStatus.getFractionRunTime() < 0.5; + return jobStatus.getFractionRunTime() < constants.CONN_CONGESTION_DELAY_FRAC; } else { return false; } @@ -166,14 +151,14 @@ public final class ConnectivityController extends StateController implements @SuppressWarnings("unused") private static boolean isStrictSatisfied(JobStatus jobStatus, Network network, - NetworkCapabilities capabilities) { + NetworkCapabilities capabilities, Constants constants) { return jobStatus.getJob().getRequiredNetwork().networkCapabilities .satisfiedByNetworkCapabilities(capabilities); } @SuppressWarnings("unused") private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network, - NetworkCapabilities capabilities) { + NetworkCapabilities capabilities, Constants constants) { // Only consider doing this for prefetching jobs if ((jobStatus.getJob().getFlags() & JobInfo.FLAG_IS_PREFETCH) == 0) { return false; @@ -185,7 +170,7 @@ public final class ConnectivityController extends StateController implements .removeCapability(NET_CAPABILITY_NOT_METERED); if (relaxed.satisfiedByNetworkCapabilities(capabilities)) { // TODO: treat this as "maybe" response; need to check quotas - return jobStatus.getFractionRunTime() > 0.5; + return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC; } else { return false; } @@ -193,21 +178,21 @@ public final class ConnectivityController extends StateController implements @VisibleForTesting static boolean isSatisfied(JobStatus jobStatus, Network network, - NetworkCapabilities capabilities) { + NetworkCapabilities capabilities, Constants constants) { // Zeroth, we gotta have a network to think about being satisfied if (network == null || capabilities == null) return false; // First, are we insane? - if (isInsane(jobStatus, network, capabilities)) return false; + if (isInsane(jobStatus, network, capabilities, constants)) return false; // Second, is the network congested? - if (isCongestionDelayed(jobStatus, network, capabilities)) return false; + if (isCongestionDelayed(jobStatus, network, capabilities, constants)) return false; // Third, is the network a strict match? - if (isStrictSatisfied(jobStatus, network, capabilities)) return true; + if (isStrictSatisfied(jobStatus, network, capabilities, constants)) return true; // Third, is the network a relaxed match? - if (isRelaxedSatisfied(jobStatus, network, capabilities)) return true; + if (isRelaxedSatisfied(jobStatus, network, capabilities, constants)) return true; return false; } @@ -223,7 +208,7 @@ public final class ConnectivityController extends StateController implements final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network); final boolean connected = (info != null) && info.isConnected(); - final boolean satisfied = isSatisfied(jobStatus, network, capabilities); + final boolean satisfied = isSatisfied(jobStatus, network, capabilities, mConstants); final boolean changed = jobStatus .setConnectivityConstraintSatisfied(connected && satisfied); diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java index 27f6c1e6f87c..a775cf5a671c 100644 --- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java +++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java @@ -18,7 +18,6 @@ package com.android.server.job.controllers; import android.annotation.UserIdInt; import android.app.job.JobInfo; -import android.content.Context; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; @@ -31,10 +30,8 @@ import android.util.SparseArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.job.JobSchedulerService; -import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; import com.android.server.job.StateControllerProto.ContentObserverController.Observer.TriggerContentData; @@ -61,9 +58,6 @@ public final class ContentObserverController extends StateController { */ private static final int URIS_URGENT_THRESHOLD = 40; - private static final Object sCreationLock = new Object(); - private static volatile ContentObserverController sController; - final private ArraySet mTrackedTasks = new ArraySet<>(); /** * Per-userid {@link JobInfo.TriggerContentUri} keyed ContentObserver cache. @@ -72,26 +66,9 @@ public final class ContentObserverController extends StateController { new SparseArray<>(); final Handler mHandler; - public static ContentObserverController get(JobSchedulerService taskManagerService) { - synchronized (sCreationLock) { - if (sController == null) { - sController = new ContentObserverController(taskManagerService, - taskManagerService.getContext(), taskManagerService.getLock()); - } - } - return sController; - } - - @VisibleForTesting - public static ContentObserverController getForTesting(StateChangedListener stateChangedListener, - Context context) { - return new ContentObserverController(stateChangedListener, context, new Object()); - } - - private ContentObserverController(StateChangedListener stateChangedListener, Context context, - Object lock) { - super(stateChangedListener, context, lock); - mHandler = new Handler(context.getMainLooper()); + public ContentObserverController(JobSchedulerService service) { + super(service); + mHandler = new Handler(mContext.getMainLooper()); } @Override diff --git a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java index aa60350134eb..127a5c876657 100644 --- a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java +++ b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java @@ -57,10 +57,6 @@ public final class DeviceIdleJobsController extends StateController { static final int PROCESS_BACKGROUND_JOBS = 1; - // Singleton factory - private static Object sCreationLock = new Object(); - private static DeviceIdleJobsController sController; - /** * These are jobs added with a special flag to indicate that they should be exempted from doze * when the app is temp whitelisted or in the foreground. @@ -69,7 +65,6 @@ public final class DeviceIdleJobsController extends StateController { private final SparseBooleanArray mForegroundUids; private final DeviceIdleUpdateFunctor mDeviceIdleUpdateFunctor; private final DeviceIdleJobsDelayHandler mHandler; - private final JobSchedulerService mJobSchedulerService; private final PowerManager mPowerManager; private final DeviceIdleController.LocalService mLocalDeviceIdleController; @@ -80,19 +75,6 @@ public final class DeviceIdleJobsController extends StateController { private int[] mDeviceIdleWhitelistAppIds; private int[] mPowerSaveTempWhitelistAppIds; - /** - * Returns a singleton for the DeviceIdleJobsController - */ - public static DeviceIdleJobsController get(JobSchedulerService service) { - synchronized (sCreationLock) { - if (sController == null) { - sController = new DeviceIdleJobsController(service, service.getContext(), - service.getLock()); - } - return sController; - } - } - // onReceive private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -134,12 +116,10 @@ public final class DeviceIdleJobsController extends StateController { } }; - private DeviceIdleJobsController(JobSchedulerService jobSchedulerService, Context context, - Object lock) { - super(jobSchedulerService, context, lock); + public DeviceIdleJobsController(JobSchedulerService service) { + super(service); - mJobSchedulerService = jobSchedulerService; - mHandler = new DeviceIdleJobsDelayHandler(context.getMainLooper()); + mHandler = new DeviceIdleJobsDelayHandler(mContext.getMainLooper()); // Register for device idle mode changes mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mLocalDeviceIdleController = @@ -169,13 +149,13 @@ public final class DeviceIdleJobsController extends StateController { if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode); if (enabled) { mHandler.removeMessages(PROCESS_BACKGROUND_JOBS); - mJobSchedulerService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor); + mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor); } else { // When coming out of doze, process all foreground uids immediately, while others // will be processed after a delay of 3 seconds. for (int i = 0; i < mForegroundUids.size(); i++) { if (mForegroundUids.valueAt(i)) { - mJobSchedulerService.getJobStore().forEachJobForSourceUid( + mService.getJobStore().forEachJobForSourceUid( mForegroundUids.keyAt(i), mDeviceIdleUpdateFunctor); } } @@ -201,7 +181,7 @@ public final class DeviceIdleJobsController extends StateController { } mForegroundUids.put(uid, active); mDeviceIdleUpdateFunctor.mChanged = false; - mJobSchedulerService.getJobStore().forEachJobForSourceUid(uid, mDeviceIdleUpdateFunctor); + mService.getJobStore().forEachJobForSourceUid(uid, mDeviceIdleUpdateFunctor); if (mDeviceIdleUpdateFunctor.mChanged) { mStateChangedListener.onControllerStateChanged(); } @@ -253,7 +233,7 @@ public final class DeviceIdleJobsController extends StateController { pw.println("Idle mode: " + mDeviceIdleMode); pw.println(); - mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { + mService.getJobStore().forEachJob(predicate, (jobStatus) -> { pw.print("#"); jobStatus.printUniqueId(pw); pw.print(" from "); @@ -281,7 +261,7 @@ public final class DeviceIdleJobsController extends StateController { proto.write(StateControllerProto.DeviceIdleJobsController.IS_DEVICE_IDLE_MODE, mDeviceIdleMode); - mJobSchedulerService.getJobStore().forEachJob(predicate, (jobStatus) -> { + mService.getJobStore().forEachJob(predicate, (jobStatus) -> { final long jsToken = proto.start(StateControllerProto.DeviceIdleJobsController.TRACKED_JOBS); @@ -322,7 +302,7 @@ public final class DeviceIdleJobsController extends StateController { // Just process all the jobs, the ones in foreground should already be running. synchronized (mLock) { mDeviceIdleUpdateFunctor.mChanged = false; - mJobSchedulerService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor); + mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor); if (mDeviceIdleUpdateFunctor.mChanged) { mStateChangedListener.onControllerStateChanged(); } diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index 2cfa2c517c5a..1dbcfd6a5bdf 100644 --- a/services/core/java/com/android/server/job/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -33,7 +33,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.util.IndentingPrintWriter; import com.android.server.am.ActivityManagerService; import com.android.server.job.JobSchedulerService; -import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; import java.util.function.Predicate; @@ -50,22 +49,8 @@ public final class IdleController extends StateController { final ArraySet mTrackedTasks = new ArraySet<>(); IdlenessTracker mIdleTracker; - // Singleton factory - private static Object sCreationLock = new Object(); - private static volatile IdleController sController; - - public static IdleController get(JobSchedulerService service) { - synchronized (sCreationLock) { - if (sController == null) { - sController = new IdleController(service, service.getContext(), service.getLock()); - } - return sController; - } - } - - private IdleController(StateChangedListener stateChangedListener, Context context, - Object lock) { - super(stateChangedListener, context, lock); + public IdleController(JobSchedulerService service) { + super(service); initIdleStateTracking(); } diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java index 2b97bca6fc5f..495109d88fd7 100644 --- a/services/core/java/com/android/server/job/controllers/StateController.java +++ b/services/core/java/com/android/server/job/controllers/StateController.java @@ -20,6 +20,8 @@ import android.content.Context; import android.util.proto.ProtoOutputStream; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.job.JobSchedulerService; +import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.StateChangedListener; import java.util.function.Predicate; @@ -30,15 +32,18 @@ import java.util.function.Predicate; * are ready to run, or whether they must be stopped. */ public abstract class StateController { + protected final JobSchedulerService mService; + protected final StateChangedListener mStateChangedListener; protected final Context mContext; protected final Object mLock; - protected final StateChangedListener mStateChangedListener; + protected final Constants mConstants; - public StateController(StateChangedListener stateChangedListener, Context context, - Object lock) { - mStateChangedListener = stateChangedListener; - mContext = context; - mLock = lock; + StateController(JobSchedulerService service) { + mService = service; + mStateChangedListener = service; + mContext = service.getContext(); + mLock = service.getLock(); + mConstants = service.getConstants(); } /** diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java index e36f02d9c74a..c2ae53f69309 100644 --- a/services/core/java/com/android/server/job/controllers/StorageController.java +++ b/services/core/java/com/android/server/job/controllers/StorageController.java @@ -31,7 +31,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.job.JobSchedulerService; -import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; import com.android.server.storage.DeviceStorageMonitorService; @@ -45,36 +44,16 @@ public final class StorageController extends StateController { private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); - private static final Object sCreationLock = new Object(); - private static volatile StorageController sController; - private final ArraySet mTrackedTasks = new ArraySet(); - private StorageTracker mStorageTracker; - - public static StorageController get(JobSchedulerService taskManagerService) { - synchronized (sCreationLock) { - if (sController == null) { - sController = new StorageController(taskManagerService, - taskManagerService.getContext(), taskManagerService.getLock()); - } - } - return sController; - } + private final StorageTracker mStorageTracker; @VisibleForTesting public StorageTracker getTracker() { return mStorageTracker; } - @VisibleForTesting - public static StorageController getForTesting(StateChangedListener stateChangedListener, - Context context) { - return new StorageController(stateChangedListener, context, new Object()); - } - - private StorageController(StateChangedListener stateChangedListener, Context context, - Object lock) { - super(stateChangedListener, context, lock); + public StorageController(JobSchedulerService service) { + super(service); mStorageTracker = new StorageTracker(); mStorageTracker.startTracking(); } diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java index 1b2292db8c00..fa48b5e974f6 100644 --- a/services/core/java/com/android/server/job/controllers/TimeController.java +++ b/services/core/java/com/android/server/job/controllers/TimeController.java @@ -32,7 +32,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.util.IndentingPrintWriter; import com.android.server.job.JobSchedulerService; -import com.android.server.job.StateChangedListener; import com.android.server.job.StateControllerProto; import java.util.Iterator; @@ -63,23 +62,13 @@ public final class TimeController extends StateController { private AlarmManager mAlarmService = null; /** List of tracked jobs, sorted asc. by deadline */ private final List mTrackedJobs = new LinkedList<>(); - /** Singleton. */ - private static TimeController mSingleton; - public static synchronized TimeController get(JobSchedulerService jms) { - if (mSingleton == null) { - mSingleton = new TimeController(jms, jms.getContext(), jms.getLock()); - } - return mSingleton; - } - - private TimeController(StateChangedListener stateChangedListener, Context context, - Object lock) { - super(stateChangedListener, context, lock); + public TimeController(JobSchedulerService service) { + super(service); mNextJobExpiredElapsedMillis = Long.MAX_VALUE; mNextDelayExpiredElapsedMillis = Long.MAX_VALUE; - mChainedAttributionEnabled = WorkSource.isChainedBatteryAttributionEnabled(context); + mChainedAttributionEnabled = WorkSource.isChainedBatteryAttributionEnabled(mContext); } /** diff --git a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 35cba1855503..887489423b4c 100644 --- a/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -33,12 +33,14 @@ import android.content.pm.PackageManagerInternal; import android.net.Network; import android.net.NetworkCapabilities; import android.os.Build; +import android.os.Handler; import android.os.SystemClock; import android.support.test.runner.AndroidJUnit4; import android.util.DataUnit; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; +import com.android.server.job.JobSchedulerService.Constants; import org.junit.Before; import org.junit.Test; @@ -49,6 +51,8 @@ import java.time.ZoneOffset; @RunWith(AndroidJUnit4.class) public class ConnectivityControllerTest { + private Constants mConstants; + @Before public void setUp() throws Exception { // Assume all packages are current SDK @@ -65,23 +69,26 @@ public class ConnectivityControllerTest { Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC); JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); + + // Assume default constants for now + mConstants = new Constants(); } @Test public void testInsane() throws Exception { - final Network network = new Network(101); + final Network net = new Network(101); final JobInfo.Builder job = createJob() .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); // Slow network is too slow - assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), network, + assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1) - .setLinkDownstreamBandwidthKbps(1))); + .setLinkDownstreamBandwidthKbps(1), mConstants)); // Fast network looks great - assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), network, + assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1024) - .setLinkDownstreamBandwidthKbps(1024))); + .setLinkDownstreamBandwidthKbps(1024), mConstants)); } @Test @@ -95,19 +102,19 @@ public class ConnectivityControllerTest { // Uncongested network is whenever { - final Network network = new Network(101); - final NetworkCapabilities capabilities = createCapabilities() + final Network net = new Network(101); + final NetworkCapabilities caps = createCapabilities() .addCapability(NET_CAPABILITY_NOT_CONGESTED); - assertTrue(ConnectivityController.isSatisfied(early, network, capabilities)); - assertTrue(ConnectivityController.isSatisfied(late, network, capabilities)); + assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants)); + assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants)); } // Congested network is more selective { - final Network network = new Network(101); - final NetworkCapabilities capabilities = createCapabilities(); - assertFalse(ConnectivityController.isSatisfied(early, network, capabilities)); - assertTrue(ConnectivityController.isSatisfied(late, network, capabilities)); + final Network net = new Network(101); + final NetworkCapabilities caps = createCapabilities(); + assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants)); + assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants)); } } @@ -126,25 +133,25 @@ public class ConnectivityControllerTest { // Unmetered network is whenever { - final Network network = new Network(101); - final NetworkCapabilities capabilities = createCapabilities() + final Network net = new Network(101); + final NetworkCapabilities caps = createCapabilities() .addCapability(NET_CAPABILITY_NOT_CONGESTED) .addCapability(NET_CAPABILITY_NOT_METERED); - assertTrue(ConnectivityController.isSatisfied(early, network, capabilities)); - assertTrue(ConnectivityController.isSatisfied(late, network, capabilities)); - assertTrue(ConnectivityController.isSatisfied(earlyPrefetch, network, capabilities)); - assertTrue(ConnectivityController.isSatisfied(latePrefetch, network, capabilities)); + assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants)); + assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants)); + assertTrue(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants)); + assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants)); } // Metered network is only when prefetching and late { - final Network network = new Network(101); - final NetworkCapabilities capabilities = createCapabilities() + final Network net = new Network(101); + final NetworkCapabilities caps = createCapabilities() .addCapability(NET_CAPABILITY_NOT_CONGESTED); - assertFalse(ConnectivityController.isSatisfied(early, network, capabilities)); - assertFalse(ConnectivityController.isSatisfied(late, network, capabilities)); - assertFalse(ConnectivityController.isSatisfied(earlyPrefetch, network, capabilities)); - assertTrue(ConnectivityController.isSatisfied(latePrefetch, network, capabilities)); + assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants)); + assertFalse(ConnectivityController.isSatisfied(late, net, caps, mConstants)); + assertFalse(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants)); + assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants)); } } -- cgit v1.2.3-59-g8ed1b