Loading services/core/java/com/android/server/job/JobSchedulerService.java +16 −1 Original line number Diff line number Diff line Loading @@ -187,6 +187,9 @@ public class JobSchedulerService extends com.android.server.SystemService private final StorageController mStorageController; /** Need directly for sending uid state changes */ private final DeviceIdleJobsController mDeviceIdleJobsController; /** Needed to get remaining quota time. */ private final QuotaController mQuotaController; /** Need directly for receiving thermal events */ private IThermalService mThermalService; /** Thermal constraint. */ Loading Loading @@ -1338,7 +1341,8 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers.add(new ContentObserverController(this)); mDeviceIdleJobsController = new DeviceIdleJobsController(this); mControllers.add(mDeviceIdleJobsController); mControllers.add(new QuotaController(this)); mQuotaController = new QuotaController(this); mControllers.add(mQuotaController); // If the job store determined that it can't yet reschedule persisted jobs, // we need to start watching the clock. Loading Loading @@ -2397,6 +2401,17 @@ public class JobSchedulerService extends com.android.server.SystemService return isComponentUsable(job); } /** Returns the maximum amount of time this job could run for. */ public long getMaxJobExecutionTimeMs(JobStatus job) { synchronized (mLock) { if (mConstants.USE_HEARTBEATS) { return JobServiceContext.EXECUTING_TIMESLICE_MILLIS; } return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job), JobServiceContext.EXECUTING_TIMESLICE_MILLIS); } } /** * Reconcile jobs in the pending queue against available execution contexts. * A controller can force a job into the pending queue even if it's already running, but Loading services/core/java/com/android/server/job/controllers/ConnectivityController.java +26 −10 Original line number Diff line number Diff line Loading @@ -88,8 +88,11 @@ public final class ConnectivityController extends StateController implements @GuardedBy("mLock") private final ArraySet<Network> mAvailableNetworks = new ArraySet<>(); private boolean mUseQuotaLimit; private static final int MSG_DATA_SAVER_TOGGLED = 0; private static final int MSG_UID_RULES_CHANGES = 1; private static final int MSG_REEVALUATE_JOBS = 2; private final Handler mHandler; Loading @@ -107,6 +110,8 @@ public final class ConnectivityController extends StateController implements mConnManager.registerNetworkCallback(request, mNetworkCallback); mNetPolicyManager.registerListener(mNetPolicyListener); mUseQuotaLimit = !mConstants.USE_HEARTBEATS; } @GuardedBy("mLock") Loading Loading @@ -149,6 +154,10 @@ public final class ConnectivityController extends StateController implements } mRequestedWhitelistJobs.clear(); } if (mUseQuotaLimit == mConstants.USE_HEARTBEATS) { mUseQuotaLimit = !mConstants.USE_HEARTBEATS; mHandler.obtainMessage(MSG_REEVALUATE_JOBS).sendToTarget(); } } /** Loading Loading @@ -318,9 +327,12 @@ public final class ConnectivityController extends StateController implements * connection, it would take 10.4 minutes, and has no chance of succeeding * before the job times out, so we'd be insane to try running it. */ @SuppressWarnings("unused") private static boolean isInsane(JobStatus jobStatus, Network network, private boolean isInsane(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { final long maxJobExecutionTimeMs = mUseQuotaLimit ? mService.getMaxJobExecutionTimeMs(jobStatus) : JobServiceContext.EXECUTING_TIMESLICE_MILLIS; final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes(); if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps(); Loading @@ -329,10 +341,11 @@ public final class ConnectivityController extends StateController implements // Divide by 8 to convert bits to bytes. final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS) / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8)); if (estimatedMillis > JobServiceContext.EXECUTING_TIMESLICE_MILLIS) { if (estimatedMillis > maxJobExecutionTimeMs) { // If we'd never finish before the timeout, we'd be insane! Slog.w(TAG, "Estimated " + downloadBytes + " download bytes over " + bandwidth + " kbps network would take " + estimatedMillis + "ms; that's insane!"); + " kbps network would take " + estimatedMillis + "ms and job has " + maxJobExecutionTimeMs + "ms to run; that's insane!"); return true; } } Loading @@ -346,10 +359,11 @@ public final class ConnectivityController extends StateController implements // Divide by 8 to convert bits to bytes. final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS) / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8)); if (estimatedMillis > JobServiceContext.EXECUTING_TIMESLICE_MILLIS) { if (estimatedMillis > maxJobExecutionTimeMs) { // If we'd never finish before the timeout, we'd be insane! Slog.w(TAG, "Estimated " + uploadBytes + " upload bytes over " + bandwidth + " kbps network would take " + estimatedMillis + "ms; that's insane!"); + " kbps network would take " + estimatedMillis + "ms and job has " + maxJobExecutionTimeMs + "ms to run; that's insane!"); return true; } } Loading @@ -358,7 +372,6 @@ public final class ConnectivityController extends StateController implements return false; } @SuppressWarnings("unused") private static boolean isCongestionDelayed(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { // If network is congested, and job is less than 50% through the Loading @@ -370,14 +383,12 @@ public final class ConnectivityController extends StateController implements } } @SuppressWarnings("unused") private static boolean isStrictSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { return jobStatus.getJob().getRequiredNetwork().networkCapabilities .satisfiedByNetworkCapabilities(capabilities); } @SuppressWarnings("unused") private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { // Only consider doing this for prefetching jobs Loading @@ -398,7 +409,7 @@ public final class ConnectivityController extends StateController implements } @VisibleForTesting static boolean isSatisfied(JobStatus jobStatus, Network network, boolean isSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { // Zeroth, we gotta have a network to think about being satisfied if (network == null || capabilities == null) return false; Loading Loading @@ -594,6 +605,9 @@ public final class ConnectivityController extends StateController implements case MSG_UID_RULES_CHANGES: updateTrackedJobs(msg.arg1, null); break; case MSG_REEVALUATE_JOBS: updateTrackedJobs(-1, null); break; } } } Loading @@ -603,6 +617,8 @@ public final class ConnectivityController extends StateController implements @Override public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { pw.print("mUseQuotaLimit="); pw.println(mUseQuotaLimit); if (mRequestedWhitelistJobs.size() > 0) { pw.print("Requested standby exceptions:"); for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) { Loading services/core/java/com/android/server/job/controllers/QuotaController.java +13 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.job.ConstantsProto; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobServiceContext; import com.android.server.job.StateControllerProto; import java.util.ArrayList; Loading Loading @@ -737,6 +738,18 @@ public final class QuotaController extends StateController { return mTopStartedJobs.contains(jobStatus); } /** Returns the maximum amount of time this job could run for. */ public long getMaxJobExecutionTimeMsLocked(@NonNull final JobStatus jobStatus) { // If quota is currently "free", then the job can run for the full amount of time. if (mChargeTracker.isCharging() || mInParole || isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { return JobServiceContext.EXECUTING_TIMESLICE_MILLIS; } return getRemainingExecutionTimeLocked(jobStatus); } /** * Returns an appropriate standby bucket for the job, taking into account any standby * exemptions. Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +52 −16 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import android.util.DataUnit; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.JobServiceContext; import com.android.server.net.NetworkPolicyManagerInternal; import org.junit.Before; Loading Loading @@ -138,22 +139,53 @@ public class ConnectivityControllerTest { DataUnit.MEBIBYTES.toBytes(1)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); final ConnectivityController controller = new ConnectivityController(mService); when(mService.getMaxJobExecutionTimeMs(any())) .thenReturn(JobServiceContext.EXECUTING_TIMESLICE_MILLIS); // Slow network is too slow assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net, assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1) .setLinkDownstreamBandwidthKbps(1), mConstants)); // Slow downstream assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net, assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1024) .setLinkDownstreamBandwidthKbps(1), mConstants)); // Slow upstream assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net, assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1) .setLinkDownstreamBandwidthKbps(1024), mConstants)); // Fast network looks great assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), net, assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1024) .setLinkDownstreamBandwidthKbps(1024), mConstants)); // Slow network still good given time assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130), mConstants)); when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L); // Slow network is too slow assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1) .setLinkDownstreamBandwidthKbps(1), mConstants)); // Slow downstream assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(137) .setLinkDownstreamBandwidthKbps(1), mConstants)); // Slow upstream assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1) .setLinkDownstreamBandwidthKbps(137), mConstants)); // Network good enough assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(137) .setLinkDownstreamBandwidthKbps(137), mConstants)); // Network slightly too slow given reduced time assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130), mConstants)); } @Test Loading @@ -166,21 +198,23 @@ public class ConnectivityControllerTest { final JobStatus early = createJobStatus(job, now - 1000, now + 2000); final JobStatus late = createJobStatus(job, now - 2000, now + 1000); final ConnectivityController controller = new ConnectivityController(mService); // Uncongested network is whenever { final Network net = new Network(101); final NetworkCapabilities caps = createCapabilities() .addCapability(NET_CAPABILITY_NOT_CONGESTED); assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants)); assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants)); assertTrue(controller.isSatisfied(early, net, caps, mConstants)); assertTrue(controller.isSatisfied(late, net, caps, mConstants)); } // Congested network is more selective { final Network net = new Network(101); final NetworkCapabilities caps = createCapabilities(); assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants)); assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants)); assertFalse(controller.isSatisfied(early, net, caps, mConstants)); assertTrue(controller.isSatisfied(late, net, caps, mConstants)); } } Loading @@ -198,16 +232,18 @@ public class ConnectivityControllerTest { final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000); final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000); final ConnectivityController controller = new ConnectivityController(mService); // Unmetered network is whenever { final Network net = new Network(101); final NetworkCapabilities caps = createCapabilities() .addCapability(NET_CAPABILITY_NOT_CONGESTED) .addCapability(NET_CAPABILITY_NOT_METERED); 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)); assertTrue(controller.isSatisfied(early, net, caps, mConstants)); assertTrue(controller.isSatisfied(late, net, caps, mConstants)); assertTrue(controller.isSatisfied(earlyPrefetch, net, caps, mConstants)); assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); } // Metered network is only when prefetching and late Loading @@ -215,10 +251,10 @@ public class ConnectivityControllerTest { final Network net = new Network(101); final NetworkCapabilities caps = createCapabilities() .addCapability(NET_CAPABILITY_NOT_CONGESTED); 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)); assertFalse(controller.isSatisfied(early, net, caps, mConstants)); assertFalse(controller.isSatisfied(late, net, caps, mConstants)); assertFalse(controller.isSatisfied(earlyPrefetch, net, caps, mConstants)); assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); } } Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +33 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; Loading Loading @@ -73,6 +74,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.JobServiceContext; import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; import com.android.server.job.controllers.QuotaController.TimingSession; Loading Loading @@ -975,6 +977,37 @@ public class QuotaControllerTest { assertEquals(expectedStats, newStatsRare); } @Test public void testGetMaxJobExecutionTimeLocked() { mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0); job.setStandbyBucket(RARE_INDEX); setCharging(); assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); setDischarging(); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); // Top-started job setProcessState(ActivityManager.PROCESS_STATE_TOP); mQuotaController.maybeStartTrackingJobLocked(job, null); mQuotaController.prepareForExecutionLocked(job); setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); mQuotaController.maybeStopTrackingJobLocked(job, null, false); setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } /** * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket * window. Loading Loading
services/core/java/com/android/server/job/JobSchedulerService.java +16 −1 Original line number Diff line number Diff line Loading @@ -187,6 +187,9 @@ public class JobSchedulerService extends com.android.server.SystemService private final StorageController mStorageController; /** Need directly for sending uid state changes */ private final DeviceIdleJobsController mDeviceIdleJobsController; /** Needed to get remaining quota time. */ private final QuotaController mQuotaController; /** Need directly for receiving thermal events */ private IThermalService mThermalService; /** Thermal constraint. */ Loading Loading @@ -1338,7 +1341,8 @@ public class JobSchedulerService extends com.android.server.SystemService mControllers.add(new ContentObserverController(this)); mDeviceIdleJobsController = new DeviceIdleJobsController(this); mControllers.add(mDeviceIdleJobsController); mControllers.add(new QuotaController(this)); mQuotaController = new QuotaController(this); mControllers.add(mQuotaController); // If the job store determined that it can't yet reschedule persisted jobs, // we need to start watching the clock. Loading Loading @@ -2397,6 +2401,17 @@ public class JobSchedulerService extends com.android.server.SystemService return isComponentUsable(job); } /** Returns the maximum amount of time this job could run for. */ public long getMaxJobExecutionTimeMs(JobStatus job) { synchronized (mLock) { if (mConstants.USE_HEARTBEATS) { return JobServiceContext.EXECUTING_TIMESLICE_MILLIS; } return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job), JobServiceContext.EXECUTING_TIMESLICE_MILLIS); } } /** * Reconcile jobs in the pending queue against available execution contexts. * A controller can force a job into the pending queue even if it's already running, but Loading
services/core/java/com/android/server/job/controllers/ConnectivityController.java +26 −10 Original line number Diff line number Diff line Loading @@ -88,8 +88,11 @@ public final class ConnectivityController extends StateController implements @GuardedBy("mLock") private final ArraySet<Network> mAvailableNetworks = new ArraySet<>(); private boolean mUseQuotaLimit; private static final int MSG_DATA_SAVER_TOGGLED = 0; private static final int MSG_UID_RULES_CHANGES = 1; private static final int MSG_REEVALUATE_JOBS = 2; private final Handler mHandler; Loading @@ -107,6 +110,8 @@ public final class ConnectivityController extends StateController implements mConnManager.registerNetworkCallback(request, mNetworkCallback); mNetPolicyManager.registerListener(mNetPolicyListener); mUseQuotaLimit = !mConstants.USE_HEARTBEATS; } @GuardedBy("mLock") Loading Loading @@ -149,6 +154,10 @@ public final class ConnectivityController extends StateController implements } mRequestedWhitelistJobs.clear(); } if (mUseQuotaLimit == mConstants.USE_HEARTBEATS) { mUseQuotaLimit = !mConstants.USE_HEARTBEATS; mHandler.obtainMessage(MSG_REEVALUATE_JOBS).sendToTarget(); } } /** Loading Loading @@ -318,9 +327,12 @@ public final class ConnectivityController extends StateController implements * connection, it would take 10.4 minutes, and has no chance of succeeding * before the job times out, so we'd be insane to try running it. */ @SuppressWarnings("unused") private static boolean isInsane(JobStatus jobStatus, Network network, private boolean isInsane(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { final long maxJobExecutionTimeMs = mUseQuotaLimit ? mService.getMaxJobExecutionTimeMs(jobStatus) : JobServiceContext.EXECUTING_TIMESLICE_MILLIS; final long downloadBytes = jobStatus.getEstimatedNetworkDownloadBytes(); if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) { final long bandwidth = capabilities.getLinkDownstreamBandwidthKbps(); Loading @@ -329,10 +341,11 @@ public final class ConnectivityController extends StateController implements // Divide by 8 to convert bits to bytes. final long estimatedMillis = ((downloadBytes * DateUtils.SECOND_IN_MILLIS) / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8)); if (estimatedMillis > JobServiceContext.EXECUTING_TIMESLICE_MILLIS) { if (estimatedMillis > maxJobExecutionTimeMs) { // If we'd never finish before the timeout, we'd be insane! Slog.w(TAG, "Estimated " + downloadBytes + " download bytes over " + bandwidth + " kbps network would take " + estimatedMillis + "ms; that's insane!"); + " kbps network would take " + estimatedMillis + "ms and job has " + maxJobExecutionTimeMs + "ms to run; that's insane!"); return true; } } Loading @@ -346,10 +359,11 @@ public final class ConnectivityController extends StateController implements // Divide by 8 to convert bits to bytes. final long estimatedMillis = ((uploadBytes * DateUtils.SECOND_IN_MILLIS) / (DataUnit.KIBIBYTES.toBytes(bandwidth) / 8)); if (estimatedMillis > JobServiceContext.EXECUTING_TIMESLICE_MILLIS) { if (estimatedMillis > maxJobExecutionTimeMs) { // If we'd never finish before the timeout, we'd be insane! Slog.w(TAG, "Estimated " + uploadBytes + " upload bytes over " + bandwidth + " kbps network would take " + estimatedMillis + "ms; that's insane!"); + " kbps network would take " + estimatedMillis + "ms and job has " + maxJobExecutionTimeMs + "ms to run; that's insane!"); return true; } } Loading @@ -358,7 +372,6 @@ public final class ConnectivityController extends StateController implements return false; } @SuppressWarnings("unused") private static boolean isCongestionDelayed(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { // If network is congested, and job is less than 50% through the Loading @@ -370,14 +383,12 @@ public final class ConnectivityController extends StateController implements } } @SuppressWarnings("unused") private static boolean isStrictSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { return jobStatus.getJob().getRequiredNetwork().networkCapabilities .satisfiedByNetworkCapabilities(capabilities); } @SuppressWarnings("unused") private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { // Only consider doing this for prefetching jobs Loading @@ -398,7 +409,7 @@ public final class ConnectivityController extends StateController implements } @VisibleForTesting static boolean isSatisfied(JobStatus jobStatus, Network network, boolean isSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { // Zeroth, we gotta have a network to think about being satisfied if (network == null || capabilities == null) return false; Loading Loading @@ -594,6 +605,9 @@ public final class ConnectivityController extends StateController implements case MSG_UID_RULES_CHANGES: updateTrackedJobs(msg.arg1, null); break; case MSG_REEVALUATE_JOBS: updateTrackedJobs(-1, null); break; } } } Loading @@ -603,6 +617,8 @@ public final class ConnectivityController extends StateController implements @Override public void dumpControllerStateLocked(IndentingPrintWriter pw, Predicate<JobStatus> predicate) { pw.print("mUseQuotaLimit="); pw.println(mUseQuotaLimit); if (mRequestedWhitelistJobs.size() > 0) { pw.print("Requested standby exceptions:"); for (int i = 0; i < mRequestedWhitelistJobs.size(); i++) { Loading
services/core/java/com/android/server/job/controllers/QuotaController.java +13 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.job.ConstantsProto; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobServiceContext; import com.android.server.job.StateControllerProto; import java.util.ArrayList; Loading Loading @@ -737,6 +738,18 @@ public final class QuotaController extends StateController { return mTopStartedJobs.contains(jobStatus); } /** Returns the maximum amount of time this job could run for. */ public long getMaxJobExecutionTimeMsLocked(@NonNull final JobStatus jobStatus) { // If quota is currently "free", then the job can run for the full amount of time. if (mChargeTracker.isCharging() || mInParole || isTopStartedJobLocked(jobStatus) || isUidInForeground(jobStatus.getSourceUid())) { return JobServiceContext.EXECUTING_TIMESLICE_MILLIS; } return getRemainingExecutionTimeLocked(jobStatus); } /** * Returns an appropriate standby bucket for the job, taking into account any standby * exemptions. Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +52 −16 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ import android.util.DataUnit; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.JobServiceContext; import com.android.server.net.NetworkPolicyManagerInternal; import org.junit.Before; Loading Loading @@ -138,22 +139,53 @@ public class ConnectivityControllerTest { DataUnit.MEBIBYTES.toBytes(1)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); final ConnectivityController controller = new ConnectivityController(mService); when(mService.getMaxJobExecutionTimeMs(any())) .thenReturn(JobServiceContext.EXECUTING_TIMESLICE_MILLIS); // Slow network is too slow assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net, assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1) .setLinkDownstreamBandwidthKbps(1), mConstants)); // Slow downstream assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net, assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1024) .setLinkDownstreamBandwidthKbps(1), mConstants)); // Slow upstream assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net, assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1) .setLinkDownstreamBandwidthKbps(1024), mConstants)); // Fast network looks great assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), net, assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1024) .setLinkDownstreamBandwidthKbps(1024), mConstants)); // Slow network still good given time assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130), mConstants)); when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L); // Slow network is too slow assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1) .setLinkDownstreamBandwidthKbps(1), mConstants)); // Slow downstream assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(137) .setLinkDownstreamBandwidthKbps(1), mConstants)); // Slow upstream assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(1) .setLinkDownstreamBandwidthKbps(137), mConstants)); // Network good enough assertTrue(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(137) .setLinkDownstreamBandwidthKbps(137), mConstants)); // Network slightly too slow given reduced time assertFalse(controller.isSatisfied(createJobStatus(job), net, createCapabilities().setLinkUpstreamBandwidthKbps(130) .setLinkDownstreamBandwidthKbps(130), mConstants)); } @Test Loading @@ -166,21 +198,23 @@ public class ConnectivityControllerTest { final JobStatus early = createJobStatus(job, now - 1000, now + 2000); final JobStatus late = createJobStatus(job, now - 2000, now + 1000); final ConnectivityController controller = new ConnectivityController(mService); // Uncongested network is whenever { final Network net = new Network(101); final NetworkCapabilities caps = createCapabilities() .addCapability(NET_CAPABILITY_NOT_CONGESTED); assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants)); assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants)); assertTrue(controller.isSatisfied(early, net, caps, mConstants)); assertTrue(controller.isSatisfied(late, net, caps, mConstants)); } // Congested network is more selective { final Network net = new Network(101); final NetworkCapabilities caps = createCapabilities(); assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants)); assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants)); assertFalse(controller.isSatisfied(early, net, caps, mConstants)); assertTrue(controller.isSatisfied(late, net, caps, mConstants)); } } Loading @@ -198,16 +232,18 @@ public class ConnectivityControllerTest { final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000); final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000); final ConnectivityController controller = new ConnectivityController(mService); // Unmetered network is whenever { final Network net = new Network(101); final NetworkCapabilities caps = createCapabilities() .addCapability(NET_CAPABILITY_NOT_CONGESTED) .addCapability(NET_CAPABILITY_NOT_METERED); 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)); assertTrue(controller.isSatisfied(early, net, caps, mConstants)); assertTrue(controller.isSatisfied(late, net, caps, mConstants)); assertTrue(controller.isSatisfied(earlyPrefetch, net, caps, mConstants)); assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); } // Metered network is only when prefetching and late Loading @@ -215,10 +251,10 @@ public class ConnectivityControllerTest { final Network net = new Network(101); final NetworkCapabilities caps = createCapabilities() .addCapability(NET_CAPABILITY_NOT_CONGESTED); 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)); assertFalse(controller.isSatisfied(early, net, caps, mConstants)); assertFalse(controller.isSatisfied(late, net, caps, mConstants)); assertFalse(controller.isSatisfied(earlyPrefetch, net, caps, mConstants)); assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); } } Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +33 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; Loading Loading @@ -73,6 +74,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerService; import com.android.server.job.JobSchedulerService.Constants; import com.android.server.job.JobServiceContext; import com.android.server.job.JobStore; import com.android.server.job.controllers.QuotaController.ExecutionStats; import com.android.server.job.controllers.QuotaController.TimingSession; Loading Loading @@ -975,6 +977,37 @@ public class QuotaControllerTest { assertEquals(expectedStats, newStatsRare); } @Test public void testGetMaxJobExecutionTimeLocked() { mQuotaController.saveTimingSession(0, SOURCE_PACKAGE, createTimingSession(sElapsedRealtimeClock.millis() - (6 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5)); JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked", 0); job.setStandbyBucket(RARE_INDEX); setCharging(); assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); setDischarging(); setProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); // Top-started job setProcessState(ActivityManager.PROCESS_STATE_TOP); mQuotaController.maybeStartTrackingJobLocked(job, null); mQuotaController.prepareForExecutionLocked(job); setProcessState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); assertEquals(JobServiceContext.EXECUTING_TIMESLICE_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked((job))); mQuotaController.maybeStopTrackingJobLocked(job, null, false); setProcessState(ActivityManager.PROCESS_STATE_RECEIVER); assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getMaxJobExecutionTimeMsLocked(job)); } /** * Test getTimeUntilQuotaConsumedLocked when the determination is based within the bucket * window. Loading