Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96cb64ba authored by Pranav Madapurmath's avatar Pranav Madapurmath
Browse files

Prevent auto-routing to wearable devices.

Currently, if BT is supported, Telecom will auto-route to one of the
available devices. Consequently, a watch that is paired with the phone
will receive the call audio when it is not explicitly requested.

This CL ensures that implicit routing to watches is ignored. When
connecting an arbitrary BT device, the wearable devices need to be
filtered out. Wearable (Pixel) devices are distinguishable by their
class/type (BluetoothClass.Device.WEARABLE_WRIST_WATCH,
BluetoothDevice.DEVICE_TYPE_WATCH). In addition to that, if there are no
devices to connect to, fallback is required to ensure that the audio
route is properly handled.

Bug: 294378768
Test: Manual test with wired headset plugged in and Pixel watch paired.
Unplugged headset during call to verify that audio is routed to earpiece
and that there aren't any issues with user switching to the watch.
Test: Unit test verifying that the audio is routed into another
supported route when there are no non-wearable devices to arbitrarily
connect to.

Change-Id: I14be322037ad968009850d69387ad1aca76a0e05
parent 49d60220
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -14,6 +14,13 @@ flag {
  bug: "289861657"
}

flag {
  name: "ignore_auto_route_to_watch_device"
  namespace: "telecom"
  description: "Ignore auto routing to wearable devices."
  bug: "294378768"
}

flag {
  name: "transit_route_before_audio_disconnect_bt"
  namespace: "telecom"
+25 −1
Original line number Diff line number Diff line
@@ -2022,6 +2022,30 @@ public class CallAudioRouteStateMachine extends StateMachine {
        return false;
    }

    private boolean isWatchActiveOrOnlyWatchesAvailable() {
        if (!mFeatureFlags.ignoreAutoRouteToWatchDevice()) {
            return false;
        }

        boolean containsWatchDevice = false;
        boolean containsNonWatchDevice = false;
        Collection<BluetoothDevice> connectedBtDevices =
                mBluetoothRouteManager.getConnectedDevices();

        for (BluetoothDevice connectedDevice: connectedBtDevices) {
            if (mBluetoothRouteManager.isWatch(connectedDevice)) {
                containsWatchDevice = true;
            } else {
                containsNonWatchDevice = true;
            }
        }

        // Don't ignore switch if watch is already the active device.
        return containsWatchDevice && !containsNonWatchDevice
                && !mBluetoothRouteManager.isWatch(
                        mBluetoothRouteManager.getBluetoothAudioConnectedDevice());
    }

    private int calculateBaselineRouteMessage(boolean isExplicitUserRequest,
            boolean includeBluetooth) {
        boolean isSkipEarpiece = false;
@@ -2034,7 +2058,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
        }
        if ((mAvailableRoutes & ROUTE_BLUETOOTH) != 0
                && !mHasUserExplicitlyLeftBluetooth
                && includeBluetooth) {
                && includeBluetooth && !isWatchActiveOrOnlyWatchesAvailable()) {
            return isExplicitUserRequest ? USER_SWITCH_BLUETOOTH : SWITCH_BLUETOOTH;
        } else if ((mAvailableRoutes & ROUTE_EARPIECE) != 0 && !isSkipEarpiece) {
            return isExplicitUserRequest ? USER_SWITCH_EARPIECE : SWITCH_EARPIECE;
+2 −1
Original line number Diff line number Diff line
@@ -261,7 +261,8 @@ public class TelecomSystem {
                    mContext.getSystemService(BluetoothManager.class).getAdapter(),
                    communicationDeviceTracker);
            BluetoothRouteManager bluetoothRouteManager = new BluetoothRouteManager(mContext, mLock,
                    bluetoothDeviceManager, new Timeouts.Adapter(), communicationDeviceTracker);
                    bluetoothDeviceManager, new Timeouts.Adapter(),
                    communicationDeviceTracker, featureFlags);
            BluetoothStateReceiver bluetoothStateReceiver = new BluetoothStateReceiver(
                    bluetoothDeviceManager, bluetoothRouteManager, communicationDeviceTracker);
            mContext.registerReceiver(bluetoothStateReceiver, BluetoothStateReceiver.INTENT_FILTER);
+43 −4
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.telecom.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
@@ -37,6 +38,7 @@ import com.android.internal.util.StateMachine;
import com.android.server.telecom.CallAudioCommunicationDeviceTracker;
import com.android.server.telecom.TelecomSystem;
import com.android.server.telecom.Timeouts;
import com.android.server.telecom.flags.FeatureFlags;

import java.util.Collection;
import java.util.HashMap;
@@ -470,10 +472,12 @@ public class BluetoothRouteManager extends StateMachine {
    private BluetoothDevice mLeAudioActiveDeviceCache = null;
    private BluetoothDevice mMostRecentlyReportedActiveDevice = null;
    private CallAudioCommunicationDeviceTracker mCommunicationDeviceTracker;
    private FeatureFlags mFeatureFlags;

    public BluetoothRouteManager(Context context, TelecomSystem.SyncRoot lock,
            BluetoothDeviceManager deviceManager, Timeouts.Adapter timeoutsAdapter,
            CallAudioCommunicationDeviceTracker communicationDeviceTracker) {
            CallAudioCommunicationDeviceTracker communicationDeviceTracker,
            FeatureFlags featureFlags) {
        super(BluetoothRouteManager.class.getSimpleName());
        mContext = context;
        mLock = lock;
@@ -481,6 +485,7 @@ public class BluetoothRouteManager extends StateMachine {
        mDeviceManager.setBluetoothRouteManager(this);
        mTimeoutsAdapter = timeoutsAdapter;
        mCommunicationDeviceTracker = communicationDeviceTracker;
        mFeatureFlags = featureFlags;

        mAudioOffState = new AudioOffState();
        addState(mAudioOffState);
@@ -672,6 +677,31 @@ public class BluetoothRouteManager extends StateMachine {
        return mDeviceManager.getUniqueConnectedDevices();
    }

    public boolean isWatch(BluetoothDevice device) {
        if (device == null) {
            Log.i(this, "isWatch: device is null. Returning false");
            return false;
        }

        BluetoothClass deviceClass = device.getBluetoothClass();
        if (deviceClass != null && deviceClass.getDeviceClass()
                == BluetoothClass.Device.WEARABLE_WRIST_WATCH) {
            return true;
        }

        // Check metadata
        byte[] deviceType = device.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE);
        if (deviceType == null) {
            return false;
        }
        String deviceTypeStr = new String(deviceType);
        if (deviceTypeStr.equals(BluetoothDevice.DEVICE_TYPE_WATCH)) {
            return true;
        }

        return false;
    }

    private String connectBtAudio(String address, boolean switchingBtDevices) {
        return connectBtAudio(address, 0, switchingBtDevices);
    }
@@ -701,10 +731,19 @@ public class BluetoothRouteManager extends StateMachine {
                ? address : getActiveDeviceAddress();
        if (actualAddress == null) {
            Log.i(this, "No device specified and BT stack has no active device."
                    + " Using arbitrary device");
                    + " Using arbitrary device - except watch");
            if (deviceList.size() > 0) {
                actualAddress = deviceList.iterator().next().getAddress();
            } else {
                for (BluetoothDevice device : deviceList) {
                    if (mFeatureFlags.ignoreAutoRouteToWatchDevice() && isWatch(device)) {
                        Log.i(this, "Skipping a watch device: " + device);
                        continue;
                    }
                    actualAddress = device.getAddress();
                    break;
                }
            }

            if (actualAddress == null) {
                Log.i(this, "No devices available at all. Not connecting.");
                return null;
            }
+1 −1
Original line number Diff line number Diff line
@@ -190,7 +190,7 @@ public class BluetoothRouteManagerTest extends TelecomTestCase {
        resetMocks();
        BluetoothRouteManager sm = new BluetoothRouteManager(mContext,
                new TelecomSystem.SyncRoot() { }, mDeviceManager,
                mTimeoutsAdapter, mCommunicationDeviceTracker);
                mTimeoutsAdapter, mCommunicationDeviceTracker, mFeatureFlags);
        sm.setListener(mListener);
        sm.setInitialStateForTesting(initialState, initialDevice);
        waitForHandlerAction(sm.getHandler(), TEST_TIMEOUT);
Loading