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/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 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..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 @@ -61,6 +61,7 @@ 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; @@ -76,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; @@ -92,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; @@ -698,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. @@ -741,13 +716,24 @@ 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 TestableNetworkCallback systemDefaultCallback = new TestableNetworkCallback(); + final TestableNetworkCallback otherUidCallback = new TestableNetworkCallback(); + final TestableNetworkCallback myUidCallback = new TestableNetworkCallback(); 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); + 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); @@ -767,20 +753,24 @@ public class VpnTest extends InstrumentationTestCase { checkTrafficOnVpn(); - maybeExpectVpnTransportInfo(mCM.getActiveNetwork()); + final Network vpnNetwork = mCM.getActiveNetwork(); + myUidCallback.expectAvailableThenValidatedCallbacks(vpnNetwork, TIMEOUT_MS); + 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). + systemDefaultCallback.assertNoCallback(); + otherUidCallback.assertNoCallback(); + mCM.unregisterNetworkCallback(systemDefaultCallback); + mCM.unregisterNetworkCallback(otherUidCallback); + mCM.unregisterNetworkCallback(myUidCallback); } 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); } } }