Snap for 4677756 from d0c6caba1e909fc4a05ddc0c04cdd82936805e7e to pi-release

Change-Id: I926788b3606fc81b0cd2f3ac39290772a05a17c7
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index ad446ec..7bc2519 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -93,6 +93,7 @@
 
         mPlayerFactory.setCallAudioManager(this);
         mCallAudioModeStateMachine.setCallAudioManager(this);
+        mCallAudioRouteStateMachine.setCallAudioManager(this);
     }
 
     @Override
@@ -385,6 +386,11 @@
                 CallAudioRouteStateMachine.TOGGLE_MUTE);
     }
 
+    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+    public void onRingerModeChange() {
+        mCallAudioModeStateMachine.sendMessage(CallAudioModeStateMachine.RINGER_MODE_CHANGE);
+    }
+
     @VisibleForTesting
     public void mute(boolean shouldMute) {
         Log.v(this, "mute, shouldMute: %b", shouldMute);
diff --git a/src/com/android/server/telecom/CallAudioModeStateMachine.java b/src/com/android/server/telecom/CallAudioModeStateMachine.java
index b5c7e7a..716f23a 100644
--- a/src/com/android/server/telecom/CallAudioModeStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioModeStateMachine.java
@@ -87,6 +87,8 @@
 
     public static final int FOREGROUND_VOIP_MODE_CHANGE = 4001;
 
+    public static final int RINGER_MODE_CHANGE = 5001;
+
     public static final int RUN_RUNNABLE = 9001;
 
     private static final SparseArray<String> MESSAGE_CODE_TO_NAME = new SparseArray<String>() {{
@@ -105,6 +107,7 @@
         put(TONE_STARTED_PLAYING, "TONE_STARTED_PLAYING");
         put(TONE_STOPPED_PLAYING, "TONE_STOPPED_PLAYING");
         put(FOREGROUND_VOIP_MODE_CHANGE, "FOREGROUND_VOIP_MODE_CHANGE");
+        put(RINGER_MODE_CHANGE, "RINGER_MODE_CHANGE");
 
         put(RUN_RUNNABLE, "RUN_RUNNABLE");
     }};
@@ -202,18 +205,22 @@
     }
 
     private class RingingFocusState extends BaseState {
-        @Override
-        public void enter() {
-            Log.i(LOG_TAG, "Audio focus entering RINGING state");
+        private void tryStartRinging() {
             if (mCallAudioManager.startRinging()) {
                 mAudioManager.requestAudioFocusForCall(AudioManager.STREAM_RING,
                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
                 mAudioManager.setMode(AudioManager.MODE_RINGTONE);
-                mCallAudioManager.setCallAudioRouteFocusState(CallAudioRouteStateMachine.RINGING_FOCUS);
+                mCallAudioManager.setCallAudioRouteFocusState(
+                        CallAudioRouteStateMachine.RINGING_FOCUS);
             } else {
-                Log.i(LOG_TAG, "Entering RINGING but not acquiring focus -- silent ringtone");
+                Log.i(LOG_TAG, "RINGING state, try start ringing but not acquiring audio focus");
             }
+        }
 
+        @Override
+        public void enter() {
+            Log.i(LOG_TAG, "Audio focus entering RINGING state");
+            tryStartRinging();
             mCallAudioManager.stopCallWaiting();
         }
 
@@ -275,6 +282,11 @@
                     transitionTo(args.foregroundCallIsVoip
                             ? mVoipCallFocusState : mSimCallFocusState);
                     return HANDLED;
+                case RINGER_MODE_CHANGE: {
+                    Log.i(LOG_TAG, "RINGING state, received RINGER_MODE_CHANGE");
+                    tryStartRinging();
+                    return HANDLED;
+                }
                 default:
                     // The forced focus switch commands are handled by BaseState.
                     return NOT_HANDLED;
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index facbf39..dfa6042 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -745,6 +745,10 @@
                     mBluetoothRouteManager.getConnectedDevices());
             setSystemAudioState(newState, true);
             updateInternalCallAudioState();
+            // Do not send RINGER_MODE_CHANGE if no Bluetooth SCO audio device is available
+            if (mBluetoothRouteManager.getBluetoothAudioConnectedDevice() != null) {
+                mCallAudioManager.onRingerModeChange();
+            }
         }
 
         @Override
@@ -770,7 +774,9 @@
                     }
                     return HANDLED;
                 case BT_AUDIO_CONNECTED:
-                    // Nothing to do
+                    // Send ringer mode change because we transit to ActiveBluetoothState even
+                    // when HFP is connecting
+                    mCallAudioManager.onRingerModeChange();
                     return HANDLED;
                 case SWITCH_BLUETOOTH:
                 case USER_SWITCH_BLUETOOTH:
@@ -1276,6 +1282,8 @@
     private CallAudioState mCurrentCallAudioState;
     private CallAudioState mLastKnownCallAudioState;
 
+    private CallAudioManager mCallAudioManager;
+
     public CallAudioRouteStateMachine(
             Context context,
             CallsManager callsManager,
@@ -1332,6 +1340,10 @@
         mRouteCodeToQuiescentState.put(ROUTE_WIRED_HEADSET, mQuiescentHeadsetRoute);
     }
 
+    public void setCallAudioManager(CallAudioManager callAudioManager) {
+        mCallAudioManager = callAudioManager;
+    }
+
     /**
      * Initializes the state machine with info on initial audio route, supported audio routes,
      * and mute status.
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 7db4bd2..72443b4 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -1460,8 +1460,21 @@
             Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall();
             Log.d(this, "Incoming call = %s Ongoing call %s", call, activeCall);
             if (activeCall != null && activeCall != call) {
-                // Hold the telephony call even if it doesn't have the hold capability.
-                if (canHold(activeCall)) {
+                // We purposely don't check if the active call CAN current hold, but rather we check
+                // whether it CAN support hold.  Consider this scenario:
+                // Call A - Active (CAPABILITY_SUPPORT_HOLD, but not CAPABILITY_HOLD)
+                // Call B - Held (CAPABILITY_SUPPORT_HOLD, but not CAPABILITY_HOLD)
+                // Call C - Incoming call
+                // In this scenario we are going to first disconnect the held call (Call B), which
+                // will mean that the active call (Call A) will now support hold.
+                if (supportsHold(activeCall)) {
+                    Call heldCall = getHeldCall();
+                    if (heldCall != null) {
+                        Log.i(this, "Disconnecting held call %s before holding active call.",
+                                heldCall);
+                        heldCall.disconnect();
+                    }
+
                     Log.d(this, "Answer %s, hold %s", call, activeCall);
                     activeCall.hold();
                 } else {
@@ -3436,7 +3449,7 @@
         // Send an error back if there are any ongoing emergency calls.
         if (hasEmergencyCall()) {
             handoverFromCall.onHandoverFailed(
-                    android.telecom.Call.Callback.HANDOVER_FAILURE_ONGOING_EMERG_CALL);
+                    android.telecom.Call.Callback.HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL);
             return;
         }
 
@@ -3733,6 +3746,10 @@
         return call.can(Connection.CAPABILITY_HOLD);
     }
 
+    private boolean supportsHold(Call call) {
+        return call.can(Connection.CAPABILITY_SUPPORT_HOLD);
+    }
+
     private final class ActionSetCallState implements PendingAction {
 
         private final Call mCall;
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
index a55aeed..cbca5a7 100644
--- a/tests/AndroidTest.xml
+++ b/tests/AndroidTest.xml
@@ -14,11 +14,13 @@
      limitations under the License.
 -->
 <configuration description="Runs Telecom Test Cases.">
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="TelecomUnitTests.apk" />
     </target_preparer>
 
-    <option name="test-suite-tag" value="apct" />
     <option name="test-tag" value="TelecomUnitTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.server.telecom.tests" />
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
index 64f5fb8..f253d19 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioModeStateMachineTest.java
@@ -21,6 +21,7 @@
 
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.CallAudioModeStateMachine;
+import com.android.server.telecom.CallAudioRouteStateMachine;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -80,6 +81,51 @@
         verify(mCallAudioManager).stopCallWaiting();
     }
 
+    @SmallTest
+    @Test
+    public void testRegainFocusWhenHfpIsConnectedSilenced() throws Throwable {
+        CallAudioModeStateMachine sm = new CallAudioModeStateMachine(mAudioManager);
+        sm.setCallAudioManager(mCallAudioManager);
+        sm.sendMessage(CallAudioModeStateMachine.ABANDON_FOCUS_FOR_TESTING);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+
+        resetMocks();
+        when(mCallAudioManager.startRinging()).thenReturn(false);
+
+        sm.sendMessage(CallAudioModeStateMachine.NEW_RINGING_CALL,
+                new CallAudioModeStateMachine.MessageArgs(
+                        false, // hasActiveOrDialingCalls
+                        true, // hasRingingCalls
+                        false, // hasHoldingCalls
+                        false, // isTonePlaying
+                        false, // foregroundCallIsVoip
+                        null // session
+                ));
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+
+        assertEquals(CallAudioModeStateMachine.RING_STATE_NAME, sm.getCurrentStateName());
+
+        verify(mAudioManager, never()).requestAudioFocusForCall(anyInt(), anyInt());
+        verify(mAudioManager, never()).setMode(anyInt());
+
+        verify(mCallAudioManager, never()).stopRinging();
+
+        verify(mCallAudioManager).stopCallWaiting();
+
+        when(mCallAudioManager.startRinging()).thenReturn(true);
+
+        sm.sendMessage(CallAudioModeStateMachine.RINGER_MODE_CHANGE);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+
+        verify(mCallAudioManager).startRinging();
+        verify(mAudioManager).requestAudioFocusForCall(AudioManager.STREAM_RING,
+                AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+        verify(mAudioManager).setMode(AudioManager.MODE_RINGTONE);
+        verify(mCallAudioManager).setCallAudioRouteFocusState(
+                CallAudioRouteStateMachine.RINGING_FOCUS);
+    }
+
+
     private void resetMocks() {
         reset(mCallAudioManager, mAudioManager);
     }
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index 76048b7..d4c3d1d 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -82,6 +82,7 @@
     @Mock WiredHeadsetManager mockWiredHeadsetManager;
     @Mock StatusBarNotifier mockStatusBarNotifier;
     @Mock Call fakeCall;
+    @Mock CallAudioManager mockCallAudioManager;
 
     private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
     private static final int TEST_TIMEOUT = 500;
@@ -187,6 +188,7 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
@@ -229,6 +231,7 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
         Collection<BluetoothDevice> availableDevices = Collections.singleton(bluetoothDevice1);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
@@ -301,6 +304,7 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
 
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
@@ -335,6 +339,7 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
         setInBandRing(false);
         when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(false);
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(false);
@@ -368,6 +373,12 @@
                 CallAudioRouteStateMachine.ACTIVE_FOCUS);
         waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
         verify(mockBluetoothRouteManager, times(1)).connectBluetoothAudio(null);
+
+        when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice())
+                .thenReturn(bluetoothDevice1);
+        stateMachine.sendMessage(CallAudioRouteStateMachine.BT_AUDIO_CONNECTED);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+        verify(mockCallAudioManager, times(1)).onRingerModeChange();
     }
 
     @SmallTest
@@ -381,6 +392,7 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
         List<BluetoothDevice> availableDevices =
                 Arrays.asList(bluetoothDevice1, bluetoothDevice2, bluetoothDevice3);
 
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
index 345312e..609a488 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteTransitionTests.java
@@ -146,6 +146,7 @@
     @Mock WiredHeadsetManager mockWiredHeadsetManager;
     @Mock StatusBarNotifier mockStatusBarNotifier;
     @Mock Call fakeCall;
+    @Mock CallAudioManager mockCallAudioManager;
     private CallAudioManager.AudioServiceFactory mAudioServiceFactory;
     private static final int TEST_TIMEOUT = 500;
     private AudioManager mockAudioManager;
@@ -218,6 +219,7 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 mParams.earpieceControl);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
 
         setupMocksForParams(stateMachine, mParams);
 
@@ -303,6 +305,7 @@
                 mockStatusBarNotifier,
                 mAudioServiceFactory,
                 mParams.earpieceControl);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
 
         // Set up bluetooth and speakerphone state
         when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(