Home | History | Annotate | Download | only in controllers
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.server.job.controllers;
     18 
     19 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
     20 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
     21 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
     22 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
     23 
     24 import static org.junit.Assert.assertFalse;
     25 import static org.junit.Assert.assertTrue;
     26 import static org.mockito.ArgumentMatchers.any;
     27 import static org.mockito.ArgumentMatchers.anyBoolean;
     28 import static org.mockito.ArgumentMatchers.anyString;
     29 import static org.mockito.ArgumentMatchers.eq;
     30 import static org.mockito.Mockito.doNothing;
     31 import static org.mockito.Mockito.mock;
     32 import static org.mockito.Mockito.reset;
     33 import static org.mockito.Mockito.when;
     34 
     35 import android.app.job.JobInfo;
     36 import android.content.ComponentName;
     37 import android.content.Context;
     38 import android.content.pm.PackageManagerInternal;
     39 import android.net.ConnectivityManager;
     40 import android.net.ConnectivityManager.NetworkCallback;
     41 import android.net.Network;
     42 import android.net.NetworkCapabilities;
     43 import android.net.NetworkInfo;
     44 import android.net.NetworkInfo.DetailedState;
     45 import android.net.NetworkPolicyManager;
     46 import android.os.Build;
     47 import android.os.SystemClock;
     48 import android.util.DataUnit;
     49 
     50 import com.android.server.LocalServices;
     51 import com.android.server.job.JobSchedulerService;
     52 import com.android.server.job.JobSchedulerService.Constants;
     53 
     54 import org.junit.Before;
     55 import org.junit.Test;
     56 import org.junit.runner.RunWith;
     57 import org.mockito.ArgumentCaptor;
     58 import org.mockito.Mock;
     59 import org.mockito.junit.MockitoJUnitRunner;
     60 
     61 import java.time.Clock;
     62 import java.time.ZoneOffset;
     63 
     64 @RunWith(MockitoJUnitRunner.class)
     65 public class ConnectivityControllerTest {
     66 
     67     @Mock private Context mContext;
     68     @Mock private ConnectivityManager mConnManager;
     69     @Mock private NetworkPolicyManager mNetPolicyManager;
     70     @Mock private JobSchedulerService mService;
     71 
     72     private Constants mConstants;
     73 
     74     private static final int UID_RED = 10001;
     75     private static final int UID_BLUE = 10002;
     76 
     77     @Before
     78     public void setUp() throws Exception {
     79         // Assume all packages are current SDK
     80         final PackageManagerInternal pm = mock(PackageManagerInternal.class);
     81         when(pm.getPackageTargetSdkVersion(anyString()))
     82                 .thenReturn(Build.VERSION_CODES.CUR_DEVELOPMENT);
     83         LocalServices.removeServiceForTest(PackageManagerInternal.class);
     84         LocalServices.addService(PackageManagerInternal.class, pm);
     85 
     86         // Freeze the clocks at this moment in time
     87         JobSchedulerService.sSystemClock =
     88                 Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC);
     89         JobSchedulerService.sUptimeMillisClock =
     90                 Clock.fixed(SystemClock.uptimeMillisClock().instant(), ZoneOffset.UTC);
     91         JobSchedulerService.sElapsedRealtimeClock =
     92                 Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC);
     93 
     94         // Assume default constants for now
     95         mConstants = new Constants();
     96 
     97         // Get our mocks ready
     98         when(mContext.getSystemServiceName(ConnectivityManager.class))
     99                 .thenReturn(Context.CONNECTIVITY_SERVICE);
    100         when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE))
    101                 .thenReturn(mConnManager);
    102         when(mContext.getSystemServiceName(NetworkPolicyManager.class))
    103                 .thenReturn(Context.NETWORK_POLICY_SERVICE);
    104         when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
    105                 .thenReturn(mNetPolicyManager);
    106         when(mService.getTestableContext()).thenReturn(mContext);
    107         when(mService.getLock()).thenReturn(mService);
    108         when(mService.getConstants()).thenReturn(mConstants);
    109     }
    110 
    111     @Test
    112     public void testInsane() throws Exception {
    113         final Network net = new Network(101);
    114         final JobInfo.Builder job = createJob()
    115                 .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
    116                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
    117 
    118         // Slow network is too slow
    119         assertFalse(ConnectivityController.isSatisfied(createJobStatus(job), net,
    120                 createCapabilities().setLinkUpstreamBandwidthKbps(1)
    121                         .setLinkDownstreamBandwidthKbps(1), mConstants));
    122         // Fast network looks great
    123         assertTrue(ConnectivityController.isSatisfied(createJobStatus(job), net,
    124                 createCapabilities().setLinkUpstreamBandwidthKbps(1024)
    125                         .setLinkDownstreamBandwidthKbps(1024), mConstants));
    126     }
    127 
    128     @Test
    129     public void testCongestion() throws Exception {
    130         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
    131         final JobInfo.Builder job = createJob()
    132                 .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
    133                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
    134         final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
    135         final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
    136 
    137         // Uncongested network is whenever
    138         {
    139             final Network net = new Network(101);
    140             final NetworkCapabilities caps = createCapabilities()
    141                     .addCapability(NET_CAPABILITY_NOT_CONGESTED);
    142             assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
    143             assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
    144         }
    145 
    146         // Congested network is more selective
    147         {
    148             final Network net = new Network(101);
    149             final NetworkCapabilities caps = createCapabilities();
    150             assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
    151             assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
    152         }
    153     }
    154 
    155     @Test
    156     public void testRelaxed() throws Exception {
    157         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
    158         final JobInfo.Builder job = createJob()
    159                 .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1))
    160                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED);
    161         final JobStatus early = createJobStatus(job, now - 1000, now + 2000);
    162         final JobStatus late = createJobStatus(job, now - 2000, now + 1000);
    163 
    164         job.setIsPrefetch(true);
    165         final JobStatus earlyPrefetch = createJobStatus(job, now - 1000, now + 2000);
    166         final JobStatus latePrefetch = createJobStatus(job, now - 2000, now + 1000);
    167 
    168         // Unmetered network is whenever
    169         {
    170             final Network net = new Network(101);
    171             final NetworkCapabilities caps = createCapabilities()
    172                     .addCapability(NET_CAPABILITY_NOT_CONGESTED)
    173                     .addCapability(NET_CAPABILITY_NOT_METERED);
    174             assertTrue(ConnectivityController.isSatisfied(early, net, caps, mConstants));
    175             assertTrue(ConnectivityController.isSatisfied(late, net, caps, mConstants));
    176             assertTrue(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
    177             assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
    178         }
    179 
    180         // Metered network is only when prefetching and late
    181         {
    182             final Network net = new Network(101);
    183             final NetworkCapabilities caps = createCapabilities()
    184                     .addCapability(NET_CAPABILITY_NOT_CONGESTED);
    185             assertFalse(ConnectivityController.isSatisfied(early, net, caps, mConstants));
    186             assertFalse(ConnectivityController.isSatisfied(late, net, caps, mConstants));
    187             assertFalse(ConnectivityController.isSatisfied(earlyPrefetch, net, caps, mConstants));
    188             assertTrue(ConnectivityController.isSatisfied(latePrefetch, net, caps, mConstants));
    189         }
    190     }
    191 
    192     @Test
    193     public void testUpdates() throws Exception {
    194         final ArgumentCaptor<NetworkCallback> callback = ArgumentCaptor
    195                 .forClass(NetworkCallback.class);
    196         doNothing().when(mConnManager).registerNetworkCallback(any(), callback.capture());
    197 
    198         final ConnectivityController controller = new ConnectivityController(mService);
    199 
    200         final Network meteredNet = new Network(101);
    201         final NetworkCapabilities meteredCaps = createCapabilities();
    202         final Network unmeteredNet = new Network(202);
    203         final NetworkCapabilities unmeteredCaps = createCapabilities()
    204                 .addCapability(NET_CAPABILITY_NOT_METERED);
    205 
    206         final JobStatus red = createJobStatus(createJob()
    207                 .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
    208                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED), UID_RED);
    209         final JobStatus blue = createJobStatus(createJob()
    210                 .setEstimatedNetworkBytes(DataUnit.MEBIBYTES.toBytes(1), 0)
    211                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY), UID_BLUE);
    212 
    213         // Pretend we're offline when job is added
    214         {
    215             reset(mConnManager);
    216             answerNetwork(UID_RED, null, null);
    217             answerNetwork(UID_BLUE, null, null);
    218 
    219             controller.maybeStartTrackingJobLocked(red, null);
    220             controller.maybeStartTrackingJobLocked(blue, null);
    221 
    222             assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    223             assertFalse(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    224         }
    225 
    226         // Metered network
    227         {
    228             reset(mConnManager);
    229             answerNetwork(UID_RED, meteredNet, meteredCaps);
    230             answerNetwork(UID_BLUE, meteredNet, meteredCaps);
    231 
    232             callback.getValue().onCapabilitiesChanged(meteredNet, meteredCaps);
    233 
    234             assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    235             assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    236         }
    237 
    238         // Unmetered network background
    239         {
    240             reset(mConnManager);
    241             answerNetwork(UID_RED, meteredNet, meteredCaps);
    242             answerNetwork(UID_BLUE, meteredNet, meteredCaps);
    243 
    244             callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
    245 
    246             assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    247             assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    248         }
    249 
    250         // Lost metered network
    251         {
    252             reset(mConnManager);
    253             answerNetwork(UID_RED, unmeteredNet, unmeteredCaps);
    254             answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
    255 
    256             callback.getValue().onLost(meteredNet);
    257 
    258             assertTrue(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    259             assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    260         }
    261 
    262         // Specific UID was blocked
    263         {
    264             reset(mConnManager);
    265             answerNetwork(UID_RED, null, null);
    266             answerNetwork(UID_BLUE, unmeteredNet, unmeteredCaps);
    267 
    268             callback.getValue().onCapabilitiesChanged(unmeteredNet, unmeteredCaps);
    269 
    270             assertFalse(red.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    271             assertTrue(blue.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY));
    272         }
    273     }
    274 
    275     private void answerNetwork(int uid, Network net, NetworkCapabilities caps) {
    276         when(mConnManager.getActiveNetworkForUid(eq(uid))).thenReturn(net);
    277         when(mConnManager.getNetworkCapabilities(eq(net))).thenReturn(caps);
    278         if (net != null) {
    279             final NetworkInfo ni = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, null, null);
    280             ni.setDetailedState(DetailedState.CONNECTED, null, null);
    281             when(mConnManager.getNetworkInfoForUid(eq(net), eq(uid), anyBoolean())).thenReturn(ni);
    282         }
    283     }
    284 
    285     private static NetworkCapabilities createCapabilities() {
    286         return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
    287                 .addCapability(NET_CAPABILITY_VALIDATED);
    288     }
    289 
    290     private static JobInfo.Builder createJob() {
    291         return new JobInfo.Builder(101, new ComponentName("foo", "bar"));
    292     }
    293 
    294     private static JobStatus createJobStatus(JobInfo.Builder job) {
    295         return createJobStatus(job, android.os.Process.NOBODY_UID, 0, Long.MAX_VALUE);
    296     }
    297 
    298     private static JobStatus createJobStatus(JobInfo.Builder job, int uid) {
    299         return createJobStatus(job, uid, 0, Long.MAX_VALUE);
    300     }
    301 
    302     private static JobStatus createJobStatus(JobInfo.Builder job,
    303             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
    304         return createJobStatus(job, android.os.Process.NOBODY_UID,
    305                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
    306     }
    307 
    308     private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
    309             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
    310         return new JobStatus(job.build(), uid, null, -1, 0, 0, null,
    311                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
    312     }
    313 }
    314