From 82f99f95b7c2be53f23542f19c7789cb626d2f87 Mon Sep 17 00:00:00 2001 From: markchien Date: Tue, 18 May 2021 20:22:17 +0800 Subject: [PATCH] Test tethered callback with TetheringInterface The old callback only report interface list, new callback could provide the mapping of interface and type. Replace old callback usage in cts with new callback and check whether old callback could get the correct interface list by comparing the result between old and new callback. Bug: 162920185 Bug: 152203943 Test: atest CtsTetheringTest on S atest CtsTetheringTestLatestSdk on R atest MtsTetheringTestLatestSdk on S and R Ignore-AOSP-First: Its dependences CL is not in aosp currently. Change-Id: I2a0b8c43fb340c3eaed7f0f90464199222a24280 --- .../android/net/EthernetTetheringTest.java | 20 ++- .../tethering/mts/TetheringModuleTest.java | 16 +- .../net/cts/util/CtsTetheringUtils.java | 151 +++++++++++++----- .../tethering/cts/TetheringManagerTest.java | 23 +-- 4 files changed, 149 insertions(+), 61 deletions(-) diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java index de94cba5f5..f1ddc6dffa 100644 --- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java +++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java @@ -80,6 +80,7 @@ import java.nio.ByteBuffer; import java.util.Collection; import java.util.List; import java.util.Random; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -349,7 +350,7 @@ public class EthernetTetheringTest { private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1); private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1); private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1); - private final String mIface; + private final TetheringInterface mIface; private volatile boolean mInterfaceWasTethered = false; private volatile boolean mInterfaceWasLocalOnly = false; @@ -358,20 +359,24 @@ public class EthernetTetheringTest { MyTetheringEventCallback(TetheringManager tm, String iface) { mTm = tm; - mIface = iface; + mIface = new TetheringInterface(TETHERING_ETHERNET, iface); } public void unregister() { mTm.unregisterTetheringEventCallback(this); mUnregistered = true; } - @Override public void onTetheredInterfacesChanged(List interfaces) { + fail("Should only call callback that takes a Set"); + } + + @Override + public void onTetheredInterfacesChanged(Set interfaces) { // Ignore stale callbacks registered by previous test cases. if (mUnregistered) return; - if (!mInterfaceWasTethered && (mIface == null || interfaces.contains(mIface))) { + if (!mInterfaceWasTethered && interfaces.contains(mIface)) { // This interface is being tethered for the first time. Log.d(TAG, "Tethering started: " + interfaces); mInterfaceWasTethered = true; @@ -384,10 +389,15 @@ public class EthernetTetheringTest { @Override public void onLocalOnlyInterfacesChanged(List interfaces) { + fail("Should only call callback that takes a Set"); + } + + @Override + public void onLocalOnlyInterfacesChanged(Set interfaces) { // Ignore stale callbacks registered by previous test cases. if (mUnregistered) return; - if (!mInterfaceWasLocalOnly && (mIface == null || interfaces.contains(mIface))) { + if (!mInterfaceWasLocalOnly && interfaces.contains(mIface)) { // This interface is being put into local-only mode for the first time. Log.d(TAG, "Local-only started: " + interfaces); mInterfaceWasLocalOnly = true; diff --git a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java index 7ffe37ad64..e0fcbfad28 100644 --- a/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java +++ b/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java @@ -20,12 +20,12 @@ import static android.Manifest.permission.NETWORK_SETTINGS; import static android.Manifest.permission.READ_DEVICE_CONFIG; import static android.Manifest.permission.TETHER_PRIVILEGED; import static android.Manifest.permission.WRITE_SETTINGS; +import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported; import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -35,6 +35,7 @@ import android.app.UiAutomation; import android.content.Context; import android.net.IpPrefix; import android.net.LinkAddress; +import android.net.TetheringInterface; import android.net.TetheringManager; import android.net.cts.util.CtsTetheringUtils; import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback; @@ -102,12 +103,13 @@ public class TetheringModuleTest { try { tetherEventCallback.assumeTetheringSupported(); assumeTrue(isWifiTetheringSupported(tetherEventCallback)); + tetherEventCallback.expectNoTetheringActive(); - mCtsTetheringUtils.startWifiTethering(tetherEventCallback); + final TetheringInterface tetheredIface = + mCtsTetheringUtils.startWifiTethering(tetherEventCallback); - final List tetheredIfaces = tetherEventCallback.getTetheredInterfaces(); - assertEquals(1, tetheredIfaces.size()); - final String wifiTetheringIface = tetheredIfaces.get(0); + assertNotNull(tetheredIface); + final String wifiTetheringIface = tetheredIface.getInterface(); NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface); // Tethering downstream only have one ipv4 address. @@ -120,11 +122,11 @@ public class TetheringModuleTest { tnt = setUpTestNetwork( new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength())); - tetherEventCallback.expectTetheredInterfacesChanged(null); + tetherEventCallback.expectNoTetheringActive(); final List wifiRegexs = tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs(); - tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs); + tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI); nif = NetworkInterface.getByName(wifiTetheringIface); final LinkAddress newHotspotAddr = getFirstIpv4Address(nif); assertNotNull(newHotspotAddr); diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java index c95dc28dd1..1bdd5337b3 100644 --- a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java +++ b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java @@ -22,7 +22,7 @@ import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED; import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED; import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -35,6 +35,7 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.Network; import android.net.TetheredClient; +import android.net.TetheringInterface; import android.net.TetheringManager; import android.net.TetheringManager.TetheringEventCallback; import android.net.TetheringManager.TetheringInterfaceRegexps; @@ -51,6 +52,7 @@ import com.android.net.module.util.ArrayTrackRecord; import java.util.Collection; import java.util.List; +import java.util.Set; public final class CtsTetheringUtils { private TetheringManager mTm; @@ -116,23 +118,38 @@ public final class CtsTetheringUtils { } } - public static boolean isIfaceMatch(final List ifaceRegexs, final List ifaces) { - return isIfaceMatch(ifaceRegexs.toArray(new String[0]), ifaces); - } - - public static boolean isIfaceMatch(final String[] ifaceRegexs, final List ifaces) { + private static boolean isRegexMatch(final String[] ifaceRegexs, String iface) { if (ifaceRegexs == null) fail("ifaceRegexs should not be null"); + for (String regex : ifaceRegexs) { + if (iface.matches(regex)) return true; + } + + return false; + } + + public static boolean isAnyIfaceMatch(final String[] ifaceRegexs, final List ifaces) { if (ifaces == null) return false; for (String s : ifaces) { - for (String regex : ifaceRegexs) { - if (s.matches(regex)) { - return true; - } + if (isRegexMatch(ifaceRegexs, s)) return true; + } + + return false; + } + + private static TetheringInterface getFirstMatchingTetheringInterface(final List regexs, + final int type, final Set ifaces) { + if (ifaces == null || regexs == null) return null; + + final String[] regexArray = regexs.toArray(new String[0]); + for (TetheringInterface iface : ifaces) { + if (isRegexMatch(regexArray, iface.getInterface()) && type == iface.getType()) { + return iface; } } - return false; + + return null; } // Must poll the callback before looking at the member. @@ -171,6 +188,8 @@ public final class CtsTetheringUtils { private TetheringInterfaceRegexps mTetherableRegex; private List mTetherableIfaces; private List mTetheredIfaces; + private String mErrorIface; + private int mErrorCode; @Override public void onTetheringSupported(boolean supported) { @@ -191,17 +210,41 @@ public final class CtsTetheringUtils { @Override public void onTetherableInterfacesChanged(List interfaces) { mTetherableIfaces = interfaces; + } + // Call the interface default implementation, which will call + // onTetherableInterfacesChanged(List). This ensures that the default implementation + // of the new callback method calls the old callback method and avoids the need to convert + // Set to List in this code. + @Override + public void onTetherableInterfacesChanged(Set interfaces) { + TetheringEventCallback.super.onTetherableInterfacesChanged(interfaces); + assertHasAllTetheringInterfaces(interfaces, mTetherableIfaces); mHistory.add(new CallbackValue(CallbackType.ON_TETHERABLE_IFACES, interfaces, 0)); } @Override public void onTetheredInterfacesChanged(List interfaces) { mTetheredIfaces = interfaces; + } + + @Override + public void onTetheredInterfacesChanged(Set interfaces) { + TetheringEventCallback.super.onTetheredInterfacesChanged(interfaces); + assertHasAllTetheringInterfaces(interfaces, mTetheredIfaces); mHistory.add(new CallbackValue(CallbackType.ON_TETHERED_IFACES, interfaces, 0)); } @Override public void onError(String ifName, int error) { + mErrorIface = ifName; + mErrorCode = error; + } + + @Override + public void onError(TetheringInterface ifName, int error) { + TetheringEventCallback.super.onError(ifName, error); + assertEquals(ifName.getInterface(), mErrorIface); + assertEquals(error, mErrorCode); mHistory.add(new CallbackValue(CallbackType.ON_ERROR, ifName, error)); } @@ -215,30 +258,66 @@ public final class CtsTetheringUtils { mHistory.add(new CallbackValue(CallbackType.ON_OFFLOAD_STATUS, status, 0)); } - public void expectTetherableInterfacesChanged(@NonNull List regexs) { + private void assertHasAllTetheringInterfaces(Set tetheringIfaces, + List ifaces) { + // This does not check that the interfaces are the same. This checks that the + // List has all the interface names contained by the Set. + assertEquals(tetheringIfaces.size(), ifaces.size()); + for (TetheringInterface tether : tetheringIfaces) { + assertTrue("iface " + tether.getInterface() + + " seen by new callback but not old callback", + ifaces.contains(tether.getInterface())); + } + } + + public void expectTetherableInterfacesChanged(@NonNull final List regexs, + final int type) { assertNotNull("No expected tetherable ifaces callback", mCurrent.poll(TIMEOUT_MS, (cv) -> { if (cv.callbackType != CallbackType.ON_TETHERABLE_IFACES) return false; - final List interfaces = (List) cv.callbackParam; - return isIfaceMatch(regexs, interfaces); + final Set interfaces = + (Set) cv.callbackParam; + return getFirstMatchingTetheringInterface(regexs, type, interfaces) != null; })); } - public void expectTetheredInterfacesChanged(@NonNull List regexs) { - assertNotNull("No expected tethered ifaces callback", mCurrent.poll(TIMEOUT_MS, - (cv) -> { - if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) return false; + public void expectNoTetheringActive() { + assertNotNull("At least one tethering type unexpectedly active", + mCurrent.poll(TIMEOUT_MS, (cv) -> { + if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) return false; - final List interfaces = (List) cv.callbackParam; + return ((Set) cv.callbackParam).isEmpty(); + })); + } - // Null regexs means no active tethering. - if (regexs == null) return interfaces.isEmpty(); + public TetheringInterface expectTetheredInterfacesChanged( + @NonNull final List regexs, final int type) { + while (true) { + final CallbackValue cv = mCurrent.poll(TIMEOUT_MS, c -> true); + if (cv == null) { + fail("No expected tethered ifaces callback, expected type: " + type); + } - return isIfaceMatch(regexs, interfaces); - })); + if (cv.callbackType != CallbackType.ON_TETHERED_IFACES) continue; + + final Set interfaces = + (Set) cv.callbackParam; + + final TetheringInterface iface = + getFirstMatchingTetheringInterface(regexs, type, interfaces); + + if (iface != null) return iface; + } } public void expectCallbackStarted() { + // This method uses its own readhead because it just check whether last tethering status + // is updated after TetheringEventCallback get registered but do not check content + // of received callbacks. Using shared readhead (mCurrent) only when the callbacks the + // method polled is also not necessary for other methods which using shared readhead. + // All of methods using mCurrent is order mattered. + final ArrayTrackRecord.ReadHead history = + mHistory.newReadHead(); int receivedBitMap = 0; // The each bit represent a type from CallbackType.ON_*. // Expect all of callbacks except for ON_ERROR. @@ -246,7 +325,7 @@ public final class CtsTetheringUtils { // Receive ON_ERROR on started callback is not matter. It just means tethering is // failed last time, should able to continue the test this time. while ((receivedBitMap & expectedBitMap) != expectedBitMap) { - final CallbackValue cv = mCurrent.poll(TIMEOUT_MS, c -> true); + final CallbackValue cv = history.poll(TIMEOUT_MS, c -> true); if (cv == null) { fail("No expected callbacks, " + "expected bitmap: " + expectedBitMap + ", actual: " + receivedBitMap); @@ -269,14 +348,14 @@ public final class CtsTetheringUtils { })); } - public void expectErrorOrTethered(final String iface) { + public void expectErrorOrTethered(final TetheringInterface iface) { assertNotNull("No expected callback", mCurrent.poll(TIMEOUT_MS, (cv) -> { if (cv.callbackType == CallbackType.ON_ERROR - && iface.equals((String) cv.callbackParam)) { + && iface.equals((TetheringInterface) cv.callbackParam)) { return true; } if (cv.callbackType == CallbackType.ON_TETHERED_IFACES - && ((List) cv.callbackParam).contains(iface)) { + && ((Set) cv.callbackParam).contains(iface)) { return true; } @@ -328,14 +407,6 @@ public final class CtsTetheringUtils { public TetheringInterfaceRegexps getTetheringInterfaceRegexps() { return mTetherableRegex; } - - public List getTetherableInterfaces() { - return mTetherableIfaces; - } - - public List getTetheredInterfaces() { - return mTetheredIfaces; - } } private static void waitForWifiEnabled(final Context ctx) throws Exception { @@ -386,10 +457,9 @@ public final class CtsTetheringUtils { return !getWifiTetherableInterfaceRegexps(callback).isEmpty(); } - public void startWifiTethering(final TestTetheringEventCallback callback) + public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback) throws InterruptedException { final List wifiRegexs = getWifiTetherableInterfaceRegexps(callback); - assertFalse(isIfaceMatch(wifiRegexs, callback.getTetheredInterfaces())); final StartTetheringCallback startTetheringCallback = new StartTetheringCallback(); final TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI) @@ -397,11 +467,14 @@ public final class CtsTetheringUtils { mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback); startTetheringCallback.verifyTetheringStarted(); - callback.expectTetheredInterfacesChanged(wifiRegexs); + final TetheringInterface iface = + callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI); callback.expectOneOfOffloadStatusChanged( TETHER_HARDWARE_OFFLOAD_STARTED, TETHER_HARDWARE_OFFLOAD_FAILED); + + return iface; } private static class StopSoftApCallback implements SoftApCallback { @@ -441,7 +514,7 @@ public final class CtsTetheringUtils { public void stopWifiTethering(final TestTetheringEventCallback callback) { mTm.stopTethering(TETHERING_WIFI); expectSoftApDisabled(); - callback.expectTetheredInterfacesChanged(null); + callback.expectNoTetheringActive(); callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); } } diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java index 71a81ff621..0a5e506c07 100644 --- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java +++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java @@ -26,8 +26,7 @@ import static android.net.TetheringManager.TETHERING_WIFI_P2P; import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; -import static android.net.cts.util.CtsTetheringUtils.isIfaceMatch; -import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported; +import static android.net.cts.util.CtsTetheringUtils.isAnyIfaceMatch; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -48,6 +47,7 @@ import android.net.ConnectivityManager; import android.net.LinkAddress; import android.net.Network; import android.net.NetworkCapabilities; +import android.net.TetheringInterface; import android.net.TetheringManager; import android.net.TetheringManager.OnTetheringEntitlementResultListener; import android.net.TetheringManager.TetheringInterfaceRegexps; @@ -190,13 +190,13 @@ public class TetheringManagerTest { } private boolean isIfaceActive(final String[] ifaceRegexs, final TetherState state) { - return isIfaceMatch(ifaceRegexs, state.mActive); + return isAnyIfaceMatch(ifaceRegexs, state.mActive); } private void assertNoErroredIfaces(final TetherState state, final String[] ifaceRegexs) { if (state == null || state.mErrored == null) return; - if (isIfaceMatch(ifaceRegexs, state.mErrored)) { + if (isAnyIfaceMatch(ifaceRegexs, state.mErrored)) { fail("Found failed tethering interfaces: " + Arrays.toString(state.mErrored.toArray())); } } @@ -256,12 +256,13 @@ public class TetheringManagerTest { try { tetherEventCallback.assumeWifiTetheringSupported(mContext); + tetherEventCallback.expectNoTetheringActive(); - mCtsTetheringUtils.startWifiTethering(tetherEventCallback); + final TetheringInterface tetheredIface = + mCtsTetheringUtils.startWifiTethering(tetherEventCallback); - final List tetheredIfaces = tetherEventCallback.getTetheredInterfaces(); - assertEquals(1, tetheredIfaces.size()); - final String wifiTetheringIface = tetheredIfaces.get(0); + assertNotNull(tetheredIface); + final String wifiTetheringIface = tetheredIface.getInterface(); mCtsTetheringUtils.stopWifiTethering(tetherEventCallback); @@ -272,7 +273,8 @@ public class TetheringManagerTest { if (ret == TETHER_ERROR_NO_ERROR) { // If calling #tether successful, there is a callback to tell the result of // tethering setup. - tetherEventCallback.expectErrorOrTethered(wifiTetheringIface); + tetherEventCallback.expectErrorOrTethered( + new TetheringInterface(TETHERING_WIFI, wifiTetheringIface)); } } finally { mTM.untether(wifiTetheringIface); @@ -319,7 +321,7 @@ public class TetheringManagerTest { mCtsTetheringUtils.startWifiTethering(tetherEventCallback); mTM.stopAllTethering(); - tetherEventCallback.expectTetheredInterfacesChanged(null); + tetherEventCallback.expectNoTetheringActive(); } finally { mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback); } @@ -417,6 +419,7 @@ public class TetheringManagerTest { try { tetherEventCallback.assumeWifiTetheringSupported(mContext); + tetherEventCallback.expectNoTetheringActive(); previousWifiEnabledState = mWm.isWifiEnabled(); if (previousWifiEnabledState) {