Loading AndroidManifest.xml +3 −0 Original line number Diff line number Diff line Loading @@ -315,6 +315,9 @@ <intent-filter> <action android:name="android.bluetooth.IBluetoothHeadsetPhone"/> </intent-filter> <intent-filter> <action android:name="android.bluetooth.IBluetoothLeCallControlCallback" /> </intent-filter> </service> <service android:name=".components.TelecomService" Loading src/com/android/server/telecom/CallAudioRouteStateMachine.java +2 −2 Original line number Diff line number Diff line Loading @@ -875,8 +875,8 @@ public class CallAudioRouteStateMachine extends StateMachine { return HANDLED; case SWITCH_FOCUS: if (msg.arg1 == NO_FOCUS) { // Only disconnect SCO audio here instead of routing away from BT entirely. mBluetoothRouteManager.disconnectSco(); // Only disconnect audio here instead of routing away from BT entirely. mBluetoothRouteManager.disconnectAudio(); reinitialize(); mCallAudioManager.notifyAudioOperationsComplete(); } else if (msg.arg1 == RINGING_FOCUS Loading src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java +149 −23 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.telecom.Log; Loading @@ -37,6 +38,11 @@ import java.util.List; import java.util.Set; public class BluetoothDeviceManager { public static final int DEVICE_TYPE_HEADSET = 0; public static final int DEVICE_TYPE_HEARING_AID = 1; public static final int DEVICE_TYPE_LE_AUDIO = 2; private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { @Override Loading @@ -52,6 +58,10 @@ public class BluetoothDeviceManager { mBluetoothHearingAid = (BluetoothHearingAid) proxy; logString = "Got BluetoothHearingAid: " + mBluetoothHearingAid; } else if (profile == BluetoothProfile.LE_AUDIO) { mBluetoothLeAudioService = (BluetoothLeAudio) proxy; logString = "Got BluetoothLeAudio: " + mBluetoothLeAudioService; } else { logString = "Connected to non-requested bluetooth service." + " Not changing bluetooth headset."; Loading @@ -74,7 +84,8 @@ public class BluetoothDeviceManager { if (profile == BluetoothProfile.HEADSET) { mBluetoothHeadset = null; lostServiceDevices = mHfpDevicesByAddress; mBluetoothRouteManager.onActiveDeviceChanged(null, false); mBluetoothRouteManager.onActiveDeviceChanged(null, DEVICE_TYPE_HEADSET); logString = "Lost BluetoothHeadset service. " + "Removing all tracked devices"; } else if (profile == BluetoothProfile.HEARING_AID) { Loading @@ -82,7 +93,15 @@ public class BluetoothDeviceManager { logString = "Lost BluetoothHearingAid service. " + "Removing all tracked devices."; lostServiceDevices = mHearingAidDevicesByAddress; mBluetoothRouteManager.onActiveDeviceChanged(null, true); mBluetoothRouteManager.onActiveDeviceChanged(null, DEVICE_TYPE_HEARING_AID); } else if (profile == BluetoothProfile.LE_AUDIO) { mBluetoothLeAudioService = null; logString = "Lost BluetoothLeAudio service. " + "Removing all tracked devices."; lostServiceDevices = mLeAudioDevicesByAddress; mBluetoothRouteManager.onActiveDeviceChanged(null, DEVICE_TYPE_LE_AUDIO); } else { return; } Loading @@ -108,6 +127,12 @@ public class BluetoothDeviceManager { new LinkedHashMap<>(); private final LinkedHashMap<BluetoothDevice, Long> mHearingAidDeviceSyncIds = new LinkedHashMap<>(); private final LinkedHashMap<String, BluetoothDevice> mLeAudioDevicesByAddress = new LinkedHashMap<>(); private final LinkedHashMap<BluetoothDevice, Integer> mGroupsByDevice = new LinkedHashMap<>(); private int mGroupIdActive = BluetoothLeAudio.GROUP_ID_INVALID; private int mGroupIdPending = BluetoothLeAudio.GROUP_ID_INVALID; private final LocalLog mLocalLog = new LocalLog(20); // This lock only protects internal state -- it doesn't lock on anything going into Telecom. Loading @@ -116,6 +141,7 @@ public class BluetoothDeviceManager { private BluetoothRouteManager mBluetoothRouteManager; private BluetoothHeadset mBluetoothHeadset; private BluetoothHearingAid mBluetoothHearingAid; private BluetoothLeAudio mBluetoothLeAudioService; private BluetoothDevice mBluetoothHearingAidActiveDeviceCache; private BluetoothAdapter mBluetoothAdapter; Loading @@ -126,6 +152,8 @@ public class BluetoothDeviceManager { BluetoothProfile.HEADSET); bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID); bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO); } } Loading @@ -133,9 +161,20 @@ public class BluetoothDeviceManager { mBluetoothRouteManager = brm; } private List<BluetoothDevice> getLeAudioConnectedDevices() { synchronized (mLock) { // Filter out disconnected devices and/or those that have no group assigned ArrayList<BluetoothDevice> devices = new ArrayList<>(mGroupsByDevice.keySet()); devices.removeIf(device -> !mLeAudioDevicesByAddress.containsValue(device)); return devices; } } public int getNumConnectedDevices() { synchronized (mLock) { return mHfpDevicesByAddress.size() + mHearingAidDevicesByAddress.size(); return mHfpDevicesByAddress.size() + mHearingAidDevicesByAddress.size() + getLeAudioConnectedDevices().size(); } } Loading @@ -143,6 +182,7 @@ public class BluetoothDeviceManager { synchronized (mLock) { ArrayList<BluetoothDevice> result = new ArrayList<>(mHfpDevicesByAddress.values()); result.addAll(mHearingAidDevicesByAddress.values()); result.addAll(getLeAudioConnectedDevices()); return Collections.unmodifiableCollection(result); } } Loading Loading @@ -177,6 +217,31 @@ public class BluetoothDeviceManager { seenHiSyncIds.add(hiSyncId); } } Set<Integer> seenGroupIds = new LinkedHashSet<>(); if (mBluetoothAdapter != null) { for (BluetoothDevice device : mBluetoothAdapter.getActiveDevices( BluetoothProfile.LE_AUDIO)) { if (device != null) { result.add(device); seenGroupIds.add(mGroupsByDevice.getOrDefault(device, -1)); break; } } } synchronized (mLock) { for (BluetoothDevice d : getLeAudioConnectedDevices()) { int groupId = mGroupsByDevice.getOrDefault(d, BluetoothLeAudio.GROUP_ID_INVALID); if (groupId == BluetoothLeAudio.GROUP_ID_INVALID || seenGroupIds.contains(groupId)) { continue; } result.add(d); seenGroupIds.add(groupId); } } return Collections.unmodifiableCollection(result); } Loading @@ -192,6 +257,10 @@ public class BluetoothDeviceManager { return mBluetoothHearingAid; } public BluetoothLeAudio getLeAudioService() { return mBluetoothLeAudioService; } public void setHeadsetServiceForTesting(BluetoothHeadset bluetoothHeadset) { mBluetoothHeadset = bluetoothHeadset; } Loading @@ -200,12 +269,33 @@ public class BluetoothDeviceManager { mBluetoothHearingAid = bluetoothHearingAid; } void onDeviceConnected(BluetoothDevice device, boolean isHearingAid) { mLocalLog.log("Device connected -- address: " + device.getAddress() + " isHeadingAid: " + isHearingAid); public void setLeAudioServiceForTesting(BluetoothLeAudio bluetoothLeAudio) { mBluetoothLeAudioService = bluetoothLeAudio; } public static String getDeviceTypeString(int deviceType) { switch (deviceType) { case DEVICE_TYPE_LE_AUDIO: return "LeAudio"; case DEVICE_TYPE_HEARING_AID: return "HearingAid"; case DEVICE_TYPE_HEADSET: return "HFP"; default: return "unknown type"; } } void onDeviceConnected(BluetoothDevice device, int deviceType) { synchronized (mLock) { LinkedHashMap<String, BluetoothDevice> targetDeviceMap; if (isHearingAid) { if (deviceType == DEVICE_TYPE_LE_AUDIO) { if (mBluetoothLeAudioService == null) { Log.w(this, "LE audio service null when receiving device added broadcast"); return; } targetDeviceMap = mLeAudioDevicesByAddress; } else if (deviceType == DEVICE_TYPE_HEARING_AID) { if (mBluetoothHearingAid == null) { Log.w(this, "Hearing aid service null when receiving device added broadcast"); return; Loading @@ -213,12 +303,16 @@ public class BluetoothDeviceManager { long hiSyncId = mBluetoothHearingAid.getHiSyncId(device); mHearingAidDeviceSyncIds.put(device, hiSyncId); targetDeviceMap = mHearingAidDevicesByAddress; } else { } else if (deviceType == DEVICE_TYPE_HEADSET) { if (mBluetoothHeadset == null) { Log.w(this, "Headset service null when receiving device added broadcast"); return; } targetDeviceMap = mHfpDevicesByAddress; } else { Log.w(this, "Device: " + device.getAddress() + " with invalid type: " + getDeviceTypeString(deviceType)); return; } if (!targetDeviceMap.containsKey(device.getAddress())) { targetDeviceMap.put(device.getAddress(), device); Loading @@ -227,16 +321,22 @@ public class BluetoothDeviceManager { } } void onDeviceDisconnected(BluetoothDevice device, boolean isHearingAid) { mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " isHeadingAid: " + isHearingAid); void onDeviceDisconnected(BluetoothDevice device, int deviceType) { mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " deviceType: " + deviceType); synchronized (mLock) { LinkedHashMap<String, BluetoothDevice> targetDeviceMap; if (isHearingAid) { if (deviceType == DEVICE_TYPE_LE_AUDIO) { targetDeviceMap = mLeAudioDevicesByAddress; } else if (deviceType == DEVICE_TYPE_HEARING_AID) { mHearingAidDeviceSyncIds.remove(device); targetDeviceMap = mHearingAidDevicesByAddress; } else { } else if (deviceType == DEVICE_TYPE_HEADSET) { targetDeviceMap = mHfpDevicesByAddress; } else { Log.w(this, "Device: " + device.getAddress() + " with invalid type: " + getDeviceTypeString(deviceType)); return; } if (targetDeviceMap.containsKey(device.getAddress())) { targetDeviceMap.remove(device.getAddress()); Loading @@ -245,17 +345,35 @@ public class BluetoothDeviceManager { } } public void disconnectAudio() { if (mBluetoothAdapter != null) { for (BluetoothDevice device: mBluetoothAdapter.getActiveDevices( BluetoothProfile.HEARING_AID)) { if (device != null) { mBluetoothAdapter.removeActiveDevice(BluetoothAdapter.ACTIVE_DEVICE_ALL); void onGroupNodeAdded(BluetoothDevice device, int groupId) { Log.i(this, device.getAddress() + " group added " + groupId); if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) { Log.w(this, "invalid parameter"); return; } synchronized (mLock) { mGroupsByDevice.put(device, groupId); } } void onGroupNodeRemoved(BluetoothDevice device, int groupId) { if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) { Log.w(this, "invalid parameter"); return; } synchronized (mLock) { mGroupsByDevice.remove(device); } } public void disconnectAudio() { if (mBluetoothAdapter != null) { mBluetoothAdapter.removeActiveDevice(BluetoothAdapter.ACTIVE_DEVICE_ALL); disconnectSco(); } } public void disconnectSco() { if (mBluetoothHeadset == null) { Loading @@ -265,10 +383,18 @@ public class BluetoothDeviceManager { } } // Connect audio to the bluetooth device at address, checking to see whether it's a hearing aid // or a HFP device, and using the proper BT API. // Connect audio to the bluetooth device at address, checking to see whether it's // le audio, hearing aid or a HFP device, and using the proper BT API. public boolean connectAudio(String address) { if (mHearingAidDevicesByAddress.containsKey(address)) { if (mLeAudioDevicesByAddress.containsKey(address)) { if (mBluetoothLeAudioService == null) { Log.w(this, "Attempting to turn on audio when the le audio service is null"); return false; } BluetoothDevice device = mLeAudioDevicesByAddress.get(address); return mBluetoothAdapter.setActiveDevice( device, BluetoothAdapter.ACTIVE_DEVICE_ALL); } else if (mHearingAidDevicesByAddress.containsKey(address)) { if (mBluetoothHearingAid == null) { Log.w(this, "Attempting to turn on audio when the hearing aid service is null"); return false; Loading src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java +75 −25 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothLeAudio; import android.content.Context; import android.os.Message; import android.telecom.Log; Loading @@ -35,10 +36,12 @@ import com.android.internal.util.StateMachine; import com.android.server.telecom.TelecomSystem; import com.android.server.telecom.Timeouts; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; Loading Loading @@ -294,7 +297,7 @@ public class BluetoothRouteManager extends StateMachine { break; case BT_AUDIO_IS_ON: if (Objects.equals(mDeviceAddress, address)) { Log.i(LOG_TAG, "HFP connection success for device %s.", mDeviceAddress); Log.i(LOG_TAG, "BT connection success for device %s.", mDeviceAddress); transitionTo(mAudioConnectedStates.get(mDeviceAddress)); } else { Log.w(LOG_TAG, "In connecting state for device %s but %s" + Loading Loading @@ -451,6 +454,7 @@ public class BluetoothRouteManager extends StateMachine { // Tracks the active devices in the BT stack (HFP or hearing aid). private BluetoothDevice mHfpActiveDeviceCache = null; private BluetoothDevice mHearingAidActiveDeviceCache = null; private BluetoothDevice mLeAudioActiveDeviceCache = null; private BluetoothDevice mMostRecentlyReportedActiveDevice = null; public BluetoothRouteManager(Context context, TelecomSystem.SyncRoot lock, Loading Loading @@ -548,8 +552,8 @@ public class BluetoothRouteManager extends StateMachine { sendMessage(DISCONNECT_HFP, args); } public void disconnectSco() { mDeviceManager.disconnectSco(); public void disconnectAudio() { mDeviceManager.disconnectAudio(); } public void cacheHearingAidDevice() { Loading Loading @@ -582,19 +586,37 @@ public class BluetoothRouteManager extends StateMachine { mListener.onBluetoothDeviceListChanged(); } public void onActiveDeviceChanged(BluetoothDevice device, boolean isHearingAid) { boolean wasActiveDevicePresent = mHearingAidActiveDeviceCache != null || mHfpActiveDeviceCache != null; if (isHearingAid) { public void onAudioOn(String address) { Session session = Log.createSubsession(); SomeArgs args = SomeArgs.obtain(); args.arg1 = session; args.arg2 = address; sendMessage(BT_AUDIO_IS_ON, args); } public void onAudioLost(String address) { Session session = Log.createSubsession(); SomeArgs args = SomeArgs.obtain(); args.arg1 = session; args.arg2 = address; sendMessage(BT_AUDIO_LOST, args); } public void onActiveDeviceChanged(BluetoothDevice device, int deviceType) { boolean wasActiveDevicePresent = hasBtActiveDevice(); if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) { mLeAudioActiveDeviceCache = device; } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID) { mHearingAidActiveDeviceCache = device; } else { } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEADSET) { mHfpActiveDeviceCache = device; } else { return; } if (device != null) mMostRecentlyReportedActiveDevice = device; boolean isActiveDevicePresent = mHearingAidActiveDeviceCache != null || mHfpActiveDeviceCache != null; boolean isActiveDevicePresent = hasBtActiveDevice(); if (wasActiveDevicePresent && !isActiveDevicePresent) { mListener.onBluetoothActiveDeviceGone(); Loading @@ -604,7 +626,9 @@ public class BluetoothRouteManager extends StateMachine { } public boolean hasBtActiveDevice() { return mHearingAidActiveDeviceCache != null || mHfpActiveDeviceCache != null; return mLeAudioActiveDeviceCache != null || mHearingAidActiveDeviceCache != null || mHfpActiveDeviceCache != null; } public Collection<BluetoothDevice> getConnectedDevices() { Loading Loading @@ -682,6 +706,9 @@ public class BluetoothRouteManager extends StateMachine { if (mHearingAidActiveDeviceCache != null) { return mHearingAidActiveDeviceCache.getAddress(); } if (mLeAudioActiveDeviceCache != null) { return mLeAudioActiveDeviceCache.getAddress(); } return null; } Loading @@ -705,29 +732,33 @@ public class BluetoothRouteManager extends StateMachine { BluetoothAdapter bluetoothAdapter = mDeviceManager.getBluetoothAdapter(); BluetoothHeadset bluetoothHeadset = mDeviceManager.getBluetoothHeadset(); BluetoothHearingAid bluetoothHearingAid = mDeviceManager.getBluetoothHearingAid(); BluetoothLeAudio bluetoothLeAudio = mDeviceManager.getLeAudioService(); BluetoothDevice hfpAudioOnDevice = null; BluetoothDevice hearingAidActiveDevice = null; BluetoothDevice leAudioActiveDevice = null; if (bluetoothAdapter == null) { Log.i(this, "getBluetoothAudioConnectedDevice: no adapter available."); return null; } if (bluetoothHeadset == null && bluetoothHearingAid == null) { if (bluetoothHeadset == null && bluetoothHearingAid == null && bluetoothLeAudio == null) { Log.i(this, "getBluetoothAudioConnectedDevice: no service available."); return null; } int activeDevices = 0; if (bluetoothHeadset != null) { for (BluetoothDevice device : bluetoothAdapter.getActiveDevices( BluetoothProfile.HEADSET)) { hfpAudioOnDevice = device; break; } if (bluetoothHeadset.getAudioState(hfpAudioOnDevice) == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { hfpAudioOnDevice = null; } else { activeDevices++; } } Loading @@ -736,24 +767,41 @@ public class BluetoothRouteManager extends StateMachine { BluetoothProfile.HEARING_AID)) { if (device != null) { hearingAidActiveDevice = device; activeDevices++; break; } } } // Return the active device reported by either HFP or hearing aid. If both are reporting // active devices, go with the most recent one as reported by the receiver. if (hfpAudioOnDevice != null) { if (hearingAidActiveDevice != null) { Log.i(this, "Both HFP and hearing aid are reporting active devices. Going with" + " the most recently reported active device: %s"); if (bluetoothLeAudio != null) { for (BluetoothDevice device : bluetoothLeAudio.getActiveDevices()) { if (device != null) { leAudioActiveDevice = device; activeDevices++; break; } } } // Return the active device reported by either HFP, hearing aid or le audio. If more than // one is reporting active devices, go with the most recent one as reported by the receiver. if (activeDevices > 1) { Log.i(this, "More than one profile reporting active devices. Going with the most" + " recently reported active device: %s", mMostRecentlyReportedActiveDevice); return mMostRecentlyReportedActiveDevice; } return hfpAudioOnDevice; if (leAudioActiveDevice != null) { return leAudioActiveDevice; } if (hearingAidActiveDevice != null) { return hearingAidActiveDevice; } return hfpAudioOnDevice; } /** * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an * active connection. Loading Loading @@ -847,10 +895,12 @@ public class BluetoothRouteManager extends StateMachine { } @VisibleForTesting public void setActiveDeviceCacheForTesting(BluetoothDevice device, boolean isHearingAid) { if (isHearingAid) { public void setActiveDeviceCacheForTesting(BluetoothDevice device, int deviceType) { if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) { mLeAudioActiveDeviceCache = device; } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID) { mHearingAidActiveDeviceCache = device; } else { } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEADSET) { mHfpActiveDeviceCache = device; } } Loading src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java +61 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
AndroidManifest.xml +3 −0 Original line number Diff line number Diff line Loading @@ -315,6 +315,9 @@ <intent-filter> <action android:name="android.bluetooth.IBluetoothHeadsetPhone"/> </intent-filter> <intent-filter> <action android:name="android.bluetooth.IBluetoothLeCallControlCallback" /> </intent-filter> </service> <service android:name=".components.TelecomService" Loading
src/com/android/server/telecom/CallAudioRouteStateMachine.java +2 −2 Original line number Diff line number Diff line Loading @@ -875,8 +875,8 @@ public class CallAudioRouteStateMachine extends StateMachine { return HANDLED; case SWITCH_FOCUS: if (msg.arg1 == NO_FOCUS) { // Only disconnect SCO audio here instead of routing away from BT entirely. mBluetoothRouteManager.disconnectSco(); // Only disconnect audio here instead of routing away from BT entirely. mBluetoothRouteManager.disconnectAudio(); reinitialize(); mCallAudioManager.notifyAudioOperationsComplete(); } else if (msg.arg1 == RINGING_FOCUS Loading
src/com/android/server/telecom/bluetooth/BluetoothDeviceManager.java +149 −23 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.telecom.Log; Loading @@ -37,6 +38,11 @@ import java.util.List; import java.util.Set; public class BluetoothDeviceManager { public static final int DEVICE_TYPE_HEADSET = 0; public static final int DEVICE_TYPE_HEARING_AID = 1; public static final int DEVICE_TYPE_LE_AUDIO = 2; private final BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { @Override Loading @@ -52,6 +58,10 @@ public class BluetoothDeviceManager { mBluetoothHearingAid = (BluetoothHearingAid) proxy; logString = "Got BluetoothHearingAid: " + mBluetoothHearingAid; } else if (profile == BluetoothProfile.LE_AUDIO) { mBluetoothLeAudioService = (BluetoothLeAudio) proxy; logString = "Got BluetoothLeAudio: " + mBluetoothLeAudioService; } else { logString = "Connected to non-requested bluetooth service." + " Not changing bluetooth headset."; Loading @@ -74,7 +84,8 @@ public class BluetoothDeviceManager { if (profile == BluetoothProfile.HEADSET) { mBluetoothHeadset = null; lostServiceDevices = mHfpDevicesByAddress; mBluetoothRouteManager.onActiveDeviceChanged(null, false); mBluetoothRouteManager.onActiveDeviceChanged(null, DEVICE_TYPE_HEADSET); logString = "Lost BluetoothHeadset service. " + "Removing all tracked devices"; } else if (profile == BluetoothProfile.HEARING_AID) { Loading @@ -82,7 +93,15 @@ public class BluetoothDeviceManager { logString = "Lost BluetoothHearingAid service. " + "Removing all tracked devices."; lostServiceDevices = mHearingAidDevicesByAddress; mBluetoothRouteManager.onActiveDeviceChanged(null, true); mBluetoothRouteManager.onActiveDeviceChanged(null, DEVICE_TYPE_HEARING_AID); } else if (profile == BluetoothProfile.LE_AUDIO) { mBluetoothLeAudioService = null; logString = "Lost BluetoothLeAudio service. " + "Removing all tracked devices."; lostServiceDevices = mLeAudioDevicesByAddress; mBluetoothRouteManager.onActiveDeviceChanged(null, DEVICE_TYPE_LE_AUDIO); } else { return; } Loading @@ -108,6 +127,12 @@ public class BluetoothDeviceManager { new LinkedHashMap<>(); private final LinkedHashMap<BluetoothDevice, Long> mHearingAidDeviceSyncIds = new LinkedHashMap<>(); private final LinkedHashMap<String, BluetoothDevice> mLeAudioDevicesByAddress = new LinkedHashMap<>(); private final LinkedHashMap<BluetoothDevice, Integer> mGroupsByDevice = new LinkedHashMap<>(); private int mGroupIdActive = BluetoothLeAudio.GROUP_ID_INVALID; private int mGroupIdPending = BluetoothLeAudio.GROUP_ID_INVALID; private final LocalLog mLocalLog = new LocalLog(20); // This lock only protects internal state -- it doesn't lock on anything going into Telecom. Loading @@ -116,6 +141,7 @@ public class BluetoothDeviceManager { private BluetoothRouteManager mBluetoothRouteManager; private BluetoothHeadset mBluetoothHeadset; private BluetoothHearingAid mBluetoothHearingAid; private BluetoothLeAudio mBluetoothLeAudioService; private BluetoothDevice mBluetoothHearingAidActiveDeviceCache; private BluetoothAdapter mBluetoothAdapter; Loading @@ -126,6 +152,8 @@ public class BluetoothDeviceManager { BluetoothProfile.HEADSET); bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID); bluetoothAdapter.getProfileProxy(context, mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO); } } Loading @@ -133,9 +161,20 @@ public class BluetoothDeviceManager { mBluetoothRouteManager = brm; } private List<BluetoothDevice> getLeAudioConnectedDevices() { synchronized (mLock) { // Filter out disconnected devices and/or those that have no group assigned ArrayList<BluetoothDevice> devices = new ArrayList<>(mGroupsByDevice.keySet()); devices.removeIf(device -> !mLeAudioDevicesByAddress.containsValue(device)); return devices; } } public int getNumConnectedDevices() { synchronized (mLock) { return mHfpDevicesByAddress.size() + mHearingAidDevicesByAddress.size(); return mHfpDevicesByAddress.size() + mHearingAidDevicesByAddress.size() + getLeAudioConnectedDevices().size(); } } Loading @@ -143,6 +182,7 @@ public class BluetoothDeviceManager { synchronized (mLock) { ArrayList<BluetoothDevice> result = new ArrayList<>(mHfpDevicesByAddress.values()); result.addAll(mHearingAidDevicesByAddress.values()); result.addAll(getLeAudioConnectedDevices()); return Collections.unmodifiableCollection(result); } } Loading Loading @@ -177,6 +217,31 @@ public class BluetoothDeviceManager { seenHiSyncIds.add(hiSyncId); } } Set<Integer> seenGroupIds = new LinkedHashSet<>(); if (mBluetoothAdapter != null) { for (BluetoothDevice device : mBluetoothAdapter.getActiveDevices( BluetoothProfile.LE_AUDIO)) { if (device != null) { result.add(device); seenGroupIds.add(mGroupsByDevice.getOrDefault(device, -1)); break; } } } synchronized (mLock) { for (BluetoothDevice d : getLeAudioConnectedDevices()) { int groupId = mGroupsByDevice.getOrDefault(d, BluetoothLeAudio.GROUP_ID_INVALID); if (groupId == BluetoothLeAudio.GROUP_ID_INVALID || seenGroupIds.contains(groupId)) { continue; } result.add(d); seenGroupIds.add(groupId); } } return Collections.unmodifiableCollection(result); } Loading @@ -192,6 +257,10 @@ public class BluetoothDeviceManager { return mBluetoothHearingAid; } public BluetoothLeAudio getLeAudioService() { return mBluetoothLeAudioService; } public void setHeadsetServiceForTesting(BluetoothHeadset bluetoothHeadset) { mBluetoothHeadset = bluetoothHeadset; } Loading @@ -200,12 +269,33 @@ public class BluetoothDeviceManager { mBluetoothHearingAid = bluetoothHearingAid; } void onDeviceConnected(BluetoothDevice device, boolean isHearingAid) { mLocalLog.log("Device connected -- address: " + device.getAddress() + " isHeadingAid: " + isHearingAid); public void setLeAudioServiceForTesting(BluetoothLeAudio bluetoothLeAudio) { mBluetoothLeAudioService = bluetoothLeAudio; } public static String getDeviceTypeString(int deviceType) { switch (deviceType) { case DEVICE_TYPE_LE_AUDIO: return "LeAudio"; case DEVICE_TYPE_HEARING_AID: return "HearingAid"; case DEVICE_TYPE_HEADSET: return "HFP"; default: return "unknown type"; } } void onDeviceConnected(BluetoothDevice device, int deviceType) { synchronized (mLock) { LinkedHashMap<String, BluetoothDevice> targetDeviceMap; if (isHearingAid) { if (deviceType == DEVICE_TYPE_LE_AUDIO) { if (mBluetoothLeAudioService == null) { Log.w(this, "LE audio service null when receiving device added broadcast"); return; } targetDeviceMap = mLeAudioDevicesByAddress; } else if (deviceType == DEVICE_TYPE_HEARING_AID) { if (mBluetoothHearingAid == null) { Log.w(this, "Hearing aid service null when receiving device added broadcast"); return; Loading @@ -213,12 +303,16 @@ public class BluetoothDeviceManager { long hiSyncId = mBluetoothHearingAid.getHiSyncId(device); mHearingAidDeviceSyncIds.put(device, hiSyncId); targetDeviceMap = mHearingAidDevicesByAddress; } else { } else if (deviceType == DEVICE_TYPE_HEADSET) { if (mBluetoothHeadset == null) { Log.w(this, "Headset service null when receiving device added broadcast"); return; } targetDeviceMap = mHfpDevicesByAddress; } else { Log.w(this, "Device: " + device.getAddress() + " with invalid type: " + getDeviceTypeString(deviceType)); return; } if (!targetDeviceMap.containsKey(device.getAddress())) { targetDeviceMap.put(device.getAddress(), device); Loading @@ -227,16 +321,22 @@ public class BluetoothDeviceManager { } } void onDeviceDisconnected(BluetoothDevice device, boolean isHearingAid) { mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " isHeadingAid: " + isHearingAid); void onDeviceDisconnected(BluetoothDevice device, int deviceType) { mLocalLog.log("Device disconnected -- address: " + device.getAddress() + " deviceType: " + deviceType); synchronized (mLock) { LinkedHashMap<String, BluetoothDevice> targetDeviceMap; if (isHearingAid) { if (deviceType == DEVICE_TYPE_LE_AUDIO) { targetDeviceMap = mLeAudioDevicesByAddress; } else if (deviceType == DEVICE_TYPE_HEARING_AID) { mHearingAidDeviceSyncIds.remove(device); targetDeviceMap = mHearingAidDevicesByAddress; } else { } else if (deviceType == DEVICE_TYPE_HEADSET) { targetDeviceMap = mHfpDevicesByAddress; } else { Log.w(this, "Device: " + device.getAddress() + " with invalid type: " + getDeviceTypeString(deviceType)); return; } if (targetDeviceMap.containsKey(device.getAddress())) { targetDeviceMap.remove(device.getAddress()); Loading @@ -245,17 +345,35 @@ public class BluetoothDeviceManager { } } public void disconnectAudio() { if (mBluetoothAdapter != null) { for (BluetoothDevice device: mBluetoothAdapter.getActiveDevices( BluetoothProfile.HEARING_AID)) { if (device != null) { mBluetoothAdapter.removeActiveDevice(BluetoothAdapter.ACTIVE_DEVICE_ALL); void onGroupNodeAdded(BluetoothDevice device, int groupId) { Log.i(this, device.getAddress() + " group added " + groupId); if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) { Log.w(this, "invalid parameter"); return; } synchronized (mLock) { mGroupsByDevice.put(device, groupId); } } void onGroupNodeRemoved(BluetoothDevice device, int groupId) { if (device == null || groupId == BluetoothLeAudio.GROUP_ID_INVALID) { Log.w(this, "invalid parameter"); return; } synchronized (mLock) { mGroupsByDevice.remove(device); } } public void disconnectAudio() { if (mBluetoothAdapter != null) { mBluetoothAdapter.removeActiveDevice(BluetoothAdapter.ACTIVE_DEVICE_ALL); disconnectSco(); } } public void disconnectSco() { if (mBluetoothHeadset == null) { Loading @@ -265,10 +383,18 @@ public class BluetoothDeviceManager { } } // Connect audio to the bluetooth device at address, checking to see whether it's a hearing aid // or a HFP device, and using the proper BT API. // Connect audio to the bluetooth device at address, checking to see whether it's // le audio, hearing aid or a HFP device, and using the proper BT API. public boolean connectAudio(String address) { if (mHearingAidDevicesByAddress.containsKey(address)) { if (mLeAudioDevicesByAddress.containsKey(address)) { if (mBluetoothLeAudioService == null) { Log.w(this, "Attempting to turn on audio when the le audio service is null"); return false; } BluetoothDevice device = mLeAudioDevicesByAddress.get(address); return mBluetoothAdapter.setActiveDevice( device, BluetoothAdapter.ACTIVE_DEVICE_ALL); } else if (mHearingAidDevicesByAddress.containsKey(address)) { if (mBluetoothHearingAid == null) { Log.w(this, "Attempting to turn on audio when the hearing aid service is null"); return false; Loading
src/com/android/server/telecom/bluetooth/BluetoothRouteManager.java +75 −25 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothLeAudio; import android.content.Context; import android.os.Message; import android.telecom.Log; Loading @@ -35,10 +36,12 @@ import com.android.internal.util.StateMachine; import com.android.server.telecom.TelecomSystem; import com.android.server.telecom.Timeouts; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; Loading Loading @@ -294,7 +297,7 @@ public class BluetoothRouteManager extends StateMachine { break; case BT_AUDIO_IS_ON: if (Objects.equals(mDeviceAddress, address)) { Log.i(LOG_TAG, "HFP connection success for device %s.", mDeviceAddress); Log.i(LOG_TAG, "BT connection success for device %s.", mDeviceAddress); transitionTo(mAudioConnectedStates.get(mDeviceAddress)); } else { Log.w(LOG_TAG, "In connecting state for device %s but %s" + Loading Loading @@ -451,6 +454,7 @@ public class BluetoothRouteManager extends StateMachine { // Tracks the active devices in the BT stack (HFP or hearing aid). private BluetoothDevice mHfpActiveDeviceCache = null; private BluetoothDevice mHearingAidActiveDeviceCache = null; private BluetoothDevice mLeAudioActiveDeviceCache = null; private BluetoothDevice mMostRecentlyReportedActiveDevice = null; public BluetoothRouteManager(Context context, TelecomSystem.SyncRoot lock, Loading Loading @@ -548,8 +552,8 @@ public class BluetoothRouteManager extends StateMachine { sendMessage(DISCONNECT_HFP, args); } public void disconnectSco() { mDeviceManager.disconnectSco(); public void disconnectAudio() { mDeviceManager.disconnectAudio(); } public void cacheHearingAidDevice() { Loading Loading @@ -582,19 +586,37 @@ public class BluetoothRouteManager extends StateMachine { mListener.onBluetoothDeviceListChanged(); } public void onActiveDeviceChanged(BluetoothDevice device, boolean isHearingAid) { boolean wasActiveDevicePresent = mHearingAidActiveDeviceCache != null || mHfpActiveDeviceCache != null; if (isHearingAid) { public void onAudioOn(String address) { Session session = Log.createSubsession(); SomeArgs args = SomeArgs.obtain(); args.arg1 = session; args.arg2 = address; sendMessage(BT_AUDIO_IS_ON, args); } public void onAudioLost(String address) { Session session = Log.createSubsession(); SomeArgs args = SomeArgs.obtain(); args.arg1 = session; args.arg2 = address; sendMessage(BT_AUDIO_LOST, args); } public void onActiveDeviceChanged(BluetoothDevice device, int deviceType) { boolean wasActiveDevicePresent = hasBtActiveDevice(); if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) { mLeAudioActiveDeviceCache = device; } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID) { mHearingAidActiveDeviceCache = device; } else { } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEADSET) { mHfpActiveDeviceCache = device; } else { return; } if (device != null) mMostRecentlyReportedActiveDevice = device; boolean isActiveDevicePresent = mHearingAidActiveDeviceCache != null || mHfpActiveDeviceCache != null; boolean isActiveDevicePresent = hasBtActiveDevice(); if (wasActiveDevicePresent && !isActiveDevicePresent) { mListener.onBluetoothActiveDeviceGone(); Loading @@ -604,7 +626,9 @@ public class BluetoothRouteManager extends StateMachine { } public boolean hasBtActiveDevice() { return mHearingAidActiveDeviceCache != null || mHfpActiveDeviceCache != null; return mLeAudioActiveDeviceCache != null || mHearingAidActiveDeviceCache != null || mHfpActiveDeviceCache != null; } public Collection<BluetoothDevice> getConnectedDevices() { Loading Loading @@ -682,6 +706,9 @@ public class BluetoothRouteManager extends StateMachine { if (mHearingAidActiveDeviceCache != null) { return mHearingAidActiveDeviceCache.getAddress(); } if (mLeAudioActiveDeviceCache != null) { return mLeAudioActiveDeviceCache.getAddress(); } return null; } Loading @@ -705,29 +732,33 @@ public class BluetoothRouteManager extends StateMachine { BluetoothAdapter bluetoothAdapter = mDeviceManager.getBluetoothAdapter(); BluetoothHeadset bluetoothHeadset = mDeviceManager.getBluetoothHeadset(); BluetoothHearingAid bluetoothHearingAid = mDeviceManager.getBluetoothHearingAid(); BluetoothLeAudio bluetoothLeAudio = mDeviceManager.getLeAudioService(); BluetoothDevice hfpAudioOnDevice = null; BluetoothDevice hearingAidActiveDevice = null; BluetoothDevice leAudioActiveDevice = null; if (bluetoothAdapter == null) { Log.i(this, "getBluetoothAudioConnectedDevice: no adapter available."); return null; } if (bluetoothHeadset == null && bluetoothHearingAid == null) { if (bluetoothHeadset == null && bluetoothHearingAid == null && bluetoothLeAudio == null) { Log.i(this, "getBluetoothAudioConnectedDevice: no service available."); return null; } int activeDevices = 0; if (bluetoothHeadset != null) { for (BluetoothDevice device : bluetoothAdapter.getActiveDevices( BluetoothProfile.HEADSET)) { hfpAudioOnDevice = device; break; } if (bluetoothHeadset.getAudioState(hfpAudioOnDevice) == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { hfpAudioOnDevice = null; } else { activeDevices++; } } Loading @@ -736,24 +767,41 @@ public class BluetoothRouteManager extends StateMachine { BluetoothProfile.HEARING_AID)) { if (device != null) { hearingAidActiveDevice = device; activeDevices++; break; } } } // Return the active device reported by either HFP or hearing aid. If both are reporting // active devices, go with the most recent one as reported by the receiver. if (hfpAudioOnDevice != null) { if (hearingAidActiveDevice != null) { Log.i(this, "Both HFP and hearing aid are reporting active devices. Going with" + " the most recently reported active device: %s"); if (bluetoothLeAudio != null) { for (BluetoothDevice device : bluetoothLeAudio.getActiveDevices()) { if (device != null) { leAudioActiveDevice = device; activeDevices++; break; } } } // Return the active device reported by either HFP, hearing aid or le audio. If more than // one is reporting active devices, go with the most recent one as reported by the receiver. if (activeDevices > 1) { Log.i(this, "More than one profile reporting active devices. Going with the most" + " recently reported active device: %s", mMostRecentlyReportedActiveDevice); return mMostRecentlyReportedActiveDevice; } return hfpAudioOnDevice; if (leAudioActiveDevice != null) { return leAudioActiveDevice; } if (hearingAidActiveDevice != null) { return hearingAidActiveDevice; } return hfpAudioOnDevice; } /** * Check if in-band ringing is currently enabled. In-band ringing could be disabled during an * active connection. Loading Loading @@ -847,10 +895,12 @@ public class BluetoothRouteManager extends StateMachine { } @VisibleForTesting public void setActiveDeviceCacheForTesting(BluetoothDevice device, boolean isHearingAid) { if (isHearingAid) { public void setActiveDeviceCacheForTesting(BluetoothDevice device, int deviceType) { if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_LE_AUDIO) { mLeAudioActiveDeviceCache = device; } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEARING_AID) { mHearingAidActiveDeviceCache = device; } else { } else if (deviceType == BluetoothDeviceManager.DEVICE_TYPE_HEADSET) { mHfpActiveDeviceCache = device; } } Loading
src/com/android/server/telecom/bluetooth/BluetoothStateReceiver.java +61 −12 File changed.Preview size limit exceeded, changes collapsed. Show changes