Merge "Hearing Aid: Connect route when right HA is connected first" into main
diff --git a/flags/Android.bp b/flags/Android.bp
index 7e2b31d..af8b683 100644
--- a/flags/Android.bp
+++ b/flags/Android.bp
@@ -29,10 +29,12 @@
       "telecom_incallservice_flags.aconfig",
       "telecom_default_phone_account_flags.aconfig",
       "telecom_callaudioroutestatemachine_flags.aconfig",
+      "telecom_calls_manager_flags.aconfig",
       "telecom_anomaly_report_flags.aconfig",
       "telecom_callaudiomodestatemachine_flags.aconfig",
       "telecom_calllog_flags.aconfig",
-      "telecom_resolve_hidden_dependencies.aconfig"
+      "telecom_resolve_hidden_dependencies.aconfig",
+      "telecom_bluetoothroutemanager_flags.aconfig",
     ],
 }
 
diff --git a/flags/telecom_bluetoothroutemanager_flags.aconfig b/flags/telecom_bluetoothroutemanager_flags.aconfig
new file mode 100644
index 0000000..ddd8571
--- /dev/null
+++ b/flags/telecom_bluetoothroutemanager_flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.server.telecom.flags"
+
+flag {
+  name: "use_actual_address_to_enter_connecting_state"
+  namespace: "telecom"
+  description: "Fix bugs that may add bluetooth device with null address."
+  bug: "306113816"
+}
+
diff --git a/flags/telecom_callaudioroutestatemachine_flags.aconfig b/flags/telecom_callaudioroutestatemachine_flags.aconfig
index b0f14a4..ab7ec88 100644
--- a/flags/telecom_callaudioroutestatemachine_flags.aconfig
+++ b/flags/telecom_callaudioroutestatemachine_flags.aconfig
@@ -40,4 +40,11 @@
   namespace: "telecom"
   description: "Refactor call audio set/clear communication device and include unsupported routes."
   bug: "308968392"
-}
\ No newline at end of file
+}
+
+flag {
+  name: "communication_device_protected_by_lock"
+  namespace: "telecom"
+  description: "Protect set/clear communication device operation with lock to avoid race condition."
+  bug: "303001133"
+}
diff --git a/flags/telecom_calls_manager_flags.aconfig b/flags/telecom_calls_manager_flags.aconfig
new file mode 100644
index 0000000..1a19480
--- /dev/null
+++ b/flags/telecom_calls_manager_flags.aconfig
@@ -0,0 +1,15 @@
+package: "com.android.server.telecom.flags"
+
+flag {
+  name: "use_improved_listener_order"
+  namespace: "telecom"
+  description: "Make InCallController the first listener to trigger"
+  bug: "24244713"
+}
+
+flag {
+  name: "fix_audio_flicker_for_outgoing_calls"
+  namespace: "telecom"
+  description: "This fix ensures the MO calls won't switch from Active to Quite b/c setDialing was not called"
+  bug: "309540769"
+}
diff --git a/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java b/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java
index 38a96eb..5fc2414 100644
--- a/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java
+++ b/src/com/android/server/telecom/CallAudioCommunicationDeviceTracker.java
@@ -24,10 +24,12 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.flags.Flags;
 
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.Semaphore;
 
 /**
  * Helper class used to keep track of the requested communication device within Telecom for audio
@@ -50,6 +52,7 @@
     private int mAudioDeviceType = sAUDIO_DEVICE_TYPE_INVALID;
     // Keep track of the locally requested BT audio device if set
     private String mBtAudioDevice = null;
+    private final Semaphore mLock =  new Semaphore(1);
 
     public CallAudioCommunicationDeviceTracker(Context context) {
         mAudioManager = context.getSystemService(AudioManager.class);
@@ -94,6 +97,9 @@
      */
     public boolean setCommunicationDevice(int audioDeviceType,
             BluetoothDevice btDevice) {
+        if (Flags.communicationDeviceProtectedByLock()) {
+            mLock.tryAcquire();
+        }
         // There is only one audio device type associated with each type of BT device.
         boolean isBtDevice = sBT_AUDIO_DEVICE_TYPES.contains(audioDeviceType);
         Log.i(this, "setCommunicationDevice: type = %s, isBtDevice = %s, btDevice = %s",
@@ -153,8 +159,16 @@
                 if (audioDeviceType == AudioDeviceInfo.TYPE_BLE_HEADSET) {
                     mBluetoothRouteManager.onAudioOn(mBtAudioDevice);
                 }
+            } else if (Flags.communicationDeviceProtectedByLock()) {
+                // Clear BT device if it's still stored. Handles race condition for when a non-BT
+                // device is set for communication shortly after a BT (LE) device is set for
+                // communication but the selection hasn't been cleared yet.
+                mBtAudioDevice = null;
             }
         }
+        if (Flags.communicationDeviceProtectedByLock()) {
+            mLock.release();
+        }
         return result;
     }
 
@@ -164,6 +178,9 @@
      * @param audioDeviceTypes The supported audio device types for the device.
      */
     public void clearCommunicationDevice(int audioDeviceType) {
+        if (Flags.communicationDeviceProtectedByLock()) {
+            mLock.tryAcquire();
+        }
         // There is only one audio device type associated with each type of BT device.
         boolean isBtDevice = sBT_AUDIO_DEVICE_TYPES.contains(audioDeviceType);
         Log.i(this, "clearCommunicationDevice: type = %s, isBtDevice = %s",
@@ -195,6 +212,9 @@
             mBluetoothRouteManager.onAudioLost(mBtAudioDevice);
             mBtAudioDevice = null;
         }
+        if (Flags.communicationDeviceProtectedByLock()) {
+            mLock.release();
+        }
     }
 
     private boolean isUsbHeadsetType(int audioDeviceType, int sourceType) {
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index 7fea8e0..f85a809 100755
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -685,10 +685,15 @@
         mCallStreamingNotification = callStreamingNotification;
         mFeatureFlags = featureFlags;
 
-        mListeners.add(mInCallController);
+        if (mFeatureFlags.useImprovedListenerOrder()) {
+            mListeners.add(mInCallController);
+        }
         mListeners.add(mInCallWakeLockController);
         mListeners.add(statusBarNotifier);
         mListeners.add(mCallLogManager);
+        if (!mFeatureFlags.useImprovedListenerOrder()) {
+            mListeners.add(mInCallController);
+        }
         mListeners.add(mCallEndpointController);
         mListeners.add(mCallDiagnosticServiceController);
         mListeners.add(mCallAudioManager);
@@ -767,6 +772,10 @@
         call.setPostCallPackageName(getRoleManagerAdapter().getDefaultCallScreeningApp(
                 call.getAssociatedUser()));
 
+        if (!mFeatureFlags.fixAudioFlickerForOutgoingCalls()) {
+            setCallState(call, callState, "successful outgoing call");
+        }
+
         if (!mCalls.contains(call)) {
             // Call was not added previously in startOutgoingCall due to it being a potential MMI
             // code, so add it now.
@@ -778,11 +787,16 @@
             listener.onConnectionServiceChanged(call, null, call.getConnectionService());
         }
 
-        // Allow the ConnectionService to start the call in the active state. This case is helpful
-        // for conference calls or meetings that can skip the dialing stage.
-        if (callState == CallState.ACTIVE) {
-            setCallState(call, callState, "skipping the dialing state and setting active");
-        } else {
+        if (mFeatureFlags.fixAudioFlickerForOutgoingCalls()) {
+            // Allow the ConnectionService to start the call in the active state. This case is
+            // helpful for conference calls or meetings that can skip the dialing stage.
+            if (callState == CallState.ACTIVE) {
+                setCallState(call, callState, "skipping the dialing state and setting active");
+            } else {
+                markCallAsDialing(call);
+            }
+        }
+        else{
             markCallAsDialing(call);
         }
     }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 941d2ec..8124f60 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -416,8 +416,13 @@
                         String actualAddress = connectBtAudio(address,
                             true /* switchingBtDevices*/);
                         if (actualAddress != null) {
-                            transitionTo(getConnectingStateForAddress(address,
-                                    "AudioConnected/CONNECT_BT"));
+                            if (mFeatureFlags.useActualAddressToEnterConnectingState()) {
+                                transitionTo(getConnectingStateForAddress(actualAddress,
+                                        "AudioConnected/CONNECT_BT"));
+                            } else {
+                                transitionTo(getConnectingStateForAddress(address,
+                                        "AudioConnected/CONNECT_BT"));
+                            }
                         } else {
                             Log.w(LOG_TAG, "Tried to connect to %s but failed" +
                                     " to connect to any BT device.", (String) args.arg2);
diff --git a/testapps/transactionalVoipApp/res/values-ca/strings.xml b/testapps/transactionalVoipApp/res/values-ca/strings.xml
index 06f1655..5500444 100644
--- a/testapps/transactionalVoipApp/res/values-ca/strings.xml
+++ b/testapps/transactionalVoipApp/res/values-ca/strings.xml
@@ -31,7 +31,7 @@
     <string name="request_earpiece_endpoint" msgid="6649571985089296573">"Auricular"</string>
     <string name="request_speaker_endpoint" msgid="1033259535289845405">"Altaveu"</string>
     <string name="request_bluetooth_endpoint" msgid="5933254250623451836">"Bluetooth"</string>
-    <string name="start_stream" msgid="3567634786280097431">"inicia la reproducció en continu"</string>
+    <string name="start_stream" msgid="3567634786280097431">"inicia la reproducció en línia"</string>
     <string name="crash_app" msgid="2548690390730057704">"llança una excepció"</string>
     <string name="update_notification" msgid="8677916482672588779">"actualitza la notificació a l\'estil de trucada en curs"</string>
 </resources>
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
index 45a2581..e1ef08a 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothRouteManagerTest.java
@@ -248,6 +248,33 @@
         sm.quitNow();
     }
 
+    @SmallTest
+    @Test
+    public void testConnectBtWithoutAddress() {
+        when(mFeatureFlags.useActualAddressToEnterConnectingState()).thenReturn(true);
+        BluetoothRouteManager sm = setupStateMachine(
+                BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX, DEVICE1);
+        setupConnectedDevices(new BluetoothDevice[]{DEVICE1, DEVICE2}, null, null, null, null,
+                null);
+        when(mTimeoutsAdapter.getRetryBluetoothConnectAudioBackoffMillis(
+                nullable(ContentResolver.class))).thenReturn(0L);
+        when(mBluetoothHeadset.connectAudio()).thenReturn(BluetoothStatusCodes.ERROR_UNKNOWN);
+        executeRoutingAction(sm, BluetoothRouteManager.CONNECT_BT, null);
+        // Wait 3 times: the first connection attempt is accounted for in executeRoutingAction,
+        // so wait twice for the retry attempt, again to make sure there are only three attempts,
+        // and once more for good luck.
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
+        verifyConnectionAttempt(DEVICE1, 1);
+        assertEquals(BluetoothRouteManager.AUDIO_CONNECTED_STATE_NAME_PREFIX
+                        + ":" + DEVICE1.getAddress(),
+                sm.getCurrentState().getName());
+        sm.getHandler().removeMessages(BluetoothRouteManager.CONNECTION_TIMEOUT);
+        sm.quitNow();
+    }
+
     private BluetoothRouteManager setupStateMachine(String initialState,
             BluetoothDevice initialDevice) {
         resetMocks();
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index fe94da4..c02a0a8 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -2559,6 +2559,7 @@
     @SmallTest
     @Test
     public void testOutgoingCallStateIsSetToAPreviousStateAndIgnored() {
+        when(mFeatureFlags.fixAudioFlickerForOutgoingCalls()).thenReturn(true);
         Call outgoingCall = addSpyCall(CallState.CONNECTING);
         mCallsManager.onSuccessfulOutgoingCall(outgoingCall, CallState.NEW);
         verify(outgoingCall, never()).setState(eq(CallState.NEW), any());
@@ -2571,6 +2572,7 @@
     @SmallTest
     @Test
     public void testOutgoingCallStateCanAvoidDialingAndGoStraightToActive() {
+        when(mFeatureFlags.fixAudioFlickerForOutgoingCalls()).thenReturn(true);
         Call outgoingCall = addSpyCall(CallState.CONNECTING);
         mCallsManager.onSuccessfulOutgoingCall(outgoingCall, CallState.ACTIVE);
         verify(outgoingCall, never()).setState(eq(CallState.DIALING), any());