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

Commit 0eda6e98 authored by Jakub Rotkiewicz's avatar Jakub Rotkiewicz Committed by Gerrit Code Review
Browse files

Merge "ActiveDeviceManager:fallback when wired dev disconnects" into main

parents bc95ce7d 21397996
Loading
Loading
Loading
Loading
+22 −21
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.bluetooth.btservice;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
@@ -47,6 +46,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -117,7 +117,7 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
    private HandlerThread mHandlerThread = null;
    private Handler mHandler = null;
    private final AudioManager mAudioManager;
    private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
    @VisibleForTesting final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;

    private final Object mLock = new Object();

@@ -783,9 +783,9 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
    }

    /** Notifications of audio device connection and disconnection events. */
    @SuppressLint("AndroidFrameworkRequiresPermission")
    private class AudioManagerAudioDeviceCallback extends AudioDeviceCallback {
        private boolean isWiredAudioHeadset(AudioDeviceInfo deviceInfo) {
    @VisibleForTesting
    class AudioManagerAudioDeviceCallback extends AudioDeviceCallback {
        private static boolean isWiredAudioHeadset(AudioDeviceInfo deviceInfo) {
            switch (deviceInfo.getType()) {
                case AudioDeviceInfo.TYPE_WIRED_HEADSET:
                case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
@@ -800,26 +800,27 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
        @Override
        public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
            Log.d(TAG, "onAudioDevicesAdded");
            boolean hasAddedWiredDevice = false;
            for (AudioDeviceInfo deviceInfo : addedDevices) {
                Log.d(
                        TAG,
                        "Audio device added: "
                                + deviceInfo.getProductName()
                                + " type: "
                                + deviceInfo.getType());
                if (isWiredAudioHeadset(deviceInfo)) {
                    hasAddedWiredDevice = true;
                    break;
                }
            if (!Arrays.stream(addedDevices)
                    .anyMatch(AudioManagerAudioDeviceCallback::isWiredAudioHeadset)) {
                return;
            }
            if (hasAddedWiredDevice) {
            wiredAudioDeviceConnected();
        }
        }

        @Override
        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {}
        public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
            Log.d(TAG, "onAudioDevicesRemoved");
            if (!Flags.fallbackWhenWiredAudioDisconnected()) {
                return;
            }
            if (!Arrays.stream(removedDevices)
                    .anyMatch(AudioManagerAudioDeviceCallback::isWiredAudioHeadset)) {
                return;
            }
            synchronized (mLock) {
                setFallbackDeviceActiveLocked();
            }
        }
    }

    ActiveDeviceManager(AdapterService service, ServiceFactory factory) {
+44 −0
Original line number Diff line number Diff line
@@ -40,8 +40,11 @@ import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothSinkAudioPolicy;
import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.AudioDevicePort;
import android.media.AudioManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import android.util.SparseIntArray;
@@ -1217,6 +1220,33 @@ public class ActiveDeviceManagerTest {
        verify(mHearingAidService, timeout(TIMEOUT_MS)).removeActiveDevice(false);
    }

    /** A wired audio device is disconnected. Check if falls back to connected A2DP. */
    @Test
    @EnableFlags(Flags.FLAG_FALLBACK_WHEN_WIRED_AUDIO_DISCONNECTED)
    public void wiredAudioDeviceDisconnected_setFallbackDevice() throws Exception {
        AudioDeviceInfo[] testDevices = createAudioDeviceInfoTestDevices();

        // Connect A2DP headphones
        a2dpConnected(mA2dpDevice, false);
        verify(mA2dpService, timeout(TIMEOUT_MS).times(1)).setActiveDevice(mA2dpDevice);
        verify(mLeAudioService, timeout(TIMEOUT_MS).times(1)).removeActiveDevice(true);

        // Connect wired audio device
        mActiveDeviceManager.mAudioManagerAudioDeviceCallback.onAudioDevicesAdded(testDevices);

        // Check wiredAudioDeviceConnected invoked properly
        verify(mA2dpService, timeout(TIMEOUT_MS)).removeActiveDevice(false);
        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull());
        verify(mHearingAidService, timeout(TIMEOUT_MS)).removeActiveDevice(false);
        verify(mLeAudioService, timeout(TIMEOUT_MS).times(2)).removeActiveDevice(true);

        // Disconnect wired audio device
        mActiveDeviceManager.mAudioManagerAudioDeviceCallback.onAudioDevicesRemoved(testDevices);

        // Verify fallback to A2DP device
        verify(mA2dpService, timeout(TIMEOUT_MS).times(2)).setActiveDevice(mA2dpDevice);
    }

    /**
     * Verifies if Le Audio Broadcast is streaming, connected a2dp device should not be set as
     * active.
@@ -1458,6 +1488,20 @@ public class ActiveDeviceManagerTest {
                BluetoothProfile.STATE_DISCONNECTED);
    }

    private AudioDeviceInfo[] createAudioDeviceInfoTestDevices() {
        AudioDevicePort a2dpPort = mock(AudioDevicePort.class);
        doReturn(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP).when(a2dpPort).type();
        doReturn("a2dp").when(a2dpPort).name();

        AudioDevicePort usbPort = mock(AudioDevicePort.class);
        doReturn(AudioDeviceInfo.TYPE_USB_HEADSET).when(usbPort).type();
        doReturn("usb").when(usbPort).name();

        AudioDeviceInfo a2dpDevice = new AudioDeviceInfo(a2dpPort);
        AudioDeviceInfo usbDevice = new AudioDeviceInfo(usbPort);
        return new AudioDeviceInfo[] {a2dpDevice, usbDevice};
    }

    private class TestDatabaseManager extends DatabaseManager {
        ArrayMap<BluetoothDevice, SparseIntArray> mProfileConnectionPolicy;