Don't follow BT audio changes outside of calls

When there's no Telecom call going on, don't use BRM to follow Bluetooth
audio changes -- this will interfere with other apps trying to use HFP.

Bug: 73274274
Test: unit test update, manual
Change-Id: Ia4e71fb71baafc9e9f8702ab72f803d47c37482a
diff --git a/src/com/android/server/telecom/CallAudioManager.java b/src/com/android/server/telecom/CallAudioManager.java
index 4b328f5..ad446ec 100644
--- a/src/com/android/server/telecom/CallAudioManager.java
+++ b/src/com/android/server/telecom/CallAudioManager.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
 import java.util.Collection;
 import java.util.HashSet;
@@ -48,6 +49,7 @@
 
     private final CallAudioRouteStateMachine mCallAudioRouteStateMachine;
     private final CallAudioModeStateMachine mCallAudioModeStateMachine;
+    private final BluetoothStateReceiver mBluetoothStateReceiver;
     private final CallsManager mCallsManager;
     private final InCallTonePlayer.Factory mPlayerFactory;
     private final Ringer mRinger;
@@ -65,6 +67,7 @@
             InCallTonePlayer.Factory playerFactory,
             Ringer ringer,
             RingbackPlayer ringbackPlayer,
+            BluetoothStateReceiver bluetoothStateReceiver,
             DtmfLocalTonePlayer dtmfLocalTonePlayer) {
         mActiveDialingOrConnectingCalls = new LinkedHashSet<>();
         mRingingCalls = new LinkedHashSet<>();
@@ -85,6 +88,7 @@
         mPlayerFactory = playerFactory;
         mRinger = ringer;
         mRingbackPlayer = ringbackPlayer;
+        mBluetoothStateReceiver = bluetoothStateReceiver;
         mDtmfLocalTonePlayer = dtmfLocalTonePlayer;
 
         mPlayerFactory.setCallAudioManager(this);
@@ -148,6 +152,9 @@
         }
         updateForegroundCall();
         mCalls.add(call);
+        if (mCalls.size() == 1) {
+            mBluetoothStateReceiver.setIsInCall(true);
+        }
 
         onCallEnteringState(call, call.getState());
     }
@@ -166,6 +173,9 @@
 
         updateForegroundCall();
         mCalls.remove(call);
+        if (mCalls.size() == 0) {
+            mBluetoothStateReceiver.setIsInCall(false);
+        }
 
         onCallLeavingState(call, call.getState());
     }
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index faaffe8..c7e994b 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -57,6 +57,7 @@
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter;
 import com.android.server.telecom.callfiltering.BlockCheckerAdapter;
 import com.android.server.telecom.callfiltering.CallFilterResultCallback;
@@ -337,6 +338,7 @@
             EmergencyCallHelper emergencyCallHelper,
             InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory,
             ClockProxy clockProxy,
+            BluetoothStateReceiver bluetoothStateReceiver,
             InCallControllerFactory inCallControllerFactory) {
         mContext = context;
         mLock = lock;
@@ -392,7 +394,8 @@
         mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine,
                 this,new CallAudioModeStateMachine((AudioManager)
                         mContext.getSystemService(Context.AUDIO_SERVICE)),
-                playerFactory, mRinger, new RingbackPlayer(playerFactory), mDtmfLocalTonePlayer);
+                playerFactory, mRinger, new RingbackPlayer(playerFactory),
+                bluetoothStateReceiver, mDtmfLocalTonePlayer);
 
         mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create(
                 mRequester, Looper.getMainLooper());
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 18b24f2..3fd0e21 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -19,6 +19,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 import com.android.server.telecom.components.UserCallIntentProcessor;
 import com.android.server.telecom.components.UserCallIntentProcessorFactory;
 import com.android.server.telecom.ui.IncomingCallNotifier;
@@ -229,6 +230,10 @@
                 new BluetoothAdapterProxy(), mLock);
         BluetoothRouteManager bluetoothRouteManager = new BluetoothRouteManager(mContext, mLock,
                 bluetoothDeviceManager, new Timeouts.Adapter());
+        BluetoothStateReceiver bluetoothStateReceiver = new BluetoothStateReceiver(
+                bluetoothDeviceManager, bluetoothRouteManager);
+        mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
+
         WiredHeadsetManager wiredHeadsetManager = new WiredHeadsetManager(mContext);
         SystemStateProvider systemStateProvider = new SystemStateProvider(mContext);
 
@@ -271,6 +276,7 @@
                 emergencyCallHelper,
                 toneGeneratorFactory,
                 clockProxy,
+                bluetoothStateReceiver,
                 inCallControllerFactory);
 
         mIncomingCallNotifier = incomingCallNotifier;
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
index e45e9af..ff81b4d 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java
@@ -80,49 +80,6 @@
                 }
            };
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Log.startSession("BM.oR");
-            try {
-                String action = intent.getAction();
-
-                if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
-                    int bluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                            BluetoothHeadset.STATE_DISCONNECTED);
-                    BluetoothDevice device =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-
-                    if (device == null) {
-                        Log.w(BluetoothDeviceManager.this, "Got null device from broadcast. " +
-                                "Ignoring.");
-                        return;
-                    }
-
-                    Log.i(BluetoothDeviceManager.this, "Device %s changed state to %d",
-                            device.getAddress(), bluetoothHeadsetState);
-
-                    synchronized (mLock) {
-                        if (bluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED) {
-                            if (!mConnectedDevicesByAddress.containsKey(device.getAddress())) {
-                                mConnectedDevicesByAddress.put(device.getAddress(), device);
-                                mBluetoothRouteManager.onDeviceAdded(device.getAddress());
-                            }
-                        } else if (bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTED
-                                || bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTING) {
-                            if (mConnectedDevicesByAddress.containsKey(device.getAddress())) {
-                                mConnectedDevicesByAddress.remove(device.getAddress());
-                                mBluetoothRouteManager.onDeviceLost(device.getAddress());
-                            }
-                        }
-                    }
-                }
-            } finally {
-                Log.endSession();
-            }
-        }
-    };
-
     private final LinkedHashMap<String, BluetoothDevice> mConnectedDevicesByAddress =
             new LinkedHashMap<>();
     private final TelecomSystem.SyncRoot mLock;
@@ -138,9 +95,6 @@
             bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
                     BluetoothProfile.HEADSET);
         }
-        IntentFilter intentFilter =
-                new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
-        context.registerReceiver(mReceiver, intentFilter);
     }
 
     public void setBluetoothRouteManager(BluetoothRouteManager brm) {
@@ -174,4 +128,22 @@
     public void setHeadsetServiceForTesting(BluetoothHeadsetProxy bluetoothHeadset) {
         mBluetoothHeadsetService = bluetoothHeadset;
     }
+
+    void onDeviceConnected(BluetoothDevice device) {
+        synchronized (mLock) {
+            if (!mConnectedDevicesByAddress.containsKey(device.getAddress())) {
+                mConnectedDevicesByAddress.put(device.getAddress(), device);
+                mBluetoothRouteManager.onDeviceAdded(device.getAddress());
+            }
+        }
+    }
+
+    void onDeviceDisconnected(BluetoothDevice device) {
+        synchronized (mLock) {
+            if (mConnectedDevicesByAddress.containsKey(device.getAddress())) {
+                mConnectedDevicesByAddress.remove(device.getAddress());
+                mBluetoothRouteManager.onDeviceLost(device.getAddress());
+            }
+        }
+    }
 }
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
index 2a4d6af..cdb8b4e 100644
--- a/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
+++ b/src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java
@@ -80,47 +80,6 @@
         void onBluetoothAudioDisconnected();
     }
 
-    // Broadcast receiver to receive audio state change broadcasts from the BT stack
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            Log.startSession("BRM.oR");
-            try {
-                String action = intent.getAction();
-
-                if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
-                    int bluetoothHeadsetAudioState =
-                            intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
-                                    BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
-                    BluetoothDevice device =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-                    if (device == null) {
-                        Log.w(BluetoothRouteManager.this, "Got null device from broadcast. " +
-                                "Ignoring.");
-                        return;
-                    }
-
-                    Log.i(BluetoothRouteManager.this, "Device %s transitioned to audio state %d",
-                            device.getAddress(), bluetoothHeadsetAudioState);
-                    Session session = Log.createSubsession();
-                    SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = session;
-                    args.arg2 = device.getAddress();
-                    switch (bluetoothHeadsetAudioState) {
-                        case BluetoothHeadset.STATE_AUDIO_CONNECTED:
-                            sendMessage(HFP_IS_ON, args);
-                            break;
-                        case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
-                            sendMessage(HFP_LOST, args);
-                            break;
-                    }
-                }
-            } finally {
-                Log.endSession();
-            }
-        }
-    };
-
     /**
      * Constants representing messages sent to the state machine.
      * Messages are expected to be sent with {@link SomeArgs} as the obj.
@@ -511,9 +470,6 @@
         mDeviceManager.setBluetoothRouteManager(this);
         mTimeoutsAdapter = timeoutsAdapter;
 
-        IntentFilter intentFilter = new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
-        context.registerReceiver(mReceiver, intentFilter);
-
         mAudioOffState = new AudioOffState();
         addState(mAudioOffState);
         setInitialState(mAudioOffState);
diff --git a/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
new file mode 100644
index 0000000..802fba0
--- /dev/null
+++ b/src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.telecom.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.telecom.Log;
+import android.telecom.Logging.Session;
+
+import com.android.internal.os.SomeArgs;
+
+import static com.android.server.telecom.bluetooth.BluetoothRouteManager.HFP_IS_ON;
+import static com.android.server.telecom.bluetooth.BluetoothRouteManager.HFP_LOST;
+
+
+public class BluetoothStateReceiver extends BroadcastReceiver {
+    private static final String LOG_TAG = BluetoothStateReceiver.class.getSimpleName();
+    public static final IntentFilter INTENT_FILTER;
+    static {
+        INTENT_FILTER = new IntentFilter();
+        INTENT_FILTER.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        INTENT_FILTER.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+    }
+
+    // If not in a call, BSR won't listen to the Bluetooth stack's HFP on/off messages, since
+    // other apps could be turning it on and off. We don't want to interfere.
+    private boolean mIsInCall = false;
+    private final BluetoothRouteManager mBluetoothRouteManager;
+    private final BluetoothDeviceManager mBluetoothDeviceManager;
+
+    public void onReceive(Context context, Intent intent) {
+        Log.startSession("BSR.oR");
+        try {
+            String action = intent.getAction();
+
+            if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
+                if (!mIsInCall) {
+                    Log.i(LOG_TAG, "Ignoring BT audio state change since we're not in a call");
+                    return;
+                }
+                int bluetoothHeadsetAudioState =
+                        intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+                                BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
+                BluetoothDevice device =
+                        intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                if (device == null) {
+                    Log.w(LOG_TAG, "Got null device from broadcast. " +
+                            "Ignoring.");
+                    return;
+                }
+
+                Log.i(LOG_TAG, "Device %s transitioned to audio state %d",
+                        device.getAddress(), bluetoothHeadsetAudioState);
+                Session session = Log.createSubsession();
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = session;
+                args.arg2 = device.getAddress();
+                switch (bluetoothHeadsetAudioState) {
+                    case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+                        mBluetoothRouteManager.sendMessage(HFP_IS_ON, args);
+                        break;
+                    case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+                        mBluetoothRouteManager.sendMessage(HFP_LOST, args);
+                        break;
+                }
+            }
+
+            else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+                int bluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+                        BluetoothHeadset.STATE_DISCONNECTED);
+                BluetoothDevice device =
+                        intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+
+                if (device == null) {
+                    Log.w(LOG_TAG, "Got null device from broadcast. " +
+                            "Ignoring.");
+                    return;
+                }
+
+                Log.i(LOG_TAG, "Device %s changed state to %d",
+                        device.getAddress(), bluetoothHeadsetState);
+
+                if (bluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED) {
+                    mBluetoothDeviceManager.onDeviceConnected(device);
+                } else if (bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTED
+                        || bluetoothHeadsetState == BluetoothHeadset.STATE_DISCONNECTING) {
+                    mBluetoothDeviceManager.onDeviceDisconnected(device);
+                }
+            }
+        } finally {
+            Log.endSession();
+        }
+    }
+
+    public BluetoothStateReceiver(BluetoothDeviceManager deviceManager,
+            BluetoothRouteManager routeManager) {
+        mBluetoothDeviceManager = deviceManager;
+        mBluetoothRouteManager = routeManager;
+    }
+
+    public void setIsInCall(boolean isInCall) {
+        mIsInCall = isInCall;
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
index 59934dc..4a48f1b 100644
--- a/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/BluetoothDeviceManagerTest.java
@@ -30,6 +30,7 @@
 import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.bluetooth.BluetoothDeviceManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -77,14 +78,8 @@
                 serviceCaptor.capture(), eq(BluetoothProfile.HEADSET));
         serviceListenerUnderTest = serviceCaptor.getValue();
 
-        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
-                ArgumentCaptor.forClass(BroadcastReceiver.class);
-        ArgumentCaptor<IntentFilter> intentFilterCaptor =
-                ArgumentCaptor.forClass(IntentFilter.class);
-        verify(mContext).registerReceiver(receiverCaptor.capture(), intentFilterCaptor.capture());
-        assertTrue(intentFilterCaptor.getValue().hasAction(
-                BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED));
-        receiverUnderTest = receiverCaptor.getValue();
+        receiverUnderTest = new BluetoothStateReceiver(mBluetoothDeviceManager,
+                null /* route mgr not needed here */);
 
         mBluetoothDeviceManager.setHeadsetServiceForTesting(mHeadsetProxy);
     }
diff --git a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
index 78f7b50..5e23dcc 100644
--- a/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallAudioManagerTest.java
@@ -31,6 +31,7 @@
 import com.android.server.telecom.InCallTonePlayer;
 import com.android.server.telecom.RingbackPlayer;
 import com.android.server.telecom.Ringer;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -64,6 +65,7 @@
     @Mock private Ringer mRinger;
     @Mock private RingbackPlayer mRingbackPlayer;
     @Mock private DtmfLocalTonePlayer mDtmfLocalTonePlayer;
+    @Mock private BluetoothStateReceiver mBluetoothStateReceiver;
 
     private CallAudioManager mCallAudioManager;
 
@@ -86,6 +88,7 @@
                 mPlayerFactory,
                 mRinger,
                 mRingbackPlayer,
+                mBluetoothStateReceiver,
                 mDtmfLocalTonePlayer);
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
index d3efa99..6bba7af 100644
--- a/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallsManagerTest.java
@@ -72,6 +72,7 @@
 import com.android.server.telecom.Timeouts;
 import com.android.server.telecom.WiredHeadsetManager;
 import com.android.server.telecom.bluetooth.BluetoothRouteManager;
+import com.android.server.telecom.bluetooth.BluetoothStateReceiver;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -140,6 +141,7 @@
     @Mock private InCallControllerFactory mInCallControllerFactory;
     @Mock private InCallController mInCallController;
     @Mock private ConnectionServiceFocusManager mConnectionSvrFocusMgr;
+    @Mock private BluetoothStateReceiver mBluetoothStateReceiver;
 
     private CallsManager mCallsManager;
 
@@ -181,6 +183,7 @@
                 mEmergencyCallHelper,
                 mToneGeneratorFactory,
                 mClockProxy,
+                mBluetoothStateReceiver,
                 mInCallControllerFactory);
 
         when(mPhoneAccountRegistrar.getPhoneAccount(