Merge "Add Settings support for Bluetooth Multi-A2DP and Multi-HFP"

This commit is contained in:
Treehugger Robot
2018-01-26 20:46:45 +00:00
committed by Gerrit Code Review
8 changed files with 160 additions and 17 deletions

View File

@@ -129,14 +129,18 @@ public class A2dpProfile implements LocalBluetoothProfile {
public boolean connect(BluetoothDevice device) {
if (mService == null) return false;
List<BluetoothDevice> sinks = getConnectedDevices();
if (sinks != null) {
for (BluetoothDevice sink : sinks) {
if (sink.equals(device)) {
Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
continue;
int max_connected_devices = mLocalAdapter.getMaxConnectedAudioDevices();
if (max_connected_devices == 1) {
// Original behavior: disconnect currently connected device
List<BluetoothDevice> sinks = getConnectedDevices();
if (sinks != null) {
for (BluetoothDevice sink : sinks) {
if (sink.equals(device)) {
Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
continue;
}
mService.disconnect(sink);
}
mService.disconnect(sink);
}
}
return mService.connect(device);
@@ -158,6 +162,16 @@ public class A2dpProfile implements LocalBluetoothProfile {
return mService.getConnectionState(device);
}
public boolean setActiveDevice(BluetoothDevice device) {
if (mService == null) return false;
return mService.setActiveDevice(device);
}
public BluetoothDevice getActiveDevice() {
if (mService == null) return null;
return mService.getActiveDevice();
}
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
@@ -181,8 +195,8 @@ public class A2dpProfile implements LocalBluetoothProfile {
boolean isA2dpPlaying() {
if (mService == null) return false;
List<BluetoothDevice> sinks = mService.getConnectedDevices();
if (!sinks.isEmpty()) {
if (mService.isA2dpPlaying(sinks.get(0))) {
for (BluetoothDevice device : sinks) {
if (mService.isA2dpPlaying(device)) {
return true;
}
}

View File

@@ -28,4 +28,5 @@ public interface BluetoothCallback {
void onDeviceDeleted(CachedBluetoothDevice cachedDevice);
void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
}

View File

@@ -16,9 +16,12 @@
package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -31,6 +34,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
@@ -106,6 +110,12 @@ public class BluetoothEventManager {
// Dock event broadcasts
addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
// Active device broadcasts
addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED,
new ActiveDeviceChangedHandler());
addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED,
new ActiveDeviceChangedHandler());
mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
}
@@ -409,4 +419,35 @@ public class BluetoothEventManager {
return deviceAdded;
}
private class ActiveDeviceChangedHandler implements Handler {
@Override
public void onReceive(Context context, Intent intent, BluetoothDevice device) {
String action = intent.getAction();
if (action == null) {
Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
return;
}
CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
int bluetoothProfile = 0;
if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
bluetoothProfile = BluetoothProfile.A2DP;
} else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
bluetoothProfile = BluetoothProfile.HEADSET;
} else {
Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action);
return;
}
dispatchActiveDeviceChanged(activeDevice, bluetoothProfile);
}
}
private void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
synchronized (mCallbacks) {
for (BluetoothCallback callback : mCallbacks) {
callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
}
}
}
}

View File

@@ -105,6 +105,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
private static final long MAX_UUID_DELAY_FOR_AUTO_CONNECT = 5000;
private static final long MAX_HOGP_DELAY_FOR_AUTO_CONNECT = 30000;
// Active device state
private boolean mIsActiveDeviceA2dp = false;
private boolean mIsActiveDeviceHeadset = false;
/**
* Describes the current device and profile for logging.
*
@@ -156,6 +160,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
mRemovedProfiles.add(profile);
mLocalNapRoleConnected = false;
}
fetchActiveDevices();
}
CachedBluetoothDevice(Context context,
@@ -359,6 +364,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
fetchName();
fetchBtClass();
updateProfiles();
fetchActiveDevices();
migratePhonebookPermissionChoice();
migrateMessagePermissionChoice();
fetchMessageRejectionCount();
@@ -454,6 +460,33 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
return mDevice.getBondState();
}
/**
* Set the device status as active or non-active per Bluetooth profile.
*
* @param isActive true if the device is active
* @param bluetoothProfile the Bluetooth profile
*/
public void setActiveDevice(boolean isActive, int bluetoothProfile) {
boolean changed = false;
switch (bluetoothProfile) {
case BluetoothProfile.A2DP:
changed = (mIsActiveDeviceA2dp != isActive);
mIsActiveDeviceA2dp = isActive;
break;
case BluetoothProfile.HEADSET:
changed = (mIsActiveDeviceHeadset != isActive);
mIsActiveDeviceHeadset = isActive;
break;
default:
Log.w(TAG, "setActiveDevice: unknown profile " + bluetoothProfile +
" isActive " + isActive);
break;
}
if (changed) {
dispatchAttributesChanged();
}
}
void setRssi(short rssi) {
if (mRssi != rssi) {
mRssi = rssi;
@@ -529,6 +562,17 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
return true;
}
private void fetchActiveDevices() {
A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
if (a2dpProfile != null) {
mIsActiveDeviceA2dp = mDevice.equals(a2dpProfile.getActiveDevice());
}
HeadsetProfile headsetProfile = mProfileManager.getHeadsetProfile();
if (headsetProfile != null) {
mIsActiveDeviceHeadset = mDevice.equals(headsetProfile.getActiveDevice());
}
}
/**
* Refreshes the UI for the BT class, including fetching the latest value
* for the class.
@@ -896,37 +940,60 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
com.android.settingslib.Utils.formatPercentage(batteryLevel);
}
// TODO: A temporary workaround solution using string description the device is active.
// Issue tracked by b/72317067 .
// An alternative solution would be visual indication.
// Intentionally not adding the strings to strings.xml for now:
// 1) If this is just a short-term solution, no need to waste translation effort
// 2) The number of strings with all possible combinations becomes enormously large.
// If string description becomes part of the final solution, we MUST NOT
// concatenate the strings here: this does not translate well.
String activeString = null;
if (mIsActiveDeviceA2dp && mIsActiveDeviceHeadset) {
activeString = ", active";
} else {
if (mIsActiveDeviceA2dp) {
activeString = ", active(media)";
}
if (mIsActiveDeviceHeadset) {
activeString = ", active(phone)";
}
}
if (activeString == null) activeString = "";
if (profileConnected) {
if (a2dpNotConnected && hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(
R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
batteryLevelPercentageString);
batteryLevelPercentageString) + activeString;
} else {
return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp) +
activeString;
}
} else if (a2dpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
batteryLevelPercentageString);
batteryLevelPercentageString) + activeString;
} else {
return mContext.getString(R.string.bluetooth_connected_no_a2dp);
return mContext.getString(R.string.bluetooth_connected_no_a2dp) + activeString;
}
} else if (hfpNotConnected) {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
batteryLevelPercentageString);
batteryLevelPercentageString) + activeString;
} else {
return mContext.getString(R.string.bluetooth_connected_no_headset);
return mContext.getString(R.string.bluetooth_connected_no_headset)
+ activeString;
}
} else {
if (batteryLevelPercentageString != null) {
return mContext.getString(R.string.bluetooth_connected_battery_level,
batteryLevelPercentageString);
batteryLevelPercentageString) + activeString;
} else {
return mContext.getString(R.string.bluetooth_connected);
return mContext.getString(R.string.bluetooth_connected) + activeString;
}
}
}

View File

@@ -153,6 +153,16 @@ public class HeadsetProfile implements LocalBluetoothProfile {
return BluetoothProfile.STATE_DISCONNECTED;
}
public boolean setActiveDevice(BluetoothDevice device) {
if (mService == null) return false;
return mService.setActiveDevice(device);
}
public BluetoothDevice getActiveDevice() {
if (mService == null) return null;
return mService.getActiveDevice();
}
public boolean isPreferred(BluetoothDevice device) {
if (mService == null) return false;
return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;

View File

@@ -239,4 +239,8 @@ public class LocalBluetoothAdapter {
public BluetoothDevice getRemoteDevice(String address) {
return mAdapter.getRemoteDevice(address);
}
public int getMaxConnectedAudioDevices() {
return mAdapter.getMaxConnectedAudioDevices();
}
}

View File

@@ -608,6 +608,9 @@ public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeCha
public void onScanningStateChanged(boolean started) { }
@Override
public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
@Override
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice,
int bluetoothProfile) { }
}
private final class BluetoothErrorListener implements Utils.ErrorListener {

View File

@@ -276,6 +276,9 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
ActuallyCachedState state = mCachedState.get(device);
if (state == null) {