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

Commit 04a4601a authored by Hugh Chen's avatar Hugh Chen Committed by Android (Google) Code Review
Browse files

Merge "Make HearingAid code more generic"

parents 524dc711 5c62ebba
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -171,7 +171,6 @@ public class BluetoothEventManager {
                callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
            }
        }
        mDeviceManager.onProfileConnectionStateChanged(device, state, bluetoothProfile);
    }

    private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
+33 −6
Original line number Diff line number Diff line
@@ -29,6 +29,8 @@ import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.VisibleForTesting;

import com.android.settingslib.R;

import java.util.ArrayList;
@@ -36,8 +38,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;

import androidx.annotation.VisibleForTesting;

/**
 * CachedBluetoothDevice represents a remote Bluetooth device. It contains
 * attributes of the device (such as the address, name, RSSI, etc.) and
@@ -54,11 +54,12 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
    private final Context mContext;
    private final BluetoothAdapter mLocalAdapter;
    private final LocalBluetoothProfileManager mProfileManager;
    private final BluetoothDevice mDevice;
    BluetoothDevice mDevice;
    private long mHiSyncId;
    // Need this since there is no method for getting RSSI
    private short mRssi;

    short mRssi;
    // mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
    // because current sub device is only for HearingAid and its profile is the same.
    private final List<LocalBluetoothProfile> mProfiles =
            Collections.synchronizedList(new ArrayList<>());

@@ -69,7 +70,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
    // Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
    private boolean mLocalNapRoleConnected;

    private boolean mJustDiscovered;
    boolean mJustDiscovered;

    private int mMessageRejectionCount;

@@ -92,6 +93,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
    private boolean mIsActiveDeviceA2dp = false;
    private boolean mIsActiveDeviceHeadset = false;
    private boolean mIsActiveDeviceHearingAid = false;
    // Group second device for Hearing Aid
    private CachedBluetoothDevice mSubDevice;

    CachedBluetoothDevice(Context context, LocalBluetoothProfileManager profileManager,
            BluetoothDevice device) {
@@ -1064,4 +1067,28 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
        return hearingAidProfile != null && hearingAidProfile.getConnectionStatus(mDevice) ==
                BluetoothProfile.STATE_CONNECTED;
    }

    public CachedBluetoothDevice getSubDevice() {
        return mSubDevice;
    }

    public void setSubDevice(CachedBluetoothDevice subDevice) {
        mSubDevice = subDevice;
    }

    public void switchSubDeviceContent() {
        // Backup from main device
        BluetoothDevice tmpDevice = mDevice;
        short tmpRssi = mRssi;
        boolean tmpJustDiscovered = mJustDiscovered;
        // Set main device from sub device
        mDevice = mSubDevice.mDevice;
        mRssi = mSubDevice.mRssi;
        mJustDiscovered = mSubDevice.mJustDiscovered;
        // Set sub device from backup
        mSubDevice.mDevice = tmpDevice;
        mSubDevice.mRssi = tmpRssi;
        mSubDevice.mJustDiscovered = tmpJustDiscovered;
        fetchActiveDevices();
    }
}
+56 −243
Original line number Diff line number Diff line
@@ -18,8 +18,6 @@ package com.android.settingslib.bluetooth;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;

@@ -27,12 +25,8 @@ import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices.
@@ -45,20 +39,14 @@ public class CachedBluetoothDeviceManager {
    private final LocalBluetoothManager mBtManager;

    @VisibleForTesting
    final List<CachedBluetoothDevice> mCachedDevices =
        new ArrayList<CachedBluetoothDevice>();
    // Contains the list of hearing aid devices that should not be shown in the UI.
    final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
    @VisibleForTesting
    final List<CachedBluetoothDevice> mHearingAidDevicesNotAddedInCache
        = new ArrayList<CachedBluetoothDevice>();
    // Maintains a list of devices which are added in mCachedDevices and have hiSyncIds.
    @VisibleForTesting
    final Map<Long, CachedBluetoothDevice> mCachedDevicesMapForHearingAids
        = new HashMap<Long, CachedBluetoothDevice>();
    HearingAidDeviceManager mHearingAidDeviceManager;

    CachedBluetoothDeviceManager(Context context, LocalBluetoothManager localBtManager) {
        mContext = context;
        mBtManager = localBtManager;
        mHearingAidDeviceManager = new HearingAidDeviceManager(localBtManager, mCachedDevices);
    }

    public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() {
@@ -92,12 +80,13 @@ public class CachedBluetoothDeviceManager {
            if (cachedDevice.getDevice().equals(device)) {
                return cachedDevice;
            }
        }
        for (CachedBluetoothDevice notCachedDevice : mHearingAidDevicesNotAddedInCache) {
            if (notCachedDevice.getDevice().equals(device)) {
                return notCachedDevice;
            // Check sub devices if it exists
            CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
            if (subDevice != null && subDevice.getDevice().equals(device)) {
                return subDevice;
            }
        }

        return null;
    }

@@ -111,24 +100,10 @@ public class CachedBluetoothDeviceManager {
        LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
        CachedBluetoothDevice newDevice = new CachedBluetoothDevice(mContext, profileManager,
                device);
        if (profileManager.getHearingAidProfile() != null
            && profileManager.getHearingAidProfile().getHiSyncId(newDevice.getDevice())
                != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
            newDevice.setHiSyncId(profileManager.getHearingAidProfile()
                .getHiSyncId(newDevice.getDevice()));
        }
        // Just add one of the hearing aids from a pair in the list that is shown in the UI.
        if (isPairAddedInCache(newDevice.getHiSyncId())) {
            synchronized (this) {
                mHearingAidDevicesNotAddedInCache.add(newDevice);
            }
        } else {
        mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice);
        synchronized (this) {
            if (!mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) {
                mCachedDevices.add(newDevice);
                if (newDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
                    && !mCachedDevicesMapForHearingAids.containsKey(newDevice.getHiSyncId())) {
                    mCachedDevicesMapForHearingAids.put(newDevice.getHiSyncId(), newDevice);
                }
                mBtManager.getEventManager().dispatchDeviceAdded(newDevice);
            }
        }
@@ -136,50 +111,19 @@ public class CachedBluetoothDeviceManager {
        return newDevice;
    }

    /**
     * Returns true if the one of the two hearing aid devices is already cached for UI.
     *
     * @param long hiSyncId
     * @return {@code True} if one of the two hearing aid devices is is already cached for UI.
     */
    private synchronized boolean isPairAddedInCache(long hiSyncId) {
        if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
            return false;
        }
        if(mCachedDevicesMapForHearingAids.containsKey(hiSyncId)) {
            return true;
        }
        return false;
    }

    /**
     * Returns device summary of the pair of the hearing aid passed as the parameter.
     *
     * @param CachedBluetoothDevice device
     * @return Device summary, or if the pair does not exist or if its not a hearing aid,
     * @return Device summary, or if the pair does not exist or if it is not a hearing aid,
     * then {@code null}.
     */
    public synchronized String getHearingAidPairDeviceSummary(CachedBluetoothDevice device) {
        String pairDeviceSummary = null;
        if (device.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
            for (CachedBluetoothDevice hearingAidDevice : mHearingAidDevicesNotAddedInCache) {
                if (hearingAidDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
                    && hearingAidDevice.getHiSyncId() == device.getHiSyncId()) {
                    pairDeviceSummary = hearingAidDevice.getConnectionSummary();
                }
            }
    public synchronized String getSubDeviceSummary(CachedBluetoothDevice device) {
        CachedBluetoothDevice subDevice = device.getSubDevice();
        if (subDevice != null && subDevice.isConnected()) {
            return subDevice.getConnectionSummary();
        }
        return pairDeviceSummary;
    }

    /**
     * Adds the 2nd hearing aid in a pair in a list that maintains the hearing aids that are
     * not dispalyed in the UI.
     *
     * @param CachedBluetoothDevice device
     */
    public synchronized void addDeviceNotaddedInMap(CachedBluetoothDevice device) {
        mHearingAidDevicesNotAddedInCache.add(device);
        return null;
    }

    /**
@@ -187,28 +131,8 @@ public class CachedBluetoothDeviceManager {
     * Hearing Aid Service is connected and the HiSyncId's are now available.
     * @param LocalBluetoothProfileManager profileManager
     */
    public synchronized void updateHearingAidsDevices(LocalBluetoothProfileManager profileManager) {
        HearingAidProfile profileProxy = profileManager.getHearingAidProfile();
        if (profileProxy == null) {
            log("updateHearingAidsDevices: getHearingAidProfile() is null");
            return;
        }
        final Set<Long> syncIdChangedSet = new HashSet<Long>();
        for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
            if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
                continue;
            }

            long newHiSyncId = profileProxy.getHiSyncId(cachedDevice.getDevice());

            if (newHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
                cachedDevice.setHiSyncId(newHiSyncId);
                syncIdChangedSet.add(newHiSyncId);
            }
        }
        for (Long syncId : syncIdChangedSet) {
            onHiSyncIdChanged(syncId);
        }
    public synchronized void updateHearingAidsDevices() {
        mHearingAidDeviceManager.updateHearingAidsDevices();
    }

    /**
@@ -232,15 +156,21 @@ public class CachedBluetoothDeviceManager {
    }

    public synchronized void clearNonBondedDevices() {

        mCachedDevicesMapForHearingAids.entrySet().removeIf(entries
            -> entries.getValue().getBondState() == BluetoothDevice.BOND_NONE);

        clearNonBondedSubDevices();
        mCachedDevices.removeIf(cachedDevice
            -> cachedDevice.getBondState() == BluetoothDevice.BOND_NONE);
    }

        mHearingAidDevicesNotAddedInCache.removeIf(hearingAidDevice
            -> hearingAidDevice.getBondState() == BluetoothDevice.BOND_NONE);
    private void clearNonBondedSubDevices() {
        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
            CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
            CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
            if (subDevice != null
                    && subDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) {
                // Sub device exists and it is not bonded
                cachedDevice.setSubDevice(null);
            }
        }
    }

    public synchronized void onScanningStateChanged(boolean started) {
@@ -250,10 +180,10 @@ public class CachedBluetoothDeviceManager {
        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
            CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
            cachedDevice.setJustDiscovered(false);
            final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
            if (subDevice != null) {
                subDevice.setJustDiscovered(false);
            }
        for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) {
            CachedBluetoothDevice notCachedDevice = mHearingAidDevicesNotAddedInCache.get(i);
            notCachedDevice.setJustDiscovered(false);
        }
    }

@@ -277,21 +207,15 @@ public class CachedBluetoothDeviceManager {
        if (bluetoothState == BluetoothAdapter.STATE_TURNING_OFF) {
            for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
                CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
                CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
                if (subDevice != null) {
                    if (subDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
                        cachedDevice.setSubDevice(null);
                    }
                }
                if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
                    cachedDevice.setJustDiscovered(false);
                    mCachedDevices.remove(i);
                    if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
                        && mCachedDevicesMapForHearingAids.containsKey(cachedDevice.getHiSyncId()))
                    {
                        mCachedDevicesMapForHearingAids.remove(cachedDevice.getHiSyncId());
                    }
                }
            }
            for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) {
                CachedBluetoothDevice notCachedDevice = mHearingAidDevicesNotAddedInCache.get(i);
                if (notCachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) {
                    notCachedDevice.setJustDiscovered(false);
                    mHearingAidDevicesNotAddedInCache.remove(i);
                }
            }
        }
@@ -305,134 +229,23 @@ public class CachedBluetoothDeviceManager {
        }
    }

    public synchronized void onHiSyncIdChanged(long hiSyncId) {
        int firstMatchedIndex = -1;

        for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
            CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
            if (cachedDevice.getHiSyncId() == hiSyncId) {
                if (firstMatchedIndex != -1) {
                    /* Found the second one */
                    int indexToRemoveFromUi;
                    CachedBluetoothDevice deviceToRemoveFromUi;

                    // Since the hiSyncIds have been updated for a connected pair of hearing aids,
                    // we remove the entry of one the hearing aids from the UI. Unless the
                    // hiSyncId get updated, the system does not know it is a hearing aid, so we add
                    // both the hearing aids as separate entries in the UI first, then remove one
                    // of them after the hiSyncId is populated. We will choose the device that
                    // is not connected to be removed.
                    if (cachedDevice.isConnected()) {
                        indexToRemoveFromUi = firstMatchedIndex;
                        deviceToRemoveFromUi = mCachedDevices.get(firstMatchedIndex);
                        mCachedDevicesMapForHearingAids.put(hiSyncId, cachedDevice);
                    } else {
                        indexToRemoveFromUi = i;
                        deviceToRemoveFromUi = cachedDevice;
                        mCachedDevicesMapForHearingAids.put(hiSyncId,
                                                            mCachedDevices.get(firstMatchedIndex));
                    }

                    mCachedDevices.remove(indexToRemoveFromUi);
                    mHearingAidDevicesNotAddedInCache.add(deviceToRemoveFromUi);
                    log("onHiSyncIdChanged: removed from UI device=" + deviceToRemoveFromUi
                        + ", with hiSyncId=" + hiSyncId);
                    mBtManager.getEventManager().dispatchDeviceRemoved(deviceToRemoveFromUi);
                    break;
                } else {
                    mCachedDevicesMapForHearingAids.put(hiSyncId, cachedDevice);
                    firstMatchedIndex = i;
                }
            }
        }
    }

    private CachedBluetoothDevice getHearingAidOtherDevice(CachedBluetoothDevice thisDevice,
                                                           long hiSyncId) {
        if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
            return null;
        }

        // Searched the lists for the other side device with the matching hiSyncId.
        for (CachedBluetoothDevice notCachedDevice : mHearingAidDevicesNotAddedInCache) {
            if ((hiSyncId == notCachedDevice.getHiSyncId()) &&
                (!Objects.equals(notCachedDevice, thisDevice))) {
                return notCachedDevice;
            }
        }

        CachedBluetoothDevice cachedDevice = mCachedDevicesMapForHearingAids.get(hiSyncId);
        if (!Objects.equals(cachedDevice, thisDevice)) {
            return cachedDevice;
        }
        return null;
    }

    private void hearingAidSwitchDisplayDevice(CachedBluetoothDevice toDisplayDevice,
                                           CachedBluetoothDevice toHideDevice, long hiSyncId)
    {
        log("hearingAidSwitchDisplayDevice: toDisplayDevice=" + toDisplayDevice
            + ", toHideDevice=" + toHideDevice);

        // Remove the "toHideDevice" device from the UI.
        mHearingAidDevicesNotAddedInCache.add(toHideDevice);
        mCachedDevices.remove(toHideDevice);
        mBtManager.getEventManager().dispatchDeviceRemoved(toHideDevice);

        // Add the "toDisplayDevice" device to the UI.
        mHearingAidDevicesNotAddedInCache.remove(toDisplayDevice);
        mCachedDevices.add(toDisplayDevice);
        mCachedDevicesMapForHearingAids.put(hiSyncId, toDisplayDevice);
        mBtManager.getEventManager().dispatchDeviceAdded(toDisplayDevice);
    }

    public synchronized void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
                                                             int state, int bluetoothProfile) {
        if (bluetoothProfile == BluetoothProfile.HEARING_AID
            && cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
            && cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) {

            long hiSyncId = cachedDevice.getHiSyncId();

            CachedBluetoothDevice otherDevice = getHearingAidOtherDevice(cachedDevice, hiSyncId);
            if (otherDevice == null) {
                // no other side device. Nothing to do.
                return;
            }

            if (state == BluetoothProfile.STATE_CONNECTED &&
                mHearingAidDevicesNotAddedInCache.contains(cachedDevice)) {
                hearingAidSwitchDisplayDevice(cachedDevice, otherDevice, hiSyncId);
            } else if (state == BluetoothProfile.STATE_DISCONNECTED
                       && otherDevice.isConnected()) {
                CachedBluetoothDevice mapDevice = mCachedDevicesMapForHearingAids.get(hiSyncId);
                if ((mapDevice != null) && (Objects.equals(cachedDevice, mapDevice))) {
                    hearingAidSwitchDisplayDevice(otherDevice, cachedDevice, hiSyncId);
                }
            }
        }
    public synchronized boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice
            cachedDevice, int state) {
        return mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
                state);
    }

    public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
        final long hiSyncId = device.getHiSyncId();

        if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID) return;

        for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) {
            CachedBluetoothDevice cachedDevice = mHearingAidDevicesNotAddedInCache.get(i);
            if (cachedDevice.getHiSyncId() == hiSyncId) {
                // TODO: Look for more cleanups on unpairing the device.
                mHearingAidDevicesNotAddedInCache.remove(i);
                if (device == cachedDevice) continue;
                log("onDeviceUnpaired: Unpair device=" + cachedDevice);
                cachedDevice.unpair();
            }
        }

        CachedBluetoothDevice mappedDevice = mCachedDevicesMapForHearingAids.get(hiSyncId);
        if ((mappedDevice != null) && (!Objects.equals(device, mappedDevice))) {
            log("onDeviceUnpaired: Unpair mapped device=" + mappedDevice);
            mappedDevice.unpair();
        CachedBluetoothDevice mainDevice = mHearingAidDeviceManager.findMainDevice(device);
        CachedBluetoothDevice subDevice = device.getSubDevice();
        if (subDevice != null) {
            // Main device is unpaired, to unpair sub device
            subDevice.unpair();
            device.setSubDevice(null);
        } else if (mainDevice != null) {
            // Sub device unpaired, to unpair main device
            mainDevice.unpair();
            mainDevice.setSubDevice(null);
        }
    }

+223 −0

File added.

Preview size limit exceeded, changes collapsed.

+1 −1
Original line number Diff line number Diff line
@@ -71,7 +71,7 @@ public class HearingAidProfile implements LocalBluetoothProfile {
            }

            // Check current list of CachedDevices to see if any are Hearing Aid devices.
            mDeviceManager.updateHearingAidsDevices(mProfileManager);
            mDeviceManager.updateHearingAidsDevices();

            mIsProfileReady=true;
        }
Loading