Merge "Send pipes to connection service when RTT changes" into pi-dev
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 41843e5..38d1c20 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -54,7 +54,7 @@
     <string name="block_number" msgid="1101252256321306179">"Añadir un número"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"¿Desbloquear el número <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g>?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Desbloquear"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquear llamadas y mensajes de texto del número"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquear llamadas y mensajes de texto de"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Número de teléfono"</string>
     <string name="block_button" msgid="8822290682524373357">"Bloquear"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Solo el propietario del dispositivo puede ver y administrar los números bloqueados."</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 15f5713..4989f54 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -28,7 +28,7 @@
     <string name="notification_missedCall_message" msgid="3049928912736917988">"Message"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"Son coupé"</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"Haut-parleur activé"</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Peux pas parler. Quoi de neuf ?"</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"Je peux pas parler. Qu\'y a-t-il ?"</string>
     <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"Je te rappelle tout de suite."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"Je t\'appellerai plus tard."</string>
     <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"Peux pas parler. On se rappelle ?"</string>
@@ -54,7 +54,7 @@
     <string name="block_number" msgid="1101252256321306179">"Ajouter un numéro"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"Débloquer <xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> ?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"Débloquer"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquer les appels et les SMS provenant du numéro :"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"Bloquer les appels et les SMS provenant du :"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"Numéro de téléphone"</string>
     <string name="block_button" msgid="8822290682524373357">"Bloquer"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"Seul le propriétaire de l\'appareil peut afficher et gérer les numéros bloqués."</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 87f1898..15337b6 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -28,12 +28,12 @@
     <string name="notification_missedCall_message" msgid="3049928912736917988">"संदेश"</string>
     <string name="accessibility_call_muted" msgid="2776111226185342220">"कॉल म्‍यूट की गई."</string>
     <string name="accessibility_speakerphone_enabled" msgid="1988512040421036359">"स्‍पीकरफ़ोन सक्षम."</string>
-    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"अभी बात नहीं हो सकती. क्‍या चल रहा है?"</string>
-    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"मैं आपको वापस कॉल करूंगा/करूंगी."</string>
+    <string name="respond_via_sms_canned_response_1" msgid="2461606462788380215">"अभी बात नहीं हो सकती. क्‍या हो रहा है?"</string>
+    <string name="respond_via_sms_canned_response_2" msgid="4074450431532859214">"मैं जल्दी ही कॉल करता हूं/करती हूं."</string>
     <string name="respond_via_sms_canned_response_3" msgid="3496079065723960450">"मैं आपको बाद में कॉल करूंगा/करूंगी."</string>
-    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"अभी बात नहीं हो सकती. बाद में कॉल करें?"</string>
+    <string name="respond_via_sms_canned_response_4" msgid="1698989243040062190">"अभी बात नहीं हो सकती. मुझे बाद में कॉल करेंगे?"</string>
     <string name="respond_via_sms_setting_title" msgid="3754000371039709383">"झटपट उत्तर"</string>
-    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"झटपट उत्तर संपादित करें"</string>
+    <string name="respond_via_sms_setting_title_2" msgid="6104662227299493906">"पहले से तैयार जवाब में बदलाव करें"</string>
     <string name="respond_via_sms_setting_summary" msgid="9150281183930613065"></string>
     <string name="respond_via_sms_edittext_dialog_title" msgid="20379890418289778">"झटपट उत्तर"</string>
     <string name="respond_via_sms_confirmation_format" msgid="7229149977515784269">"<xliff:g id="PHONE_NUMBER">%s</xliff:g> को संदेश भेजा गया."</string>
@@ -50,11 +50,11 @@
     <string name="change_default_dialer_dialog_negative" msgid="9078144617060173845">"अभी नहीं"</string>
     <string name="change_default_dialer_warning_message" msgid="1417671460801684999">"<xliff:g id="NEW_APP">%s</xliff:g> कॉल करने और कॉल से संबंधित सभी पहलुओं को नियंत्रित कर पाएगा. केवल उन्हीं ऐप्लिकेशन को डिफ़ॉल्ट फ़ोन ऐप्लिकेशन के रूप में सेट करना चाहिए जिन पर आप विश्वास करते हैं."</string>
     <string name="blocked_numbers" msgid="2751843139572970579">"अवरोधित नंबर"</string>
-    <string name="blocked_numbers_msg" msgid="1045015186124965643">"आपको अवरुद्ध किए गए नंबर से कॉल या लेख संदेश नहीं मिलेंगे."</string>
+    <string name="blocked_numbers_msg" msgid="1045015186124965643">"आपको ब्लॉक किए गए नंबर से कॉल या मैसेज नहीं मिलेंगे."</string>
     <string name="block_number" msgid="1101252256321306179">"नंबर जोड़ें"</string>
     <string name="unblock_dialog_body" msgid="1614238499771862793">"<xliff:g id="NUMBER_TO_BLOCK">%1$s</xliff:g> को अनवरोधित करें?"</string>
     <string name="unblock_button" msgid="3078048901972674170">"अनवरोधित करें"</string>
-    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"इसके कॉल और लेख अवरुद्ध करें"</string>
+    <string name="add_blocked_dialog_body" msgid="9030243212265516828">"इसके कॉल और मैसेज रोकें"</string>
     <string name="add_blocked_number_hint" msgid="6847675097085433553">"फ़ोन नंबर"</string>
     <string name="block_button" msgid="8822290682524373357">"अवरुद्ध करें"</string>
     <string name="non_primary_user" msgid="5180129233352533459">"केवल डिवाइस स्वामी अवरुद्ध किए गए नंबर देख और प्रबंधित कर सकते हैं."</string>
diff --git a/src/com/android/server/telecom/BluetoothHeadsetProxy.java b/src/com/android/server/telecom/BluetoothHeadsetProxy.java
index f32f454..0f492df 100644
--- a/src/com/android/server/telecom/BluetoothHeadsetProxy.java
+++ b/src/com/android/server/telecom/BluetoothHeadsetProxy.java
@@ -56,8 +56,8 @@
         return mBluetoothHeadset.getConnectionState(device);
     }
 
-    public boolean isAudioConnected(BluetoothDevice device) {
-        return mBluetoothHeadset.isAudioConnected(device);
+    public int getAudioState(BluetoothDevice device) {
+        return mBluetoothHeadset.getAudioState(device);
     }
 
     public boolean connectAudio() {
diff --git a/src/com/android/server/telecom/CallAudioRouteStateMachine.java b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
index 0d96ccb..4274017 100644
--- a/src/com/android/server/telecom/CallAudioRouteStateMachine.java
+++ b/src/com/android/server/telecom/CallAudioRouteStateMachine.java
@@ -1452,7 +1452,10 @@
             BluetoothDevice connectedDevice =
                     mBluetoothRouteManager.getBluetoothAudioConnectedDevice();
             if (address == null && connectedDevice != null) {
-                // null means connect to any device, so don't bother reconnecting
+                // null means connect to any device, so don't bother reconnecting. Also, send a
+                // message to ourselves telling us that BT audio is already connected.
+                Log.i(this, "HFP audio already on. Skipping connecting.");
+                sendInternalMessage(BT_AUDIO_CONNECTED);
                 return;
             }
             if (connectedDevice == null || !Objects.equals(address, connectedDevice.getAddress())) {
@@ -1630,7 +1633,7 @@
         return UserHandle.USER_OWNER;
     }
 
-    private boolean isInActiveState() {
+    public boolean isInActiveState() {
         AudioState currentState = (AudioState) getCurrentState();
         if (currentState == null) {
             Log.w(this, "Current state is null, assuming inactive state");
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 497a3b0..e4e5722 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -1915,12 +1915,28 @@
             if (canHold(activeCall)) {
                 activeCall.hold();
                 return true;
-            } else if (supportsHold(call)) {
+            } else if (supportsHold(activeCall)
+                    && activeCall.getConnectionService() == call.getConnectionService()) {
+
+                // Handle the case where the active call and the new call are from the same CS, and
+                // the currently active call supports hold but cannot currently be held.
+                // In this case we'll look for the other held call for this connectionService and
+                // disconnect it prior to holding the active call.
+                // E.g.
+                // Call A - Held   (Supports hold, can't hold)
+                // Call B - Active (Supports hold, can't hold)
+                // Call C - Incoming
+                // Here we need to disconnect A prior to holding B so that C can be answered.
+                // This case is driven by telephony requirements ultimately.
                 Call heldCall = getHeldCallByConnectionService(call.getConnectionService());
                 if (heldCall != null) {
                     heldCall.disconnect();
-                    Log.i(this, "Disconnecting held call %s before holding active call.", heldCall);
+                    Log.i(this, "holdActiveCallForNewCall: Disconnect held call %s before "
+                                    + "holding active call %s.",
+                            heldCall.getId(), activeCall.getId());
                 }
+                Log.i(this, "holdActiveCallForNewCall: Holding active %s before making %s active.",
+                        activeCall.getId(), call.getId());
                 activeCall.hold();
                 return true;
             } else {
@@ -1928,6 +1944,8 @@
                 // service, then disconnect it, otherwise allow the connection service to
                 // figure out the right states.
                 if (activeCall.getConnectionService() != call.getConnectionService()) {
+                    Log.i(this, "holdActiveCallForNewCall: disconnecting %s so that %s can be "
+                            + "made active.", activeCall.getId(), call.getId());
                     activeCall.disconnect();
                 }
             }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 298129a..ba56145 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom.bluetooth;
 
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
 import android.content.Context;
 import android.os.Message;
 import android.telecom.Log;
@@ -653,7 +654,8 @@
 
         for (int i = 0; i < deviceList.size(); i++) {
             BluetoothDevice device = deviceList.get(i);
-            boolean isAudioOn = bluetoothHeadset.isAudioConnected(device);
+            boolean isAudioOn = bluetoothHeadset.getAudioState(device)
+                    != BluetoothHeadset.STATE_AUDIO_DISCONNECTED;
             Log.v(this, "isBluetoothAudioConnected: ==> isAudioOn = " + isAudioOn
                     + "for headset: " + device);
             if (isAudioOn) {
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 30848f8..5b45828 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom.tests;
 
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
 import android.content.ContentResolver;
 import android.os.Parcel;
 import android.telecom.Log;
@@ -130,7 +131,8 @@
         when(mDeviceManager.getConnectedDevices()).thenReturn(Arrays.asList(devices));
         when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
         if (activeDevice != null) {
-            when(mHeadsetProxy.isAudioConnected(eq(activeDevice))).thenReturn(true);
+            when(mHeadsetProxy.getAudioState(eq(activeDevice)))
+                    .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
         }
         doAnswer(invocation -> {
             BluetoothDevice first = getFirstExcluding(devices,
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
index b9c1427..e7cd6ee 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteTransitionTests.java
@@ -17,6 +17,7 @@
 package com.android.server.telecom.tests;
 
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
 import android.content.ContentResolver;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -306,7 +307,8 @@
         when(mHeadsetProxy.getConnectedDevices()).thenReturn(Arrays.asList(devices));
         when(mHeadsetProxy.getActiveDevice()).thenReturn(activeDevice);
         if (audioOnDevice != null) {
-            when(mHeadsetProxy.isAudioConnected(eq(audioOnDevice))).thenReturn(true);
+            when(mHeadsetProxy.getAudioState(eq(audioOnDevice)))
+                    .thenReturn(BluetoothHeadset.STATE_AUDIO_CONNECTED);
         }
         doAnswer(invocation -> {
             BluetoothDevice first = getFirstExcluding(devices,
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
index 6330996..9c90d3e 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioRouteStateMachineTest.java
@@ -433,6 +433,46 @@
 
     @SmallTest
     @Test
+    public void testFocusChangeWithAlreadyActiveBtDevice() {
+        CallAudioRouteStateMachine stateMachine = new CallAudioRouteStateMachine(
+                mContext,
+                mockCallsManager,
+                mockBluetoothRouteManager,
+                mockWiredHeadsetManager,
+                mockStatusBarNotifier,
+                mAudioServiceFactory,
+                CallAudioRouteStateMachine.EARPIECE_FORCE_ENABLED);
+        stateMachine.setCallAudioManager(mockCallAudioManager);
+        List<BluetoothDevice> availableDevices =
+                Arrays.asList(bluetoothDevice1, bluetoothDevice2);
+
+        // Set up a state where there's an HFP connected bluetooth device already.
+        when(mockAudioManager.isSpeakerphoneOn()).thenReturn(false);
+        when(mockBluetoothRouteManager.isBluetoothAudioConnectedOrPending()).thenReturn(true);
+        when(mockBluetoothRouteManager.isBluetoothAvailable()).thenReturn(true);
+        when(mockBluetoothRouteManager.getConnectedDevices()).thenReturn(availableDevices);
+        when(mockBluetoothRouteManager.getBluetoothAudioConnectedDevice())
+                .thenReturn(bluetoothDevice1);
+
+        // We want to be in the QuiescentBluetoothRoute because there's no call yet
+        CallAudioState initState = new CallAudioState(false, CallAudioState.ROUTE_BLUETOOTH,
+                CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_BLUETOOTH,
+                bluetoothDevice1, availableDevices);
+        stateMachine.initialize(initState);
+
+        // Switch to active, pretending that a call came in.
+        stateMachine.sendMessageWithSessionInfo(CallAudioRouteStateMachine.SWITCH_FOCUS,
+                CallAudioRouteStateMachine.ACTIVE_FOCUS);
+        waitForHandlerAction(stateMachine.getHandler(), TEST_TIMEOUT);
+
+        // Make sure that we've successfully switched to the active BT route without actually
+        // calling connectAudio.
+        verify(mockBluetoothRouteManager, never()).connectBluetoothAudio(nullable(String.class));
+        assertTrue(stateMachine.isInActiveState());
+    }
+
+    @SmallTest
+    @Test
     public void testInitializationWithEarpieceNoHeadsetNoBluetooth() {
         CallAudioState expectedState = new CallAudioState(false, CallAudioState.ROUTE_EARPIECE,
                 CallAudioState.ROUTE_EARPIECE | CallAudioState.ROUTE_SPEAKER);