• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 package com.google.android.downloader;
16 
17 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
18 import static android.os.Looper.getMainLooper;
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertThrows;
21 import static org.robolectric.Shadows.shadowOf;
22 
23 import android.Manifest.permission;
24 import android.app.Application;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.net.ConnectivityManager;
28 import android.net.Network;
29 import android.net.NetworkCapabilities;
30 import android.net.NetworkInfo.DetailedState;
31 import android.net.NetworkInfo.State;
32 import android.os.Build.VERSION_CODES;
33 import androidx.test.core.app.ApplicationProvider;
34 import com.google.android.downloader.DownloadConstraints.NetworkType;
35 import com.google.common.collect.ImmutableSet;
36 import com.google.common.util.concurrent.ListenableFuture;
37 import java.time.Duration;
38 import java.util.EnumSet;
39 import java.util.concurrent.ExecutionException;
40 import java.util.concurrent.ScheduledExecutorService;
41 import java.util.concurrent.TimeoutException;
42 import org.junit.Before;
43 import org.junit.Rule;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.robolectric.RobolectricTestRunner;
47 import org.robolectric.annotation.Config;
48 import org.robolectric.shadows.ShadowApplication;
49 import org.robolectric.shadows.ShadowConnectivityManager;
50 import org.robolectric.shadows.ShadowNetwork;
51 import org.robolectric.shadows.ShadowNetworkCapabilities;
52 import org.robolectric.shadows.ShadowNetworkInfo;
53 
54 /** Unit tests for AndroidConnectivityHandler. */
55 @RunWith(RobolectricTestRunner.class)
56 @Config(sdk = VERSION_CODES.KITKAT)
57 public final class AndroidConnectivityHandlerTest {
58   private static final long DEFAULT_TIMEOUT_MILLIS = 1000;
59 
60   @Rule
61   public TestExecutorRule testExecutorRule =
62       new TestExecutorRule(Duration.ofMillis(2 * DEFAULT_TIMEOUT_MILLIS));
63 
64   private Application application;
65   private ConnectivityManager connectivityManager;
66   private ScheduledExecutorService scheduledExecutorService;
67 
68   @Before
setUp()69   public void setUp() {
70     application = ApplicationProvider.getApplicationContext();
71     scheduledExecutorService = testExecutorRule.newSingleThreadScheduledExecutor();
72     connectivityManager =
73         (ConnectivityManager) application.getSystemService(Context.CONNECTIVITY_SERVICE);
74   }
75 
76   @Test
checkConnectivity_permissionNotGranted()77   public void checkConnectivity_permissionNotGranted() {
78     assertThrows(IllegalStateException.class, this::createConnectivityHandler);
79   }
80 
81   @Test
checkConnectivity_noConnectivityRequired()82   public void checkConnectivity_noConnectivityRequired() throws Exception {
83     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
84 
85     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
86     ListenableFuture<Void> result = connectivityHandler.checkConnectivity(DownloadConstraints.NONE);
87 
88     assertThat(result.isDone()).isTrue();
89     assertThat(result.get()).isNull();
90   }
91 
92   @Test
checkConnectivity_noNetwork()93   public void checkConnectivity_noNetwork() {
94     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
95 
96     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
97 
98     shadowOf(connectivityManager).setActiveNetworkInfo(null);
99 
100     ListenableFuture<Void> result =
101         connectivityHandler.checkConnectivity(DownloadConstraints.NETWORK_CONNECTED);
102 
103     assertThat(result.isDone()).isFalse();
104   }
105 
106   @Test
checkConnectivity_networkNotConnected()107   public void checkConnectivity_networkNotConnected() {
108     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
109 
110     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
111 
112     shadowOf(connectivityManager)
113         .setActiveNetworkInfo(
114             ShadowNetworkInfo.newInstance(
115                 DetailedState.DISCONNECTED,
116                 ConnectivityManager.TYPE_WIFI,
117                 0,
118                 false,
119                 State.DISCONNECTED));
120 
121     ListenableFuture<Void> result =
122         connectivityHandler.checkConnectivity(DownloadConstraints.NETWORK_CONNECTED);
123 
124     assertThat(result.isDone()).isFalse();
125   }
126 
127   @Test
checkConnectivity_wrongNetworkType()128   public void checkConnectivity_wrongNetworkType() {
129     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
130 
131     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
132 
133     shadowOf(connectivityManager)
134         .setActiveNetworkInfo(
135             ShadowNetworkInfo.newInstance(
136                 DetailedState.CONNECTED,
137                 ConnectivityManager.TYPE_MOBILE,
138                 0,
139                 true,
140                 State.CONNECTED));
141 
142     ListenableFuture<Void> result =
143         connectivityHandler.checkConnectivity(DownloadConstraints.NETWORK_UNMETERED);
144 
145     assertThat(result.isDone()).isFalse();
146   }
147 
148   @Test
checkConnectivity_anyNetworkType()149   public void checkConnectivity_anyNetworkType() {
150     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
151 
152     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
153 
154     shadowOf(connectivityManager)
155         .setActiveNetworkInfo(
156             ShadowNetworkInfo.newInstance(
157                 DetailedState.CONNECTED,
158                 ConnectivityManager.TYPE_MOBILE,
159                 0,
160                 true,
161                 State.CONNECTED));
162 
163     ListenableFuture<Void> result =
164         connectivityHandler.checkConnectivity(
165             DownloadConstraints.builder()
166                 .setRequireUnmeteredNetwork(false)
167                 .setRequiredNetworkTypes(ImmutableSet.of(NetworkType.ANY))
168                 .build());
169     assertThat(result.isDone()).isTrue();
170   }
171 
172   @Test
checkConnectivity_unknownNetworkType()173   public void checkConnectivity_unknownNetworkType() {
174     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
175 
176     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
177 
178     shadowOf(connectivityManager)
179         .setActiveNetworkInfo(
180             ShadowNetworkInfo.newInstance(
181                 DetailedState.CONNECTED,
182                 100, // Invalid network type
183                 0,
184                 true,
185                 State.CONNECTED));
186 
187     ListenableFuture<Void> result =
188         connectivityHandler.checkConnectivity(
189             DownloadConstraints.builder()
190                 .setRequiredNetworkTypes(EnumSet.of(NetworkType.WIFI))
191                 .setRequireUnmeteredNetwork(false)
192                 .build());
193 
194     assertThat(result.isDone()).isFalse();
195   }
196 
197   @Test
checkConnectivity_requiredNetworkConnected_wifiOnly()198   public void checkConnectivity_requiredNetworkConnected_wifiOnly() {
199     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
200 
201     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
202 
203     shadowOf(connectivityManager)
204         .setActiveNetworkInfo(
205             ShadowNetworkInfo.newInstance(
206                 DetailedState.CONNECTED, ConnectivityManager.TYPE_WIFI, 0, true, State.CONNECTED));
207 
208     ListenableFuture<Void> result =
209         connectivityHandler.checkConnectivity(DownloadConstraints.NETWORK_UNMETERED);
210 
211     assertThat(result.isDone()).isTrue();
212   }
213 
214   @Test
checkConnectivity_requiredNetworkConnected_wifiOrCellular()215   public void checkConnectivity_requiredNetworkConnected_wifiOrCellular() {
216     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
217 
218     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
219 
220     shadowOf(connectivityManager)
221         .setActiveNetworkInfo(
222             ShadowNetworkInfo.newInstance(
223                 DetailedState.CONNECTED,
224                 ConnectivityManager.TYPE_MOBILE,
225                 0,
226                 true,
227                 State.CONNECTED));
228 
229     ListenableFuture<Void> result =
230         connectivityHandler.checkConnectivity(DownloadConstraints.NETWORK_CONNECTED);
231 
232     assertThat(result.isDone()).isTrue();
233   }
234 
235   @Config(sdk = VERSION_CODES.M)
236   @Test
checkConnectivity_requiredNetworkConnected_sdk23()237   public void checkConnectivity_requiredNetworkConnected_sdk23() {
238     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
239 
240     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
241 
242     ShadowConnectivityManager shadowConnectivityManager = shadowOf(connectivityManager);
243     Network network = ShadowNetwork.newInstance(0);
244     shadowConnectivityManager.addNetwork(
245         network,
246         ShadowNetworkInfo.newInstance(
247             DetailedState.CONNECTED, ConnectivityManager.TYPE_WIFI, 0, true, State.CONNECTED));
248     NetworkCapabilities networkCapabilities = ShadowNetworkCapabilities.newInstance();
249     shadowOf(networkCapabilities).addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
250     shadowConnectivityManager.setNetworkCapabilities(network, networkCapabilities);
251     shadowConnectivityManager.setDefaultNetworkActive(true);
252 
253     ListenableFuture<Void> result =
254         connectivityHandler.checkConnectivity(DownloadConstraints.NETWORK_CONNECTED);
255 
256     assertThat(result.isDone()).isTrue();
257   }
258 
259   @Test
checkConnectivity_networkChange()260   public void checkConnectivity_networkChange() throws Exception {
261     ShadowApplication shadowApplication = shadowOf(application);
262     shadowApplication.grantPermissions(permission.ACCESS_NETWORK_STATE);
263 
264     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
265 
266     shadowOf(connectivityManager)
267         .setActiveNetworkInfo(
268             ShadowNetworkInfo.newInstance(
269                 DetailedState.DISCONNECTED,
270                 ConnectivityManager.TYPE_WIFI,
271                 0,
272                 false,
273                 State.DISCONNECTED));
274 
275     ListenableFuture<Void> result =
276         connectivityHandler.checkConnectivity(DownloadConstraints.NETWORK_CONNECTED);
277     assertThat(result.isDone()).isFalse();
278     assertThat(shadowApplication.getRegisteredReceivers()).hasSize(1);
279     assertThat(shadowApplication.getRegisteredReceivers().get(0).getBroadcastReceiver())
280         .isInstanceOf(AndroidConnectivityHandler.NetworkBroadcastReceiver.class);
281     assertThat(
282             shadowApplication
283                 .getRegisteredReceivers()
284                 .get(0)
285                 .getIntentFilter()
286                 .hasAction(CONNECTIVITY_ACTION))
287         .isTrue();
288 
289     // Change state to be available.
290     shadowOf(connectivityManager)
291         .setActiveNetworkInfo(
292             ShadowNetworkInfo.newInstance(
293                 DetailedState.CONNECTED, ConnectivityManager.TYPE_WIFI, 0, true, State.CONNECTED));
294 
295     application.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
296 
297     shadowOf(getMainLooper()).idle();
298 
299     assertThat(result.isDone()).isTrue();
300     assertThat(result.get()).isNull();
301     assertThat(shadowApplication.getRegisteredReceivers()).isEmpty();
302   }
303 
304   @Test
checkConnectivity_timeout()305   public void checkConnectivity_timeout() {
306     shadowOf(application).grantPermissions(permission.ACCESS_NETWORK_STATE);
307 
308     AndroidConnectivityHandler connectivityHandler = createConnectivityHandler();
309 
310     shadowOf(connectivityManager)
311         .setActiveNetworkInfo(
312             ShadowNetworkInfo.newInstance(
313                 DetailedState.DISCONNECTED,
314                 ConnectivityManager.TYPE_WIFI,
315                 0,
316                 false,
317                 State.DISCONNECTED));
318 
319     ListenableFuture<Void> result =
320         connectivityHandler.checkConnectivity(DownloadConstraints.NETWORK_CONNECTED);
321     assertThat(result.isDone()).isFalse();
322 
323     ExecutionException exception = assertThrows(ExecutionException.class, result::get);
324     assertThat(exception).hasCauseThat().isInstanceOf(TimeoutException.class);
325   }
326 
createConnectivityHandler()327   private AndroidConnectivityHandler createConnectivityHandler() {
328     return new AndroidConnectivityHandler(
329         application, scheduledExecutorService, DEFAULT_TIMEOUT_MILLIS);
330   }
331 }
332