summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJimmy Chen <jimmycmchen@google.com>2020-01-10 02:48:44 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-01-10 02:48:44 +0000
commitf9d00427e5d0a57a3cd1268413cba288028f4916 (patch)
tree80b9bbead0aec247fb96301b302193bf6323d514
parentd48bb2934fbf8edc80bad3b10471771d19c06f13 (diff)
parentf0dfa8b3a9703927b297ef502068532de4ee0663 (diff)
Merge "Wifi: defer turning wifi off for IMS deregistration"
-rw-r--r--service/java/com/android/server/wifi/ClientModeManager.java150
-rw-r--r--tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java546
2 files changed, 688 insertions, 8 deletions
diff --git a/service/java/com/android/server/wifi/ClientModeManager.java b/service/java/com/android/server/wifi/ClientModeManager.java
index f1e91af2f..e3072e8df 100644
--- a/service/java/com/android/server/wifi/ClientModeManager.java
+++ b/service/java/com/android/server/wifi/ClientModeManager.java
@@ -20,9 +20,21 @@ import android.annotation.NonNull;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
+import android.os.PersistableBundle;
import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsException;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.ImsReasonInfo;
+import android.telephony.ims.RegistrationManager;
+import android.telephony.ims.feature.MmTelFeature;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.Log;
@@ -31,6 +43,7 @@ import com.android.internal.util.Preconditions;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.wifi.WifiNative.InterfaceCallback;
+import com.android.server.wifi.util.WifiHandler;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -54,6 +67,7 @@ public class ClientModeManager implements ActiveModeManager {
private String mClientInterfaceName;
private boolean mIfaceIsUp = false;
private @Role int mRole = ROLE_UNSPECIFIED;
+ private DeferStopHandler mDeferStopHandler;
ClientModeManager(Context context, @NonNull Looper looper, WifiNative wifiNative,
Listener listener, WifiMetrics wifiMetrics, SarManager sarManager,
@@ -66,6 +80,7 @@ public class ClientModeManager implements ActiveModeManager {
mWakeupController = wakeupController;
mClientModeImpl = clientModeImpl;
mStateMachine = new ClientModeStateMachine(looper);
+ mDeferStopHandler = new DeferStopHandler(TAG, looper);
}
/**
@@ -89,7 +104,125 @@ public class ClientModeManager implements ActiveModeManager {
updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
WifiManager.WIFI_STATE_ENABLING);
}
- mStateMachine.quitNow();
+ mDeferStopHandler.start(
+ getWifiOffDeferringTimeMs(),
+ () -> mStateMachine.quitNow());
+ }
+
+ private class DeferStopHandler extends WifiHandler {
+ private boolean mIsDeferring = false;
+ private ImsMmTelManager mImsMmTelManager = null;
+ private Looper mLooper = null;
+ private Runnable mStopRunnable = null;
+ private final Runnable mRunnable = () -> continueToStopWifi();
+
+ private RegistrationManager.RegistrationCallback mImsRegistrationCallback =
+ new RegistrationManager.RegistrationCallback() {
+ @Override
+ public void onRegistered(int imsRadioTech) {
+ if (mIsDeferring) continueToStopWifi();
+ }
+
+ @Override
+ public void onUnregistered(ImsReasonInfo imsReasonInfo) {
+ if (mIsDeferring) continueToStopWifi();
+ }
+ };
+
+ DeferStopHandler(String tag, Looper looper) {
+ super(tag, looper);
+ mLooper = looper;
+ }
+
+ public void start(int delayMs, @NonNull Runnable stopRunnable) {
+ if (mIsDeferring) return;
+
+ if (stopRunnable == null) {
+ Log.w(TAG, "Invalid stop runnable.");
+ return;
+ }
+ mStopRunnable = stopRunnable;
+
+ // Most cases don't need delay, check it first to avoid unnecessary work.
+ if (delayMs == 0) {
+ continueToStopWifi();
+ return;
+ }
+
+ mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(
+ SubscriptionManager.getDefaultVoiceSubscriptionId());
+ if (mImsMmTelManager == null || !postDelayed(mRunnable, delayMs)) {
+ // if no delay or failed to add runnable, stop Wifi immediately.
+ continueToStopWifi();
+ return;
+ }
+
+ mIsDeferring = true;
+ Log.d(TAG, "Start DeferWifiOff handler with deferring time "
+ + delayMs + " ms.");
+ try {
+ mImsMmTelManager.registerImsRegistrationCallback(
+ new HandlerExecutor(new Handler(mLooper)),
+ mImsRegistrationCallback);
+ } catch (RuntimeException | ImsException e) {
+ Log.e(TAG, "registerImsRegistrationCallback failed", e);
+ continueToStopWifi();
+ }
+ }
+
+ private void continueToStopWifi() {
+ Log.d(TAG, "Continue to stop wifi");
+ if (mStopRunnable != null) {
+ mStopRunnable.run();
+ }
+
+ if (!mIsDeferring) return;
+
+ Log.d(TAG, "Stop DeferWifiOff handler.");
+ removeCallbacks(mRunnable);
+ if (mImsMmTelManager != null) {
+ try {
+ mImsMmTelManager.unregisterImsRegistrationCallback(mImsRegistrationCallback);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "unregisterImsRegistrationCallback failed", e);
+ }
+ }
+ mIsDeferring = false;
+ }
+ }
+
+ /**
+ * Get deferring time before turning off WiFi.
+ */
+ private int getWifiOffDeferringTimeMs() {
+ int defaultVoiceSubId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ if (defaultVoiceSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return 0;
+ }
+
+ ImsMmTelManager imsMmTelManager = ImsMmTelManager.createForSubscriptionId(
+ defaultVoiceSubId);
+ // If no wifi calling, no delay
+ if (!imsMmTelManager.isAvailable(
+ MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
+ ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)) {
+ return 0;
+ }
+
+ TelephonyManager defaultVoiceTelephonyManager =
+ TelephonyManager.from(mContext).createForSubscriptionId(defaultVoiceSubId);
+ // if LTE is available, no delay needed as IMS will be registered over LTE
+ if (defaultVoiceTelephonyManager.getVoiceNetworkType()
+ == TelephonyManager.NETWORK_TYPE_LTE) {
+ return 0;
+ }
+
+ CarrierConfigManager configManager =
+ (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ PersistableBundle config = configManager.getConfigForSubId(defaultVoiceSubId);
+ return (config != null)
+ ? config.getInt(CarrierConfigManager.Ims.KEY_WIFI_OFF_DEFERRING_TIME_INT)
+ : 0;
}
@Override
@@ -290,11 +423,16 @@ public class ClientModeManager implements ActiveModeManager {
case CMD_SWITCH_TO_SCAN_ONLY_MODE:
updateConnectModeState(WifiManager.WIFI_STATE_DISABLING,
WifiManager.WIFI_STATE_ENABLED);
- if (!mWifiNative.switchClientInterfaceToScanMode(mClientInterfaceName)) {
- mModeListener.onStartFailure();
- break;
- }
- transitionTo(mScanOnlyModeState);
+ mDeferStopHandler.start(
+ getWifiOffDeferringTimeMs(),
+ () -> {
+ if (!mWifiNative.switchClientInterfaceToScanMode(
+ mClientInterfaceName)) {
+ mModeListener.onStartFailure();
+ return;
+ }
+ transitionTo(mScanOnlyModeState);
+ });
break;
case CMD_INTERFACE_DOWN:
Log.e(TAG, "Detected an interface down, reporting failure to "
diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java
index 8bbfc399e..aa7b1bc24 100644
--- a/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/ClientModeManagerTest.java
@@ -25,25 +25,43 @@ import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_STATE_UNKNOWN;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.lenient;
+import android.app.test.MockAnswerUtil.AnswerWithArguments;
import android.content.Context;
import android.content.Intent;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.os.test.TestLooper;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsMmTelManager;
+import android.telephony.ims.RegistrationManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Unit tests for {@link ClientModeManager}.
@@ -53,6 +71,7 @@ public class ClientModeManagerTest extends WifiBaseTest {
private static final String TAG = "ClientModeManagerTest";
private static final String TEST_INTERFACE_NAME = "testif0";
private static final String OTHER_INTERFACE_NAME = "notTestIf";
+ private static final int TEST_WIFI_OFF_DEFERRING_TIME_MS = 4000;
TestLooper mLooper;
@@ -65,19 +84,81 @@ public class ClientModeManagerTest extends WifiBaseTest {
@Mock SarManager mSarManager;
@Mock WakeupController mWakeupController;
@Mock ClientModeImpl mClientModeImpl;
+ @Mock TelephonyManager mTelephonyManager;
+ @Mock CarrierConfigManager mCarrierConfigManager;
+ @Mock PersistableBundle mCarrierConfigBundle;
+ @Mock ImsMmTelManager mImsMmTelManager;
+ private RegistrationManager.RegistrationCallback mImsMmTelManagerRegistrationCallback = null;
+
+ private MockitoSession mStaticMockSession = null;
final ArgumentCaptor<WifiNative.InterfaceCallback> mInterfaceCallbackCaptor =
ArgumentCaptor.forClass(WifiNative.InterfaceCallback.class);
+ /**
+ * If mContext is reset, call it again to ensure system services could be retrieved
+ * from the context.
+ */
+ private void setUpSystemServiceForContext() {
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
+ .thenReturn(mCarrierConfigManager);
+ }
+
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mLooper = new TestLooper();
+ setUpSystemServiceForContext();
+
+ /**
+ * default mock for IMS deregistration:
+ * * No wifi calling
+ * * No network
+ * * no deferring time for wifi off
+ */
+ mStaticMockSession = mockitoSession()
+ .mockStatic(ImsMmTelManager.class)
+ .mockStatic(SubscriptionManager.class)
+ .startMocking();
+ lenient().when(ImsMmTelManager.createForSubscriptionId(anyInt()))
+ .thenReturn(mImsMmTelManager);
+ lenient().when(SubscriptionManager.getDefaultVoiceSubscriptionId())
+ .thenReturn(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
+ doAnswer(new AnswerWithArguments() {
+ public void answer(Executor executor, RegistrationManager.RegistrationCallback c) {
+ mImsMmTelManagerRegistrationCallback = c;
+ }
+ }).when(mImsMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ doAnswer(new AnswerWithArguments() {
+ public void answer(RegistrationManager.RegistrationCallback c) {
+ if (mImsMmTelManagerRegistrationCallback == c) {
+ mImsMmTelManagerRegistrationCallback = null;
+ }
+ }
+ }).when(mImsMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ when(mImsMmTelManager.isAvailable(anyInt(), anyInt())).thenReturn(false);
+
+ when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getVoiceNetworkType())
+ .thenReturn(TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfigBundle);
+ when(mCarrierConfigBundle
+ .getInt(eq(CarrierConfigManager.Ims.KEY_WIFI_OFF_DEFERRING_TIME_INT)))
+ .thenReturn(0);
+ mLooper = new TestLooper();
mClientModeManager = createClientModeManager();
mLooper.dispatchAll();
}
+ @After
+ public void cleanUp() throws Exception {
+ mStaticMockSession.finishMocking();
+ }
+
private ClientModeManager createClientModeManager() {
return new ClientModeManager(mContext, mLooper.getLooper(), mWifiNative, mListener,
mWifiMetrics, mSarManager, mWakeupController, mClientModeImpl);
@@ -255,6 +336,9 @@ public class ClientModeManagerTest extends WifiBaseTest {
ClientModeImpl.SCAN_ONLY_MODE, TEST_INTERFACE_NAME);
verify(mSarManager, times(2)).setScanOnlyWifiState(WIFI_STATE_ENABLED);
+ verify(mImsMmTelManager, never()).registerImsRegistrationCallback(any(), any());
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
// Ensure that no public broadcasts were sent.
verifyNoMoreInteractions(mContext);
verify(mListener, times(3)).onStarted();
@@ -307,10 +391,14 @@ public class ClientModeManagerTest extends WifiBaseTest {
public void clientModeStopCleansUpState() throws Exception {
startClientInConnectModeAndVerifyEnabled();
reset(mContext, mListener);
+ setUpSystemServiceForContext();
mClientModeManager.stop();
mLooper.dispatchAll();
verify(mListener).onStopped();
+ verify(mImsMmTelManager, never()).registerImsRegistrationCallback(any(), any());
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
// on an explicit stop, we should not trigger the callback
@@ -324,11 +412,13 @@ public class ClientModeManagerTest extends WifiBaseTest {
public void clientModeStopWhenNotStartedDoesNotUpdateScanStateUpdates() throws Exception {
startClientInConnectModeAndVerifyEnabled();
reset(mContext);
+ setUpSystemServiceForContext();
mClientModeManager.stop();
mLooper.dispatchAll();
verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
reset(mContext, mListener);
+ setUpSystemServiceForContext();
// now call stop again
mClientModeManager.stop();
mLooper.dispatchAll();
@@ -343,6 +433,7 @@ public class ClientModeManagerTest extends WifiBaseTest {
public void clientModeStartedStopsWhenInterfaceDown() throws Exception {
startClientInConnectModeAndVerifyEnabled();
reset(mContext);
+ setUpSystemServiceForContext();
when(mClientModeImpl.isConnectedMacRandomizationEnabled()).thenReturn(false);
mInterfaceCallbackCaptor.getValue().onDown(TEST_INTERFACE_NAME);
mLooper.dispatchAll();
@@ -360,6 +451,7 @@ public class ClientModeManagerTest extends WifiBaseTest {
throws Exception {
startClientInConnectModeAndVerifyEnabled();
reset(mContext);
+ setUpSystemServiceForContext();
when(mClientModeImpl.isConnectedMacRandomizationEnabled()).thenReturn(true);
mInterfaceCallbackCaptor.getValue().onDown(TEST_INTERFACE_NAME);
mLooper.dispatchAll();
@@ -374,6 +466,7 @@ public class ClientModeManagerTest extends WifiBaseTest {
public void clientModeStartedStopsOnInterfaceDestroyed() throws Exception {
startClientInConnectModeAndVerifyEnabled();
reset(mContext);
+ setUpSystemServiceForContext();
mInterfaceCallbackCaptor.getValue().onDestroyed(TEST_INTERFACE_NAME);
mLooper.dispatchAll();
verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
@@ -429,4 +522,453 @@ public class ClientModeManagerTest extends WifiBaseTest {
inOrder.verify(mWakeupController).stop();
inOrder.verify(mWifiNative).teardownInterface(eq(TEST_INTERFACE_NAME));
}
+
+ private void setUpVoWifiTest(
+ boolean isWifiCallingAvailable,
+ int voiceNetworkType,
+ int wifiOffDeferringTimeMs) {
+ when(mImsMmTelManager.isAvailable(anyInt(), anyInt())).thenReturn(isWifiCallingAvailable);
+ when(mTelephonyManager.getVoiceNetworkType())
+ .thenReturn(voiceNetworkType);
+ when(mCarrierConfigBundle
+ .getInt(eq(CarrierConfigManager.Ims.KEY_WIFI_OFF_DEFERRING_TIME_INT)))
+ .thenReturn(wifiOffDeferringTimeMs);
+ }
+
+ /**
+ * ClientMode stop properly with IMS deferring time without WifiCalling.
+ */
+ @Test
+ public void clientModeStopWithWifiOffDeferringTimeNoWifiCalling() throws Exception {
+ setUpVoWifiTest(false,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+ verify(mListener).onStopped();
+
+ verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
+
+ verify(mImsMmTelManager, never()).registerImsRegistrationCallback(any(), any());
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
+ // on an explicit stop, we should not trigger the callback
+ verifyNoMoreInteractions(mListener);
+ }
+
+ /**
+ * ClientMode stop properly with IMS deferring time, Wifi calling, and LTE.
+ */
+ @Test
+ public void clientModeStopWithWifiOffDeferringTimeWithWifiCallingAndLte() throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+ verify(mListener).onStopped();
+
+ verify(mImsMmTelManager, never()).registerImsRegistrationCallback(any(), any());
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
+ verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
+
+ // on an explicit stop, we should not trigger the callback
+ verifyNoMoreInteractions(mListener);
+ }
+
+ /**
+ * ClientMode stop properly with IMS deferring time and Wifi calling, but no LTE.
+ *
+ * IMS deregistration is done before reaching the timeout.
+ */
+ @Test
+ public void clientModeStopWithWifiOffDeferringTimeAndWifiCallingNoLteOnImsUnregistered()
+ throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+
+ // Not yet finish IMS deregistration.
+ verify(mImsMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+ verify(mListener, never()).onStopped();
+
+ // Notify wifi service IMS service is de-registered.
+ assertNotNull(mImsMmTelManagerRegistrationCallback);
+ mImsMmTelManagerRegistrationCallback.onUnregistered(null);
+ mLooper.dispatchAll();
+
+ // Now Wifi could be turned off actually.
+ verify(mImsMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ assertNull(mImsMmTelManagerRegistrationCallback);
+ verify(mListener).onStopped();
+
+ verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
+
+ // on an explicit stop, we should not trigger the callback
+ verifyNoMoreInteractions(mListener);
+ }
+
+ /**
+ * ClientMode stop properly with IMS deferring time and Wifi calling, but no LTE.
+ *
+ * IMS deregistration is done before reaching the timeout.
+ */
+ @Test
+ public void clientModeStopWithWifiOffDeferringTimeAndWifiCallingNoLteOnImsRegistered()
+ throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+
+ // Not yet finish IMS deregistration.
+ verify(mImsMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+ verify(mListener, never()).onStopped();
+
+ // Notify wifi service IMS service is de-registered.
+ assertNotNull(mImsMmTelManagerRegistrationCallback);
+ mImsMmTelManagerRegistrationCallback.onRegistered(0);
+ mLooper.dispatchAll();
+
+ // Now Wifi could be turned off actually.
+ verify(mImsMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ assertNull(mImsMmTelManagerRegistrationCallback);
+ verify(mListener).onStopped();
+
+ verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
+
+ // on an explicit stop, we should not trigger the callback
+ verifyNoMoreInteractions(mListener);
+ }
+
+ /**
+ * ClientMode stop properly with IMS deferring time and Wifi calling, but no LTE.
+ *
+ * IMS deregistration is NOT done before reaching the timeout.
+ */
+ @Test
+ public void clientModeStopWithWifiOffDeferringTimeAndWifiCallingNoLteTimedOut()
+ throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+ verify(mImsMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+ verify(mListener, never()).onStopped();
+
+ // 1/2 deferring time passed, should be still waiting for the callback.
+ mLooper.moveTimeForward(TEST_WIFI_OFF_DEFERRING_TIME_MS / 2);
+ mLooper.dispatchAll();
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+ verify(mListener, never()).onStopped();
+
+ // Exceeding the timeout, wifi should be stopped.
+ mLooper.moveTimeForward(TEST_WIFI_OFF_DEFERRING_TIME_MS / 2 + 1000);
+ mLooper.dispatchAll();
+ verify(mImsMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ assertNull(mImsMmTelManagerRegistrationCallback);
+ verify(mListener).onStopped();
+
+ verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
+
+ // on an explicit stop, we should not trigger the callback
+ verifyNoMoreInteractions(mListener);
+ }
+
+ /**
+ * ClientMode stop properly with IMS deferring time and Wifi calling, but no LTE.
+ *
+ * IMS deregistration is NOT done before reaching the timeout with multiple stop calls.
+ */
+ @Test
+ public void clientModeStopWithWifiOffDeferringTimeAndWifiCallingNoLteTimedOutMultipleStop()
+ throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+ verify(mImsMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+ verify(mListener, never()).onStopped();
+
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+ // should not register another listener.
+ verify(mImsMmTelManager, times(1)).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+ verify(mListener, never()).onStopped();
+
+ // Exceeding the timeout, wifi should be stopped.
+ mLooper.moveTimeForward(TEST_WIFI_OFF_DEFERRING_TIME_MS + 1000);
+ mLooper.dispatchAll();
+ verify(mImsMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ assertNull(mImsMmTelManagerRegistrationCallback);
+ verify(mListener).onStopped();
+
+ verifyConnectModeNotificationsForCleanShutdown(WIFI_STATE_ENABLED);
+
+ // on an explicit stop, we should not trigger the callback
+ verifyNoMoreInteractions(mListener);
+ }
+
+ /**
+ * Switch to scan mode properly with IMS deferring time without WifiCalling.
+ */
+ @Test
+ public void switchToScanOnlyModeWithWifiOffDeferringTimeNoWifiCalling() throws Exception {
+ setUpVoWifiTest(false,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ when(mWifiNative.switchClientInterfaceToScanMode(any()))
+ .thenReturn(true);
+
+ mClientModeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).switchClientInterfaceToScanMode(TEST_INTERFACE_NAME);
+ verify(mImsMmTelManager, never()).registerImsRegistrationCallback(any(), any());
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+ }
+
+ /**
+ * Switch to scan mode properly with IMS deferring time, Wifi calling, and LTE.
+ */
+ @Test
+ public void
+ switchToScanOnlyModeWithWifiOffDeferringTimeAndWifiCallingAndLte() throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_LTE,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ when(mWifiNative.switchClientInterfaceToScanMode(any()))
+ .thenReturn(true);
+
+ mClientModeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative).switchClientInterfaceToScanMode(TEST_INTERFACE_NAME);
+ verify(mImsMmTelManager, never()).registerImsRegistrationCallback(any(), any());
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+ }
+
+ /**
+ * Switch to scan mode properly with IMS deferring time and Wifi calling, but no LTE.
+ *
+ * IMS deregistration is done before reaching the timeout.
+ */
+ @Test
+ public void switchToScanOnlyModeWithWifiOffDeferringTimeAndWifiCallingNoLteOnImsUnregistered()
+ throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ when(mWifiNative.switchClientInterfaceToScanMode(any()))
+ .thenReturn(true);
+
+ mClientModeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY);
+ mLooper.dispatchAll();
+
+ // Not yet finish IMS deregistration.
+ verify(mWifiNative, never()).switchClientInterfaceToScanMode(any());
+ verify(mImsMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
+ // Notify wifi service IMS service is de-registered.
+ assertNotNull(mImsMmTelManagerRegistrationCallback);
+ mImsMmTelManagerRegistrationCallback.onUnregistered(null);
+ mLooper.dispatchAll();
+
+ // Now Wifi could be switched to scan mode actually.
+ verify(mWifiNative).switchClientInterfaceToScanMode(TEST_INTERFACE_NAME);
+ verify(mImsMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ assertNull(mImsMmTelManagerRegistrationCallback);
+ }
+
+ /**
+ * Switch to scan mode properly with IMS deferring time and Wifi calling, but no LTE.
+ *
+ * IMS deregistration is done before reaching the timeout.
+ */
+ @Test
+ public void switchToScanOnlyModeWithWifiOffDeferringTimeAndWifiCallingNoLteOnImsRegistered()
+ throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ when(mWifiNative.switchClientInterfaceToScanMode(any()))
+ .thenReturn(true);
+
+ mClientModeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY);
+ mLooper.dispatchAll();
+
+ // Not yet finish IMS deregistration.
+ verify(mWifiNative, never()).switchClientInterfaceToScanMode(any());
+ verify(mImsMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
+ // Notify wifi service IMS service is de-registered.
+ assertNotNull(mImsMmTelManagerRegistrationCallback);
+ mImsMmTelManagerRegistrationCallback.onRegistered(0);
+ mLooper.dispatchAll();
+
+ // Now Wifi could be switched to scan mode actually.
+ verify(mWifiNative).switchClientInterfaceToScanMode(TEST_INTERFACE_NAME);
+ verify(mImsMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ assertNull(mImsMmTelManagerRegistrationCallback);
+ }
+
+ /**
+ * Switch to scan mode properly with IMS deferring time and Wifi calling, but no LTE.
+ *
+ * IMS deregistration is NOT done before reaching the timeout.
+ */
+ @Test
+ public void switchToScanOnlyModeWithWifiOffDeferringTimeAndWifiCallingNoLteTimedOut()
+ throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ when(mWifiNative.switchClientInterfaceToScanMode(any()))
+ .thenReturn(true);
+
+ mClientModeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY);
+ mLooper.dispatchAll();
+
+ verify(mWifiNative, never()).switchClientInterfaceToScanMode(any());
+ verify(mImsMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
+ // 1/2 deferring time passed, should be still waiting for the callback.
+ mLooper.moveTimeForward(TEST_WIFI_OFF_DEFERRING_TIME_MS / 2);
+ mLooper.dispatchAll();
+ verify(mWifiNative, never()).switchClientInterfaceToScanMode(any());
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
+ // Exceeding the timeout, wifi should be stopped.
+ mLooper.moveTimeForward(TEST_WIFI_OFF_DEFERRING_TIME_MS / 2 + 1000);
+ mLooper.dispatchAll();
+ verify(mWifiNative).switchClientInterfaceToScanMode(TEST_INTERFACE_NAME);
+ verify(mImsMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ assertNull(mImsMmTelManagerRegistrationCallback);
+ }
+
+ /**
+ * Switch to scan mode properly with IMS deferring time and Wifi calling, but no LTE.
+ *
+ * IMS deregistration is NOT done before reaching the timeout with multiple stop calls.
+ */
+ @Test
+ public void
+ switchToScanOnlyModeWithWifiOffDeferringTimeAndWifiCallingNoLteTimedOutMultipleSwitch()
+ throws Exception {
+ setUpVoWifiTest(true,
+ TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ TEST_WIFI_OFF_DEFERRING_TIME_MS);
+
+ startClientInConnectModeAndVerifyEnabled();
+ reset(mContext, mListener);
+ setUpSystemServiceForContext();
+ when(mWifiNative.switchClientInterfaceToScanMode(any()))
+ .thenReturn(true);
+
+ mClientModeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY);
+ mLooper.dispatchAll();
+
+ verify(mImsMmTelManager).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
+ mClientModeManager.stop();
+ mLooper.dispatchAll();
+ // should not register another listener.
+ verify(mWifiNative, never()).switchClientInterfaceToScanMode(any());
+ verify(mImsMmTelManager, times(1)).registerImsRegistrationCallback(
+ any(Executor.class),
+ any(RegistrationManager.RegistrationCallback.class));
+ verify(mImsMmTelManager, never()).unregisterImsRegistrationCallback(any());
+
+ // Exceeding the timeout, wifi should be stopped.
+ mLooper.moveTimeForward(TEST_WIFI_OFF_DEFERRING_TIME_MS + 1000);
+ mLooper.dispatchAll();
+ verify(mWifiNative).switchClientInterfaceToScanMode(TEST_INTERFACE_NAME);
+ verify(mImsMmTelManager).unregisterImsRegistrationCallback(
+ any(RegistrationManager.RegistrationCallback.class));
+ assertNull(mImsMmTelManagerRegistrationCallback);
+ }
}