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