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

Commit 257bcc26 authored by Hyundo Moon's avatar Hyundo Moon Committed by Gerrit Code Review
Browse files

Merge "Suppress noisy intent when a fallback device exist (A2DP)"

parents 8784b063 cd7925fa
Loading
Loading
Loading
Loading
+31 −39
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import static com.android.bluetooth.Utils.enforceCdmAssociation;
import static com.android.bluetooth.Utils.hasBluetoothPrivilegedPermission;

import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.app.compat.CompatChanges;
import android.bluetooth.BluetoothA2dp;
@@ -90,7 +91,8 @@ public class A2dpService extends ProfileService {
    A2dpNativeInterface mA2dpNativeInterface;
    @VisibleForTesting
    ServiceFactory mFactory = new ServiceFactory();
    private AudioManager mAudioManager;
    @VisibleForTesting
    AudioManager mAudioManager;
    private A2dpCodecConfig mA2dpCodecConfig;
    private CompanionDeviceManager mCompanionDeviceManager;

@@ -99,7 +101,7 @@ public class A2dpService extends ProfileService {
    private final ConcurrentMap<BluetoothDevice, A2dpStateMachine> mStateMachines =
            new ConcurrentHashMap<>();

    // Protect setActiveDevice() so all invoked is handled squentially
    // Protect setActiveDevice()/removeActiveDevice() so all invoked is handled sequentially
    private final Object mActiveSwitchingGuard = new Object();

    // Timeout for state machine thread join, to prevent potential ANR.
@@ -186,7 +188,7 @@ public class A2dpService extends ProfileService {
        mAdapterService.notifyActivityAttributionInfo(getAttributionSource(), deviceAddress);

        // Step 9: Clear active device
        setActiveDevice(null);
        removeActiveDevice(false);

        return true;
    }
@@ -487,38 +489,25 @@ public class A2dpService extends ProfileService {
        }
    }

    private void removeActiveDevice(boolean forceStopPlayingAudio) {
    /**
     * Removes the current active device.
     *
     * @param stopAudio whether the current media playback should be stopped.
     * @return true on success, otherwise false
     */
    public boolean removeActiveDevice(boolean stopAudio) {
        synchronized (mActiveSwitchingGuard) {
            BluetoothDevice previousActiveDevice = null;
            synchronized (mStateMachines) {
                if (mActiveDevice == null) return;
                if (mActiveDevice == null) return true;
                previousActiveDevice = mActiveDevice;
            }

            int prevActiveConnectionState = getConnectionState(previousActiveDevice);

            // As per b/202602952, if we remove the active device due to a disconnection,
            // we need to check if another device is connected and set it active instead.
            // Calling this before any other active related calls has the same effect as
            // a classic active device switch.
            BluetoothDevice fallbackdevice = getFallbackDevice();
            if (fallbackdevice != null && prevActiveConnectionState
                    != BluetoothProfile.STATE_CONNECTED) {
                setActiveDevice(fallbackdevice);
                return;
            }

            // This needs to happen before we inform the audio manager that the device
            // disconnected. Please see comment in updateAndBroadcastActiveDevice() for why.
            updateAndBroadcastActiveDevice(null);

            // Make sure the Audio Manager knows the previous Active device is disconnected.
            // However, if A2DP is still connected and not forcing stop audio for that remote
            // device, the user has explicitly switched the output to the local device and music
            // should continue playing. Otherwise, the remote device has been indeed disconnected
            // and audio should be suspended before switching the output to the local device.
            boolean stopAudio = forceStopPlayingAudio || (prevActiveConnectionState
                        != BluetoothProfile.STATE_CONNECTED);
            // Make sure the Audio Manager knows the previous active device is no longer active.
            mAudioManager.handleBluetoothActiveDeviceChanged(null, previousActiveDevice,
                    BluetoothProfileConnectionInfo.createA2dpInfo(!stopAudio, -1));

@@ -527,9 +516,11 @@ public class A2dpService extends ProfileService {
                if (!mA2dpNativeInterface.setActiveDevice(null)) {
                    Log.w(TAG, "setActiveDevice(null): Cannot remove active device in native "
                            + "layer");
                    return false;
                }
            }
        }
        return true;
    }

    /**
@@ -540,7 +531,7 @@ public class A2dpService extends ProfileService {
     * @return true on success, false on error
     */
    @VisibleForTesting
    public boolean setSilenceMode(BluetoothDevice device, boolean silence) {
    public boolean setSilenceMode(@NonNull BluetoothDevice device, boolean silence) {
        if (DBG) {
            Log.d(TAG, "setSilenceMode(" + device + "): " + silence);
        }
@@ -560,17 +551,16 @@ public class A2dpService extends ProfileService {
    /**
     * Set the active device.
     *
     * @param device the active device
     * @param device the active device. Should not be null.
     * @return true on success, otherwise false
     */
    public boolean setActiveDevice(BluetoothDevice device) {
        synchronized (mActiveSwitchingGuard) {
    public boolean setActiveDevice(@NonNull BluetoothDevice device) {
        if (device == null) {
                // Remove active device and continue playing audio only if necessary.
                removeActiveDevice(false);
                return true;
            Log.e(TAG, "device should not be null!");
            return false;
        }

        synchronized (mActiveSwitchingGuard) {
            A2dpStateMachine sm = null;
            BluetoothDevice previousActiveDevice = null;
            synchronized (mStateMachines) {
@@ -1256,10 +1246,8 @@ public class A2dpService extends ProfileService {
        if (toState == BluetoothProfile.STATE_CONNECTED && (mMaxConnectedAudioDevices == 1)) {
            setActiveDevice(device);
        }
        // Check if the active device is not connected anymore
        if (isActiveDevice(device) && (fromState == BluetoothProfile.STATE_CONNECTED)) {
            setActiveDevice(null);
        }
        // When disconnected, ActiveDeviceManager will call setActiveDevice(null)

        // Check if the device is disconnected - if unbond, remove the state machine
        if (toState == BluetoothProfile.STATE_DISCONNECTED) {
            if (mAdapterService.getBondState(device) == BluetoothDevice.BOND_NONE) {
@@ -1393,8 +1381,12 @@ public class A2dpService extends ProfileService {
                A2dpService service = getService(source);
                boolean result = false;
                if (service != null) {
                    if (device == null) {
                        result = service.removeActiveDevice(false);
                    } else {
                        result = service.setActiveDevice(device);
                    }
                }
                receiver.send(result);
            } catch (RuntimeException e) {
                receiver.propagateException(e);
+3 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.bluetooth.avrcp;

import android.annotation.NonNull;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
@@ -292,7 +293,7 @@ public class AvrcpTargetService extends ProfileService {
        return service.getActiveDevice();
    }

    private void setA2dpActiveDevice(BluetoothDevice device) {
    private void setA2dpActiveDevice(@NonNull BluetoothDevice device) {
        A2dpService service = A2dpService.getA2dpService();
        if (service == null) {
            Log.d(TAG, "setA2dpActiveDevice: A2dp service not found");
@@ -459,6 +460,7 @@ public class AvrcpTargetService extends ProfileService {
        Log.i(TAG, "setActiveDevice: device=" + device);
        if (device == null) {
            Log.wtf(TAG, "setActiveDevice: could not find device " + device);
            return;
        }
        setA2dpActiveDevice(device);
    }
+56 −29
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.bluetooth.btservice;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothA2dp;
@@ -307,7 +309,7 @@ class ActiveDeviceManager {
            mHearingAidConnectedDevices.add(device);
            // New connected device: select it as active
            setHearingAidActiveDevice(device);
            setA2dpActiveDevice(null);
            setA2dpActiveDevice(null, true);
            setHfpActiveDevice(null);
            setLeAudioActiveDevice(null);
        }
@@ -327,12 +329,12 @@ class ActiveDeviceManager {
                    && mPendingLeHearingAidActiveDevice.isEmpty()) {
                // New connected device: select it as active
                setLeAudioActiveDevice(device);
                setA2dpActiveDevice(null);
                setA2dpActiveDevice(null, true);
                setHfpActiveDevice(null);
            } else if (mPendingLeHearingAidActiveDevice.contains(device)) {
                setLeHearingAidActiveDevice(device);
                setHearingAidActiveDevice(null);
                setA2dpActiveDevice(null);
                setA2dpActiveDevice(null, true);
                setHfpActiveDevice(null);
            }
        }
@@ -355,7 +357,7 @@ class ActiveDeviceManager {
                // New connected device: select it as active
                setLeHearingAidActiveDevice(device);
                setHearingAidActiveDevice(null);
                setA2dpActiveDevice(null);
                setA2dpActiveDevice(null, true);
                setHfpActiveDevice(null);
            }
        }
@@ -368,10 +370,9 @@ class ActiveDeviceManager {
            }
            mA2dpConnectedDevices.remove(device);
            if (Objects.equals(mA2dpActiveDevice, device)) {
                if (mA2dpConnectedDevices.isEmpty()) {
                    setA2dpActiveDevice(null);
                if (!setFallbackDeviceActiveLocked()) {
                    setA2dpActiveDevice(null, false);
                }
                setFallbackDeviceActiveLocked();
            }
        }
    }
@@ -487,7 +488,7 @@ class ActiveDeviceManager {
                }
            }
            if (device != null) {
                setA2dpActiveDevice(null);
                setA2dpActiveDevice(null, true);
                setHfpActiveDevice(null);
                setLeAudioActiveDevice(null);
            }
@@ -504,7 +505,7 @@ class ActiveDeviceManager {
            }
            // Just assign locally the new value
            if (device != null && !Objects.equals(mLeAudioActiveDevice, device)) {
                setA2dpActiveDevice(null);
                setA2dpActiveDevice(null, true);
                setHfpActiveDevice(null);
                setHearingAidActiveDevice(null);
            }
@@ -522,7 +523,7 @@ class ActiveDeviceManager {
            }
            // Just assign locally the new value
            if (device != null && !Objects.equals(mLeHearingAidActiveDevice, device)) {
                setA2dpActiveDevice(null);
                setA2dpActiveDevice(null, true);
                setHfpActiveDevice(null);
                setHearingAidActiveDevice(null);
            }
@@ -633,18 +634,33 @@ class ActiveDeviceManager {
        return mHandlerThread.getLooper();
    }

    private void setA2dpActiveDevice(BluetoothDevice device) {
        synchronized (mLock) {
    private void setA2dpActiveDevice(@NonNull BluetoothDevice device) {
        setA2dpActiveDevice(device, false);
    }

    private void setA2dpActiveDevice(@Nullable BluetoothDevice device, boolean hasFallbackDevice) {
        if (DBG) {
                Log.d(TAG, "setA2dpActiveDevice(" + device + ")");
            Log.d(TAG, "setA2dpActiveDevice(" + device + ")"
                    + (device == null ? " hasFallbackDevice=" + hasFallbackDevice : ""));
        }

        final A2dpService a2dpService = mFactory.getA2dpService();
        if (a2dpService == null) {
            return;
        }
            if (!a2dpService.setActiveDevice(device)) {

        boolean success = false;
        if (device == null) {
            success = a2dpService.removeActiveDevice(!hasFallbackDevice);
        } else {
            success = a2dpService.setActiveDevice(device);
        }

        if (!success) {
            return;
        }

        synchronized (mLock) {
            mA2dpActiveDevice = device;
        }
    }
@@ -733,13 +749,20 @@ class ActiveDeviceManager {
        }
    }

    private void setFallbackDeviceActiveLocked() {
    /**
     * TODO: This method can return true when a fallback device for an unrelated profile is found.
     *       Take disconnected profile as an argument, and find the exact fallback device.
     *       Also, split this method to smaller methods for better readability.
     *
     * @return true when the fallback device is activated, false otherwise
     */
    private boolean setFallbackDeviceActiveLocked() {
        if (DBG) {
            Log.d(TAG, "setFallbackDeviceActive");
        }
        DatabaseManager dbManager = mAdapterService.getDatabase();
        if (dbManager == null) {
            return;
            return false;
        }
        List<BluetoothDevice> connectedHearingAidDevices = new ArrayList<>();
        if (!mHearingAidConnectedDevices.isEmpty()) {
@@ -757,7 +780,7 @@ class ActiveDeviceManager {
                        Log.d(TAG, "set hearing aid device active: " + device);
                    }
                    setHearingAidActiveDevice(device);
                    setA2dpActiveDevice(null);
                    setA2dpActiveDevice(null, true);
                    setHfpActiveDevice(null);
                    setLeAudioActiveDevice(null);
                } else {
@@ -766,10 +789,10 @@ class ActiveDeviceManager {
                    }
                    setLeHearingAidActiveDevice(device);
                    setHearingAidActiveDevice(null);
                    setA2dpActiveDevice(null);
                    setA2dpActiveDevice(null, true);
                    setHfpActiveDevice(null);
                }
                return;
                return true;
            }
        }

@@ -820,7 +843,7 @@ class ActiveDeviceManager {
                        Log.d(TAG, "set LE audio device active: " + device);
                    }
                    setLeAudioActiveDevice(device);
                    setA2dpActiveDevice(null);
                    setA2dpActiveDevice(null, true);
                    setHfpActiveDevice(null);
                }
            } else {
@@ -838,11 +861,15 @@ class ActiveDeviceManager {
                        Log.d(TAG, "set LE audio device active: " + device);
                    }
                    setLeAudioActiveDevice(device);
                    setA2dpActiveDevice(null);
                    setA2dpActiveDevice(null, true);
                    setHfpActiveDevice(null);
                }
            }
            return true;
        }

        // No fallback device is found.
        return false;
    }

    private void resetState() {
@@ -908,7 +935,7 @@ class ActiveDeviceManager {
        if (DBG) {
            Log.d(TAG, "wiredAudioDeviceConnected");
        }
        setA2dpActiveDevice(null);
        setA2dpActiveDevice(null, true);
        setHfpActiveDevice(null);
        setHearingAidActiveDevice(null);
        setLeAudioActiveDevice(null);
+5 −1
Original line number Diff line number Diff line
@@ -5298,8 +5298,12 @@ public class AdapterService extends Service {
                || mA2dpService.getConnectionPolicy(device)
                == BluetoothProfile.CONNECTION_POLICY_ALLOWED)) {
            Log.i(TAG, "setActiveDevice: Setting active A2dp device " + device);
            if (device == null) {
                mA2dpService.removeActiveDevice(false);
            } else {
                mA2dpService.setActiveDevice(device);
            }
        }

        if (mHearingAidService != null && (device == null
                || mHearingAidService.getConnectionPolicy(device)
+2 −3
Original line number Diff line number Diff line
@@ -25,8 +25,6 @@ import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_READ;
import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE;
import static android.bluetooth.BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE;

import static java.util.Map.entry;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
@@ -937,9 +935,10 @@ public class MediaControlGattService implements MediaControlGattServiceInterface
                    + " request up");
        }

        // TODO: Activate/deactivate devices with ActiveDeviceManager
        if (req.getOpcode() == Request.Opcodes.PLAY) {
            if (mAdapterService.getActiveDevices(BluetoothProfile.A2DP).size() > 0) {
                A2dpService.getA2dpService().setActiveDevice(null);
                A2dpService.getA2dpService().removeActiveDevice(false);
            }
            if (mAdapterService.getActiveDevices(BluetoothProfile.HEARING_AID).size() > 0) {
                HearingAidService.getHearingAidService().setActiveDevice(null);
Loading