[QNS] Add handling emergency call over IMS

IMS ANE will monitor emergency call status if emergency call over IMS is
allowed.

Bug: 233581891
Test: atest QualifiedNetworksStatusTracker
Change-Id: I4612fffe90571ff416427bec981ed1cec6e3aed2
diff --git a/src/com/android/qns/AccessNetworkEvaluator.java b/src/com/android/qns/AccessNetworkEvaluator.java
index dde7303..6328fec 100644
--- a/src/com/android/qns/AccessNetworkEvaluator.java
+++ b/src/com/android/qns/AccessNetworkEvaluator.java
@@ -636,7 +636,11 @@
         evaluate();
     }
 
-    private void onSetCallType(@QnsConstants.QnsCallType int callType) {
+    @VisibleForTesting
+    void onSetCallType(@QnsConstants.QnsCallType int callType) {
+        if (mApnType == ApnSetting.TYPE_IMS && callType == QnsConstants.CALL_TYPE_EMERGENCY) {
+            if (!mDataConnectionStatusTracker.isActiveState()) return;
+        }
         mCallType = callType;
         mRestrictManager.setQnsCallType(mCallType);
         log("onSetCallType CallType:" + mCallType);
@@ -680,6 +684,10 @@
             case DataConnectionStatusTracker.EVENT_DATA_CONNECTION_DISCONNECTED:
                 needEvaluate = true;
                 initLastNotifiedQualifiedNetwork();
+                if (mApnType == ApnSetting.TYPE_IMS) {
+                    if (mAltEventListener != null) mAltEventListener.clearNormalCallInfo();
+                }
+                mCallType = QnsConstants.CALL_TYPE_IDLE;
                 break;
             case DataConnectionStatusTracker.EVENT_DATA_CONNECTION_CONNECTED:
                 mHandler.post(() -> onDataConnectionConnected(info.getTransportType()));
@@ -1241,6 +1249,9 @@
                         : AccessNetworkType.IWLAN;
         if (mConfigManager.isHandoverAllowedByPolicy(
                 mApnType, srcAccessNetwork, dstAccessNetwork, mCoverage)) {
+            if (mApnType == ApnSetting.TYPE_IMS && mCallType == QnsConstants.CALL_TYPE_EMERGENCY) {
+                return false;
+            }
             return true;
         } else {
             if (mApnType == ApnSetting.TYPE_IMS && mCallType == QnsConstants.CALL_TYPE_IDLE) {
diff --git a/src/com/android/qns/AlternativeEventListener.java b/src/com/android/qns/AlternativeEventListener.java
index 5ca7aac..a1af8b5 100644
--- a/src/com/android/qns/AlternativeEventListener.java
+++ b/src/com/android/qns/AlternativeEventListener.java
@@ -17,6 +17,8 @@
 package com.android.qns;
 
 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ACTIVE;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_ALERTING;
+import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_DIALING;
 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED;
 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_HOLDING;
 import static android.telephony.PreciseCallState.PRECISE_CALL_STATE_IDLE;
@@ -30,6 +32,7 @@
 import android.os.Message;
 import android.os.Registrant;
 import android.telephony.Annotation;
+import android.telephony.PreciseDataConnectionState;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 import android.util.Log;
@@ -359,6 +362,12 @@
         }
     }
 
+    void clearNormalCallInfo() {
+        mCallInfoManager.clearCallInfo();
+        mCallInfoManager.mLastReportedCallType = QnsConstants.CALL_TYPE_IDLE;
+        unregisterLowRtpQualityEvent(ApnSetting.TYPE_IMS, null);
+    }
+
     @VisibleForTesting
     void notifyRtpLowQuality(int callType, int reason) {
         if (callType == QnsConstants.CALL_TYPE_VOICE) {
@@ -373,6 +382,14 @@
             } else {
                 log("notifyRtpLowQuality mEmcLowRtpQuallityListener is null.");
             }
+            if (mCallInfoManager.mLastReportedCallType == QnsConstants.CALL_TYPE_EMERGENCY) {
+                if (mLowRtpQuallityListener != null) {
+                    log("notifyRtpLowQuality for emergency call to IMS ANE");
+                    mLowRtpQuallityListener.notifyResult(reason);
+                } else {
+                    log("notifyRtpLowQuality mLowRtpQuallityListener is null.");
+                }
+            }
         }
     }
 
@@ -395,6 +412,29 @@
                                 QnsConstants.CALL_TYPE_EMERGENCY);
                     }
                 }
+                if (mCallTypeChangedEventListener != null) {
+                    if ((state == PRECISE_CALL_STATE_ACTIVE
+                                    || state == PRECISE_CALL_STATE_DIALING
+                                    || state == PRECISE_CALL_STATE_ALERTING)
+                            && !isDataNetworkConnected(ApnSetting.TYPE_EMERGENCY)
+                            && isDataNetworkConnected(ApnSetting.TYPE_IMS)) {
+                        log("Emergency call is progressing without emergency PDN");
+                        if (mCallInfoManager.mLastReportedCallType
+                                != QnsConstants.CALL_TYPE_EMERGENCY) {
+                            mCallTypeChangedEventListener.notifyResult(
+                                    QnsConstants.CALL_TYPE_EMERGENCY);
+                            mCallInfoManager.mLastReportedCallType =
+                                    QnsConstants.CALL_TYPE_EMERGENCY;
+                        }
+                    } else if (state == PRECISE_CALL_STATE_DISCONNECTED) {
+                        log("Emergency call disconnected");
+                        if (mCallInfoManager.mLastReportedCallType
+                                == QnsConstants.CALL_TYPE_EMERGENCY) {
+                            mCallTypeChangedEventListener.notifyResult(QnsConstants.CALL_TYPE_IDLE);
+                            mCallInfoManager.mLastReportedCallType = QnsConstants.CALL_TYPE_IDLE;
+                        }
+                    }
+                }
                 return;
             }
             if (mCallTypeChangedEventListener != null) {
@@ -446,6 +486,17 @@
         }
     }
 
+    private boolean isDataNetworkConnected(int apnType) {
+        PreciseDataConnectionState preciseDataStatus =
+                QnsTelephonyListener.getInstance(mContext, mSlotIndex)
+                        .getLastPreciseDataConnectionState(apnType);
+        if (preciseDataStatus == null) return false;
+        int state = preciseDataStatus.getState();
+        return (state == TelephonyManager.DATA_CONNECTED
+                || state == TelephonyManager.DATA_HANDOVER_IN_PROGRESS
+                || state == TelephonyManager.DATA_SUSPENDED);
+    }
+
     @VisibleForTesting
     protected void close() {
         mCallTypeChangedEventListener = null;
diff --git a/src/com/android/qns/QnsCarrierConfigManager.java b/src/com/android/qns/QnsCarrierConfigManager.java
index e56d14c..fde1fbd 100644
--- a/src/com/android/qns/QnsCarrierConfigManager.java
+++ b/src/com/android/qns/QnsCarrierConfigManager.java
@@ -66,12 +66,6 @@
  * management & listing related Access Network to pass to Telephony
  */
 public class QnsCarrierConfigManager {
-
-    /**
-     * Keys supporting the configurations to support ANE in HO decision and Access Network
-     * candidating
-     */
-
     /**
      * Boolean indicating the WFC services in QNS Side is enabled, when airplane mode is On
      *
diff --git a/tests/src/com/android/qns/AccessNetworkEvaluatorTest.java b/tests/src/com/android/qns/AccessNetworkEvaluatorTest.java
index 37c9c68..fe0abbc 100644
--- a/tests/src/com/android/qns/AccessNetworkEvaluatorTest.java
+++ b/tests/src/com/android/qns/AccessNetworkEvaluatorTest.java
@@ -336,6 +336,68 @@
     }
 
     @Test
+    public void testMoveTransportTypeAllowedEmergencyOverIms() {
+        when(configManager.isHandoverAllowedByPolicy(
+                        ApnSetting.TYPE_IMS,
+                        AccessNetworkConstants.AccessNetworkType.IWLAN,
+                        AccessNetworkConstants.AccessNetworkType.EUTRAN,
+                        QnsConstants.COVERAGE_HOME))
+                .thenReturn(true);
+        when(configManager.isHandoverAllowedByPolicy(
+                        ApnSetting.TYPE_IMS,
+                        AccessNetworkConstants.AccessNetworkType.EUTRAN,
+                        AccessNetworkConstants.AccessNetworkType.IWLAN,
+                        QnsConstants.COVERAGE_HOME))
+                .thenReturn(true);
+        when(configManager.isVolteRoamingSupported(anyInt())).thenReturn(true);
+        when(dataConnectionStatusTracker.isActiveState()).thenReturn(true);
+        when(dataConnectionStatusTracker.getLastTransportType())
+                .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
+        List<Integer> accessNetworks = new ArrayList<>();
+        accessNetworks.add(AccessNetworkConstants.AccessNetworkType.IWLAN);
+        QnsTelephonyListener.QnsTelephonyInfo info = qnsTelephonyListener.new QnsTelephonyInfo();
+        info.setCellularAvailable(true);
+        info.setCoverage(false);
+        info.setDataTech(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+        info.setVoiceTech(ServiceState.RIL_RADIO_TECHNOLOGY_LTE);
+        info.setDataRegState(ServiceState.STATE_IN_SERVICE);
+        QnsTelephonyListener.QnsTelephonyInfoIms infoIms =
+                qnsTelephonyListener.new QnsTelephonyInfoIms(info, true, true, false, false);
+        ane.onQnsTelephonyInfoChanged(infoIms);
+        ane.updateLastNotifiedQualifiedNetwork(accessNetworks);
+        ane.onSetCallType(QnsConstants.CALL_TYPE_EMERGENCY);
+        assertTrue(ane.needHandoverPolicyCheck());
+        assertFalse(ane.moveTransportTypeAllowed());
+
+        when(configManager.isHandoverAllowedByPolicy(
+                        ApnSetting.TYPE_IMS,
+                        AccessNetworkConstants.AccessNetworkType.IWLAN,
+                        AccessNetworkConstants.AccessNetworkType.UTRAN,
+                        QnsConstants.COVERAGE_HOME))
+                .thenReturn(false);
+        info = qnsTelephonyListener.new QnsTelephonyInfo();
+        info.setCellularAvailable(true);
+        info.setCoverage(false);
+        info.setDataTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
+        info.setVoiceTech(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS);
+        info.setDataRegState(ServiceState.STATE_IN_SERVICE);
+        infoIms = qnsTelephonyListener.new QnsTelephonyInfoIms(info, true, true, false, false);
+        ane.onQnsTelephonyInfoChanged(infoIms);
+        ane.updateLastNotifiedQualifiedNetwork(accessNetworks);
+        ane.onSetCallType(QnsConstants.CALL_TYPE_VOICE);
+        assertTrue(ane.needHandoverPolicyCheck());
+        assertFalse(ane.moveTransportTypeAllowed());
+        ane.updateLastNotifiedQualifiedNetwork(accessNetworks);
+        ane.onSetCallType(QnsConstants.CALL_TYPE_IDLE);
+        assertTrue(ane.needHandoverPolicyCheck());
+        assertTrue(ane.moveTransportTypeAllowed());
+        ane.updateLastNotifiedQualifiedNetwork(accessNetworks);
+        ane.onSetCallType(QnsConstants.CALL_TYPE_EMERGENCY);
+        assertTrue(ane.needHandoverPolicyCheck());
+        assertFalse(ane.moveTransportTypeAllowed());
+    }
+
+    @Test
     public void testVopsCheckRequired() {
         when(dataConnectionStatusTracker.getLastTransportType())
                 .thenReturn(AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
diff --git a/tests/src/com/android/qns/AlternativeEventListenerTest.java b/tests/src/com/android/qns/AlternativeEventListenerTest.java
index 744a2e7..e635eb8 100644
--- a/tests/src/com/android/qns/AlternativeEventListenerTest.java
+++ b/tests/src/com/android/qns/AlternativeEventListenerTest.java
@@ -19,12 +19,14 @@
 import static org.junit.Assert.*;
 
 import android.content.Context;
+import android.net.LinkProperties;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
 import android.os.test.TestLooper;
 import android.telephony.AccessNetworkConstants;
 import android.telephony.PreciseCallState;
+import android.telephony.PreciseDataConnectionState;
 import android.telephony.TelephonyManager;
 import android.telephony.data.ApnSetting;
 
@@ -49,6 +51,7 @@
     private Handler mHandler;
     CountDownLatch mLatch;
     int[] mThresholds;
+    QnsTelephonyListener mQtListener;
 
     class AltEventProvider extends AlternativeEventProvider {
 
@@ -82,11 +85,13 @@
         mHandler = new Handler(mTestLooper.getLooper());
         mListener = AlternativeEventListener.getInstance(sMockContext, SLOT_INDEX);
         mAltEventProvider = new AltEventProvider(sMockContext, SLOT_INDEX);
+        mQtListener = QnsTelephonyListener.getInstance(sMockContext, 0);
         // mListener.setEventProvider(mAltEventProvider);
     }
 
     @After
     public void tearDown() {
+        mQtListener.close();
         mListener.close();
     }
 
@@ -321,6 +326,112 @@
     }
 
     @Test
+    public void testEmergencyOverImsCallTypeChangedScenarios() {
+        mListener.registerCallTypeChangedListener(ApnSetting.TYPE_IMS, mHandler, 1, null);
+        PreciseDataConnectionState emergencyDataStatus =
+                new PreciseDataConnectionState.Builder()
+                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_INVALID)
+                        .setState(TelephonyManager.DATA_DISCONNECTED)
+                        .setNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+                        .setApnSetting(
+                                new ApnSetting.Builder()
+                                        .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY)
+                                        .setApnName("sos")
+                                        .setEntryName("sos")
+                                        .build())
+                        .setLinkProperties(new LinkProperties())
+                        .build();
+        PreciseDataConnectionState imsDataStatus =
+                new PreciseDataConnectionState.Builder()
+                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+                        .setState(TelephonyManager.DATA_CONNECTED)
+                        .setNetworkType(AccessNetworkConstants.AccessNetworkType.IWLAN)
+                        .setApnSetting(
+                                new ApnSetting.Builder()
+                                        .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+                                        .setApnName("ims")
+                                        .setEntryName("ims")
+                                        .build())
+                        .build();
+        mQtListener.mTelephonyListener.onPreciseDataConnectionStateChanged(emergencyDataStatus);
+        mQtListener.mTelephonyListener.onPreciseDataConnectionStateChanged(imsDataStatus);
+        // Test1:
+        mAltEventProvider.notifyCallInfo(
+                1, QnsConstants.CALL_TYPE_EMERGENCY, PreciseCallState.PRECISE_CALL_STATE_DIALING);
+
+        Message msg = mTestLooper.nextMessage();
+        assertNotNull(msg);
+        AsyncResult result = (AsyncResult) msg.obj;
+        assertNotNull(result.result);
+        assertEquals(QnsConstants.CALL_TYPE_EMERGENCY, (int) result.result);
+
+        // Test2:
+        mAltEventProvider.notifyCallInfo(
+                1, QnsConstants.CALL_TYPE_EMERGENCY, PreciseCallState.PRECISE_CALL_STATE_ACTIVE);
+        msg = mTestLooper.nextMessage();
+
+        // Should not notify if call type is not changed
+        assertNull(msg);
+
+        // Test3:
+        imsDataStatus =
+                new PreciseDataConnectionState.Builder()
+                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_INVALID)
+                        .setState(TelephonyManager.DATA_DISCONNECTED)
+                        .setNetworkType(AccessNetworkConstants.AccessNetworkType.EUTRAN)
+                        .setApnSetting(
+                                new ApnSetting.Builder()
+                                        .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+                                        .setApnName("ims")
+                                        .setEntryName("ims")
+                                        .build())
+                        .build();
+        mQtListener.mTelephonyListener.onPreciseDataConnectionStateChanged(imsDataStatus);
+
+        mAltEventProvider.notifyCallInfo(
+                1,
+                QnsConstants.CALL_TYPE_EMERGENCY,
+                PreciseCallState.PRECISE_CALL_STATE_DISCONNECTED);
+        msg = mTestLooper.nextMessage();
+        assertNotNull(msg);
+        result = (AsyncResult) msg.obj;
+        assertNotNull(result.result);
+        assertEquals(QnsConstants.CALL_TYPE_IDLE, (int) result.result);
+
+        // Test4:
+        emergencyDataStatus =
+                new PreciseDataConnectionState.Builder()
+                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+                        .setState(TelephonyManager.DATA_CONNECTED)
+                        .setNetworkType(AccessNetworkConstants.AccessNetworkType.IWLAN)
+                        .setApnSetting(
+                                new ApnSetting.Builder()
+                                        .setApnTypeBitmask(ApnSetting.TYPE_EMERGENCY)
+                                        .setApnName("sos")
+                                        .setEntryName("sos")
+                                        .build())
+                        .build();
+        mQtListener.mTelephonyListener.onPreciseDataConnectionStateChanged(emergencyDataStatus);
+        imsDataStatus =
+                new PreciseDataConnectionState.Builder()
+                        .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WLAN)
+                        .setState(TelephonyManager.DATA_CONNECTED)
+                        .setNetworkType(AccessNetworkConstants.AccessNetworkType.IWLAN)
+                        .setApnSetting(
+                                new ApnSetting.Builder()
+                                        .setApnTypeBitmask(ApnSetting.TYPE_IMS)
+                                        .setApnName("ims")
+                                        .setEntryName("ims")
+                                        .build())
+                        .build();
+        mQtListener.mTelephonyListener.onPreciseDataConnectionStateChanged(imsDataStatus);
+        mAltEventProvider.notifyCallInfo(
+                2, QnsConstants.CALL_TYPE_EMERGENCY, PreciseCallState.PRECISE_CALL_STATE_ACTIVE);
+        msg = mTestLooper.nextMessage();
+        assertNull(msg);
+    }
+
+    @Test
     public void testOnSrvccStateChanged() {
         mListener.registerCallTypeChangedListener(ApnSetting.TYPE_IMS, mHandler, 1, null);