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