diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java index bb32052bc0..198b009be6 100644 --- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java @@ -62,6 +62,8 @@ import android.service.notification.NotificationListenerService; import android.util.Log; import android.util.Pair; +import androidx.annotation.Nullable; + import com.android.compatibility.common.util.AmUtils; import com.android.compatibility.common.util.BatteryUtils; import com.android.compatibility.common.util.DeviceConfigStateHelper; @@ -283,8 +285,30 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { } protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception { + assertBackgroundNetworkAccess(expectAllowed, null); + } + + /** + * Asserts whether the active network is available or not for the background app. If the network + * is unavailable, also checks whether it is blocked by the expected error. + * + * @param expectAllowed expect background network access to be allowed or not. + * @param expectedUnavailableError the expected error when {@code expectAllowed} is false. It's + * meaningful only when the {@code expectAllowed} is 'false'. + * Throws an IllegalArgumentException when {@code expectAllowed} + * is true and this parameter is not null. When the + * {@code expectAllowed} is 'false' and this parameter is null, + * this function does not compare error type of the networking + * access failure. + */ + protected void assertBackgroundNetworkAccess(boolean expectAllowed, + @Nullable final String expectedUnavailableError) throws Exception { assertBackgroundState(); - assertNetworkAccess(expectAllowed /* expectAvailable */, false /* needScreenOn */); + if (expectAllowed && expectedUnavailableError != null) { + throw new IllegalArgumentException("expectedUnavailableError is not null"); + } + assertNetworkAccess(expectAllowed /* expectAvailable */, false /* needScreenOn */, + expectedUnavailableError); } protected void assertForegroundNetworkAccess() throws Exception { @@ -407,12 +431,17 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { */ private void assertNetworkAccess(boolean expectAvailable, boolean needScreenOn) throws Exception { + assertNetworkAccess(expectAvailable, needScreenOn, null); + } + + private void assertNetworkAccess(boolean expectAvailable, boolean needScreenOn, + @Nullable final String expectedUnavailableError) throws Exception { final int maxTries = 5; String error = null; int timeoutMs = 500; for (int i = 1; i <= maxTries; i++) { - error = checkNetworkAccess(expectAvailable); + error = checkNetworkAccess(expectAvailable, expectedUnavailableError); if (error == null) return; @@ -479,12 +508,15 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { * * @return error message with the mismatch (or empty if assertion passed). */ - private String checkNetworkAccess(boolean expectAvailable) throws Exception { + private String checkNetworkAccess(boolean expectAvailable, + @Nullable final String expectedUnavailableError) throws Exception { final String resultData = mServiceClient.checkNetworkStatus(); - return checkForAvailabilityInResultData(resultData, expectAvailable); + return checkForAvailabilityInResultData(resultData, expectAvailable, + expectedUnavailableError); } - private String checkForAvailabilityInResultData(String resultData, boolean expectAvailable) { + private String checkForAvailabilityInResultData(String resultData, boolean expectAvailable, + @Nullable final String expectedUnavailableError) { if (resultData == null) { assertNotNull("Network status from app2 is null", resultData); } @@ -516,6 +548,10 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { if (expectedState != state || expectedDetailedState != detailedState) { errors.append(String.format("Connection state mismatch: expected %s/%s, got %s/%s\n", expectedState, expectedDetailedState, state, detailedState)); + } else if (!expectAvailable && (expectedUnavailableError != null) + && !connectionCheckDetails.contains(expectedUnavailableError)) { + errors.append("Connection unavailable reason mismatch: expected " + + expectedUnavailableError + "\n"); } if (errors.length() > 0) { @@ -914,7 +950,7 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { final String resultData = result.get(0).second; if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) { final String error = checkForAvailabilityInResultData( - resultData, expectAvailable); + resultData, expectAvailable, null /* expectedUnavailableError */); if (error != null) { fail("Network is not available for activity in app2 (" + mUid + "): " + error); @@ -949,7 +985,7 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { final String resultData = result.get(0).second; if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) { final String error = checkForAvailabilityInResultData( - resultData, expectAvailable); + resultData, expectAvailable, null /* expectedUnavailableError */); if (error != null) { Log.d(TAG, "Network state is unexpected, checking again. " + error); // Right now we could end up in an unexpected state if expedited job diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java index ab3cf14705..82f4a65184 100644 --- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java @@ -32,8 +32,11 @@ import static org.junit.Assume.assumeTrue; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.cts.util.CtsNetUtils; import android.util.Log; +import com.android.modules.utils.build.SdkLevel; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -46,6 +49,9 @@ import java.util.concurrent.TimeUnit; public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase { private Network mNetwork; private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback(); + private CtsNetUtils mCtsNetUtils; + private static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google"; + @Rule public final MeterednessConfigurationRule mMeterednessConfiguration = new MeterednessConfigurationRule(); @@ -218,6 +224,26 @@ public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCa mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, false /* hasCapability */, NET_CAPABILITY_NOT_METERED); mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false); + + // Before Android T, DNS queries over private DNS should be but are not restricted by Power + // Saver or Data Saver. The issue is fixed in mainline update and apps can no longer request + // DNS queries when its network is restricted by Power Saver. The fix takes effect backwards + // starting from Android T. But for Data Saver, the fix is not backward compatible since + // there are some platform changes involved. It is only available on devices that a specific + // trunk flag is enabled. + // + // This test can not only verify that the network traffic from apps is blocked at the right + // time, but also verify whether it is correctly blocked at the DNS stage, or at a later + // socket connection stage. + if (SdkLevel.isAtLeastT()) { + // Enable private DNS + mCtsNetUtils = new CtsNetUtils(mContext); + mCtsNetUtils.storePrivateDnsSetting(); + mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER); + mCtsNetUtils.awaitPrivateDnsSetting( + "NetworkCallbackTest wait private DNS setting timeout", mNetwork, + GOOGLE_PRIVATE_DNS_SERVER, true); + } } @After @@ -227,6 +253,10 @@ public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCa setRestrictBackground(false); setBatterySaverMode(false); unregisterNetworkCallback(); + + if (SdkLevel.isAtLeastT() && (mCtsNetUtils != null)) { + mCtsNetUtils.restorePrivateDnsSetting(); + } } @RequiredProperties({DATA_SAVER_MODE}) @@ -235,6 +265,8 @@ public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCa try { // Enable restrict background setRestrictBackground(true); + // TODO: Verify expectedUnavailableError when aconfig support mainline. + // (see go/aconfig-in-mainline-problems) assertBackgroundNetworkAccess(false); assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); @@ -247,6 +279,7 @@ public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCa // Remove from whitelist removeRestrictBackgroundWhitelist(mUid); + // TODO: Verify expectedUnavailableError when aconfig support mainline. assertBackgroundNetworkAccess(false); assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); @@ -278,7 +311,11 @@ public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCa try { // Enable Power Saver setBatterySaverMode(true); - assertBackgroundNetworkAccess(false); + if (SdkLevel.isAtLeastT()) { + assertBackgroundNetworkAccess(false, "java.net.UnknownHostException"); + } else { + assertBackgroundNetworkAccess(false); + } mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); @@ -298,7 +335,11 @@ public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCa try { // Enable Power Saver setBatterySaverMode(true); - assertBackgroundNetworkAccess(false); + if (SdkLevel.isAtLeastT()) { + assertBackgroundNetworkAccess(false, "java.net.UnknownHostException"); + } else { + assertBackgroundNetworkAccess(false); + } mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */);