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