diff --git a/tests/cts/hostside/Android.bp b/tests/cts/hostside/Android.bp index 2688fb8ff9369e66dc599913c229aa8f8b234ec0..f6c043059bc29aafbf17d941f0ae76c03bc85c05 100644 --- a/tests/cts/hostside/Android.bp +++ b/tests/cts/hostside/Android.bp @@ -27,7 +27,10 @@ java_test_host { name: "CtsHostsideNetworkTests", defaults: ["cts_defaults"], // Only compile source java files in this apk. - srcs: ["src/**/*.java"], + srcs: [ + "src/**/*.java", + ":ArgumentConstants", + ], libs: [ "net-tests-utils-host-device-common", "cts-tradefed", diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp index d5554915b1aec07710b44c608254b6e5b8c097a8..cf4afa9a443d1a4b89faf1f56954e16e7ea45bc8 100644 --- a/tests/cts/hostside/app/Android.bp +++ b/tests/cts/hostside/app/Android.bp @@ -36,7 +36,10 @@ java_defaults { "android.test.runner", "android.test.base", ], - srcs: ["src/**/*.java"], + srcs: [ + "src/**/*.java", + ":ArgumentConstants", + ], // Tag this module as a cts test artifact test_suites: [ "general-tests", diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDefaultRestrictionsTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDefaultRestrictionsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8a3e79021ddf596ff81e14eda4d1f7a10e83c4fa --- /dev/null +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDefaultRestrictionsTest.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.net.hostside; + +import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING; + +import static org.junit.Assume.assumeTrue; + +import android.os.SystemClock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Base class for default, always-on network restrictions. + */ +abstract class AbstractDefaultRestrictionsTest extends AbstractRestrictBackgroundNetworkTestCase { + + @Before + public final void setUp() throws Exception { + super.setUp(); + + removePowerSaveModeWhitelist(TEST_APP2_PKG); + removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG); + + registerBroadcastReceiver(); + assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove()); + } + + @After + public final void tearDown() throws Exception { + super.tearDown(); + + removePowerSaveModeWhitelist(TEST_APP2_PKG); + removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG); + } + + @Test + public void testFgsNetworkAccess() throws Exception { + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS); + assertNetworkAccess(false, null); + + launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE); + } + + @Test + public void testActivityNetworkAccess() throws Exception { + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS); + assertNetworkAccess(false, null); + + launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY); + } + + @Test + public void testBackgroundNetworkAccess_inFullAllowlist() throws Exception { + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS); + assertNetworkAccess(false, null); + + addPowerSaveModeWhitelist(TEST_APP2_PKG); + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + assertNetworkAccess(true, null); + } + + @Test + public void testBackgroundNetworkAccess_inExceptIdleAllowlist() throws Exception { + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS); + assertNetworkAccess(false, null); + + addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG); + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + assertNetworkAccess(true, null); + } +} 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 29aac3c5661a2112ae03ebb5c2d1df7f417be72d..2ca88325cf718a03b18129c06c097e05e88dc05e 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 @@ -23,6 +23,7 @@ import static android.app.job.JobScheduler.RESULT_SUCCESS; import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; import static android.os.BatteryManager.BATTERY_PLUGGED_ANY; +import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY; import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand; import static com.android.cts.net.hostside.NetworkPolicyTestUtils.forceRunJob; import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager; @@ -65,11 +66,13 @@ import android.util.Log; import android.util.Pair; import androidx.annotation.Nullable; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.AmUtils; import com.android.compatibility.common.util.BatteryUtils; import com.android.compatibility.common.util.DeviceConfigStateHelper; import com.android.compatibility.common.util.ThrowingRunnable; +import com.android.modules.utils.build.SdkLevel; import org.junit.Rule; import org.junit.rules.RuleChain; @@ -90,6 +93,8 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { protected static final String TEST_PKG = "com.android.cts.net.hostside"; protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2"; + // TODO(b/321797685): Configure it via device-config once it is available. + protected static final long PROCESS_STATE_TRANSITION_DELAY_MS = TimeUnit.SECONDS.toMillis(5); private static final String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity"; private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService"; @@ -97,7 +102,6 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { private static final ComponentName TEST_JOB_COMPONENT = new ComponentName( TEST_APP2_PKG, TEST_APP2_JOB_SERVICE_CLASS); - private static final int TEST_JOB_ID = 7357437; private static final int SLEEP_TIME_SEC = 1; @@ -152,8 +156,6 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { private static final IntentFilter BATTERY_CHANGED_FILTER = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - private static final String APP_NOT_FOREGROUND_ERROR = "app_not_fg"; - protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 20_000; // 20 sec private static final long BROADCAST_TIMEOUT_MS = 5_000; @@ -181,7 +183,16 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { mUid = getUid(TEST_APP2_PKG); mMyUid = getUid(mContext.getPackageName()); mServiceClient = new MyServiceClient(mContext); - mServiceClient.bind(); + + final Bundle args = InstrumentationRegistry.getArguments(); + final int bindPriorityFlags; + if (Boolean.valueOf(args.getString(ARG_WAIVE_BIND_PRIORITY, "false"))) { + bindPriorityFlags = Context.BIND_WAIVE_PRIORITY; + } else { + bindPriorityFlags = Context.BIND_NOT_FOREGROUND; + } + mServiceClient.bind(bindPriorityFlags); + mPowerManager = mContext.getSystemService(PowerManager.class); executeShellCommand("cmd netpolicy start-watching " + mUid); // Some of the test cases assume that Data saver mode is initially disabled, which might not @@ -205,6 +216,22 @@ public abstract class AbstractRestrictBackgroundNetworkTestCase { if (null != lock && lock.isHeld()) lock.release(); } + /** + * Check if the feature blocking network for top_sleeping and lower priority proc-states is + * enabled. This is a manual check because the feature flag infrastructure may not be available + * in all the branches that will get this code. + * TODO: b/322115994 - Use @RequiresFlagsEnabled with + * Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE once the tests are moved to cts. + */ + protected boolean isNetworkBlockedForTopSleepingAndAbove() { + if (!SdkLevel.isAtLeastV()) { + return false; + } + final String output = executeShellCommand("device_config get backstage_power" + + " com.android.server.net.network_blocked_for_top_sleeping_and_above"); + return Boolean.parseBoolean(output); + } + protected int getUid(String packageName) throws Exception { return mContext.getPackageManager().getPackageUid(packageName, 0); } diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java index 4004789c6cac9f7f30451e82aaaef986c6aa1c7b..c1d576d14da30429849ce5a462aa68c468105379 100644 --- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java @@ -18,6 +18,7 @@ package com.android.cts.net.hostside; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING; import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getUiDevice; import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground; @@ -28,8 +29,13 @@ import static com.android.cts.net.hostside.Property.DOZE_MODE; import static com.android.cts.net.hostside.Property.METERED_NETWORK; import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK; +import static org.junit.Assume.assumeTrue; + +import android.os.SystemClock; import android.util.Log; +import com.android.compatibility.common.util.ThrowingRunnable; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -63,14 +69,14 @@ public class ConnOnActivityStartTest extends AbstractRestrictBackgroundNetworkTe @RequiredProperties({BATTERY_SAVER_MODE}) public void testStartActivity_batterySaver() throws Exception { setBatterySaverMode(true); - assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver"); + assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver", null); } @Test @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK}) public void testStartActivity_dataSaver() throws Exception { setRestrictBackground(true); - assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver"); + assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver", null); } @Test @@ -79,7 +85,7 @@ public class ConnOnActivityStartTest extends AbstractRestrictBackgroundNetworkTe setDozeMode(true); // TODO (235284115): We need to turn on Doze every time before starting // the activity. - assertLaunchedActivityHasNetworkAccess("testStartActivity_doze"); + assertLaunchedActivityHasNetworkAccess("testStartActivity_doze", null); } @Test @@ -89,11 +95,24 @@ public class ConnOnActivityStartTest extends AbstractRestrictBackgroundNetworkTe setAppIdle(true); // TODO (235284115): We need to put the app into app standby mode every // time before starting the activity. - assertLaunchedActivityHasNetworkAccess("testStartActivity_appStandby"); + assertLaunchedActivityHasNetworkAccess("testStartActivity_appStandby", null); + } + + @Test + public void testStartActivity_default() throws Exception { + assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove()); + assertLaunchedActivityHasNetworkAccess("testStartActivity_default", () -> { + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS); + }); } - private void assertLaunchedActivityHasNetworkAccess(String testName) throws Exception { + private void assertLaunchedActivityHasNetworkAccess(String testName, + ThrowingRunnable onBeginIteration) throws Exception { for (int i = 0; i < TEST_ITERATION_COUNT; ++i) { + if (onBeginIteration != null) { + onBeginIteration.run(); + } Log.i(TAG, testName + " start #" + i); launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY); getUiDevice().pressHome(); diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsMeteredTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f3a1026261061433e4bfe51a3c29e195eb850310 --- /dev/null +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsMeteredTest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.net.hostside; + +import static com.android.cts.net.hostside.Property.METERED_NETWORK; + +@RequiredProperties({METERED_NETWORK}) +public class DefaultRestrictionsMeteredTest extends AbstractDefaultRestrictionsTest { +} diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsNonMeteredTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5651dd05c76dc57382a94196fab38dff3e0c502a --- /dev/null +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DefaultRestrictionsNonMeteredTest.java @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.net.hostside; + +import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK; + +@RequiredProperties({NON_METERED_NETWORK}) +public class DefaultRestrictionsNonMeteredTest extends AbstractDefaultRestrictionsTest { +} diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java index 93cc91125a0c0e2b9c664e5a42980a298b7cff69..980ecd5df67cf008360bb9b1093e72c93f9a041f 100644 --- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java @@ -34,26 +34,30 @@ public class MyServiceClient { private Context mContext; private ServiceConnection mServiceConnection; - private IMyService mService; + private volatile IMyService mService; + private final ConditionVariable mServiceCondition = new ConditionVariable(); public MyServiceClient(Context context) { mContext = context; } - public void bind() { + /** + * Binds to a service in the test app to communicate state. + * @param bindPriorityFlags Flags to influence the process-state of the bound app. + */ + public void bind(int bindPriorityFlags) { if (mService != null) { throw new IllegalStateException("Already bound"); } - - final ConditionVariable cv = new ConditionVariable(); mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = IMyService.Stub.asInterface(service); - cv.open(); + mServiceCondition.open(); } @Override public void onServiceDisconnected(ComponentName name) { + mServiceCondition.close(); mService = null; } }; @@ -63,12 +67,8 @@ public class MyServiceClient { // Needs to use BIND_NOT_FOREGROUND so app2 does not run in // the same process state as app mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE - | Context.BIND_NOT_FOREGROUND); - cv.block(TIMEOUT_MS); - if (mService == null) { - throw new IllegalStateException( - "Could not bind to MyService service after " + TIMEOUT_MS + "ms"); - } + | bindPriorityFlags); + ensureServiceConnection(); } public void unbind() { @@ -77,37 +77,56 @@ public class MyServiceClient { } } + private void ensureServiceConnection() { + if (mService != null) { + return; + } + mServiceCondition.block(TIMEOUT_MS); + if (mService == null) { + throw new IllegalStateException( + "Could not bind to MyService service after " + TIMEOUT_MS + "ms"); + } + } + public void registerBroadcastReceiver() throws RemoteException { + ensureServiceConnection(); mService.registerBroadcastReceiver(); } public int getCounters(String receiverName, String action) throws RemoteException { + ensureServiceConnection(); return mService.getCounters(receiverName, action); } public String checkNetworkStatus() throws RemoteException { + ensureServiceConnection(); return mService.checkNetworkStatus(); } public String getRestrictBackgroundStatus() throws RemoteException { + ensureServiceConnection(); return mService.getRestrictBackgroundStatus(); } public void sendNotification(int notificationId, String notificationType) throws RemoteException { + ensureServiceConnection(); mService.sendNotification(notificationId, notificationType); } public void registerNetworkCallback(final NetworkRequest request, INetworkCallback cb) throws RemoteException { + ensureServiceConnection(); mService.registerNetworkCallback(request, cb); } public void unregisterNetworkCallback() throws RemoteException { + ensureServiceConnection(); mService.unregisterNetworkCallback(); } public int scheduleJob(JobInfo jobInfo) throws RemoteException { + ensureServiceConnection(); return mService.scheduleJob(jobInfo); } } 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 eb2347da2edf820818de33487ad8f503ffa71694..5552b8f8b7d75d66bf6acb72c6b2ef6f98713f05 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 @@ -17,6 +17,7 @@ package com.android.cts.net.hostside; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED; @@ -34,6 +35,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.cts.util.CtsNetUtils; +import android.os.SystemClock; import android.util.Log; import com.android.modules.utils.build.SdkLevel; @@ -43,6 +45,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import java.util.ArrayList; import java.util.Objects; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -145,12 +148,22 @@ public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCa public Network expectAvailableCallbackAndGetNetwork() { final CallbackInfo cb = nextCallback(TEST_CONNECT_TIMEOUT_MS); if (cb.state != CallbackState.AVAILABLE) { - fail("Network is not available. Instead obtained the following callback :" - + cb); + fail("Network is not available. Instead obtained the following callback :" + cb); } return cb.network; } + public void drainAndWaitForIdle() { + try { + do { + mCallbacks.drainTo(new ArrayList<>()); + } while (mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS) != null); + } catch (InterruptedException ie) { + Log.e(TAG, "Interrupted while draining callback queue", ie); + Thread.currentThread().interrupt(); + } + } + public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) { expectCallback(CallbackState.BLOCKED_STATUS, expectedNetwork, expectBlocked); } @@ -225,7 +238,7 @@ public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCa // Check that the network is metered. mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, false /* hasCapability */, NET_CAPABILITY_NOT_METERED); - mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false); + mTestNetworkCallback.drainAndWaitForIdle(); // 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 @@ -357,6 +370,58 @@ public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCa } } + @Test + public void testOnBlockedStatusChanged_default() throws Exception { + assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove()); + + try { + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + assertNetworkAccess(false, null); + assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); + + launchActivity(); + assertTopState(); + assertNetworkAccess(true, null); + mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); + assertNetworkAccessBlockedByBpf(false, mUid, true /* metered */); + + finishActivity(); + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS); + assertNetworkAccess(false, null); + mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); + assertNetworkAccessBlockedByBpf(true, mUid, true /* metered */); + + } finally { + mMeterednessConfiguration.resetNetworkMeteredness(); + } + + // Set to non-metered network + mMeterednessConfiguration.configureNetworkMeteredness(false); + mTestNetworkCallback.expectCapabilitiesCallbackEventually(mNetwork, + true /* hasCapability */, NET_CAPABILITY_NOT_METERED); + try { + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + assertNetworkAccess(false, null); + assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */); + + launchActivity(); + assertTopState(); + assertNetworkAccess(true, null); + mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, false); + assertNetworkAccessBlockedByBpf(false, mUid, false /* metered */); + + finishActivity(); + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS); + assertNetworkAccess(false, null); + mTestNetworkCallback.expectBlockedStatusCallbackEventually(mNetwork, true); + assertNetworkAccessBlockedByBpf(true, mUid, false /* metered */); + } finally { + mMeterednessConfiguration.resetNetworkMeteredness(); + } + } + // TODO: 1. test against VPN lockdown. // 2. test against multiple networks. } diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java index 7aeca77178c6f224da3c0741575d80c86a0a2bd1..968e270fdd05791617dfa32e298b02df422c19ce 100644 --- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java +++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyManagerTest.java @@ -17,6 +17,7 @@ package com.android.cts.net.hostside; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_TOP_SLEEPING; import static android.os.Process.SYSTEM_UID; import static com.android.cts.net.hostside.NetworkPolicyTestUtils.assertIsUidRestrictedOnMeteredNetworks; @@ -28,6 +29,9 @@ import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import android.os.SystemClock; import org.junit.After; import org.junit.Before; @@ -238,4 +242,33 @@ public class NetworkPolicyManagerTest extends AbstractRestrictBackgroundNetworkT assertIsUidRestrictedOnMeteredNetworks(mUid, false /* expectedResult */); } } + + @Test + public void testIsUidNetworkingBlocked_whenInBackground() throws Exception { + assumeTrue("Feature not enabled", isNetworkBlockedForTopSleepingAndAbove()); + + try { + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS); + assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */); + assertTrue(isUidNetworkingBlocked(mUid, NON_METERED)); + + launchActivity(); + assertTopState(); + assertNetworkingBlockedStatusForUid(mUid, METERED, false /* expectedResult */); + assertFalse(isUidNetworkingBlocked(mUid, NON_METERED)); + + finishActivity(); + assertProcessStateBelow(PROCESS_STATE_TOP_SLEEPING); + SystemClock.sleep(PROCESS_STATE_TRANSITION_DELAY_MS); + assertNetworkingBlockedStatusForUid(mUid, METERED, true /* expectedResult */); + assertTrue(isUidNetworkingBlocked(mUid, NON_METERED)); + + addPowerSaveModeWhitelist(TEST_APP2_PKG); + assertNetworkingBlockedStatusForUid(mUid, METERED, false /* expectedResult */); + assertFalse(isUidNetworkingBlocked(mUid, NON_METERED)); + } finally { + removePowerSaveModeWhitelist(TEST_APP2_PKG); + } + } } diff --git a/tests/cts/hostside/instrumentation_arguments/Android.bp b/tests/cts/hostside/instrumentation_arguments/Android.bp new file mode 100644 index 0000000000000000000000000000000000000000..cdede3662758ac78862cc447043523420475f4f7 --- /dev/null +++ b/tests/cts/hostside/instrumentation_arguments/Android.bp @@ -0,0 +1,22 @@ +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +filegroup { + name: "ArgumentConstants", + srcs: ["src/**/*.java"], +} diff --git a/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java b/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java new file mode 100644 index 0000000000000000000000000000000000000000..472e347c1dc56d6780a47b39b9ebffcccad44b9c --- /dev/null +++ b/tests/cts/hostside/instrumentation_arguments/src/com/android/cts/net/arguments/InstrumentationArguments.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.net.arguments; + +public interface InstrumentationArguments { + String ARG_WAIVE_BIND_PRIORITY = "waive_bind_priority"; +} diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java index 849ac7ca72d6fa893de85692d74e19ce6467416e..880e826322da0dc39bb8790d077150956e737558 100644 --- a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java +++ b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java @@ -16,6 +16,8 @@ package com.android.cts.net; +import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY; + import android.platform.test.annotations.FlakyTest; import com.android.testutils.SkipPresubmit; @@ -26,9 +28,12 @@ import com.android.tradefed.testtype.junit4.BeforeClassWithInfo; import org.junit.Test; +import java.util.Map; + @SkipPresubmit(reason = "Out of SLO flakiness") public class HostsideConnOnActivityStartTest extends HostsideNetworkTestCase { private static final String TEST_CLASS = TEST_PKG + ".ConnOnActivityStartTest"; + @BeforeClassWithInfo public static void setUpOnce(TestInformation testInfo) throws Exception { uninstallPackage(testInfo, TEST_APP2_PKG, false); @@ -60,4 +65,11 @@ public class HostsideConnOnActivityStartTest extends HostsideNetworkTestCase { public void testStartActivity_appStandby() throws Exception { runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_appStandby"); } + + // TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test. + @Test + public void testStartActivity_default() throws Exception { + runDeviceTestsWithArgs(TEST_PKG, TEST_CLASS, "testStartActivity_default", + Map.of(ARG_WAIVE_BIND_PRIORITY, "true")); + } } diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java new file mode 100644 index 0000000000000000000000000000000000000000..0d01fc12af3407ff1b7d664907e569e82591c327 --- /dev/null +++ b/tests/cts/hostside/src/com/android/cts/net/HostsideDefaultNetworkRestrictionsTests.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.net; + +import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY; + +import com.android.testutils.SkipPresubmit; +import com.android.tradefed.device.DeviceNotAvailableException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +// TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side tests. +@SkipPresubmit(reason = "Monitoring for flakiness") +public class HostsideDefaultNetworkRestrictionsTests extends HostsideNetworkTestCase { + private static final String METERED_TEST_CLASS = TEST_PKG + ".DefaultRestrictionsMeteredTest"; + private static final String NON_METERED_TEST_CLASS = + TEST_PKG + ".DefaultRestrictionsNonMeteredTest"; + + @Before + public void setUp() throws Exception { + uninstallPackage(TEST_APP2_PKG, false); + installPackage(TEST_APP2_APK); + } + + @After + public void tearDown() throws Exception { + uninstallPackage(TEST_APP2_PKG, true); + } + + private void runMeteredTest(String methodName) throws DeviceNotAvailableException { + runDeviceTestsWithArgs(TEST_PKG, METERED_TEST_CLASS, methodName, + Map.of(ARG_WAIVE_BIND_PRIORITY, "true")); + } + + private void runNonMeteredTest(String methodName) throws DeviceNotAvailableException { + runDeviceTestsWithArgs(TEST_PKG, NON_METERED_TEST_CLASS, methodName, + Map.of(ARG_WAIVE_BIND_PRIORITY, "true")); + } + + @Test + public void testMeteredNetworkAccess_defaultRestrictions_testActivityNetworkAccess() + throws Exception { + runMeteredTest("testActivityNetworkAccess"); + } + + @Test + public void testMeteredNetworkAccess_defaultRestrictions_testFgsNetworkAccess() + throws Exception { + runMeteredTest("testFgsNetworkAccess"); + } + + @Test + public void testMeteredNetworkAccess_defaultRestrictions_inFullAllowlist() throws Exception { + runMeteredTest("testBackgroundNetworkAccess_inFullAllowlist"); + } + + @Test + public void testMeteredNetworkAccess_defaultRestrictions_inExceptIdleAllowlist() + throws Exception { + runMeteredTest("testBackgroundNetworkAccess_inExceptIdleAllowlist"); + } + + @Test + public void testNonMeteredNetworkAccess_defaultRestrictions_testActivityNetworkAccess() + throws Exception { + runNonMeteredTest("testActivityNetworkAccess"); + } + + @Test + public void testNonMeteredNetworkAccess_defaultRestrictions_testFgsNetworkAccess() + throws Exception { + runNonMeteredTest("testFgsNetworkAccess"); + } + + @Test + public void testNonMeteredNetworkAccess_defaultRestrictions_inFullAllowlist() throws Exception { + runNonMeteredTest("testBackgroundNetworkAccess_inFullAllowlist"); + } + + @Test + public void testNonMeteredNetworkAccess_defaultRestrictions_inExceptIdleAllowlist() + throws Exception { + runNonMeteredTest("testBackgroundNetworkAccess_inExceptIdleAllowlist"); + } +} diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java index 04bd1ad4d2f6876da4897990bd18f02a42c2f945..361f7c7d0bed5a26dd8fee18c3d14951b0285b12 100644 --- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java +++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java @@ -15,12 +15,16 @@ */ package com.android.cts.net; +import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY; + import com.android.testutils.SkipPresubmit; import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.util.Map; + @SkipPresubmit(reason = "Out of SLO flakiness") public class HostsideNetworkCallbackTests extends HostsideNetworkTestCase { @@ -46,5 +50,12 @@ public class HostsideNetworkCallbackTests extends HostsideNetworkTestCase { runDeviceTests(TEST_PKG, TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_powerSaver"); } + + // TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test. + @Test + public void testOnBlockedStatusChanged_default() throws Exception { + runDeviceTestsWithArgs(TEST_PKG, TEST_PKG + ".NetworkCallbackTest", + "testOnBlockedStatusChanged_default", Map.of(ARG_WAIVE_BIND_PRIORITY, "true")); + } } diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java index 3ddb88b70f65682cd0001ebe6c3154601f977466..e97db58a42b9c5e3021b8c8a38502efd6d4a1f5a 100644 --- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java +++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkPolicyManagerTests.java @@ -16,10 +16,14 @@ package com.android.cts.net; +import static com.android.cts.net.arguments.InstrumentationArguments.ARG_WAIVE_BIND_PRIORITY; + import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.util.Map; + public class HostsideNetworkPolicyManagerTests extends HostsideNetworkTestCase { @Before public void setUp() throws Exception { @@ -71,4 +75,12 @@ public class HostsideNetworkPolicyManagerTests extends HostsideNetworkTestCase { runDeviceTests(TEST_PKG, TEST_PKG + ".NetworkPolicyManagerTest", "testIsUidRestrictedOnMeteredNetworks"); } + + // TODO(b/321848487): Annotate with @RequiresFlagsEnabled to mirror the device-side test. + @Test + public void testIsUidNetworkingBlocked_whenInBackground() throws Exception { + runDeviceTestsWithArgs(TEST_PKG, TEST_PKG + ".NetworkPolicyManagerTest", + "testIsUidNetworkingBlocked_whenInBackground", + Map.of(ARG_WAIVE_BIND_PRIORITY, "true")); + } } diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java index 3358fd7a6d60153595be991b151c3aa84c047686..ca95ed6e466b4836a471257df4706553bcb8e80d 100644 --- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java +++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java @@ -31,10 +31,13 @@ import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.AfterClassWithInfo; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import com.android.tradefed.testtype.junit4.BeforeClassWithInfo; +import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; import com.android.tradefed.util.RunUtil; import org.junit.runner.RunWith; +import java.util.Map; + @RunWith(DeviceJUnit4ClassRunner.class) abstract class HostsideNetworkTestCase extends BaseHostJUnit4Test { protected static final boolean DEBUG = false; @@ -146,6 +149,17 @@ abstract class HostsideNetworkTestCase extends BaseHostJUnit4Test { + packageName + ", u=" + currentUser); } + protected boolean runDeviceTestsWithArgs(String packageName, String className, + String methodName, Map<String, String> args) throws DeviceNotAvailableException { + final DeviceTestRunOptions deviceTestRunOptions = new DeviceTestRunOptions(packageName) + .setTestClassName(className) + .setTestMethodName(methodName); + for (Map.Entry<String, String> arg : args.entrySet()) { + deviceTestRunOptions.addInstrumentationArg(arg.getKey(), arg.getValue()); + } + return runDeviceTests(deviceTestRunOptions); + } + protected String runCommand(String command) throws DeviceNotAvailableException { Log.d(TAG, "Command: '" + command + "'"); final String output = getDevice().executeShellCommand(command);