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

Commit e2fa77ec authored by Alex Dadukin's avatar Alex Dadukin Committed by Automerger Merge Worker
Browse files

Merge "Implement BluetoothRouteController that supports Audio Policies" into udc-dev am: 03a1c5f7

parents 0b2d1605 03a1c5f7
Loading
Loading
Loading
Loading
+175 −278
Original line number Original line Diff line number Diff line
/*
/*
 * Copyright 2020 The Android Open Source Project
 * Copyright (C) 2023 The Android Open Source Project
 *
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@ package com.android.server.media;


import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED;
import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED;
import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTED;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
@@ -37,18 +36,18 @@ import android.media.AudioSystem;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2Info;
import android.os.UserHandle;
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseIntArray;


import com.android.internal.R;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Set;


/**
/**
@@ -61,58 +60,70 @@ import java.util.Set;
 * <p>Selected route override should be used by {@link AudioManager} which is aware of Audio
 * <p>Selected route override should be used by {@link AudioManager} which is aware of Audio
 * Policies.
 * Policies.
 */
 */
class AudioPoliciesBluetoothRouteController implements BluetoothRouteController {
/* package */ class AudioPoliciesBluetoothRouteController
        implements BluetoothRouteController {
    private static final String TAG = "APBtRouteController";
    private static final String TAG = "APBtRouteController";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);


    private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
    private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
    private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";
    private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_";


    // Maps hardware address to BluetoothRouteInfo
    @NonNull
    private final AdapterStateChangedReceiver mAdapterStateChangedReceiver =
            new AdapterStateChangedReceiver();

    @NonNull
    private final DeviceStateChangedReceiver mDeviceStateChangedReceiver =
            new DeviceStateChangedReceiver();

    @NonNull
    private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
    private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
    private final List<BluetoothRouteInfo> mActiveRoutes = new ArrayList<>();


    // Route type -> volume map
    @NonNull
    private final SparseIntArray mVolumeMap = new SparseIntArray();
    private final SparseIntArray mVolumeMap = new SparseIntArray();


    @NonNull
    private final Context mContext;
    private final Context mContext;
    @NonNull
    private final BluetoothAdapter mBluetoothAdapter;
    private final BluetoothAdapter mBluetoothAdapter;
    private final BluetoothRoutesUpdatedListener mListener;
    @NonNull
    private final BluetoothRouteController.BluetoothRoutesUpdatedListener mListener;
    @NonNull
    private final BluetoothProfileMonitor mBluetoothProfileMonitor;
    @NonNull
    private final AudioManager mAudioManager;
    private final AudioManager mAudioManager;
    private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();


    private final AdapterStateChangedReceiver mAdapterStateChangedReceiver =
    @Nullable
            new AdapterStateChangedReceiver();
    private BluetoothRouteInfo mSelectedBluetoothRoute;
    private final DeviceStateChangedReceiver mDeviceStateChangedReceiver =

            new DeviceStateChangedReceiver();
    AudioPoliciesBluetoothRouteController(@NonNull Context context,
            @NonNull BluetoothAdapter bluetoothAdapter,
            @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
        this(context, bluetoothAdapter,
                new BluetoothProfileMonitor(context, bluetoothAdapter), listener);
    }


    private BluetoothA2dp mA2dpProfile;
    @VisibleForTesting
    private BluetoothHearingAid mHearingAidProfile;
    AudioPoliciesBluetoothRouteController(@NonNull Context context,
    private BluetoothLeAudio mLeAudioProfile;
            @NonNull BluetoothAdapter bluetoothAdapter,
            @NonNull BluetoothProfileMonitor bluetoothProfileMonitor,
            @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
        Objects.requireNonNull(context);
        Objects.requireNonNull(bluetoothAdapter);
        Objects.requireNonNull(bluetoothProfileMonitor);
        Objects.requireNonNull(listener);


    AudioPoliciesBluetoothRouteController(Context context, BluetoothAdapter btAdapter,
            BluetoothRoutesUpdatedListener listener) {
        mContext = context;
        mContext = context;
        mBluetoothAdapter = btAdapter;
        mBluetoothAdapter = bluetoothAdapter;
        mBluetoothProfileMonitor = bluetoothProfileMonitor;
        mAudioManager = mContext.getSystemService(AudioManager.class);
        mListener = listener;
        mListener = listener;
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);

        buildBluetoothRoutes();
        updateBluetoothRoutes();
    }
    }


    /**
     * Registers listener to bluetooth status changes as the provided user.
     *
     * The registered receiver listens to {@link BluetoothA2dp#ACTION_ACTIVE_DEVICE_CHANGED} and
     * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED } events for {@link BluetoothProfile#A2DP},
     * {@link BluetoothProfile#HEARING_AID}, and {@link BluetoothProfile#LE_AUDIO} bluetooth profiles.
     *
     * @param user {@code UserHandle} as which receiver is registered
     */
    @Override
    @Override
    public void start(UserHandle user) {
    public void start(UserHandle user) {
        mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
        mBluetoothProfileMonitor.start();
        mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
        mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO);


        IntentFilter adapterStateChangedIntentFilter = new IntentFilter();
        IntentFilter adapterStateChangedIntentFilter = new IntentFilter();


@@ -143,22 +154,59 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
    }
    }


    @Override
    @Override
    public boolean selectRoute(String deviceAddress) {
    public boolean selectRoute(@Nullable String deviceAddress) {
        // Temporary no-op.
        synchronized (this) {
            // Fetch all available devices in order to avoid race conditions with Bluetooth stack.
            updateBluetoothRoutes();

            if (deviceAddress == null) {
                mSelectedBluetoothRoute = null;
                return true;
            }

            BluetoothRouteInfo bluetoothRouteInfo = mBluetoothRoutes.get(deviceAddress);

            if (bluetoothRouteInfo == null) {
                Slog.w(TAG, "Cannot find bluetooth route for " + deviceAddress);
                return false;
                return false;
            }
            }


            mSelectedBluetoothRoute = bluetoothRouteInfo;
            setRouteConnectionState(mSelectedBluetoothRoute, STATE_CONNECTED);

            updateConnectivityStateForDevicesInTheSameGroup();

            return true;
        }
    }

    /**
    /**
     * Transfers to a given bluetooth route.
     * Updates connectivity state for devices in the same devices group.
     * The dedicated BT device with the route would be activated.
     *
     * <p>{@link BluetoothProfile#LE_AUDIO} and {@link BluetoothProfile#HEARING_AID} support
     * grouping devices. Devices that belong to the same group should have the same routeId but
     * different physical address.
     *
     *
     * @param routeId the id of the Bluetooth device. {@code null} denotes to clear the use of
     * <p>In case one of the devices from the group is selected then other devices should also
     *               BT routes.
     * reflect this by changing their connectivity status to
     * {@link MediaRoute2Info#CONNECTION_STATE_CONNECTED}.
     */
     */
    private void updateConnectivityStateForDevicesInTheSameGroup() {
        synchronized (this) {
            for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
                if (TextUtils.equals(btRoute.mRoute.getId(), mSelectedBluetoothRoute.mRoute.getId())
                        && !TextUtils.equals(btRoute.mBtDevice.getAddress(),
                        mSelectedBluetoothRoute.mBtDevice.getAddress())) {
                    setRouteConnectionState(btRoute, STATE_CONNECTED);
                }
            }
        }
    }

    @Override
    @Override
    public void transferTo(@Nullable String routeId) {
    public void transferTo(@Nullable String routeId) {
        if (routeId == null) {
        if (routeId == null) {
            clearActiveDevices();
            mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO);
            return;
            return;
        }
        }


@@ -169,38 +217,38 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
            return;
            return;
        }
        }


        if (mBluetoothAdapter != null) {
        mBluetoothAdapter.setActiveDevice(btRouteInfo.mBtDevice, ACTIVE_DEVICE_AUDIO);
        mBluetoothAdapter.setActiveDevice(btRouteInfo.mBtDevice, ACTIVE_DEVICE_AUDIO);
    }
    }
    }


    private BluetoothRouteInfo findBluetoothRouteWithRouteId(String routeId) {
    @Nullable
    private BluetoothRouteInfo findBluetoothRouteWithRouteId(@Nullable String routeId) {
        if (routeId == null) {
        if (routeId == null) {
            return null;
            return null;
        }
        }
        synchronized (this) {
            for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) {
            for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) {
                if (TextUtils.equals(btRouteInfo.mRoute.getId(), routeId)) {
                if (TextUtils.equals(btRouteInfo.mRoute.getId(), routeId)) {
                    return btRouteInfo;
                    return btRouteInfo;
                }
                }
            }
            }
        }
        return null;
        return null;
    }
    }


    /**
    private void updateBluetoothRoutes() {
     * Clears the active device for all known profiles.
        Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();
     */

    private void clearActiveDevices() {
        if (bondedDevices == null) {
        if (mBluetoothAdapter != null) {
            return;
            mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO);
        }
        }
        }


    private void buildBluetoothRoutes() {
        synchronized (this) {
            mBluetoothRoutes.clear();
            mBluetoothRoutes.clear();
        Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices();

        if (bondedDevices != null) {
            // We need to query all available to BT stack devices in order to avoid inconsistency
            // between external services, like, AndroidManager, and BT stack.
            for (BluetoothDevice device : bondedDevices) {
            for (BluetoothDevice device : bondedDevices) {
                if (device.isConnected()) {
                if (isDeviceConnected(device)) {
                    BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
                    BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
                    if (newBtRoute.mConnectedProfiles.size() > 0) {
                    if (newBtRoute.mConnectedProfiles.size() > 0) {
                        mBluetoothRoutes.put(device.getAddress(), newBtRoute);
                        mBluetoothRoutes.put(device.getAddress(), newBtRoute);
@@ -210,20 +258,31 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
        }
        }
    }
    }


    @VisibleForTesting
        /* package */ boolean isDeviceConnected(@NonNull BluetoothDevice device) {
        return device.isConnected();
    }

    @Nullable
    @Nullable
    @Override
    @Override
    public MediaRoute2Info getSelectedRoute() {
    public MediaRoute2Info getSelectedRoute() {
        // For now, active routes can be multiple only when a pair of hearing aid devices is active.
        synchronized (this) {
        // Let the first active device represent them.
            if (mSelectedBluetoothRoute == null) {
        return (mActiveRoutes.isEmpty() ? null : mActiveRoutes.get(0).mRoute);
                return null;
            }

            return mSelectedBluetoothRoute.mRoute;
        }
    }
    }


    @NonNull
    @NonNull
    @Override
    @Override
    public List<MediaRoute2Info> getTransferableRoutes() {
    public List<MediaRoute2Info> getTransferableRoutes() {
        List<MediaRoute2Info> routes = getAllBluetoothRoutes();
        List<MediaRoute2Info> routes = getAllBluetoothRoutes();
        for (BluetoothRouteInfo btRoute : mActiveRoutes) {
        synchronized (this) {
            routes.remove(btRoute.mRoute);
            if (mSelectedBluetoothRoute != null) {
                routes.remove(mSelectedBluetoothRoute.mRoute);
            }
        }
        }
        return routes;
        return routes;
    }
    }
@@ -240,6 +299,7 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
            routeIds.add(selectedRoute.getId());
            routeIds.add(selectedRoute.getId());
        }
        }


        synchronized (this) {
            for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
            for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
                // A pair of hearing aid devices or having the same hardware address
                // A pair of hearing aid devices or having the same hardware address
                if (routeIds.contains(btRoute.mRoute.getId())) {
                if (routeIds.contains(btRoute.mRoute.getId())) {
@@ -248,14 +308,10 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
                routes.add(btRoute.mRoute);
                routes.add(btRoute.mRoute);
                routeIds.add(btRoute.mRoute.getId());
                routeIds.add(btRoute.mRoute.getId());
            }
            }
        }
        return routes;
        return routes;
    }
    }


    /**
     * Updates the volume for {@link AudioManager#getDevicesForStream(int) devices}.
     *
     * @return true if devices can be handled by the provider.
     */
    @Override
    @Override
    public boolean updateVolumeForDevices(int devices, int volume) {
    public boolean updateVolumeForDevices(int devices, int volume) {
        int routeType;
        int routeType;
@@ -270,32 +326,31 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
        } else {
        } else {
            return false;
            return false;
        }
        }
        mVolumeMap.put(routeType, volume);


        boolean shouldNotify = false;
        synchronized (this) {
        for (BluetoothRouteInfo btRoute : mActiveRoutes) {
            mVolumeMap.put(routeType, volume);
            if (btRoute.mRoute.getType() != routeType) {
            if (mSelectedBluetoothRoute == null
                continue;
                    || mSelectedBluetoothRoute.mRoute.getType() != routeType) {
                return false;
            }
            }
            btRoute.mRoute = new MediaRoute2Info.Builder(btRoute.mRoute)

            mSelectedBluetoothRoute.mRoute =
                    new MediaRoute2Info.Builder(mSelectedBluetoothRoute.mRoute)
                            .setVolume(volume)
                            .setVolume(volume)
                            .build();
                            .build();
            shouldNotify = true;
        }
        }
        if (shouldNotify) {

        notifyBluetoothRoutesUpdated();
        notifyBluetoothRoutesUpdated();
        }
        return true;
        return true;
    }
    }


    private void notifyBluetoothRoutesUpdated() {
    private void notifyBluetoothRoutesUpdated() {
        if (mListener != null) {
        mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
        mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
    }
    }
    }


    private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
    private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
        BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
        BluetoothRouteInfo
                newBtRoute = new BluetoothRouteInfo();
        newBtRoute.mBtDevice = device;
        newBtRoute.mBtDevice = device;


        String routeId = device.getAddress();
        String routeId = device.getAddress();
@@ -305,20 +360,20 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
        }
        }
        int type = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
        int type = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
        newBtRoute.mConnectedProfiles = new SparseBooleanArray();
        newBtRoute.mConnectedProfiles = new SparseBooleanArray();
        if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) {
        if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.A2DP, device)) {
            newBtRoute.mConnectedProfiles.put(BluetoothProfile.A2DP, true);
            newBtRoute.mConnectedProfiles.put(BluetoothProfile.A2DP, true);
        }
        }
        if (mHearingAidProfile != null
        if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.HEARING_AID, device)) {
                && mHearingAidProfile.getConnectedDevices().contains(device)) {
            newBtRoute.mConnectedProfiles.put(BluetoothProfile.HEARING_AID, true);
            newBtRoute.mConnectedProfiles.put(BluetoothProfile.HEARING_AID, true);
            // Intentionally assign the same ID for a pair of devices to publish only one of them.
            // Intentionally assign the same ID for a pair of devices to publish only one of them.
            routeId = HEARING_AID_ROUTE_ID_PREFIX + mHearingAidProfile.getHiSyncId(device);
            routeId = HEARING_AID_ROUTE_ID_PREFIX
                    + mBluetoothProfileMonitor.getGroupId(BluetoothProfile.HEARING_AID, device);
            type = MediaRoute2Info.TYPE_HEARING_AID;
            type = MediaRoute2Info.TYPE_HEARING_AID;
        }
        }
        if (mLeAudioProfile != null
        if (mBluetoothProfileMonitor.isProfileSupported(BluetoothProfile.LE_AUDIO, device)) {
                && mLeAudioProfile.getConnectedDevices().contains(device)) {
            newBtRoute.mConnectedProfiles.put(BluetoothProfile.LE_AUDIO, true);
            newBtRoute.mConnectedProfiles.put(BluetoothProfile.LE_AUDIO, true);
            routeId = LE_AUDIO_ROUTE_ID_PREFIX + mLeAudioProfile.getGroupId(device);
            routeId = LE_AUDIO_ROUTE_ID_PREFIX
                    + mBluetoothProfileMonitor.getGroupId(BluetoothProfile.LE_AUDIO, device);
            type = MediaRoute2Info.TYPE_BLE_HEADSET;
            type = MediaRoute2Info.TYPE_BLE_HEADSET;
        }
        }


@@ -351,70 +406,17 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
                .setConnectionState(state);
                .setConnectionState(state);
        builder.setType(btRoute.getRouteType());
        builder.setType(btRoute.getRouteType());


        if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
            builder.setVolume(mVolumeMap.get(btRoute.getRouteType(), 0));
        }
        btRoute.mRoute = builder.build();
    }

    private void addActiveRoute(BluetoothRouteInfo btRoute) {
        if (btRoute == null) {
            Slog.w(TAG, "addActiveRoute: btRoute is null");
            return;
        }
        if (DEBUG) {
            Log.d(TAG, "Adding active route: " + btRoute.mRoute);
        }
        if (mActiveRoutes.contains(btRoute)) {
            Slog.w(TAG, "addActiveRoute: btRoute is already added.");
            return;
        }
        setRouteConnectionState(btRoute, STATE_CONNECTED);
        mActiveRoutes.add(btRoute);
    }


    private void removeActiveRoute(BluetoothRouteInfo btRoute) {
        if (DEBUG) {
            Log.d(TAG, "Removing active route: " + btRoute.mRoute);
        }
        if (mActiveRoutes.remove(btRoute)) {
            setRouteConnectionState(btRoute, STATE_DISCONNECTED);
        }
    }


    private void clearActiveRoutesWithType(int type) {
        if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
        if (DEBUG) {
            int currentVolume;
            Log.d(TAG, "Clearing active routes with type. type=" + type);
            synchronized (this) {
        }
                currentVolume = mVolumeMap.get(btRoute.getRouteType(), 0);
        Iterator<BluetoothRouteInfo> iter = mActiveRoutes.iterator();
        while (iter.hasNext()) {
            BluetoothRouteInfo btRoute = iter.next();
            if (btRoute.mRoute.getType() == type) {
                iter.remove();
                setRouteConnectionState(btRoute, STATE_DISCONNECTED);
            }
        }
            }
            }

            builder.setVolume(currentVolume);
    private void addActiveDevices(BluetoothDevice device) {
        // Let the given device be the first active device
        BluetoothRouteInfo activeBtRoute = mBluetoothRoutes.get(device.getAddress());
        // This could happen if ACTION_ACTIVE_DEVICE_CHANGED is sent before
        // ACTION_CONNECTION_STATE_CHANGED is sent.
        if (activeBtRoute == null) {
            activeBtRoute = createBluetoothRoute(device);
            mBluetoothRoutes.put(device.getAddress(), activeBtRoute);
        }
        }
        addActiveRoute(activeBtRoute);


        // A bluetooth route with the same route ID should be added.
        btRoute.mRoute = builder.build();
        for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
            if (TextUtils.equals(btRoute.mRoute.getId(), activeBtRoute.mRoute.getId())
                    && !TextUtils.equals(btRoute.mBtDevice.getAddress(),
                    activeBtRoute.mBtDevice.getAddress())) {
                addActiveRoute(btRoute);
            }
        }
    }
    }


    private static class BluetoothRouteInfo {
    private static class BluetoothRouteInfo {
@@ -437,71 +439,25 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
        }
        }
    }
    }


    // These callbacks run on the main thread.
    private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener {
        @Override
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            List<BluetoothDevice> activeDevices;
            switch (profile) {
                case BluetoothProfile.A2DP:
                    mA2dpProfile = (BluetoothA2dp) proxy;
                    // It may contain null.
                    activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.A2DP);
                    break;
                case BluetoothProfile.HEARING_AID:
                    mHearingAidProfile = (BluetoothHearingAid) proxy;
                    activeDevices = mBluetoothAdapter.getActiveDevices(
                            BluetoothProfile.HEARING_AID);
                    break;
                case BluetoothProfile.LE_AUDIO:
                    mLeAudioProfile = (BluetoothLeAudio) proxy;
                    activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
                    break;
                default:
                    return;
            }
            for (BluetoothDevice device : proxy.getConnectedDevices()) {
                BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
                if (btRoute == null) {
                    btRoute = createBluetoothRoute(device);
                    mBluetoothRoutes.put(device.getAddress(), btRoute);
                }
                if (activeDevices.contains(device)) {
                    addActiveRoute(btRoute);
                }
            }
            notifyBluetoothRoutesUpdated();
        }

        @Override
        public void onServiceDisconnected(int profile) {
            switch (profile) {
                case BluetoothProfile.A2DP:
                    mA2dpProfile = null;
                    break;
                case BluetoothProfile.HEARING_AID:
                    mHearingAidProfile = null;
                    break;
                case BluetoothProfile.LE_AUDIO:
                    mLeAudioProfile = null;
                    break;
                default:
                    return;
            }
        }
    }

    private class AdapterStateChangedReceiver extends BroadcastReceiver {
    private class AdapterStateChangedReceiver extends BroadcastReceiver {
        @Override
        @Override
        public void onReceive(Context context, Intent intent) {
        public void onReceive(Context context, Intent intent) {
            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
            if (state == BluetoothAdapter.STATE_OFF
            if (state == BluetoothAdapter.STATE_OFF
                    || state == BluetoothAdapter.STATE_TURNING_OFF) {
                    || state == BluetoothAdapter.STATE_TURNING_OFF) {
                synchronized (AudioPoliciesBluetoothRouteController.this) {
                    mBluetoothRoutes.clear();
                    mBluetoothRoutes.clear();
                }
                notifyBluetoothRoutesUpdated();
                notifyBluetoothRoutesUpdated();
            } else if (state == BluetoothAdapter.STATE_ON) {
            } else if (state == BluetoothAdapter.STATE_ON) {
                buildBluetoothRoutes();
                updateBluetoothRoutes();
                if (!mBluetoothRoutes.isEmpty()) {

                boolean shouldCallListener;
                synchronized (AudioPoliciesBluetoothRouteController.this) {
                    shouldCallListener = !mBluetoothRoutes.isEmpty();
                }

                if (shouldCallListener) {
                    notifyBluetoothRoutesUpdated();
                    notifyBluetoothRoutesUpdated();
                }
                }
            }
            }
@@ -511,75 +467,16 @@ class AudioPoliciesBluetoothRouteController implements BluetoothRouteController
    private class DeviceStateChangedReceiver extends BroadcastReceiver {
    private class DeviceStateChangedReceiver extends BroadcastReceiver {
        @Override
        @Override
        public void onReceive(Context context, Intent intent) {
        public void onReceive(Context context, Intent intent) {
            BluetoothDevice device = intent.getParcelableExtra(
                    BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class);

            switch (intent.getAction()) {
            switch (intent.getAction()) {
                case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
                case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
                    clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP);
                    if (device != null) {
                        addActiveRoute(mBluetoothRoutes.get(device.getAddress()));
                    }
                    notifyBluetoothRoutesUpdated();
                    break;
                case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
                case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
                    clearActiveRoutesWithType(MediaRoute2Info.TYPE_HEARING_AID);
                    if (device != null) {
                        if (DEBUG) {
                            Log.d(TAG, "Setting active hearing aid devices. device=" + device);
                        }

                        addActiveDevices(device);
                    }
                    notifyBluetoothRoutesUpdated();
                    break;
                case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
                case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED:
                    clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLE_HEADSET);
                    if (device != null) {
                        if (DEBUG) {
                            Log.d(TAG, "Setting active le audio devices. device=" + device);
                        }

                        addActiveDevices(device);
                    }
                    notifyBluetoothRoutesUpdated();
                    break;
                case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
                case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
                    handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device);
                    break;
                case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
                case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
                    handleConnectionStateChanged(BluetoothProfile.HEARING_AID, intent, device);
                    break;
                case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
                case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED:
                    handleConnectionStateChanged(BluetoothProfile.LE_AUDIO, intent, device);
                    updateBluetoothRoutes();
                    break;
            }
        }

        private void handleConnectionStateChanged(int profile, Intent intent,
                BluetoothDevice device) {
            int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
            BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
            if (state == BluetoothProfile.STATE_CONNECTED) {
                if (btRoute == null) {
                    btRoute = createBluetoothRoute(device);
                    if (btRoute.mConnectedProfiles.size() > 0) {
                        mBluetoothRoutes.put(device.getAddress(), btRoute);
                    notifyBluetoothRoutesUpdated();
                    notifyBluetoothRoutesUpdated();
            }
            }
                } else {
                    btRoute.mConnectedProfiles.put(profile, true);
                }
            } else if (state == BluetoothProfile.STATE_DISCONNECTING
                    || state == BluetoothProfile.STATE_DISCONNECTED) {
                if (btRoute != null) {
                    btRoute.mConnectedProfiles.delete(profile);
                    if (btRoute.mConnectedProfiles.size() == 0) {
                        removeActiveRoute(mBluetoothRoutes.remove(device.getAddress()));
                        notifyBluetoothRoutesUpdated();
                    }
                }
            }
        }
        }
    }
    }
}
}
+178 −0

File added.

Preview size limit exceeded, changes collapsed.

+293 −0

File added.

Preview size limit exceeded, changes collapsed.