Merge "Don't set RTT property if original connection null"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b14ce89..7d32253 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -66,6 +66,11 @@
     <protected-broadcast android:name= "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
     <protected-broadcast android:name= "com.android.internal.provider.action.VOICEMAIL_SMS_RECEIVED" />
     <protected-broadcast android:name= "com.android.intent.isim_refresh" />
+    <protected-broadcast android:name= "com.android.ims.ACTION_RCS_SERVICE_AVAILABLE" />
+    <protected-broadcast android:name= "com.android.ims.ACTION_RCS_SERVICE_UNAVAILABLE" />
+    <protected-broadcast android:name= "com.android.ims.ACTION_RCS_SERVICE_DIED" />
+    <protected-broadcast android:name= "com.android.ims.ACTION_PRESENCE_CHANGED" />
+    <protected-broadcast android:name= "com.android.ims.ACTION_PUBLISH_STATUS_CHANGED" />
     <protected-broadcast android:name= "com.android.ims.IMS_SERVICE_UP" />
     <protected-broadcast android:name= "com.android.ims.IMS_SERVICE_DOWN" />
     <protected-broadcast android:name= "com.android.ims.IMS_INCOMING_CALL" />
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index 9d43d60..fd6510b 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -219,7 +219,8 @@
         case EXIT_ECM_BLOCK_OTHERS:
         case EXIT_ECM_DIALOG:
             CharSequence text = getDialogText(mEcmTimeout);
-            mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this)
+            mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this,
+                    android.R.style.Theme_DeviceDefault_Dialog_Alert)
                     .setIcon(R.drawable.ic_emergency_callback_mode)
                     .setTitle(R.string.phone_in_ecm_notification_title)
                     .setMessage(text)
@@ -247,7 +248,8 @@
             return mAlertDialog;
 
         case EXIT_ECM_IN_EMERGENCY_CALL_DIALOG:
-            mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this)
+            mAlertDialog = new AlertDialog.Builder(EmergencyCallbackModeExitDialog.this,
+                    android.R.style.Theme_DeviceDefault_Dialog_Alert)
                     .setIcon(R.drawable.ic_emergency_callback_mode)
                     .setTitle(R.string.phone_in_ecm_notification_title)
                     .setMessage(R.string.alert_dialog_in_ecm_call)
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index 1f406ea..bd0ae0f 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -720,13 +720,6 @@
             int max = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
             mActiveSubInfos = new ArrayList<SubscriptionInfo>(max);
 
-            IntentFilter intentFilter = new IntentFilter(
-                    TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
-            activity.registerReceiver(mPhoneChangeReceiver, intentFilter);
-
-            activity.getContentResolver().registerContentObserver(ENFORCE_MANAGED_URI, false,
-                    mDpcEnforcedContentObserver);
-
             Log.i(LOG_TAG, "onCreate:-");
         }
 
@@ -753,6 +746,10 @@
             @Override
             public void onReceive(Context context, Intent intent) {
                 Log.i(LOG_TAG, "onReceive:");
+                if (getActivity() == null || getContext() == null) {
+                    // Received broadcast and activity is in the process of being torn down.
+                    return;
+                }
                 // When the radio changes (ex: CDMA->GSM), refresh all options.
                 updateBody();
             }
@@ -766,6 +763,10 @@
             @Override
             public void onChange(boolean selfChange) {
                 Log.i(LOG_TAG, "DPC enforced onChange:");
+                if (getActivity() == null || getContext() == null) {
+                    // Received content change and activity is in the process of being torn down.
+                    return;
+                }
                 updateBody();
             }
         }
@@ -774,11 +775,6 @@
         public void onDestroy() {
             unbindNetworkQueryService();
             super.onDestroy();
-            if (getActivity() != null) {
-                getActivity().unregisterReceiver(mPhoneChangeReceiver);
-                getActivity().getContentResolver().unregisterContentObserver(
-                        mDpcEnforcedContentObserver);
-            }
         }
 
         @Override
@@ -815,6 +811,13 @@
 
             mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
 
+            final Context context = getActivity();
+            IntentFilter intentFilter = new IntentFilter(
+                    TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED);
+            context.registerReceiver(mPhoneChangeReceiver, intentFilter);
+            context.getContentResolver().registerContentObserver(ENFORCE_MANAGED_URI, false,
+                    mDpcEnforcedContentObserver);
+
             Log.i(LOG_TAG, "onResume:-");
 
         }
@@ -942,7 +945,8 @@
                 // in case it is currently something else. That is possible if user
                 // changed the setting while roaming and is now back to home network.
                 settingsNetworkMode = preferredNetworkMode;
-            } else if (isWorldMode()) {
+            } else if (carrierConfig.getBoolean(
+                    CarrierConfigManager.KEY_WORLD_PHONE_BOOL) == true) {
                 prefSet.removePreference(mButtonEnabledNetworks);
                 // set the listener for the mButtonPreferredNetworkMode list preference so we can issue
                 // change Preferred Network Mode.
@@ -1150,6 +1154,10 @@
 
             mSubscriptionManager
                     .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
+
+            final Context context = getActivity();
+            context.unregisterReceiver(mPhoneChangeReceiver);
+            context.getContentResolver().unregisterContentObserver(mDpcEnforcedContentObserver);
             if (DBG) log("onPause:-");
         }
 
@@ -1823,7 +1831,6 @@
             return super.onOptionsItemSelected(item);
         }
 
-        // TODO: b/80541766 this should use the carrier config, not the resource overlay
         private boolean isWorldMode() {
             boolean worldModeOn = false;
             final String configString = getResources().getString(R.string.config_world_mode);
diff --git a/src/com/android/phone/NetworkSelectListPreference.java b/src/com/android/phone/NetworkSelectListPreference.java
index 3ba1512..2a55839 100644
--- a/src/com/android/phone/NetworkSelectListPreference.java
+++ b/src/com/android/phone/NetworkSelectListPreference.java
@@ -227,7 +227,7 @@
         TelephonyManager telephonyManager = (TelephonyManager)
                 getContext().getSystemService(Context.TELEPHONY_SERVICE);
 
-        setSummary(telephonyManager.getNetworkOperatorName());
+        setSummary(telephonyManager.getNetworkOperatorName(mSubId));
 
         setOnPreferenceChangeListener(this);
     }
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 1666174..bda90a5 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -42,6 +42,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.WorkSource;
@@ -2735,6 +2736,45 @@
         return PhoneFactory.getImsResolver().isResolvingBinding();
     }
 
+    /**
+     * Sets the ImsService Package Name that Telephony will bind to.
+     *
+     * @param slotId the slot ID that the ImsService should bind for.
+     * @param isCarrierImsService true if the ImsService is the carrier override, false if the
+     *         ImsService is the device default ImsService.
+     * @param packageName The package name of the application that contains the ImsService to bind
+     *         to.
+     * @return true if setting the ImsService to bind to succeeded, false if it did not.
+     * @hide
+     */
+    public boolean setImsService(int slotId, boolean isCarrierImsService, String packageName) {
+        int[] subIds = SubscriptionManager.getSubId(slotId);
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID),
+                "setImsService");
+
+        return PhoneFactory.getImsResolver().overrideImsServiceConfiguration(slotId,
+                isCarrierImsService, packageName);
+    }
+
+    /**
+     * Return the ImsService configuration.
+     *
+     * @param slotId The slot that the ImsService is associated with.
+     * @param isCarrierImsService true, if the ImsService is a carrier override, false if it is
+     *         the device default.
+     * @return the package name of the ImsService configuration.
+     */
+    public String getImsService(int slotId, boolean isCarrierImsService) {
+        int[] subIds = SubscriptionManager.getSubId(slotId);
+        TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(mApp,
+                (subIds != null ? subIds[0] : SubscriptionManager.INVALID_SUBSCRIPTION_ID),
+                "getImsService");
+
+        return PhoneFactory.getImsResolver().getImsServiceConfiguration(slotId,
+                isCarrierImsService);
+    }
+
     public void setImsRegistrationState(boolean registered) {
         enforceModifyPermission();
         mPhone.setImsRegistrationState(registered);
@@ -3983,6 +4023,13 @@
         DumpsysHandler.dump(mPhone.getContext(), fd, writer, args);
     }
 
+    @Override
+    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver)
+            throws RemoteException {
+        (new TelephonyShellCommand(this)).exec(this, in, out, err, args, callback, resultReceiver);
+    }
+
     /**
      * Get aggregated video call data usage since boot.
      *
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
new file mode 100644
index 0000000..fce1086
--- /dev/null
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import android.os.RemoteException;
+import android.os.ShellCommand;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.internal.telephony.ITelephony;
+
+import java.io.PrintWriter;
+
+/**
+ * Takes actions based on the adb commands given by "adb shell cmd phone ...". Be careful, no
+ * permission checks have been done before onCommand was called. Make sure any commands processed
+ * here also contain the appropriate permissions checks.
+ */
+
+public class TelephonyShellCommand extends ShellCommand {
+
+    private static final String LOG_TAG = "TelephonyShellCommand";
+    // Don't commit with this true.
+    private static final boolean VDBG = true;
+
+    private static final String IMS_SUBCOMMAND = "ims";
+    private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
+    private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
+    private static final String IMS_ENABLE = "enable";
+    private static final String IMS_DISABLE = "disable";
+
+    // Take advantage of existing methods that already contain permissions checks when possible.
+    private final ITelephony mInterface;
+
+    public TelephonyShellCommand(ITelephony binder) {
+        mInterface = binder;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(null);
+        }
+
+        switch (cmd) {
+            case IMS_SUBCOMMAND: {
+                return handleImsCommand();
+            }
+            default: {
+                return handleDefaultCommands(cmd);
+            }
+        }
+    }
+
+    @Override
+    public void onHelp() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("Telephony Commands:");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println("  ims");
+        pw.println("    IMS Commands.");
+        onHelpIms();
+    }
+
+    private void onHelpIms() {
+        PrintWriter pw = getOutPrintWriter();
+        pw.println("IMS Commands:");
+        pw.println("  ims set-ims-service [-s SLOT_ID] (-c | -d) PACKAGE_NAME");
+        pw.println("    Sets the ImsService defined in PACKAGE_NAME to to be the bound");
+        pw.println("    ImsService. Options are:");
+        pw.println("      -s: the slot ID that the ImsService should be bound for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("      -c: Override the ImsService defined in the carrier configuration.");
+        pw.println("      -d: Override the ImsService defined in the device overlay.");
+        pw.println("  ims get-ims-service [-s SLOT_ID] [-c | -d]");
+        pw.println("    Gets the package name of the currently defined ImsService.");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID for the registered ImsService. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("      -c: The ImsService defined as the carrier configured ImsService.");
+        pw.println("      -c: The ImsService defined as the device default ImsService.");
+        pw.println("  ims enable [-s SLOT_ID]");
+        pw.println("    enables IMS for the SIM slot specified, or for the default voice SIM slot");
+        pw.println("    if none is specified.");
+        pw.println("  ims disable [-s SLOT_ID]");
+        pw.println("    disables IMS for the SIM slot specified, or for the default voice SIM");
+        pw.println("    slot if none is specified.");
+    }
+
+    private int handleImsCommand() {
+        String arg = getNextArg();
+        if (arg == null) {
+            onHelpIms();
+            return 0;
+        }
+
+        switch (arg) {
+            case IMS_SET_CARRIER_SERVICE: {
+                return handleImsSetServiceCommand();
+            }
+            case IMS_GET_CARRIER_SERVICE: {
+                return handleImsGetServiceCommand();
+            }
+            case IMS_ENABLE: {
+                return handleEnableIms();
+            }
+            case IMS_DISABLE: {
+                return handleDisableIms();
+            }
+        }
+
+        return -1;
+    }
+
+    // ims set-ims-service
+    private int handleImsSetServiceCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+        int slotId = SubscriptionManager.getDefaultVoicePhoneId();
+        Boolean isCarrierService = null;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-s": {
+                    try {
+                        slotId = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        errPw.println("ims set-ims-service requires an integer as a SLOT_ID.");
+                        return -1;
+                    }
+                    break;
+                }
+                case "-c": {
+                    isCarrierService = true;
+                    break;
+                }
+                case "-d": {
+                    isCarrierService = false;
+                    break;
+                }
+            }
+        }
+        // Mandatory param, either -c or -d
+        if (isCarrierService == null) {
+            errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set.");
+            return -1;
+        }
+
+        String packageName = getNextArg();
+
+        try {
+            if (packageName == null) {
+                packageName = "";
+            }
+            boolean result = mInterface.setImsService(slotId, isCarrierService, packageName);
+            if (VDBG) {
+                Log.v(LOG_TAG, "ims set-ims-service -s " + slotId + " "
+                        + (isCarrierService ? "-c " : "-d ") + packageName + ", result=" + result);
+            }
+            getOutPrintWriter().println(result);
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "ims set-ims-service -s " + slotId + " "
+                    + (isCarrierService ? "-c " : "-d ") + packageName + ", error"
+                    + e.getMessage());
+            errPw.println("Exception: " + e.getMessage());
+            return -1;
+        }
+        return 0;
+    }
+
+    // ims get-ims-service
+    private int handleImsGetServiceCommand() {
+        PrintWriter errPw = getErrPrintWriter();
+        int slotId = SubscriptionManager.getDefaultVoicePhoneId();
+        Boolean isCarrierService = null;
+
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-s": {
+                    try {
+                        slotId = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        errPw.println("ims set-ims-service requires an integer as a SLOT_ID.");
+                        return -1;
+                    }
+                    break;
+                }
+                case "-c": {
+                    isCarrierService = true;
+                    break;
+                }
+                case "-d": {
+                    isCarrierService = false;
+                    break;
+                }
+            }
+        }
+        // Mandatory param, either -c or -d
+        if (isCarrierService == null) {
+            errPw.println("ims set-ims-service requires either \"-c\" or \"-d\" to be set.");
+            return -1;
+        }
+
+        String result;
+        try {
+            result = mInterface.getImsService(slotId, isCarrierService);
+        } catch (RemoteException e) {
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "ims get-ims-service -s " + slotId + " "
+                    + (isCarrierService ? "-c " : "-d ") + ", returned: " + result);
+        }
+        getOutPrintWriter().println(result);
+        return 0;
+    }
+
+    private int handleEnableIms() {
+        int slotId = SubscriptionManager.getDefaultVoicePhoneId();
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-s": {
+                    try {
+                        slotId = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        getErrPrintWriter().println("ims enable requires an integer as a SLOT_ID.");
+                        return -1;
+                    }
+                    break;
+                }
+            }
+        }
+        try {
+            mInterface.enableIms(slotId);
+        } catch (RemoteException e) {
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "ims enable -s " + slotId);
+        }
+        return 0;
+    }
+
+    private int handleDisableIms() {
+        int slotId = SubscriptionManager.getDefaultVoicePhoneId();
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-s": {
+                    try {
+                        slotId = Integer.parseInt(getNextArgRequired());
+                    } catch (NumberFormatException e) {
+                        getErrPrintWriter().println(
+                                "ims disable requires an integer as a SLOT_ID.");
+                        return -1;
+                    }
+                    break;
+                }
+            }
+        }
+        try {
+            mInterface.disableIms(slotId);
+        } catch (RemoteException e) {
+            return -1;
+        }
+        if (VDBG) {
+            Log.v(LOG_TAG, "ims disable -s " + slotId);
+        }
+        return 0;
+    }
+}
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index b3369b5..c7b2096 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -1037,6 +1037,8 @@
             }
             connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
                     cause, e.getMessage()));
+            connection.clearOriginalConnection();
+            connection.destroy();
             return;
         }
 
@@ -1059,6 +1061,8 @@
             Log.d(this, "placeOutgoingConnection, phone.dial returned null");
             connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
                     telephonyDisconnectCause, "Connection is null"));
+            connection.clearOriginalConnection();
+            connection.destroy();
         } else {
             connection.setOriginalConnection(originalConnection);
         }