From a6b3b70278fbb9eb76a154d77088e1e51fea1fc6 Mon Sep 17 00:00:00 2001 From: markchien Date: Sun, 19 Apr 2020 22:50:35 +0800 Subject: [PATCH] Test legacy tether/untether API and onError callback Test legacy tether/untether API behavior. tether() rely on downstream setup ready for tethering. Currently the only user is bluetooth tethering. For other tetherings, there is no guaranteed that calling tether() can always switch given interface to tethered. Tethering may callback with onError depend on the interface status. Caller should use startTethering/stopTethering API instead of these legacy APIs. This change also change the precondition verification of startTethering. If tethering interface got error last time, such interface would not be reported as tetherable. Bug: 150632712 Test: atest CtsTetheringTest Change-Id: Ifb3a0618208ffd0ff224c60f377036bc22ba0565 Merged-In: Ifb3a0618208ffd0ff224c60f377036bc22ba0565 --- .../tethering/cts/TetheringManagerTest.java | 116 ++++++++---------- 1 file changed, 54 insertions(+), 62 deletions(-) diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java index 60f9400363..11ffe2c456 100644 --- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java +++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java @@ -21,6 +21,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.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; 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; @@ -31,6 +32,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import android.app.UiAutomation; import android.content.BroadcastReceiver; @@ -64,10 +66,8 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) @@ -140,28 +140,24 @@ public class TetheringManagerTest { public final LinkedBlockingQueue mResult = new LinkedBlockingQueue<>(); - // This method expects either an event where one of the interfaces is active, or events - // where the interfaces are available followed by one event where one of the interfaces is - // active. Here is a typical example for wifi tethering: - // AVAILABLE(wlan0) -> AVAILABLE(wlan1) -> ACTIVATE(wlan1). - public void expectActiveTethering(String[] ifaceRegexs) { - TetherState state = null; + // Expects that tethering reaches the desired state. + // - If active is true, expects that tethering is enabled on at least one interface + // matching ifaceRegexs. + // - If active is false, expects that tethering is disabled on all the interfaces matching + // ifaceRegexs. + // Fails if any interface matching ifaceRegexs becomes errored. + public void expectTethering(final boolean active, final String[] ifaceRegexs) { while (true) { - state = pollAndAssertNoError(DEFAULT_TIMEOUT_MS); - if (state == null) fail("Do not receive active state change broadcast"); + final TetherState state = pollAndAssertNoError(DEFAULT_TIMEOUT_MS, ifaceRegexs); + assertNotNull("Did not receive expected state change, active: " + active, state); - if (isIfaceActive(ifaceRegexs, state)) return; - - if (!isIfaceAvailable(ifaceRegexs, state)) break; + if (isIfaceActive(ifaceRegexs, state) == active) return; } - - fail("Tethering is not actived, available ifaces: " + state.mAvailable.toString() - + ", active ifaces: " + state.mActive.toString()); } - private TetherState pollAndAssertNoError(final int timeout) { + private TetherState pollAndAssertNoError(final int timeout, final String[] ifaceRegexs) { final TetherState state = pollTetherState(timeout); - assertNoErroredIfaces(state); + assertNoErroredIfaces(state, ifaceRegexs); return state; } @@ -178,43 +174,13 @@ public class TetheringManagerTest { return isIfaceMatch(ifaceRegexs, state.mActive); } - private boolean isIfaceAvailable(final String[] ifaceRegexs, final TetherState state) { - return isIfaceMatch(ifaceRegexs, state.mAvailable); - } - - // This method requires a broadcast to have been recorded iff the timeout is non-zero. - public void expectNoActiveTethering(final int timeout) { - final TetherState state = pollAndAssertNoError(timeout); - - if (state == null) { - if (timeout != 0) { - fail("Do not receive tethering state change broadcast"); - } - return; - } - - assertNoActiveIfaces(state); - - for (final TetherState ts : mResult) { - assertNoErroredIfaces(ts); - - assertNoActiveIfaces(ts); - } - } - - private void assertNoErroredIfaces(final TetherState state) { + private void assertNoErroredIfaces(final TetherState state, final String[] ifaceRegexs) { if (state == null || state.mErrored == null) return; - if (state.mErrored.size() > 0) { + if (isIfaceMatch(ifaceRegexs, state.mErrored)) { fail("Found failed tethering interfaces: " + Arrays.toString(state.mErrored.toArray())); } } - - private void assertNoActiveIfaces(final TetherState state) { - if (state.mActive != null && state.mActive.size() > 0) { - fail("Found active tethering interface: " + Arrays.toString(state.mActive.toArray())); - } - } } private static class StartTetheringCallback implements TetheringManager.StartTetheringCallback { @@ -295,17 +261,18 @@ public class TetheringManagerTest { final String[] wifiRegexs = mTM.getTetherableWifiRegexs(); if (wifiRegexs.length == 0) return; - mTetherChangeReceiver.expectNoActiveTethering(0 /** timeout */); + final String[] tetheredIfaces = mTM.getTetheredIfaces(); + assertTrue(tetheredIfaces.length == 0); final StartTetheringCallback startTetheringCallback = new StartTetheringCallback(); mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(), c -> c.run() /* executor */, startTetheringCallback); startTetheringCallback.verifyTetheringStarted(); - mTetherChangeReceiver.expectActiveTethering(wifiRegexs); + mTetherChangeReceiver.expectTethering(true /* active */, wifiRegexs); mTM.stopTethering(TETHERING_WIFI); - mTetherChangeReceiver.expectNoActiveTethering(DEFAULT_TIMEOUT_MS); + mTetherChangeReceiver.expectTethering(false /* active */, wifiRegexs); } @Test @@ -459,6 +426,21 @@ public class TetheringManagerTest { })); } + public void expectErrorOrTethered(final String iface) { + assertNotNull("No expected callback", mHistory.poll(TIMEOUT_MS, (cv) -> { + if (cv.callbackType == CallbackType.ON_ERROR + && iface.equals((String) cv.callbackParam)) { + return true; + } + if (cv.callbackType == CallbackType.ON_TETHERED_IFACES + && ((List) cv.callbackParam).contains(iface)) { + return true; + } + + return false; + })); + } + public TetheringInterfaceRegexps getTetheringInterfaceRegexps() { return mTetherableRegex; } @@ -497,20 +479,13 @@ public class TetheringManagerTest { private void startWifiTethering(final TestTetheringEventCallback callback) throws InterruptedException { final List wifiRegexs = getWifiTetherableInterfaceRegexps(callback); - final boolean isIfaceAvailWhenNoTethering = - isIfaceMatch(wifiRegexs, callback.getTetherableInterfaces()); + assertFalse(isIfaceMatch(wifiRegexs, callback.getTetheredInterfaces())); final StartTetheringCallback startTetheringCallback = new StartTetheringCallback(); mTM.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(), c -> c.run() /* executor */, startTetheringCallback); startTetheringCallback.verifyTetheringStarted(); - // If interface is already available before starting tethering, the available callback may - // not be sent after tethering enabled. - if (!isIfaceAvailWhenNoTethering) { - callback.expectTetherableInterfacesChanged(wifiRegexs); - } - callback.expectTetheredInterfacesChanged(wifiRegexs); callback.expectOneOfOffloadStatusChanged( @@ -534,9 +509,26 @@ public class TetheringManagerTest { startWifiTethering(tetherEventCallback); + final List tetheredIfaces = tetherEventCallback.getTetheredInterfaces(); + assertEquals(1, tetheredIfaces.size()); + final String wifiTetheringIface = tetheredIfaces.get(0); + stopWifiTethering(tetherEventCallback); - unregisterTetheringEventCallback(tetherEventCallback); + try { + final int ret = mTM.tether(wifiTetheringIface); + + // There is no guarantee that the wifi interface will be available after disabling + // the hotspot, so don't fail the test if the call to tether() fails. + assumeTrue(ret == TETHER_ERROR_NO_ERROR); + + // If calling #tether successful, there is a callback to tell the result of tethering + // setup. + tetherEventCallback.expectErrorOrTethered(wifiTetheringIface); + } finally { + mTM.untether(wifiTetheringIface); + unregisterTetheringEventCallback(tetherEventCallback); + } } @Test