am baee2955: Allowing enable/disable of phone accounts. (3/3)

* commit 'baee2955f596f7b79971feb12fa21031a069677d':
  Allowing enable/disable of phone accounts. (3/3)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f668255..dd9b06d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -436,6 +436,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".settings.PhoneAccountSelectionPreferenceActivity"
+                  android:label="@string/phone_accounts"
+                  android:theme="@style/DialerSettingsLight">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
         <!-- bluetooth phone service -->
         <service android:name="BluetoothPhoneService">
             <intent-filter>
@@ -489,7 +497,6 @@
             <intent-filter>
                 <action android:name="android.net.sip.SIP_SERVICE_UP" />
                 <action android:name="com.android.phone.SIP_INCOMING_CALL" />
-                <action android:name="com.android.phone.SIP_ADD_PHONE" />
                 <action android:name="com.android.phone.SIP_REMOVE_PHONE" />
                 <action android:name="com.android.phone.SIP_CALL_OPTION_CHANGED" />
             </intent-filter>
diff --git a/res/xml/phone_account_selection.xml b/res/xml/phone_account_selection.xml
new file mode 100644
index 0000000..9c5995c
--- /dev/null
+++ b/res/xml/phone_account_selection.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2014 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
+  -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <PreferenceCategory
+        android:key="phone_accounts_list"
+        android:title="@string/phone_accounts_selection_header"
+        android:persistent="true" />
+</PreferenceScreen>
\ No newline at end of file
diff --git a/res/xml/phone_account_selection_activity.xml b/res/xml/phone_account_selection_activity.xml
new file mode 100644
index 0000000..9425297
--- /dev/null
+++ b/res/xml/phone_account_selection_activity.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2014 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
+  -->
+
+<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
+    <header
+        android:fragment="com.android.phone.settings.PhoneAccountSelectionPreferenceActivity$PhoneAccountSelectionPreferenceFragment"
+        android:title="@string/phone_accounts" />
+</preference-headers>
\ No newline at end of file
diff --git a/sip/res/values/strings.xml b/sip/res/values/strings.xml
index 8b85ff4..0163a31 100644
--- a/sip/res/values/strings.xml
+++ b/sip/res/values/strings.xml
@@ -86,8 +86,6 @@
     <!-- Title of the button to show in a alert dialog. [CHAR LIMIT=NONE] -->
     <string name="close_profile">Close</string>
 
-    <!-- Summary of a SIP account in the list of SIP accounts, consists of primary account status and registration status. This string indicates that this SIP account is a primary account and the string placeholder is the registration status, which will be one of the strings with name starting with "registration_status_" below. [CHAR LIMIT=NONE] -->
-    <string name="primary_account_summary_with">Primary account. <xliff:g id="registration_status" example="Registering...">%s</xliff:g></string>
     <!-- Text of SIP-call registration status, checking current registration status with SIP service [CHAR LIMIT=NONE] -->
     <string name="registration_status_checking_status">Checking status...</string>
     <!-- Text of SIP-call registration status, registering with SIP server in order to receive calls on this account [CHAR LIMIT=NONE] -->
@@ -135,10 +133,6 @@
     <string name="transport_title">Transport type</string>
     <!-- Text of the keepalive preference. [CHAR LIMIT=NONE] -->
     <string name="send_keepalive_title">Send keep-alive</string>
-    <!-- Text of the set-primary preference. Simplified from "Make this my primary account". [CHAR LIMIT=NONE] -->
-    <string name="set_primary_title">Set as primary account</string>
-    <!-- Text of the set-primary preference summary. [CHAR LIMIT=NONE] -->
-    <string name="set_primary_summary">Used for outbound calls</string>
     <!-- Text of the advanced settings section. [CHAR LIMIT=NONE] -->
     <string name="advanced_settings">Optional settings</string>
     <!-- Text of the username used in authentication. [CHAR LIMIT=NONE] -->
@@ -198,7 +192,6 @@
     <string translatable="false" name="port">Port</string>
     <string translatable="false" name="transport">Protocol</string>
     <string translatable="false" name="send_keepalive">SendKeepAlive</string>
-    <string translatable="false" name="set_primary">SetPrimary</string>
     <string translatable="false" name="advanced_settings_container">advanced settings container</string>
     <string translatable="false" name="auth_username">AuthUserName</string>
 
diff --git a/sip/res/xml/sip_edit.xml b/sip/res/xml/sip_edit.xml
index f091fc1..15e5ce1 100644
--- a/sip/res/xml/sip_edit.xml
+++ b/sip/res/xml/sip_edit.xml
@@ -45,12 +45,6 @@
         android:singleLine="true"
         android:inputType="textNoSuggestions"/>
 
-    <CheckBoxPreference
-        android:key="@string/set_primary"
-        android:title="@string/set_primary_title"
-        android:persistent="false"
-        android:summary="@string/set_primary_summary"/>
-
     <PreferenceScreen
         android:key="@string/advanced_settings"
         android:title="@string/advanced_settings"
diff --git a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
index 6149231..b00acba 100644
--- a/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
+++ b/sip/src/com/android/services/telephony/sip/SipAccountRegistry.java
@@ -16,21 +16,15 @@
 
 package com.android.services.telephony.sip;
 
-import android.content.ComponentName;
 import android.content.Context;
-import android.net.Uri;
 import android.net.sip.SipException;
 import android.net.sip.SipManager;
 import android.net.sip.SipProfile;
-import android.provider.Settings;
 import android.telecomm.PhoneAccount;
 import android.telecomm.PhoneAccountHandle;
 import android.telecomm.TelecommManager;
 import android.util.Log;
 
-import com.android.phone.R;
-
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -50,41 +44,58 @@
             return mProfile;
         }
 
-        boolean register(SipManager sipManager, Context context) {
-            if (VERBOSE) log("register, profile: " + mProfile);
+        /**
+         * Starts the SIP service associated with the SIP profile.
+         *
+         * @param sipManager The SIP manager.
+         * @param context The context.
+         * @param isReceivingCalls {@code True} if the sip service is being started to make and
+         *          receive calls.  {@code False} if the sip service is being started only for
+         *          outgoing calls.
+         * @return {@code True} if the service started successfully.
+         */
+        boolean startSipService(SipManager sipManager, Context context, boolean isReceivingCalls) {
+            if (VERBOSE) log("startSipService, profile: " + mProfile);
             try {
-                sipManager.open(
-                        mProfile,
-                        SipUtil.createIncomingCallPendingIntent(context, mProfile.getUriString()),
-                        null);
-                TelecommManager.from(context).registerPhoneAccount(createPhoneAccount(context));
+                // Stop the Sip service for the profile if it is already running.  This is important
+                // if we are changing the state of the "receive calls" option.
+                sipManager.close(mProfile.getUriString());
+
+                // Start the sip service for the profile.
+                if (isReceivingCalls) {
+                    sipManager.open(
+                            mProfile,
+                            SipUtil.createIncomingCallPendingIntent(context,
+                                    mProfile.getUriString()),
+                            null);
+                } else {
+                    sipManager.open(mProfile);
+                }
                 return true;
             } catch (SipException e) {
-                log("register, profile: " + mProfile.getProfileName() +
+                log("startSipService, profile: " + mProfile.getProfileName() +
                         ", exception: " + e);
             }
             return false;
         }
 
-        private PhoneAccount createPhoneAccount(Context context) {
-            boolean useSipForPstnCalls = useSipForPstnCalls(context);
-
-            PhoneAccountHandle accountHandle =
-                    SipUtil.createAccountHandle(context, mProfile.getUriString());
-            List supportedUriSchemes = Arrays.asList(PhoneAccount.SCHEME_SIP);
-            if (useSipForPstnCalls) {
-                supportedUriSchemes.add(PhoneAccount.SCHEME_TEL);
+        /**
+         * Stops the SIP service associated with the SIP profile.  The {@code SipAccountRegistry} is
+         * informed when the service has been stopped via an intent which triggers
+         * {@link SipAccountRegistry#removeSipProfile(String)}.
+         *
+         * @param sipManager The SIP manager.
+         * @return {@code True} if stop was successful.
+         */
+        boolean stopSipService(SipManager sipManager) {
+            try {
+                sipManager.close(mProfile.getUriString());
+                return true;
+            } catch (Exception e) {
+                log("stopSipService, stop failed for profile: " + mProfile.getUriString() +
+                        ", exception: " + e);
             }
-
-            PhoneAccount.Builder builder = PhoneAccount.builder(
-                        accountHandle, mProfile.getDisplayName())
-                    .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
-                    .setAddress(Uri.parse(mProfile.getUriString()))
-                    .setShortDescription(mProfile.getDisplayName())
-                    .setIconResId(R.drawable.ic_dialer_sip_black_24dp)
-                    .setSupportedUriSchemes(supportedUriSchemes);
-
-            return builder.build();
+            return false;
         }
     }
 
@@ -101,84 +112,174 @@
     }
 
     void setup(Context context) {
-        clearCurrentSipAccounts(context);
-        registerProfiles(context, null);
+        startSipProfilesAsync((Context) context, (String) null);
     }
 
-    void addPhone(Context context, String sipUri) {
-        registerProfiles(context, sipUri);
+    /**
+     * Starts the SIP service for the specified SIP profile and ensures it has a valid registered
+     * {@link PhoneAccount}.
+     *
+     * @param context The context.
+     * @param sipUri The Uri of the {@link SipProfile} to start, or {@code null} for all.
+     */
+    void startSipService(Context context, String sipUri) {
+        startSipProfilesAsync((Context) context, (String) sipUri);
     }
 
-    void removePhone(Context context, String sipUri) {
-        for (AccountEntry entry : mAccounts) {
-            if (Objects.equals(sipUri, entry.getProfile().getUriString())) {
-                TelecommManager.from(context).unregisterPhoneAccount(
-                        SipUtil.createAccountHandle(context, sipUri));
-                mAccounts.remove(entry);
-                break;
-            }
+    /**
+     * Removes a {@link SipProfile} from the account registry.  Does not stop/close the associated
+     * SIP service (this method is invoked via an intent from the SipService once a profile has
+     * been stopped/closed).
+     *
+     * @param sipUri The Uri of the {@link SipProfile} to remove from the registry.
+     */
+    void removeSipProfile(String sipUri) {
+        AccountEntry accountEntry = getAccountEntry(sipUri);
+
+        if (accountEntry != null) {
+            mAccounts.remove(accountEntry);
         }
     }
 
     /**
+     * Stops a SIP profile and un-registers its associated {@link android.telecomm.PhoneAccount}.
+     * Called after a SIP profile is deleted.  The {@link AccountEntry} will be removed when the
+     * service has been stopped.  The {@code SipService} fires the {@code ACTION_SIP_REMOVE_PHONE}
+     * intent, which triggers {@link SipAccountRegistry#removeSipProfile(String)} to perform the
+     * removal.
+     *
+     * @param context The context.
+     * @param sipUri The {@code Uri} of the sip profile.
+     */
+    void stopSipService(Context context, String sipUri) {
+        // Stop the sip service for the profile.
+        AccountEntry accountEntry = getAccountEntry(sipUri);
+        if (accountEntry != null ) {
+            SipManager sipManager = SipManager.newInstance(context);
+            accountEntry.stopSipService(sipManager);
+        }
+
+        // Un-register its PhoneAccount.
+        PhoneAccountHandle handle = SipUtil.createAccountHandle(context, sipUri);
+        TelecommManager.from(context).unregisterPhoneAccount(handle);
+    }
+
+    /**
+     * Causes the SIP service to be restarted for all {@link SipProfile}s.  For example, if the user
+     * toggles the "receive calls" option for SIP, this method handles restarting the SIP services
+     * in the new mode.
+     *
+     * @param context The context.
+     */
+    void restartSipService(Context context) {
+        startSipProfiles(context, null);
+    }
+
+    /**
+     * Performs an asynchronous call to
+     * {@link SipAccountRegistry#startSipProfiles(android.content.Context, String)}, starting the
+     * specified SIP profile and registering its {@link android.telecomm.PhoneAccount}.
+     *
+     * @param context The context.
+     * @param sipUri A specific SIP uri to start.
+     */
+    private void startSipProfilesAsync(final Context context, final String sipUri) {
+        if (VERBOSE) log("startSipProfiles, start auto registration");
+
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                startSipProfiles(context, sipUri);
+            }}
+        ).start();
+    }
+
+    /**
      * Loops through all SIP accounts from the SIP database, starts each service and registers
      * each with the telecomm framework. If a specific sipUri is specified, this will only register
      * the associated SIP account.
      *
+     * Also handles migration from using the primary account shared preference to indicate the
+     * {@link SipProfile} to be used for outgoing Sip connections to using the enabled flag on
+     * {@link android.telecomm.PhoneAccount}s to indicate which profiles can be used for outgoing
+     * or ingoing calls.
+     *
      * @param context The context.
-     * @param sipUri A specific SIP uri to register.
+     * @param sipUri A specific SIP uri to start, or {@code null} to start all.
      */
-    private void registerProfiles(final Context context, final String sipUri) {
-        if (VERBOSE) log("registerProfiles, start auto registration");
+    private void startSipProfiles(Context context, String sipUri) {
         final SipSharedPreferences sipSharedPreferences = new SipSharedPreferences(context);
-        new Thread(new Runnable() {
-            @Override
-            public void run() {
-                SipManager sipManager = SipManager.newInstance(context);
-                SipProfileDb profileDb = new SipProfileDb(context);
-                String primaryProfile = sipSharedPreferences.getPrimaryAccount();
-                List<SipProfile> sipProfileList = profileDb.retrieveSipProfileList();
-
-                for (SipProfile profile : sipProfileList) {
-                    boolean isPrimaryProfile = profile.getUriString().equals(primaryProfile);
-                    if (profile.getAutoRegistration() || isPrimaryProfile) {
-                        if (sipUri == null || Objects.equals(sipUri, profile.getUriString())) {
-                            registerAccountForProfile(profile, sipManager, context);
-                        }
-                    }
-                }
-            }}
-        ).start();
-    }
-
-    private void registerAccountForProfile(
-            SipProfile profile, SipManager sipManager, Context context) {
-        AccountEntry entry = new AccountEntry(profile);
-
-        if (entry.register(sipManager, context)) {
-            mAccounts.add(entry);
-        }
-    }
-
-    private void clearCurrentSipAccounts(Context context) {
-        ComponentName sipComponentName = new ComponentName(context, SipConnectionService.class);
+        boolean isReceivingCalls = sipSharedPreferences.isReceivingCallsEnabled();
+        String primaryProfile = sipSharedPreferences.getPrimaryAccount();
         TelecommManager telecommManager = TelecommManager.from(context);
-        List<PhoneAccountHandle> accountHandles = telecommManager.getEnabledPhoneAccounts();
-        for (PhoneAccountHandle handle : accountHandles) {
-            if (sipComponentName.equals(handle.getComponentName())) {
-                telecommManager.unregisterPhoneAccount(handle);
+        SipManager sipManager = SipManager.newInstance(context);
+        SipProfileDb profileDb = new SipProfileDb(context);
+        List<SipProfile> sipProfileList = profileDb.retrieveSipProfileList();
+
+        for (SipProfile profile : sipProfileList) {
+            // Register a PhoneAccount for the profile and optionally enable the primary
+            // profile.
+            if (sipUri == null || Objects.equals(sipUri, profile.getUriString())) {
+                PhoneAccount phoneAccount = SipUtil.createPhoneAccount(context, profile);
+                telecommManager.registerPhoneAccount(phoneAccount);
+
+                // If this profile was the primary profile in the past, mark it as enabled.
+                boolean isPrimaryProfile = primaryProfile != null &&
+                        profile.getUriString().equals(primaryProfile);
+                if (isPrimaryProfile) {
+                    telecommManager.setPhoneAccountEnabled(
+                            phoneAccount.getAccountHandle(), true);
+                }
             }
+
+            // Start the SIP service for the profile.
+            if (SipUtil.isPhoneAccountEnabled(context, profile)) {
+                if (sipUri == null || Objects.equals(sipUri, profile.getUriString())) {
+                    startSipServiceForProfile(profile, sipManager, context, isReceivingCalls);
+                }
+            }
+        }
+
+        if (primaryProfile != null) {
+            // Remove the primary account shared preference, ensuring the migration does not
+            // occur again in the future.
+            sipSharedPreferences.cleanupPrimaryAccountSetting();
         }
     }
 
     /**
-     * Determines if the user has chosen to use SIP for PSTN calls as well as SIP calls.
+     * Starts the SIP service for a sip profile and saves a new {@code AccountEntry} in the
+     * registry.
+     *
+     * @param profile The {@link SipProfile} to start.
+     * @param sipManager The SIP manager.
      * @param context The context.
-     * @return {@code True} if SIP should be used for PSTN calls.
+     * @param isReceivingCalls {@code True} if the profile should be started such that it can
+     *      receive incoming calls.
      */
-    private boolean useSipForPstnCalls(Context context) {
-        final SipSharedPreferences sipSharedPreferences = new SipSharedPreferences(context);
-        return sipSharedPreferences.getSipCallOption().equals(Settings.System.SIP_ALWAYS);
+    private void startSipServiceForProfile(SipProfile profile, SipManager sipManager,
+            Context context, boolean isReceivingCalls) {
+        removeSipProfile(profile.getUriString());
+
+        AccountEntry entry = new AccountEntry(profile);
+        if (entry.startSipService(sipManager, context, isReceivingCalls)) {
+            mAccounts.add(entry);
+        }
+    }
+
+    /**
+     * Retrieves the {@link AccountEntry} from the registry with the specified Uri.
+     *
+     * @param sipUri The Uri of the profile to retrieve.
+     * @return The {@link AccountEntry}, or {@code null} is it was not found.
+     */
+    private AccountEntry getAccountEntry(String sipUri) {
+        for (AccountEntry entry : mAccounts) {
+            if (Objects.equals(sipUri, entry.getProfile().getUriString())) {
+                return entry;
+            }
+        }
+        return null;
     }
 
     private void log(String message) {
diff --git a/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java b/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java
index a113805..35c07a1 100644
--- a/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java
+++ b/sip/src/com/android/services/telephony/sip/SipBroadcastReceiver.java
@@ -47,14 +47,10 @@
         } else if (action.equals(SipManager.ACTION_SIP_SERVICE_UP) ||
                 action.equals(SipManager.ACTION_SIP_CALL_OPTION_CHANGED)) {
             sipAccountRegistry.setup(context);
-        } else if (action.equals(SipManager.ACTION_SIP_ADD_PHONE)) {
-            if (VERBOSE) log("SIP_ADD_PHONE " + intent.getStringExtra(SipManager.EXTRA_LOCAL_URI));
-            sipAccountRegistry.addPhone(context, intent.getStringExtra(SipManager.EXTRA_LOCAL_URI));
         } else if (action.equals(SipManager.ACTION_SIP_REMOVE_PHONE)) {
             if (VERBOSE) log("SIP_REMOVE_PHONE " +
-                    intent.getStringExtra(SipManager.EXTRA_LOCAL_URI));
-            sipAccountRegistry.removePhone(
-                    context, intent.getStringExtra(SipManager.EXTRA_LOCAL_URI));
+                            intent.getStringExtra(SipManager.EXTRA_LOCAL_URI));
+            sipAccountRegistry.removeSipProfile(intent.getStringExtra(SipManager.EXTRA_LOCAL_URI));
         } else {
             if (VERBOSE) log("onReceive, action not processed: " + action);
         }
diff --git a/sip/src/com/android/services/telephony/sip/SipConnectionService.java b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
index 8956454..16efc4d 100644
--- a/sip/src/com/android/services/telephony/sip/SipConnectionService.java
+++ b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
@@ -61,12 +61,6 @@
         super.onCreate();
     }
 
-    static PhoneAccountHandle getPhoneAccountHandle(Context context) {
-        return new PhoneAccountHandle(
-                new ComponentName(context, SipConnectionService.class),
-                null /* id */);
-    }
-
     @Override
     public Connection onCreateOutgoingConnection(
             PhoneAccountHandle connectionManagerAccount,
@@ -86,7 +80,6 @@
                     DisconnectCause.OUTGOING_FAILURE, "Did not match service connection");
         }
 
-
         final SipConnection connection = new SipConnection();
         connection.setHandle(request.getAddress(), TelecommManager.PRESENTATION_ALLOWED);
         connection.setInitializing();
diff --git a/sip/src/com/android/services/telephony/sip/SipEditor.java b/sip/src/com/android/services/telephony/sip/SipEditor.java
index b35f28c..fbf3bd1 100644
--- a/sip/src/com/android/services/telephony/sip/SipEditor.java
+++ b/sip/src/com/android/services/telephony/sip/SipEditor.java
@@ -16,11 +16,6 @@
 
 package com.android.services.telephony.sip;
 
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.services.telephony.sip.SipUtil;
-
-import android.app.ActionBar;
 import android.app.AlertDialog;
 import android.content.Intent;
 import android.net.sip.SipManager;
@@ -38,7 +33,6 @@
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
-import android.view.View;
 import android.widget.Button;
 import android.widget.Toast;
 
@@ -63,18 +57,16 @@
     private static final char SCRAMBLED = '*';
     private static final int NA = 0;
 
-    private PrimaryAccountSelector mPrimaryAccountSelector;
     private AdvancedSettings mAdvancedSettings;
     private SipSharedPreferences mSharedPreferences;
     private boolean mDisplayNameSet;
     private boolean mHomeButtonClicked;
     private boolean mUpdateRequired;
-    private boolean mUpdatedFieldIsEmpty;
 
-    private SipManager mSipManager;
     private SipProfileDb mProfileDb;
     private SipProfile mOldProfile;
     private Button mRemoveButton;
+    private SipAccountRegistry mSipAccountRegistry;
 
     enum PreferenceKey {
         Username(R.string.username, 0, R.string.default_preference_summary),
@@ -159,9 +151,9 @@
         if (VERBOSE) log("onCreate, start profile editor");
         super.onCreate(savedInstanceState);
 
-        mSipManager = SipManager.newInstance(this);
         mSharedPreferences = new SipSharedPreferences(this);
         mProfileDb = new SipProfileDb(this);
+        mSipAccountRegistry = SipAccountRegistry.getInstance();
 
         setContentView(R.layout.sip_settings_ui);
         addPreferencesFromResource(R.xml.sip_edit);
@@ -180,7 +172,6 @@
         }
 
         mAdvancedSettings = new AdvancedSettings();
-        mPrimaryAccountSelector = new PrimaryAccountSelector(p);
 
         loadPreferencesFromProfile(p);
     }
@@ -244,32 +235,29 @@
         return super.onKeyDown(keyCode, event);
     }
 
+    /**
+     * Saves a {@link SipProfile} and registers the associated
+     * {@link android.telecomm.PhoneAccount}.
+     *
+     * @param p The {@link SipProfile} to register.
+     * @throws IOException Exception resulting from profile save.
+     */
     private void saveAndRegisterProfile(SipProfile p) throws IOException {
         if (p == null) return;
         mProfileDb.saveProfile(p);
-        if (p.getAutoRegistration() || mSharedPreferences.isPrimaryAccount(p.getUriString())) {
-            try {
-                mSipManager.open(
-                        p, SipUtil.createIncomingCallPendingIntent(this, p.getUriString()), null);
-            } catch (Exception e) {
-                log("saveAndRegisterProfile, register failed for profile: " + p.getUriString() +
-                        ", exception: " + e);
-            }
-        }
+        mSipAccountRegistry.startSipService(this, p.getUriString());
     }
 
+    /**
+     * Deletes a {@link SipProfile} and un-registers the associated
+     * {@link android.telecomm.PhoneAccount}.
+     *
+     * @param p The {@link SipProfile} to delete.
+     */
     private void deleteAndUnregisterProfile(SipProfile p) {
         if (p == null) return;
         mProfileDb.deleteProfile(p);
-        unregisterProfile(p.getUriString());
-    }
-
-    private void unregisterProfile(String uri) {
-        try {
-            mSipManager.close(uri);
-        } catch (Exception e) {
-            log("unregisterProfile, unregister failed for profile: " + uri + ", exception: " + e);
-        }
+        mSipAccountRegistry.stopSipService(this, p.getUriString());
     }
 
     private void setRemovedProfileAndFinish() {
@@ -371,15 +359,6 @@
         }
     }
 
-    private void unregisterOldPrimaryAccount() {
-        String primaryAccountUri = mSharedPreferences.getPrimaryAccount();
-        if (VERBOSE) log("unregisterOldPrimaryAccount, old primary: " + primaryAccountUri);
-        if ((primaryAccountUri != null) && !mSharedPreferences.isReceivingCallsEnabled()) {
-            if (VERBOSE) log("unregisterOldPrimaryAccount, calling unregister");
-            unregisterProfile(primaryAccountUri);
-        }
-    }
-
     private void replaceProfile(final SipProfile oldProfile, final SipProfile newProfile) {
         // Replace profile in a background thread as it takes time to access the
         // storage; do finish() once everything goes fine.
@@ -388,12 +367,6 @@
         new Thread(new Runnable() {
             public void run() {
                 try {
-                    // if new profile is primary, unregister the old primary account
-                    if ((newProfile != null) && mPrimaryAccountSelector.isSelected()) {
-                        unregisterOldPrimaryAccount();
-                    }
-
-                    mPrimaryAccountSelector.commit(newProfile);
                     deleteAndUnregisterProfile(oldProfile);
                     saveAndRegisterProfile(newProfile);
                     finish();
@@ -430,10 +403,8 @@
     public boolean onPreferenceChange(Preference pref, Object newValue) {
         if (!mUpdateRequired) {
             mUpdateRequired = true;
-            if (mOldProfile != null) {
-                unregisterProfile(mOldProfile.getUriString());
-            }
         }
+
         if (pref instanceof CheckBoxPreference) {
             invalidateOptionsMenu();
             return true;
@@ -547,47 +518,6 @@
         return new String(cc);
     }
 
-    // only takes care of the primary account setting in SipSharedSettings
-    private class PrimaryAccountSelector {
-        private CheckBoxPreference mCheckbox;
-        private final boolean mWasPrimaryAccount;
-
-        // @param profile profile to be edited; null if adding new profile
-        PrimaryAccountSelector(SipProfile profile) {
-            mCheckbox = (CheckBoxPreference) getPreferenceScreen()
-                    .findPreference(getString(R.string.set_primary));
-            boolean noPrimaryAccountSet = !mSharedPreferences.hasPrimaryAccount();
-            boolean editNewProfile = (profile == null);
-            mWasPrimaryAccount = !editNewProfile && mSharedPreferences.isPrimaryAccount(
-                    profile.getUriString());
-
-            if (VERBOSE) {
-                log(" noPrimaryAccountSet: " + noPrimaryAccountSet);
-                log(" editNewProfile: " + editNewProfile);
-                log(" mWasPrimaryAccount: " + mWasPrimaryAccount);
-            }
-
-            mCheckbox.setChecked(mWasPrimaryAccount || (editNewProfile && noPrimaryAccountSet));
-        }
-
-        boolean isSelected() {
-            return mCheckbox.isChecked();
-        }
-
-        // profile is null if the user removes it
-        void commit(SipProfile profile) {
-            if ((profile != null) && mCheckbox.isChecked()) {
-                mSharedPreferences.setPrimaryAccount(profile.getUriString());
-            } else if (mWasPrimaryAccount) {
-                mSharedPreferences.unsetPrimaryAccount();
-            }
-            if (VERBOSE) {
-                log("PrimaryAccountSelector.commit, new primary account: " +
-                        mSharedPreferences.getPrimaryAccount());
-            }
-        }
-    }
-
     private class AdvancedSettings implements Preference.OnPreferenceClickListener {
         private Preference mAdvancedSettingsTrigger;
         private Preference[] mPreferences;
diff --git a/sip/src/com/android/services/telephony/sip/SipSettings.java b/sip/src/com/android/services/telephony/sip/SipSettings.java
index f6e6101..1dca310 100644
--- a/sip/src/com/android/services/telephony/sip/SipSettings.java
+++ b/sip/src/com/android/services/telephony/sip/SipSettings.java
@@ -39,6 +39,8 @@
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceCategory;
+import android.telecomm.PhoneAccount;
+import android.telecomm.TelecommManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
@@ -104,13 +106,9 @@
 
         void updateSummary(String registrationStatus) {
             int profileUid = mProfile.getCallingUid();
-            boolean isPrimary = mProfile.getUriString().equals(
-                    mSipSharedPreferences.getPrimaryAccount());
             if (VERBOSE) {
                 log("SipPreference.updateSummary, profile uid: " + profileUid +
-                        " isPrimary: " + isPrimary +
                         " registration: " + registrationStatus +
-                        " Primary: " + mSipSharedPreferences.getPrimaryAccount() +
                         " status: " + registrationStatus);
             }
             String summary = "";
@@ -118,9 +116,6 @@
                 // from third party apps
                 summary = getString(R.string.third_party_account_summary,
                         getPackageNameFromUid(profileUid));
-            } else if (isPrimary) {
-                summary = getString(R.string.primary_account_summary_with,
-                        registrationStatus);
             } else {
                 summary = registrationStatus;
             }
@@ -219,36 +214,35 @@
                 });
     }
 
-    private synchronized void handleSipReceiveCallsOption(boolean enabled) {
-        mSipSharedPreferences.setReceivingCallsEnabled(enabled);
+    /**
+     * Handles changes to the "receive calls" option.
+     *
+     * @param isReceivingCalls {@code True} if receiving incoming SIP calls.
+     */
+    private synchronized void handleSipReceiveCallsOption(boolean isReceivingCalls) {
+        mSipSharedPreferences.setReceivingCallsEnabled(isReceivingCalls);
+
+        // Mark all profiles as auto-register if we are now receiving calls.
         List<SipProfile> sipProfileList = mProfileDb.retrieveSipProfileList();
         for (SipProfile p : sipProfileList) {
-            String sipUri = p.getUriString();
-            p = updateAutoRegistrationFlag(p, enabled);
-            try {
-                if (enabled) {
-                    mSipManager.open(
-                            p, SipUtil.createIncomingCallPendingIntent(this, sipUri), null);
-                } else {
-                    mSipManager.close(sipUri);
-                    if (mSipSharedPreferences.isPrimaryAccount(sipUri)) {
-                        // re-open in order to make calls
-                        mSipManager.open(p);
-                    }
-                }
-            } catch (Exception e) {
-                log("handleSipReceiveCallsOption, register failed: " + e);
-            }
+            p = updateAutoRegistrationFlag(p, isReceivingCalls);
         }
+
+        // Restart all Sip services to ensure we reflect whether we are receiving calls.
+        SipAccountRegistry sipAccountRegistry = SipAccountRegistry.getInstance();
+        sipAccountRegistry.restartSipService(this);
+
         updateProfilesStatus();
     }
 
-    private SipProfile updateAutoRegistrationFlag(
-            SipProfile p, boolean enabled) {
+    private SipProfile updateAutoRegistrationFlag(SipProfile p, boolean enabled) {
         SipProfile newProfile = new SipProfile.Builder(p)
                 .setAutoRegistration(enabled)
                 .build();
         try {
+            // Note: The profile is updated, but the associated PhoneAccount is left alone since
+            // the only thing that changed is the auto-registration flag, which is not part of the
+            // PhoneAccount.
             mProfileDb.deleteProfile(p);
             mProfileDb.saveProfile(newProfile);
         } catch (Exception e) {
diff --git a/sip/src/com/android/services/telephony/sip/SipSharedPreferences.java b/sip/src/com/android/services/telephony/sip/SipSharedPreferences.java
index b5d1fc2..bad7dd5 100644
--- a/sip/src/com/android/services/telephony/sip/SipSharedPreferences.java
+++ b/sip/src/com/android/services/telephony/sip/SipSharedPreferences.java
@@ -16,14 +16,12 @@
 
 package com.android.services.telephony.sip;
 
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.net.sip.SipManager;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
-import android.text.TextUtils;
 import android.util.Log;
 
 /**
@@ -34,7 +32,13 @@
     private static final boolean VERBOSE = false; /* STOP SHIP if true */
 
     private static final String SIP_SHARED_PREFERENCES = "SIP_PREFERENCES";
+
+    /**
+     * @deprecated Primary account selection for SIP accounts is no longer relevant.
+     */
+    @Deprecated
     private static final String KEY_PRIMARY_ACCOUNT = "primary";
+
     private static final String KEY_NUMBER_OF_PROFILES = "profiles";
 
     private SharedPreferences mPreferences;
@@ -46,31 +50,15 @@
         mContext = context;
     }
 
-    public void setPrimaryAccount(String accountUri) {
-        SharedPreferences.Editor editor = mPreferences.edit();
-        editor.putString(KEY_PRIMARY_ACCOUNT, accountUri);
-        editor.apply();
-    }
-
-    public void unsetPrimaryAccount() {
-        setPrimaryAccount(null);
-    }
-
-    /** Returns the primary account URI or null if it does not exist. */
+    /**
+     * Returns the primary account URI or null if it does not exist.
+     * @deprecated The primary account setting is no longer used.
+     */
+    @Deprecated
     public String getPrimaryAccount() {
         return mPreferences.getString(KEY_PRIMARY_ACCOUNT, null);
     }
 
-    public boolean isPrimaryAccount(String accountUri) {
-        return accountUri.equals(
-                mPreferences.getString(KEY_PRIMARY_ACCOUNT, null));
-    }
-
-    public boolean hasPrimaryAccount() {
-        return !TextUtils.isEmpty(
-                mPreferences.getString(KEY_PRIMARY_ACCOUNT, null));
-    }
-
     public void setProfilesCount(int number) {
         SharedPreferences.Editor editor = mPreferences.edit();
         editor.putInt(KEY_NUMBER_OF_PROFILES, number);
@@ -114,6 +102,18 @@
         }
     }
 
+    /**
+     * Performs cleanup of the shared preferences, removing the deprecated primary account key if
+     * it exists.
+     */
+    public void cleanupPrimaryAccountSetting() {
+        if (mPreferences.contains(KEY_PRIMARY_ACCOUNT)) {
+            SharedPreferences.Editor editor = mPreferences.edit();
+            editor.remove(KEY_PRIMARY_ACCOUNT);
+            editor.apply();
+        }
+    }
+
     // TODO: back up to Android Backup
 
     private static void log(String msg) {
diff --git a/sip/src/com/android/services/telephony/sip/SipUtil.java b/sip/src/com/android/services/telephony/sip/SipUtil.java
index e926fa1..340ebda 100644
--- a/sip/src/com/android/services/telephony/sip/SipUtil.java
+++ b/sip/src/com/android/services/telephony/sip/SipUtil.java
@@ -16,14 +16,23 @@
 
 package com.android.services.telephony.sip;
 
+import com.android.phone.R;
+
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.net.Uri;
 import android.net.sip.SipManager;
+import android.net.sip.SipProfile;
+import android.provider.Settings;
+import android.telecomm.PhoneAccount;
 import android.telecomm.PhoneAccountHandle;
 import android.telecomm.TelecommManager;
 
+import java.util.Arrays;
+import java.util.List;
+
 public class SipUtil {
     static final String LOG_TAG = "SIP";
     static final String EXTRA_INCOMING_CALL_INTENT =
@@ -33,21 +42,15 @@
     static final String GATEWAY_PROVIDER_PACKAGE =
             "com.android.phone.extra.GATEWAY_PROVIDER_PACKAGE";
 
-    private static boolean sIsVoipSupported;
-    private static boolean sIsVoipSupportedInitialized;
-
     private SipUtil() {
     }
 
     public static boolean isVoipSupported(Context context) {
-        if (!sIsVoipSupportedInitialized) {
-            sIsVoipSupported = SipManager.isVoipSupported(context) &&
-                    context.getResources().getBoolean(
-                            com.android.internal.R.bool.config_built_in_sip_phone) &&
-                    context.getResources().getBoolean(
-                            com.android.internal.R.bool.config_voice_capable);
-        }
-        return sIsVoipSupported;
+        return SipManager.isVoipSupported(context) &&
+                context.getResources().getBoolean(
+                        com.android.internal.R.bool.config_built_in_sip_phone) &&
+                context.getResources().getBoolean(
+                        com.android.internal.R.bool.config_voice_capable);
     }
 
     static PendingIntent createIncomingCallPendingIntent(
@@ -74,4 +77,55 @@
         return new PhoneAccountHandle(
                 new ComponentName(context, SipConnectionService.class), sipUri);
     }
+
+    /**
+     * Determines if the {@link android.telecomm.PhoneAccount} associated with a {@link SipProfile}
+     * is enabled.
+     *
+     * @param context The {@link Context}.
+     * @param profile The {@link SipProfile}.
+     * @return {@code True} if the {@code PhoneAccount} is enabled.
+     */
+    static boolean isPhoneAccountEnabled(Context context, SipProfile profile) {
+        PhoneAccount phoneAccount = TelecommManager.from(context)
+                .getPhoneAccount(SipUtil.createAccountHandle(context, profile.getUriString()));
+        return phoneAccount != null && phoneAccount.isEnabled();
+    }
+
+    /**
+     * Creates a PhoneAccount for a SipProfile.
+     *
+     * @param context The context
+     * @param profile The SipProfile.
+     * @return The PhoneAccount.
+     */
+    static PhoneAccount createPhoneAccount(Context context, SipProfile profile) {
+
+        PhoneAccountHandle accountHandle =
+                SipUtil.createAccountHandle(context, profile.getUriString());
+
+        List supportedUriSchemes = Arrays.asList(PhoneAccount.SCHEME_SIP);
+        if (useSipForPstnCalls(context)) {
+            supportedUriSchemes.add(PhoneAccount.SCHEME_TEL);
+        }
+
+        PhoneAccount.Builder builder = PhoneAccount.builder(accountHandle, profile.getDisplayName())
+                .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                .setAddress(Uri.parse(profile.getUriString()))
+                .setShortDescription(profile.getDisplayName())
+                .setIconResId(R.drawable.ic_dialer_sip_black_24dp)
+                .setSupportedUriSchemes(supportedUriSchemes);
+
+        return builder.build();
+    }
+
+    /**
+     * Determines if the user has chosen to use SIP for PSTN calls as well as SIP calls.
+     * @param context The context.
+     * @return {@code True} if SIP should be used for PSTN calls.
+     */
+    private static boolean useSipForPstnCalls(Context context) {
+        final SipSharedPreferences sipSharedPreferences = new SipSharedPreferences(context);
+        return sipSharedPreferences.getSipCallOption().equals(Settings.System.SIP_ALWAYS);
+    }
 }
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 88de152..bb23292 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -465,6 +465,11 @@
      */
     private boolean mReadingSettingsForDefaultProvider = false;
 
+    /**
+     * Used to indicate that the voicemail preference should be shown.
+     */
+    private boolean mShowVoicemailPreference = false;
+
     /*
      * Click Listeners, handle click based on objects attached to UI.
      */
@@ -603,6 +608,22 @@
         return false;
     }
 
+    /**
+     * Called just prior to showing an AccountSelection dialog to re-populate the model of the
+     * AccountSelection dialog.  Important to ensure changes to the enabled state of
+     * {@code PhoneAccount}s are reflected in the dialog.
+     *
+     * @param pref The account selection preference dialog being shown.
+     */
+    @Override
+    public void onAccountSelectionDialogShow(AccountSelectionPreference pref) {
+        if (pref == mDefaultOutgoingAccount) {
+            populateDefaultOutgoingAccountsModel();
+        } else if (pref == mSimCallManagerAccount) {
+            populateSimCallManagerAccountsModel();
+        }
+    }
+
     @Override
     public void onDialogClosed(EditPhoneNumberPreference preference, int buttonClicked) {
         if (DBG) log("onPreferenceClick: request preference click on dialog close: " +
@@ -1509,13 +1530,121 @@
         super.onCreate(icicle);
         if (DBG) log("onCreate(). Intent: " + getIntent());
         mPhone = PhoneGlobals.getPhone();
+        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+
+        // create intent to bring up contact list
+        mContactListIntent = new Intent(Intent.ACTION_GET_CONTENT);
+        mContactListIntent.setType(android.provider.Contacts.Phones.CONTENT_ITEM_TYPE);
+
+        mVoicemailRingtoneLookupRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (mVoicemailNotificationRingtone != null) {
+                    SettingsUtil.updateRingtoneName(
+                            mPhone.getContext(),
+                            mVoicemailRingtoneLookupComplete,
+                            RingtoneManager.TYPE_NOTIFICATION,
+                            mVoicemailNotificationRingtone,
+                            MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY);
+                }
+            }
+        };
+
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            // android.R.id.home will be triggered in onOptionsItemSelected()
+            actionBar.setDisplayShowHomeEnabled(true);
+            actionBar.setDisplayHomeAsUpEnabled(true);
+            actionBar.setDisplayShowTitleEnabled(true);
+        }
+
+        // Show the voicemail preference in onResume if the calling intent specifies the
+        // ACTION_ADD_VOICEMAIL action.
+        mShowVoicemailPreference = (icicle == null) &&
+                getIntent().getAction().equals(ACTION_ADD_VOICEMAIL);
+    }
+
+    private void initPhoneAccountPreferences() {
+        mDefaultOutgoingAccount = (AccountSelectionPreference)
+                findPreference(DEFAULT_OUTGOING_ACCOUNT_KEY);
+        mSimCallManagerAccount = (AccountSelectionPreference)
+                findPreference(WIFI_CALL_MANAGER_ACCOUNT_KEY);
+
+        TelecommManager telecommManager = TelecommManager.from(this);
+
+        int allPhoneAccountsCount = telecommManager.getAllPhoneAccountsCount();
+        // Show the phone accounts preference if there are is more than one phone account (this
+        // includes disabled phone accounts).  The default selection, however, only includes those
+        // PhoneAccounts which are enabled.
+        if (allPhoneAccountsCount > 1) {
+            populateDefaultOutgoingAccountsModel();
+
+            mDefaultOutgoingAccount.setListener(this);
+        } else {
+            getPreferenceScreen().removePreference(mDefaultOutgoingAccount);
+        }
+
+        List<PhoneAccountHandle> simCallManagers = telecommManager.getSimCallManagers();
+        if (!simCallManagers.isEmpty()) {
+            populateSimCallManagerAccountsModel();
+            mSimCallManagerAccount.setListener(this);
+        } else {
+            getPreferenceScreen().removePreference(mSimCallManagerAccount);
+        }
+    }
+
+    private void createSipCallSettings() {
+        // Add Internet call settings.
+        if (SipUtil.isVoipSupported(this)) {
+            mSipManager = SipManager.newInstance(this);
+            mSipSharedPreferences = new SipSharedPreferences(this);
+            addPreferencesFromResource(
+                    com.android.services.telephony.sip.R.xml.sip_settings_category);
+            mButtonSipCallOptions = getSipCallOptionPreference();
+            mButtonSipCallOptions.setOnPreferenceChangeListener(this);
+            mButtonSipCallOptions.setValueIndex(
+                    mButtonSipCallOptions.findIndexOfValue(
+                            mSipSharedPreferences.getSipCallOption()));
+            mButtonSipCallOptions.setSummary(mButtonSipCallOptions.getEntry());
+        }
+    }
+
+    private boolean canLaunchIntent(Intent intent) {
+        PackageManager pm = getPackageManager();
+        return pm.resolveActivity(intent, PackageManager.GET_ACTIVITIES) != null;
+    }
+
+    // Gets the call options for SIP depending on whether SIP is allowed only
+    // on Wi-Fi only; also make the other options preference invisible.
+    private ListPreference getSipCallOptionPreference() {
+        ListPreference wifiAnd3G = (ListPreference)
+                findPreference(BUTTON_SIP_CALL_OPTIONS);
+        ListPreference wifiOnly = (ListPreference)
+                findPreference(BUTTON_SIP_CALL_OPTIONS_WIFI_ONLY);
+        PreferenceScreen sipSettings = (PreferenceScreen)
+                getPreferenceScreen().findPreference(SIP_SETTINGS_PREFERENCE_SCREEN_KEY);
+        if (SipManager.isSipWifiOnly(this)) {
+            sipSettings.removePreference(wifiAnd3G);
+            return wifiOnly;
+        } else {
+            sipSettings.removePreference(wifiOnly);
+            return wifiAnd3G;
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mForeground = true;
+
+        PreferenceScreen preferenceScreen = getPreferenceScreen();
+        if (preferenceScreen != null) {
+            preferenceScreen.removeAll();
+        }
 
         addPreferencesFromResource(R.xml.call_feature_setting);
-
         initPhoneAccountPreferences();
 
-        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
-
         // get buttons
         PreferenceScreen prefSet = getPreferenceScreen();
         mSubMenuVoicemailSettings = (EditPhoneNumberPreference)findPreference(BUTTON_VOICEMAIL_KEY);
@@ -1541,7 +1670,6 @@
             initVoiceMailProviders();
         }
 
-        final ContentResolver contentResolver = getContentResolver();
 
         if (mButtonDTMF != null) {
             if (getResources().getBoolean(R.bool.dtmf_type_enabled)) {
@@ -1609,134 +1737,29 @@
             }
         }
 
-        // create intent to bring up contact list
-        mContactListIntent = new Intent(Intent.ACTION_GET_CONTENT);
-        mContactListIntent.setType(android.provider.Contacts.Phones.CONTENT_ITEM_TYPE);
-
         // check the intent that started this activity and pop up the voicemail
         // dialog if we've been asked to.
         // If we have at least one non default VM provider registered then bring up
         // the selection for the VM provider, otherwise bring up a VM number dialog.
         // We only bring up the dialog the first time we are called (not after orientation change)
-        if (icicle == null) {
-            if (getIntent().getAction().equals(ACTION_ADD_VOICEMAIL) &&
-                    mVoicemailProviders != null) {
-                if (DBG) {
-                    log("ACTION_ADD_VOICEMAIL Intent is thrown. current VM data size: "
-                            + mVMProvidersData.size());
-                }
-                if (mVMProvidersData.size() > 1) {
-                    simulatePreferenceClick(mVoicemailProviders);
-                } else {
-                    onPreferenceChange(mVoicemailProviders, DEFAULT_VM_PROVIDER_KEY);
-                    mVoicemailProviders.setValue(DEFAULT_VM_PROVIDER_KEY);
-                }
+        if (mShowVoicemailPreference && mVoicemailProviders != null) {
+            if (DBG) {
+                log("ACTION_ADD_VOICEMAIL Intent is thrown. current VM data size: "
+                        + mVMProvidersData.size());
             }
+            if (mVMProvidersData.size() > 1) {
+                simulatePreferenceClick(mVoicemailProviders);
+            } else {
+                onPreferenceChange(mVoicemailProviders, DEFAULT_VM_PROVIDER_KEY);
+                mVoicemailProviders.setValue(DEFAULT_VM_PROVIDER_KEY);
+            }
+            mShowVoicemailPreference = false;
         }
+
         updateVoiceNumberField();
         mVMProviderSettingsForced = false;
         createSipCallSettings();
 
-        mVoicemailRingtoneLookupRunnable = new Runnable() {
-            @Override
-            public void run() {
-                if (mVoicemailNotificationRingtone != null) {
-                    SettingsUtil.updateRingtoneName(
-                            mPhone.getContext(),
-                            mVoicemailRingtoneLookupComplete,
-                            RingtoneManager.TYPE_NOTIFICATION,
-                            mVoicemailNotificationRingtone,
-                            MSG_UPDATE_VOICEMAIL_RINGTONE_SUMMARY);
-                }
-            }
-        };
-
-        ActionBar actionBar = getActionBar();
-        if (actionBar != null) {
-            // android.R.id.home will be triggered in onOptionsItemSelected()
-            actionBar.setDisplayShowHomeEnabled(true);
-            actionBar.setDisplayHomeAsUpEnabled(true);
-            actionBar.setDisplayShowTitleEnabled(true);
-        }
-    }
-
-    private void initPhoneAccountPreferences() {
-        mDefaultOutgoingAccount = (AccountSelectionPreference)
-                findPreference(DEFAULT_OUTGOING_ACCOUNT_KEY);
-        mSimCallManagerAccount = (AccountSelectionPreference)
-                findPreference(WIFI_CALL_MANAGER_ACCOUNT_KEY);
-
-        TelecommManager telecommManager = TelecommManager.from(this);
-
-        List<PhoneAccountHandle> enabledPhoneAccounts = telecommManager.getEnabledPhoneAccounts();
-        if (enabledPhoneAccounts.size() > 1) {
-            mDefaultOutgoingAccount.setModel(
-                    telecommManager,
-                    enabledPhoneAccounts,
-                    telecommManager.getUserSelectedOutgoingPhoneAccount(),
-                    getString(R.string.phone_accounts_ask_every_time));
-            mDefaultOutgoingAccount.setListener(this);
-        } else {
-            getPreferenceScreen().removePreference(mDefaultOutgoingAccount);
-        }
-
-        List<PhoneAccountHandle> simCallManagers = telecommManager.getSimCallManagers();
-        if (!simCallManagers.isEmpty()) {
-            mSimCallManagerAccount.setModel(
-                    telecommManager,
-                    simCallManagers,
-                    telecommManager.getSimCallManager(),
-                    getString(R.string.wifi_calling_do_not_use));
-            mSimCallManagerAccount.setListener(this);
-        } else {
-            getPreferenceScreen().removePreference(mSimCallManagerAccount);
-        }
-    }
-
-    private void createSipCallSettings() {
-        // Add Internet call settings.
-        if (SipUtil.isVoipSupported(this)) {
-            mSipManager = SipManager.newInstance(this);
-            mSipSharedPreferences = new SipSharedPreferences(this);
-            addPreferencesFromResource(
-                    com.android.services.telephony.sip.R.xml.sip_settings_category);
-            mButtonSipCallOptions = getSipCallOptionPreference();
-            mButtonSipCallOptions.setOnPreferenceChangeListener(this);
-            mButtonSipCallOptions.setValueIndex(
-                    mButtonSipCallOptions.findIndexOfValue(
-                            mSipSharedPreferences.getSipCallOption()));
-            mButtonSipCallOptions.setSummary(mButtonSipCallOptions.getEntry());
-        }
-    }
-
-    private boolean canLaunchIntent(Intent intent) {
-        PackageManager pm = getPackageManager();
-        return pm.resolveActivity(intent, PackageManager.GET_ACTIVITIES) != null;
-    }
-
-    // Gets the call options for SIP depending on whether SIP is allowed only
-    // on Wi-Fi only; also make the other options preference invisible.
-    private ListPreference getSipCallOptionPreference() {
-        ListPreference wifiAnd3G = (ListPreference)
-                findPreference(BUTTON_SIP_CALL_OPTIONS);
-        ListPreference wifiOnly = (ListPreference)
-                findPreference(BUTTON_SIP_CALL_OPTIONS_WIFI_ONLY);
-        PreferenceScreen sipSettings = (PreferenceScreen)
-                getPreferenceScreen().findPreference(SIP_SETTINGS_PREFERENCE_SCREEN_KEY);
-        if (SipManager.isSipWifiOnly(this)) {
-            sipSettings.removePreference(wifiAnd3G);
-            return wifiOnly;
-        } else {
-            sipSettings.removePreference(wifiOnly);
-            return wifiAnd3G;
-        }
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mForeground = true;
-
         if (isAirplaneModeOn()) {
             Preference sipSettings = findPreference(SIP_SETTINGS_PREFERENCE_SCREEN_KEY);
             PreferenceScreen screen = getPreferenceScreen();
@@ -2117,6 +2140,41 @@
     }
 
     /**
+     * Populates the phone accounts which could potentially be selected as the default.
+     */
+    private void populateDefaultOutgoingAccountsModel() {
+        if (mDefaultOutgoingAccount == null ) {
+            return;
+        }
+
+        TelecommManager telecommManager = TelecommManager.from(this);
+        List<PhoneAccountHandle> enabledPhoneAccounts = telecommManager.getEnabledPhoneAccounts();
+        mDefaultOutgoingAccount.setModel(
+                telecommManager,
+                enabledPhoneAccounts,
+                telecommManager.getUserSelectedOutgoingPhoneAccount(),
+                getString(R.string.phone_accounts_ask_every_time));
+    }
+
+    /**
+     * Populates the phone accounts which could potentially be selected as the default sim call
+     * manager.
+     */
+    private void populateSimCallManagerAccountsModel() {
+        if (mSimCallManagerAccount == null) {
+            return;
+        }
+
+        TelecommManager telecommManager = TelecommManager.from(this);
+        List<PhoneAccountHandle> simCallManagers = telecommManager.getSimCallManagers();
+        mSimCallManagerAccount.setModel(
+                telecommManager,
+                simCallManagers,
+                telecommManager.getSimCallManager(),
+                getString(R.string.wifi_calling_do_not_use));
+    }
+
+    /**
      * Deletes settings for the specified provider.
      */
     private void deleteSettingsForVoicemailProvider(String key) {
diff --git a/src/com/android/phone/settings/AccountSelectionPreference.java b/src/com/android/phone/settings/AccountSelectionPreference.java
index 32149ae..8469a18 100644
--- a/src/com/android/phone/settings/AccountSelectionPreference.java
+++ b/src/com/android/phone/settings/AccountSelectionPreference.java
@@ -16,7 +16,13 @@
 
 package com.android.phone.settings;
 
+import com.android.phone.R;
+
+import android.app.AlertDialog;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.UserHandle;
 import android.preference.ListPreference;
 import android.preference.Preference;
 import android.telecomm.PhoneAccountHandle;
@@ -31,8 +37,10 @@
 
     public interface AccountSelectionListener {
         boolean onAccountSelected(AccountSelectionPreference pref, PhoneAccountHandle account);
+        void onAccountSelectionDialogShow(AccountSelectionPreference pref);
     }
 
+    private final Context mContext;
     private AccountSelectionListener mListener;
     private PhoneAccountHandle[] mAccounts;
     private String[] mEntryValues;
@@ -40,11 +48,13 @@
 
     public AccountSelectionPreference(Context context) {
         super(context);
+        mContext = context;
         setOnPreferenceChangeListener(this);
     }
 
     public AccountSelectionPreference(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mContext = context;
         setOnPreferenceChangeListener(this);
     }
 
@@ -93,4 +103,39 @@
         }
         return false;
     }
+
+    /**
+     * Modifies the dialog to change the default "Cancel" button to "Choose Accounts", which
+     * triggers the {@link PhoneAccountSelectionPreferenceActivity} to be shown.
+     *
+     * @param builder The {@code AlertDialog.Builder}.
+     */
+    @Override
+    protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+        // Notify the listener that the dialog is about to be built.  This is important so that the
+        // list of enabled accounts can be updated prior to showing the dialog.
+        mListener.onAccountSelectionDialogShow(this);
+
+        final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int whichButton) {
+                showSelectPhoneAccounts();
+            }
+        };
+        builder.setNegativeButton(R.string.phone_accounts_choose_accounts, listener);
+        super.onPrepareDialogBuilder(builder);
+    }
+
+    /**
+     * Displays the {@link PhoneAccountSelectionPreferenceActivity} where the user is able to
+     * enable and disable phone accounts.
+     */
+    private void showSelectPhoneAccounts() {
+        Intent intent = new Intent(mContext, PhoneAccountSelectionPreferenceActivity.class);
+        intent.setAction(Intent.ACTION_MAIN);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+                | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
+    }
 }
diff --git a/src/com/android/phone/settings/PhoneAccountSelectionPreferenceActivity.java b/src/com/android/phone/settings/PhoneAccountSelectionPreferenceActivity.java
new file mode 100644
index 0000000..7dd1190
--- /dev/null
+++ b/src/com/android/phone/settings/PhoneAccountSelectionPreferenceActivity.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2014 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.settings;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceFragment;
+import android.preference.SwitchPreference;
+import android.telecomm.PhoneAccount;
+import android.telecomm.PhoneAccountHandle;
+import android.telecomm.TelecommManager;
+
+import com.android.internal.util.CharSequences;
+import com.android.phone.R;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Preference activity used to facilitate enabling and disabling phone accounts by the user.
+ */
+public class PhoneAccountSelectionPreferenceActivity extends PreferenceActivity {
+
+    /**
+     * Preference fragment containing a list of all {@link PhoneAccount}s in the form of switches
+     * the user can use to enable or disable accounts.
+     */
+    public static class PhoneAccountSelectionPreferenceFragment extends PreferenceFragment
+            implements Preference.OnPreferenceChangeListener {
+        private static final String CATEGORY_PHONE_ACCOUNTS_KEY = "phone_accounts_list";
+
+        private TelecommManager mTelecommManager;
+        private PreferenceCategory mPhoneAccountsCategory;
+
+        /**
+         * Represents a single {@link PhoneAccount} for the purpose enabling and disabling.
+         */
+        static class PhoneAccountPreference extends SwitchPreference {
+            private PhoneAccountHandle mPhoneAccountHandle;
+            private boolean mPreviousState;
+
+            public PhoneAccountPreference(Context context, PhoneAccount phoneAccount) {
+                super(context);
+
+                setPhoneAccount(phoneAccount);
+            }
+
+            /**
+             * Configures the {@code PhoneAccountPreference} for the passed in {@link PhoneAccount}.
+             *
+             * @param phoneAccount The phone account.
+             */
+            private void setPhoneAccount(PhoneAccount phoneAccount) {
+                mPhoneAccountHandle = phoneAccount.getAccountHandle();
+                mPreviousState = phoneAccount.isEnabled();
+                this.setTitle(phoneAccount.getLabel());
+                this.setChecked(mPreviousState);
+            }
+
+            public boolean getPreviousState() {
+                return mPreviousState;
+            }
+
+            public PhoneAccountHandle getPhoneAccountHandle() {
+                return mPhoneAccountHandle;
+            }
+        }
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+
+            addPreferencesFromResource(R.xml.phone_account_selection);
+            mPhoneAccountsCategory = (PreferenceCategory) findPreference(
+                    CATEGORY_PHONE_ACCOUNTS_KEY);
+            mTelecommManager = TelecommManager.from(this.getActivity());
+
+            List<PhoneAccount> phoneAccounts = mTelecommManager.getAllPhoneAccounts();
+            Collections.sort(phoneAccounts, new Comparator<PhoneAccount>() {
+                @Override
+                public int compare(PhoneAccount o1, PhoneAccount o2) {
+                    return CharSequences.compareToIgnoreCase(o1.getLabel(), o2.getLabel());
+                }
+            });
+
+            for (PhoneAccount phoneAccount : phoneAccounts) {
+                // Do not add Sim PhoneAccounts.
+                if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)) {
+                    continue;
+                }
+
+                PhoneAccountPreference phoneAccountPreference = new PhoneAccountPreference(
+                        getActivity(), phoneAccount);
+                phoneAccountPreference.setOnPreferenceChangeListener(this);
+                phoneAccountPreference.setEnabled(!phoneAccount.hasCapabilities(
+                        PhoneAccount.CAPABILITY_ALWAYS_ENABLED));
+                mPhoneAccountsCategory.addPreference(phoneAccountPreference);
+            }
+        }
+
+        /**
+         * Handles changes to preferences
+         * @param preference The preference which changed.
+         * @param newValue The new value of the preference.
+         * @return
+         */
+        @Override
+        public boolean onPreferenceChange(Preference preference, Object newValue) {
+            if (preference instanceof PhoneAccountPreference) {
+                PhoneAccountPreference phoneAccountPreference = (PhoneAccountPreference) preference;
+                boolean newState = Boolean.valueOf(newValue.toString()).booleanValue();
+
+                if (newState != phoneAccountPreference.getPreviousState()) {
+                    mTelecommManager.setPhoneAccountEnabled(
+                            phoneAccountPreference.getPhoneAccountHandle(), newState);
+                }
+                return true;
+            }
+            return false;
+        }
+    }
+
+    @Override
+    public void onBuildHeaders(List<Header> target) {
+        loadHeadersFromResource(R.xml.phone_account_selection_activity, target);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        setTitle(getResources().getString(R.string.call_settings));
+        ActionBar actionBar = getActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayShowHomeEnabled(false);
+            actionBar.setDisplayHomeAsUpEnabled(false);
+        }
+
+        // By default, show the main fragment.
+        Intent intent = getIntent();
+        if (intent.getStringArrayExtra(EXTRA_SHOW_FRAGMENT) == null) {
+            getIntent().putExtra(EXTRA_SHOW_FRAGMENT,
+                    PhoneAccountSelectionPreferenceFragment.class.getName());
+        }
+
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public boolean isValidFragment(String fragmentName) {
+        return true;
+    }
+}
diff --git a/src/com/android/services/telephony/TelecommAccountRegistry.java b/src/com/android/services/telephony/TelecommAccountRegistry.java
index 749739a..7ec2c4e 100644
--- a/src/com/android/services/telephony/TelecommAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecommAccountRegistry.java
@@ -45,6 +45,8 @@
  * removal of SIMs and SIP accounts.
  */
 final class TelecommAccountRegistry {
+    private static final boolean DBG = false; /* STOP SHIP if true */
+
     private final static int[] phoneAccountIcons = {
             R.drawable.ic_multi_sim,
             R.drawable.ic_multi_sim1,
@@ -67,7 +69,6 @@
         }
 
         void teardown() {
-            mTelecommManager.unregisterPhoneAccount(mAccount.getAccountHandle());
             mIncomingCallNotifier.teardown();
         }
 
@@ -117,22 +118,37 @@
                     dummyPrefix + mContext.getResources().getString(
                             R.string.sim_description_default, slotId);
 
+            // By default all SIM phone accounts can place emergency calls.
+            int capabilities = PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
+                    PhoneAccount.CAPABILITY_CALL_PROVIDER |
+                    PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS;
+
+            // Indicate the emergency calling PhoneAccount is ALWAYS enabled.  This capability is
+            // important to ensure the emergency-only PhoneAccount cannot be disabled.
+            if (isEmergency) {
+                capabilities |= PhoneAccount.CAPABILITY_ALWAYS_ENABLED;
+            }
+
             PhoneAccount account = PhoneAccount.builder(phoneAccountHandle, label)
                     .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, line1Number, null))
                     .setSubscriptionAddress(
                             Uri.fromParts(PhoneAccount.SCHEME_TEL, subNumber, null))
-                    .setCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
-                            PhoneAccount.CAPABILITY_CALL_PROVIDER)
+                    .setCapabilities(capabilities)
                     .setIconResId(getPhoneAccountIcon(slotId))
                     .setShortDescription(description)
                     .setSupportedUriSchemes(Arrays.asList(
                             PhoneAccount.SCHEME_TEL, PhoneAccount.SCHEME_VOICEMAIL))
+                    .setEnabled(true)
                     .build();
 
             // Register with Telecomm and put into the account entry.
             mTelecommManager.registerPhoneAccount(account);
             return account;
         }
+
+        public PhoneAccountHandle getPhoneAccountHandle() {
+            return mAccount != null ? mAccount.getAccountHandle() : null;
+        }
     }
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -190,7 +206,6 @@
         intentFilter.addAction(TelephonyIntents.ACTION_SUBINFO_RECORD_UPDATED);
         intentFilter.addAction(TelephonyIntents.ACTION_SUBINFO_CONTENT_CHANGE);
         mContext.registerReceiver(mReceiver, intentFilter);
-        setupAccounts();
     }
 
     static PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
@@ -207,22 +222,43 @@
         return new PhoneAccountHandle(pstnConnectionServiceName, id);
     }
 
-    private void clearCurrentTelephonyAccounts() {
+    /**
+     * Determines if the list of {@link AccountEntry}(s) contains an {@link AccountEntry} with a
+     * specified {@link PhoneAccountHandle}.
+     *
+     * @param handle The {@link PhoneAccountHandle}.
+     * @return {@code True} if an entry exists.
+     */
+    private boolean hasAccountEntryForPhoneAccount(PhoneAccountHandle handle) {
+        for (AccountEntry entry : mAccounts) {
+            if (entry.getPhoneAccountHandle().equals(handle)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Un-registers any {@link PhoneAccount}s which are no longer present in the list
+     * {@code AccountEntry}(s).
+     */
+    private void cleanupPhoneAccounts() {
         ComponentName telephonyComponentName =
                 new ComponentName(mContext, TelephonyConnectionService.class);
-        List<PhoneAccountHandle> accountHandles = mTelecommManager.getEnabledPhoneAccounts();
+
+        List<PhoneAccountHandle> accountHandles = mTelecommManager.getAllPhoneAccountHandles();
         for (PhoneAccountHandle handle : accountHandles) {
-            if (telephonyComponentName.equals(handle.getComponentName())) {
+            if (telephonyComponentName.equals(handle.getComponentName()) &&
+                    !hasAccountEntryForPhoneAccount(handle)) {
+                Log.d(this, "Unregistering phone account %s.", handle);
                 mTelecommManager.unregisterPhoneAccount(handle);
             }
         }
     }
 
     private void setupAccounts() {
-        // Before we do anything, we need to clear whatever entries we registered at boot.
-        clearCurrentTelephonyAccounts();
-
-        // Go through SIM-based phones and register ourselves
+        // Go through SIM-based phones and register ourselves -- registering an existing account
+        // will cause the existing entry to be replaced.
         Phone[] phones = PhoneFactory.getPhones();
         Log.d(this, "Found %d phones.  Attempting to register.", phones.length);
         for (Phone phone : phones) {
@@ -242,9 +278,12 @@
         }
 
         // Add a fake account entry.
-        if (phones.length > 0 && "TRUE".equals(System.getProperty("dummy_sim"))) {
+        if ( DBG && phones.length > 0 && "TRUE".equals(System.getProperty("dummy_sim"))) {
             mAccounts.add(new AccountEntry(phones[0], false /* emergency */, true /* isDummy */));
         }
+
+        // Clean up any PhoneAccounts that are no longer relevant
+        cleanupPhoneAccounts();
     }
 
     private int getPhoneAccountIcon(int index) {