Add timeout for ImsState callback

If the ImsRegistration or MmTelCapabilities callbacks are not received, read the states from ImsStateTracker and continue with domain selection.

Bug: 343021500
Flag: EXEMPT bugfix
Test: atest TeleServiceTests:NormalCallDomainSelectorTest
Change-Id: I2f74b2587b9a273cbc5c966a4bf76d4db23a43ac
diff --git a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
index aedd06e..bf6c647 100644
--- a/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
+++ b/src/com/android/services/telephony/domainselection/NormalCallDomainSelector.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.content.Context;
 import android.os.Looper;
+import android.os.Message;
 import android.os.PersistableBundle;
 import android.telecom.TelecomManager;
 import android.telephony.Annotation.DisconnectCauses;
@@ -45,6 +46,13 @@
 
     private static final String LOG_TAG = "NCDS";
 
+    // Wait-time for IMS state change callback.
+    @VisibleForTesting
+    protected static final int WAIT_FOR_IMS_STATE_TIMEOUT_MS = 3000; // 3 seconds
+
+    @VisibleForTesting
+    protected static final int MSG_WAIT_FOR_IMS_STATE_TIMEOUT = 11;
+
     @VisibleForTesting
     protected enum SelectorState {
         ACTIVE,
@@ -67,12 +75,40 @@
             logd("Subscribing to state callbacks. Subid:" + subId);
             mImsStateTracker.addServiceStateListener(this);
             mImsStateTracker.addImsStateListener(this);
+
         } else {
             loge("Invalid Subscription. Subid:" + subId);
         }
     }
 
     @Override
+    public void handleMessage(Message message) {
+        switch (message.what) {
+
+            case MSG_WAIT_FOR_IMS_STATE_TIMEOUT: {
+                loge("ImsStateTimeout. ImsState callback not received");
+                if (mSelectorState != SelectorState.ACTIVE) {
+                    return;
+                }
+
+                if (!mImsRegStateReceived) {
+                    onImsRegistrationStateChanged();
+                }
+
+                if (!mMmTelCapabilitiesReceived) {
+                    onImsMmTelCapabilitiesChanged();
+                }
+            }
+            break;
+
+            default: {
+                super.handleMessage(message);
+            }
+            break;
+        }
+    }
+
+    @Override
     public void selectDomain(SelectionAttributes attributes, TransportSelectorCallback callback) {
         mSelectionAttributes = attributes;
         mTransportSelectorCallback = callback;
@@ -104,6 +140,7 @@
 
         if (subId == getSubId()) {
             logd("NormalCallDomainSelection triggered. Sub-id:" + subId);
+            sendEmptyMessageDelayed(MSG_WAIT_FOR_IMS_STATE_TIMEOUT, WAIT_FOR_IMS_STATE_TIMEOUT_MS);
             post(() -> selectDomain());
         } else {
             mSelectorState = SelectorState.INACTIVE;
@@ -389,6 +426,10 @@
             return;
         }
 
+        if (hasMessages(MSG_WAIT_FOR_IMS_STATE_TIMEOUT)) {
+            removeMessages(MSG_WAIT_FOR_IMS_STATE_TIMEOUT);
+        }
+
         // Check IMS registration state.
         if (!mImsStateTracker.isImsRegistered()) {
             logd("IMS is NOT registered");
diff --git a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
index cce07d0..49411bd 100644
--- a/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
+++ b/tests/src/com/android/services/telephony/domainselection/NormalCallDomainSelectorTest.java
@@ -19,6 +19,7 @@
 import static android.telephony.DomainSelectionService.SELECTOR_TYPE_CALLING;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Mockito.any;
@@ -666,6 +667,82 @@
                 mNormalCallDomainSelector.getSelectorState());
     }
 
+    @Test
+    public void testImsRegistrationStateTimeoutMessage() {
+        final TestTransportSelectorCallback transportSelectorCallback =
+                new TestTransportSelectorCallback(mNormalCallDomainSelector);
+
+        final ServiceState serviceState = new ServiceState();
+        serviceState.setState(ServiceState.STATE_IN_SERVICE);
+        mNormalCallDomainSelector.onServiceStateUpdated(serviceState);
+        doReturn(true).when(mMockImsStateTracker).isImsStateReady();
+        doReturn(true).when(mMockImsStateTracker).isImsRegistered();
+        doReturn(true).when(mMockImsStateTracker).isImsVoiceCapable();
+        doReturn(false).when(mMockImsStateTracker).isImsVideoCapable();
+        doReturn(true).when(mMockImsStateTracker).isImsRegisteredOverWlan();
+
+        DomainSelectionService.SelectionAttributes attributes =
+                new DomainSelectionService.SelectionAttributes.Builder(
+                        SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+                        .setAddress(TEST_URI)
+                        .setCallId(TEST_CALLID)
+                        .setEmergency(false)
+                        .setVideoCall(false)
+                        .setExitedFromAirplaneMode(false)
+                        .build();
+
+        mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+        assertTrue(mNormalCallDomainSelector.hasMessages(
+                NormalCallDomainSelector.MSG_WAIT_FOR_IMS_STATE_TIMEOUT));
+
+        mNormalCallDomainSelector.onImsRegistrationStateChanged();
+        mNormalCallDomainSelector.onImsMmTelCapabilitiesChanged();
+        processAllMessages();
+
+        assertFalse(mNormalCallDomainSelector.hasMessages(
+                NormalCallDomainSelector.MSG_WAIT_FOR_IMS_STATE_TIMEOUT));
+        assertTrue(transportSelectorCallback.mWlanSelected);
+        assertEquals(NormalCallDomainSelector.SelectorState.INACTIVE,
+                mNormalCallDomainSelector.getSelectorState());
+    }
+
+    @Test
+    public void testImsRegistrationStateTimeoutHandler() {
+        final TestTransportSelectorCallback transportSelectorCallback =
+                new TestTransportSelectorCallback(mNormalCallDomainSelector);
+
+        final ServiceState serviceState = new ServiceState();
+        serviceState.setState(ServiceState.STATE_IN_SERVICE);
+        mNormalCallDomainSelector.onServiceStateUpdated(serviceState);
+        doReturn(true).when(mMockImsStateTracker).isImsStateReady();
+        doReturn(false).when(mMockImsStateTracker).isImsRegistered();
+        doReturn(true).when(mMockImsStateTracker).isImsVoiceCapable();
+        doReturn(false).when(mMockImsStateTracker).isImsVideoCapable();
+        doReturn(true).when(mMockImsStateTracker).isImsRegisteredOverWlan();
+
+        DomainSelectionService.SelectionAttributes attributes =
+                new DomainSelectionService.SelectionAttributes.Builder(
+                        SLOT_ID, SUB_ID_1, SELECTOR_TYPE_CALLING)
+                        .setAddress(TEST_URI)
+                        .setCallId(TEST_CALLID)
+                        .setEmergency(false)
+                        .setVideoCall(false)
+                        .setExitedFromAirplaneMode(false)
+                        .build();
+
+        mNormalCallDomainSelector.selectDomain(attributes, transportSelectorCallback);
+        assertTrue(mNormalCallDomainSelector.hasMessages(
+                NormalCallDomainSelector.MSG_WAIT_FOR_IMS_STATE_TIMEOUT));
+
+        mTestableLooper.moveTimeForward(
+                NormalCallDomainSelector.WAIT_FOR_IMS_STATE_TIMEOUT_MS + 10);
+        processAllMessages();
+
+        assertEquals(transportSelectorCallback.mSelectedDomain, NetworkRegistrationInfo.DOMAIN_CS);
+        assertEquals(NormalCallDomainSelector.SelectorState.INACTIVE,
+                mNormalCallDomainSelector.getSelectorState());
+    }
+
     static class TestTransportSelectorCallback implements TransportSelectorCallback,
             WwanSelectorCallback {
         public boolean mCreated;