• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 android.net.cts;
18 
19 import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
20 import static android.content.pm.PackageManager.FEATURE_WIFI;
21 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
22 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
23 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
24 
25 import static org.junit.Assert.assertEquals;
26 import static org.junit.Assert.assertFalse;
27 import static org.junit.Assert.assertNotNull;
28 import static org.junit.Assert.assertNull;
29 import static org.junit.Assert.fail;
30 
31 import android.content.ContentResolver;
32 import android.content.Context;
33 import android.net.ConnectivityManager;
34 import android.net.Network;
35 import android.net.NetworkCapabilities;
36 import android.net.NetworkUtils;
37 import android.net.cts.util.CtsNetUtils;
38 import android.platform.test.annotations.AppModeFull;
39 import android.system.ErrnoException;
40 import android.system.OsConstants;
41 import android.util.ArraySet;
42 import android.util.Log;
43 
44 import androidx.test.platform.app.InstrumentationRegistry;
45 
46 import com.android.net.module.util.CollectionUtils;
47 import com.android.testutils.AutoReleaseNetworkCallbackRule;
48 import com.android.testutils.ConnectivityDiagnosticsCollector;
49 import com.android.testutils.DevSdkIgnoreRunner;
50 import com.android.testutils.DeviceConfigRule;
51 
52 import org.junit.Before;
53 import org.junit.Rule;
54 import org.junit.Test;
55 import org.junit.runner.RunWith;
56 
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.Set;
60 
61 @DevSdkIgnoreRunner.RestoreDefaultNetwork
62 @RunWith(DevSdkIgnoreRunner.class)
63 public class MultinetworkApiTest {
64     @Rule(order = 1)
65     public final DeviceConfigRule mDeviceConfigRule = new DeviceConfigRule();
66 
67     @Rule(order = 2)
68     public final AutoReleaseNetworkCallbackRule
69             mNetworkCallbackRule = new AutoReleaseNetworkCallbackRule();
70 
71     static {
72         System.loadLibrary("nativemultinetwork_jni");
73     }
74 
75     private static final String TAG = "MultinetworkNativeApiTest";
76     static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google";
77 
78     public static class QueryTestResult {
79         public final int sourcePort;
80         public final int attempts;
81         public final int errNo;
82 
QueryTestResult(int sourcePort, int attempts, int errNo)83         public QueryTestResult(int sourcePort, int attempts, int errNo) {
84             this.sourcePort = sourcePort;
85             this.attempts = attempts;
86             this.errNo = errNo;
87         }
88 
89         @Override
toString()90         public String toString() {
91             return "QueryTestResult{"
92                     + "sourcePort=" + sourcePort
93                     + ", attempts=" + attempts
94                     + ", errNo=" + errNo
95                     + '}';
96         }
97     }
98 
99     /**
100      * @return 0 on success
101      */
runGetaddrinfoCheck(long networkHandle)102     private static native int runGetaddrinfoCheck(long networkHandle);
runSetprocnetwork(long networkHandle)103     private static native int runSetprocnetwork(long networkHandle);
runSetsocknetwork(long networkHandle)104     private static native int runSetsocknetwork(long networkHandle);
runDatagramCheck(long networkHandle, int sourcePort)105     private static native QueryTestResult runDatagramCheck(long networkHandle, int sourcePort);
runResNapiMalformedCheck(long networkHandle)106     private static native void runResNapiMalformedCheck(long networkHandle);
runResNcancelCheck(long networkHandle)107     private static native void runResNcancelCheck(long networkHandle);
runResNqueryCheck(long networkHandle)108     private static native void runResNqueryCheck(long networkHandle);
runResNsendCheck(long networkHandle)109     private static native void runResNsendCheck(long networkHandle);
runResNnxDomainCheck(long networkHandle)110     private static native void runResNnxDomainCheck(long networkHandle);
111 
112 
113     private ContentResolver mCR;
114     private ConnectivityManager mCM;
115     private CtsNetUtils mCtsNetUtils;
116     private Context mContext;
117     private Network mRequestedCellNetwork;
118 
119     @Before
setUp()120     public void setUp() throws Exception {
121         mContext = InstrumentationRegistry.getInstrumentation().getContext();
122         mCM = mContext.getSystemService(ConnectivityManager.class);
123         mCR = mContext.getContentResolver();
124         mCtsNetUtils = new CtsNetUtils(mContext);
125     }
126 
127     @Test
testGetaddrinfo()128     public void testGetaddrinfo() throws Exception {
129         for (Network network : getTestableNetworks()) {
130             int errno = runGetaddrinfoCheck(network.getNetworkHandle());
131             if (errno != 0) {
132                 throw new ErrnoException(
133                         "getaddrinfo on " + mCM.getNetworkInfo(network), -errno);
134             }
135         }
136     }
137 
138     @Test
139     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
testSetprocnetwork()140     public void testSetprocnetwork() throws Exception {
141         // Hopefully no prior test in this process space has set a default network.
142         assertNull(mCM.getProcessDefaultNetwork());
143         assertEquals(0, NetworkUtils.getBoundNetworkForProcess());
144 
145         for (Network network : getTestableNetworks()) {
146             mCM.setProcessDefaultNetwork(null);
147             assertNull(mCM.getProcessDefaultNetwork());
148 
149             int errno = runSetprocnetwork(network.getNetworkHandle());
150             if (errno != 0) {
151                 throw new ErrnoException(
152                         "setprocnetwork on " + mCM.getNetworkInfo(network), -errno);
153             }
154             Network processDefault = mCM.getProcessDefaultNetwork();
155             assertNotNull(processDefault);
156             assertEquals(network, processDefault);
157             // TODO: open DatagramSockets, connect them to 192.0.2.1 and 2001:db8::,
158             // and ensure that the source address is in fact on this network as
159             // determined by mCM.getLinkProperties(network).
160 
161             mCM.setProcessDefaultNetwork(null);
162         }
163 
164         for (Network network : getTestableNetworks()) {
165             NetworkUtils.bindProcessToNetwork(0);
166             assertNull(mCM.getBoundNetworkForProcess());
167 
168             int errno = runSetprocnetwork(network.getNetworkHandle());
169             if (errno != 0) {
170                 throw new ErrnoException(
171                         "setprocnetwork on " + mCM.getNetworkInfo(network), -errno);
172             }
173             assertEquals(network, new Network(mCM.getBoundNetworkForProcess()));
174             // TODO: open DatagramSockets, connect them to 192.0.2.1 and 2001:db8::,
175             // and ensure that the source address is in fact on this network as
176             // determined by mCM.getLinkProperties(network).
177 
178             NetworkUtils.bindProcessToNetwork(0);
179         }
180     }
181 
182     @Test
183     @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
testSetsocknetwork()184     public void testSetsocknetwork() throws Exception {
185         for (Network network : getTestableNetworks()) {
186             int errno = runSetsocknetwork(network.getNetworkHandle());
187             if (errno != 0) {
188                 throw new ErrnoException(
189                         "setsocknetwork on " + mCM.getNetworkInfo(network), -errno);
190             }
191         }
192     }
193 
runNativeDatagramTransmissionDiagnostics(Network network, QueryTestResult failedResult)194     private void runNativeDatagramTransmissionDiagnostics(Network network,
195             QueryTestResult failedResult) {
196         final ConnectivityDiagnosticsCollector collector = ConnectivityDiagnosticsCollector
197                 .getInstance();
198         if (collector == null) {
199             Log.e(TAG, "Missing ConnectivityDiagnosticsCollector, not adding diagnostics");
200             return;
201         }
202 
203         final int numReruns = 10;
204         final ArrayList<QueryTestResult> reruns = new ArrayList<>(numReruns);
205         for (int i = 0; i < numReruns; i++) {
206             final QueryTestResult rerunResult =
207                     runDatagramCheck(network.getNetworkHandle(), 0 /* sourcePort */);
208             Log.d(TAG, "Rerun result " + i + ": " + rerunResult);
209             reruns.add(rerunResult);
210         }
211         // Rerun on the original port after trying the other ports, to check that the results are
212         // consistent, as opposed to the network recovering halfway through.
213         int originalPortFailedReruns = 0;
214         for (int i = 0; i < numReruns; i++) {
215             final QueryTestResult originalPortRerun = runDatagramCheck(network.getNetworkHandle(),
216                     failedResult.sourcePort);
217             Log.d(TAG, "Rerun result " + i + " with original port: " + originalPortRerun);
218             if (originalPortRerun.errNo != 0) {
219                 originalPortFailedReruns++;
220             }
221         }
222 
223         final int noRetrySuccessResults = reruns.stream()
224                 .filter(result -> result.errNo == 0 && result.attempts == 1)
225                 .mapToInt(result -> 1)
226                 .sum();
227         final int failedResults = reruns.stream()
228                 .filter(result -> result.errNo != 0)
229                 .mapToInt(result -> 1)
230                 .sum();
231         collector.addFailureAttribute("numReruns", numReruns);
232         collector.addFailureAttribute("noRetrySuccessReruns", noRetrySuccessResults);
233         collector.addFailureAttribute("failedReruns", failedResults);
234         collector.addFailureAttribute("originalPortFailedReruns", originalPortFailedReruns);
235     }
236 
237     @Test
testNativeDatagramTransmission()238     public void testNativeDatagramTransmission() throws Exception {
239         for (Network network : getTestableNetworks()) {
240             final QueryTestResult result = runDatagramCheck(network.getNetworkHandle(),
241                     0 /* sourcePort */);
242             if (result.errNo == 0) {
243                 continue;
244             }
245             final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
246             final int[] transports = nc != null ? nc.getTransportTypes() : null;
247             if (CollectionUtils.contains(transports, TRANSPORT_WIFI)) {
248                 runNativeDatagramTransmissionDiagnostics(network, result);
249             }
250 
251             // Log the whole result (with source port and attempts) to logcat, but use only the
252             // errno and transport in the fail message so similar failures have consistent messages
253             final String error = "DatagramCheck on transport " + Arrays.toString(transports)
254                     + " failed: " + result.errNo;
255             Log.e(TAG, error + ", result: " + result);
256             fail(error);
257         }
258     }
259 
260     @Test
testNoSuchNetwork()261     public void testNoSuchNetwork() throws Exception {
262         final Network eNoNet = new Network(54321);
263         assertNull(mCM.getNetworkInfo(eNoNet));
264 
265         final long eNoNetHandle = eNoNet.getNetworkHandle();
266         assertEquals(-OsConstants.ENONET, runSetsocknetwork(eNoNetHandle));
267         assertEquals(-OsConstants.ENONET, runSetprocnetwork(eNoNetHandle));
268         // TODO: correct test permissions so this call is not silently re-mapped
269         // to query on the default network.
270         // assertEquals(-OsConstants.ENONET, runGetaddrinfoCheck(eNoNetHandle));
271     }
272 
273     @Test
testNetworkHandle()274     public void testNetworkHandle() throws Exception {
275         // Test Network -> NetworkHandle -> Network results in the same Network.
276         for (Network network : getTestableNetworks()) {
277             long networkHandle = network.getNetworkHandle();
278             Network newNetwork = Network.fromNetworkHandle(networkHandle);
279             assertEquals(newNetwork, network);
280         }
281 
282         // Test that only obfuscated handles are allowed.
283         try {
284             Network.fromNetworkHandle(100);
285             fail();
286         } catch (IllegalArgumentException e) {}
287         try {
288             Network.fromNetworkHandle(-1);
289             fail();
290         } catch (IllegalArgumentException e) {}
291         try {
292             Network.fromNetworkHandle(0);
293             fail();
294         } catch (IllegalArgumentException e) {}
295     }
296 
297     @Test
testResNApi()298     public void testResNApi() throws Exception {
299         for (Network network : getTestableNetworks()) {
300             // Throws AssertionError directly in jni function if test fail.
301             runResNqueryCheck(network.getNetworkHandle());
302             runResNsendCheck(network.getNetworkHandle());
303             runResNcancelCheck(network.getNetworkHandle());
304             runResNapiMalformedCheck(network.getNetworkHandle());
305 
306             final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
307             // Some cellular networks configure their DNS servers never to return NXDOMAIN, so don't
308             // test NXDOMAIN on these DNS servers.
309             // b/144521720
310             if (nc != null && !nc.hasTransport(TRANSPORT_CELLULAR)) {
311                 runResNnxDomainCheck(network.getNetworkHandle());
312             }
313         }
314     }
315 
316     @Test
317     @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
testResNApiNXDomainPrivateDns()318     public void testResNApiNXDomainPrivateDns() throws Exception {
319         // Use async private DNS resolution to avoid flakes due to races applying the setting
320         mDeviceConfigRule.setConfig(NAMESPACE_CONNECTIVITY,
321                 "networkmonitor_async_privdns_resolution", "1");
322         mCtsNetUtils.reconnectWifiIfSupported();
323         mCtsNetUtils.reconnectCellIfSupported();
324 
325         mCtsNetUtils.storePrivateDnsSetting();
326 
327         mDeviceConfigRule.runAfterNextCleanup(() -> {
328             mCtsNetUtils.reconnectWifiIfSupported();
329             mCtsNetUtils.reconnectCellIfSupported();
330         });
331         // Enable private DNS strict mode and set server to dns.google before doing NxDomain test.
332         // b/144521720
333         try {
334             mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER);
335             for (Network network : getTestableNetworks()) {
336               // Wait for private DNS setting to propagate.
337               mCtsNetUtils.awaitPrivateDnsSetting("NxDomain test wait private DNS setting timeout",
338                         network, GOOGLE_PRIVATE_DNS_SERVER, true);
339               runResNnxDomainCheck(network.getNetworkHandle());
340             }
341         } finally {
342             mCtsNetUtils.restorePrivateDnsSetting();
343         }
344     }
345 
346     /**
347      * Get all testable Networks with internet capability.
348      */
getTestableNetworks()349     private Set<Network> getTestableNetworks() throws InterruptedException {
350         // Calling requestNetwork() to request a cell or Wi-Fi network via CtsNetUtils or
351         // NetworkCallbackRule requires the CHANGE_NETWORK_STATE permission. This permission cannot
352         // be granted to instant apps. Therefore, return currently available testable networks
353         // directly in instant mode.
354         if (mContext.getApplicationInfo().isInstantApp()) {
355             return new ArraySet<>(mCtsNetUtils.getTestableNetworks());
356         }
357 
358         // Obtain cell and Wi-Fi through CtsNetUtils (which uses NetworkCallbacks), as they may have
359         // just been reconnected by the test using NetworkCallbacks, so synchronous calls may not
360         // yet return them (synchronous calls and callbacks should not be mixed for a given
361         // Network).
362         final Set<Network> testableNetworks = new ArraySet<>();
363         if (mContext.getPackageManager().hasSystemFeature(FEATURE_TELEPHONY)) {
364             if (mRequestedCellNetwork == null) {
365                 mRequestedCellNetwork = mNetworkCallbackRule.requestCell();
366             }
367             assertNotNull("Cell network requested but not obtained", mRequestedCellNetwork);
368             testableNetworks.add(mRequestedCellNetwork);
369         }
370 
371         if (mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI)) {
372             testableNetworks.add(mCtsNetUtils.ensureWifiConnected());
373         }
374 
375         // Obtain other networks through the synchronous API, if any.
376         for (Network network : mCtsNetUtils.getTestableNetworks()) {
377             final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
378             if (nc != null
379                     && !nc.hasTransport(TRANSPORT_WIFI)
380                     && !nc.hasTransport(TRANSPORT_CELLULAR)) {
381                 testableNetworks.add(network);
382             }
383         }
384 
385         // In practice this should not happen as getTestableNetworks throws if there is no network
386         // at all.
387         assertFalse("This device does not support WiFi nor cell data, and does not have any other "
388                         + "network connected. This test requires at least one internet-providing "
389                         + "network.",
390                 testableNetworks.isEmpty());
391         return testableNetworks;
392     }
393 }
394