Merge "Solve flakiness of RadioOnStateListenerTest"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9e56ef8..16ad93a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -310,6 +310,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="CdmaCallForwardOptions"
+                android:label="@string/labelCF"
+                android:configChanges="orientation|screenSize|keyboardHidden"
+                android:theme="@style/DialerSettingsLight">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="GsmUmtsCallBarringOptions"
                 android:label="@string/labelCallBarring"
                 android:configChanges="orientation|screenSize|keyboardHidden"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a19f3df..b74b2ff 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -275,6 +275,14 @@
     <string name="disable_cfnrc_forbidden">Your carrier doesn\'t support disabling call forwarding when your phone is unreachable.</string>
     <string name="registration_cf_forbidden">Your carrier doesn\'t support call forwarding.</string>
 
+    <!-- Cdma Call waiting settings screen, setting option name -->
+    <string name="cdma_call_waiting">Turn on call waiting?</string>
+    <string name="enable_cdma_call_waiting_setting">During a call, you\'ll be notified about incoming calls</string>
+    <string name="enable_cdma_cw">Turn on</string>
+    <string name="disable_cdma_cw">Cancel</string>
+    <string name="cdma_call_waiting_in_ims_on">CDMA Call Waiting under IMS On</string>
+    <string name="cdma_call_waiting_in_ims_off">CDMA Call Waiting under IMS Off</string>
+
     <!-- Title of the progress dialog displayed while updating Call settings -->
     <string name="updating_title">Call settings</string>
     <!-- Toast in Call settings when asked to launch settings for a secondary user -->
@@ -2154,5 +2162,4 @@
     <string name="carrier_provisioning">Carrier Provisioning Info</string>
     <!-- Trigger Carrier Provisioning [CHAR LIMIT=NONE] -->
     <string name="trigger_carrier_provisioning">Trigger Carrier Provisioning</string>
-
 </resources>
diff --git a/res/xml/cdma_call_privacy.xml b/res/xml/cdma_call_privacy.xml
index 1aeeefe..a16a504 100644
--- a/res/xml/cdma_call_privacy.xml
+++ b/res/xml/cdma_call_privacy.xml
@@ -7,4 +7,14 @@
         android:title="@string/voice_privacy"
         android:persistent="false"
         android:summary="@string/voice_privacy_summary"/>
+
+    <PreferenceScreen
+        android:key="call_forwarding_key"
+        android:title="@string/labelCF"
+        android:persistent="false" />
+
+    <com.android.phone.CdmaCallWaitingPreference
+        android:key="call_waiting_key"
+        android:title="@string/labelCW"
+        android:persistent="false" />
 </PreferenceScreen>
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 3f57cae..9faffa1 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -396,11 +396,7 @@
             } else {
                 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                     prefSet.removePreference(fdnButton);
-
-                    if (!carrierConfig.getBoolean(
-                            CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
-                        addPreferencesFromResource(R.xml.cdma_call_privacy);
-                    }
+                    addPreferencesFromResource(R.xml.cdma_call_privacy);
                 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                     if (mPhone.getIccCard() == null || !mPhone.getIccCard().getIccFdnAvailable()) {
                         prefSet.removePreference(fdnButton);
diff --git a/src/com/android/phone/CallForwardEditPreference.java b/src/com/android/phone/CallForwardEditPreference.java
index 24dc020..8e0b685 100644
--- a/src/com/android/phone/CallForwardEditPreference.java
+++ b/src/com/android/phone/CallForwardEditPreference.java
@@ -122,7 +122,27 @@
         Log.d(LOG_TAG, "mButtonClicked=" + mButtonClicked + ", positiveResult=" + positiveResult);
         // Ignore this event if the user clicked the cancel button, or if the dialog is dismissed
         // without any button being pressed (back button press or click event outside the dialog).
-        if (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+        if (isUnknownStatus() && this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
+            int action = (mButtonClicked == DialogInterface.BUTTON_POSITIVE) ?
+                CommandsInterface.CF_ACTION_REGISTRATION :
+                CommandsInterface.CF_ACTION_DISABLE;
+            final String number = (action == CommandsInterface.CF_ACTION_DISABLE) ?
+                    "" : getPhoneNumber();
+
+            Log.d(LOG_TAG, "reason=" + reason + ", action=" + action + ", number=" + number);
+
+            // Display no forwarding number while we're waiting for confirmation.
+            setSummaryOff("");
+
+            mPhone.setCallForwardingOption(action,
+                    reason,
+                    number,
+                    mServiceClass,
+                    0,
+                    mHandler.obtainMessage(MyHandler.MESSAGE_SET_CF,
+                        action,
+                        MyHandler.MESSAGE_SET_CF));
+        } else if (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
             int action = (isToggled() || (mButtonClicked == DialogInterface.BUTTON_POSITIVE)) ?
                     CommandsInterface.CF_ACTION_REGISTRATION :
                     CommandsInterface.CF_ACTION_DISABLE;
@@ -194,6 +214,7 @@
             Log.i(LOG_TAG, "handleGetCFResponse: Overridding CF number");
         }
 
+        setUnknownStatus(callForwardInfo.status == CommandsInterface.SS_STATUS_UNKNOWN);
         setToggled(callForwardInfo.status == 1);
         boolean displayVoicemailNumber = false;
         if (TextUtils.isEmpty(callForwardInfo.number)) {
@@ -343,6 +364,7 @@
             AsyncResult ar = (AsyncResult) msg.obj;
 
             callForwardInfo = null;
+            boolean summaryOff = false;
             if (ar.exception != null) {
                 Log.d(LOG_TAG, "handleGetCFResponse: ar.exception=" + ar.exception);
                 if (ar.exception instanceof CommandException) {
@@ -372,6 +394,8 @@
                             CallForwardInfo info = cfInfoArray[i];
                             handleCallForwardResult(info);
 
+                            summaryOff = (info.status == CommandsInterface.SS_STATUS_UNKNOWN);
+
                             if (ar.userObj instanceof Throwable) {
                                 Log.d(LOG_TAG, "Skipped duplicated error dialog");
                                 continue;
@@ -430,7 +454,15 @@
             // Now whether or not we got a new number, reset our enabled
             // summary text since it may have been replaced by an empty
             // placeholder.
-            updateSummaryText();
+            // for CDMA, doesn't display summary.
+            if (summaryOff) {
+                setSummaryOff("");
+            } else {
+                // Now whether or not we got a new number, reset our enabled
+                // summary text since it may have been replaced by an empty
+                // placeholder.
+                updateSummaryText();
+            }
         }
 
         private void handleSetCFResponse(Message msg) {
@@ -439,6 +471,16 @@
                 Log.d(LOG_TAG, "handleSetCFResponse: ar.exception=" + ar.exception);
                 // setEnabled(false);
             }
+
+            if (ar.result != null) {
+                int arr = (int)ar.result;
+                if (arr == CommandsInterface.SS_STATUS_UNKNOWN) {
+                    Log.d(LOG_TAG, "handleSetCFResponse: no need to re get in CDMA");
+                    mTcpListener.onFinished(CallForwardEditPreference.this, false);
+                    return;
+                }
+            }
+
             Log.d(LOG_TAG, "handleSetCFResponse: re get");
             if (!mCallForwardByUssd) {
                 mPhone.getCallForwardingOption(reason, mServiceClass,
diff --git a/src/com/android/phone/CdmaCallForwardOptions.java b/src/com/android/phone/CdmaCallForwardOptions.java
new file mode 100644
index 0000000..a8d2e93
--- /dev/null
+++ b/src/com/android/phone/CdmaCallForwardOptions.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2020 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.app.ActionBar;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.PersistableBundle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.util.Log;
+import android.view.MenuItem;
+
+import com.android.internal.telephony.CallForwardInfo;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+
+import java.util.ArrayList;
+
+public class CdmaCallForwardOptions extends TimeConsumingPreferenceActivity {
+    private static final String LOG_TAG = "CdmaCallForwardOptions";
+
+    private static final String NUM_PROJECTION[] = {
+        android.provider.ContactsContract.CommonDataKinds.Phone.NUMBER
+    };
+
+    private static final String BUTTON_CFU_KEY   = "button_cfu_key";
+    private static final String BUTTON_CFB_KEY   = "button_cfb_key";
+    private static final String BUTTON_CFNRY_KEY = "button_cfnry_key";
+    private static final String BUTTON_CFNRC_KEY = "button_cfnrc_key";
+
+    private static final String KEY_TOGGLE = "toggle";
+    private static final String KEY_STATUS = "status";
+    private static final String KEY_NUMBER = "number";
+    private static final String KEY_ENABLE = "enable";
+
+    private CallForwardEditPreference mButtonCFU;
+    private CallForwardEditPreference mButtonCFB;
+    private CallForwardEditPreference mButtonCFNRy;
+    private CallForwardEditPreference mButtonCFNRc;
+
+    private final ArrayList<CallForwardEditPreference> mPreferences =
+            new ArrayList<CallForwardEditPreference> ();
+    private int mInitIndex= 0;
+
+    private boolean mFirstResume;
+    private Bundle mIcicle;
+    private Phone mPhone;
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+    private boolean mReplaceInvalidCFNumbers;
+    private boolean mCallForwardByUssd;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        addPreferencesFromResource(R.xml.callforward_options);
+
+        mSubscriptionInfoHelper = new SubscriptionInfoHelper(this, getIntent());
+        mSubscriptionInfoHelper.setActionBarTitle(
+                getActionBar(), getResources(), R.string.call_forwarding_settings_with_label);
+        mPhone = mSubscriptionInfoHelper.getPhone();
+
+        PersistableBundle b = null;
+        boolean supportCFNRc = true;
+        if (mSubscriptionInfoHelper.hasSubId()) {
+            b = PhoneGlobals.getInstance().getCarrierConfigForSubId(
+                    mSubscriptionInfoHelper.getSubId());
+        } else {
+            b = PhoneGlobals.getInstance().getCarrierConfig();
+        }
+        if (b != null) {
+            mReplaceInvalidCFNumbers = b.getBoolean(
+                    CarrierConfigManager.KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL);
+            mCallForwardByUssd = b.getBoolean(
+                    CarrierConfigManager.KEY_USE_CALL_FORWARDING_USSD_BOOL);
+            supportCFNRc = b.getBoolean(
+                    CarrierConfigManager.KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL);
+        }
+
+        PreferenceScreen prefSet = getPreferenceScreen();
+        mButtonCFU = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFU_KEY);
+        mButtonCFB = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFB_KEY);
+        mButtonCFNRy = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRY_KEY);
+        mButtonCFNRc = (CallForwardEditPreference) prefSet.findPreference(BUTTON_CFNRC_KEY);
+
+        mButtonCFU.setParentActivity(this, mButtonCFU.reason);
+        mButtonCFB.setParentActivity(this, mButtonCFB.reason);
+        mButtonCFNRy.setParentActivity(this, mButtonCFNRy.reason);
+        mButtonCFNRc.setParentActivity(this, mButtonCFNRc.reason);
+
+        mPreferences.add(mButtonCFU);
+        mPreferences.add(mButtonCFB);
+        mPreferences.add(mButtonCFNRy);
+
+        if (supportCFNRc) {
+            mPreferences.add(mButtonCFNRc);
+        } else {
+            // When CFNRc is not supported, mButtonCFNRc is grayed out from the menu.
+            // Default state for the preferences in this PreferenceScreen is disabled.
+            // Only preferences listed in the ArrayList mPreferences will be enabled.
+            // By not adding mButtonCFNRc to mPreferences it will be kept disabled.
+            Log.d(LOG_TAG, "onCreate: CFNRc is not supported, grey out the item.");
+        }
+
+        if (mCallForwardByUssd) {
+            //the call forwarding ussd command's behavior is similar to the call forwarding when
+            //unanswered,so only display the call forwarding when unanswered item.
+            prefSet.removePreference(mButtonCFU);
+            prefSet.removePreference(mButtonCFB);
+            prefSet.removePreference(mButtonCFNRc);
+            mPreferences.remove(mButtonCFU);
+            mPreferences.remove(mButtonCFB);
+            mPreferences.remove(mButtonCFNRc);
+            mButtonCFNRy.setDependency(null);
+        }
+
+        // we wait to do the initialization until onResume so that the
+        // TimeConsumingPreferenceActivity dialog can display as it
+        // relies on onResume / onPause to maintain its foreground state.
+
+        mFirstResume = true;
+        mIcicle = icicle;
+
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            // android.R.id.home will be triggered in onOptionsItemSelected()
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        if (mFirstResume) {
+            if (mIcicle == null) {
+                Log.d(LOG_TAG, "start to init ");
+                CallForwardEditPreference pref = mPreferences.get(mInitIndex);
+                pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+                pref.startCallForwardOptionsQuery();
+
+            } else {
+                mInitIndex = mPreferences.size();
+
+                for (CallForwardEditPreference pref : mPreferences) {
+                    Bundle bundle = mIcicle.getParcelable(pref.getKey());
+                    pref.setToggled(bundle.getBoolean(KEY_TOGGLE));
+                    pref.setEnabled(bundle.getBoolean(KEY_ENABLE));
+                    CallForwardInfo cf = new CallForwardInfo();
+                    cf.number = bundle.getString(KEY_NUMBER);
+                    cf.status = bundle.getInt(KEY_STATUS);
+                    pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+                    pref.restoreCallForwardInfo(cf);
+                }
+            }
+            mFirstResume = false;
+            mIcicle = null;
+        }
+    }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        for (CallForwardEditPreference pref : mPreferences) {
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(KEY_TOGGLE, pref.isToggled());
+            bundle.putBoolean(KEY_ENABLE, pref.isEnabled());
+            if (pref.callForwardInfo != null) {
+                bundle.putString(KEY_NUMBER, pref.callForwardInfo.number);
+                bundle.putInt(KEY_STATUS, pref.callForwardInfo.status);
+            }
+            outState.putParcelable(pref.getKey(), bundle);
+        }
+    }
+
+    @Override
+    public void onFinished(Preference preference, boolean reading) {
+        if (mInitIndex < mPreferences.size()-1 && !isFinishing()) {
+            mInitIndex++;
+            CallForwardEditPreference pref = mPreferences.get(mInitIndex);
+            pref.init(this, mPhone, mReplaceInvalidCFNumbers, mCallForwardByUssd);
+            pref.startCallForwardOptionsQuery();
+        }
+
+        super.onFinished(preference, reading);
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        Log.d(LOG_TAG, "onActivityResult: done");
+        if (resultCode != RESULT_OK) {
+            Log.d(LOG_TAG, "onActivityResult: contact picker result not OK.");
+            return;
+        }
+        Cursor cursor = null;
+        try {
+            cursor = getContentResolver().query(data.getData(),
+                NUM_PROJECTION, null, null, null);
+            if ((cursor == null) || (!cursor.moveToFirst())) {
+                Log.d(LOG_TAG, "onActivityResult: bad contact data, no results found.");
+                return;
+            }
+
+            switch (requestCode) {
+                case CommandsInterface.CF_REASON_UNCONDITIONAL:
+                    mButtonCFU.onPickActivityResult(cursor.getString(0));
+                    break;
+                case CommandsInterface.CF_REASON_BUSY:
+                    mButtonCFB.onPickActivityResult(cursor.getString(0));
+                    break;
+                case CommandsInterface.CF_REASON_NO_REPLY:
+                    mButtonCFNRy.onPickActivityResult(cursor.getString(0));
+                    break;
+                case CommandsInterface.CF_REASON_NOT_REACHABLE:
+                    mButtonCFNRc.onPickActivityResult(cursor.getString(0));
+                    break;
+                default:
+                    // TODO: may need exception here.
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final int itemId = item.getItemId();
+        if (itemId == android.R.id.home) {  // See ActionBar#setDisplayHomeAsUpEnabled()
+            CallFeaturesSetting.goUpToTopLevelSetting(this, mSubscriptionInfoHelper);
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/src/com/android/phone/CdmaCallOptions.java b/src/com/android/phone/CdmaCallOptions.java
index acfa496..8f4fa67 100644
--- a/src/com/android/phone/CdmaCallOptions.java
+++ b/src/com/android/phone/CdmaCallOptions.java
@@ -27,11 +27,13 @@
 
 import com.android.internal.telephony.PhoneConstants;
 
-public class CdmaCallOptions extends PreferenceActivity {
+public class CdmaCallOptions extends TimeConsumingPreferenceActivity {
     private static final String LOG_TAG = "CdmaCallOptions";
     private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
 
     private static final String BUTTON_VP_KEY = "button_voice_privacy_key";
+    private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+    private static final String CALL_WAITING_KEY = "call_waiting_key";
     private SwitchPreference mButtonVoicePrivacy;
 
     @Override
@@ -55,8 +57,15 @@
         if (subInfoHelper.getPhone().getPhoneType() != PhoneConstants.PHONE_TYPE_CDMA
                 || carrierConfig.getBoolean(CarrierConfigManager.KEY_VOICE_PRIVACY_DISABLE_UI_BOOL)) {
             // disable the entire screen
-            getPreferenceScreen().setEnabled(false);
+            mButtonVoicePrivacy.setEnabled(false);
         }
+
+        Preference callForwardingPref = getPreferenceScreen().findPreference(CALL_FORWARDING_KEY);
+        callForwardingPref.setIntent(subInfoHelper.getIntent(CdmaCallForwardOptions.class));
+
+        CdmaCallWaitingPreference callWaitingPref = (CdmaCallWaitingPreference)getPreferenceScreen()
+                                                     .findPreference(CALL_WAITING_KEY);
+        callWaitingPref.init(this, subInfoHelper.getPhone());
     }
 
     @Override
@@ -76,5 +85,4 @@
         }
         return false;
     }
-
 }
diff --git a/src/com/android/phone/CdmaCallWaitingPreference.java b/src/com/android/phone/CdmaCallWaitingPreference.java
new file mode 100644
index 0000000..4cda7ba
--- /dev/null
+++ b/src/com/android/phone/CdmaCallWaitingPreference.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 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 com.android.internal.telephony.CommandException;
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.Phone;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.util.AttributeSet;
+import android.util.Log;
+
+public class CdmaCallWaitingPreference extends Preference {
+    private static final String LOG_TAG = "CdmaCallWaitingPreference";
+    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
+
+    private int mButtonClicked;
+    private Context mContext;
+    private Phone mPhone;
+    private SubscriptionInfoHelper mSubscriptionInfoHelper;
+    private TimeConsumingPreferenceListener mTcpListener;
+    private MyHandler mHandler = new MyHandler();
+
+    public CdmaCallWaitingPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mContext = context;
+    }
+
+    public CdmaCallWaitingPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.preferenceStyle);
+    }
+
+    public CdmaCallWaitingPreference(Context context) {
+        this(context, null);
+    }
+
+    public void init(TimeConsumingPreferenceListener listener, Phone phone) {
+        mPhone = phone;
+        mTcpListener = listener;
+        Log.d(LOG_TAG, "phone id= " + mPhone.getPhoneId());
+        mPhone.getCallWaiting(mHandler.obtainMessage(MyHandler.MESSAGE_GET_CALL_WAITING,
+                    MyHandler.MESSAGE_GET_CALL_WAITING, MyHandler.MESSAGE_GET_CALL_WAITING));
+        if (mTcpListener != null) {
+            mTcpListener.onStarted(this, true);
+        }
+    }
+
+    @Override
+    public void onClick() {
+        super.onClick();
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+        builder.setTitle(mContext.getText(R.string.cdma_call_waiting));
+        builder.setMessage(mContext.getText(R.string.enable_cdma_call_waiting_setting));
+        builder.setPositiveButton(R.string.enable_cdma_cw, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int whichButton) {
+                mPhone.setCallWaiting(true,
+                        mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+                if (mTcpListener != null) {
+                    mTcpListener.onStarted(CdmaCallWaitingPreference.this, false);
+                }
+            }
+        });
+        builder.setNegativeButton(R.string.disable_cdma_cw, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int whichButton) {
+                mPhone.setCallWaiting(false,
+                        mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_WAITING));
+                if (mTcpListener != null) {
+                    mTcpListener.onStarted(CdmaCallWaitingPreference.this, false);
+                }
+            }
+        });
+        builder.create().show();
+    }
+
+    private class MyHandler extends Handler {
+        static final int MESSAGE_GET_CALL_WAITING = 0;
+        static final int MESSAGE_SET_CALL_WAITING = 1;
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_GET_CALL_WAITING:
+                    handleGetCallWaitingResponse(msg);
+                    break;
+                case MESSAGE_SET_CALL_WAITING:
+                    handleSetCallWaitingResponse(msg);
+                    break;
+            }
+        }
+
+        private void handleGetCallWaitingResponse(Message msg) {
+            AsyncResult ar = (AsyncResult) msg.obj;
+
+            if (mTcpListener != null) {
+                if (msg.arg2 == MESSAGE_SET_CALL_WAITING) {
+                    mTcpListener.onFinished(CdmaCallWaitingPreference.this, false);
+                } else {
+                    mTcpListener.onFinished(CdmaCallWaitingPreference.this, true);
+                }
+            }
+
+            if (ar.exception instanceof CommandException) {
+                if (DBG) {
+                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: CommandException=" +
+                            ar.exception);
+                }
+                if (mTcpListener != null) {
+                    mTcpListener.onException(CdmaCallWaitingPreference.this,
+                                             (CommandException)ar.exception);
+                }
+            } else if (ar.userObj instanceof Throwable || ar.exception != null) {
+                if (DBG) {
+                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: Exception" + ar.exception);
+                }
+                if (mTcpListener != null) {
+                    mTcpListener.onError(CdmaCallWaitingPreference.this,
+                                         TimeConsumingPreferenceActivity.RESPONSE_ERROR);
+                }
+            } else {
+                if (DBG) {
+                    Log.d(LOG_TAG, "handleGetCallWaitingResponse: CW state successfully queried.");
+                }
+                int[] cwArray = (int[])ar.result;
+                if (cwArray == null) {
+                    if (mTcpListener != null) {
+                        mTcpListener.onError(CdmaCallWaitingPreference.this,
+                                             TimeConsumingPreferenceActivity.RESPONSE_ERROR);
+                    }
+                    return;
+                }
+
+                try {
+                    if (cwArray[0] == CommandsInterface.SS_STATUS_UNKNOWN) {
+                        setSummary("");
+                    } else if(cwArray[0] == 1) {
+                        setSummary(mContext.getString(R.string.cdma_call_waiting_in_ims_on));
+                    } else if(cwArray[0] == 0) {
+                        setSummary(mContext.getString(R.string.cdma_call_waiting_in_ims_off));
+                    }
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    setSummary("");
+                    Log.e(LOG_TAG, "handleGetCallWaitingResponse: improper result: err ="
+                            + e.getMessage());
+                }
+            }
+        }
+
+        private void handleSetCallWaitingResponse(Message msg) {
+            AsyncResult ar = (AsyncResult) msg.obj;
+
+            if (ar.exception != null) {
+                if (DBG) {
+                    Log.d(LOG_TAG, "handleSetCallWaitingResponse: ar.exception=" + ar.exception);
+                }
+            }
+
+            if (ar.result != null) {
+                int arr = (int)ar.result;
+                if (arr == CommandsInterface.SS_STATUS_UNKNOWN) {
+                    Log.d(LOG_TAG, "handleSetCallWaitingResponse: no need to re get in CDMA");
+                    mTcpListener.onFinished(CdmaCallWaitingPreference.this, false);
+                    return;
+                }
+            }
+
+            if (DBG) Log.d(LOG_TAG, "handleSetCallWaitingResponse: re get");
+            mPhone.getCallWaiting(obtainMessage(MESSAGE_GET_CALL_WAITING,
+                        MESSAGE_SET_CALL_WAITING, MESSAGE_SET_CALL_WAITING, ar.exception));
+        }
+    }
+}
diff --git a/src/com/android/phone/EditPhoneNumberPreference.java b/src/com/android/phone/EditPhoneNumberPreference.java
index 74b8a45..505c284 100644
--- a/src/com/android/phone/EditPhoneNumberPreference.java
+++ b/src/com/android/phone/EditPhoneNumberPreference.java
@@ -16,6 +16,9 @@
 
 package com.android.phone;
 
+import static android.view.View.LAYOUT_DIRECTION_LOCALE;
+import static android.view.View.TEXT_DIRECTION_LOCALE;
+
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.Context;
@@ -37,6 +40,8 @@
 import android.widget.ImageButton;
 import android.widget.TextView;
 
+import com.android.internal.telephony.CommandsInterface;
+
 public class EditPhoneNumberPreference extends EditTextPreference {
 
     //allowed modes for this preference.
@@ -90,6 +95,7 @@
     private String mPhoneNumber;
     private boolean mChecked;
 
+    private boolean mIsUnknownStatus;
 
     /**
      * Interface for the dialog closed listener, related to
@@ -209,7 +215,9 @@
                 }
             }
             editText.setText(BidiFormatter.getInstance().unicodeWrap(
-                    mPhoneNumber, TextDirectionHeuristics.LTR));
+                    mPhoneNumber, TextDirectionHeuristics.LOCALE));
+            editText.setTextDirection(TEXT_DIRECTION_LOCALE);
+            editText.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
             editText.setMovementMethod(ArrowKeyMovementMethod.getInstance());
             editText.setKeyListener(DialerKeyListener.getInstance());
             editText.setOnFocusChangeListener(mDialogFocusChangeListener);
@@ -254,7 +262,13 @@
         // displayed, since there is no need to hide the edittext
         // field anymore.
         if (mConfirmationMode == CM_ACTIVATION) {
-            if (mChecked) {
+            if (mIsUnknownStatus) {
+                builder.setPositiveButton(mEnableText, this);
+                builder.setNeutralButton(mDisableText, this);
+                if (mPrefId == CommandsInterface.CF_REASON_ALL) {
+                    builder.setPositiveButton(null, null);
+                }
+            } else if (mChecked) {
                 builder.setPositiveButton(mChangeNumberText, this);
                 builder.setNeutralButton(mDisableText, this);
             } else {
@@ -310,7 +324,8 @@
     @Override
     public void onClick(DialogInterface dialog, int which) {
         // The neutral button (button3) is always the toggle.
-        if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL)) {
+        if ((mConfirmationMode == CM_ACTIVATION) && (which == DialogInterface.BUTTON_NEUTRAL)
+             && !mIsUnknownStatus) {
             //flip the toggle if we are in the correct mode.
             setToggled(!isToggled());
         }
@@ -499,4 +514,12 @@
     public void showPhoneNumberDialog() {
         showDialog(null);
     }
+
+    public void setUnknownStatus(boolean isUnknown) {
+        mIsUnknownStatus = isUnknown;
+    }
+
+    public boolean isUnknownStatus() {
+        return mIsUnknownStatus;
+    }
 }
diff --git a/src/com/android/phone/EmergencyCallbackModeService.java b/src/com/android/phone/EmergencyCallbackModeService.java
index c7e4cc7..6b247bd 100644
--- a/src/com/android/phone/EmergencyCallbackModeService.java
+++ b/src/com/android/phone/EmergencyCallbackModeService.java
@@ -30,6 +30,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.UserHandle;
 import android.sysprop.TelephonyProperties;
 import android.telephony.TelephonyManager;
 import android.util.Log;
@@ -106,7 +107,8 @@
             mPhone.unregisterForEcmTimerReset(mHandler);
 
             // Cancel the notification and timer
-            mNotificationManager.cancel(R.string.phone_in_ecm_notification_title);
+            mNotificationManager.cancelAsUser(null, R.string.phone_in_ecm_notification_title,
+                    UserHandle.ALL);
             mTimer.cancel();
         }
     }
@@ -221,7 +223,8 @@
         builder.setChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
 
         // Show notification
-        mNotificationManager.notify(R.string.phone_in_ecm_notification_title, builder.build());
+        mNotificationManager.notifyAsUser(null, R.string.phone_in_ecm_notification_title,
+                builder.build(), UserHandle.ALL);
     }
 
     /**
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index fa996a6..59a05c7 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -2285,16 +2285,7 @@
     }
 
     @Override
-    public String getNetworkCountryIsoForPhone(int phoneId, String callingPackage,
-            String callingFeatureId) {
-        if (!TextUtils.isEmpty(callingPackage)) {
-            final int subId = mSubscriptionController.getSubIdUsingPhoneId(phoneId);
-            if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
-                    callingFeatureId, "getNetworkCountryIsoForPhone")) {
-                return "";
-            }
-        }
-
+    public String getNetworkCountryIsoForPhone(int phoneId) {
         // Reporting the correct network country is ambiguous when IWLAN could conflict with
         // registered cell info, so return a NULL country instead.
         final long identity = Binder.clearCallingIdentity();
@@ -3053,14 +3044,15 @@
     }
 
     @Override
-    public void sendVisualVoicemailSmsForSubscriber(String callingPackage, int subId,
-            String number, int port, String text, PendingIntent sentIntent) {
+    public void sendVisualVoicemailSmsForSubscriber(String callingPackage,
+            String callingAttributionTag, int subId, String number, int port, String text,
+            PendingIntent sentIntent) {
         mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
         enforceVisualVoicemailPackage(callingPackage, subId);
         enforceSendSmsPermission();
         SmsController smsController = PhoneFactory.getSmsController();
-        smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, subId, number, port, text,
-                sentIntent);
+        smsController.sendVisualVoicemailSmsForSubscriber(callingPackage, callingAttributionTag,
+                subId, number, port, text, sentIntent);
     }
 
     /**
diff --git a/src/com/android/phone/settings/SuppServicesUiUtil.java b/src/com/android/phone/settings/SuppServicesUiUtil.java
index 4e9841f..4f1a79f 100644
--- a/src/com/android/phone/settings/SuppServicesUiUtil.java
+++ b/src/com/android/phone/settings/SuppServicesUiUtil.java
@@ -84,7 +84,7 @@
                 .create();
     }
 
-    private static String makeMessage(Context context, String preferenceKey, Phone phone) {
+    public static String makeMessage(Context context, String preferenceKey, Phone phone) {
         String message = "";
         int simSlot = (phone.getPhoneId() == 0) ? 1 : 2;
         String suppServiceName = getSuppServiceName(context, preferenceKey);
diff --git a/src/com/android/services/telephony/MmiCodeUtil.java b/src/com/android/services/telephony/MmiCodeUtil.java
new file mode 100644
index 0000000..d208ec3
--- /dev/null
+++ b/src/com/android/services/telephony/MmiCodeUtil.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 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.services.telephony;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class MmiCodeUtil {
+    //***** Constants
+
+    // Supp Service codes from TS 22.030 Annex B
+
+    //Called line presentation
+    static final String SC_CLIP    = "30";
+    static final String SC_CLIR    = "31";
+
+    // Call Forwarding
+    static final String SC_CFU     = "21";
+    static final String SC_CFB     = "67";
+    static final String SC_CFNRy   = "61";
+    static final String SC_CFNR    = "62";
+
+    static final String SC_CF_All = "002";
+    static final String SC_CF_All_Conditional = "004";
+
+    // Call Waiting
+    static final String SC_WAIT     = "43";
+
+    // Call Barring
+    static final String SC_BAOC         = "33";
+    static final String SC_BAOIC        = "331";
+    static final String SC_BAOICxH      = "332";
+    static final String SC_BAIC         = "35";
+    static final String SC_BAICr        = "351";
+
+    static final String SC_BA_ALL       = "330";
+    static final String SC_BA_MO        = "333";
+    static final String SC_BA_MT        = "353";
+
+    // Supp Service Password registration
+    static final String SC_PWD          = "03";
+
+    // PIN/PIN2/PUK/PUK2
+    static final String SC_PIN          = "04";
+    static final String SC_PIN2         = "042";
+    static final String SC_PUK          = "05";
+    static final String SC_PUK2         = "052";
+
+    // See TS 22.030 6.5.2 "Structure of the MMI"
+
+    static Pattern sPatternSuppService = Pattern.compile(
+        "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
+/*       1  2                    3          4  5       6   7         8    9     10  11             12
+
+         1 = Full string up to and including #
+         2 = action (activation/interrogation/registration/erasure)
+         3 = service code
+         5 = SIA
+         7 = SIB
+         9 = SIC
+         10 = dialing number
+*/
+
+    static final int MATCH_GROUP_SERVICE_CODE = 3;
+
+    public static final String BUTTON_CLIR_KEY  = "button_clir_key";
+    public static final String BUTTON_CW_KEY    = "button_cw_key";
+    public static final String CALL_FORWARDING_KEY = "call_forwarding_key";
+    public static final String CALL_BARRING_KEY = "call_barring_key";
+
+    //***** Public Class methods
+    public static String getMmiServiceCode(String dialString) {
+        Matcher m;
+        String ret = null;
+
+        m = sPatternSuppService.matcher(dialString);
+
+        if (m.matches()) {
+            ret = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
+        }
+
+        return ret;
+    }
+
+    private static String makeEmptyNull(String s) {
+        if (s != null && s.length() == 0) return null;
+
+        return s;
+    }
+
+    static boolean isServiceCodeCallForwarding(String sc) {
+        return sc != null &&
+                (sc.equals(SC_CFU)
+                || sc.equals(SC_CFB) || sc.equals(SC_CFNRy)
+                || sc.equals(SC_CFNR) || sc.equals(SC_CF_All)
+                || sc.equals(SC_CF_All_Conditional));
+    }
+
+    static boolean isServiceCodeCallBarring(String sc) {
+        return sc != null &&
+            (sc.equals(SC_BAOC)
+             || sc.equals(SC_BAOIC) || sc.equals(SC_BAOICxH)
+             || sc.equals(SC_BAIC) || sc.equals(SC_BAICr)
+             || sc.equals(SC_BA_ALL) || sc.equals(SC_BA_MO)
+             || sc.equals(SC_BA_MT));
+    }
+
+    static boolean isPinPukCommand(String sc) {
+        return sc != null && (sc.equals(SC_PIN) || sc.equals(SC_PIN2)
+                              || sc.equals(SC_PUK) || sc.equals(SC_PUK2));
+     }
+
+    public static String getSuppServiceKey(String dialString) {
+        String sc = getMmiServiceCode(dialString);
+        if (sc != null && sc.equals(SC_CLIP)) {
+            return "";
+        } else if (sc != null && sc.equals(SC_CLIR)) {
+            return BUTTON_CLIR_KEY;
+        } else if (isServiceCodeCallForwarding(sc)) {
+            return CALL_FORWARDING_KEY;
+        } else if (isServiceCodeCallBarring(sc)) {
+            return CALL_BARRING_KEY;
+        } else if (sc != null && sc.equals(SC_PWD)) {
+            return "";
+        } else if (sc != null && sc.equals(SC_WAIT)) {
+            return BUTTON_CW_KEY;
+        } else if (isPinPukCommand(sc)) {
+            return "";
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
index 116c61d..25ac220 100644
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ b/src/com/android/services/telephony/RadioOnHelper.java
@@ -77,7 +77,8 @@
      * RadioOnHelper's handler (thus ensuring that the rest of the sequence is entirely
      * serialized, and runs on the main looper.)
      */
-    public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback) {
+    public void triggerRadioOnAndListen(RadioOnStateListener.Callback callback,
+            boolean forEmergencyCall, Phone phoneForEmergencyCall) {
         setupListeners();
         mCallback = callback;
         mInProgressListeners.clear();
@@ -89,16 +90,17 @@
             }
 
             mInProgressListeners.add(mListeners.get(i));
-            mListeners.get(i).waitForRadioOn(phone, this);
+            mListeners.get(i).waitForRadioOn(phone, this, forEmergencyCall,
+                    forEmergencyCall && phone == phoneForEmergencyCall);
         }
 
-        powerOnRadio();
+        powerOnRadio(forEmergencyCall, phoneForEmergencyCall);
     }
     /**
      * Attempt to power on the radio (i.e. take the device out of airplane mode). We'll eventually
      * get an onServiceStateChanged() callback when the radio successfully comes up.
      */
-    private void powerOnRadio() {
+    private void powerOnRadio(boolean forEmergencyCall, Phone phoneForEmergencyCall) {
 
         // If airplane mode is on, we turn it off the same way that the Settings activity turns it
         // off.
@@ -112,7 +114,7 @@
 
             for (Phone phone : PhoneFactory.getPhones()) {
                 Log.d(this, "powerOnRadio, enabling Radio");
-                phone.setRadioPower(true);
+                phone.setRadioPower(true, forEmergencyCall, phone == phoneForEmergencyCall, false);
             }
 
             // Post the broadcast intend for change in airplane mode
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 53f97f0..93e1e3c 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -70,7 +70,10 @@
                         Phone phone = (Phone) args.arg1;
                         RadioOnStateListener.Callback callback =
                                 (RadioOnStateListener.Callback) args.arg2;
-                        startSequenceInternal(phone, callback);
+                        boolean forEmergencyCall = (boolean) args.arg3;
+                        boolean isSelectedPhoneForEmergencyCall = (boolean) args.arg4;
+                        startSequenceInternal(phone, callback, forEmergencyCall,
+                                isSelectedPhoneForEmergencyCall);
                     } finally {
                         args.recycle();
                     }
@@ -97,6 +100,10 @@
 
     private Callback mCallback;  // The callback to notify upon completion.
     private Phone mPhone;  // The phone that will attempt to place the call.
+    private boolean mForEmergencyCall; // Whether radio is being turned on for emergency call.
+    // Whether this phone is selected to place emergency call. Can be true only if
+    // mForEmergencyCall is true.
+    private boolean mSelectedPhoneForEmergencyCall;
     private int mNumRetriesSoFar;
 
     /**
@@ -113,7 +120,8 @@
      * RadioOnStateListener's handler (thus ensuring that the rest of the sequence is entirely
      * serialized, and runs only on the handler thread.)
      */
-    public void waitForRadioOn(Phone phone, Callback callback) {
+    public void waitForRadioOn(Phone phone, Callback callback,
+            boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
         Log.d(this, "waitForRadioOn: Phone " + phone.getPhoneId());
 
         if (mPhone != null) {
@@ -124,6 +132,8 @@
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = phone;
         args.arg2 = callback;
+        args.arg3 = forEmergencyCall;
+        args.arg4 = isSelectedPhoneForEmergencyCall;
         mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
     }
 
@@ -132,7 +142,8 @@
      *
      * @see #waitForRadioOn
      */
-    private void startSequenceInternal(Phone phone, Callback callback) {
+    private void startSequenceInternal(Phone phone, Callback callback,
+            boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall) {
         Log.d(this, "startSequenceInternal: Phone " + phone.getPhoneId());
 
         // First of all, clean up any state left over from a prior RadioOn call sequence. This
@@ -142,6 +153,8 @@
 
         mPhone = phone;
         mCallback = callback;
+        mForEmergencyCall = forEmergencyCall;
+        mSelectedPhoneForEmergencyCall = isSelectedPhoneForEmergencyCall;
 
         registerForServiceStateChanged();
         // Register for RADIO_OFF to handle cases where emergency call is dialed before
@@ -234,7 +247,8 @@
                 cleanup();
             } else {
                 Log.d(this, "Trying (again) to turn on the radio.");
-                mPhone.setRadioPower(true);
+                mPhone.setRadioPower(true, mForEmergencyCall, mSelectedPhoneForEmergencyCall,
+                        false);
                 startRetryTimer();
             }
         }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 854d935..665bf22 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -17,10 +17,13 @@
 package com.android.services.telephony;
 
 import android.annotation.NonNull;
+import android.app.AlertDialog;
+import android.app.Dialog;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.Uri;
@@ -46,6 +49,7 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.text.TextUtils;
 import android.util.Pair;
+import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Call;
@@ -65,6 +69,7 @@
 import com.android.phone.MMIDialogActivity;
 import com.android.phone.PhoneUtils;
 import com.android.phone.R;
+import com.android.phone.settings.SuppServicesUiUtil;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -430,6 +435,14 @@
     }
 
     /**
+     * Overrides radioOnHelper for testing.
+     */
+    @VisibleForTesting
+    public void setRadioOnHelper(RadioOnHelper radioOnHelper) {
+        mRadioOnHelper = radioOnHelper;
+    }
+
+    /**
      * Overrides PhoneSwitcher dependencies for testing.
      */
     @VisibleForTesting
@@ -772,13 +785,16 @@
         boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
                 || isRadioPowerDownOnBluetooth();
 
+        // Get the right phone object from the account data passed in.
+        final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
+                /* Note: when not an emergency, handle can be null for unknown callers */
+                handle == null ? null : handle.getSchemeSpecificPart());
+
         if (needToTurnOnRadio) {
             final Uri resultHandle = handle;
-            // By default, Connection based on the default Phone, since we need to return to Telecom
-            // now.
-            final int originalPhoneType = mPhoneFactoryProxy.getDefaultPhone().getPhoneType();
+            final int originalPhoneType = phone.getPhoneType();
             final Connection resultConnection = getTelephonyConnection(request, numberToDial,
-                    isEmergencyNumber, resultHandle, PhoneFactory.getDefaultPhone());
+                    isEmergencyNumber, resultHandle, phone);
             if (mRadioOnHelper == null) {
                 mRadioOnHelper = new RadioOnHelper(this);
             }
@@ -786,7 +802,7 @@
                 @Override
                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
                     handleOnComplete(isRadioReady, isEmergencyNumber, resultConnection, request,
-                            numberToDial, resultHandle, originalPhoneType);
+                            numberToDial, resultHandle, originalPhoneType, phone);
                 }
 
                 @Override
@@ -815,7 +831,7 @@
                                 || serviceState == ServiceState.STATE_IN_SERVICE;
                     }
                 }
-            });
+            }, isEmergencyNumber && !isTestEmergencyNumber, phone);
             // Return the still unconnected GsmConnection and wait for the Radios to boot before
             // connecting it to the underlying Phone.
             return resultConnection;
@@ -831,10 +847,6 @@
                                 "Add call restricted due to ongoing video call"));
             }
 
-            // Get the right phone object from the account data passed in.
-            final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
-                    /* Note: when not an emergency, handle can be null for unknown callers */
-                    handle == null ? null : handle.getSchemeSpecificPart());
             if (!isEmergencyNumber) {
                 final Connection resultConnection = getTelephonyConnection(request, numberToDial,
                         false, handle, phone);
@@ -911,18 +923,21 @@
      */
     private void handleOnComplete(boolean isRadioReady, boolean isEmergencyNumber,
             Connection originalConnection, ConnectionRequest request, String numberToDial,
-            Uri handle, int originalPhoneType) {
+            Uri handle, int originalPhoneType, Phone phone) {
         // Make sure the Call has not already been canceled by the user.
         if (originalConnection.getState() == Connection.STATE_DISCONNECTED) {
             Log.i(this, "Call disconnected before the outgoing call was placed. Skipping call "
                     + "placement.");
+            if (isEmergencyNumber) {
+                // If call is already canceled by the user, notify modem to exit emergency call
+                // mode by sending radio on with forEmergencyCall=false.
+                for (Phone curPhone : mPhoneFactoryProxy.getPhones()) {
+                    curPhone.setRadioPower(true, false, false, true);
+                }
+            }
             return;
         }
-        // Get the right phone object since the radio has been turned on successfully.
         if (isRadioReady) {
-            final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
-                    /* Note: when not an emergency, handle can be null for unknown callers */
-                    handle == null ? null : handle.getSchemeSpecificPart());
             if (!isEmergencyNumber) {
                 adjustAndPlaceOutgoingConnection(phone, originalConnection, request, numberToDial,
                         handle, originalPhoneType, false);
@@ -1640,6 +1655,12 @@
             TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
         String number = connection.getAddress().getSchemeSpecificPart();
 
+        if (showDataDialog(phone, number)) {
+            connection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
+                        android.telephony.DisconnectCause.DIALED_MMI, "UT is not available"));
+            return;
+        }
+
         com.android.internal.telephony.Connection originalConnection = null;
         try {
             if (phone != null) {
@@ -2347,6 +2368,78 @@
         }
     }
 
+    private boolean showDataDialog(Phone phone, String number) {
+        boolean ret = false;
+        final Context context = getApplicationContext();
+        String suppKey = MmiCodeUtil.getSuppServiceKey(number);
+        if (suppKey != null) {
+            boolean clirOverUtPrecautions = false;
+            boolean cfOverUtPrecautions = false;
+            boolean cbOverUtPrecautions = false;
+            boolean cwOverUtPrecautions = false;
+
+            CarrierConfigManager cfgManager = (CarrierConfigManager)
+                phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+            if (cfgManager != null) {
+                clirOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+                    .getBoolean(CarrierConfigManager.KEY_CALLER_ID_OVER_UT_WARNING_BOOL);
+                cfOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+                    .getBoolean(CarrierConfigManager.KEY_CALL_FORWARDING_OVER_UT_WARNING_BOOL);
+                cbOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+                    .getBoolean(CarrierConfigManager.KEY_CALL_BARRING_OVER_UT_WARNING_BOOL);
+                cwOverUtPrecautions = cfgManager.getConfigForSubId(phone.getSubId())
+                    .getBoolean(CarrierConfigManager.KEY_CALL_WAITING_OVER_UT_WARNING_BOOL);
+            }
+
+            boolean isSsOverUtPrecautions = SuppServicesUiUtil
+                .isSsOverUtPrecautions(context, phone);
+            if (isSsOverUtPrecautions) {
+                boolean showDialog = false;
+                if (suppKey == MmiCodeUtil.BUTTON_CLIR_KEY && clirOverUtPrecautions) {
+                    showDialog = true;
+                } else if (suppKey == MmiCodeUtil.CALL_FORWARDING_KEY && cfOverUtPrecautions) {
+                    showDialog = true;
+                } else if (suppKey == MmiCodeUtil.CALL_BARRING_KEY && cbOverUtPrecautions) {
+                    showDialog = true;
+                } else if (suppKey == MmiCodeUtil.BUTTON_CW_KEY && cwOverUtPrecautions) {
+                    showDialog = true;
+                }
+
+                if (showDialog) {
+                    Log.d(this, "Creating UT Data enable dialog");
+                    String message = SuppServicesUiUtil.makeMessage(context, suppKey, phone);
+                    AlertDialog.Builder builder = new AlertDialog.Builder(context);
+                    DialogInterface.OnClickListener networkSettingsClickListener =
+                            new Dialog.OnClickListener() {
+                                @Override
+                                public void onClick(DialogInterface dialog, int which) {
+                                    Intent intent = new Intent(Intent.ACTION_MAIN);
+                                    ComponentName mobileNetworkSettingsComponent
+                                        = new ComponentName(
+                                                context.getString(
+                                                    R.string.mobile_network_settings_package),
+                                                context.getString(
+                                                    R.string.mobile_network_settings_class));
+                                    intent.setComponent(mobileNetworkSettingsComponent);
+                                    context.startActivity(intent);
+                                }
+                            };
+                    Dialog dialog = builder.setMessage(message)
+                        .setNeutralButton(context.getResources().getString(
+                                R.string.settings_label),
+                                networkSettingsClickListener)
+                        .setPositiveButton(context.getResources().getString(
+                                R.string.supp_service_over_ut_precautions_dialog_dismiss), null)
+                        .create();
+                    dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+                    dialog.show();
+                    ret = true;
+                }
+            }
+        }
+        return ret;
+    }
+
     /**
      * Adds a {@link Conference} to the telephony ConnectionService and registers a listener for
      * changes to the conference.  Should be used instead of {@link #addConference(Conference)}.
diff --git a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
index 073aa2c..25df848 100644
--- a/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
+++ b/tests/src/com/android/services/telephony/RadioOnStateListenerTest.java
@@ -83,7 +83,7 @@
     @SmallTest
     public void testRegisterForCallback() {
         mMockPhone.mCi = mMockCi;
-        mListener.waitForRadioOn(mMockPhone, mCallback);
+        mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
 
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
@@ -105,10 +105,11 @@
     public void testPhoneChangeState_OkToCallTrue() {
         ServiceState state = new ServiceState();
         state.setState(ServiceState.STATE_IN_SERVICE);
+        when(mMockPhone.getServiceState()).thenReturn(state);
         when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
         when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(true);
         mMockPhone.mCi = mMockCi;
-        mListener.waitForRadioOn(mMockPhone, mCallback);
+        mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
         mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
@@ -132,7 +133,7 @@
         when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
         when(mMockPhone.getServiceState()).thenReturn(state);
         mMockPhone.mCi = mMockCi;
-        mListener.waitForRadioOn(mMockPhone, mCallback);
+        mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
         waitForHandlerAction(mListener.getHandler(), TIMEOUT_MS);
 
         mListener.getHandler().obtainMessage(RadioOnStateListener.MSG_SERVICE_STATE_CHANGED,
@@ -161,10 +162,32 @@
 
         // Wait for the timer to expire and check state manually in onRetryTimeout
         mMockPhone.mCi = mMockCi;
-        mListener.waitForRadioOn(mMockPhone, mCallback);
+        mListener.waitForRadioOn(mMockPhone, mCallback, false, false);
         waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
 
         verify(mCallback).onComplete(eq(mListener), eq(false));
-        verify(mMockPhone, times(2)).setRadioPower(eq(true));
+        verify(mMockPhone, times(2)).setRadioPower(eq(true),
+                eq(false), eq(false), eq(false));
+    }
+
+    @Test
+    @SmallTest
+    public void testTimeout_RetryFailure_ForEmergency() {
+        ServiceState state = new ServiceState();
+        state.setState(ServiceState.STATE_POWER_OFF);
+        when(mMockPhone.getState()).thenReturn(PhoneConstants.State.IDLE);
+        when(mMockPhone.getServiceState()).thenReturn(state);
+        when(mCallback.isOkToCall(eq(mMockPhone), anyInt())).thenReturn(false);
+        mListener.setTimeBetweenRetriesMillis(0/*ms*/);
+        mListener.setMaxNumRetries(2);
+
+        // Wait for the timer to expire and check state manually in onRetryTimeout
+        mMockPhone.mCi = mMockCi;
+        mListener.waitForRadioOn(mMockPhone, mCallback, true, true);
+        waitForHandlerActionDelayed(mListener.getHandler(), TIMEOUT_MS, TIMEOUT_MS /*delay*/);
+
+        verify(mCallback).onComplete(eq(mListener), eq(false));
+        verify(mMockPhone, times(2)).setRadioPower(eq(true),
+                eq(true), eq(true), eq(false));
     }
 }
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index 6e11e51..2060e6f 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,7 +16,10 @@
 
 package com.android.services.telephony;
 
+import static com.android.internal.telephony.RILConstants.GSM_PHONE;
+
 import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
@@ -26,6 +29,7 @@
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -58,6 +62,7 @@
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneSwitcher;
+import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
 import com.android.internal.telephony.gsm.SuppServiceNotification;
 
@@ -93,6 +98,7 @@
     private static final PhoneAccountHandle PHONE_ACCOUNT_HANDLE_2 = new PhoneAccountHandle(
             TEST_COMPONENT_NAME, TEST_ACCOUNT_ID2);
     private static final Uri TEST_ADDRESS = Uri.parse("tel:+16505551212");
+    private android.telecom.Connection mConnection;
 
     @Mock TelephonyConnectionService.TelephonyManagerProxy mTelephonyManagerProxy;
     @Mock TelephonyConnectionService.SubscriptionManagerProxy mSubscriptionManagerProxy;
@@ -106,6 +112,8 @@
     @Mock Handler mMockHandler;
     @Mock EmergencyNumberTracker mEmergencyNumberTracker;
     @Mock PhoneSwitcher mPhoneSwitcher;
+    @Mock RadioOnHelper mRadioOnHelper;
+    @Mock ServiceStateTracker mSST;
 
     private static class TestTelephonyConnectionService extends TelephonyConnectionService {
 
@@ -135,6 +143,8 @@
         doReturn(false).when(mDeviceState).shouldCheckSimStateBeforeOutgoingCall(any());
         mTestConnectionService.setPhoneSwitcherProxy(mPhoneSwitcherProxy);
         doReturn(mPhoneSwitcher).when(mPhoneSwitcherProxy).getPhoneSwitcher();
+        when(mPhoneNumberUtilsProxy.convertToEmergencyNumber(any(), anyString()))
+                .thenAnswer(invocation -> invocation.getArgument(1));
         mTestConnectionService.setPhoneNumberUtilsProxy(mPhoneNumberUtilsProxy);
         mTestConnectionService.setPhoneUtilsProxy(mPhoneUtilsProxy);
         HandlerThread mockHandlerThread = mock(HandlerThread.class);
@@ -143,6 +153,7 @@
         doReturn(mMockHandler).when(mHandlerFactory).createHandler(any());
         mTestConnectionService.setHandlerFactory(mHandlerFactory);
         mTestConnectionService.setDeviceState(mDeviceState);
+        mTestConnectionService.setRadioOnHelper(mRadioOnHelper);
         doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
                 .toTelecomDisconnectCause(anyInt(), any());
         doReturn(new DisconnectCause(DisconnectCause.UNKNOWN)).when(mDisconnectCauseFactory)
@@ -941,6 +952,64 @@
     }
 
     /**
+     * Test that the TelephonyConnectionService successfully turns radio on before placing the
+     * emergency call.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmerge_exitingApm_disconnected() {
+        when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+        Phone testPhone = setupConnectionServiceInApm();
+
+        ArgumentCaptor<RadioOnStateListener.Callback> callback =
+                ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+                eq(testPhone));
+
+        assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+        when(mSST.isRadioOn()).thenReturn(true);
+        assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+
+        mConnection.setDisconnected(null);
+        callback.getValue().onComplete(null, true);
+        for (Phone phone : mPhoneFactoryProxy.getPhones()) {
+            verify(phone).setRadioPower(true, false, false, true);
+        }
+    }
+
+    /**
+     * Test that the TelephonyConnectionService successfully turns radio on before placing the
+     * emergency call.
+     */
+    @Test
+    @SmallTest
+    public void testCreateOutgoingEmergencyConnection_exitingApm_placeCall() {
+        when(mDeviceState.isAirplaneModeOn(any())).thenReturn(true);
+        Phone testPhone = setupConnectionServiceInApm();
+
+        ArgumentCaptor<RadioOnStateListener.Callback> callback =
+                ArgumentCaptor.forClass(RadioOnStateListener.Callback.class);
+        verify(mRadioOnHelper).triggerRadioOnAndListen(callback.capture(), eq(true),
+                eq(testPhone));
+
+        assertFalse(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+        when(mSST.isRadioOn()).thenReturn(true);
+        assertTrue(callback.getValue().isOkToCall(testPhone, ServiceState.STATE_OUT_OF_SERVICE));
+
+        callback.getValue().onComplete(null, true);
+        Runnable delayDialRunnable = verifyRunnablePosted();
+
+        try {
+            doAnswer(invocation -> null).when(mContext).startActivity(any());
+            delayDialRunnable.run();
+            verify(testPhone).dial(anyString(), any());
+        } catch (CallStateException e) {
+            // This shouldn't happen
+            fail();
+        }
+    }
+
+    /**
      * Test that the TelephonyConnectionService does not perform a DDS switch when the carrier
      * supports control-plane fallback.
      */
@@ -1115,9 +1184,49 @@
         doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
         doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
 
-        android.telecom.Connection testConnection = mTestConnectionService
-                .onCreateOutgoingConnection(PHONE_ACCOUNT_HANDLE_1, connectionRequest);
-        assertNotNull("test connection was not set up correctly.", testConnection);
+        mConnection = mTestConnectionService.onCreateOutgoingConnection(
+                PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", mConnection);
+
+        return testPhone0;
+    }
+
+
+    /**
+     * Set up a mock MSIM device with TEST_ADDRESS set as an emergency number in airplane mode.
+     * @return the Phone associated with slot 0.
+     */
+    private Phone setupConnectionServiceInApm() {
+        ConnectionRequest connectionRequest = new ConnectionRequest.Builder()
+                .setAccountHandle(PHONE_ACCOUNT_HANDLE_1)
+                .setAddress(TEST_ADDRESS)
+                .build();
+        Phone testPhone0 = makeTestPhone(0 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+                false /*isEmergencyOnly*/);
+        Phone testPhone1 = makeTestPhone(1 /*phoneId*/, ServiceState.STATE_POWER_OFF,
+                false /*isEmergencyOnly*/);
+        doReturn(GSM_PHONE).when(testPhone0).getPhoneType();
+        doReturn(GSM_PHONE).when(testPhone1).getPhoneType();
+        List<Phone> phones = new ArrayList<>(2);
+        doReturn(false).when(testPhone0).isRadioOn();
+        doReturn(false).when(testPhone1).isRadioOn();
+        phones.add(testPhone0);
+        phones.add(testPhone1);
+        setPhones(phones);
+        setupHandleToPhoneMap(PHONE_ACCOUNT_HANDLE_1, testPhone0);
+        setupDeviceConfig(testPhone0, testPhone1, 0);
+        doReturn(true).when(mTelephonyManagerProxy).isCurrentEmergencyNumber(
+                TEST_ADDRESS.getSchemeSpecificPart());
+        HashMap<Integer, List<EmergencyNumber>> emergencyNumbers = new HashMap<>(1);
+        List<EmergencyNumber> numbers = new ArrayList<>();
+        numbers.add(setupEmergencyNumber(TEST_ADDRESS));
+        emergencyNumbers.put(0 /*subId*/, numbers);
+        doReturn(emergencyNumbers).when(mTelephonyManagerProxy).getCurrentEmergencyNumberList();
+        doReturn(2).when(mTelephonyManagerProxy).getPhoneCount();
+
+        mConnection = mTestConnectionService.onCreateOutgoingConnection(
+                PHONE_ACCOUNT_HANDLE_1, connectionRequest);
+        assertNotNull("test connection was not set up correctly.", mConnection);
 
         return testPhone0;
     }
@@ -1163,6 +1272,7 @@
         when(phone.getPhoneId()).thenReturn(phoneId);
         when(phone.getDefaultPhone()).thenReturn(phone);
         when(phone.getEmergencyNumberTracker()).thenReturn(mEmergencyNumberTracker);
+        when(phone.getServiceStateTracker()).thenReturn(mSST);
         when(mEmergencyNumberTracker.getEmergencyNumber(anyString())).thenReturn(null);
         return phone;
     }
@@ -1193,6 +1303,7 @@
 
     private void setPhones(List<Phone> phones) {
         when(mPhoneFactoryProxy.getPhones()).thenReturn(phones.toArray(new Phone[phones.size()]));
+        when(mPhoneFactoryProxy.getDefaultPhone()).thenReturn(phones.get(0));
     }
 
     private void setPhonesDialConnection(Phone phone, Connection c) {