From 24d8b8391e85dc14c5f5635da90b503f8496b4af Mon Sep 17 00:00:00 2001 From: markchien Date: Thu, 11 Jun 2020 19:20:38 +0800 Subject: [PATCH] Fix CtsTetheringTest on devices without permanent softAp interfaces. Normally stop wifi tethering flow would be: Tethering#stopTethering -> WifiManager#stopSoftAp -> softAp disabled, then have WIIF_AP_STATE_CHANGED intent -> stop IpServer and broadcast TETHER_STATE_CHANGED intent. SoftAp is disabled before tethering stop. Because tethering would shutdown the corresponding IpServer if it observed the interface is removed. For those devices that softAp interface would be removed when stop tethering, the flow may be Tethering#stopTethering -> WifiManager#stopSoftAp -> softAp disabing, softAp interface is removed -> tethering trigger stop IpServer and broadcast TETHER_STATE_CHANGED intent -> -> softAp disabled, then wifi broadcast WIIF_AP_STATE_CHANGED intent. In this case, tethering is stopped ready before softap is disabled. For this case, CtsTeteringTest would have race between two test cases. If two case need to start wifi tethering for testing and stop wifi tethering after finish testing. The second test may suffer from startTethering fail problem due to softAP is not disabled yet. E WifiService: Tethering is already active. Bug: 157806780 Test: atest CtsTetheringTest Change-Id: I0ba6bc9dcbf7829dcad5561c707d5f5c5540f10b --- .../tethering/cts/TetheringManagerTest.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java index f7160dd502..5e2f62787c 100644 --- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java +++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java @@ -57,8 +57,11 @@ import android.net.TetheringManager.TetheringInterfaceRegexps; import android.net.TetheringManager.TetheringRequest; import android.net.cts.util.CtsNetUtils; import android.net.cts.util.CtsNetUtils.TestNetworkCallback; +import android.net.wifi.WifiClient; import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.SoftApCallback; import android.os.Bundle; +import android.os.ConditionVariable; import android.os.PersistableBundle; import android.os.ResultReceiver; import android.telephony.CarrierConfigManager; @@ -135,6 +138,40 @@ public class TetheringManagerTest { dropShellPermissionIdentity(); } + private static class StopSoftApCallback implements SoftApCallback { + private final ConditionVariable mWaiting = new ConditionVariable(); + @Override + public void onStateChanged(int state, int failureReason) { + if (state == WifiManager.WIFI_AP_STATE_DISABLED) mWaiting.open(); + } + + @Override + public void onConnectedClientsChanged(List clients) { } + + public void waitForSoftApStopped() { + if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) { + fail("stopSoftAp Timeout"); + } + } + } + + // Wait for softAp to be disabled. This is necessary on devices where stopping softAp + // deletes the interface. On these devices, tethering immediately stops when the softAp + // interface is removed, but softAp is not yet fully disabled. Wait for softAp to be + // fully disabled, because otherwise the next test might fail because it attempts to + // start softAp before it's fully stopped. + private void expectSoftApDisabled() { + final StopSoftApCallback callback = new StopSoftApCallback(); + try { + mWm.registerSoftApCallback(c -> c.run(), callback); + // registerSoftApCallback will immediately call the callback with the current state, so + // this callback will fire even if softAp is already disabled. + callback.waitForSoftApStopped(); + } finally { + mWm.unregisterSoftApCallback(callback); + } + } + private class TetherChangeReceiver extends BroadcastReceiver { private class TetherState { final ArrayList mAvailable; @@ -294,6 +331,7 @@ public class TetheringManagerTest { mTetherChangeReceiver.expectTethering(true /* active */, wifiRegexs); mTM.stopTethering(TETHERING_WIFI); + expectSoftApDisabled(); mTetherChangeReceiver.expectTethering(false /* active */, wifiRegexs); } @@ -544,6 +582,7 @@ public class TetheringManagerTest { private void stopWifiTethering(final TestTetheringEventCallback callback) { mTM.stopTethering(TETHERING_WIFI); + expectSoftApDisabled(); callback.expectTetheredInterfacesChanged(null); callback.expectOneOfOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); }