Merge "Revert "Add API method to extract subscription ID from phone account (3/3)"" into lmp-sprout-dev
diff --git a/res/values-mcc311-mnc220/config.xml b/res/values-mcc311-mnc220/config.xml
index 7c269e0..22701f0 100644
--- a/res/values-mcc311-mnc220/config.xml
+++ b/res/values-mcc311-mnc220/config.xml
@@ -19,4 +19,6 @@
 <resources>
     <!-- Flag indicating if dtmf tone type is enabled -->
     <bool name="dtmf_type_enabled">true</bool>
+    <!-- CDMA activation goes through OTASP. -->
+    <bool name="config_use_otasp_for_provisioning">true</bool>
 </resources>
diff --git a/res/values-mcc311-mnc580/config.xml b/res/values-mcc311-mnc580/config.xml
index 7c269e0..22701f0 100644
--- a/res/values-mcc311-mnc580/config.xml
+++ b/res/values-mcc311-mnc580/config.xml
@@ -19,4 +19,6 @@
 <resources>
     <!-- Flag indicating if dtmf tone type is enabled -->
     <bool name="dtmf_type_enabled">true</bool>
+    <!-- CDMA activation goes through OTASP. -->
+    <bool name="config_use_otasp_for_provisioning">true</bool>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 3daf89a..cd82234 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -127,8 +127,13 @@
     <!-- Class name for the default main Dialer activity [DO NOT TRANSLATE] -->
     <string name="dialer_default_class" translatable="false">com.android.dialer.DialtactsActivity</string>
 
-    <!-- CDMA activation goes through HFA [DO NOT TRANSLATE] -->
-    <bool name="config_use_hfa_for_provisioning" translatable="false">false</bool>
+    <!-- CDMA activation goes through HFA -->
+    <bool name="config_use_hfa_for_provisioning">false</bool>
+
+    <!-- CDMA activation goes through OTASP.
+        TODO: This should be combined with config_use_hfa_for_provisioning and implemented
+        as an enum (NONE, HFA, OTASP). -->
+    <bool name="config_use_otasp_for_provisioning">false</bool>
 
     <!-- Display carrier settings menu if true -->
     <bool name="config_carrier_settings_enable">false</bool>
@@ -141,4 +146,7 @@
 
     <!-- Show APN Settings for some CDMA carriers -->
     <bool name="config_show_apn_setting_cdma">false</bool>
+
+    <!-- Allows the telephony HFA logic to run even if we're not in setup wizard. -->
+    <bool name="config_allow_hfa_outside_of_setup_wizard">true</bool>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 382d39f..498b632 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -275,6 +275,8 @@
     <style name="SimImportTheme" parent="@android:style/Theme.Material.Light">
         <item name="android:actionBarStyle">@style/TelephonyActionBarStyle</item>
         <item name="android:colorPrimaryDark">@color/dialer_theme_color_dark</item>
+        <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
+        <item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
     </style>
 
     <style name="OutgoingCallBroadcasterTheme" parent="@android:style/Theme.Holo.NoActionBar">
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index b85cb7e..c38df5c 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -485,7 +485,10 @@
             mAudioManager.setParameter(HAC_KEY, hac != 0 ? HAC_VAL_ON : HAC_VAL_OFF);
             return true;
         } else if (preference == mVoicemailSettings) {
-            mVoicemailSettings.getDialog().getActionBar().setDisplayHomeAsUpEnabled(false);
+            final Dialog dialog = mVoicemailSettings.getDialog();
+            if (dialog != null) {
+                dialog.getActionBar().setDisplayHomeAsUpEnabled(false);
+            }
             if (DBG) log("onPreferenceTreeClick: Voicemail Settings Preference is clicked.");
             if (preference.getIntent() != null) {
                 if (DBG) {
@@ -511,7 +514,10 @@
                 return false;
             }
         } else if (preference == mVoicemailSettingsScreen) {
-            mVoicemailSettingsScreen.getDialog().getActionBar().setDisplayHomeAsUpEnabled(false);
+            final Dialog dialog = mVoicemailSettingsScreen.getDialog();
+            if (dialog != null) {
+                dialog.getActionBar().setDisplayHomeAsUpEnabled(false);
+            }
             return false;
         }
         return false;
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index be1e3b2..f22913c 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -142,8 +142,6 @@
 
         callStateMonitor.addListener(this);
 
-        createSignalInfoToneGenerator();
-
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
         if (adapter != null) {
             adapter.getProfileProxy(mApplication.getApplicationContext(),
@@ -944,7 +942,7 @@
         @Override
         public void run() {
             log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")...");
-
+            createSignalInfoToneGenerator();
             if (mSignalInfoToneGenerator != null) {
                 //First stop any ongoing SignalInfo tone
                 mSignalInfoToneGenerator.stopTone();
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index 921b7f7..7f4bd1b 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -50,6 +50,8 @@
  */
 public class EmergencyCallbackModeExitDialog extends Activity implements OnDismissListener {
 
+    private static final String TAG = "EmergencyCallbackMode";
+
     /** Intent to trigger the Emergency Callback Mode exit dialog */
     static final String ACTION_SHOW_ECM_EXIT_DIALOG =
             "com.android.phone.action.ACTION_SHOW_ECM_EXIT_DIALOG";
@@ -77,9 +79,12 @@
         super.onCreate(savedInstanceState);
 
         // Check if phone is in Emergency Callback Mode. If not, exit.
-        if (!Boolean.parseBoolean(
-                    SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE))) {
+        final boolean isInEcm = Boolean.parseBoolean(
+                SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE));
+        Log.i(TAG, "ECMModeExitDialog launched - isInEcm: " + isInEcm);
+        if (!isInEcm) {
             finish();
+            return;
         }
 
         mHandler = new Handler();
@@ -103,9 +108,15 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
-        unregisterReceiver(mEcmExitReceiver);
+        try {
+            unregisterReceiver(mEcmExitReceiver);
+        } catch (IllegalArgumentException e) {
+            // Receiver was never registered - silently ignore.
+        }
         // Unregister ECM timer reset notification
-        mPhone.unregisterForEcmTimerReset(mHandler);
+        if (mPhone != null) {
+            mPhone.unregisterForEcmTimerReset(mHandler);
+        }
     }
 
     @Override
@@ -148,11 +159,16 @@
             if (mService != null) {
                 mEcmTimeout = mService.getEmergencyCallbackModeTimeout();
                 mInEmergencyCall = mService.getEmergencyCallbackModeCallState();
+                try {
+                    // Unbind from remote service
+                    unbindService(mConnection);
+                } catch (IllegalArgumentException e) {
+                    // Failed to unbind from service. Don't crash as this brings down the entire
+                    // radio.
+                    Log.w(TAG, "Failed to unbind from EmergencyCallbackModeService");
+                }
             }
 
-            // Unbind from remote service
-            unbindService(mConnection);
-
             // Show dialog
             mHandler.post(new Runnable() {
                 public void run() {
@@ -166,7 +182,10 @@
      * Shows Emergency Callback Mode dialog and starts countdown timer
      */
     private void showEmergencyCallbackModeExitDialog() {
-
+        if (!this.isResumed()) {
+            Log.w(TAG, "Tried to show dialog, but activity was already finished");
+            return;
+        }
         if(mInEmergencyCall) {
             mDialogType = EXIT_ECM_IN_EMERGENCY_CALL_DIALOG;
             showDialog(EXIT_ECM_IN_EMERGENCY_CALL_DIALOG);
@@ -283,6 +302,7 @@
     /**
      * Closes activity when dialog is dismissed
      */
+    @Override
     public void onDismiss(DialogInterface dialog) {
         EmergencyCallbackModeExitDialog.this.setResult(RESULT_OK, (new Intent())
                 .putExtra(EXTRA_EXIT_ECM_RESULT, false));
diff --git a/src/com/android/phone/EmergencyCallbackModeService.java b/src/com/android/phone/EmergencyCallbackModeService.java
index e1f7fb3..3310df1 100644
--- a/src/com/android/phone/EmergencyCallbackModeService.java
+++ b/src/com/android/phone/EmergencyCallbackModeService.java
@@ -144,26 +144,40 @@
         showNotification(ecmTimeout);
 
         // Start countdown timer for the notification updates
-        mTimer = new CountDownTimer(ecmTimeout, 1000) {
+        if (mTimer != null) {
+            mTimer.cancel();
+        } else {
+            mTimer = new CountDownTimer(ecmTimeout, 1000) {
 
-            @Override
-            public void onTick(long millisUntilFinished) {
-                mTimeLeft = millisUntilFinished;
-                EmergencyCallbackModeService.this.showNotification(millisUntilFinished);
-            }
+                @Override
+                public void onTick(long millisUntilFinished) {
+                    mTimeLeft = millisUntilFinished;
+                    EmergencyCallbackModeService.this.showNotification(millisUntilFinished);
+                }
 
-            @Override
-            public void onFinish() {
-                //Do nothing
-            }
+                @Override
+                public void onFinish() {
+                    //Do nothing
+                }
 
-        }.start();
+            };
+        }
+        mTimer.start();
     }
 
     /**
      * Shows notification for Emergency Callback Mode
      */
     private void showNotification(long millisUntilFinished) {
+        final boolean isInEcm = Boolean.parseBoolean(
+                SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE));
+        if (!isInEcm) {
+            Log.i(LOG_TAG, "Asked to show notification but not in ECM mode");
+            if (mTimer != null) {
+                mTimer.cancel();
+            }
+            return;
+        }
         final Notification.Builder builder = new Notification.Builder(getApplicationContext());
         builder.setOngoing(true);
         builder.setPriority(Notification.PRIORITY_HIGH);
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index e29d4c8..a8de874 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -38,6 +38,7 @@
 import android.text.method.DialerKeyListener;
 import android.util.Log;
 import android.view.KeyEvent;
+import android.view.MenuItem;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
@@ -606,6 +607,16 @@
         }
     }
 
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        final int itemId = item.getItemId();
+        if (itemId == android.R.id.home) {
+            onBackPressed();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
     /**
      * Update the enabledness of the "Dial" and "Backspace" buttons if applicable.
      */
diff --git a/src/com/android/phone/InCallScreenShowActivation.java b/src/com/android/phone/InCallScreenShowActivation.java
index fd202db..34710a1 100644
--- a/src/com/android/phone/InCallScreenShowActivation.java
+++ b/src/com/android/phone/InCallScreenShowActivation.java
@@ -73,74 +73,81 @@
 
             boolean usesHfa = getResources().getBoolean(R.bool.config_use_hfa_for_provisioning);
             if (usesHfa) {
-                Log.d(LOG_TAG, "Starting Hfa from ACTION_PERFORM_CDMA_PROVISIONING");
+                Log.i(LOG_TAG, "Starting Hfa from ACTION_PERFORM_CDMA_PROVISIONING");
                 startHfa();
                 finish();
                 return;
             }
 
-            // On voice-capable devices, we perform CDMA provisioning in
-            // "interactive" mode by directly launching the InCallScreen.
-            // boolean interactiveMode = PhoneGlobals.sVoiceCapable;
-            // TODO: Renable interactive mode for device provisioning.
-            boolean interactiveMode = false;
-            Log.d(LOG_TAG, "ACTION_PERFORM_CDMA_PROVISIONING (interactiveMode = "
-                  + interactiveMode + ")...");
+            boolean usesOtasp = getResources().getBoolean(R.bool.config_use_otasp_for_provisioning);
+            if (usesOtasp) {
+                // On voice-capable devices, we perform CDMA provisioning in
+                // "interactive" mode by directly launching the InCallScreen.
+                // boolean interactiveMode = PhoneGlobals.sVoiceCapable;
+                // TODO: Renable interactive mode for device provisioning.
+                boolean interactiveMode = false;
+                Log.i(LOG_TAG, "ACTION_PERFORM_CDMA_PROVISIONING (interactiveMode = "
+                      + interactiveMode + ")...");
 
-            // Testing: this intent extra allows test apps manually
-            // enable/disable "interactive mode", regardless of whether
-            // the current device is voice-capable.  This is allowed only
-            // in userdebug or eng builds.
-            if (intent.hasExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE)
-                    && (SystemProperties.getInt("ro.debuggable", 0) == 1)) {
-                interactiveMode =
-                        intent.getBooleanExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE, false);
-                Log.d(LOG_TAG, "===> MANUALLY OVERRIDING interactiveMode to " + interactiveMode);
-            }
-
-            // We allow the caller to pass a PendingIntent (as the
-            // EXTRA_NONINTERACTIVE_OTASP_RESULT_PENDING_INTENT extra)
-            // which we'll later use to notify them when the OTASP call
-            // fails or succeeds.
-            //
-            // Stash that away here, and we'll fire it off later in
-            // OtaUtils.sendOtaspResult().
-            app.cdmaOtaScreenState.otaspResultCodePendingIntent =
-                        (PendingIntent) intent.getParcelableExtra(
-                                OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
-
-            if (interactiveMode) {
-                // On voice-capable devices, launch an OTASP call and arrange
-                // for the in-call UI to come up.  (The InCallScreen will
-                // notice that an OTASP call is active, and display the
-                // special OTASP UI instead of the usual in-call controls.)
-
-                if (DBG) Log.d(LOG_TAG, "==> Starting interactive CDMA provisioning...");
-                OtaUtils.startInteractiveOtasp(this);
-
-                // The result we set here is actually irrelevant, since the
-                // InCallScreen's "interactive" OTASP sequence never actually
-                // finish()es; it ends by directly launching the Home
-                // activity.  So our caller won't actually ever get an
-                // onActivityResult() call in this case.
-                setResult(OtaUtils.RESULT_INTERACTIVE_OTASP_STARTED);
-            } else {
-                // On data-only devices, manually launch the OTASP call
-                // *without* displaying any UI.  (Our caller, presumably
-                // SetupWizardActivity, is responsible for displaying some
-                // sort of progress UI.)
-
-                if (DBG) Log.d(LOG_TAG, "==> Starting non-interactive CDMA provisioning...");
-                int callStatus = OtaUtils.startNonInteractiveOtasp(this);
-
-                if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
-                    if (DBG) Log.d(LOG_TAG, "  ==> successful result from startNonInteractiveOtasp(): "
-                          + callStatus);
-                    setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_STARTED);
-                } else {
-                    Log.w(LOG_TAG, "Failure code from startNonInteractiveOtasp(): " + callStatus);
-                    setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_FAILED);
+                // Testing: this intent extra allows test apps manually
+                // enable/disable "interactive mode", regardless of whether
+                // the current device is voice-capable.  This is allowed only
+                // in userdebug or eng builds.
+                if (intent.hasExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE)
+                        && (SystemProperties.getInt("ro.debuggable", 0) == 1)) {
+                    interactiveMode =
+                            intent.getBooleanExtra(OtaUtils.EXTRA_OVERRIDE_INTERACTIVE_MODE, false);
+                    Log.d(LOG_TAG, "==> MANUALLY OVERRIDING interactiveMode to " + interactiveMode);
                 }
+
+                // We allow the caller to pass a PendingIntent (as the
+                // EXTRA_NONINTERACTIVE_OTASP_RESULT_PENDING_INTENT extra)
+                // which we'll later use to notify them when the OTASP call
+                // fails or succeeds.
+                //
+                // Stash that away here, and we'll fire it off later in
+                // OtaUtils.sendOtaspResult().
+                app.cdmaOtaScreenState.otaspResultCodePendingIntent =
+                            (PendingIntent) intent.getParcelableExtra(
+                                    OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
+
+                if (interactiveMode) {
+                    // On voice-capable devices, launch an OTASP call and arrange
+                    // for the in-call UI to come up.  (The InCallScreen will
+                    // notice that an OTASP call is active, and display the
+                    // special OTASP UI instead of the usual in-call controls.)
+
+                    if (DBG) Log.d(LOG_TAG, "==> Starting interactive CDMA provisioning...");
+                    OtaUtils.startInteractiveOtasp(this);
+
+                    // The result we set here is actually irrelevant, since the
+                    // InCallScreen's "interactive" OTASP sequence never actually
+                    // finish()es; it ends by directly launching the Home
+                    // activity.  So our caller won't actually ever get an
+                    // onActivityResult() call in this case.
+                    setResult(OtaUtils.RESULT_INTERACTIVE_OTASP_STARTED);
+                } else {
+                    // On data-only devices, manually launch the OTASP call
+                    // *without* displaying any UI.  (Our caller, presumably
+                    // SetupWizardActivity, is responsible for displaying some
+                    // sort of progress UI.)
+
+                    if (DBG) Log.d(LOG_TAG, "==> Starting non-interactive CDMA provisioning...");
+                    int callStatus = OtaUtils.startNonInteractiveOtasp(this);
+
+                    if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
+                        if (DBG) Log.d(LOG_TAG,
+                                "  ==> successful result from startNonInteractiveOtasp(): " +
+                                callStatus);
+                        setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_STARTED);
+                    } else {
+                        Log.w(LOG_TAG, "Failure code from startNonInteractiveOtasp(): " +
+                                callStatus);
+                        setResult(OtaUtils.RESULT_NONINTERACTIVE_OTASP_FAILED);
+                    }
+                }
+            } else {
+                Log.i(LOG_TAG, "Skipping activation.");
             }
         } else {
             Log.e(LOG_TAG, "Unexpected intent action: " + intent);
@@ -180,28 +187,34 @@
      * Starts the HFA provisioning process by bringing up the HFA Activity.
      */
     private void startHfa() {
-        final Intent intent = new Intent();
+        boolean isWizardRunning = isWizardRunning(this);
+        // We always run our HFA logic if we're in setup wizard, but if we're outside of setup
+        // wizard then we have to check a config to see if we should still run HFA.
+        if (isWizardRunning ||
+                getResources().getBoolean(R.bool.config_allow_hfa_outside_of_setup_wizard)) {
 
-        final PendingIntent otaResponseIntent = getIntent().getParcelableExtra(
-                OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
+            final Intent intent = new Intent();
 
-        final boolean showUi = !isWizardRunning(this);
+            final PendingIntent otaResponseIntent = getIntent().getParcelableExtra(
+                    OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
 
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            final boolean showUi = !isWizardRunning;
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
-        if (otaResponseIntent != null) {
-            intent.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT, otaResponseIntent);
+            if (otaResponseIntent != null) {
+                intent.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT, otaResponseIntent);
+            }
+
+            Log.v(LOG_TAG, "Starting hfa activation activity");
+            if (showUi) {
+                intent.setClassName(this, HfaActivity.class.getName());
+                startActivity(intent);
+            } else {
+                intent.setClassName(this, HfaService.class.getName());
+                startService(intent);
+            }
+
         }
-
-        Log.v(LOG_TAG, "Starting hfa activation activity");
-        if (showUi) {
-            intent.setClassName(this, HfaActivity.class.getName());
-            startActivity(intent);
-        } else {
-            intent.setClassName(this, HfaService.class.getName());
-            startService(intent);
-        }
-
         setResult(RESULT_OK);
     }
 }
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index feccf95..6ed9cbc 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -24,8 +24,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.UserInfo;
 import android.net.Uri;
 import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.preference.PreferenceManager;
 import android.provider.ContactsContract.PhoneLookup;
 import android.provider.Settings;
@@ -40,6 +43,8 @@
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.TelephonyCapabilities;
 
+import java.util.List;
+
 /**
  * NotificationManager-related utility code for the Phone app.
  *
@@ -73,6 +78,7 @@
     private Context mContext;
     private NotificationManager mNotificationManager;
     private StatusBarManager mStatusBarManager;
+    private UserManager mUserManager;
     private Toast mToast;
 
     public StatusBarHelper statusBarHelper;
@@ -96,6 +102,7 @@
                 (NotificationManager) app.getSystemService(Context.NOTIFICATION_SERVICE);
         mStatusBarManager =
                 (StatusBarManager) app.getSystemService(Context.STATUS_BAR_SERVICE);
+        mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
         mPhone = app.phone;  // TODO: better style to use mCM.getDefaultPhone() everywhere instead
         statusBarHelper = new StatusBarHelper();
     }
@@ -324,19 +331,29 @@
                     .setContentText(notificationText)
                     .setContentIntent(pendingIntent)
                     .setSound(ringtoneUri)
-                    .setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
-            Notification notification = builder.getNotification();
+                    .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
+                    .setOngoing(true);
 
             CallFeaturesSetting.migrateVoicemailVibrationSettingsIfNeeded(prefs);
             final boolean vibrate = prefs.getBoolean(
                     CallFeaturesSetting.BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY, false);
             if (vibrate) {
-                notification.defaults |= Notification.DEFAULT_VIBRATE;
+                builder.setDefaults(Notification.DEFAULT_VIBRATE);
             }
-            notification.flags |= Notification.FLAG_NO_CLEAR;
-            mNotificationManager.notify(VOICEMAIL_NOTIFICATION, notification);
+
+            final Notification notification = builder.build();
+            List<UserInfo> users = mUserManager.getUsers(true);
+            for (int i = 0; i < users.size(); i++) {
+                UserHandle userHandle = users.get(i).getUserHandle();
+                if (!mUserManager.hasUserRestriction(
+                        UserManager.DISALLOW_OUTGOING_CALLS, userHandle)) {
+                    mNotificationManager.notifyAsUser(
+                            null /* tag */, VOICEMAIL_NOTIFICATION, notification, userHandle);
+                }
+            }
         } else {
-            mNotificationManager.cancel(VOICEMAIL_NOTIFICATION);
+            mNotificationManager.cancelAsUser(
+                    null /* tag */, VOICEMAIL_NOTIFICATION, UserHandle.ALL);
         }
     }
 
@@ -359,39 +376,28 @@
             // effort though, since there are multiple layers of messages that
             // will need to propagate that information.
 
-            Notification notification;
-            final boolean showExpandedNotification = true;
-            if (showExpandedNotification) {
-                Intent intent = new Intent(Intent.ACTION_MAIN);
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                intent.setClassName("com.android.phone",
-                        "com.android.phone.CallFeaturesSetting");
+            Notification.Builder builder = new Notification.Builder(mContext)
+                    .setSmallIcon(R.drawable.stat_sys_phone_call_forward)
+                    .setContentTitle(mContext.getString(R.string.labelCF))
+                    .setContentText(mContext.getString(R.string.sum_cfu_enabled_indicator))
+                    .setShowWhen(false)
+                    .setOngoing(true);
 
-                notification = new Notification(
-                        R.drawable.stat_sys_phone_call_forward,  // icon
-                        null, // tickerText
-                        0); // The "timestamp" of this notification is meaningless;
-                            // we only care about whether CFI is currently on or not.
-                notification.setLatestEventInfo(
-                        mContext, // context
-                        mContext.getString(R.string.labelCF), // expandedTitle
-                        mContext.getString(R.string.sum_cfu_enabled_indicator), // expandedText
-                        PendingIntent.getActivity(mContext, 0, intent, 0)); // contentIntent
-            } else {
-                notification = new Notification(
-                        R.drawable.stat_sys_phone_call_forward,  // icon
-                        null,  // tickerText
-                        System.currentTimeMillis()  // when
-                        );
+            Intent intent = new Intent(Intent.ACTION_MAIN);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.setClassName("com.android.phone", "com.android.phone.CallFeaturesSetting");
+            PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+            List<UserInfo> users = mUserManager.getUsers(true);
+            for (int i = 0; i < users.size(); i++) {
+                UserHandle userHandle = users.get(i).getUserHandle();
+                builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
+                    mNotificationManager.notifyAsUser(
+                            null /* tag */, CALL_FORWARD_NOTIFICATION, builder.build(), userHandle);
             }
-
-            notification.flags |= Notification.FLAG_ONGOING_EVENT;  // also implies FLAG_NO_CLEAR
-
-            mNotificationManager.notify(
-                    CALL_FORWARD_NOTIFICATION,
-                    notification);
         } else {
-            mNotificationManager.cancel(CALL_FORWARD_NOTIFICATION);
+            mNotificationManager.cancelAsUser(
+                    null /* tag */, CALL_FORWARD_NOTIFICATION, UserHandle.ALL);
         }
     }
 
@@ -405,20 +411,25 @@
 
         // "Mobile network settings" screen / dialog
         Intent intent = new Intent(mContext, com.android.phone.MobileNetworkSettings.class);
+        PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
 
         final CharSequence contentText = mContext.getText(R.string.roaming_reenable_message);
 
-        final Notification.Builder builder = new Notification.Builder(mContext);
-        builder.setSmallIcon(android.R.drawable.stat_sys_warning);
-        builder.setContentTitle(mContext.getText(R.string.roaming));
-        builder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
-        builder.setContentText(contentText);
-        builder.setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0));
+        final Notification.Builder builder = new Notification.Builder(mContext)
+                .setSmallIcon(android.R.drawable.stat_sys_warning)
+                .setContentTitle(mContext.getText(R.string.roaming))
+                .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
+                .setContentText(contentText);
 
-        final Notification notif = new Notification.BigTextStyle(builder).bigText(contentText)
-                .build();
-
-        mNotificationManager.notify(DATA_DISCONNECTED_ROAMING_NOTIFICATION, notif);
+        List<UserInfo> users = mUserManager.getUsers(true);
+        for (int i = 0; i < users.size(); i++) {
+            UserHandle userHandle = users.get(i).getUserHandle();
+            builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
+            final Notification notif =
+                    new Notification.BigTextStyle(builder).bigText(contentText).build();
+            mNotificationManager.notifyAsUser(
+                    null /* tag */, DATA_DISCONNECTED_ROAMING_NOTIFICATION, notif, userHandle);
+        }
     }
 
     /**
@@ -436,16 +447,13 @@
     private void showNetworkSelection(String operator) {
         if (DBG) log("showNetworkSelection(" + operator + ")...");
 
-        String titleText = mContext.getString(
-                R.string.notification_network_selection_title);
-        String expandedText = mContext.getString(
-                R.string.notification_network_selection_text, operator);
-
-        Notification notification = new Notification();
-        notification.icon = android.R.drawable.stat_sys_warning;
-        notification.when = 0;
-        notification.flags = Notification.FLAG_ONGOING_EVENT;
-        notification.tickerText = null;
+        Notification.Builder builder = new Notification.Builder(mContext)
+                .setSmallIcon(android.R.drawable.stat_sys_warning)
+                .setContentTitle(mContext.getString(R.string.notification_network_selection_title))
+                .setContentText(
+                        mContext.getString(R.string.notification_network_selection_text, operator))
+                .setShowWhen(false)
+                .setOngoing(true);
 
         // create the target network operators settings intent
         Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -454,11 +462,18 @@
         // Use NetworkSetting to handle the selection intent
         intent.setComponent(new ComponentName("com.android.phone",
                 "com.android.phone.NetworkSetting"));
-        PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+        PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
 
-        notification.setLatestEventInfo(mContext, titleText, expandedText, pi);
-
-        mNotificationManager.notify(SELECTED_OPERATOR_FAIL_NOTIFICATION, notification);
+        List<UserInfo> users = mUserManager.getUsers(true);
+        for (int i = 0; i < users.size(); i++) {
+            UserHandle userHandle = users.get(i).getUserHandle();
+            builder.setContentIntent(userHandle.isOwner() ? contentIntent : null);
+            mNotificationManager.notifyAsUser(
+                    null /* tag */,
+                    SELECTED_OPERATOR_FAIL_NOTIFICATION,
+                    builder.build(),
+                    userHandle);
+        }
     }
 
     /**
@@ -466,7 +481,8 @@
      */
     private void cancelNetworkSelection() {
         if (DBG) log("cancelNetworkSelection()...");
-        mNotificationManager.cancel(SELECTED_OPERATOR_FAIL_NOTIFICATION);
+        mNotificationManager.cancelAsUser(
+                null /* tag */, SELECTED_OPERATOR_FAIL_NOTIFICATION, UserHandle.ALL);
     }
 
     /**
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 4dd8ee2..31337ee 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -434,12 +434,10 @@
             PhoneUtils.setAudioMode(mCM);
         }
 
-        if (TelephonyCapabilities.supportsOtasp(phone)) {
-            cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData();
-            cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData();
-            cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState();
-            cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState();
-        }
+        cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData();
+        cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData();
+        cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState();
+        cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState();
 
         // XXX pre-load the SimProvider so that it's ready
         resolver.getType(Uri.parse("content://icc/adn"));
@@ -759,21 +757,7 @@
             cdmaPhoneCallState = new CdmaPhoneCallState();
             cdmaPhoneCallState.CdmaPhoneCallStateInit();
         }
-        if (TelephonyCapabilities.supportsOtasp(phone)) {
-            //create instances of CDMA OTA data classes
-            if (cdmaOtaProvisionData == null) {
-                cdmaOtaProvisionData = new OtaUtils.CdmaOtaProvisionData();
-            }
-            if (cdmaOtaConfigData == null) {
-                cdmaOtaConfigData = new OtaUtils.CdmaOtaConfigData();
-            }
-            if (cdmaOtaScreenState == null) {
-                cdmaOtaScreenState = new OtaUtils.CdmaOtaScreenState();
-            }
-            if (cdmaOtaInCallScreenUiState == null) {
-                cdmaOtaInCallScreenUiState = new OtaUtils.CdmaOtaInCallScreenUiState();
-            }
-        } else {
+        if (!TelephonyCapabilities.supportsOtasp(phone)) {
             //Clean up OTA data in GSM/UMTS. It is valid only for CDMA
             clearOtaState();
         }
diff --git a/src/com/android/phone/SimContacts.java b/src/com/android/phone/SimContacts.java
index a069e41..8192795 100644
--- a/src/com/android/phone/SimContacts.java
+++ b/src/com/android/phone/SimContacts.java
@@ -39,6 +39,7 @@
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
 import android.telecom.PhoneAccount;
+import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.ContextMenu;
@@ -242,8 +243,13 @@
 
     @Override
     protected Uri resolveIntent() {
-        Intent intent = getIntent();
-        intent.setData(Uri.parse("content://icc/adn"));
+        final Intent intent = getIntent();
+        if (intent.hasExtra("subscription_id")) {
+            final long subId = intent.getLongExtra("subscription_id", -1);
+            intent.setData(Uri.parse("content://icc/adn/subId/" + subId));
+        } else {
+            intent.setData(Uri.parse("content://icc/adn"));
+        }
         if (Intent.ACTION_PICK.equals(intent.getAction())) {
             // "index" is 1-based
             mInitialSelection = intent.getIntExtra("index", 0) - 1;
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 2a14f0b..c48812c 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -37,7 +37,8 @@
     private static final String SELECT_CALL_ASSISTANT_PREF_KEY =
             "wifi_calling_call_assistant_preference";
 
-    private static final String SIP_SETTINGS_CATEGORY_PREF_KEY = "phone_accounts_sip_settings_key";
+    private static final String SIP_SETTINGS_CATEGORY_PREF_KEY =
+            "phone_accounts_sip_settings_category_key";
     private static final String USE_SIP_PREF_KEY = "use_sip_calling_options_key";
     private static final String SIP_RECEIVE_CALLS_PREF_KEY = "sip_receive_calls_key";
 
@@ -104,8 +105,17 @@
                     ? R.array.sip_call_options_wifi_only_entries
                     : R.array.sip_call_options_entries);
             mUseSipCalling.setOnPreferenceChangeListener(this);
-            mUseSipCalling.setValueIndex(
-                    mUseSipCalling.findIndexOfValue(mSipSharedPreferences.getSipCallOption()));
+
+            int optionsValueIndex =
+                    mUseSipCalling.findIndexOfValue(mSipSharedPreferences.getSipCallOption());
+            if (optionsValueIndex == -1) {
+                // If the option is invalid (eg. deprecated value), default to SIP_ADDRESS_ONLY.
+                mSipSharedPreferences.setSipCallOption(
+                        getResources().getString(R.string.sip_address_only));
+                optionsValueIndex =
+                        mUseSipCalling.findIndexOfValue(mSipSharedPreferences.getSipCallOption());
+            }
+            mUseSipCalling.setValueIndex(optionsValueIndex);
             mUseSipCalling.setSummary(mUseSipCalling.getEntry());
 
             mSipReceiveCallsPreference = (CheckBoxPreference)
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index 5d470ab..1915fe0 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -79,7 +79,6 @@
             EmergencyTonePlayer emergencyTonePlayer,
             boolean allowMute,
             boolean isOutgoing) {
-
         super(connection);
         mEmergencyTonePlayer = emergencyTonePlayer;
         mAllowMute = allowMute;
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index 0c8f8bc..f4c8a22 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -17,7 +17,6 @@
 package com.android.services.telephony;
 
 import android.content.BroadcastReceiver;
-
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -27,6 +26,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.UserHandle;
+import android.telecom.CallState;
 import android.telecom.PhoneAccount;
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
@@ -39,6 +39,7 @@
 import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
+
 import com.google.common.base.Preconditions;
 
 import java.util.Objects;
@@ -51,6 +52,7 @@
     /** New ringing connection event code. */
     private static final int EVENT_NEW_RINGING_CONNECTION = 100;
     private static final int EVENT_CDMA_CALL_WAITING = 101;
+    private static final int EVENT_UNKNOWN_CONNECTION = 102;
 
     /** The phone proxy object to listen to. */
     private final PhoneProxy mPhoneProxy;
@@ -76,6 +78,9 @@
                 case EVENT_CDMA_CALL_WAITING:
                     handleCdmaCallWaiting((AsyncResult) msg.obj);
                     break;
+                case EVENT_UNKNOWN_CONNECTION:
+                    handleNewUnknownConnection((AsyncResult) msg.obj);
+                    break;
                 default:
                     break;
             }
@@ -142,6 +147,8 @@
                         mHandler, EVENT_NEW_RINGING_CONNECTION, null);
                 mPhoneBase.registerForCallWaiting(
                         mHandler, EVENT_CDMA_CALL_WAITING, null);
+                mPhoneBase.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION,
+                        null);
             }
         }
     }
@@ -151,6 +158,7 @@
             Log.i(this, "Unregistering: %s", mPhoneBase);
             mPhoneBase.unregisterForNewRingingConnection(mHandler);
             mPhoneBase.unregisterForCallWaiting(mHandler);
+            mPhoneBase.unregisterForUnknownConnection(mHandler);
         }
     }
 
@@ -187,6 +195,33 @@
         }
     }
 
+    private void handleNewUnknownConnection(AsyncResult asyncResult) {
+        Log.i(this, "handleNewUnknownConnection");
+        if (!(asyncResult.result instanceof Connection)) {
+            Log.w(this, "handleNewUnknownConnection called with non-Connection object");
+            return;
+        }
+        Connection connection = (Connection) asyncResult.result;
+        if (connection != null) {
+            Call call = connection.getCall();
+            if (call != null && call.getState().isAlive()) {
+                addNewUnknownCall(connection);
+            }
+        }
+    }
+
+    private void addNewUnknownCall(Connection connection) {
+        Bundle extras = null;
+        if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED &&
+                !TextUtils.isEmpty(connection.getAddress())) {
+            extras = new Bundle();
+            Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null);
+            extras.putParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE, uri);
+        }
+        TelecomManager.from(mPhoneProxy.getContext()).addNewUnknownCall(
+                TelecomAccountRegistry.makePstnPhoneAccountHandle(mPhoneProxy), extras);
+    }
+
     /**
      * Sends the incoming call intent to telecom.
      */
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 9e7589c..1cc6ad6 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -485,7 +485,7 @@
         return true;
     }
 
-    protected void updateState() {
+    void updateState() {
         if (mOriginalConnection == null) {
             return;
         }
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index cb7894c..73a57d3 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -40,6 +40,8 @@
 import com.android.internal.telephony.cdma.CDMAPhone;
 import com.android.phone.MMIDialogActivity;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -82,6 +84,13 @@
             // TODO: We don't check for SecurityException here (requires
             // CALL_PRIVILEGED permission).
             final Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
+            if (phone == null) {
+                Log.d(this, "onCreateOutgoingConnection, phone is null");
+                return Connection.createFailedConnection(
+                        DisconnectCauseUtil.toTelecomDisconnectCause(
+                                android.telephony.DisconnectCause.OUTGOING_FAILURE,
+                                "Phone is null"));
+            }
             number = phone.getVoiceMailNumber();
             if (TextUtils.isEmpty(number)) {
                 Log.d(this, "onCreateOutgoingConnection, no voicemail number set.");
@@ -238,6 +247,57 @@
     }
 
     @Override
+    public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
+            ConnectionRequest request) {
+        Log.i(this, "onCreateUnknownConnection, request: " + request);
+
+        Phone phone = getPhoneForAccount(request.getAccountHandle(), false);
+        if (phone == null) {
+            return Connection.createFailedConnection(
+                    DisconnectCauseUtil.toTelecomDisconnectCause(
+                            android.telephony.DisconnectCause.ERROR_UNSPECIFIED));
+        }
+
+        final List<com.android.internal.telephony.Connection> allConnections = new ArrayList<>();
+        final Call ringingCall = phone.getRingingCall();
+        if (ringingCall.hasConnections()) {
+            allConnections.addAll(ringingCall.getConnections());
+        }
+        final Call foregroundCall = phone.getForegroundCall();
+        if (foregroundCall.hasConnections()) {
+            allConnections.addAll(foregroundCall.getConnections());
+        }
+        final Call backgroundCall = phone.getBackgroundCall();
+        if (backgroundCall.hasConnections()) {
+            allConnections.addAll(phone.getBackgroundCall().getConnections());
+        }
+
+        com.android.internal.telephony.Connection unknownConnection = null;
+        for (com.android.internal.telephony.Connection telephonyConnection : allConnections) {
+            if (!isOriginalConnectionKnown(telephonyConnection)) {
+                unknownConnection = telephonyConnection;
+                break;
+            }
+        }
+
+        if (unknownConnection == null) {
+            Log.i(this, "onCreateUnknownConnection, did not find previously unknown connection.");
+            return Connection.createCanceledConnection();
+        }
+
+        TelephonyConnection connection =
+                createConnectionFor(phone, unknownConnection,
+                        !unknownConnection.isIncoming() /* isOutgoing */);
+
+        if (connection == null) {
+            return Connection.createCanceledConnection();
+        } else {
+            connection.updateState();
+            return connection;
+        }
+    }
+
+    @Override
     public void onConference(Connection connection1, Connection connection2) {
         if (connection1 instanceof TelephonyConnection &&
                 connection2 instanceof TelephonyConnection) {