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

Commit 141de3b2 authored by Hall Liu's avatar Hall Liu Committed by android-build-merger
Browse files

Merge changes from topic "bt-hearing-aid"

am: 67203a82

Change-Id: I2b7349db6676a9e3b2bf66ef80e93f6586a867b4
parents 8cb9aec2 67203a82
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -308,6 +308,12 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    mHasUserExplicitlyLeftBluetooth = false;
                    return NOT_HANDLED;
                case SWITCH_FOCUS:
                    // Perform BT hearing aid active device caching/restoration
                    if (mAudioFocusType != NO_FOCUS && msg.arg1 == NO_FOCUS) {
                        mBluetoothRouteManager.restoreHearingAidDevice();
                    } else if (mAudioFocusType == NO_FOCUS && msg.arg1 != NO_FOCUS) {
                        mBluetoothRouteManager.cacheHearingAidDevice();
                    }
                    mAudioFocusType = msg.arg1;
                    return NOT_HANDLED;
                default:
@@ -806,7 +812,8 @@ public class CallAudioRouteStateMachine extends StateMachine {
                    return HANDLED;
                case SWITCH_FOCUS:
                    if (msg.arg1 == NO_FOCUS) {
                        setBluetoothOff();
                        // Only disconnect SCO audio here instead of routing away from BT entirely.
                        mBluetoothRouteManager.disconnectSco();
                        reinitialize();
                    } else if (msg.arg1 == RINGING_FOCUS
                            && !mBluetoothRouteManager.isInbandRingingEnabled()) {
@@ -1268,7 +1275,7 @@ public class CallAudioRouteStateMachine extends StateMachine {
     */
    private int mDeviceSupportedRoutes;
    private int mAvailableRoutes;
    private int mAudioFocusType;
    private int mAudioFocusType = NO_FOCUS;
    private boolean mWasOnSpeaker;
    private boolean mIsMuted;

+159 −35
Original line number Diff line number Diff line
@@ -18,27 +18,22 @@ package com.android.server.telecom.bluetooth;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.telecom.Log;

import com.android.server.telecom.BluetoothAdapterProxy;
import com.android.server.telecom.BluetoothHeadsetProxy;
import com.android.server.telecom.TelecomSystem;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.Set;

public class BluetoothDeviceManager {
    private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
@@ -52,8 +47,12 @@ public class BluetoothDeviceManager {
                                mBluetoothHeadsetService =
                                        new BluetoothHeadsetProxy((BluetoothHeadset) proxy);
                                Log.i(this, "- Got BluetoothHeadset: " + mBluetoothHeadsetService);
                            } else if (profile == BluetoothProfile.HEARING_AID) {
                                mBluetoothHearingAidService = (BluetoothHearingAid) proxy;
                                Log.i(this, "- Got BluetoothHearingAid: "
                                        + mBluetoothHearingAidService);
                            } else {
                                Log.w(this, "Connected to non-headset bluetooth service." +
                                Log.w(this, "Connected to non-requested bluetooth service." +
                                        " Not changing bluetooth headset.");
                            }
                        }
@@ -67,12 +66,25 @@ public class BluetoothDeviceManager {
                    Log.startSession("BMSL.oSD");
                    try {
                        synchronized (mLock) {
                            LinkedHashMap<String, BluetoothDevice> lostServiceDevices;
                            if (profile == BluetoothProfile.HEADSET) {
                                mBluetoothHeadsetService = null;
                            Log.i(BluetoothDeviceManager.this, "Lost BluetoothHeadset service. " +
                                Log.i(BluetoothDeviceManager.this,
                                        "Lost BluetoothHeadset service. " +
                                                "Removing all tracked devices.");
                                lostServiceDevices = mHfpDevicesByAddress;
                            } else if (profile == BluetoothProfile.HEARING_AID) {
                                mBluetoothHearingAidService = null;
                                Log.i(BluetoothDeviceManager.this,
                                        "Lost BluetoothHearingAid service. " +
                                                "Removing all tracked devices.");
                                lostServiceDevices = mHearingAidDevicesByAddress;
                            } else {
                                return;
                            }
                            List<BluetoothDevice> devicesToRemove = new LinkedList<>(
                                    mConnectedDevicesByAddress.values());
                            mConnectedDevicesByAddress.clear();
                                    lostServiceDevices.values());
                            lostServiceDevices.clear();
                            for (BluetoothDevice device : devicesToRemove) {
                                mBluetoothRouteManager.onDeviceLost(device.getAddress());
                            }
@@ -83,7 +95,11 @@ public class BluetoothDeviceManager {
                }
           };

    private final LinkedHashMap<String, BluetoothDevice> mConnectedDevicesByAddress =
    private final LinkedHashMap<String, BluetoothDevice> mHfpDevicesByAddress =
            new LinkedHashMap<>();
    private final LinkedHashMap<String, BluetoothDevice> mHearingAidDevicesByAddress =
            new LinkedHashMap<>();
    private final LinkedHashMap<BluetoothDevice, Long> mHearingAidDeviceSyncIds =
            new LinkedHashMap<>();

    // This lock only protects internal state -- it doesn't lock on anything going into Telecom.
@@ -91,12 +107,15 @@ public class BluetoothDeviceManager {

    private BluetoothRouteManager mBluetoothRouteManager;
    private BluetoothHeadsetProxy mBluetoothHeadsetService;
    private BluetoothHearingAid mBluetoothHearingAidService;
    private BluetoothDevice mBluetoothHearingAidActiveDeviceCache;

    public BluetoothDeviceManager(Context context, BluetoothAdapterProxy bluetoothAdapter) {

        if (bluetoothAdapter != null) {
            bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
                    BluetoothProfile.HEADSET);
            bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener,
                    BluetoothProfile.HEARING_AID);
        }
    }

@@ -106,58 +125,163 @@ public class BluetoothDeviceManager {

    public int getNumConnectedDevices() {
        synchronized (mLock) {
            return mConnectedDevicesByAddress.size();
            return mHfpDevicesByAddress.size() + mHearingAidDevicesByAddress.size();
        }
    }

    public Collection<BluetoothDevice> getConnectedDevices() {
        synchronized (mLock) {
            return Collections.unmodifiableCollection(
                    new ArrayList<>(mConnectedDevicesByAddress.values()));
            ArrayList<BluetoothDevice> result = new ArrayList<>(mHfpDevicesByAddress.values());
            result.addAll(mHearingAidDevicesByAddress.values());
            return Collections.unmodifiableCollection(result);
        }
    }

    public String getMostRecentlyConnectedDevice(String excludeAddress) {
        String result = null;
    // Same as getConnectedDevices except it filters out the hearing aid devices that are linked
    // together by their hiSyncId.
    public Collection<BluetoothDevice> getUniqueConnectedDevices() {
        ArrayList<BluetoothDevice> result = new ArrayList<>(mHfpDevicesByAddress.values());
        Set<Long> seenHiSyncIds = new LinkedHashSet<>();
        // Add the left-most active device to the seen list so that we match up with the list
        // generated in BluetoothRouteManager.
        if (mBluetoothHearingAidService != null) {
            for (BluetoothDevice device : mBluetoothHearingAidService.getActiveDevices()) {
                if (device != null) {
                    result.add(device);
                    seenHiSyncIds.add(mHearingAidDeviceSyncIds.getOrDefault(device, -1L));
                    break;
                }
            }
        }
        synchronized (mLock) {
            for (String addr : mConnectedDevicesByAddress.keySet()) {
                if (!Objects.equals(addr, excludeAddress)) {
                    result = addr;
            for (BluetoothDevice d : mHearingAidDevicesByAddress.values()) {
                long hiSyncId = mHearingAidDeviceSyncIds.getOrDefault(d, -1L);
                if (seenHiSyncIds.contains(hiSyncId)) {
                    continue;
                }
                result.add(d);
                seenHiSyncIds.add(hiSyncId);
            }
        }
        return result;
        return Collections.unmodifiableCollection(result);
    }

    public BluetoothHeadsetProxy getHeadsetService() {
        return mBluetoothHeadsetService;
    }

    public BluetoothHearingAid getHearingAidService() {
        return mBluetoothHearingAidService;
    }

    public void setHeadsetServiceForTesting(BluetoothHeadsetProxy bluetoothHeadset) {
        mBluetoothHeadsetService = bluetoothHeadset;
    }

    public BluetoothDevice getDeviceFromAddress(String address) {
        synchronized (mLock) {
            return mConnectedDevicesByAddress.get(address);
        }
    public void setHearingAidServiceForTesting(BluetoothHearingAid bluetoothHearingAid) {
        mBluetoothHearingAidService = bluetoothHearingAid;
    }

    void onDeviceConnected(BluetoothDevice device) {
    void onDeviceConnected(BluetoothDevice device, boolean isHearingAid) {
        synchronized (mLock) {
            if (!mConnectedDevicesByAddress.containsKey(device.getAddress())) {
                mConnectedDevicesByAddress.put(device.getAddress(), device);
            LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
            if (isHearingAid) {
                long hiSyncId = mBluetoothHearingAidService.getHiSyncId(device);
                mHearingAidDeviceSyncIds.put(device, hiSyncId);
                targetDeviceMap = mHearingAidDevicesByAddress;
            } else {
                targetDeviceMap = mHfpDevicesByAddress;
            }
            if (!targetDeviceMap.containsKey(device.getAddress())) {
                targetDeviceMap.put(device.getAddress(), device);
                mBluetoothRouteManager.onDeviceAdded(device.getAddress());
            }
        }
    }

    void onDeviceDisconnected(BluetoothDevice device) {
    void onDeviceDisconnected(BluetoothDevice device, boolean isHearingAid) {
        synchronized (mLock) {
            if (mConnectedDevicesByAddress.containsKey(device.getAddress())) {
                mConnectedDevicesByAddress.remove(device.getAddress());
            LinkedHashMap<String, BluetoothDevice> targetDeviceMap;
            if (isHearingAid) {
                mHearingAidDeviceSyncIds.remove(device);
                targetDeviceMap = mHearingAidDevicesByAddress;
            } else {
                targetDeviceMap = mHfpDevicesByAddress;
            }
            if (targetDeviceMap.containsKey(device.getAddress())) {
                targetDeviceMap.remove(device.getAddress());
                mBluetoothRouteManager.onDeviceLost(device.getAddress());
            }
        }
    }

    public void disconnectAudio() {
        if (mBluetoothHearingAidService == null) {
            Log.w(this, "Trying to disconnect audio but no hearing aid service exists");
        } else {
            for (BluetoothDevice device : mBluetoothHearingAidService.getActiveDevices()) {
                if (device != null) {
                    mBluetoothHearingAidService.setActiveDevice(null);
                }
            }
        }
        disconnectSco();
    }

    public void disconnectSco() {
        if (mBluetoothHeadsetService == null) {
            Log.w(this, "Trying to disconnect audio but no headset service exists.");
        } else {
            mBluetoothHeadsetService.disconnectAudio();
        }
    }

    // Connect audio to the bluetooth device at address, checking to see whether it's a hearing aid
    // or a HFP device, and using the proper BT API.
    public boolean connectAudio(String address) {
        if (mHearingAidDevicesByAddress.containsKey(address)) {
            if (mBluetoothHearingAidService == null) {
                Log.w(this, "Attempting to turn on audio when the hearing aid service is null");
                return false;
            }
            return mBluetoothHearingAidService.setActiveDevice(
                    mHearingAidDevicesByAddress.get(address));
        } else if (mHfpDevicesByAddress.containsKey(address)) {
            BluetoothDevice device = mHfpDevicesByAddress.get(address);
            if (mBluetoothHeadsetService == null) {
                Log.w(this, "Attempting to turn on audio when the headset service is null");
                return false;
            }
            boolean success = mBluetoothHeadsetService.setActiveDevice(device);
            if (!success) {
                Log.w(this, "Couldn't set active device to %s", address);
                return false;
            }
            if (!mBluetoothHeadsetService.isAudioOn()) {
                return mBluetoothHeadsetService.connectAudio();
            }
            return true;
        } else {
            Log.w(this, "Attempting to turn on audio for a disconnected device");
            return false;
        }
    }

    public void cacheHearingAidDevice() {
        if (mBluetoothHearingAidService != null) {
             for (BluetoothDevice device : mBluetoothHearingAidService.getActiveDevices()) {
                 if (device != null) {
                     mBluetoothHearingAidActiveDeviceCache = device;
                 }
             }
        }
    }

    public void restoreHearingAidDevice() {
        if (mBluetoothHearingAidActiveDeviceCache != null && mBluetoothHearingAidService != null) {
            mBluetoothHearingAidService.setActiveDevice(mBluetoothHearingAidActiveDeviceCache);
            mBluetoothHearingAidActiveDeviceCache = null;
        }
    }

}
+97 −88

File changed.

Preview size limit exceeded, changes collapsed.

+34 −11
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.server.telecom.bluetooth;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -27,8 +29,8 @@ 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;
import static com.android.server.telecom.bluetooth.BluetoothRouteManager.BT_AUDIO_IS_ON;
import static com.android.server.telecom.bluetooth.BluetoothRouteManager.BT_AUDIO_LOST;


public class BluetoothStateReceiver extends BroadcastReceiver {
@@ -39,6 +41,8 @@ public class BluetoothStateReceiver extends BroadcastReceiver {
        INTENT_FILTER.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
        INTENT_FILTER.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
        INTENT_FILTER.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
        INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
        INTENT_FILTER.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
    }

    // If not in a call, BSR won't listen to the Bluetooth stack's HFP on/off messages, since
@@ -55,9 +59,11 @@ public class BluetoothStateReceiver extends BroadcastReceiver {
                case BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED:
                    handleAudioStateChanged(intent);
                    break;
                case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
                case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED:
                    handleConnectionStateChanged(intent);
                    break;
                case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
                case BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED:
                    handleActiveDeviceChanged(intent);
                    break;
@@ -91,10 +97,10 @@ public class BluetoothStateReceiver extends BroadcastReceiver {
        args.arg2 = device.getAddress();
        switch (bluetoothHeadsetAudioState) {
            case BluetoothHeadset.STATE_AUDIO_CONNECTED:
                mBluetoothRouteManager.sendMessage(HFP_IS_ON, args);
                mBluetoothRouteManager.sendMessage(BT_AUDIO_IS_ON, args);
                break;
            case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
                mBluetoothRouteManager.sendMessage(HFP_LOST, args);
                mBluetoothRouteManager.sendMessage(BT_AUDIO_LOST, args);
                break;
        }
    }
@@ -114,19 +120,36 @@ public class BluetoothStateReceiver extends BroadcastReceiver {
        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);
        boolean isHearingAid = BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED
                .equals(intent.getAction());
        if (bluetoothHeadsetState == BluetoothProfile.STATE_CONNECTED) {
            mBluetoothDeviceManager.onDeviceConnected(device, isHearingAid);
        } else if (bluetoothHeadsetState == BluetoothProfile.STATE_DISCONNECTED
                || bluetoothHeadsetState == BluetoothProfile.STATE_DISCONNECTING) {
            mBluetoothDeviceManager.onDeviceDisconnected(device, isHearingAid);
        }
    }

    private void handleActiveDeviceChanged(Intent intent) {
        BluetoothDevice device =
                intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        Log.i(LOG_TAG, "Device %s is now the preferred HFP device", device);
        mBluetoothRouteManager.onActiveDeviceChanged(device);
        boolean isHearingAid =
                BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED.equals(intent.getAction());
        Log.i(LOG_TAG, "Device %s is now the preferred BT device for %s", device,
                isHearingAid ? "heading aid" : "HFP");

        mBluetoothRouteManager.onActiveDeviceChanged(device, isHearingAid);
        if (isHearingAid) {
            Session session = Log.createSubsession();
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = session;
            if (device == null) {
                mBluetoothRouteManager.sendMessage(BT_AUDIO_LOST, args);
            } else {
                args.arg2 = device.getAddress();
                mBluetoothRouteManager.sendMessage(BT_AUDIO_IS_ON, args);
            }
        }
    }

    public BluetoothStateReceiver(BluetoothDeviceManager deviceManager,
+73 −37

File changed.

Preview size limit exceeded, changes collapsed.

Loading