Loading apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java +0 −1 Original line number Diff line number Diff line Loading @@ -113,7 +113,6 @@ public class ComponentController extends StateController { userFilter.addAction(Intent.ACTION_USER_STOPPED); mContext.registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); } @Override Loading apex/jobscheduler/service/java/com/android/server/job/controllers/Package.java 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.job.controllers; import java.util.Objects; /** Wrapper class to represent a userId-pkgName combo. */ final class Package { public final String packageName; public final int userId; Package(int userId, String packageName) { this.userId = userId; this.packageName = packageName; } @Override public String toString() { return packageToString(userId, packageName); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Package)) { return false; } Package other = (Package) obj; return userId == other.userId && Objects.equals(packageName, other.packageName); } @Override public int hashCode() { return packageName.hashCode() + userId; } /** * Standardize the output of userId-packageName combo. */ static String packageToString(int userId, String packageName) { return "<" + userId + ">" + packageName; } } apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +112 −13 Original line number Diff line number Diff line Loading @@ -20,9 +20,13 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; import static com.android.server.job.controllers.Package.packageToString; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.content.Context; import android.os.Looper; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArraySet; Loading @@ -36,6 +40,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.job.JobSchedulerService; import com.android.server.utils.AlarmQueue; import java.util.function.Predicate; Loading @@ -57,6 +62,7 @@ public class PrefetchController extends StateController { */ @GuardedBy("mLock") private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>(); private final ThresholdAlarmListener mThresholdAlarmListener; /** * The cutoff point to decide if a prefetch job is worth running or not. If the app is expected Loading @@ -69,6 +75,8 @@ public class PrefetchController extends StateController { public PrefetchController(JobSchedulerService service) { super(service); mPcConstants = new PcConstants(); mThresholdAlarmListener = new ThresholdAlarmListener( mContext, JobSchedulerBackgroundThread.get().getLooper()); } @Override Loading @@ -82,9 +90,13 @@ public class PrefetchController extends StateController { jobs = new ArraySet<>(); mTrackedJobs.add(userId, pkgName, jobs); } jobs.add(jobStatus); updateConstraintLocked(jobStatus, sSystemClock.millis(), sElapsedRealtimeClock.millis()); final long now = sSystemClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); if (jobs.add(jobStatus) && jobs.size() == 1 && !willBeLaunchedSoonLocked(userId, pkgName, now)) { updateThresholdAlarmLocked(userId, pkgName, now, nowElapsed); } updateConstraintLocked(jobStatus, now, nowElapsed); } } Loading @@ -92,10 +104,11 @@ public class PrefetchController extends StateController { @GuardedBy("mLock") public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) { final ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); if (jobs != null) { jobs.remove(jobStatus); final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); if (jobs != null && jobs.remove(jobStatus) && jobs.size() == 0) { mThresholdAlarmListener.removeAlarmForKey(new Package(userId, pkgName)); } } Loading @@ -109,6 +122,7 @@ public class PrefetchController extends StateController { final int userId = UserHandle.getUserId(uid); mTrackedJobs.delete(userId, packageName); mEstimatedLaunchTimes.delete(userId, packageName); mThresholdAlarmListener.removeAlarmForKey(new Package(userId, packageName)); } @Override Loading @@ -116,6 +130,7 @@ public class PrefetchController extends StateController { public void onUserRemovedLocked(int userId) { mTrackedJobs.delete(userId); mEstimatedLaunchTimes.delete(userId); mThresholdAlarmListener.removeAlarmsForUserId(userId); } /** Return the app's next estimated launch time. */ Loading @@ -124,8 +139,14 @@ public class PrefetchController extends StateController { public long getNextEstimatedLaunchTimeLocked(@NonNull JobStatus jobStatus) { final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); return getNextEstimatedLaunchTimeLocked(userId, pkgName, sSystemClock.millis()); } @GuardedBy("mLock") @CurrentTimeMillisLong private long getNextEstimatedLaunchTimeLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now) { Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); final long now = sSystemClock.millis(); if (nextEstimatedLaunchTime == null || nextEstimatedLaunchTime < now) { // TODO(194532703): get estimated time from UsageStats nextEstimatedLaunchTime = now + 2 * HOUR_IN_MILLIS; Loading @@ -135,8 +156,8 @@ public class PrefetchController extends StateController { } @GuardedBy("mLock") private boolean maybeUpdateConstraintForPkgLocked(long now, long nowElapsed, int userId, String pkgName) { private boolean maybeUpdateConstraintForPkgLocked(@CurrentTimeMillisLong long now, @ElapsedRealtimeLong long nowElapsed, int userId, String pkgName) { final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); if (jobs == null) { return false; Loading @@ -150,10 +171,43 @@ public class PrefetchController extends StateController { } @GuardedBy("mLock") private boolean updateConstraintLocked(@NonNull JobStatus jobStatus, long now, long nowElapsed) { private boolean updateConstraintLocked(@NonNull JobStatus jobStatus, @CurrentTimeMillisLong long now, @ElapsedRealtimeLong long nowElapsed) { return jobStatus.setPrefetchConstraintSatisfied(nowElapsed, getNextEstimatedLaunchTimeLocked(jobStatus) <= now + mLaunchTimeThresholdMs); willBeLaunchedSoonLocked( jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), now)); } @GuardedBy("mLock") private void updateThresholdAlarmLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now, @ElapsedRealtimeLong long nowElapsed) { final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); if (jobs == null || jobs.size() == 0) { mThresholdAlarmListener.removeAlarmForKey(new Package(userId, pkgName)); return; } final long nextEstimatedLaunchTime = getNextEstimatedLaunchTimeLocked(userId, pkgName, now); if (nextEstimatedLaunchTime - now > mLaunchTimeThresholdMs) { // Set alarm to be notified when this crosses the threshold. final long timeToCrossThresholdMs = nextEstimatedLaunchTime - (now + mLaunchTimeThresholdMs); mThresholdAlarmListener.addAlarm(new Package(userId, pkgName), nowElapsed + timeToCrossThresholdMs); } else { mThresholdAlarmListener.removeAlarmForKey(new Package(userId, pkgName)); } } /** * Returns true if the app is expected to be launched soon, where "soon" is within the next * {@link #mLaunchTimeThresholdMs} time. */ @GuardedBy("mLock") private boolean willBeLaunchedSoonLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now) { return getNextEstimatedLaunchTimeLocked(userId, pkgName, now) <= now + mLaunchTimeThresholdMs; } @Override Loading Loading @@ -186,6 +240,9 @@ public class PrefetchController extends StateController { now, nowElapsed, userId, packageName)) { changedJobs.addAll(mTrackedJobs.valueAt(u, p)); } if (!willBeLaunchedSoonLocked(userId, packageName, now)) { updateThresholdAlarmLocked(userId, packageName, now, nowElapsed); } } } } Loading @@ -196,6 +253,42 @@ public class PrefetchController extends StateController { } } /** Track when apps will cross the "will run soon" threshold. */ private class ThresholdAlarmListener extends AlarmQueue<Package> { private ThresholdAlarmListener(Context context, Looper looper) { super(context, looper, "*job.prefetch*", "Prefetch threshold", false, PcConstants.DEFAULT_LAUNCH_TIME_THRESHOLD_MS / 10); } @Override protected boolean isForUser(@NonNull Package key, int userId) { return key.userId == userId; } @Override protected void processExpiredAlarms(@NonNull ArraySet<Package> expired) { final ArraySet<JobStatus> changedJobs = new ArraySet<>(); synchronized (mLock) { final long now = sSystemClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = 0; i < expired.size(); ++i) { Package p = expired.valueAt(i); if (!willBeLaunchedSoonLocked(p.userId, p.packageName, now)) { Slog.e(TAG, "Alarm expired for " + packageToString(p.userId, p.packageName) + " at the wrong time"); updateThresholdAlarmLocked(p.userId, p.packageName, now, nowElapsed); } else if (maybeUpdateConstraintForPkgLocked( now, nowElapsed, p.userId, p.packageName)) { changedJobs.addAll(mTrackedJobs.get(p.userId, p.packageName)); } } } if (changedJobs.size() > 0) { mStateChangedListener.onControllerStateChanged(changedJobs); } } } @VisibleForTesting class PcConstants { private boolean mShouldReevaluateConstraints = false; Loading Loading @@ -225,6 +318,9 @@ public class PrefetchController extends StateController { if (mLaunchTimeThresholdMs != newLaunchTimeThresholdMs) { mLaunchTimeThresholdMs = newLaunchTimeThresholdMs; mShouldReevaluateConstraints = true; // Give a leeway of 10% of the launch time threshold between alarms. mThresholdAlarmListener.setMinTimeBetweenAlarmsMs( mLaunchTimeThresholdMs / 10); } break; } Loading Loading @@ -294,6 +390,9 @@ public class PrefetchController extends StateController { pw.println(); } }); pw.println(); mThresholdAlarmListener.dump(pw); } @Override Loading apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +10 −58 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.controllers.Package.packageToString; import android.Manifest; import android.annotation.NonNull; Loading Loading @@ -79,7 +80,6 @@ import com.android.server.utils.AlarmQueue; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; Loading Loading @@ -123,52 +123,6 @@ public final class QuotaController extends StateController { PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.GET_PERMISSIONS | PackageManager.MATCH_KNOWN_PACKAGES; /** * Standardize the output of userId-packageName combo. */ private static String string(int userId, String packageName) { return "<" + userId + ">" + packageName; } private static final class Package { public final String packageName; public final int userId; Package(int userId, String packageName) { this.userId = userId; this.packageName = packageName; } @Override public String toString() { return string(userId, packageName); } public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(StateControllerProto.QuotaController.Package.USER_ID, userId); proto.write(StateControllerProto.QuotaController.Package.NAME, packageName); proto.end(token); } @Override public boolean equals(Object obj) { if (obj instanceof Package) { Package other = (Package) obj; return userId == other.userId && Objects.equals(packageName, other.packageName); } else { return false; } } @Override public int hashCode() { return packageName.hashCode() + userId; } } private static int hashLong(long val) { return (int) (val ^ (val >>> 32)); } Loading Loading @@ -1741,7 +1695,6 @@ public final class QuotaController extends StateController { return; } final String pkgString = string(userId, packageName); ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket); final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket); final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats, Loading @@ -1755,7 +1708,8 @@ public final class QuotaController extends StateController { if (inRegularQuota && remainingEJQuota > 0) { // Already in quota. Why was this method called? if (DEBUG) { Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + packageToString(userId, packageName) + " even though it already has " + getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) + "ms in its quota."); Loading Loading @@ -1811,8 +1765,8 @@ public final class QuotaController extends StateController { // In some strange cases, an app may end be in the NEVER bucket but could have run // some regular jobs. This results in no EJ timing sessions and QC having a bad // time. Slog.wtf(TAG, string(userId, packageName) + " has 0 EJ quota without running anything"); Slog.wtf(TAG, packageToString(userId, packageName) + " has 0 EJ quota without running anything"); return; } } Loading Loading @@ -2272,7 +2226,6 @@ public final class QuotaController extends StateController { public void dump(ProtoOutputStream proto, long fieldId, Predicate<JobStatus> predicate) { final long token = proto.start(fieldId); mPkg.dumpDebug(proto, StateControllerProto.QuotaController.Timer.PKG); proto.write(StateControllerProto.QuotaController.Timer.IS_ACTIVE, isActive()); proto.write(StateControllerProto.QuotaController.Timer.START_TIME_ELAPSED, mStartTimeElapsed); Loading Loading @@ -2381,7 +2334,6 @@ public final class QuotaController extends StateController { public void dump(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); mPkg.dumpDebug(proto, StateControllerProto.QuotaController.TopAppTimer.PKG); proto.write(StateControllerProto.QuotaController.TopAppTimer.IS_ACTIVE, isActive()); proto.write(StateControllerProto.QuotaController.TopAppTimer.START_TIME_ELAPSED, mStartTimeElapsed); Loading Loading @@ -2413,7 +2365,7 @@ public final class QuotaController extends StateController { void updateStandbyBucket( final int userId, final @NonNull String packageName, final int bucketIndex) { if (DEBUG) { Slog.i(TAG, "Moving pkg " + string(userId, packageName) Slog.i(TAG, "Moving pkg " + packageToString(userId, packageName) + " to bucketIndex " + bucketIndex); } List<JobStatus> restrictedChanges = new ArrayList<>(); Loading Loading @@ -2641,7 +2593,7 @@ public final class QuotaController extends StateController { String packageName = (String) msg.obj; int userId = msg.arg1; if (DEBUG) { Slog.d(TAG, "Checking pkg " + string(userId, packageName)); Slog.d(TAG, "Checking pkg " + packageToString(userId, packageName)); } if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), userId, packageName)) { Loading Loading @@ -2722,7 +2674,7 @@ public final class QuotaController extends StateController { final String pkgName = event.getPackageName(); if (DEBUG) { Slog.d(TAG, "Processing event " + event.getEventType() + " for " + string(userId, pkgName)); + " for " + packageToString(userId, pkgName)); } switch (event.getEventType()) { case UsageEvents.Event.ACTIVITY_RESUMED: Loading Loading @@ -4119,7 +4071,7 @@ public final class QuotaController extends StateController { final String pkgName = mExecutionStatsCache.keyAt(u, p); ExecutionStats[] stats = mExecutionStatsCache.valueAt(u, p); pw.println(string(userId, pkgName)); pw.println(packageToString(userId, pkgName)); pw.increaseIndent(); for (int i = 0; i < stats.length; ++i) { ExecutionStats executionStats = stats[i]; Loading @@ -4143,7 +4095,7 @@ public final class QuotaController extends StateController { final String pkgName = mEJStats.keyAt(u, p); ShrinkableDebits debits = mEJStats.valueAt(u, p); pw.print(string(userId, pkgName)); pw.print(packageToString(userId, pkgName)); pw.print(": "); debits.dumpLocked(pw); } Loading Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/ComponentController.java +0 −1 Original line number Diff line number Diff line Loading @@ -113,7 +113,6 @@ public class ComponentController extends StateController { userFilter.addAction(Intent.ACTION_USER_STOPPED); mContext.registerReceiverAsUser( mBroadcastReceiver, UserHandle.ALL, userFilter, null, null); } @Override Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/Package.java 0 → 100644 +59 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.job.controllers; import java.util.Objects; /** Wrapper class to represent a userId-pkgName combo. */ final class Package { public final String packageName; public final int userId; Package(int userId, String packageName) { this.userId = userId; this.packageName = packageName; } @Override public String toString() { return packageToString(userId, packageName); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Package)) { return false; } Package other = (Package) obj; return userId == other.userId && Objects.equals(packageName, other.packageName); } @Override public int hashCode() { return packageName.hashCode() + userId; } /** * Standardize the output of userId-packageName combo. */ static String packageToString(int userId, String packageName) { return "<" + userId + ">" + packageName; } }
apex/jobscheduler/service/java/com/android/server/job/controllers/PrefetchController.java +112 −13 Original line number Diff line number Diff line Loading @@ -20,9 +20,13 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; import static com.android.server.job.controllers.Package.packageToString; import android.annotation.CurrentTimeMillisLong; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.content.Context; import android.os.Looper; import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArraySet; Loading @@ -36,6 +40,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.JobSchedulerBackgroundThread; import com.android.server.job.JobSchedulerService; import com.android.server.utils.AlarmQueue; import java.util.function.Predicate; Loading @@ -57,6 +62,7 @@ public class PrefetchController extends StateController { */ @GuardedBy("mLock") private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>(); private final ThresholdAlarmListener mThresholdAlarmListener; /** * The cutoff point to decide if a prefetch job is worth running or not. If the app is expected Loading @@ -69,6 +75,8 @@ public class PrefetchController extends StateController { public PrefetchController(JobSchedulerService service) { super(service); mPcConstants = new PcConstants(); mThresholdAlarmListener = new ThresholdAlarmListener( mContext, JobSchedulerBackgroundThread.get().getLooper()); } @Override Loading @@ -82,9 +90,13 @@ public class PrefetchController extends StateController { jobs = new ArraySet<>(); mTrackedJobs.add(userId, pkgName, jobs); } jobs.add(jobStatus); updateConstraintLocked(jobStatus, sSystemClock.millis(), sElapsedRealtimeClock.millis()); final long now = sSystemClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); if (jobs.add(jobStatus) && jobs.size() == 1 && !willBeLaunchedSoonLocked(userId, pkgName, now)) { updateThresholdAlarmLocked(userId, pkgName, now, nowElapsed); } updateConstraintLocked(jobStatus, now, nowElapsed); } } Loading @@ -92,10 +104,11 @@ public class PrefetchController extends StateController { @GuardedBy("mLock") public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) { final ArraySet<JobStatus> jobs = mTrackedJobs.get(jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()); if (jobs != null) { jobs.remove(jobStatus); final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); if (jobs != null && jobs.remove(jobStatus) && jobs.size() == 0) { mThresholdAlarmListener.removeAlarmForKey(new Package(userId, pkgName)); } } Loading @@ -109,6 +122,7 @@ public class PrefetchController extends StateController { final int userId = UserHandle.getUserId(uid); mTrackedJobs.delete(userId, packageName); mEstimatedLaunchTimes.delete(userId, packageName); mThresholdAlarmListener.removeAlarmForKey(new Package(userId, packageName)); } @Override Loading @@ -116,6 +130,7 @@ public class PrefetchController extends StateController { public void onUserRemovedLocked(int userId) { mTrackedJobs.delete(userId); mEstimatedLaunchTimes.delete(userId); mThresholdAlarmListener.removeAlarmsForUserId(userId); } /** Return the app's next estimated launch time. */ Loading @@ -124,8 +139,14 @@ public class PrefetchController extends StateController { public long getNextEstimatedLaunchTimeLocked(@NonNull JobStatus jobStatus) { final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); return getNextEstimatedLaunchTimeLocked(userId, pkgName, sSystemClock.millis()); } @GuardedBy("mLock") @CurrentTimeMillisLong private long getNextEstimatedLaunchTimeLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now) { Long nextEstimatedLaunchTime = mEstimatedLaunchTimes.get(userId, pkgName); final long now = sSystemClock.millis(); if (nextEstimatedLaunchTime == null || nextEstimatedLaunchTime < now) { // TODO(194532703): get estimated time from UsageStats nextEstimatedLaunchTime = now + 2 * HOUR_IN_MILLIS; Loading @@ -135,8 +156,8 @@ public class PrefetchController extends StateController { } @GuardedBy("mLock") private boolean maybeUpdateConstraintForPkgLocked(long now, long nowElapsed, int userId, String pkgName) { private boolean maybeUpdateConstraintForPkgLocked(@CurrentTimeMillisLong long now, @ElapsedRealtimeLong long nowElapsed, int userId, String pkgName) { final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); if (jobs == null) { return false; Loading @@ -150,10 +171,43 @@ public class PrefetchController extends StateController { } @GuardedBy("mLock") private boolean updateConstraintLocked(@NonNull JobStatus jobStatus, long now, long nowElapsed) { private boolean updateConstraintLocked(@NonNull JobStatus jobStatus, @CurrentTimeMillisLong long now, @ElapsedRealtimeLong long nowElapsed) { return jobStatus.setPrefetchConstraintSatisfied(nowElapsed, getNextEstimatedLaunchTimeLocked(jobStatus) <= now + mLaunchTimeThresholdMs); willBeLaunchedSoonLocked( jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), now)); } @GuardedBy("mLock") private void updateThresholdAlarmLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now, @ElapsedRealtimeLong long nowElapsed) { final ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); if (jobs == null || jobs.size() == 0) { mThresholdAlarmListener.removeAlarmForKey(new Package(userId, pkgName)); return; } final long nextEstimatedLaunchTime = getNextEstimatedLaunchTimeLocked(userId, pkgName, now); if (nextEstimatedLaunchTime - now > mLaunchTimeThresholdMs) { // Set alarm to be notified when this crosses the threshold. final long timeToCrossThresholdMs = nextEstimatedLaunchTime - (now + mLaunchTimeThresholdMs); mThresholdAlarmListener.addAlarm(new Package(userId, pkgName), nowElapsed + timeToCrossThresholdMs); } else { mThresholdAlarmListener.removeAlarmForKey(new Package(userId, pkgName)); } } /** * Returns true if the app is expected to be launched soon, where "soon" is within the next * {@link #mLaunchTimeThresholdMs} time. */ @GuardedBy("mLock") private boolean willBeLaunchedSoonLocked(int userId, @NonNull String pkgName, @CurrentTimeMillisLong long now) { return getNextEstimatedLaunchTimeLocked(userId, pkgName, now) <= now + mLaunchTimeThresholdMs; } @Override Loading Loading @@ -186,6 +240,9 @@ public class PrefetchController extends StateController { now, nowElapsed, userId, packageName)) { changedJobs.addAll(mTrackedJobs.valueAt(u, p)); } if (!willBeLaunchedSoonLocked(userId, packageName, now)) { updateThresholdAlarmLocked(userId, packageName, now, nowElapsed); } } } } Loading @@ -196,6 +253,42 @@ public class PrefetchController extends StateController { } } /** Track when apps will cross the "will run soon" threshold. */ private class ThresholdAlarmListener extends AlarmQueue<Package> { private ThresholdAlarmListener(Context context, Looper looper) { super(context, looper, "*job.prefetch*", "Prefetch threshold", false, PcConstants.DEFAULT_LAUNCH_TIME_THRESHOLD_MS / 10); } @Override protected boolean isForUser(@NonNull Package key, int userId) { return key.userId == userId; } @Override protected void processExpiredAlarms(@NonNull ArraySet<Package> expired) { final ArraySet<JobStatus> changedJobs = new ArraySet<>(); synchronized (mLock) { final long now = sSystemClock.millis(); final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = 0; i < expired.size(); ++i) { Package p = expired.valueAt(i); if (!willBeLaunchedSoonLocked(p.userId, p.packageName, now)) { Slog.e(TAG, "Alarm expired for " + packageToString(p.userId, p.packageName) + " at the wrong time"); updateThresholdAlarmLocked(p.userId, p.packageName, now, nowElapsed); } else if (maybeUpdateConstraintForPkgLocked( now, nowElapsed, p.userId, p.packageName)) { changedJobs.addAll(mTrackedJobs.get(p.userId, p.packageName)); } } } if (changedJobs.size() > 0) { mStateChangedListener.onControllerStateChanged(changedJobs); } } } @VisibleForTesting class PcConstants { private boolean mShouldReevaluateConstraints = false; Loading Loading @@ -225,6 +318,9 @@ public class PrefetchController extends StateController { if (mLaunchTimeThresholdMs != newLaunchTimeThresholdMs) { mLaunchTimeThresholdMs = newLaunchTimeThresholdMs; mShouldReevaluateConstraints = true; // Give a leeway of 10% of the launch time threshold between alarms. mThresholdAlarmListener.setMinTimeBetweenAlarmsMs( mLaunchTimeThresholdMs / 10); } break; } Loading Loading @@ -294,6 +390,9 @@ public class PrefetchController extends StateController { pw.println(); } }); pw.println(); mThresholdAlarmListener.dump(pw); } @Override Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +10 −58 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.controllers.Package.packageToString; import android.Manifest; import android.annotation.NonNull; Loading Loading @@ -79,7 +80,6 @@ import com.android.server.utils.AlarmQueue; import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; Loading Loading @@ -123,52 +123,6 @@ public final class QuotaController extends StateController { PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.GET_PERMISSIONS | PackageManager.MATCH_KNOWN_PACKAGES; /** * Standardize the output of userId-packageName combo. */ private static String string(int userId, String packageName) { return "<" + userId + ">" + packageName; } private static final class Package { public final String packageName; public final int userId; Package(int userId, String packageName) { this.userId = userId; this.packageName = packageName; } @Override public String toString() { return string(userId, packageName); } public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(StateControllerProto.QuotaController.Package.USER_ID, userId); proto.write(StateControllerProto.QuotaController.Package.NAME, packageName); proto.end(token); } @Override public boolean equals(Object obj) { if (obj instanceof Package) { Package other = (Package) obj; return userId == other.userId && Objects.equals(packageName, other.packageName); } else { return false; } } @Override public int hashCode() { return packageName.hashCode() + userId; } } private static int hashLong(long val) { return (int) (val ^ (val >>> 32)); } Loading Loading @@ -1741,7 +1695,6 @@ public final class QuotaController extends StateController { return; } final String pkgString = string(userId, packageName); ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket); final boolean isUnderJobCountQuota = isUnderJobCountQuotaLocked(stats, standbyBucket); final boolean isUnderTimingSessionCountQuota = isUnderSessionCountQuotaLocked(stats, Loading @@ -1755,7 +1708,8 @@ public final class QuotaController extends StateController { if (inRegularQuota && remainingEJQuota > 0) { // Already in quota. Why was this method called? if (DEBUG) { Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + pkgString Slog.e(TAG, "maybeScheduleStartAlarmLocked called for " + packageToString(userId, packageName) + " even though it already has " + getRemainingExecutionTimeLocked(userId, packageName, standbyBucket) + "ms in its quota."); Loading Loading @@ -1811,8 +1765,8 @@ public final class QuotaController extends StateController { // In some strange cases, an app may end be in the NEVER bucket but could have run // some regular jobs. This results in no EJ timing sessions and QC having a bad // time. Slog.wtf(TAG, string(userId, packageName) + " has 0 EJ quota without running anything"); Slog.wtf(TAG, packageToString(userId, packageName) + " has 0 EJ quota without running anything"); return; } } Loading Loading @@ -2272,7 +2226,6 @@ public final class QuotaController extends StateController { public void dump(ProtoOutputStream proto, long fieldId, Predicate<JobStatus> predicate) { final long token = proto.start(fieldId); mPkg.dumpDebug(proto, StateControllerProto.QuotaController.Timer.PKG); proto.write(StateControllerProto.QuotaController.Timer.IS_ACTIVE, isActive()); proto.write(StateControllerProto.QuotaController.Timer.START_TIME_ELAPSED, mStartTimeElapsed); Loading Loading @@ -2381,7 +2334,6 @@ public final class QuotaController extends StateController { public void dump(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); mPkg.dumpDebug(proto, StateControllerProto.QuotaController.TopAppTimer.PKG); proto.write(StateControllerProto.QuotaController.TopAppTimer.IS_ACTIVE, isActive()); proto.write(StateControllerProto.QuotaController.TopAppTimer.START_TIME_ELAPSED, mStartTimeElapsed); Loading Loading @@ -2413,7 +2365,7 @@ public final class QuotaController extends StateController { void updateStandbyBucket( final int userId, final @NonNull String packageName, final int bucketIndex) { if (DEBUG) { Slog.i(TAG, "Moving pkg " + string(userId, packageName) Slog.i(TAG, "Moving pkg " + packageToString(userId, packageName) + " to bucketIndex " + bucketIndex); } List<JobStatus> restrictedChanges = new ArrayList<>(); Loading Loading @@ -2641,7 +2593,7 @@ public final class QuotaController extends StateController { String packageName = (String) msg.obj; int userId = msg.arg1; if (DEBUG) { Slog.d(TAG, "Checking pkg " + string(userId, packageName)); Slog.d(TAG, "Checking pkg " + packageToString(userId, packageName)); } if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), userId, packageName)) { Loading Loading @@ -2722,7 +2674,7 @@ public final class QuotaController extends StateController { final String pkgName = event.getPackageName(); if (DEBUG) { Slog.d(TAG, "Processing event " + event.getEventType() + " for " + string(userId, pkgName)); + " for " + packageToString(userId, pkgName)); } switch (event.getEventType()) { case UsageEvents.Event.ACTIVITY_RESUMED: Loading Loading @@ -4119,7 +4071,7 @@ public final class QuotaController extends StateController { final String pkgName = mExecutionStatsCache.keyAt(u, p); ExecutionStats[] stats = mExecutionStatsCache.valueAt(u, p); pw.println(string(userId, pkgName)); pw.println(packageToString(userId, pkgName)); pw.increaseIndent(); for (int i = 0; i < stats.length; ++i) { ExecutionStats executionStats = stats[i]; Loading @@ -4143,7 +4095,7 @@ public final class QuotaController extends StateController { final String pkgName = mEJStats.keyAt(u, p); ShrinkableDebits debits = mEJStats.valueAt(u, p); pw.print(string(userId, pkgName)); pw.print(packageToString(userId, pkgName)); pw.print(": "); debits.dumpLocked(pw); } Loading