From 4217652b5768ddf4953e43384365d20c7329077d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Narajowski?= Date: Wed, 17 May 2023 13:46:18 +0000 Subject: [PATCH] bt: Add late bonding confirmation mechanism If a coordinated set member is bonding late the user will be notified and asked for confirmation on pairing with this device. The dialog box will display specific message for this case. Bug: 282193044 Test: manual Tag: #feature Change-Id: I59e41bcba5788d6644bd0614189059b4bc26b175 --- .../bluetooth/CachedBluetoothDevice.java | 16 ++++- .../CachedBluetoothDeviceManager.java | 60 ++++++++++++++++--- .../bluetooth/CsipDeviceManager.java | 11 ++++ 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 1aa17413d05bf..2e6bb535a8f01 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -47,6 +47,7 @@ import com.android.settingslib.Utils; import com.android.settingslib.utils.ThreadUtils; import com.android.settingslib.widget.AdaptiveOutlineDrawable; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; @@ -78,6 +79,7 @@ public class CachedBluetoothDevice implements Comparable BluetoothDevice mDevice; private HearingAidInfo mHearingAidInfo; private int mGroupId; + private Timestamp mBondTimestamp; // Need this since there is no method for getting RSSI short mRssi; @@ -889,15 +891,25 @@ public class CachedBluetoothDevice implements Comparable mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_UNKNOWN); mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_UNKNOWN); mDevice.setSimAccessPermission(BluetoothDevice.ACCESS_UNKNOWN); + + mBondTimestamp = null; } refresh(); - if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) { - connect(); + if (bondState == BluetoothDevice.BOND_BONDED) { + mBondTimestamp = new Timestamp(System.currentTimeMillis()); + + if (mDevice.isBondingInitiatedLocally()) { + connect(); + } } } + public Timestamp getBondTimestamp() { + return mBondTimestamp; + } + public BluetoothClass getBtClass() { return mDevice.getBluetoothClass(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java index d55144eefea9a..0db88af92a568 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java @@ -25,6 +25,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -37,6 +38,8 @@ public class CachedBluetoothDeviceManager { private static final String TAG = "CachedBluetoothDeviceManager"; private static final boolean DEBUG = BluetoothUtils.D; + @VisibleForTesting static int sLateBondingTimeoutMillis = 5000; // 5s + private Context mContext; private final LocalBluetoothManager mBtManager; @@ -47,6 +50,7 @@ public class CachedBluetoothDeviceManager { @VisibleForTesting CsipDeviceManager mCsipDeviceManager; BluetoothDevice mOngoingSetMemberPair; + boolean mIsLateBonding; public CachedBluetoothDeviceManager(Context context, LocalBluetoothManager localBtManager) { mContext = context; @@ -309,6 +313,7 @@ public class CachedBluetoothDeviceManager { // To clear the SetMemberPair flag when the Bluetooth is turning off. mOngoingSetMemberPair = null; + mIsLateBonding = false; } } @@ -377,15 +382,53 @@ public class CachedBluetoothDeviceManager { private synchronized boolean shouldPairByCsip(BluetoothDevice device, int groupId) { boolean isOngoingSetMemberPair = mOngoingSetMemberPair != null; int bondState = device.getBondState(); - if (isOngoingSetMemberPair || bondState != BluetoothDevice.BOND_NONE - || !mCsipDeviceManager.isExistedGroupId(groupId)) { - Log.d(TAG, "isOngoingSetMemberPair: " + isOngoingSetMemberPair - + " , device.getBondState: " + bondState); + boolean groupExists = mCsipDeviceManager.isExistedGroupId(groupId); + Log.d(TAG, + "isOngoingSetMemberPair=" + isOngoingSetMemberPair + ", bondState=" + bondState + + ", groupExists=" + groupExists + ", groupId=" + groupId); + + if (isOngoingSetMemberPair || bondState != BluetoothDevice.BOND_NONE || !groupExists) { return false; } return true; } + private synchronized boolean checkLateBonding(int groupId) { + CachedBluetoothDevice firstDevice = mCsipDeviceManager.getFirstMemberDevice(groupId); + if (firstDevice == null) { + Log.d(TAG, "No first device in group: " + groupId); + return false; + } + + Timestamp then = firstDevice.getBondTimestamp(); + if (then == null) { + Log.d(TAG, "No bond timestamp"); + return true; + } + + Timestamp now = new Timestamp(System.currentTimeMillis()); + + long diff = (now.getTime() - then.getTime()); + Log.d(TAG, "Time difference to first bonding: " + diff + "ms"); + + return diff > sLateBondingTimeoutMillis; + } + + /** + * Called to check if there is an ongoing bonding for the device and it is late bonding. + * If the device is not matching the ongoing bonding device then false will be returned. + * + * @param device The device to check. + */ + public synchronized boolean isLateBonding(BluetoothDevice device) { + if (!isOngoingPairByCsip(device)) { + Log.d(TAG, "isLateBonding: pair not ongoing or not matching device"); + return false; + } + + return mIsLateBonding; + } + /** * Called when we found a set member of a group. The function will check the {@code groupId} if * it exists and the bond state of the device is BOND_NONE, and if there isn't any ongoing pair @@ -398,12 +441,14 @@ public class CachedBluetoothDeviceManager { if (!shouldPairByCsip(device, groupId)) { return; } - Log.d(TAG, "Bond " + device.getAnonymizedAddress() + " by CSIP"); + Log.d(TAG, "Bond " + device.getAnonymizedAddress() + " groupId=" + groupId + " by CSIP "); mOngoingSetMemberPair = device; + mIsLateBonding = checkLateBonding(groupId); syncConfigFromMainDevice(device, groupId); if (!device.createBond(BluetoothDevice.TRANSPORT_LE)) { Log.d(TAG, "Bonding could not be started"); mOngoingSetMemberPair = null; + mIsLateBonding = false; } } @@ -439,7 +484,7 @@ public class CachedBluetoothDeviceManager { * function, and would not like to update the UI. If not, return {@code false}. */ public synchronized boolean onBondStateChangedIfProcess(BluetoothDevice device, int bondState) { - if (mOngoingSetMemberPair == null || !mOngoingSetMemberPair.equals(device)) { + if (!isOngoingPairByCsip(device)) { return false; } @@ -448,6 +493,7 @@ public class CachedBluetoothDeviceManager { } mOngoingSetMemberPair = null; + mIsLateBonding = false; if (bondState != BluetoothDevice.BOND_NONE) { if (findDevice(device) == null) { final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager(); @@ -471,7 +517,7 @@ public class CachedBluetoothDeviceManager { * {@code false}. */ public boolean isOngoingPairByCsip(BluetoothDevice device) { - return !(mOngoingSetMemberPair == null) && mOngoingSetMemberPair.equals(device); + return mOngoingSetMemberPair != null && mOngoingSetMemberPair.equals(device); } private void log(String msg) { diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java index 8269b56c425bb..3a6da2c98a654 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java @@ -241,6 +241,17 @@ public class CsipDeviceManager { return groupDevicesList; } + public CachedBluetoothDevice getFirstMemberDevice(int groupId) { + List members = getGroupDevicesFromAllOfDevicesList(groupId); + if (members.isEmpty()) + return null; + + CachedBluetoothDevice firstMember = members.get(0); + log("getFirstMemberDevice: groupId=" + groupId + + " address=" + firstMember.getDevice().getAnonymizedAddress()); + return firstMember; + } + @VisibleForTesting CachedBluetoothDevice getPreferredMainDevice(int groupId, List groupDevicesList) {