Merge "Add utility for voicemail dialogs." into lmp-mr1-dev
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index c099623..4651677 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -165,7 +165,6 @@
     private static final String CALL_FORWARDING_KEY = "call_forwarding_key";
     private static final String ADDITIONAL_GSM_SETTINGS_KEY = "additional_gsm_call_settings_key";
 
-    private static final String DEFAULT_OUTGOING_ACCOUNT_KEY = "default_outgoing_account";
     private static final String PHONE_ACCOUNT_SETTINGS_KEY =
             "phone_account_settings_preference_screen";
 
@@ -1124,7 +1123,6 @@
         super.onCreate(icicle);
         if (DBG) log("onCreate: Intent is " + getIntent());
 
-        mPhone = PhoneGlobals.getPhone();
         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
         mVmProviderSettingsUtil = new VoicemailProviderSettingsUtil(getApplicationContext());
 
@@ -1136,6 +1134,7 @@
         mSubscriptionInfoHelper = new SubscriptionInfoHelper(getIntent());
         mSubscriptionInfoHelper.setActionBarTitle(
                 getActionBar(), getResources(), R.string.call_settings_with_label);
+        mPhone = mSubscriptionInfoHelper.getPhone();
    }
 
     private void initPhoneAccountPreferences() {
@@ -1151,11 +1150,6 @@
         }
     }
 
-    private boolean canLaunchIntent(Intent intent) {
-        PackageManager pm = getPackageManager();
-        return pm.resolveActivity(intent, PackageManager.GET_ACTIVITIES) != null;
-    }
-
     @Override
     protected void onResume() {
         super.onResume();
@@ -1191,7 +1185,6 @@
                 (CheckBoxPreference) findPreference(BUTTON_VOICEMAIL_NOTIFICATION_VIBRATE_KEY);
         initVoiceMailProviders();
 
-
         if (getResources().getBoolean(R.bool.dtmf_type_enabled)) {
             mButtonDTMF.setOnPreferenceChangeListener(this);
             int dtmf = Settings.System.getInt(getContentResolver(),
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index d618b5c..3c385f9 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -76,6 +76,10 @@
     private static final String BUTTON_ENABLED_NETWORKS_KEY = "enabled_networks_key";
     private static final String BUTTON_4G_LTE_KEY = "enhanced_4g_lte";
     private static final String BUTTON_CELL_BROADCAST_SETTINGS = "cell_broadcast_settings";
+    private static final String BUTTON_APN_EXPAND_KEY = "button_apn_key";
+    private static final String BUTTON_OPERATOR_SELECTION_EXPAND_KEY = "button_carrier_sel_key";
+    private static final String BUTTON_CARRIER_SETTINGS_KEY = "carrier_settings_key";
+    private static final String BUTTON_CDMA_SYSTEM_SELECT_KEY = "cdma_system_select_key";
 
     static final int preferredNetworkMode = Phone.PREFERRED_NT_MODE;
 
@@ -693,8 +697,13 @@
                         R.string.preferred_network_mode_lte_cdma_evdo_summary);
                 break;
             case Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
-                mButtonPreferredNetworkMode.setSummary(
-                        R.string.preferred_network_mode_global_summary);
+                if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+                    mButtonPreferredNetworkMode.setSummary(
+                            R.string.preferred_network_mode_global_summary);
+                } else {
+                    mButtonPreferredNetworkMode.setSummary(
+                            R.string.preferred_network_mode_lte_summary);
+                }
                 break;
             case Phone.NT_MODE_GLOBAL:
                 mButtonPreferredNetworkMode.setSummary(
@@ -737,9 +746,11 @@
                 }
                 break;
             case Phone.NT_MODE_LTE_GSM_WCDMA:
-                if(isWorldMode()) {
+                if (isWorldMode()) {
                     mButtonEnabledNetworks.setSummary(
                             R.string.preferred_network_mode_lte_gsm_umts_summary);
+                    controlCdmaOptions(false);
+                    controlGsmOptions(true);
                     break;
                 }
             case Phone.NT_MODE_LTE_ONLY:
@@ -756,9 +767,11 @@
                 }
                 break;
             case Phone.NT_MODE_LTE_CDMA_AND_EVDO:
-                if(isWorldMode()) {
+                if (isWorldMode()) {
                     mButtonEnabledNetworks.setSummary(
                             R.string.preferred_network_mode_lte_cdma_summary);
+                    controlCdmaOptions(true);
+                    controlGsmOptions(false);
                 } else {
                     mButtonEnabledNetworks.setValue(
                             Integer.toString(Phone.NT_MODE_LTE_CDMA_AND_EVDO));
@@ -778,9 +791,18 @@
                 mButtonEnabledNetworks.setSummary(R.string.network_1x);
                 break;
             case Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
+                if (isWorldMode()) {
+                    controlCdmaOptions(true);
+                    controlGsmOptions(false);
+                }
                 mButtonEnabledNetworks.setValue(
                         Integer.toString(Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA));
-                mButtonEnabledNetworks.setSummary(R.string.network_global);
+                if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
+                    mButtonEnabledNetworks.setSummary(R.string.network_global);
+                } else {
+                    mButtonEnabledNetworks.setSummary((mShow4GForLTE == true)
+                            ? R.string.network_4G : R.string.network_lte);
+                }
                 break;
             default:
                 String errMsg = "Invalid Network Mode (" + NetworkMode + "). Ignore.";
@@ -860,4 +882,46 @@
 
         return worldModeOn;
     }
+
+    private void controlGsmOptions(boolean enable) {
+        PreferenceScreen prefSet = getPreferenceScreen();
+        if (prefSet == null) {
+            return;
+        }
+
+        if (enable && mGsmUmtsOptions == null) {
+            mGsmUmtsOptions = new GsmUmtsOptions(this, prefSet);
+        }
+        PreferenceScreen apnExpand =
+                (PreferenceScreen) prefSet.findPreference(BUTTON_APN_EXPAND_KEY);
+        PreferenceScreen operatorSelectionExpand =
+                (PreferenceScreen) prefSet.findPreference(BUTTON_OPERATOR_SELECTION_EXPAND_KEY);
+        PreferenceScreen carrierSettings =
+                (PreferenceScreen) prefSet.findPreference(BUTTON_CARRIER_SETTINGS_KEY);
+        if (apnExpand != null) {
+            apnExpand.setEnabled(enable);
+        }
+        if (operatorSelectionExpand != null) {
+            operatorSelectionExpand.setEnabled(enable);
+        }
+        if (carrierSettings != null) {
+            prefSet.removePreference(carrierSettings);
+        }
+    }
+
+    private void controlCdmaOptions(boolean enable) {
+        PreferenceScreen prefSet = getPreferenceScreen();
+        if (prefSet == null) {
+            return;
+        }
+        if (enable && mCdmaOptions == null) {
+            mCdmaOptions = new CdmaOptions(this, prefSet, mPhone);
+        }
+        CdmaSystemSelectListPreference systemSelect =
+                (CdmaSystemSelectListPreference)prefSet.findPreference
+                        (BUTTON_CDMA_SYSTEM_SELECT_KEY);
+        if (systemSelect != null) {
+            systemSelect.setEnabled(enable);
+        }
+    }
 }
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 62eb272..e957924 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -162,6 +162,7 @@
 
     // True if we are beginning a call, but the phone state has not changed yet
     private boolean mBeginningCall;
+    private boolean mDataDisconnectedDueToRoaming = false;
 
     // Last phone state seen by updatePhoneState()
     private PhoneConstants.State mLastPhoneState = PhoneConstants.State.IDLE;
@@ -800,9 +801,11 @@
                         && "DISCONNECTED".equals(intent.getStringExtra(PhoneConstants.STATE_KEY))
                         && Phone.REASON_ROAMING_ON.equals(
                             intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY));
-                mHandler.sendEmptyMessage(disconnectedDueToRoaming
-                                          ? EVENT_DATA_ROAMING_DISCONNECTED
-                                          : EVENT_DATA_ROAMING_OK);
+                if (mDataDisconnectedDueToRoaming != disconnectedDueToRoaming) {
+                    mDataDisconnectedDueToRoaming = disconnectedDueToRoaming;
+                    mHandler.sendEmptyMessage(disconnectedDueToRoaming
+                            ? EVENT_DATA_ROAMING_DISCONNECTED : EVENT_DATA_ROAMING_OK);
+                }
             } else if ((action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) &&
                     (mPUKEntryActivity != null)) {
                 // if an attempt to un-PUK-lock the device was made, while we're
diff --git a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
index 6f436fd..5226e0d 100644
--- a/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
+++ b/src/com/android/phone/settings/PhoneAccountSettingsFragment.java
@@ -18,7 +18,6 @@
 import android.util.Log;
 
 import com.android.phone.R;
-import com.android.phone.CallFeaturesSetting;
 import com.android.phone.SubscriptionInfoHelper;
 import com.android.services.telephony.sip.SipAccountRegistry;
 import com.android.services.telephony.sip.SipSharedPreferences;
@@ -31,10 +30,6 @@
                 Preference.OnPreferenceClickListener,
                 AccountSelectionPreference.AccountSelectionListener {
 
-    private static final Intent CONNECTION_SERVICE_CONFIGURE_INTENT =
-            new Intent(TelecomManager.ACTION_CONNECTION_SERVICE_CONFIGURE)
-                    .addCategory(Intent.CATEGORY_DEFAULT);
-
     private static final String ACCOUNTS_LIST_CATEGORY_KEY =
             "phone_accounts_accounts_list_category_key";
 
@@ -184,10 +179,20 @@
     @Override
     public boolean onPreferenceClick(Preference pref) {
         if (pref == mConfigureCallAssistant) {
-            try {
-                startActivity(CONNECTION_SERVICE_CONFIGURE_INTENT);
-            } catch (ActivityNotFoundException e) {
-                Log.d(LOG_TAG, "Could not resolve telecom connection service configure intent.");
+            String packageName = null;
+            PhoneAccountHandle handle = mTelecomManager.getSimCallManager();
+            if (handle != null) {
+                packageName = handle.getComponentName().getPackageName();
+            }
+            if (packageName != null) {
+                Intent intent = new Intent(TelecomManager.ACTION_CONNECTION_SERVICE_CONFIGURE)
+                        .addCategory(Intent.CATEGORY_DEFAULT)
+                        .setPackage(packageName);
+                try {
+                    startActivity(intent);
+                } catch (ActivityNotFoundException e) {
+                    Log.d(LOG_TAG, "Could not resolve call assistant configure intent: " + intent);
+                }
             }
             return true;
         }
diff --git a/src/com/android/services/telephony/CdmaConnection.java b/src/com/android/services/telephony/CdmaConnection.java
index d2988da..c52a824 100644
--- a/src/com/android/services/telephony/CdmaConnection.java
+++ b/src/com/android/services/telephony/CdmaConnection.java
@@ -160,9 +160,6 @@
         if (mAllowMute) {
             capabilities |= PhoneCapabilities.MUTE;
         }
-        if (getConference() == null) {
-            capabilities |= PhoneCapabilities.ADD_CALL;
-        }
         return capabilities;
     }
 
diff --git a/src/com/android/services/telephony/GsmConnection.java b/src/com/android/services/telephony/GsmConnection.java
index 4482d36..d2e68c6 100644
--- a/src/com/android/services/telephony/GsmConnection.java
+++ b/src/com/android/services/telephony/GsmConnection.java
@@ -48,7 +48,6 @@
     @Override
     protected int buildCallCapabilities() {
         int capabilities = super.buildCallCapabilities();
-        capabilities |= PhoneCapabilities.ADD_CALL;
         capabilities |= PhoneCapabilities.MUTE;
         capabilities |= PhoneCapabilities.SUPPORT_HOLD;
         if (getState() == STATE_ACTIVE || getState() == STATE_HOLDING) {
diff --git a/src/com/android/services/telephony/TelephonyConference.java b/src/com/android/services/telephony/TelephonyConference.java
index 21dac0c..fc009bc 100644
--- a/src/com/android/services/telephony/TelephonyConference.java
+++ b/src/com/android/services/telephony/TelephonyConference.java
@@ -33,10 +33,15 @@
  */
 public class TelephonyConference extends Conference {
 
+    /**
+     * When {@code true}, indicates that conference participant information from an IMS conference
+     * event package has been received.
+     */
+    private boolean mParticipantsReceived = false;
+
     public TelephonyConference(PhoneAccountHandle phoneAccount) {
         super(phoneAccount);
         setCapabilities(
-                PhoneCapabilities.ADD_CALL |
                 PhoneCapabilities.SUPPORT_HOLD |
                 PhoneCapabilities.HOLD |
                 PhoneCapabilities.MUTE |
@@ -133,7 +138,10 @@
     public void onConnectionAdded(Connection connection) {
         // If the conference was an IMS connection currently or before, disable MANAGE_CONFERENCE
         // as the default behavior. If there is a conference event package, this may be overridden.
-        if (((TelephonyConnection) connection).wasImsConnection()) {
+        // If a conference event package was received, do not attempt to remove manage conference.
+        if (connection instanceof TelephonyConnection &&
+                ((TelephonyConnection) connection).wasImsConnection() &&
+                !mParticipantsReceived) {
             int capabilities = getCapabilities();
             if (PhoneCapabilities.can(capabilities, PhoneCapabilities.MANAGE_CONFERENCE)) {
                 int newCapabilities =
@@ -191,4 +199,17 @@
         }
         return (TelephonyConnection) connections.get(0);
     }
+
+    /**
+     * Flags the conference to indicate that a conference event package has been received and there
+     * is now participant data present which would permit conference management.
+     */
+    public void setParticipantsReceived() {
+        if (!mParticipantsReceived) {
+            int capabilities = getCapabilities();
+            capabilities |= PhoneCapabilities.MANAGE_CONFERENCE;
+            setCapabilities(capabilities);
+        }
+        mParticipantsReceived = true;
+    }
 }
diff --git a/src/com/android/services/telephony/TelephonyConferenceController.java b/src/com/android/services/telephony/TelephonyConferenceController.java
index 43385fd..55c8338 100644
--- a/src/com/android/services/telephony/TelephonyConferenceController.java
+++ b/src/com/android/services/telephony/TelephonyConferenceController.java
@@ -61,23 +61,22 @@
         }
 
         /**
-         * Handles notifications from an connection that a participant in a conference has changed
+         * Handles notifications from an connection that participant(s) in a conference have changed
          * state.
          *
          * @param c The connection.
-         * @param participant The participant information.
+         * @param participants The participant information.
          */
         @Override
-        public void onConferenceParticipantChanged(Connection c,
-                ConferenceParticipant participant) {
+        public void onConferenceParticipantsChanged(Connection c,
+                List<ConferenceParticipant> participants) {
 
             if (c == null) {
                 return;
             }
+            Log.v(this, "onConferenceParticipantsChanged: %d participants", participants.size());
             TelephonyConnection telephonyConnection = (TelephonyConnection) c;
-
-            Log.v(this, "onConferenceParticipantChanged: %s", participant);
-            handleConferenceParticipantUpdate(telephonyConnection, participant);
+            handleConferenceParticipantsUpdate(telephonyConnection, participants);
         }
     };
 
@@ -118,8 +117,8 @@
     }
 
     private void recalculate() {
-        recalculateConferenceable();
         recalculateConference();
+        recalculateConferenceable();
     }
 
     private boolean isFullConference(Conference conference) {
@@ -184,6 +183,7 @@
                     nonConferencedConnections.add(c);
                 }
             }
+            Log.v(this, "conference conferenceable: %s", nonConferencedConnections);
             mTelephonyConference.setConferenceableConnections(nonConferencedConnections);
         }
 
@@ -219,6 +219,8 @@
         Log.d(this, "Recalculate conference calls %s %s.",
                 mTelephonyConference, conferencedConnections);
 
+        boolean wasParticipantsAdded = false;
+
         // If the number of telephony connections drops below the limit, the conference can be
         // considered terminated.
         // We must have less than 2 GSM connections and less than 1 IMS connection.
@@ -240,7 +242,8 @@
                 List<Connection> existingConnections = mTelephonyConference.getConnections();
                 // Remove any that no longer exist
                 for (Connection connection : existingConnections) {
-                    if (!conferencedConnections.contains(connection)) {
+                    if (connection instanceof TelephonyConnection &&
+                            !conferencedConnections.contains(connection)) {
                         mTelephonyConference.removeConnection(connection);
                     }
                 }
@@ -256,8 +259,9 @@
                 for (Connection conferenceParticipant :
                         mConferenceParticipantConnections.values()) {
 
-                    if (conferenceParticipant.getState() == Connection.STATE_ACTIVE) {
+                    if (conferenceParticipant.getState() == Connection.STATE_NEW) {
                         if (!existingConnections.contains(conferenceParticipant)) {
+                            wasParticipantsAdded = true;
                             mTelephonyConference.addConnection(conferenceParticipant);
                         }
                     }
@@ -273,12 +277,19 @@
                 // Add the conference participants
                 for (Connection conferenceParticipant :
                         mConferenceParticipantConnections.values()) {
+                    wasParticipantsAdded = true;
                     mTelephonyConference.addConnection(conferenceParticipant);
                 }
 
                 mConnectionService.addConference(mTelephonyConference);
             }
 
+            // If we added conference participants (e.g. via an IMS conference event package),
+            // notify the conference so that the MANAGE_CONFERENCE capability can be added.
+            if (wasParticipantsAdded) {
+                mTelephonyConference.setParticipantsReceived();
+            }
+
             // Set the conference state to the same state as its child connections.
             Connection conferencedConnection = mTelephonyConference.getPrimaryConnection();
             switch (conferencedConnection.getState()) {
@@ -310,21 +321,40 @@
     }
 
     /**
-     * Handles state changes for a conference participant.
+     * Handles state changes for conference participant(s).
      *
      * @param parent The connection which was notified of the conference participant.
-     * @param participant The conference participant.
+     * @param participants The conference participant information.
      */
-    private void handleConferenceParticipantUpdate(
-            TelephonyConnection parent, ConferenceParticipant participant) {
+    private void handleConferenceParticipantsUpdate(
+            TelephonyConnection parent, List<ConferenceParticipant> participants) {
 
-        Uri endpoint = participant.getEndpoint();
-        if (!mConferenceParticipantConnections.containsKey(endpoint)) {
-            createConferenceParticipantConnection(parent, participant);
-        } else {
-            ConferenceParticipantConnection connection =
-                    mConferenceParticipantConnections.get(endpoint);
-            connection.updateState(participant.getState());
+        boolean recalculateConference = false;
+        ArrayList<ConferenceParticipant> newParticipants = new ArrayList<>(participants.size());
+
+        for (ConferenceParticipant participant : participants) {
+            Uri endpoint = participant.getEndpoint();
+            if (!mConferenceParticipantConnections.containsKey(endpoint)) {
+                createConferenceParticipantConnection(parent, participant);
+                newParticipants.add(participant);
+                recalculateConference = true;
+            } else {
+                ConferenceParticipantConnection connection =
+                        mConferenceParticipantConnections.get(endpoint);
+                connection.updateState(participant.getState());
+            }
+        }
+
+        if (recalculateConference) {
+            // Recalculate to add new connections to the conference.
+            recalculateConference();
+
+            // Now that conference is established, set the state for all participants.
+            for (ConferenceParticipant newParticipant : newParticipants) {
+                ConferenceParticipantConnection connection =
+                        mConferenceParticipantConnections.get(newParticipant.getEndpoint());
+                connection.updateState(newParticipant.getState());
+            }
         }
     }
 
@@ -346,13 +376,9 @@
         ConferenceParticipantConnection connection = new ConferenceParticipantConnection(
                 parent, participant);
         connection.addConnectionListener(mConnectionListener);
-        connection.updateState(Connection.STATE_HOLDING);
         mConferenceParticipantConnections.put(participant.getEndpoint(), connection);
         PhoneAccountHandle phoneAccountHandle =
-                TelecomAccountRegistry.makePstnPhoneAccountHandle(parent.getPhone());
+                 TelecomAccountRegistry.makePstnPhoneAccountHandle(parent.getPhone());
         mConnectionService.addExistingConnection(phoneAccountHandle, connection);
-        // Recalculate to add to the conference and set its state appropriately.
-        recalculateConference();
-        connection.updateState(participant.getState());
     }
 }
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index da3de63..056f62f 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -35,6 +35,7 @@
 
 import java.lang.Override;
 import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -155,14 +156,14 @@
         }
 
         /**
-         * Handles a change in the state of a conference participant, as reported by the
+         * Handles a change in the state of conference participant(s), as reported by the
          * {@link com.android.internal.telephony.Connection}.
          *
-         * @param participant The participant which changed.
+         * @param participants The participant(s) which changed.
          */
         @Override
-        public void onConferenceParticipantChanged(ConferenceParticipant participant) {
-            updateConferenceParticipant(participant);
+        public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {
+            updateConferenceParticipants(participants);
         }
     };
 
@@ -395,7 +396,6 @@
     protected int buildCallCapabilities() {
         int callCapabilities = 0;
         if (isImsConnection()) {
-            callCapabilities |= PhoneCapabilities.ADD_CALL;
             callCapabilities |= PhoneCapabilities.SUPPORT_HOLD;
             if (getState() == STATE_ACTIVE || getState() == STATE_HOLDING) {
                 callCapabilities |= PhoneCapabilities.HOLD;
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index c402409..8ef9ae8 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -429,8 +429,10 @@
     @Override
     public void removeConnection(Connection connection) {
         super.removeConnection(connection);
-        TelephonyConnection telephonyConnection = (TelephonyConnection)connection;
-        telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
+        if (connection instanceof TelephonyConnection) {
+            TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
+            telephonyConnection.removeTelephonyConnectionListener(mTelephonyConnectionListener);
+        }
     }
 
     /**