Use Emergency PhoneAccount for MT ECM Call on no SIM

Use the correct Emergency PhoneAccount for a MT call on a phone with no
SIM that is in ECM. Also, reject incoming SIP calls that are received on
a disabled PhoneAccount instead of crashing the Phone process.

Bug: 27820226
Change-Id: Iab81df05ba9c4f94f08576baee47475d59760a13
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 1749df2..aa62575 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -73,6 +73,7 @@
  * Misc utilities for the Phone app.
  */
 public class PhoneUtils {
+    public static final String EMERGENCY_ACCOUNT_HANDLE_ID = "E";
     private static final String LOG_TAG = "PhoneUtils";
     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
@@ -2410,7 +2411,8 @@
             Phone phone, String prefix, boolean isEmergency) {
         // TODO: Should use some sort of special hidden flag to decorate this account as
         // an emergency-only account
-        String id = isEmergency ? "E" : prefix + String.valueOf(phone.getIccSerialNumber());
+        String id = isEmergency ? EMERGENCY_ACCOUNT_HANDLE_ID : prefix +
+                String.valueOf(phone.getIccSerialNumber());
         return makePstnPhoneAccountHandleWithPrefix(id, prefix, isEmergency);
     }
 
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index cfcf466..c0fe2fb 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -16,18 +16,30 @@
 
 package com.android.services.telephony;
 
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.Call;
+import com.android.internal.telephony.CallStateException;
 import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.DriverCall;
+import com.android.internal.telephony.GsmCdmaCallTracker;
+import com.android.internal.telephony.GsmCdmaConnection;
+import com.android.internal.telephony.GsmCdmaPhone;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.TelephonyComponentFactory;
+import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
 import com.android.internal.telephony.imsphone.ImsExternalConnection;
@@ -191,8 +203,16 @@
                 extras.putInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID,
                         externalConnection.getCallId());
             }
-            TelecomManager.from(mPhone.getContext()).addNewUnknownCall(
-                    PhoneUtils.makePstnPhoneAccountHandle(mPhone), extras);
+            PhoneAccountHandle handle = findCorrectPhoneAccountHandle();
+            if (handle == null) {
+                try {
+                    connection.hangup();
+                } catch (CallStateException e) {
+                    // connection already disconnected. Do nothing
+                }
+            } else {
+                TelecomManager.from(mPhone.getContext()).addNewUnknownCall(handle, extras);
+            }
         } else {
             Log.i(this, "swapped an old connection, new one is: %s", connection);
         }
@@ -209,8 +229,44 @@
             Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null);
             extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri);
         }
-        TelecomManager.from(mPhone.getContext()).addNewIncomingCall(
-                PhoneUtils.makePstnPhoneAccountHandle(mPhone), extras);
+        PhoneAccountHandle handle = findCorrectPhoneAccountHandle();
+        if (handle == null) {
+            try {
+                connection.hangup();
+            } catch (CallStateException e) {
+                // connection already disconnected. Do nothing
+            }
+        } else {
+            TelecomManager.from(mPhone.getContext()).addNewIncomingCall(handle, extras);
+        }
+    }
+
+    /**
+     * Returns the PhoneAccount associated with this {@code PstnIncomingCallNotifier}'s phone. On a
+     * device with No SIM or in airplane mode, it can return an Emergency-only PhoneAccount. If no
+     * PhoneAccount is registered with telecom, return null.
+     * @return A valid PhoneAccountHandle that is registered to Telecom or null if there is none
+     * registered.
+     */
+    private PhoneAccountHandle findCorrectPhoneAccountHandle() {
+        TelecomAccountRegistry telecomAccountRegistry = TelecomAccountRegistry.getInstance(null);
+        // Check to see if a the SIM PhoneAccountHandle Exists for the Call.
+        PhoneAccountHandle handle = PhoneUtils.makePstnPhoneAccountHandle(mPhone);
+        if (telecomAccountRegistry.hasAccountEntryForPhoneAccount(handle)) {
+            return handle;
+        }
+        // The PhoneAccountHandle does not match any PhoneAccount registered in Telecom.
+        // This is only known to happen if there is no SIM card in the device and the device
+        // receives an MT call while in ECM. Use the Emergency PhoneAccount to receive the account
+        // if it exists.
+        PhoneAccountHandle emergencyHandle =
+                PhoneUtils.makePstnPhoneAccountHandleWithPrefix(mPhone, "", true);
+        if(telecomAccountRegistry.hasAccountEntryForPhoneAccount(emergencyHandle)) {
+            Log.i(this, "Receiving MT call in ECM. Using Emergency PhoneAccount Instead.");
+            return emergencyHandle;
+        }
+        Log.w(this, "PhoneAccount not found.");
+        return null;
     }
 
     /**
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index c6a121b..3ed8356 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -477,7 +477,7 @@
      * @param handle The {@link PhoneAccountHandle}.
      * @return {@code True} if an entry exists.
      */
-    private boolean hasAccountEntryForPhoneAccount(PhoneAccountHandle handle) {
+    boolean hasAccountEntryForPhoneAccount(PhoneAccountHandle handle) {
         for (AccountEntry entry : mAccounts) {
             if (entry.getPhoneAccountHandle().equals(handle)) {
                 return true;
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 4844df9..311e58c 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -343,8 +343,17 @@
             PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
         Log.i(this, "onCreateIncomingConnection, request: " + request);
-
-        Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
+        // If there is an incoming emergency CDMA Call (while the phone is in ECBM w/ No SIM),
+        // make sure the PhoneAccount lookup retrieves the default Emergency Phone.
+        PhoneAccountHandle accountHandle = request.getAccountHandle();
+        boolean isEmergency = false;
+        if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
+                accountHandle.getId())) {
+            Log.i(this, "Emergency PhoneAccountHandle is being used for incoming call... " +
+                    "Treat as an Emergency Call.");
+            isEmergency = true;
+        }
+        Phone phone = getPhoneForAccount(accountHandle, isEmergency);
         if (phone == null) {
             return Connection.createFailedConnection(
                     DisconnectCauseUtil.toTelecomDisconnectCause(
@@ -391,8 +400,17 @@
     public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
             ConnectionRequest request) {
         Log.i(this, "onCreateUnknownConnection, request: " + request);
-
-        Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
+        // Use the registered emergency Phone if the PhoneAccountHandle is set to Telephony's
+        // Emergency PhoneAccount
+        PhoneAccountHandle accountHandle = request.getAccountHandle();
+        boolean isEmergency = false;
+        if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
+                accountHandle.getId())) {
+            Log.i(this, "Emergency PhoneAccountHandle is being used for unknown call... " +
+                    "Treat as an Emergency Call.");
+            isEmergency = true;
+        }
+        Phone phone = getPhoneForAccount(accountHandle, isEmergency);
         if (phone == null) {
             return Connection.createFailedConnection(
                     DisconnectCauseUtil.toTelecomDisconnectCause(
@@ -573,7 +591,6 @@
             int phoneId = SubscriptionController.getInstance().getPhoneId(subId);
             chosenPhone = PhoneFactory.getPhone(phoneId);
         }
-
         // If this is an emergency call and the phone we originally planned to make this call
         // with is not in service or was invalid, try to find one that is in service, using the
         // default as a last chance backup.