Make HearingAid code more generic

-group hearingAid code into HearingAidDeviceManager
-add subDevice in CachedBluetoothDevice to store second hearingAid device
-add test case

Bug: 112735753
Test: make -j42 RunSettingsLibRoboTests
Change-Id: I6f554076b3e4e525959aba84e735b15f0e55d9d3
This commit is contained in:
timhypeng
2018-08-28 09:59:03 +08:00
committed by Hugh Chen
parent 86d0d9d8e5
commit 5c62ebba30
11 changed files with 893 additions and 672 deletions

View File

@@ -171,7 +171,6 @@ public class BluetoothEventManager {
callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
}
}
mDeviceManager.onProfileConnectionStateChanged(device, state, bluetoothProfile);
}
private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {

View File

@@ -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();
}
}

View File

@@ -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 {
synchronized (this) {
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);
}
for (int i = mHearingAidDevicesNotAddedInCache.size() - 1; i >= 0; i--) {
CachedBluetoothDevice notCachedDevice = mHearingAidDevicesNotAddedInCache.get(i);
notCachedDevice.setJustDiscovered(false);
final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
if (subDevice != null) {
subDevice.setJustDiscovered(false);
}
}
}
@@ -277,162 +207,45 @@ 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);
}
}
}
}
public synchronized void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
int bluetoothProfile) {
for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
boolean isActive = Objects.equals(cachedDevice, activeDevice);
cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
}
}
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);
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* HearingAidDeviceManager manages the set of remote HearingAid Bluetooth devices.
*/
public class HearingAidDeviceManager {
private static final String TAG = "HearingAidDeviceManager";
private static final boolean DEBUG = BluetoothUtils.D;
private final LocalBluetoothManager mBtManager;
private final List<CachedBluetoothDevice> mCachedDevices;
HearingAidDeviceManager(LocalBluetoothManager localBtManager,
List<CachedBluetoothDevice> CachedDevices) {
mBtManager = localBtManager;
mCachedDevices = CachedDevices;
}
void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice) {
long hiSyncId = getHiSyncId(newDevice.getDevice());
if (isValidHiSyncId(hiSyncId)) {
// Once hiSyncId is valid, assign hiSyncId
newDevice.setHiSyncId(hiSyncId);
}
}
private long getHiSyncId(BluetoothDevice device) {
LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
HearingAidProfile profileProxy = profileManager.getHearingAidProfile();
if (profileProxy != null) {
return profileProxy.getHiSyncId(device);
}
return BluetoothHearingAid.HI_SYNC_ID_INVALID;
}
boolean setSubDeviceIfNeeded(CachedBluetoothDevice newDevice) {
final long hiSyncId = newDevice.getHiSyncId();
if (isValidHiSyncId(hiSyncId)) {
final CachedBluetoothDevice hearingAidDevice = getCachedDevice(hiSyncId);
// Just add one of the hearing aids from a pair in the list that is shown in the UI.
// Once there is another device with the same hiSyncId, to add new device as sub
// device.
if (hearingAidDevice != null) {
hearingAidDevice.setSubDevice(newDevice);
return true;
}
}
return false;
}
private boolean isValidHiSyncId(long hiSyncId) {
return hiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID;
}
private CachedBluetoothDevice getCachedDevice(long hiSyncId) {
for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
if (cachedDevice.getHiSyncId() == hiSyncId) {
return cachedDevice;
}
}
return null;
}
// To collect all HearingAid devices and call #onHiSyncIdChanged to group device by HiSyncId
void updateHearingAidsDevices() {
final Set<Long> newSyncIdSet = new HashSet<Long>();
for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
// Do nothing if HiSyncId has been assigned
if (!isValidHiSyncId(cachedDevice.getHiSyncId())) {
final long newHiSyncId = getHiSyncId(cachedDevice.getDevice());
// Do nothing if there is no HiSyncId on Bluetooth device
if (isValidHiSyncId(newHiSyncId)) {
cachedDevice.setHiSyncId(newHiSyncId);
newSyncIdSet.add(newHiSyncId);
}
}
}
for (Long syncId : newSyncIdSet) {
onHiSyncIdChanged(syncId);
}
}
// Group devices by hiSyncId
@VisibleForTesting
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) {
continue;
}
if (firstMatchedIndex == -1) {
// Found the first one
firstMatchedIndex = i;
continue;
}
// Found the second one
int indexToRemoveFromUi;
CachedBluetoothDevice subDevice;
CachedBluetoothDevice mainDevice;
// 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()) {
mainDevice = cachedDevice;
indexToRemoveFromUi = firstMatchedIndex;
subDevice = mCachedDevices.get(firstMatchedIndex);
} else {
mainDevice = mCachedDevices.get(firstMatchedIndex);
indexToRemoveFromUi = i;
subDevice = cachedDevice;
}
mainDevice.setSubDevice(subDevice);
mCachedDevices.remove(indexToRemoveFromUi);
log("onHiSyncIdChanged: removed from UI device =" + subDevice
+ ", with hiSyncId=" + hiSyncId);
mBtManager.getEventManager().dispatchDeviceRemoved(subDevice);
break;
}
}
// @return {@code true}, the event is processed inside the method. It is for updating
// hearing aid device on main-sub relationship when receiving connected or disconnected.
// @return {@code false}, it is not hearing aid device or to process it same as other profiles
boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice cachedDevice,
int state) {
switch (state) {
case BluetoothProfile.STATE_CONNECTED:
onHiSyncIdChanged(cachedDevice.getHiSyncId());
CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice);
if (mainDevice != null){
if (mainDevice.isConnected()) {
// When main device exists and in connected state, receiving sub device
// connection. To refresh main device UI
mainDevice.refresh();
return true;
} else {
// When both Hearing Aid devices are disconnected, receiving sub device
// connection. To switch content and dispatch to notify UI change
mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice);
mainDevice.switchSubDeviceContent();
mainDevice.refresh();
// It is necessary to do remove and add for updating the mapping on
// preference and device
mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
return true;
}
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
mainDevice = findMainDevice(cachedDevice);
if (mainDevice != null) {
// When main device exists, receiving sub device disconnection
// To update main device UI
mainDevice.refresh();
return true;
}
CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
if (subDevice != null && subDevice.isConnected()) {
// Main device is disconnected and sub device is connected
// To copy data from sub device to main device
mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
cachedDevice.switchSubDeviceContent();
cachedDevice.refresh();
// It is necessary to do remove and add for updating the mapping on
// preference and device
mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice);
return true;
}
break;
}
return false;
}
CachedBluetoothDevice findMainDevice(CachedBluetoothDevice device) {
for (CachedBluetoothDevice cachedDevice : mCachedDevices) {
if (isValidHiSyncId(cachedDevice.getHiSyncId())) {
CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
if (subDevice != null && subDevice.equals(device)) {
return cachedDevice;
}
}
}
return null;
}
private void log(String msg) {
if (DEBUG) {
Log.d(TAG, msg);
}
}
}

View File

@@ -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;
}

View File

@@ -40,7 +40,6 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.internal.R;
import com.android.internal.util.CollectionUtils;
import java.util.ArrayList;
@@ -289,21 +288,21 @@ public class LocalBluetoothProfileManager {
(newState == BluetoothProfile.STATE_CONNECTED)) {
// Check if the HiSyncID has being initialized
if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
if (newHiSyncId != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
cachedDevice.setHiSyncId(newHiSyncId);
mDeviceManager.onHiSyncIdChanged(newHiSyncId);
}
}
}
cachedDevice.onProfileStateChanged(mProfile, newState);
cachedDevice.refresh();
// Dispatch profile changed after device update
mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
mProfile.getProfileId());
if (!(cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID
&& mDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice,
newState))) {
cachedDevice.refresh();
mEventManager.dispatchProfileConnectionStateChanged(cachedDevice, newState,
mProfile.getProfileId());
}
}
}

View File

@@ -96,8 +96,5 @@ public class BluetoothEventManagerTest {
verify(mBluetoothCallback).onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
verify(mCachedDeviceManager).onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
}
}

View File

@@ -18,7 +18,9 @@ package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
@@ -80,6 +82,7 @@ public class CachedBluetoothDeviceManagerTest {
private CachedBluetoothDevice mCachedDevice2;
private CachedBluetoothDevice mCachedDevice3;
private CachedBluetoothDeviceManager mCachedDeviceManager;
private HearingAidDeviceManager mHearingAidDeviceManager;
private Context mContext;
@Before
@@ -105,13 +108,12 @@ public class CachedBluetoothDeviceManagerTest {
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mPanProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
doAnswer((invocation) -> mHearingAidProfile).
when(mLocalProfileManager).getHearingAidProfile();
mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
mCachedDevice1 = spy(
new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(
new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
mCachedDevice3 = spy(
new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3));
mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
mCachedDevice3 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3));
}
/**
@@ -132,6 +134,76 @@ public class CachedBluetoothDeviceManagerTest {
assertThat(mCachedDeviceManager.findDevice(mDevice2)).isEqualTo(cachedDevice2);
}
/**
* Test to verify getSubDevice(), new device has the same HiSyncId.
*/
@Test
public void addDevice_sameHiSyncId_validSubDevice() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice1.getSubDevice()).isEqualTo(cachedDevice2);
}
/**
* Test to verify getSubDevice(), new device has the different HiSyncId.
*/
@Test
public void addDevice_differentHiSyncId_validSubDevice() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID2).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice1.getSubDevice()).isNull();
}
/**
* Test to verify addDevice(), new device has the same HiSyncId.
*/
@Test
public void addDevice_sameHiSyncId_validCachedDevices_mainDevicesAdded_subDevicesNotAdded() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice1);
assertThat(devices).doesNotContain(cachedDevice2);
}
/**
* Test to verify addDevice(), new device has the different HiSyncId.
*/
@Test
public void addDevice_differentHiSyncId_validCachedDevices_bothAdded() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID2).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice1);
assertThat(devices).contains(cachedDevice2);
}
/**
* Test to verify findDevice(), new device has the same HiSyncId.
*/
@Test
public void findDevice_sameHiSyncId_foundBothDevice() {
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(mCachedDeviceManager.findDevice(mDevice1)).isEqualTo(cachedDevice1);
assertThat(mCachedDeviceManager.findDevice(mDevice2)).isEqualTo(cachedDevice2);
}
/**
* Test to verify getName().
*/
@@ -190,453 +262,89 @@ public class CachedBluetoothDeviceManagerTest {
}
/**
* Test to verify clearNonBondedDevices() for hearing aids.
* Test to verify clearNonBondedDevices() for hearing aids sub device.
*/
@Test
public void clearNonBondedDevices_HearingAids_nonBondedHAsClearedFromCachedDevicesMap() {
public void clearNonBondedDevices_nonBondedSubDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
cachedDevice1.setSubDevice(cachedDevice2);
mCachedDevice1.setHiSyncId(HISYNCID1);
mCachedDevice2.setHiSyncId(HISYNCID2);
mCachedDeviceManager.mCachedDevicesMapForHearingAids.put(HISYNCID1, mCachedDevice1);
mCachedDeviceManager.mCachedDevicesMapForHearingAids.put(HISYNCID2, mCachedDevice2);
assertThat(cachedDevice1.getSubDevice()).isEqualTo(cachedDevice2);
mCachedDeviceManager.clearNonBondedDevices();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
.doesNotContain(mCachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
.contains(mCachedDevice1);
assertThat(cachedDevice1.getSubDevice()).isNull();
}
/**
* Test to verify onHiSyncIdChanged() for hearing aid devices with same HiSyncId.
* Test to verify OnDeviceUnpaired() for main hearing Aid device unpair.
*/
@Test
public void onHiSyncIdChanged_sameHiSyncId_populateInDifferentLists() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
// Since both devices do not have hiSyncId, they should be added in mCachedDevices.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
cachedDevice1.setHiSyncId(HISYNCID1);
cachedDevice2.setHiSyncId(HISYNCID1);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
// Since both devices have the same hiSyncId, one should remain in mCachedDevices
// and the other should be removed from mCachedDevices and get added in
// mHearingAidDevicesNotAddedInCache. The one that is in mCachedDevices should also be
// added in mCachedDevicesMapForHearingAids.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids)
.containsKey(HISYNCID1);
}
/**
* Test to verify onHiSyncIdChanged() for 2 hearing aid devices with same HiSyncId but one
* device is connected and other is disconnected. The connected device should be chosen.
*/
@Test
public void onHiSyncIdChanged_sameHiSyncIdAndOneConnected_chooseConnectedDevice() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
/* Device 1 is connected and Device 2 is disconnected */
when(mHearingAidProfile.getConnectionStatus(mDevice1)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
when(mHearingAidProfile.getConnectionStatus(mDevice2)).
thenReturn(BluetoothProfile.STATE_DISCONNECTED);
// Since both devices do not have hiSyncId, they should be added in mCachedDevices.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
cachedDevice1.setHiSyncId(HISYNCID1);
cachedDevice2.setHiSyncId(HISYNCID1);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
// Only the connected device, device 1, should be visible to UI.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
containsExactly(HISYNCID1, cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).
containsExactly(cachedDevice2);
}
/**
* Test to verify onHiSyncIdChanged() for hearing aid devices with different HiSyncId.
*/
@Test
public void onHiSyncIdChanged_differentHiSyncId_populateInSameList() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
// Since both devices do not have hiSyncId, they should be added in mCachedDevices.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
cachedDevice1.setHiSyncId(HISYNCID1);
cachedDevice2.setHiSyncId(HISYNCID2);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID2);
// Since both devices do not have same hiSyncId, they should remain in mCachedDevices and
// also be added in mCachedDevicesMapForHearingAids.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(2);
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice2);
assertThat(devices).contains(cachedDevice1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
.contains(cachedDevice1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
.contains(cachedDevice2);
}
/**
* Test to verify onProfileConnectionStateChanged() for single hearing aid device connection.
*/
@Test
public void onProfileConnectionStateChanged_singleDeviceConnected_visible() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
// Since both devices do not have hiSyncId, they should be added in mCachedDevices.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
cachedDevice1.setHiSyncId(HISYNCID1);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
// Connect the Device 1
mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
containsExactly(HISYNCID1, cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
// Disconnect the Device 1
mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.HEARING_AID);
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
containsExactly(HISYNCID1, cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
}
/**
* Test to verify onProfileConnectionStateChanged() for two hearing aid devices where both
* devices are disconnected and they get connected.
*/
@Test
public void onProfileConnectionStateChanged_twoDevicesConnected_oneDeviceVisible() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
public void onDeviceUnpaired_unpairMainDevice() {
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
// Since both devices do not have hiSyncId, they should be added in mCachedDevices.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
cachedDevice1.setHiSyncId(HISYNCID1);
cachedDevice2.setHiSyncId(HISYNCID1);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
// There should be one cached device but can be either one.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
// Connect the Device 1
mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).
containsExactly(HISYNCID1, cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevices).contains(cachedDevice1);
when(mHearingAidProfile.getConnectionStatus(mDevice1)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
when(mHearingAidProfile.getConnectionStatus(mDevice2)).
thenReturn(BluetoothProfile.STATE_DISCONNECTED);
// Connect the Device 2
mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice2,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
assertThat(mCachedDeviceManager.mCachedDevices).hasSize(1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
}
/**
* Test to verify onProfileConnectionStateChanged() for two hearing aid devices where both
* devices are connected and they get disconnected.
*/
@Test
public void onProfileConnectionStateChanged_twoDevicesDisconnected_oneDeviceVisible() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
cachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
cachedDevice2.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
when(mHearingAidProfile.getConnectionStatus(mDevice1)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
when(mHearingAidProfile.getConnectionStatus(mDevice2)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
// Since both devices do not have hiSyncId, they should be added in mCachedDevices.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).isEmpty();
cachedDevice1.setHiSyncId(HISYNCID1);
cachedDevice2.setHiSyncId(HISYNCID1);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
/* Disconnect the Device 1 */
mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice1,
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.HEARING_AID);
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).containsExactly(cachedDevice2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
assertThat(mCachedDeviceManager.mCachedDevices).contains(cachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids)
.containsExactly(HISYNCID1, cachedDevice2);
when(mHearingAidProfile.getConnectionStatus(mDevice1)).
thenReturn(BluetoothProfile.STATE_DISCONNECTED);
when(mHearingAidProfile.getConnectionStatus(mDevice2)).
thenReturn(BluetoothProfile.STATE_CONNECTED);
/* Disconnect the Device 2 */
mCachedDeviceManager.onProfileConnectionStateChanged(cachedDevice2,
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.HEARING_AID);
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
assertThat(mCachedDeviceManager.mCachedDevices).hasSize(1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
}
/**
* Test to verify OnDeviceUnpaired() for a paired hearing Aid device pair.
*/
@Test
public void onDeviceUnpaired_bothHearingAidsPaired_removesItsPairFromList() {
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
cachedDevice1.setHiSyncId(HISYNCID1);
cachedDevice2.setHiSyncId(HISYNCID1);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
// Check if one device is in mCachedDevices and one in mHearingAidDevicesNotAddedInCache.
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice2);
assertThat(devices).doesNotContain(cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
.doesNotContain(cachedDevice2);
cachedDevice1.setSubDevice(cachedDevice2);
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice2);
// Check if its pair is removed from mHearingAidDevicesNotAddedInCache.
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
.doesNotContain(cachedDevice1);
verify(mDevice1).removeBond();
}
/**
* Test to verify OnDeviceUnpaired() for paired hearing Aid devices which are not a pair.
* Test to verify OnDeviceUnpaired() for sub hearing Aid device unpair.
*/
@Test
public void onDeviceUnpaired_bothHearingAidsNotPaired_doesNotRemoveAnyDeviceFromList() {
public void onDeviceUnpaired_unpairSubDevice() {
when(mDevice2.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3);
assertThat(cachedDevice2).isNotNull();
cachedDevice1.setSubDevice(cachedDevice2);
cachedDevice1.setHiSyncId(HISYNCID1);
cachedDevice2.setHiSyncId(HISYNCID1);
cachedDevice3.setHiSyncId(HISYNCID2);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID1);
mCachedDeviceManager.onHiSyncIdChanged(HISYNCID2);
// Check if one device is in mCachedDevices and one in mHearingAidDevicesNotAddedInCache.
Collection<CachedBluetoothDevice> devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice2);
assertThat(devices).contains(cachedDevice3);
assertThat(devices).doesNotContain(cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
.doesNotContain(cachedDevice2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
.doesNotContain(cachedDevice3);
// Call onDeviceUnpaired for the one in mCachedDevices with no pair.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice3);
// Check if no list is changed.
devices = mCachedDeviceManager.getCachedDevicesCopy();
assertThat(devices).contains(cachedDevice2);
assertThat(devices).contains(cachedDevice3);
assertThat(devices).doesNotContain(cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).contains(cachedDevice1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
.doesNotContain(cachedDevice2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache)
.doesNotContain(cachedDevice3);
// Call onDeviceUnpaired for the one in mCachedDevices.
mCachedDeviceManager.onDeviceUnpaired(cachedDevice1);
verify(mDevice2).removeBond();
}
/**
* Test to verify addDevice() for hearing aid devices with same HiSyncId.
* Test to verify getSubDeviceSummary() for disconnected sub device
*/
@Test
public void addDevice_hearingAidDevicesWithSameHiSyncId_populateInDifferentLists() {
doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
.getHearingAidProfile();
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
public void getSubDeviceSummary_SubDeviceDisconnected() {
when(mCachedDevice2.isConnected()).thenReturn(false);
mCachedDevice1.setSubDevice(mCachedDevice2);
mCachedDeviceManager.getSubDeviceSummary(mCachedDevice1);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
// The first hearing aid device should be populated in mCachedDevice and
// mCachedDevicesMapForHearingAids.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
.contains(cachedDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
// The second hearing aid device should be populated in mHearingAidDevicesNotAddedInCache.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).hasSize(1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
verify(mCachedDevice2, never()).getConnectionSummary();
}
/**
* Test to verify addDevice() for hearing aid devices with different HiSyncId.
* Test to verify getSubDeviceSummary() for connected sub device
*/
@Test
public void addDevice_hearingAidDevicesWithDifferentHiSyncId_populateInSameList() {
doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
.getHearingAidProfile();
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID2).when(mHearingAidProfile).getHiSyncId(mDevice2);
CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
assertThat(cachedDevice1).isNotNull();
// The first hearing aid device should be populated in mCachedDevice and
// mCachedDevicesMapForHearingAids.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(1);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
.contains(cachedDevice1);
public void getSubDeviceSummary_SubDeviceConnected() {
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDevice1.setSubDevice(mCachedDevice2);
mCachedDeviceManager.getSubDeviceSummary(mCachedDevice1);
CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2);
assertThat(cachedDevice2).isNotNull();
// The second hearing aid device should also be populated in mCachedDevice
// and mCachedDevicesMapForHearingAids as its not a pair of the first one.
assertThat(mCachedDeviceManager.getCachedDevicesCopy()).hasSize(2);
assertThat(mCachedDeviceManager.mHearingAidDevicesNotAddedInCache).isEmpty();
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids).hasSize(2);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
.contains(cachedDevice1);
assertThat(mCachedDeviceManager.mCachedDevicesMapForHearingAids.values())
.contains(cachedDevice2);
}
/**
* Test to verify getHearingAidPairDeviceSummary() for hearing aid devices with same HiSyncId.
*/
@Test
public void getHearingAidPairDeviceSummary_bothHearingAidsPaired_returnsSummaryOfPair() {
mCachedDevice1.setHiSyncId(HISYNCID1);
mCachedDevice2.setHiSyncId(HISYNCID1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mHearingAidDevicesNotAddedInCache.add(mCachedDevice2);
doAnswer((invocation) -> DEVICE_SUMMARY_1).when(mCachedDevice1).getConnectionSummary();
doAnswer((invocation) -> DEVICE_SUMMARY_2).when(mCachedDevice2).getConnectionSummary();
assertThat(mCachedDeviceManager.getHearingAidPairDeviceSummary(mCachedDevice1))
.isEqualTo(DEVICE_SUMMARY_2);
}
/**
* Test to verify getHearingAidPairDeviceSummary() for hearing aid devices with different
* HiSyncId.
*/
@Test
public void getHearingAidPairDeviceSummary_bothHearingAidsNotPaired_returnsNull() {
mCachedDevice1.setHiSyncId(HISYNCID1);
mCachedDevice2.setHiSyncId(HISYNCID2);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mHearingAidDevicesNotAddedInCache.add(mCachedDevice2);
doAnswer((invocation) -> DEVICE_SUMMARY_1).when(mCachedDevice1).getConnectionSummary();
doAnswer((invocation) -> DEVICE_SUMMARY_2).when(mCachedDevice2).getConnectionSummary();
assertThat(mCachedDeviceManager.getHearingAidPairDeviceSummary(mCachedDevice1))
.isEqualTo(null);
verify(mCachedDevice2).getConnectionSummary();
}
/**
* Test to verify updateHearingAidsDevices().
*/
@Test
public void updateHearingAidDevices_hiSyncIdAvailable_setsHiSyncId() {
doAnswer((invocation) -> mHearingAidProfile).when(mLocalProfileManager)
.getHearingAidProfile();
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice1);
doAnswer((invocation) -> HISYNCID1).when(mHearingAidProfile).getHiSyncId(mDevice2);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
mCachedDeviceManager.updateHearingAidsDevices(mLocalProfileManager);
public void updateHearingAidDevices_directToHearingAidDeviceManager() {
mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mLocalBluetoothManager,
mCachedDeviceManager.mCachedDevices));
mCachedDeviceManager.mHearingAidDeviceManager = mHearingAidDeviceManager;
mCachedDeviceManager.updateHearingAidsDevices();
// Assert that the mCachedDevice1 and mCachedDevice2 have an updated HiSyncId.
assertThat(mCachedDevice1.getHiSyncId()).isEqualTo(HISYNCID1);
assertThat(mCachedDevice2.getHiSyncId()).isEqualTo(HISYNCID1);
verify(mHearingAidDeviceManager).updateHearingAidsDevices();
}
/**

View File

@@ -48,6 +48,10 @@ public class CachedBluetoothDeviceTest {
private final static String DEVICE_ALIAS = "TestAlias";
private final static String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF";
private final static String DEVICE_ALIAS_NEW = "TestAliasNew";
private final static short RSSI_1 = 10;
private final static short RSSI_2 = 11;
private final static boolean JUSTDISCOVERED_1 = true;
private final static boolean JUSTDISCOVERED_2 = false;
@Mock
private LocalBluetoothProfileManager mProfileManager;
@Mock
@@ -60,6 +64,8 @@ public class CachedBluetoothDeviceTest {
private HearingAidProfile mHearingAidProfile;
@Mock
private BluetoothDevice mDevice;
@Mock
private BluetoothDevice mSubDevice;
private CachedBluetoothDevice mCachedDevice;
private ShadowAudioManager mShadowAudioManager;
private Context mContext;
@@ -657,4 +663,39 @@ public class CachedBluetoothDeviceTest {
doReturn(status).when(profile).getConnectionStatus(mDevice);
mCachedDevice.onProfileStateChanged(profile, status);
}
@Test
public void getSubDevice_setSubDevice() {
CachedBluetoothDevice subCachedDevice = new CachedBluetoothDevice(mContext, mProfileManager,
mSubDevice);
mCachedDevice.setSubDevice(subCachedDevice);
assertThat(mCachedDevice.getSubDevice()).isEqualTo(subCachedDevice);
}
@Test
public void switchSubDeviceContent() {
CachedBluetoothDevice subCachedDevice = new CachedBluetoothDevice(mContext, mProfileManager,
mSubDevice);
mCachedDevice.mRssi = RSSI_1;
mCachedDevice.mJustDiscovered = JUSTDISCOVERED_1;
subCachedDevice.mRssi = RSSI_2;
subCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
mCachedDevice.setSubDevice(subCachedDevice);
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_1);
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
assertThat(mCachedDevice.mDevice).isEqualTo(mDevice);
assertThat(subCachedDevice.mRssi).isEqualTo(RSSI_2);
assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
assertThat(subCachedDevice.mDevice).isEqualTo(mSubDevice);
mCachedDevice.switchSubDeviceContent();
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
assertThat(subCachedDevice.mRssi).isEqualTo(RSSI_1);
assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
assertThat(subCachedDevice.mDevice).isEqualTo(mDevice);
}
}

View File

@@ -0,0 +1,410 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.bluetooth;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class HearingAidDeviceManagerTest {
private final static long HISYNCID1 = 10;
private final static long HISYNCID2 = 11;
private final static String DEVICE_NAME_1 = "TestName_1";
private final static String DEVICE_NAME_2 = "TestName_2";
private final static String DEVICE_ALIAS_1 = "TestAlias_1";
private final static String DEVICE_ALIAS_2 = "TestAlias_2";
private final static String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
private final static String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
private final BluetoothClass DEVICE_CLASS =
new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
@Mock
private LocalBluetoothProfileManager mLocalProfileManager;
@Mock
private LocalBluetoothManager mLocalBluetoothManager;
@Mock
private BluetoothEventManager mBluetoothEventManager;
@Mock
private HearingAidProfile mHearingAidProfile;
@Mock
private BluetoothDevice mDevice1;
@Mock
private BluetoothDevice mDevice2;
private CachedBluetoothDevice mCachedDevice1;
private CachedBluetoothDevice mCachedDevice2;
private CachedBluetoothDeviceManager mCachedDeviceManager;
private HearingAidDeviceManager mHearingAidDeviceManager;
private Context mContext;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
when(mDevice1.getName()).thenReturn(DEVICE_NAME_1);
when(mDevice2.getName()).thenReturn(DEVICE_NAME_2);
when(mDevice1.getAliasName()).thenReturn(DEVICE_ALIAS_1);
when(mDevice2.getAliasName()).thenReturn(DEVICE_ALIAS_2);
when(mDevice1.getBluetoothClass()).thenReturn(DEVICE_CLASS);
when(mDevice2.getBluetoothClass()).thenReturn(DEVICE_CLASS);
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBluetoothEventManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mLocalBluetoothManager,
mCachedDeviceManager.mCachedDevices));
mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
}
/**
* Test initHearingAidDeviceIfNeeded, a valid HiSyncId will be assigned
*/
@Test
public void initHearingAidDeviceIfNeeded_validHiSyncId_verifyHiSyncId() {
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
assertThat(mCachedDevice1.getHiSyncId()).isNotEqualTo(HISYNCID1);
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
assertThat(mCachedDevice1.getHiSyncId()).isEqualTo(HISYNCID1);
}
/**
* Test initHearingAidDeviceIfNeeded, an invalid HiSyncId will not be assigned
*/
@Test
public void initHearingAidDeviceIfNeeded_invalidHiSyncId_notToSetHiSyncId() {
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(
BluetoothHearingAid.HI_SYNC_ID_INVALID);
mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(mCachedDevice1);
verify(mCachedDevice1, never()).setHiSyncId(anyLong());
}
/**
* Test setSubDeviceIfNeeded, a device with same HiSyncId will be set as sub device
*/
@Test
public void setSubDeviceIfNeeded_sameHiSyncId_setSubDevice() {
mCachedDevice1.setHiSyncId(HISYNCID1);
mCachedDevice2.setHiSyncId(HISYNCID1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mHearingAidDeviceManager.setSubDeviceIfNeeded(mCachedDevice2);
assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
}
/**
* Test setSubDeviceIfNeeded, a device with different HiSyncId will not be set as sub device
*/
@Test
public void setSubDeviceIfNeeded_differentHiSyncId_notSetSubDevice() {
mCachedDevice1.setHiSyncId(HISYNCID1);
mCachedDevice2.setHiSyncId(HISYNCID2);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mHearingAidDeviceManager.setSubDeviceIfNeeded(mCachedDevice2);
assertThat(mCachedDevice1.getSubDevice()).isNull();
}
/**
* Test updateHearingAidsDevices, to link two devices with the same HiSyncId.
* When first paired devices is connected and second paired device is disconnected, first
* paired device would be set as main device and second device will be removed from
* CachedDevices list.
*/
@Test
public void updateHearingAidsDevices_firstPairedDevicesConnected_verifySubDevice() {
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(true);
when(mCachedDevice2.isConnected()).thenReturn(false);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isTrue();
assertThat(mCachedDevice1.getSubDevice()).isNull();
mHearingAidDeviceManager.updateHearingAidsDevices();
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isFalse();
assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
}
/**
* Test updateHearingAidsDevices, to link two devices with the same HiSyncId.
* When second paired devices is connected and first paired device is disconnected, second
* paired device would be set as main device and first device will be removed from
* CachedDevices list.
*/
@Test
public void updateHearingAidsDevices_secondPairedDeviceConnected_verifySubDevice() {
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(false);
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice1)).isTrue();
assertThat(mCachedDevice2.getSubDevice()).isNull();
mHearingAidDeviceManager.updateHearingAidsDevices();
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice1)).isFalse();
assertThat(mCachedDevice2.getSubDevice()).isEqualTo(mCachedDevice1);
}
/**
* Test updateHearingAidsDevices, to link two devices with the same HiSyncId.
* When both devices are connected, to build up main and sub relationship and to remove sub
* device from CachedDevices list.
*/
@Test
public void updateHearingAidsDevices_BothConnected_verifySubDevice() {
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(true);
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isTrue();
assertThat(mCachedDevice1.getSubDevice()).isNull();
mHearingAidDeviceManager.updateHearingAidsDevices();
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isFalse();
assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
}
/**
* Test updateHearingAidsDevices, dispatch callback
*/
@Test
public void updateHearingAidsDevices_dispatchDeviceRemovedCallback() {
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
when(mHearingAidProfile.getHiSyncId(mDevice2)).thenReturn(HISYNCID1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
mHearingAidDeviceManager.updateHearingAidsDevices();
verify(mBluetoothEventManager).dispatchDeviceRemoved(mCachedDevice1);
}
/**
* Test updateHearingAidsDevices, do nothing when HiSyncId is invalid
*/
@Test
public void updateHearingAidsDevices_invalidHiSyncId_doNothing() {
when(mHearingAidProfile.getHiSyncId(mDevice1)).
thenReturn(BluetoothHearingAid.HI_SYNC_ID_INVALID);
when(mHearingAidProfile.getHiSyncId(mDevice2)).
thenReturn(BluetoothHearingAid.HI_SYNC_ID_INVALID);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
mHearingAidDeviceManager.updateHearingAidsDevices();
verify(mHearingAidDeviceManager, never()).onHiSyncIdChanged(anyLong());
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When first hearing aid device is connected, to process it same as other generic devices.
* No need to process it.
*/
@Test
public void onProfileConnectionStateChanged_connected_singleDevice_returnFalse() {
when(mHearingAidProfile.getHiSyncId(mDevice1)).thenReturn(HISYNCID1);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice1, BluetoothProfile.STATE_CONNECTED)).isFalse();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When a new hearing aid device is connected, to set it as sub device by onHiSyncIdChanged().
* And, to verify new device is not in CachedDevices list.
*/
@Test
public void onProfileConnectionStateChanged_connected_newDevice_verifySubDevice() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(true);
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice2);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isTrue();
assertThat(mCachedDevice1.getSubDevice()).isNull();
mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDeviceManager.mCachedDevices.contains(mCachedDevice2)).isFalse();
assertThat(mCachedDevice1.getSubDevice()).isEqualTo(mCachedDevice2);
verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When sub device is disconnected, do nothing and return False for main device connected event
*/
@Test
public void
onProfileConnectionStateChanged_connected_mainDevice_subDeviceDisconnected_returnFalse() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.isConnected()).thenReturn(false);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice1, BluetoothProfile.STATE_CONNECTED)).isFalse();
verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When main device is connected, do main device refresh() for sub device connected event
*/
@Test
public void
onProfileConnectionStateChanged_connected_subDevice_mainDeviceConnected_verifyRefresh() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue();
verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
verify(mCachedDevice1).refresh();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When main device is disconnected, to verify switch() result for sub device connected
* event
*/
@Test
public void onProfileConnectionStateChanged_connected_subDevice_mainDeviceDisconnected_switch()
{
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice1.isConnected()).thenReturn(false);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue();
assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
verify(mHearingAidDeviceManager).onHiSyncIdChanged(anyLong());
verify(mCachedDevice1).refresh();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When sub device is connected, to verify switch() result for main device disconnected
* event
*/
@Test
public void onProfileConnectionStateChanged_disconnected_mainDevice_subDeviceConnected_switch()
{
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.isConnected()).thenReturn(true);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1);
assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isTrue();
assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2);
assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1);
verify(mCachedDevice1).refresh();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* When sub device is disconnected, do nothing and return False for main device disconnected
* event
*/
@Test
public void
onProfileConnectionStateChanged_disconnected_mainDevice_subDeviceDisconnected_returnFalse() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.isConnected()).thenReturn(false);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isFalse();
}
/**
* Test onProfileConnectionStateChangedIfProcessed.
* Refresh main device UI for sub device disconnected event
*/
@Test
public void onProfileConnectionStateChanged_disconnected_subDevice_verifyRefresh() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(
mCachedDevice2, BluetoothProfile.STATE_DISCONNECTED)).isTrue();
verify(mCachedDevice1).refresh();
}
@Test
public void findMainDevice() {
when(mCachedDevice1.getHiSyncId()).thenReturn(HISYNCID1);
when(mCachedDevice2.getHiSyncId()).thenReturn(HISYNCID1);
mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
mCachedDevice1.setSubDevice(mCachedDevice2);
assertThat(mHearingAidDeviceManager.findMainDevice(mCachedDevice2)).
isEqualTo(mCachedDevice1);
}
}

View File

@@ -55,6 +55,8 @@ import java.util.List;
@RunWith(SettingsLibRobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class LocalBluetoothProfileManagerTest {
private final static long HISYNCID = 10;
@Mock
private CachedBluetoothDeviceManager mDeviceManager;
@Mock
@@ -79,6 +81,7 @@ public class LocalBluetoothProfileManagerTest {
mContext, null));
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
when(mDeviceManager.findDevice(mDevice)).thenReturn(mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mDevice);
mProfileManager = new LocalBluetoothProfileManager(mContext, mLocalBluetoothAdapter,
mDeviceManager, mEventManager);
}
@@ -186,13 +189,14 @@ public class LocalBluetoothProfileManagerTest {
/**
* Verify BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED with uuid intent will dispatch to
* profile connection state changed callback
* CachedBluetoothDeviceManager method
*/
@Test
public void stateChangedHandler_receiveHAPConnectionStateChanged_shouldDispatchCallback() {
public void stateChangedHandler_receiveHAPConnectionStateChanged_shouldDispatchDeviceManager() {
mShadowBluetoothAdapter.setSupportedProfiles(generateList(
new int[] {BluetoothProfile.HEARING_AID}));
mProfileManager.updateLocalProfiles();
when(mCachedBluetoothDevice.getHiSyncId()).thenReturn(HISYNCID);
mIntent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
@@ -201,8 +205,8 @@ public class LocalBluetoothProfileManagerTest {
mContext.sendBroadcast(mIntent);
verify(mEventManager).dispatchProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
verify(mDeviceManager).onProfileConnectionStateChangedIfProcessed(mCachedBluetoothDevice,
BluetoothProfile.STATE_CONNECTED);
}
/**