EthernetTetheringTest: toggle wifi while test network is not selected

Used to avoid flaky test because upstream changed events
order can't be guaranteed. Once tethering choose non-test
upstream {wifi, ..}, test network wont't be chosen.

Fix flaky tests which are using initTetheringTester():
testIcmpv6Echo
testTetherClatUdp
testTetherUdpV4_VerifyBpf
testTetherUdpV4
testTetherUdpV4Dns
testTetherUdpV6

Moreover, add permission ACCESS_WIFI_STATE to AndroidManifest.xml
to avoid nested runAsShell while using CtsNetUtils to access WIFI.

STACKTRACE:
java.lang.IllegalStateException: adoptShellPermissionIdentity calls must not be nested
at com.android.testutils.TestPermissionUtil.runAsShell(TestPermissionUtil.kt:49)
at com.android.testutils.TestPermissionUtil.runAsShell(TestPermissionUtil.kt:70)
at com.android.testutils.TestPermissionUtil.runAsShell$default(TestPermissionUtil.kt:65)
at com.android.testutils.TestPermissionUtil.runAsShell(Unknown Source:16)
at android.net.cts.util.CtsNetUtils.disconnectFromWifi(CtsNetUtils.java:293)
at android.net.cts.util.CtsNetUtils.disconnectFromWifi(CtsNetUtils.java:255)
at android.net.cts.util.CtsNetUtils.toggleWifi(CtsNetUtils.java:160)

Bug: 243314243
Test: atest EthernetTetheringTest
Change-Id: I814899ae71f5c302d490a0dd81d7210ac138faa5
This commit is contained in:
Hungming Chen 2022-08-29 21:43:55 +08:00 • committed by Nucca Chen
commit d09bb0773a
3 changed files with 74 additions and 5 deletions

View file

@ -28,6 +28,7 @@ java_defaults {
static_libs: [
"NetworkStackApiStableLib",
"androidx.test.rules",
"cts-net-utils",
"mockito-target-extended-minus-junit4",
"net-tests-utils",
"net-utils-device-common-bpf",

View file

@ -17,6 +17,7 @@
package="com.android.networkstack.tethering.tests.integration">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<!-- The test need CHANGE_NETWORK_STATE permission to use requestNetwork API to setup test
network. Since R shell application don't have such permission, grant permission to the test
here. TODO: Remove CHANGE_NETWORK_STATE permission here and use adopt shell perssion to

View file

@ -20,6 +20,7 @@ import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
@ -54,12 +55,14 @@ import static org.junit.Assume.assumeTrue;
import android.app.UiAutomation;
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.EthernetManager.TetheredInterfaceCallback;
import android.net.EthernetManager.TetheredInterfaceRequest;
import android.net.TetheringManager.StartTetheringCallback;
import android.net.TetheringManager.TetheringEventCallback;
import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringTester.TetheredDevice;
import android.net.cts.util.CtsNetUtils;;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
@ -234,6 +237,8 @@ public class EthernetTetheringTest {
private final Context mContext = InstrumentationRegistry.getContext();
private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class);
private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class);
private final PackageManager mPackageManager = mContext.getPackageManager();
private final CtsNetUtils mCtsNetUtils = new CtsNetUtils(mContext);
private TestNetworkInterface mDownstreamIface;
private HandlerThread mHandlerThread;
@ -690,10 +695,17 @@ public class EthernetTetheringTest {
}
}
public Network awaitUpstreamChanged() throws Exception {
public Network awaitUpstreamChanged(boolean throwTimeoutException) throws Exception {
if (!mUpstreamLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
fail("Did not receive upstream " + (mAcceptAnyUpstream ? "any" : mExpectedUpstream)
+ " callback after " + TIMEOUT_MS + "ms");
final String errorMessage = "Did not receive upstream "
+ (mAcceptAnyUpstream ? "any" : mExpectedUpstream)
+ " callback after " + TIMEOUT_MS + "ms";
if (throwTimeoutException) {
throw new TimeoutException(errorMessage);
} else {
fail(errorMessage);
}
}
return mUpstream;
}
@ -1232,6 +1244,52 @@ public class EthernetTetheringTest {
}
}
// TODO: remove triggering upstream reselection once test network can replace selected upstream
// network in Tethering module.
private void maybeRetryTestedUpstreamChanged(final Network expectedUpstream,
final TimeoutException fallbackException) throws Exception {
// Fall back original exception because no way to reselect if there is no WIFI feature.
assertTrue(fallbackException.toString(), mPackageManager.hasSystemFeature(FEATURE_WIFI));
// Try to toggle wifi network, if any, to reselect upstream network via default network
// switching. Because test network has higher priority than internet network, this can
// help selecting test network to be upstream network for testing. This tries to avoid
// the flaky upstream selection under multinetwork environment. Internet and test network
// upstream changed event order is not guaranteed. Once tethering selects non-test
// upstream {wifi, ..}, test network won't be selected anymore. If too many test cases
// trigger the reselection, the total test time may over test suite 1 minmute timeout.
// Probably need to disable/restore all internet networks in a common place of test
// process. Currently, EthernetTetheringTest is part of CTS test which needs wifi network
// connection if device has wifi feature. CtsNetUtils#toggleWifi() checks wifi connection
// during the toggling process.
// See Tethering#chooseUpstreamType, CtsNetUtils#toggleWifi.
// TODO: toggle cellular network if the device has no WIFI feature.
Log.d(TAG, "Toggle WIFI to retry upstream selection");
mCtsNetUtils.toggleWifi();
// Wait for expected upstream.
final CompletableFuture<Network> future = new CompletableFuture<>();
final TetheringEventCallback callback = new TetheringEventCallback() {
@Override
public void onUpstreamChanged(Network network) {
Log.d(TAG, "Got upstream changed: " + network);
if (Objects.equals(expectedUpstream, network)) {
future.complete(network);
}
}
};
try {
mTm.registerTetheringEventCallback(mHandler::post, callback);
assertEquals("onUpstreamChanged for unexpected network", expectedUpstream,
future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (TimeoutException e) {
throw new AssertionError("Did not receive upstream " + expectedUpstream
+ " callback after " + TIMEOUT_MS + "ms");
} finally {
mTm.unregisterTetheringEventCallback(callback);
}
}
private TetheringTester initTetheringTester(List<LinkAddress> upstreamAddresses,
List<InetAddress> upstreamDnses) throws Exception {
assumeFalse(isInterfaceForTetheringAvailable());
@ -1250,8 +1308,17 @@ public class EthernetTetheringTest {
mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName(),
mUpstreamTracker.getNetwork());
assertEquals("onUpstreamChanged for unexpected network", mUpstreamTracker.getNetwork(),
mTetheringEventCallback.awaitUpstreamChanged());
try {
assertEquals("onUpstreamChanged for test network", mUpstreamTracker.getNetwork(),
mTetheringEventCallback.awaitUpstreamChanged(
true /* throwTimeoutException */));
} catch (TimeoutException e) {
// Due to race condition inside tethering module, test network may not be selected as
// tethering upstream. Force tethering retry upstream if possible. If it is not
// possible to retry, fail the test with the original timeout exception.
maybeRetryTestedUpstreamChanged(mUpstreamTracker.getNetwork(), e);
}
mDownstreamReader = makePacketReader(mDownstreamIface);
mUpstreamReader = makePacketReader(mUpstreamTracker.getTestIface());