Merge changes from topic "hearingAidsInT" into tm-dev
* changes: Show left/right side of the hearing aid device for ASHA profile Using reflection to get private BluetoothHearingAid APIs for short term
This commit is contained in:
@@ -77,6 +77,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
private final LocalBluetoothProfileManager mProfileManager;
|
private final LocalBluetoothProfileManager mProfileManager;
|
||||||
private final Object mProfileLock = new Object();
|
private final Object mProfileLock = new Object();
|
||||||
BluetoothDevice mDevice;
|
BluetoothDevice mDevice;
|
||||||
|
private int mDeviceSide;
|
||||||
|
private int mDeviceMode;
|
||||||
private long mHiSyncId;
|
private long mHiSyncId;
|
||||||
private int mGroupId;
|
private int mGroupId;
|
||||||
// Need this since there is no method for getting RSSI
|
// Need this since there is no method for getting RSSI
|
||||||
@@ -335,6 +337,22 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
connectDevice();
|
connectDevice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDeviceSide() {
|
||||||
|
return mDeviceSide;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceSide(int side) {
|
||||||
|
mDeviceSide = side;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeviceMode() {
|
||||||
|
return mDeviceMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeviceMode(int mode) {
|
||||||
|
mDeviceMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
public long getHiSyncId() {
|
public long getHiSyncId() {
|
||||||
return mHiSyncId;
|
return mHiSyncId;
|
||||||
}
|
}
|
||||||
@@ -1111,7 +1129,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
stringRes = R.string.bluetooth_battery_level;
|
stringRes = R.string.bluetooth_battery_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set active string in following device connected situation.
|
// Set active string in following device connected situation, also show battery
|
||||||
|
// information if they have.
|
||||||
// 1. Hearing Aid device active.
|
// 1. Hearing Aid device active.
|
||||||
// 2. Headset device active with in-calling state.
|
// 2. Headset device active with in-calling state.
|
||||||
// 3. A2DP device active without in-calling state.
|
// 3. A2DP device active without in-calling state.
|
||||||
@@ -1130,6 +1149,24 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
|
|||||||
stringRes = R.string.bluetooth_active_no_battery_level;
|
stringRes = R.string.bluetooth_active_no_battery_level;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to show left/right information if can not get it from battery for hearing
|
||||||
|
// aids specifically.
|
||||||
|
if (mIsActiveDeviceHearingAid
|
||||||
|
&& stringRes == R.string.bluetooth_active_no_battery_level) {
|
||||||
|
final CachedBluetoothDevice subDevice = getSubDevice();
|
||||||
|
if (subDevice != null && subDevice.isConnected()) {
|
||||||
|
stringRes = R.string.bluetooth_hearing_aid_left_and_right_active;
|
||||||
|
} else {
|
||||||
|
if (mDeviceSide == HearingAidProfile.DeviceSide.SIDE_LEFT) {
|
||||||
|
stringRes = R.string.bluetooth_hearing_aid_left_active;
|
||||||
|
} else if (mDeviceSide == HearingAidProfile.DeviceSide.SIDE_RIGHT) {
|
||||||
|
stringRes = R.string.bluetooth_hearing_aid_right_active;
|
||||||
|
} else {
|
||||||
|
stringRes = R.string.bluetooth_active_no_battery_level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,48 @@ import android.bluetooth.BluetoothProfile;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.IntDef;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.android.settingslib.R;
|
import com.android.settingslib.R;
|
||||||
import com.android.settingslib.Utils;
|
import com.android.settingslib.Utils;
|
||||||
|
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class HearingAidProfile implements LocalBluetoothProfile {
|
public class HearingAidProfile implements LocalBluetoothProfile {
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef({
|
||||||
|
DeviceSide.SIDE_INVALID,
|
||||||
|
DeviceSide.SIDE_LEFT,
|
||||||
|
DeviceSide.SIDE_RIGHT
|
||||||
|
})
|
||||||
|
|
||||||
|
/** Side definition for hearing aids. See {@link BluetoothHearingAid}. */
|
||||||
|
public @interface DeviceSide {
|
||||||
|
int SIDE_INVALID = -1;
|
||||||
|
int SIDE_LEFT = 0;
|
||||||
|
int SIDE_RIGHT = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
@IntDef({
|
||||||
|
DeviceMode.MODE_INVALID,
|
||||||
|
DeviceMode.MODE_MONAURAL,
|
||||||
|
DeviceMode.MODE_BINAURAL
|
||||||
|
})
|
||||||
|
|
||||||
|
/** Mode definition for hearing aids. See {@link BluetoothHearingAid}. */
|
||||||
|
public @interface DeviceMode {
|
||||||
|
int MODE_INVALID = -1;
|
||||||
|
int MODE_MONAURAL = 0;
|
||||||
|
int MODE_BINAURAL = 1;
|
||||||
|
}
|
||||||
|
|
||||||
private static final String TAG = "HearingAidProfile";
|
private static final String TAG = "HearingAidProfile";
|
||||||
private static boolean V = true;
|
private static boolean V = true;
|
||||||
|
|
||||||
@@ -212,6 +247,11 @@ public class HearingAidProfile implements LocalBluetoothProfile {
|
|||||||
return isEnabled;
|
return isEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells remote device to set an absolute volume.
|
||||||
|
*
|
||||||
|
* @param volume Absolute volume to be set on remote
|
||||||
|
*/
|
||||||
public void setVolume(int volume) {
|
public void setVolume(int volume) {
|
||||||
if (mService == null) {
|
if (mService == null) {
|
||||||
return;
|
return;
|
||||||
@@ -219,6 +259,12 @@ public class HearingAidProfile implements LocalBluetoothProfile {
|
|||||||
mService.setVolume(volume);
|
mService.setVolume(volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the HiSyncId (unique hearing aid device identifier) of the device.
|
||||||
|
*
|
||||||
|
* @param device Bluetooth device
|
||||||
|
* @return the HiSyncId of the device
|
||||||
|
*/
|
||||||
public long getHiSyncId(BluetoothDevice device) {
|
public long getHiSyncId(BluetoothDevice device) {
|
||||||
if (mService == null || device == null) {
|
if (mService == null || device == null) {
|
||||||
return BluetoothHearingAid.HI_SYNC_ID_INVALID;
|
return BluetoothHearingAid.HI_SYNC_ID_INVALID;
|
||||||
@@ -226,6 +272,59 @@ public class HearingAidProfile implements LocalBluetoothProfile {
|
|||||||
return mService.getHiSyncId(device);
|
return mService.getHiSyncId(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the side of the device.
|
||||||
|
*
|
||||||
|
* @param device Bluetooth device.
|
||||||
|
* @return side of the device. See {@link DeviceSide}.
|
||||||
|
*/
|
||||||
|
@DeviceSide
|
||||||
|
public int getDeviceSide(@NonNull BluetoothDevice device) {
|
||||||
|
final int defaultValue = DeviceSide.SIDE_INVALID;
|
||||||
|
if (mService == null) {
|
||||||
|
Log.w(TAG, "Proxy not attached to HearingAidService");
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Method method = mService.getClass().getDeclaredMethod("getDeviceSideInternal",
|
||||||
|
BluetoothDevice.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return (int) method.invoke(mService, device);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
Log.e(TAG, "fail to get getDeviceSideInternal\n" + e.toString() + "\n"
|
||||||
|
+ Log.getStackTraceString(new Throwable()));
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the mode of the device.
|
||||||
|
*
|
||||||
|
* @param device Bluetooth device
|
||||||
|
* @return mode of the device. See {@link DeviceMode}.
|
||||||
|
*/
|
||||||
|
@DeviceMode
|
||||||
|
public int getDeviceMode(@NonNull BluetoothDevice device) {
|
||||||
|
final int defaultValue = DeviceMode.MODE_INVALID;
|
||||||
|
if (mService == null) {
|
||||||
|
Log.w(TAG, "Proxy not attached to HearingAidService");
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Method method = mService.getClass().getDeclaredMethod("getDeviceModeInternal",
|
||||||
|
BluetoothDevice.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return (int) method.invoke(mService, device);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
Log.e(TAG, "fail to get getDeviceModeInternal\n" + e.toString() + "\n"
|
||||||
|
+ Log.getStackTraceString(new Throwable()));
|
||||||
|
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return NAME;
|
return NAME;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -340,6 +340,11 @@ public class LocalBluetoothProfileManager {
|
|||||||
if (getHearingAidProfile() != null &&
|
if (getHearingAidProfile() != null &&
|
||||||
mProfile instanceof HearingAidProfile &&
|
mProfile instanceof HearingAidProfile &&
|
||||||
(newState == BluetoothProfile.STATE_CONNECTED)) {
|
(newState == BluetoothProfile.STATE_CONNECTED)) {
|
||||||
|
final int side = getHearingAidProfile().getDeviceSide(cachedDevice.getDevice());
|
||||||
|
final int mode = getHearingAidProfile().getDeviceMode(cachedDevice.getDevice());
|
||||||
|
cachedDevice.setDeviceSide(side);
|
||||||
|
cachedDevice.setDeviceMode(mode);
|
||||||
|
|
||||||
// Check if the HiSyncID has being initialized
|
// Check if the HiSyncID has being initialized
|
||||||
if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
|
if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
|
||||||
long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
|
long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ public class CachedBluetoothDeviceTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private BluetoothDevice mSubDevice;
|
private BluetoothDevice mSubDevice;
|
||||||
private CachedBluetoothDevice mCachedDevice;
|
private CachedBluetoothDevice mCachedDevice;
|
||||||
|
private CachedBluetoothDevice mSubCachedDevice;
|
||||||
private AudioManager mAudioManager;
|
private AudioManager mAudioManager;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
|
private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
|
||||||
@@ -95,7 +96,9 @@ public class CachedBluetoothDeviceTest {
|
|||||||
when(mPanProfile.isProfileReady()).thenReturn(true);
|
when(mPanProfile.isProfileReady()).thenReturn(true);
|
||||||
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
|
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
|
||||||
mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice));
|
mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice));
|
||||||
|
mSubCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mSubDevice));
|
||||||
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
|
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
|
||||||
|
doAnswer((invocation) -> mBatteryLevel).when(mSubCachedDevice).getBatteryLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -351,8 +354,9 @@ public class CachedBluetoothDeviceTest {
|
|||||||
assertThat(mCachedDevice.getConnectionSummary()).isNull();
|
assertThat(mCachedDevice.getConnectionSummary()).isNull();
|
||||||
|
|
||||||
// Set device as Active for Hearing Aid and test connection state summary
|
// Set device as Active for Hearing Aid and test connection state summary
|
||||||
|
mCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT);
|
||||||
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
|
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
|
||||||
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
|
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active, left only");
|
||||||
|
|
||||||
// Set Hearing Aid profile to be disconnected and test connection state summary
|
// Set Hearing Aid profile to be disconnected and test connection state summary
|
||||||
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
|
mCachedDevice.onActiveDeviceChanged(false, BluetoothProfile.HEARING_AID);
|
||||||
@@ -390,17 +394,36 @@ public class CachedBluetoothDeviceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getConnectionSummary_testHearingAidInCall_returnActive() {
|
public void getConnectionSummary_testHearingAidRightEarInCall_returnActiveRightEar() {
|
||||||
// Arrange:
|
// Arrange:
|
||||||
// 1. Profile: {HEARING_AID, Connected, Active}
|
// 1. Profile: {HEARING_AID, Connected, Active, Right ear}
|
||||||
// 2. Audio Manager: In Call
|
// 2. Audio Manager: In Call
|
||||||
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
|
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
|
||||||
|
mCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_RIGHT);
|
||||||
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
|
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
|
||||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||||
|
|
||||||
// Act & Assert:
|
// Act & Assert:
|
||||||
// Get "Active" result without Battery Level.
|
// Get "Active" result without Battery Level.
|
||||||
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active");
|
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active, right only");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getConnectionSummary_testHearingAidBothEarInCall_returnActiveBothEar() {
|
||||||
|
// Arrange:
|
||||||
|
// 1. Profile: {HEARING_AID, Connected, Active, Both ear}
|
||||||
|
// 2. Audio Manager: In Call
|
||||||
|
mCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_RIGHT);
|
||||||
|
updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
|
||||||
|
mSubCachedDevice.setDeviceSide(HearingAidProfile.DeviceSide.SIDE_LEFT);
|
||||||
|
updateSubDeviceProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
|
||||||
|
mCachedDevice.setSubDevice(mSubCachedDevice);
|
||||||
|
mCachedDevice.onActiveDeviceChanged(true, BluetoothProfile.HEARING_AID);
|
||||||
|
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||||
|
|
||||||
|
// Act & Assert:
|
||||||
|
// Get "Active" result without Battery Level.
|
||||||
|
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo("Active, left and right");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -925,39 +948,41 @@ public class CachedBluetoothDeviceTest {
|
|||||||
mCachedDevice.onProfileStateChanged(profile, status);
|
mCachedDevice.onProfileStateChanged(profile, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateSubDeviceProfileStatus(LocalBluetoothProfile profile, int status) {
|
||||||
|
doReturn(status).when(profile).getConnectionStatus(mSubDevice);
|
||||||
|
mSubCachedDevice.onProfileStateChanged(profile, status);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getSubDevice_setSubDevice() {
|
public void getSubDevice_setSubDevice() {
|
||||||
CachedBluetoothDevice subCachedDevice = new CachedBluetoothDevice(mContext, mProfileManager,
|
mCachedDevice.setSubDevice(mSubCachedDevice);
|
||||||
mSubDevice);
|
|
||||||
mCachedDevice.setSubDevice(subCachedDevice);
|
|
||||||
|
|
||||||
assertThat(mCachedDevice.getSubDevice()).isEqualTo(subCachedDevice);
|
assertThat(mCachedDevice.getSubDevice()).isEqualTo(mSubCachedDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void switchSubDeviceContent() {
|
public void switchSubDeviceContent() {
|
||||||
CachedBluetoothDevice subCachedDevice = new CachedBluetoothDevice(mContext, mProfileManager,
|
|
||||||
mSubDevice);
|
|
||||||
mCachedDevice.mRssi = RSSI_1;
|
mCachedDevice.mRssi = RSSI_1;
|
||||||
mCachedDevice.mJustDiscovered = JUSTDISCOVERED_1;
|
mCachedDevice.mJustDiscovered = JUSTDISCOVERED_1;
|
||||||
subCachedDevice.mRssi = RSSI_2;
|
mSubCachedDevice.mRssi = RSSI_2;
|
||||||
subCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
|
mSubCachedDevice.mJustDiscovered = JUSTDISCOVERED_2;
|
||||||
mCachedDevice.setSubDevice(subCachedDevice);
|
mCachedDevice.setSubDevice(mSubCachedDevice);
|
||||||
|
|
||||||
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_1);
|
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_1);
|
||||||
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
|
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
|
||||||
assertThat(mCachedDevice.mDevice).isEqualTo(mDevice);
|
assertThat(mCachedDevice.mDevice).isEqualTo(mDevice);
|
||||||
assertThat(subCachedDevice.mRssi).isEqualTo(RSSI_2);
|
assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_2);
|
||||||
assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
|
assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
|
||||||
assertThat(subCachedDevice.mDevice).isEqualTo(mSubDevice);
|
assertThat(mSubCachedDevice.mDevice).isEqualTo(mSubDevice);
|
||||||
mCachedDevice.switchSubDeviceContent();
|
mCachedDevice.switchSubDeviceContent();
|
||||||
|
|
||||||
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
|
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
|
||||||
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
|
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
|
||||||
assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
|
assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
|
||||||
assertThat(subCachedDevice.mRssi).isEqualTo(RSSI_1);
|
assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_1);
|
||||||
assertThat(subCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
|
assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
|
||||||
assertThat(subCachedDevice.mDevice).isEqualTo(mDevice);
|
assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user