Merge "Add an option to set the selected phone account as the default. (2/3)" into lmp-sprout-dev
diff --git a/src/com/android/server/telecom/BluetoothPhoneService.java b/src/com/android/server/telecom/BluetoothPhoneService.java
index 43be93a..51dbbdf 100644
--- a/src/com/android/server/telecom/BluetoothPhoneService.java
+++ b/src/com/android/server/telecom/BluetoothPhoneService.java
@@ -103,6 +103,7 @@
     private int mBluetoothCallState = CALL_STATE_IDLE;
     private String mRingingAddress = null;
     private int mRingingAddressType = 0;
+    private Call mOldHeldCall = null;
 
     /**
      * Binder implementation of IBluetoothHeadsetPhone. Implements the command interface that the
@@ -643,7 +644,7 @@
         }
 
         int numActiveCalls = activeCall == null ? 0 : 1;
-        int numHeldCalls = heldCall == null ? 0 : 1;
+        int numHeldCalls = callsManager.getNumHeldCalls();
 
         // For conference calls which support swapping the active call within the conference
         // (namely CDMA calls) we need to expose that as a held call in order for the BT device
@@ -664,6 +665,7 @@
                  bluetoothCallState != mBluetoothCallState ||
                  !TextUtils.equals(ringingAddress, mRingingAddress) ||
                  ringingAddressType != mRingingAddressType ||
+                 heldCall != mOldHeldCall ||
                  force)) {
 
             // If the call is transitioning into the alerting state, send DIALING first.
@@ -672,6 +674,7 @@
             boolean sendDialingFirst = mBluetoothCallState != bluetoothCallState &&
                     bluetoothCallState == CALL_STATE_ALERTING;
 
+            mOldHeldCall = heldCall;
             mNumActiveCalls = numActiveCalls;
             mNumHeldCalls = numHeldCalls;
             mBluetoothCallState = bluetoothCallState;
diff --git a/src/com/android/server/telecom/Call.java b/src/com/android/server/telecom/Call.java
index edb3a38..a0a8a62 100644
--- a/src/com/android/server/telecom/Call.java
+++ b/src/com/android/server/telecom/Call.java
@@ -557,8 +557,12 @@
     }
 
     void setCallCapabilities(int callCapabilities) {
+        setCallCapabilities(callCapabilities, false /* forceUpdate */);
+    }
+
+    void setCallCapabilities(int callCapabilities, boolean forceUpdate) {
         Log.v(this, "setCallCapabilities: %s", PhoneCapabilities.toString(callCapabilities));
-        if (mCallCapabilities != callCapabilities) {
+        if (forceUpdate || mCallCapabilities != callCapabilities) {
            mCallCapabilities = callCapabilities;
             for (Listener l : mListeners) {
                 l.onCallCapabilitiesChanged(this);
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 1e305e2..a89dcea 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -330,7 +330,7 @@
     }
 
     private void updateAudioStreamAndMode() {
-        Log.v(this, "updateAudioStreamAndMode, mIsRinging: %b, mIsTonePlaying: %b", mIsRinging,
+        Log.i(this, "updateAudioStreamAndMode, mIsRinging: %b, mIsTonePlaying: %b", mIsRinging,
                 mIsTonePlaying);
         if (mIsRinging) {
             requestAudioFocusAndSetMode(AudioManager.STREAM_RING, AudioManager.MODE_RINGTONE);
@@ -359,7 +359,7 @@
     }
 
     private void requestAudioFocusAndSetMode(int stream, int mode) {
-        Log.v(this, "requestAudioFocusAndSetMode, stream: %d -> %d", mAudioFocusStreamType, stream);
+        Log.i(this, "requestAudioFocusAndSetMode, stream: %d -> %d", mAudioFocusStreamType, stream);
         Preconditions.checkState(stream != STREAM_NONE);
 
         // Even if we already have focus, if the stream is different we update audio manager to give
@@ -392,7 +392,12 @@
         Preconditions.checkState(hasFocus());
         int oldMode = mAudioManager.getMode();
         Log.v(this, "Request to change audio mode from %d to %d", oldMode, newMode);
+
         if (oldMode != newMode) {
+            if (oldMode == AudioManager.MODE_IN_CALL && newMode == AudioManager.MODE_RINGTONE) {
+                Log.i(this, "Transition from IN_CALL -> RINGTONE. Resetting to NORMAL first.");
+                mAudioManager.setMode(AudioManager.MODE_NORMAL);
+            }
             mAudioManager.setMode(newMode);
             mMostRecentlyUsedMode = newMode;
         }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index e2b9d87..e07b279 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -436,7 +436,10 @@
             return null;
         }
 
-        if (phoneAccountHandle == null && accounts.size() > 1 && !isEmergencyCall) {
+        boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 &&
+                !isEmergencyCall;
+
+        if (needsAccountSelection) {
             // This is the state where the user is expected to select an account
             call.setState(CallState.PRE_DIAL_WAIT);
             extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts);
@@ -447,7 +450,7 @@
         call.setExtras(extras);
 
         // Do not add the call if it is a potential MMI code.
-        if (isPotentialMMICode(handle) || isPotentialInCallMMICode) {
+        if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) {
             call.addListener(this);
         } else {
             addCall(call);
@@ -876,6 +879,16 @@
         return getFirstCallWithState(CallState.ON_HOLD);
     }
 
+    int getNumHeldCalls() {
+        int count = 0;
+        for (Call call : mCalls) {
+            if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) {
+                count++;
+            }
+        }
+        return count;
+    }
+
     Call getFirstCallWithState(int... states) {
         return getFirstCallWithState(null, states);
     }
@@ -1000,6 +1013,14 @@
             }
             updateForegroundCall();
         }
+
+        // Now that a call has been removed, other calls may gain new call capabilities (for
+        // example, if only one call is left, it is now add-call capable again). Trigger the
+        // recalculation of the call's current capabilities by forcing an update. (See
+        // InCallController.toParcelableCall()).
+        for (Call otherCall : mCalls) {
+            otherCall.setCallCapabilities(otherCall.getCallCapabilities(), true /* forceUpdate */);
+        }
     }
 
     /**
diff --git a/src/com/android/server/telecom/MissedCallNotifier.java b/src/com/android/server/telecom/MissedCallNotifier.java
index 76d0ae8..2c1ffb8 100644
--- a/src/com/android/server/telecom/MissedCallNotifier.java
+++ b/src/com/android/server/telecom/MissedCallNotifier.java
@@ -34,6 +34,8 @@
 import android.provider.CallLog.Calls;
 import android.telecom.CallState;
 import android.telecom.DisconnectCause;
+import android.telecom.PhoneAccount;
+import android.telephony.PhoneNumberUtils;
 import android.text.BidiFormatter;
 import android.text.TextDirectionHeuristics;
 import android.text.TextUtils;
@@ -278,13 +280,19 @@
                     try {
                         while (cursor.moveToNext()) {
                             // Get data about the missed call from the cursor
-                            Uri handle = Uri.parse(cursor.getString(
-                                    cursor.getColumnIndexOrThrow(Calls.NUMBER)));
-                            int presentation = cursor.getInt(cursor.getColumnIndexOrThrow(
+                            final String handleString = cursor.getString(
+                                    cursor.getColumnIndexOrThrow(Calls.NUMBER));
+                            final int presentation = cursor.getInt(cursor.getColumnIndexOrThrow(
                                     Calls.NUMBER_PRESENTATION));
 
-                            if (presentation != Calls.PRESENTATION_ALLOWED) {
+                            final Uri handle;
+                            if (presentation != Calls.PRESENTATION_ALLOWED
+                                    || TextUtils.isEmpty(handleString)) {
                                 handle = null;
+                            } else {
+                                handle = Uri.fromParts(PhoneNumberUtils.isUriNumber(handleString) ?
+                                        PhoneAccount.SCHEME_SIP : PhoneAccount.SCHEME_TEL,
+                                                handleString, null);
                             }
 
                             // Convert the data to a call object
diff --git a/src/com/android/server/telecom/Ringer.java b/src/com/android/server/telecom/Ringer.java
index 68c188b..5945306 100644
--- a/src/com/android/server/telecom/Ringer.java
+++ b/src/com/android/server/telecom/Ringer.java
@@ -140,12 +140,8 @@
     private void onRespondedToIncomingCall(Call call) {
         // Only stop the ringer if this call is the top-most incoming call.
         if (getTopMostUnansweredCall() == call) {
-            stopRinging();
-            stopCallWaiting();
+            removeFromUnansweredCall(call);
         }
-
-        // We do not remove the call from mRingingCalls until the call state changes from
-        // STATE_RINGING or the call is removed. see onCallStateChanged or onCallRemoved.
     }
 
     private Call getTopMostUnansweredCall() {
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index d2b3da7..3af67d8 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -331,6 +331,7 @@
      */
     @Override
     public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
+        enforceReadPermissionOrDefaultDialer();
         try {
             return mPhoneAccountRegistrar.isVoiceMailNumber(accountHandle, number);
         } catch (Exception e) {