blob: 35cba1855503ea65bc0c54c7cd95ac49c428ba11 [file] [log] [blame]
/*
* Copyright (C) 2018 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 static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.pm.PackageManagerInternal;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.os.Build;
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 org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.time.Clock;
import java.time.ZoneOffset;
@RunWith(AndroidJUnit4.class)
public class ConnectivityControllerTest {
@Before
public void setUp() throws Exception {
// Assume all packages are current SDK
final PackageManagerInternal pm = mock(PackageManagerInternal.class);
when(pm.getPackageTargetSdkVersion(anyString()))
.thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, pm);
// Freeze the clocks at this moment in time
JobSchedulerService.sSystemClock =
Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
JobSchedulerService.sUptimeMillisClock =
Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
JobSchedulerService.sElapsedRealtimeClock =
Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
}
@Test
public void testInsane() throws Exception {
final Network network = 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,
createCapabilities().setLinkUpstreamBandwidthKbps(1)
.setLinkDownstreamBandwidthKbps(1)));
// Fast network looks great
assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), network,
createCapabilities().setLinkUpstreamBandwidthKbps(1024)
.setLinkDownstreamBandwidthKbps(1024)));
}
@Test
public void testCongestion() throws Exception {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final JobInfo.Builder job = createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
// Uncongested network is whenever
{
final Network network = new Network(101);
final NetworkCapabilities capabilities = createCapabilities()
.addCapability(NET_CAPABILITY_NOT_CONGESTED);
assertTrue(ConnectivityController.isSatisfied(early, network, capabilities));
assertTrue(ConnectivityController.isSatisfied(late, network, capabilities));
}
// 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));
}
}
@Test
public void testRelaxed() throws Exception {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
final JobInfo.Builder job = createJob()
.setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
job.setIsPrefetch(true);
final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
// Unmetered network is whenever
{
final Network network = new Network(101);
final NetworkCapabilities capabilities = 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));
}
// Metered network is only when prefetching and late
{
final Network network = new Network(101);
final NetworkCapabilities capabilities = 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));
}
}
private static NetworkCapabilities createCapabilities() {
return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
.addCapability(NET_CAPABILITY_VALIDATED);
}
private static JobInfo.Builder createJob() {
return new JobInfo.Builder(101, new ComponentName("foo", "bar"));
}
private static JobStatus createJobStatus(JobInfo.Builder job) {
return createJobStatus(job, 0, Long.MAX_VALUE);
}
private static JobStatus createJobStatus(JobInfo.Builder job, long earliestRunTimeElapsedMillis,
long latestRunTimeElapsedMillis) {
return new JobStatus(job.build(), 0, null, -1, 0, 0, null, earliestRunTimeElapsedMillis,
latestRunTimeElapsedMillis, 0, 0, null, 0);
}
}