diff options
8 files changed, 160 insertions, 17 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java index 0946181ab710..0ff70a3dc9f7 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java @@ -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; } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java index 4c41b490d403..ac3599cae05b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java @@ -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); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index f57d02bb92fa..3cda9c9e3789 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -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); + } + } + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 9caff100cb9d..fb0f75b522b3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -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; } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java index d45fe1a38a22..ee1219126fe3 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java @@ -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; diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java index 22674cb6de9b..cda4e454fe74 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java @@ -239,4 +239,8 @@ public class LocalBluetoothAdapter { public BluetoothDevice getRemoteDevice(String address) { return mAdapter.getRemoteDevice(address); } + + public int getMaxConnectedAudioDevices() { + return mAdapter.getMaxConnectedAudioDevices(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java index 4b775a5a61e4..b8411e2963bd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java +++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java @@ -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 { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 3b15c2b8253f..fcf084b29327 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -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) { |