1 /* 2 * Copyright (C) 2009 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.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; 20 import static android.content.pm.PackageManager.FEATURE_ETHERNET; 21 import static android.content.pm.PackageManager.FEATURE_TELEPHONY; 22 import static android.content.pm.PackageManager.FEATURE_USB_HOST; 23 import static android.content.pm.PackageManager.FEATURE_WIFI; 24 import static android.content.pm.PackageManager.GET_PERMISSIONS; 25 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 26 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; 27 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 28 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; 29 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; 30 import static android.net.NetworkCapabilities.TRANSPORT_WIFI; 31 import static android.net.cts.util.CtsNetUtils.ConnectivityActionReceiver; 32 import static android.net.cts.util.CtsNetUtils.HTTP_PORT; 33 import static android.net.cts.util.CtsNetUtils.NETWORK_CALLBACK_ACTION; 34 import static android.net.cts.util.CtsNetUtils.TEST_HOST; 35 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback; 36 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; 37 import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE; 38 import static android.system.OsConstants.AF_INET; 39 import static android.system.OsConstants.AF_INET6; 40 import static android.system.OsConstants.AF_UNSPEC; 41 42 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 43 import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; 44 45 import static org.junit.Assert.assertEquals; 46 import static org.junit.Assert.assertFalse; 47 import static org.junit.Assert.assertNotNull; 48 import static org.junit.Assert.assertNotSame; 49 import static org.junit.Assert.assertNull; 50 import static org.junit.Assert.assertTrue; 51 import static org.junit.Assert.fail; 52 import static org.junit.Assume.assumeTrue; 53 54 import android.annotation.NonNull; 55 import android.app.Instrumentation; 56 import android.app.PendingIntent; 57 import android.app.UiAutomation; 58 import android.content.BroadcastReceiver; 59 import android.content.ContentResolver; 60 import android.content.Context; 61 import android.content.Intent; 62 import android.content.IntentFilter; 63 import android.content.pm.PackageInfo; 64 import android.content.pm.PackageManager; 65 import android.content.res.Resources; 66 import android.net.ConnectivityManager; 67 import android.net.ConnectivityManager.NetworkCallback; 68 import android.net.IpSecManager; 69 import android.net.IpSecManager.UdpEncapsulationSocket; 70 import android.net.LinkProperties; 71 import android.net.Network; 72 import android.net.NetworkCapabilities; 73 import android.net.NetworkConfig; 74 import android.net.NetworkInfo; 75 import android.net.NetworkInfo.DetailedState; 76 import android.net.NetworkInfo.State; 77 import android.net.NetworkRequest; 78 import android.net.NetworkUtils; 79 import android.net.SocketKeepalive; 80 import android.net.cts.util.CtsNetUtils; 81 import android.net.util.KeepaliveUtils; 82 import android.net.wifi.WifiManager; 83 import android.os.Binder; 84 import android.os.Build; 85 import android.os.Looper; 86 import android.os.MessageQueue; 87 import android.os.SystemClock; 88 import android.os.SystemProperties; 89 import android.os.VintfRuntimeInfo; 90 import android.platform.test.annotations.AppModeFull; 91 import android.provider.Settings; 92 import android.text.TextUtils; 93 import android.util.Log; 94 import android.util.Pair; 95 96 import androidx.test.InstrumentationRegistry; 97 import androidx.test.runner.AndroidJUnit4; 98 99 import com.android.internal.util.ArrayUtils; 100 import com.android.testutils.SkipPresubmit; 101 102 import libcore.io.Streams; 103 104 import org.junit.After; 105 import org.junit.Before; 106 import org.junit.Test; 107 import org.junit.runner.RunWith; 108 109 import java.io.FileDescriptor; 110 import java.io.IOException; 111 import java.io.InputStream; 112 import java.io.InputStreamReader; 113 import java.io.OutputStream; 114 import java.net.HttpURLConnection; 115 import java.net.Inet4Address; 116 import java.net.Inet6Address; 117 import java.net.InetAddress; 118 import java.net.InetSocketAddress; 119 import java.net.Socket; 120 import java.net.URL; 121 import java.net.UnknownHostException; 122 import java.nio.charset.StandardCharsets; 123 import java.util.ArrayList; 124 import java.util.Collection; 125 import java.util.HashMap; 126 import java.util.concurrent.CompletableFuture; 127 import java.util.concurrent.CountDownLatch; 128 import java.util.concurrent.Executor; 129 import java.util.concurrent.LinkedBlockingQueue; 130 import java.util.concurrent.TimeUnit; 131 import java.util.function.Supplier; 132 import java.util.regex.Matcher; 133 import java.util.regex.Pattern; 134 135 @RunWith(AndroidJUnit4.class) 136 public class ConnectivityManagerTest { 137 138 private static final String TAG = ConnectivityManagerTest.class.getSimpleName(); 139 140 public static final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE; 141 public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI; 142 143 private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1 144 private static final int KEEPALIVE_CALLBACK_TIMEOUT_MS = 2000; 145 private static final int INTERVAL_KEEPALIVE_RETRY_MS = 500; 146 private static final int MAX_KEEPALIVE_RETRY_COUNT = 3; 147 private static final int MIN_KEEPALIVE_INTERVAL = 10; 148 149 // Changing meteredness on wifi involves reconnecting, which can take several seconds (involves 150 // re-associating, DHCP...) 151 private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 30_000; 152 private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20; 153 private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500; 154 // device could have only one interface: data, wifi. 155 private static final int MIN_NUM_NETWORK_TYPES = 1; 156 157 // Airplane Mode BroadcastReceiver Timeout 158 private static final int AIRPLANE_MODE_CHANGE_TIMEOUT_MS = 5000; 159 160 // Minimum supported keepalive counts for wifi and cellular. 161 public static final int MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT = 1; 162 public static final int MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT = 3; 163 164 private static final String NETWORK_METERED_MULTIPATH_PREFERENCE_RES_NAME = 165 "config_networkMeteredMultipathPreference"; 166 private static final String KEEPALIVE_ALLOWED_UNPRIVILEGED_RES_NAME = 167 "config_allowedUnprivilegedKeepalivePerUid"; 168 private static final String KEEPALIVE_RESERVED_PER_SLOT_RES_NAME = 169 "config_reservedPrivilegedKeepaliveSlots"; 170 171 private Context mContext; 172 private Instrumentation mInstrumentation; 173 private ConnectivityManager mCm; 174 private WifiManager mWifiManager; 175 private PackageManager mPackageManager; 176 private final HashMap<Integer, NetworkConfig> mNetworks = 177 new HashMap<Integer, NetworkConfig>(); 178 boolean mWifiWasDisabled; 179 private UiAutomation mUiAutomation; 180 private CtsNetUtils mCtsNetUtils; 181 182 @Before setUp()183 public void setUp() throws Exception { 184 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 185 mContext = mInstrumentation.getContext(); 186 mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 187 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 188 mPackageManager = mContext.getPackageManager(); 189 mCtsNetUtils = new CtsNetUtils(mContext); 190 mWifiWasDisabled = false; 191 192 // Get com.android.internal.R.array.networkAttributes 193 int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android"); 194 String[] naStrings = mContext.getResources().getStringArray(resId); 195 //TODO: What is the "correct" way to determine if this is a wifi only device? 196 boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false); 197 for (String naString : naStrings) { 198 try { 199 NetworkConfig n = new NetworkConfig(naString); 200 if (wifiOnly && ConnectivityManager.isNetworkTypeMobile(n.type)) { 201 continue; 202 } 203 mNetworks.put(n.type, n); 204 } catch (Exception e) {} 205 } 206 mUiAutomation = mInstrumentation.getUiAutomation(); 207 } 208 209 @After tearDown()210 public void tearDown() throws Exception { 211 // Return WiFi to its original disabled state after tests that explicitly connect. 212 if (mWifiWasDisabled) { 213 mCtsNetUtils.disconnectFromWifi(null); 214 } 215 if (mCtsNetUtils.cellConnectAttempted()) { 216 mCtsNetUtils.disconnectFromCell(); 217 } 218 } 219 220 /** 221 * Make sure WiFi is connected to an access point if it is not already. If 222 * WiFi is enabled as a result of this function, it will be disabled 223 * automatically in tearDown(). 224 */ ensureWifiConnected()225 private Network ensureWifiConnected() { 226 mWifiWasDisabled = !mWifiManager.isWifiEnabled(); 227 // Even if wifi is enabled, the network may not be connected or ready yet 228 return mCtsNetUtils.connectToWifi(); 229 } 230 231 @Test testIsNetworkTypeValid()232 public void testIsNetworkTypeValid() { 233 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE)); 234 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI)); 235 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_MMS)); 236 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_SUPL)); 237 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_DUN)); 238 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_HIPRI)); 239 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIMAX)); 240 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_BLUETOOTH)); 241 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_DUMMY)); 242 assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_ETHERNET)); 243 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_FOTA)); 244 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IMS)); 245 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_CBS)); 246 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI_P2P)); 247 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE_IA)); 248 assertFalse(mCm.isNetworkTypeValid(-1)); 249 assertTrue(mCm.isNetworkTypeValid(0)); 250 assertTrue(mCm.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE)); 251 assertFalse(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.MAX_NETWORK_TYPE+1)); 252 253 NetworkInfo[] ni = mCm.getAllNetworkInfo(); 254 255 for (NetworkInfo n: ni) { 256 assertTrue(ConnectivityManager.isNetworkTypeValid(n.getType())); 257 } 258 259 } 260 261 @Test testSetNetworkPreference()262 public void testSetNetworkPreference() { 263 // getNetworkPreference() and setNetworkPreference() are both deprecated so they do 264 // not preform any action. Verify they are at least still callable. 265 mCm.setNetworkPreference(mCm.getNetworkPreference()); 266 } 267 268 @Test testGetActiveNetworkInfo()269 public void testGetActiveNetworkInfo() { 270 NetworkInfo ni = mCm.getActiveNetworkInfo(); 271 272 assertNotNull("You must have an active network connection to complete CTS", ni); 273 assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType())); 274 assertTrue(ni.getState() == State.CONNECTED); 275 } 276 277 @Test testGetActiveNetwork()278 public void testGetActiveNetwork() { 279 Network network = mCm.getActiveNetwork(); 280 assertNotNull("You must have an active network connection to complete CTS", network); 281 282 NetworkInfo ni = mCm.getNetworkInfo(network); 283 assertNotNull("Network returned from getActiveNetwork was invalid", ni); 284 285 // Similar to testGetActiveNetworkInfo above. 286 assertTrue(ConnectivityManager.isNetworkTypeValid(ni.getType())); 287 assertTrue(ni.getState() == State.CONNECTED); 288 } 289 290 @Test testGetNetworkInfo()291 public void testGetNetworkInfo() { 292 for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE+1; type++) { 293 if (shouldBeSupported(type)) { 294 NetworkInfo ni = mCm.getNetworkInfo(type); 295 assertTrue("Info shouldn't be null for " + type, ni != null); 296 State state = ni.getState(); 297 assertTrue("Bad state for " + type, State.UNKNOWN.ordinal() >= state.ordinal() 298 && state.ordinal() >= State.CONNECTING.ordinal()); 299 DetailedState ds = ni.getDetailedState(); 300 assertTrue("Bad detailed state for " + type, 301 DetailedState.FAILED.ordinal() >= ds.ordinal() 302 && ds.ordinal() >= DetailedState.IDLE.ordinal()); 303 } else { 304 assertNull("Info should be null for " + type, mCm.getNetworkInfo(type)); 305 } 306 } 307 } 308 309 @Test testGetAllNetworkInfo()310 public void testGetAllNetworkInfo() { 311 NetworkInfo[] ni = mCm.getAllNetworkInfo(); 312 assertTrue(ni.length >= MIN_NUM_NETWORK_TYPES); 313 for (int type = 0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { 314 int desiredFoundCount = (shouldBeSupported(type) ? 1 : 0); 315 int foundCount = 0; 316 for (NetworkInfo i : ni) { 317 if (i.getType() == type) foundCount++; 318 } 319 if (foundCount != desiredFoundCount) { 320 Log.e(TAG, "failure in testGetAllNetworkInfo. Dump of returned NetworkInfos:"); 321 for (NetworkInfo networkInfo : ni) Log.e(TAG, " " + networkInfo); 322 } 323 assertTrue("Unexpected foundCount of " + foundCount + " for type " + type, 324 foundCount == desiredFoundCount); 325 } 326 } 327 328 /** 329 * Tests that connections can be opened on WiFi and cellphone networks, 330 * and that they are made from different IP addresses. 331 */ 332 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 333 @Test 334 @SkipPresubmit(reason = "Virtual devices use a single internet connection for all networks") testOpenConnection()335 public void testOpenConnection() throws Exception { 336 boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI) 337 && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY); 338 if (!canRunTest) { 339 Log.i(TAG,"testOpenConnection cannot execute unless device supports both WiFi " 340 + "and a cellular connection"); 341 return; 342 } 343 344 Network wifiNetwork = mCtsNetUtils.connectToWifi(); 345 Network cellNetwork = mCtsNetUtils.connectToCell(); 346 // This server returns the requestor's IP address as the response body. 347 URL url = new URL("http://google-ipv6test.appspot.com/ip.js?fmt=text"); 348 String wifiAddressString = httpGet(wifiNetwork, url); 349 String cellAddressString = httpGet(cellNetwork, url); 350 351 assertFalse(String.format("Same address '%s' on two different networks (%s, %s)", 352 wifiAddressString, wifiNetwork, cellNetwork), 353 wifiAddressString.equals(cellAddressString)); 354 355 // Verify that the IP addresses that the requests appeared to come from are actually on the 356 // respective networks. 357 assertOnNetwork(wifiAddressString, wifiNetwork); 358 assertOnNetwork(cellAddressString, cellNetwork); 359 360 assertFalse("Unexpectedly equal: " + wifiNetwork, wifiNetwork.equals(cellNetwork)); 361 } 362 363 /** 364 * Performs a HTTP GET to the specified URL on the specified Network, and returns 365 * the response body decoded as UTF-8. 366 */ httpGet(Network network, URL httpUrl)367 private static String httpGet(Network network, URL httpUrl) throws IOException { 368 HttpURLConnection connection = (HttpURLConnection) network.openConnection(httpUrl); 369 try { 370 InputStream inputStream = connection.getInputStream(); 371 return Streams.readFully(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); 372 } finally { 373 connection.disconnect(); 374 } 375 } 376 assertOnNetwork(String adressString, Network network)377 private void assertOnNetwork(String adressString, Network network) throws UnknownHostException { 378 InetAddress address = InetAddress.getByName(adressString); 379 LinkProperties linkProperties = mCm.getLinkProperties(network); 380 // To make sure that the request went out on the right network, check that 381 // the IP address seen by the server is assigned to the expected network. 382 // We can only do this for IPv6 addresses, because in IPv4 we will likely 383 // have a private IPv4 address, and that won't match what the server sees. 384 if (address instanceof Inet6Address) { 385 assertContains(linkProperties.getAddresses(), address); 386 } 387 } 388 assertContains(Collection<T> collection, T element)389 private static<T> void assertContains(Collection<T> collection, T element) { 390 assertTrue(element + " not found in " + collection, collection.contains(element)); 391 } 392 assertStartUsingNetworkFeatureUnsupported(int networkType, String feature)393 private void assertStartUsingNetworkFeatureUnsupported(int networkType, String feature) { 394 try { 395 mCm.startUsingNetworkFeature(networkType, feature); 396 fail("startUsingNetworkFeature is no longer supported in the current API version"); 397 } catch (UnsupportedOperationException expected) {} 398 } 399 assertStopUsingNetworkFeatureUnsupported(int networkType, String feature)400 private void assertStopUsingNetworkFeatureUnsupported(int networkType, String feature) { 401 try { 402 mCm.startUsingNetworkFeature(networkType, feature); 403 fail("stopUsingNetworkFeature is no longer supported in the current API version"); 404 } catch (UnsupportedOperationException expected) {} 405 } 406 assertRequestRouteToHostUnsupported(int networkType, int hostAddress)407 private void assertRequestRouteToHostUnsupported(int networkType, int hostAddress) { 408 try { 409 mCm.requestRouteToHost(networkType, hostAddress); 410 fail("requestRouteToHost is no longer supported in the current API version"); 411 } catch (UnsupportedOperationException expected) {} 412 } 413 414 @Test testStartUsingNetworkFeature()415 public void testStartUsingNetworkFeature() { 416 417 final String invalidateFeature = "invalidateFeature"; 418 final String mmsFeature = "enableMMS"; 419 420 assertStartUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature); 421 assertStopUsingNetworkFeatureUnsupported(TYPE_MOBILE, invalidateFeature); 422 assertStartUsingNetworkFeatureUnsupported(TYPE_WIFI, mmsFeature); 423 } 424 shouldEthernetBeSupported()425 private boolean shouldEthernetBeSupported() { 426 // Instant mode apps aren't allowed to query the Ethernet service due to selinux policies. 427 // When in instant mode, don't fail if the Ethernet service is available. Instead, rely on 428 // the fact that Ethernet should be supported if the device has a hardware Ethernet port, or 429 // if the device can be a USB host and thus can use USB Ethernet adapters. 430 // 431 // Note that this test this will still fail in instant mode if a device supports Ethernet 432 // via other hardware means. We are not currently aware of any such device. 433 return (mContext.getSystemService(Context.ETHERNET_SERVICE) != null) || 434 mPackageManager.hasSystemFeature(FEATURE_ETHERNET) || 435 mPackageManager.hasSystemFeature(FEATURE_USB_HOST); 436 } 437 shouldBeSupported(int networkType)438 private boolean shouldBeSupported(int networkType) { 439 return mNetworks.containsKey(networkType) || 440 (networkType == ConnectivityManager.TYPE_VPN) || 441 (networkType == ConnectivityManager.TYPE_ETHERNET && shouldEthernetBeSupported()); 442 } 443 444 @Test testIsNetworkSupported()445 public void testIsNetworkSupported() { 446 for (int type = -1; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { 447 boolean supported = mCm.isNetworkSupported(type); 448 if (shouldBeSupported(type)) { 449 assertTrue("Network type " + type + " should be supported", supported); 450 } else { 451 assertFalse("Network type " + type + " should not be supported", supported); 452 } 453 } 454 } 455 456 @Test testRequestRouteToHost()457 public void testRequestRouteToHost() { 458 for (int type = -1 ; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { 459 assertRequestRouteToHostUnsupported(type, HOST_ADDRESS); 460 } 461 } 462 463 @Test testTest()464 public void testTest() { 465 mCm.getBackgroundDataSetting(); 466 } 467 makeWifiNetworkRequest()468 private NetworkRequest makeWifiNetworkRequest() { 469 return new NetworkRequest.Builder() 470 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 471 .build(); 472 } 473 474 /** 475 * Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to 476 * see if we get a callback for the TRANSPORT_WIFI transport type being available. 477 * 478 * <p>In order to test that a NetworkCallback occurs, we need some change in the network 479 * state (either a transport or capability is now available). The most straightforward is 480 * WiFi. We could add a version that uses the telephony data connection but it's not clear 481 * that it would increase test coverage by much (how many devices have 3G radio but not Wifi?). 482 */ 483 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 484 @Test testRegisterNetworkCallback()485 public void testRegisterNetworkCallback() { 486 if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) { 487 Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi"); 488 return; 489 } 490 491 // We will register for a WIFI network being available or lost. 492 final TestNetworkCallback callback = new TestNetworkCallback(); 493 mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback); 494 495 final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback(); 496 mCm.registerDefaultNetworkCallback(defaultTrackingCallback); 497 498 Network wifiNetwork = null; 499 500 try { 501 ensureWifiConnected(); 502 503 // Now we should expect to get a network callback about availability of the wifi 504 // network even if it was already connected as a state-based action when the callback 505 // is registered. 506 wifiNetwork = callback.waitForAvailable(); 507 assertNotNull("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI", 508 wifiNetwork); 509 510 assertNotNull("Did not receive NetworkCallback.onAvailable for any default network", 511 defaultTrackingCallback.waitForAvailable()); 512 } catch (InterruptedException e) { 513 fail("Broadcast receiver or NetworkCallback wait was interrupted."); 514 } finally { 515 mCm.unregisterNetworkCallback(callback); 516 mCm.unregisterNetworkCallback(defaultTrackingCallback); 517 } 518 } 519 520 /** 521 * Tests both registerNetworkCallback and unregisterNetworkCallback similarly to 522 * {@link #testRegisterNetworkCallback} except that a {@code PendingIntent} is used instead 523 * of a {@code NetworkCallback}. 524 */ 525 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 526 @Test testRegisterNetworkCallback_withPendingIntent()527 public void testRegisterNetworkCallback_withPendingIntent() { 528 if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) { 529 Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi"); 530 return; 531 } 532 533 // Create a ConnectivityActionReceiver that has an IntentFilter for our locally defined 534 // action, NETWORK_CALLBACK_ACTION. 535 IntentFilter filter = new IntentFilter(); 536 filter.addAction(NETWORK_CALLBACK_ACTION); 537 538 ConnectivityActionReceiver receiver = new ConnectivityActionReceiver( 539 mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED); 540 mContext.registerReceiver(receiver, filter); 541 542 // Create a broadcast PendingIntent for NETWORK_CALLBACK_ACTION. 543 Intent intent = new Intent(NETWORK_CALLBACK_ACTION); 544 PendingIntent pendingIntent = PendingIntent.getBroadcast( 545 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 546 547 // We will register for a WIFI network being available or lost. 548 mCm.registerNetworkCallback(makeWifiNetworkRequest(), pendingIntent); 549 550 try { 551 ensureWifiConnected(); 552 553 // Now we expect to get the Intent delivered notifying of the availability of the wifi 554 // network even if it was already connected as a state-based action when the callback 555 // is registered. 556 assertTrue("Did not receive expected Intent " + intent + " for TRANSPORT_WIFI", 557 receiver.waitForState()); 558 } catch (InterruptedException e) { 559 fail("Broadcast receiver or NetworkCallback wait was interrupted."); 560 } finally { 561 mCm.unregisterNetworkCallback(pendingIntent); 562 pendingIntent.cancel(); 563 mContext.unregisterReceiver(receiver); 564 } 565 } 566 567 /** 568 * Exercises the requestNetwork with NetworkCallback API. This checks to 569 * see if we get a callback for an INTERNET request. 570 */ 571 @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps") 572 @Test testRequestNetworkCallback()573 public void testRequestNetworkCallback() { 574 final TestNetworkCallback callback = new TestNetworkCallback(); 575 mCm.requestNetwork(new NetworkRequest.Builder() 576 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 577 .build(), callback); 578 579 try { 580 // Wait to get callback for availability of internet 581 Network internetNetwork = callback.waitForAvailable(); 582 assertNotNull("Did not receive NetworkCallback#onAvailable for INTERNET", 583 internetNetwork); 584 } catch (InterruptedException e) { 585 fail("NetworkCallback wait was interrupted."); 586 } finally { 587 mCm.unregisterNetworkCallback(callback); 588 } 589 } 590 591 /** 592 * Exercises the requestNetwork with NetworkCallback API with timeout - expected to 593 * fail. Use WIFI and switch Wi-Fi off. 594 */ 595 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 596 @Test testRequestNetworkCallback_onUnavailable()597 public void testRequestNetworkCallback_onUnavailable() { 598 final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled(); 599 if (previousWifiEnabledState) { 600 mCtsNetUtils.disconnectFromWifi(null); 601 } 602 603 final TestNetworkCallback callback = new TestNetworkCallback(); 604 mCm.requestNetwork(new NetworkRequest.Builder() 605 .addTransportType(TRANSPORT_WIFI) 606 .build(), callback, 100); 607 608 try { 609 // Wait to get callback for unavailability of requested network 610 assertTrue("Did not receive NetworkCallback#onUnavailable", 611 callback.waitForUnavailable()); 612 } catch (InterruptedException e) { 613 fail("NetworkCallback wait was interrupted."); 614 } finally { 615 mCm.unregisterNetworkCallback(callback); 616 if (previousWifiEnabledState) { 617 mCtsNetUtils.connectToWifi(); 618 } 619 } 620 } 621 getFirstV4Address(Network network)622 private InetAddress getFirstV4Address(Network network) { 623 LinkProperties linkProperties = mCm.getLinkProperties(network); 624 for (InetAddress address : linkProperties.getAddresses()) { 625 if (address instanceof Inet4Address) { 626 return address; 627 } 628 } 629 return null; 630 } 631 632 /** Verify restricted networks cannot be requested. */ 633 @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps") 634 @Test testRestrictedNetworks()635 public void testRestrictedNetworks() { 636 // Verify we can request unrestricted networks: 637 NetworkRequest request = new NetworkRequest.Builder() 638 .addCapability(NET_CAPABILITY_INTERNET).build(); 639 NetworkCallback callback = new NetworkCallback(); 640 mCm.requestNetwork(request, callback); 641 mCm.unregisterNetworkCallback(callback); 642 // Verify we cannot request restricted networks: 643 request = new NetworkRequest.Builder().addCapability(NET_CAPABILITY_IMS).build(); 644 callback = new NetworkCallback(); 645 try { 646 mCm.requestNetwork(request, callback); 647 fail("No exception thrown when restricted network requested."); 648 } catch (SecurityException expected) {} 649 } 650 651 // Returns "true", "false" or "none" getWifiMeteredStatus(String ssid)652 private String getWifiMeteredStatus(String ssid) throws Exception { 653 // Interestingly giving the SSID as an argument to list wifi-networks 654 // only works iff the network in question has the "false" policy. 655 // Also unfortunately runShellCommand does not pass the command to the interpreter 656 // so it's not possible to | grep the ssid. 657 final String command = "cmd netpolicy list wifi-networks"; 658 final String policyString = runShellCommand(mInstrumentation, command); 659 660 final Matcher m = Pattern.compile("^" + ssid + ";(true|false|none)$", 661 Pattern.MULTILINE | Pattern.UNIX_LINES).matcher(policyString); 662 if (!m.find()) { 663 fail("Unexpected format from cmd netpolicy"); 664 } 665 return m.group(1); 666 } 667 668 // metered should be "true", "false" or "none" setWifiMeteredStatus(String ssid, String metered)669 private void setWifiMeteredStatus(String ssid, String metered) throws Exception { 670 final String setCommand = "cmd netpolicy set metered-network " + ssid + " " + metered; 671 runShellCommand(mInstrumentation, setCommand); 672 assertEquals(getWifiMeteredStatus(ssid), metered); 673 } 674 unquoteSSID(String ssid)675 private String unquoteSSID(String ssid) { 676 // SSID is returned surrounded by quotes if it can be decoded as UTF-8. 677 // Otherwise it's guaranteed not to start with a quote. 678 if (ssid.charAt(0) == '"') { 679 return ssid.substring(1, ssid.length() - 1); 680 } else { 681 return ssid; 682 } 683 } 684 waitForActiveNetworkMetered(int targetTransportType, boolean requestedMeteredness)685 private void waitForActiveNetworkMetered(int targetTransportType, boolean requestedMeteredness) 686 throws Exception { 687 final CountDownLatch latch = new CountDownLatch(1); 688 final NetworkCallback networkCallback = new NetworkCallback() { 689 @Override 690 public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { 691 if (!nc.hasTransport(targetTransportType)) return; 692 693 final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED); 694 if (metered == requestedMeteredness) { 695 latch.countDown(); 696 } 697 } 698 }; 699 // Registering a callback here guarantees onCapabilitiesChanged is called immediately 700 // with the current setting. Therefore, if the setting has already been changed, 701 // this method will return right away, and if not it will wait for the setting to change. 702 mCm.registerDefaultNetworkCallback(networkCallback); 703 if (!latch.await(NETWORK_CHANGE_METEREDNESS_TIMEOUT, TimeUnit.MILLISECONDS)) { 704 fail("Timed out waiting for active network metered status to change to " 705 + requestedMeteredness + " ; network = " + mCm.getActiveNetwork()); 706 } 707 mCm.unregisterNetworkCallback(networkCallback); 708 } 709 assertMultipathPreferenceIsEventually(Network network, int oldValue, int expectedValue)710 private void assertMultipathPreferenceIsEventually(Network network, int oldValue, 711 int expectedValue) { 712 // Quick check : if oldValue == expectedValue, there is no way to guarantee the test 713 // is not flaky. 714 assertNotSame(oldValue, expectedValue); 715 716 for (int i = 0; i < NUM_TRIES_MULTIPATH_PREF_CHECK; ++i) { 717 final int actualValue = mCm.getMultipathPreference(network); 718 if (actualValue == expectedValue) { 719 return; 720 } 721 if (actualValue != oldValue) { 722 fail("Multipath preference is neither previous (" + oldValue 723 + ") nor expected (" + expectedValue + ")"); 724 } 725 SystemClock.sleep(INTERVAL_MULTIPATH_PREF_CHECK_MS); 726 } 727 fail("Timed out waiting for multipath preference to change. expected = " 728 + expectedValue + " ; actual = " + mCm.getMultipathPreference(network)); 729 } 730 getCurrentMeteredMultipathPreference(ContentResolver resolver)731 private int getCurrentMeteredMultipathPreference(ContentResolver resolver) { 732 final String rawMeteredPref = Settings.Global.getString(resolver, 733 NETWORK_METERED_MULTIPATH_PREFERENCE); 734 return TextUtils.isEmpty(rawMeteredPref) 735 ? getIntResourceForName(NETWORK_METERED_MULTIPATH_PREFERENCE_RES_NAME) 736 : Integer.parseInt(rawMeteredPref); 737 } 738 findNextPrefValue(ContentResolver resolver)739 private int findNextPrefValue(ContentResolver resolver) { 740 // A bit of a nuclear hammer, but race conditions in CTS are bad. To be able to 741 // detect a correct setting value without race conditions, the next pref must 742 // be a valid value (range 0..3) that is different from the old setting of the 743 // metered preference and from the unmetered preference. 744 final int meteredPref = getCurrentMeteredMultipathPreference(resolver); 745 final int unmeteredPref = ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED; 746 if (0 != meteredPref && 0 != unmeteredPref) return 0; 747 if (1 != meteredPref && 1 != unmeteredPref) return 1; 748 return 2; 749 } 750 751 /** 752 * Verify that getMultipathPreference does return appropriate values 753 * for metered and unmetered networks. 754 */ 755 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 756 @Test testGetMultipathPreference()757 public void testGetMultipathPreference() throws Exception { 758 final ContentResolver resolver = mContext.getContentResolver(); 759 ensureWifiConnected(); 760 final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID()); 761 final String oldMeteredSetting = getWifiMeteredStatus(ssid); 762 final String oldMeteredMultipathPreference = Settings.Global.getString( 763 resolver, NETWORK_METERED_MULTIPATH_PREFERENCE); 764 try { 765 final int initialMeteredPreference = getCurrentMeteredMultipathPreference(resolver); 766 int newMeteredPreference = findNextPrefValue(resolver); 767 Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE, 768 Integer.toString(newMeteredPreference)); 769 setWifiMeteredStatus(ssid, "true"); 770 waitForActiveNetworkMetered(TRANSPORT_WIFI, true); 771 // Wifi meterness changes from unmetered to metered will disconnect and reconnect since 772 // R. 773 final Network network = ensureWifiConnected(); 774 assertEquals(ssid, unquoteSSID(mWifiManager.getConnectionInfo().getSSID())); 775 assertEquals(mCm.getNetworkCapabilities(network).hasCapability( 776 NET_CAPABILITY_NOT_METERED), false); 777 assertMultipathPreferenceIsEventually(network, initialMeteredPreference, 778 newMeteredPreference); 779 780 final int oldMeteredPreference = newMeteredPreference; 781 newMeteredPreference = findNextPrefValue(resolver); 782 Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE, 783 Integer.toString(newMeteredPreference)); 784 assertEquals(mCm.getNetworkCapabilities(network).hasCapability( 785 NET_CAPABILITY_NOT_METERED), false); 786 assertMultipathPreferenceIsEventually(network, 787 oldMeteredPreference, newMeteredPreference); 788 789 setWifiMeteredStatus(ssid, "false"); 790 // No disconnect from unmetered to metered. 791 waitForActiveNetworkMetered(TRANSPORT_WIFI, false); 792 assertEquals(mCm.getNetworkCapabilities(network).hasCapability( 793 NET_CAPABILITY_NOT_METERED), true); 794 assertMultipathPreferenceIsEventually(network, newMeteredPreference, 795 ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED); 796 } finally { 797 Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE, 798 oldMeteredMultipathPreference); 799 setWifiMeteredStatus(ssid, oldMeteredSetting); 800 } 801 } 802 803 // TODO: move the following socket keep alive test to dedicated test class. 804 /** 805 * Callback used in tcp keepalive offload that allows caller to wait callback fires. 806 */ 807 private static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback { 808 public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR }; 809 810 public static class CallbackValue { 811 public final CallbackType callbackType; 812 public final int error; 813 CallbackValue(final CallbackType type, final int error)814 private CallbackValue(final CallbackType type, final int error) { 815 this.callbackType = type; 816 this.error = error; 817 } 818 819 public static class OnStartedCallback extends CallbackValue { OnStartedCallback()820 OnStartedCallback() { super(CallbackType.ON_STARTED, 0); } 821 } 822 823 public static class OnStoppedCallback extends CallbackValue { OnStoppedCallback()824 OnStoppedCallback() { super(CallbackType.ON_STOPPED, 0); } 825 } 826 827 public static class OnErrorCallback extends CallbackValue { OnErrorCallback(final int error)828 OnErrorCallback(final int error) { super(CallbackType.ON_ERROR, error); } 829 } 830 831 @Override equals(Object o)832 public boolean equals(Object o) { 833 return o.getClass() == this.getClass() 834 && this.callbackType == ((CallbackValue) o).callbackType 835 && this.error == ((CallbackValue) o).error; 836 } 837 838 @Override toString()839 public String toString() { 840 return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, error); 841 } 842 } 843 844 private final LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>(); 845 846 @Override onStarted()847 public void onStarted() { 848 mCallbacks.add(new CallbackValue.OnStartedCallback()); 849 } 850 851 @Override onStopped()852 public void onStopped() { 853 mCallbacks.add(new CallbackValue.OnStoppedCallback()); 854 } 855 856 @Override onError(final int error)857 public void onError(final int error) { 858 mCallbacks.add(new CallbackValue.OnErrorCallback(error)); 859 } 860 pollCallback()861 public CallbackValue pollCallback() { 862 try { 863 return mCallbacks.poll(KEEPALIVE_CALLBACK_TIMEOUT_MS, 864 TimeUnit.MILLISECONDS); 865 } catch (InterruptedException e) { 866 fail("Callback not seen after " + KEEPALIVE_CALLBACK_TIMEOUT_MS + " ms"); 867 } 868 return null; 869 } expectCallback(CallbackValue expectedCallback)870 private void expectCallback(CallbackValue expectedCallback) { 871 final CallbackValue actualCallback = pollCallback(); 872 assertEquals(expectedCallback, actualCallback); 873 } 874 expectStarted()875 public void expectStarted() { 876 expectCallback(new CallbackValue.OnStartedCallback()); 877 } 878 expectStopped()879 public void expectStopped() { 880 expectCallback(new CallbackValue.OnStoppedCallback()); 881 } 882 expectError(int error)883 public void expectError(int error) { 884 expectCallback(new CallbackValue.OnErrorCallback(error)); 885 } 886 } 887 getAddrByName(final String hostname, final int family)888 private InetAddress getAddrByName(final String hostname, final int family) throws Exception { 889 final InetAddress[] allAddrs = InetAddress.getAllByName(hostname); 890 for (InetAddress addr : allAddrs) { 891 if (family == AF_INET && addr instanceof Inet4Address) return addr; 892 893 if (family == AF_INET6 && addr instanceof Inet6Address) return addr; 894 895 if (family == AF_UNSPEC) return addr; 896 } 897 return null; 898 } 899 getConnectedSocket(final Network network, final String host, final int port, final int family)900 private Socket getConnectedSocket(final Network network, final String host, final int port, 901 final int family) throws Exception { 902 final Socket s = network.getSocketFactory().createSocket(); 903 try { 904 final InetAddress addr = getAddrByName(host, family); 905 if (addr == null) fail("Fail to get destination address for " + family); 906 907 final InetSocketAddress sockAddr = new InetSocketAddress(addr, port); 908 s.connect(sockAddr); 909 } catch (Exception e) { 910 s.close(); 911 throw e; 912 } 913 return s; 914 } 915 getSupportedKeepalivesForNet(@onNull Network network)916 private int getSupportedKeepalivesForNet(@NonNull Network network) throws Exception { 917 final NetworkCapabilities nc = mCm.getNetworkCapabilities(network); 918 919 // Get number of supported concurrent keepalives for testing network. 920 final int[] keepalivesPerTransport = KeepaliveUtils.getSupportedKeepalives(mContext); 921 return KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities( 922 keepalivesPerTransport, nc); 923 } 924 isTcpKeepaliveSupportedByKernel()925 private static boolean isTcpKeepaliveSupportedByKernel() { 926 final String kVersionString = VintfRuntimeInfo.getKernelVersion(); 927 return compareMajorMinorVersion(kVersionString, "4.8") >= 0; 928 } 929 getVersionFromString(String version)930 private static Pair<Integer, Integer> getVersionFromString(String version) { 931 // Only gets major and minor number of the version string. 932 final Pattern versionPattern = Pattern.compile("^(\\d+)(\\.(\\d+))?.*"); 933 final Matcher m = versionPattern.matcher(version); 934 if (m.matches()) { 935 final int major = Integer.parseInt(m.group(1)); 936 final int minor = TextUtils.isEmpty(m.group(3)) ? 0 : Integer.parseInt(m.group(3)); 937 return new Pair<>(major, minor); 938 } else { 939 return new Pair<>(0, 0); 940 } 941 } 942 943 // TODO: Move to util class. compareMajorMinorVersion(final String s1, final String s2)944 private static int compareMajorMinorVersion(final String s1, final String s2) { 945 final Pair<Integer, Integer> v1 = getVersionFromString(s1); 946 final Pair<Integer, Integer> v2 = getVersionFromString(s2); 947 948 if (v1.first == v2.first) { 949 return Integer.compare(v1.second, v2.second); 950 } else { 951 return Integer.compare(v1.first, v2.first); 952 } 953 } 954 955 /** 956 * Verifies that version string compare logic returns expected result for various cases. 957 * Note that only major and minor number are compared. 958 */ 959 @Test testMajorMinorVersionCompare()960 public void testMajorMinorVersionCompare() { 961 assertEquals(0, compareMajorMinorVersion("4.8.1", "4.8")); 962 assertEquals(1, compareMajorMinorVersion("4.9", "4.8.1")); 963 assertEquals(1, compareMajorMinorVersion("5.0", "4.8")); 964 assertEquals(1, compareMajorMinorVersion("5", "4.8")); 965 assertEquals(0, compareMajorMinorVersion("5", "5.0")); 966 assertEquals(1, compareMajorMinorVersion("5-beta1", "4.8")); 967 assertEquals(0, compareMajorMinorVersion("4.8.0.0", "4.8")); 968 assertEquals(0, compareMajorMinorVersion("4.8-RC1", "4.8")); 969 assertEquals(0, compareMajorMinorVersion("4.8", "4.8")); 970 assertEquals(-1, compareMajorMinorVersion("3.10", "4.8.0")); 971 assertEquals(-1, compareMajorMinorVersion("4.7.10.10", "4.8")); 972 } 973 974 /** 975 * Verifies that the keepalive API cannot create any keepalive when the maximum number of 976 * keepalives is set to 0. 977 */ 978 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 979 @Test testKeepaliveWifiUnsupported()980 public void testKeepaliveWifiUnsupported() throws Exception { 981 if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) { 982 Log.i(TAG, "testKeepaliveUnsupported cannot execute unless device" 983 + " supports WiFi"); 984 return; 985 } 986 987 final Network network = ensureWifiConnected(); 988 if (getSupportedKeepalivesForNet(network) != 0) return; 989 final InetAddress srcAddr = getFirstV4Address(network); 990 assumeTrue("This test requires native IPv4", srcAddr != null); 991 992 runWithShellPermissionIdentity(() -> { 993 assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 1, 0)); 994 assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1)); 995 }); 996 } 997 998 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 999 @Test 1000 @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware") testCreateTcpKeepalive()1001 public void testCreateTcpKeepalive() throws Exception { 1002 if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) { 1003 Log.i(TAG, "testCreateTcpKeepalive cannot execute unless device supports WiFi"); 1004 return; 1005 } 1006 1007 final Network network = ensureWifiConnected(); 1008 if (getSupportedKeepalivesForNet(network) == 0) return; 1009 final InetAddress srcAddr = getFirstV4Address(network); 1010 assumeTrue("This test requires native IPv4", srcAddr != null); 1011 1012 // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support 1013 // NAT-T keepalive. If keepalive limits from resource overlay is not zero, TCP keepalive 1014 // needs to be supported except if the kernel doesn't support it. 1015 if (!isTcpKeepaliveSupportedByKernel()) { 1016 // Verify that the callback result is expected. 1017 runWithShellPermissionIdentity(() -> { 1018 assertEquals(0, createConcurrentSocketKeepalives(network, srcAddr, 0, 1)); 1019 }); 1020 Log.i(TAG, "testCreateTcpKeepalive is skipped for kernel " 1021 + VintfRuntimeInfo.getKernelVersion()); 1022 return; 1023 } 1024 1025 final byte[] requestBytes = CtsNetUtils.HTTP_REQUEST.getBytes("UTF-8"); 1026 // So far only ipv4 tcp keepalive offload is supported. 1027 // TODO: add test case for ipv6 tcp keepalive offload when it is supported. 1028 try (Socket s = getConnectedSocket(network, TEST_HOST, HTTP_PORT, AF_INET)) { 1029 1030 // Should able to start keep alive offload when socket is idle. 1031 final Executor executor = mContext.getMainExecutor(); 1032 final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(); 1033 1034 mUiAutomation.adoptShellPermissionIdentity(); 1035 try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) { 1036 sk.start(MIN_KEEPALIVE_INTERVAL); 1037 callback.expectStarted(); 1038 1039 // App should not able to write during keepalive offload. 1040 final OutputStream out = s.getOutputStream(); 1041 try { 1042 out.write(requestBytes); 1043 fail("Should not able to write"); 1044 } catch (IOException e) { } 1045 // App should not able to read during keepalive offload. 1046 final InputStream in = s.getInputStream(); 1047 byte[] responseBytes = new byte[4096]; 1048 try { 1049 in.read(responseBytes); 1050 fail("Should not able to read"); 1051 } catch (IOException e) { } 1052 1053 // Stop. 1054 sk.stop(); 1055 callback.expectStopped(); 1056 } finally { 1057 mUiAutomation.dropShellPermissionIdentity(); 1058 } 1059 1060 // Ensure socket is still connected. 1061 assertTrue(s.isConnected()); 1062 assertFalse(s.isClosed()); 1063 1064 // Let socket be not idle. 1065 try { 1066 final OutputStream out = s.getOutputStream(); 1067 out.write(requestBytes); 1068 } catch (IOException e) { 1069 fail("Failed to write data " + e); 1070 } 1071 // Make sure response data arrives. 1072 final MessageQueue fdHandlerQueue = Looper.getMainLooper().getQueue(); 1073 final FileDescriptor fd = s.getFileDescriptor$(); 1074 final CountDownLatch mOnReceiveLatch = new CountDownLatch(1); 1075 fdHandlerQueue.addOnFileDescriptorEventListener(fd, EVENT_INPUT, (readyFd, events) -> { 1076 mOnReceiveLatch.countDown(); 1077 return 0; // Unregister listener. 1078 }); 1079 if (!mOnReceiveLatch.await(2, TimeUnit.SECONDS)) { 1080 fdHandlerQueue.removeOnFileDescriptorEventListener(fd); 1081 fail("Timeout: no response data"); 1082 } 1083 1084 // Should get ERROR_SOCKET_NOT_IDLE because there is still data in the receive queue 1085 // that has not been read. 1086 mUiAutomation.adoptShellPermissionIdentity(); 1087 try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) { 1088 sk.start(MIN_KEEPALIVE_INTERVAL); 1089 callback.expectError(SocketKeepalive.ERROR_SOCKET_NOT_IDLE); 1090 } finally { 1091 mUiAutomation.dropShellPermissionIdentity(); 1092 } 1093 } 1094 } 1095 createConcurrentKeepalivesOfType( int requestCount, @NonNull TestSocketKeepaliveCallback callback, Supplier<SocketKeepalive> kaFactory)1096 private ArrayList<SocketKeepalive> createConcurrentKeepalivesOfType( 1097 int requestCount, @NonNull TestSocketKeepaliveCallback callback, 1098 Supplier<SocketKeepalive> kaFactory) { 1099 final ArrayList<SocketKeepalive> kalist = new ArrayList<>(); 1100 1101 int remainingRetries = MAX_KEEPALIVE_RETRY_COUNT; 1102 1103 // Test concurrent keepalives with the given supplier. 1104 while (kalist.size() < requestCount) { 1105 final SocketKeepalive ka = kaFactory.get(); 1106 ka.start(MIN_KEEPALIVE_INTERVAL); 1107 TestSocketKeepaliveCallback.CallbackValue cv = callback.pollCallback(); 1108 assertNotNull(cv); 1109 if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_ERROR) { 1110 if (kalist.size() == 0 && cv.error == SocketKeepalive.ERROR_UNSUPPORTED) { 1111 // Unsupported. 1112 break; 1113 } else if (cv.error == SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES) { 1114 // Limit reached or temporary unavailable due to stopped slot is not yet 1115 // released. 1116 if (remainingRetries > 0) { 1117 SystemClock.sleep(INTERVAL_KEEPALIVE_RETRY_MS); 1118 remainingRetries--; 1119 continue; 1120 } 1121 break; 1122 } 1123 } 1124 if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_STARTED) { 1125 kalist.add(ka); 1126 } else { 1127 fail("Unexpected error when creating " + (kalist.size() + 1) + " " 1128 + ka.getClass().getSimpleName() + ": " + cv); 1129 } 1130 } 1131 1132 return kalist; 1133 } 1134 createConcurrentNattSocketKeepalives( @onNull Network network, @NonNull InetAddress srcAddr, int requestCount, @NonNull TestSocketKeepaliveCallback callback)1135 private @NonNull ArrayList<SocketKeepalive> createConcurrentNattSocketKeepalives( 1136 @NonNull Network network, @NonNull InetAddress srcAddr, int requestCount, 1137 @NonNull TestSocketKeepaliveCallback callback) throws Exception { 1138 1139 final Executor executor = mContext.getMainExecutor(); 1140 1141 // Initialize a real NaT-T socket. 1142 final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE); 1143 final UdpEncapsulationSocket nattSocket = mIpSec.openUdpEncapsulationSocket(); 1144 final InetAddress dstAddr = getAddrByName(TEST_HOST, AF_INET); 1145 assertNotNull(srcAddr); 1146 assertNotNull(dstAddr); 1147 1148 // Test concurrent Nat-T keepalives. 1149 final ArrayList<SocketKeepalive> result = createConcurrentKeepalivesOfType(requestCount, 1150 callback, () -> mCm.createSocketKeepalive(network, nattSocket, 1151 srcAddr, dstAddr, executor, callback)); 1152 1153 nattSocket.close(); 1154 return result; 1155 } 1156 createConcurrentTcpSocketKeepalives( @onNull Network network, int requestCount, @NonNull TestSocketKeepaliveCallback callback)1157 private @NonNull ArrayList<SocketKeepalive> createConcurrentTcpSocketKeepalives( 1158 @NonNull Network network, int requestCount, 1159 @NonNull TestSocketKeepaliveCallback callback) { 1160 final Executor executor = mContext.getMainExecutor(); 1161 1162 // Create concurrent TCP keepalives. 1163 return createConcurrentKeepalivesOfType(requestCount, callback, () -> { 1164 // Assert that TCP connections can be established. The file descriptor of tcp 1165 // sockets will be duplicated and kept valid in service side if the keepalives are 1166 // successfully started. 1167 try (Socket tcpSocket = getConnectedSocket(network, TEST_HOST, HTTP_PORT, 1168 AF_INET)) { 1169 return mCm.createSocketKeepalive(network, tcpSocket, executor, callback); 1170 } catch (Exception e) { 1171 fail("Unexpected error when creating TCP socket: " + e); 1172 } 1173 return null; 1174 }); 1175 } 1176 1177 /** 1178 * Creates concurrent keepalives until the specified counts of each type of keepalives are 1179 * reached or the expected error callbacks are received for each type of keepalives. 1180 * 1181 * @return the total number of keepalives created. 1182 */ 1183 private int createConcurrentSocketKeepalives( 1184 @NonNull Network network, @NonNull InetAddress srcAddr, int nattCount, int tcpCount) 1185 throws Exception { 1186 final ArrayList<SocketKeepalive> kalist = new ArrayList<>(); 1187 final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback(); 1188 1189 kalist.addAll(createConcurrentNattSocketKeepalives(network, srcAddr, nattCount, callback)); 1190 kalist.addAll(createConcurrentTcpSocketKeepalives(network, tcpCount, callback)); 1191 1192 final int ret = kalist.size(); 1193 1194 // Clean up. 1195 for (final SocketKeepalive ka : kalist) { 1196 ka.stop(); 1197 callback.expectStopped(); 1198 } 1199 kalist.clear(); 1200 1201 return ret; 1202 } 1203 1204 /** 1205 * Verifies that the concurrent keepalive slots meet the minimum requirement, and don't 1206 * get leaked after iterations. 1207 */ 1208 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 1209 @Test 1210 @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware") 1211 public void testSocketKeepaliveLimitWifi() throws Exception { 1212 if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) { 1213 Log.i(TAG, "testSocketKeepaliveLimitWifi cannot execute unless device" 1214 + " supports WiFi"); 1215 return; 1216 } 1217 1218 final Network network = ensureWifiConnected(); 1219 final int supported = getSupportedKeepalivesForNet(network); 1220 if (supported == 0) { 1221 return; 1222 } 1223 final InetAddress srcAddr = getFirstV4Address(network); 1224 assumeTrue("This test requires native IPv4", srcAddr != null); 1225 1226 runWithShellPermissionIdentity(() -> { 1227 // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT. 1228 assertGreaterOrEqual(supported, MIN_SUPPORTED_WIFI_KEEPALIVE_COUNT); 1229 1230 // Verifies that Nat-T keepalives can be established. 1231 assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 1232 supported + 1, 0)); 1233 // Verifies that keepalives don't get leaked in second round. 1234 assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported, 1235 0)); 1236 }); 1237 1238 // If kernel < 4.8 then it doesn't support TCP keepalive, but it might still support 1239 // NAT-T keepalive. Test below cases only if TCP keepalive is supported by kernel. 1240 if (!isTcpKeepaliveSupportedByKernel()) return; 1241 1242 runWithShellPermissionIdentity(() -> { 1243 assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0, 1244 supported + 1)); 1245 1246 // Verifies that different types can be established at the same time. 1247 assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 1248 supported / 2, supported - supported / 2)); 1249 1250 // Verifies that keepalives don't get leaked in second round. 1251 assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 0, 1252 supported)); 1253 assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 1254 supported / 2, supported - supported / 2)); 1255 }); 1256 } 1257 1258 /** 1259 * Verifies that the concurrent keepalive slots meet the minimum telephony requirement, and 1260 * don't get leaked after iterations. 1261 */ 1262 @AppModeFull(reason = "Cannot request network in instant app mode") 1263 @Test 1264 @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware") 1265 public void testSocketKeepaliveLimitTelephony() throws Exception { 1266 if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) { 1267 Log.i(TAG, "testSocketKeepaliveLimitTelephony cannot execute unless device" 1268 + " supports telephony"); 1269 return; 1270 } 1271 1272 final int firstSdk = Build.VERSION.FIRST_SDK_INT; 1273 if (firstSdk < Build.VERSION_CODES.Q) { 1274 Log.i(TAG, "testSocketKeepaliveLimitTelephony: skip test for devices launching" 1275 + " before Q: " + firstSdk); 1276 return; 1277 } 1278 1279 final Network network = mCtsNetUtils.connectToCell(); 1280 final int supported = getSupportedKeepalivesForNet(network); 1281 final InetAddress srcAddr = getFirstV4Address(network); 1282 assumeTrue("This test requires native IPv4", srcAddr != null); 1283 1284 runWithShellPermissionIdentity(() -> { 1285 // Verifies that the supported keepalive slots meet minimum requirement. 1286 assertGreaterOrEqual(supported, MIN_SUPPORTED_CELLULAR_KEEPALIVE_COUNT); 1287 // Verifies that Nat-T keepalives can be established. 1288 assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, 1289 supported + 1, 0)); 1290 // Verifies that keepalives don't get leaked in second round. 1291 assertEquals(supported, createConcurrentSocketKeepalives(network, srcAddr, supported, 1292 0)); 1293 }); 1294 } 1295 1296 private int getIntResourceForName(@NonNull String resName) { 1297 final Resources r = mContext.getResources(); 1298 final int resId = r.getIdentifier(resName, "integer", "android"); 1299 return r.getInteger(resId); 1300 } 1301 1302 /** 1303 * Verifies that the keepalive slots are limited as customized for unprivileged requests. 1304 */ 1305 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 1306 @Test 1307 @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware") 1308 public void testSocketKeepaliveUnprivileged() throws Exception { 1309 if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) { 1310 Log.i(TAG, "testSocketKeepaliveUnprivileged cannot execute unless device" 1311 + " supports WiFi"); 1312 return; 1313 } 1314 1315 final Network network = ensureWifiConnected(); 1316 final int supported = getSupportedKeepalivesForNet(network); 1317 if (supported == 0) { 1318 return; 1319 } 1320 final InetAddress srcAddr = getFirstV4Address(network); 1321 assumeTrue("This test requires native IPv4", srcAddr != null); 1322 1323 // Resource ID might be shifted on devices that compiled with different symbols. 1324 // Thus, resolve ID at runtime is needed. 1325 final int allowedUnprivilegedPerUid = 1326 getIntResourceForName(KEEPALIVE_ALLOWED_UNPRIVILEGED_RES_NAME); 1327 final int reservedPrivilegedSlots = 1328 getIntResourceForName(KEEPALIVE_RESERVED_PER_SLOT_RES_NAME); 1329 // Verifies that unprivileged request per uid cannot exceed the limit customized in the 1330 // resource. Currently, unprivileged keepalive slots are limited to Nat-T only, this test 1331 // does not apply to TCP. 1332 assertGreaterOrEqual(supported, reservedPrivilegedSlots); 1333 assertGreaterOrEqual(supported, allowedUnprivilegedPerUid); 1334 final int expectedUnprivileged = 1335 Math.min(allowedUnprivilegedPerUid, supported - reservedPrivilegedSlots); 1336 assertEquals(expectedUnprivileged, 1337 createConcurrentSocketKeepalives(network, srcAddr, supported + 1, 0)); 1338 } 1339 1340 private static void assertGreaterOrEqual(long greater, long lesser) { 1341 assertTrue("" + greater + " expected to be greater than or equal to " + lesser, 1342 greater >= lesser); 1343 } 1344 1345 /** 1346 * Verifies that apps are not allowed to access restricted networks even if they declare the 1347 * CONNECTIVITY_USE_RESTRICTED_NETWORKS permission in their manifests. 1348 * See. b/144679405. 1349 */ 1350 @AppModeFull(reason = "Cannot get WifiManager in instant app mode") 1351 @Test 1352 public void testRestrictedNetworkPermission() throws Exception { 1353 // Ensure that CONNECTIVITY_USE_RESTRICTED_NETWORKS isn't granted to this package. 1354 final PackageInfo app = mPackageManager.getPackageInfo(mContext.getPackageName(), 1355 GET_PERMISSIONS); 1356 final int index = ArrayUtils.indexOf( 1357 app.requestedPermissions, CONNECTIVITY_USE_RESTRICTED_NETWORKS); 1358 assertTrue(index >= 0); 1359 assertTrue(app.requestedPermissionsFlags[index] != PERMISSION_GRANTED); 1360 1361 // Ensure that NetworkUtils.queryUserAccess always returns false since this package should 1362 // not have netd system permission to call this function. 1363 final Network wifiNetwork = ensureWifiConnected(); 1364 assertFalse(NetworkUtils.queryUserAccess(Binder.getCallingUid(), wifiNetwork.netId)); 1365 1366 // Ensure that this package cannot bind to any restricted network that's currently 1367 // connected. 1368 Network[] networks = mCm.getAllNetworks(); 1369 for (Network network : networks) { 1370 NetworkCapabilities nc = mCm.getNetworkCapabilities(network); 1371 if (nc != null && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) { 1372 try { 1373 network.bindSocket(new Socket()); 1374 fail("Bind to restricted network " + network + " unexpectedly succeeded"); 1375 } catch (IOException expected) {} 1376 } 1377 } 1378 } 1379 1380 /** 1381 * Verifies that apps are allowed to call setAirplaneMode if they declare 1382 * NETWORK_AIRPLANE_MODE permission in their manifests. 1383 * See b/145164696. 1384 */ 1385 @AppModeFull(reason = "NETWORK_AIRPLANE_MODE permission can't be granted to instant apps") 1386 @Test 1387 public void testSetAirplaneMode() throws Exception{ 1388 // store the current state of airplane mode 1389 final boolean isAirplaneModeEnabled = isAirplaneModeEnabled(); 1390 1391 // disable airplane mode to reach a known state 1392 runShellCommand("cmd connectivity airplane-mode disable"); 1393 1394 try { 1395 // Verify we cannot set Airplane Mode without correct permission: 1396 try { 1397 setAndVerifyAirplaneMode(true); 1398 fail("SecurityException should have been thrown when setAirplaneMode was called" 1399 + "without holding permission NETWORK_AIRPLANE_MODE."); 1400 } catch (SecurityException expected) {} 1401 1402 // disable airplane mode again to reach a known state 1403 runShellCommand("cmd connectivity airplane-mode disable"); 1404 1405 // adopt shell permission which holds NETWORK_AIRPLANE_MODE 1406 mUiAutomation.adoptShellPermissionIdentity(); 1407 1408 // Verify we can enable Airplane Mode with correct permission: 1409 try { 1410 setAndVerifyAirplaneMode(true); 1411 } catch (SecurityException e) { 1412 fail("SecurityException should not have been thrown when setAirplaneMode(true) was" 1413 + "called whilst holding the NETWORK_AIRPLANE_MODE permission."); 1414 } 1415 1416 // Verify we can disable Airplane Mode with correct permission: 1417 try { 1418 setAndVerifyAirplaneMode(false); 1419 } catch (SecurityException e) { 1420 fail("SecurityException should not have been thrown when setAirplaneMode(false) was" 1421 + "called whilst holding the NETWORK_AIRPLANE_MODE permission."); 1422 } 1423 1424 } finally { 1425 // Restore the previous state of airplane mode and permissions: 1426 runShellCommand("cmd connectivity airplane-mode " 1427 + (isAirplaneModeEnabled ? "enable" : "disable")); 1428 mUiAutomation.dropShellPermissionIdentity(); 1429 } 1430 } 1431 1432 private void setAndVerifyAirplaneMode(Boolean expectedResult) 1433 throws Exception { 1434 final CompletableFuture<Boolean> actualResult = new CompletableFuture(); 1435 BroadcastReceiver receiver = new BroadcastReceiver() { 1436 @Override 1437 public void onReceive(Context context, Intent intent) { 1438 // The defaultValue of getExtraBoolean should be the opposite of what is 1439 // expected, thus ensuring a test failure if the extra is absent. 1440 actualResult.complete(intent.getBooleanExtra("state", !expectedResult)); 1441 } 1442 }; 1443 try { 1444 mContext.registerReceiver(receiver, 1445 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); 1446 mCm.setAirplaneMode(expectedResult); 1447 final String msg = "Setting Airplane Mode failed,"; 1448 assertEquals(msg, expectedResult, actualResult.get(AIRPLANE_MODE_CHANGE_TIMEOUT_MS, 1449 TimeUnit.MILLISECONDS)); 1450 } finally { 1451 mContext.unregisterReceiver(receiver); 1452 } 1453 } 1454 1455 private static boolean isAirplaneModeEnabled() { 1456 return runShellCommand("cmd connectivity airplane-mode") 1457 .trim().equals("enabled"); 1458 } 1459 } 1460