From d07752e1d162803f78532745ffcc11284b4eccd6 Mon Sep 17 00:00:00 2001 From: Kweku Adams Date: Thu, 17 Jun 2021 15:15:38 -0700 Subject: [PATCH] Check opportunistic quota on metered networks. Check that a job's total estimated data transfer will fit in the opportunistic quota before allowing it to run on the unmetered network. Bug: 189104483 Test: atest FrameworksMockingServicesTests:ConnectivityControllerTest Test: atest CtsJobSchedulerTestCases:ConnectivityConstraintTest Change-Id: I6d7e83d87dc3d1522c934e7441f4975418d571e7 --- .../controllers/ConnectivityController.java | 23 ++++++++++++++----- .../ConnectivityControllerTest.java | 23 ++++++++++++++++++- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index e8065aa535f33..41ff9c8e3b7eb 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -633,23 +633,34 @@ public final class ConnectivityController extends RestrictingController implemen } } - private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network, + private boolean isRelaxedSatisfied(JobStatus jobStatus, Network network, NetworkCapabilities capabilities, Constants constants) { // Only consider doing this for unrestricted prefetching jobs if (!jobStatus.getJob().isPrefetch() || jobStatus.getStandbyBucket() == RESTRICTED_INDEX) { return false; } + final long estDownloadBytes = jobStatus.getEstimatedNetworkDownloadBytes(); + if (estDownloadBytes <= 0) { + // Need to at least know the estimated download bytes for a prefetch job. + return false; + } // See if we match after relaxing any unmetered request final NetworkCapabilities.Builder builder = copyCapabilities(jobStatus.getJob().getRequiredNetwork()); builder.removeCapability(NET_CAPABILITY_NOT_METERED); - if (builder.build().satisfiedByNetworkCapabilities(capabilities)) { - // TODO: treat this as "maybe" response; need to check quotas - return jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC; - } else { - return false; + if (builder.build().satisfiedByNetworkCapabilities(capabilities) + && jobStatus.getFractionRunTime() > constants.CONN_PREFETCH_RELAX_FRAC) { + final long opportunisticQuotaBytes = + mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota( + network, NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS); + final long estUploadBytes = jobStatus.getEstimatedNetworkUploadBytes(); + final long estimatedBytes = estDownloadBytes + + (estUploadBytes == JobInfo.NETWORK_BYTES_UNKNOWN ? 0 : estUploadBytes); + return opportunisticQuotaBytes >= estimatedBytes; } + + return false; } @VisibleForTesting diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java index 3dc7bc1154ea3..c40742931a94a 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java @@ -299,8 +299,17 @@ public class ConnectivityControllerTest { final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000); final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000); + job.setEstimatedNetworkBytes(JobInfo.NETWORK_BYTES_UNKNOWN, DataUnit.MEBIBYTES.toBytes(1)); + final JobStatus latePrefetchUnknownDown = createJobStatus(job, now - 2000, now + 1000); + job.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), JobInfo.NETWORK_BYTES_UNKNOWN); + final JobStatus latePrefetchUnknownUp = createJobStatus(job, now - 2000, now + 1000); + final ConnectivityController controller = new ConnectivityController(mService); + when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota( + any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS))) + .thenReturn(0L); + // Unmetered network is whenever { final Network net = mock(Network.class); @@ -312,9 +321,11 @@ public class ConnectivityControllerTest { assertTrue(controller.isSatisfied(late, net, caps, mConstants)); assertTrue(controller.isSatisfied(earlyPrefetch, net, caps, mConstants)); assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); + assertTrue(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); + assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); } - // Metered network is only when prefetching and late + // Metered network is only when prefetching, late, and in opportunistic quota { final Network net = mock(Network.class); final NetworkCapabilities caps = createCapabilitiesBuilder() @@ -323,7 +334,17 @@ public class ConnectivityControllerTest { assertFalse(controller.isSatisfied(early, net, caps, mConstants)); assertFalse(controller.isSatisfied(late, net, caps, mConstants)); assertFalse(controller.isSatisfied(earlyPrefetch, net, caps, mConstants)); + assertFalse(controller.isSatisfied(latePrefetch, net, caps, mConstants)); + assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); + assertFalse(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); + + when(mNetPolicyManagerInternal.getSubscriptionOpportunisticQuota( + any(), eq(NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS))) + .thenReturn(9876543210L); assertTrue(controller.isSatisfied(latePrefetch, net, caps, mConstants)); + // Only relax restrictions when we at least know the estimated download bytes. + assertFalse(controller.isSatisfied(latePrefetchUnknownDown, net, caps, mConstants)); + assertTrue(controller.isSatisfied(latePrefetchUnknownUp, net, caps, mConstants)); } }