From ed26ffe92d044ef1f0c634270aa3daa2caf70f87 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 8 Apr 2021 12:50:42 +0900 Subject: [PATCH 1/3] Add CTS tests for registerDefaultNetworkCallbackAsUid. Bug: 165835257 Test: test-only change Change-Id: Ib3266665735ee587f3f8ae82dadd1e4ca7ce4353 --- tests/cts/hostside/app/Android.bp | 1 + .../com/android/cts/net/hostside/VpnTest.java | 48 ++++++++++++------- .../net/cts/ConnectivityManagerTest.java | 28 +++++++---- 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp index f351b47fad..112b9eb778 100644 --- a/tests/cts/hostside/app/Android.bp +++ b/tests/cts/hostside/app/Android.bp @@ -27,6 +27,7 @@ android_test_helper_app { "androidx.test.rules", "androidx.test.ext.junit", "compatibility-device-util-axt", + "cts-net-utils", "ctstestrunner-axt", "ub-uiautomator", "CtsHostsideNetworkTestsAidl", diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java index 9b437e64a8..a47d3042ae 100755 --- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java @@ -55,12 +55,14 @@ import android.net.Uri; import android.net.VpnManager; import android.net.VpnService; import android.net.VpnTransportInfo; +import android.net.cts.util.CtsNetUtils.TestNetworkCallback; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.SystemProperties; +import android.os.UserHandle; import android.provider.Settings; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject; @@ -741,13 +743,20 @@ public class VpnTest extends InstrumentationTestCase { getInstrumentation().getTargetContext(), MyVpnService.ACTION_ESTABLISHED); receiver.register(); - // Expect the system default network not to change. - final NeverChangeNetworkCallback neverChangeCallback = new NeverChangeNetworkCallback(); + // Test the behaviour of a variety of types of network callbacks. final Network defaultNetwork = mCM.getActiveNetwork(); + final NeverChangeNetworkCallback systemDefaultCallback = new NeverChangeNetworkCallback(); + final NeverChangeNetworkCallback otherUidCallback = new NeverChangeNetworkCallback(); + final TestNetworkCallback myUidCallback = new TestNetworkCallback(); if (SdkLevel.isAtLeastS()) { - runWithShellPermissionIdentity(() -> - mCM.registerSystemDefaultNetworkCallback(neverChangeCallback, - new Handler(Looper.getMainLooper())), NETWORK_SETTINGS); + final int otherUid = UserHandle.getUid(UserHandle.of(5), Process.FIRST_APPLICATION_UID); + final Handler h = new Handler(Looper.getMainLooper()); + runWithShellPermissionIdentity(() -> { + mCM.registerSystemDefaultNetworkCallback(systemDefaultCallback, h); + mCM.registerDefaultNetworkCallbackAsUid(otherUid, otherUidCallback, h); + mCM.registerDefaultNetworkCallbackAsUid(Process.myUid(), myUidCallback, h); + }, NETWORK_SETTINGS); + assertEquals(defaultNetwork, myUidCallback.waitForAvailable()); } FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS); @@ -767,20 +776,27 @@ public class VpnTest extends InstrumentationTestCase { checkTrafficOnVpn(); - maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); + final Network vpnNetwork = myUidCallback.waitForAvailable(); + assertEquals(vpnNetwork, mCM.getActiveNetwork()); + assertNotEqual(defaultNetwork, vpnNetwork); + maybeExpectVpnTransportInfo(vpnNetwork); - assertNotEqual(defaultNetwork, mCM.getActiveNetwork()); if (SdkLevel.isAtLeastS()) { // Check that system default network callback has not seen any network changes, even - // though the app's default network changed. This needs to be done before testing - // private DNS because checkStrictModePrivateDns will set the private DNS server to - // a nonexistent name, which will cause validation to fail and cause the default - // network to switch (e.g., from wifi to cellular). - assertEquals(defaultNetwork, neverChangeCallback.getFirstNetwork()); - neverChangeCallback.assertNeverChanged(); - runWithShellPermissionIdentity( - () -> mCM.unregisterNetworkCallback(neverChangeCallback), - NETWORK_SETTINGS); + // though the app's default network changed. Also check that otherUidCallback saw no + // network changes, because otherUid is in a different user and not subject to the VPN. + // This needs to be done before testing private DNS because checkStrictModePrivateDns + // will set the private DNS server to a nonexistent name, which will cause validation to + // fail and could cause the default network to switch (e.g., from wifi to cellular). + assertEquals(defaultNetwork, systemDefaultCallback.getFirstNetwork()); + systemDefaultCallback.assertNeverChanged(); + assertEquals(defaultNetwork, otherUidCallback.getFirstNetwork()); + otherUidCallback.assertNeverChanged(); + runWithShellPermissionIdentity(() -> { + mCM.unregisterNetworkCallback(systemDefaultCallback); + mCM.unregisterNetworkCallback(otherUidCallback); + mCM.unregisterNetworkCallback(myUidCallback); + }, NETWORK_SETTINGS); } checkStrictModePrivateDns(); diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java index 096f656eaf..18f05888b4 100644 --- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java +++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java @@ -111,6 +111,7 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.MessageQueue; +import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.os.VintfRuntimeInfo; @@ -587,12 +588,14 @@ public class ConnectivityManagerTest { final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback(); mCm.registerDefaultNetworkCallback(defaultTrackingCallback); - final TestNetworkCallback systemDefaultTrackingCallback = new TestNetworkCallback(); + final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback(); + final TestNetworkCallback perUidCallback = new TestNetworkCallback(); + final Handler h = new Handler(Looper.getMainLooper()); if (shouldTestSApis()) { - runWithShellPermissionIdentity(() -> - mCmShim.registerSystemDefaultNetworkCallback(systemDefaultTrackingCallback, - new Handler(Looper.getMainLooper())), - NETWORK_SETTINGS); + runWithShellPermissionIdentity(() -> { + mCmShim.registerSystemDefaultNetworkCallback(systemDefaultCallback, h); + mCmShim.registerDefaultNetworkCallbackAsUid(Process.myUid(), perUidCallback, h); + }, NETWORK_SETTINGS); } Network wifiNetwork = null; @@ -607,22 +610,27 @@ public class ConnectivityManagerTest { assertNotNull("Did not receive onAvailable for TRANSPORT_WIFI request", wifiNetwork); + final Network defaultNetwork = defaultTrackingCallback.waitForAvailable(); assertNotNull("Did not receive onAvailable on default network callback", - defaultTrackingCallback.waitForAvailable()); + defaultNetwork); if (shouldTestSApis()) { assertNotNull("Did not receive onAvailable on system default network callback", - systemDefaultTrackingCallback.waitForAvailable()); + systemDefaultCallback.waitForAvailable()); + final Network perUidNetwork = perUidCallback.waitForAvailable(); + assertNotNull("Did not receive onAvailable on per-UID default network callback", + perUidNetwork); + assertEquals(defaultNetwork, perUidNetwork); } + } catch (InterruptedException e) { fail("Broadcast receiver or NetworkCallback wait was interrupted."); } finally { mCm.unregisterNetworkCallback(callback); mCm.unregisterNetworkCallback(defaultTrackingCallback); if (shouldTestSApis()) { - runWithShellPermissionIdentity( - () -> mCm.unregisterNetworkCallback(systemDefaultTrackingCallback), - NETWORK_SETTINGS); + mCm.unregisterNetworkCallback(systemDefaultCallback); + mCm.unregisterNetworkCallback(perUidCallback); } } } From fd3b95c1f7b2e80c46026a1d4174454df57e7f07 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 8 Apr 2021 20:58:40 +0900 Subject: [PATCH 2/3] Ensure the HostsideVpnTests passes with keyguard locked. The test app used in CtsHostsideNetworkTests already dismisses the keyguard for its own activity, but that doesn't work for the VPN tests. This is because as soon as the VPN dialog appears, the test activity is no longer in the foreground and the keyguard comes back. As a result, the test cannot click on the VPN dialog. Test: test-only change Change-Id: I7be1d7fb46a2f9547bc5325d75b5bd5546e6dc18 --- .../app/src/com/android/cts/net/hostside/MyActivity.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java index 0d0bc58504..55eec1190d 100644 --- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java @@ -17,6 +17,7 @@ package com.android.cts.net.hostside; import android.app.Activity; +import android.app.KeyguardManager; import android.content.Intent; import android.os.Bundle; import android.view.WindowManager; @@ -34,6 +35,11 @@ public class MyActivity extends Activity { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); + + // Dismiss the keyguard so that the tests can click on the VPN confirmation dialog. + // FLAG_DISMISS_KEYGUARD is not sufficient to do this because as soon as the dialog appears, + // this activity goes into the background and the keyguard reappears. + getSystemService(KeyguardManager.class).requestDismissKeyguard(this, null /* callback */); } @Override From 08b33ff7fba0f716a54ae6c8fb725e39d87d6ff9 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Thu, 8 Apr 2021 22:07:31 +0900 Subject: [PATCH 3/3] Cleanups to VPN hostside tests. Use TestableNetworkCallback instead of a hand-rolled class. Remove unnecessary runWithShellPermissionIdentity around unregisterNetworkCallback calls. Bug: 165835257 Test: test-only change Change-Id: I4557dfc64136f9c0b4bdaa1248c33b13e96ba3ed --- .../com/android/cts/net/hostside/VpnTest.java | 60 ++++++------------- 1 file changed, 17 insertions(+), 43 deletions(-) diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java index a47d3042ae..c0600e761f 100755 --- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java @@ -55,7 +55,6 @@ import android.net.Uri; import android.net.VpnManager; import android.net.VpnService; import android.net.VpnTransportInfo; -import android.net.cts.util.CtsNetUtils.TestNetworkCallback; import android.net.wifi.WifiManager; import android.os.Handler; import android.os.Looper; @@ -78,6 +77,7 @@ import android.util.Log; import com.android.compatibility.common.util.BlockingBroadcastReceiver; import com.android.modules.utils.build.SdkLevel; +import com.android.testutils.TestableNetworkCallback; import java.io.Closeable; import java.io.FileDescriptor; @@ -94,6 +94,7 @@ import java.net.Socket; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.Random; import java.util.concurrent.CompletableFuture; @@ -700,34 +701,6 @@ public class VpnTest extends InstrumentationTestCase { setAndVerifyPrivateDns(initialMode); } - private class NeverChangeNetworkCallback extends NetworkCallback { - private CountDownLatch mLatch = new CountDownLatch(1); - private volatile Network mFirstNetwork; - private volatile Network mOtherNetwork; - - public void onAvailable(Network n) { - // Don't assert here, as it crashes the test with a hard to debug message. - if (mFirstNetwork == null) { - mFirstNetwork = n; - mLatch.countDown(); - } else if (mOtherNetwork == null) { - mOtherNetwork = n; - } - } - - public Network getFirstNetwork() throws Exception { - assertTrue( - "System default callback got no network after " + TIMEOUT_MS + "ms. " - + "Please ensure the device has a working Internet connection.", - mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); - return mFirstNetwork; - } - - public void assertNeverChanged() { - assertNull(mOtherNetwork); - } - } - public void testDefault() throws Exception { if (!supportedHardware()) return; // If adb TCP port opened, this test may running by adb over network. @@ -745,9 +718,9 @@ public class VpnTest extends InstrumentationTestCase { // Test the behaviour of a variety of types of network callbacks. final Network defaultNetwork = mCM.getActiveNetwork(); - final NeverChangeNetworkCallback systemDefaultCallback = new NeverChangeNetworkCallback(); - final NeverChangeNetworkCallback otherUidCallback = new NeverChangeNetworkCallback(); - final TestNetworkCallback myUidCallback = new TestNetworkCallback(); + final TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback(); + final TestableNetworkCallback otherUidCallback = new TestableNetworkCallback(); + final TestableNetworkCallback myUidCallback = new TestableNetworkCallback(); if (SdkLevel.isAtLeastS()) { final int otherUid = UserHandle.getUid(UserHandle.of(5), Process.FIRST_APPLICATION_UID); final Handler h = new Handler(Looper.getMainLooper()); @@ -756,7 +729,11 @@ public class VpnTest extends InstrumentationTestCase { mCM.registerDefaultNetworkCallbackAsUid(otherUid, otherUidCallback, h); mCM.registerDefaultNetworkCallbackAsUid(Process.myUid(), myUidCallback, h); }, NETWORK_SETTINGS); - assertEquals(defaultNetwork, myUidCallback.waitForAvailable()); + for (TestableNetworkCallback callback : + List.of(systemDefaultCallback, otherUidCallback, myUidCallback)) { + callback.expectAvailableCallbacks(defaultNetwork, false /* suspended */, + true /* validated */, false /* blocked */, TIMEOUT_MS); + } } FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS); @@ -776,7 +753,8 @@ public class VpnTest extends InstrumentationTestCase { checkTrafficOnVpn(); - final Network vpnNetwork = myUidCallback.waitForAvailable(); + final Network vpnNetwork = mCM.getActiveNetwork(); + myUidCallback.expectAvailableThenValidatedCallbacks(vpnNetwork, TIMEOUT_MS); assertEquals(vpnNetwork, mCM.getActiveNetwork()); assertNotEqual(defaultNetwork, vpnNetwork); maybeExpectVpnTransportInfo(vpnNetwork); @@ -788,15 +766,11 @@ public class VpnTest extends InstrumentationTestCase { // This needs to be done before testing private DNS because checkStrictModePrivateDns // will set the private DNS server to a nonexistent name, which will cause validation to // fail and could cause the default network to switch (e.g., from wifi to cellular). - assertEquals(defaultNetwork, systemDefaultCallback.getFirstNetwork()); - systemDefaultCallback.assertNeverChanged(); - assertEquals(defaultNetwork, otherUidCallback.getFirstNetwork()); - otherUidCallback.assertNeverChanged(); - runWithShellPermissionIdentity(() -> { - mCM.unregisterNetworkCallback(systemDefaultCallback); - mCM.unregisterNetworkCallback(otherUidCallback); - mCM.unregisterNetworkCallback(myUidCallback); - }, NETWORK_SETTINGS); + systemDefaultCallback.assertNoCallback(); + otherUidCallback.assertNoCallback(); + mCM.unregisterNetworkCallback(systemDefaultCallback); + mCM.unregisterNetworkCallback(otherUidCallback); + mCM.unregisterNetworkCallback(myUidCallback); } checkStrictModePrivateDns();