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); }