1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.bluetooth; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.BluetoothCsipSetCoordinator; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothProfile; 23 import android.bluetooth.le.ScanFilter; 24 import android.content.Context; 25 import android.util.Log; 26 27 import androidx.annotation.NonNull; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.settingslib.flags.Flags; 31 32 import java.sql.Timestamp; 33 import java.util.ArrayList; 34 import java.util.Collection; 35 import java.util.HashSet; 36 import java.util.List; 37 import java.util.Set; 38 39 /** 40 * CachedBluetoothDeviceManager manages the set of remote Bluetooth devices. 41 */ 42 public class CachedBluetoothDeviceManager { 43 private static final String TAG = "CachedBluetoothDeviceManager"; 44 private static final boolean DEBUG = BluetoothUtils.D; 45 46 @VisibleForTesting static int sLateBondingTimeoutMillis = 10000; // 10s 47 48 private Context mContext; 49 private final LocalBluetoothManager mBtManager; 50 51 @VisibleForTesting 52 final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>(); 53 @VisibleForTesting 54 HearingAidDeviceManager mHearingAidDeviceManager; 55 @VisibleForTesting 56 CsipDeviceManager mCsipDeviceManager; 57 BluetoothDevice mOngoingSetMemberPair; 58 boolean mIsLateBonding; 59 int mGroupIdOfLateBonding; 60 CachedBluetoothDeviceManager(Context context, LocalBluetoothManager localBtManager)61 public CachedBluetoothDeviceManager(Context context, LocalBluetoothManager localBtManager) { 62 mContext = context; 63 mBtManager = localBtManager; 64 mHearingAidDeviceManager = new HearingAidDeviceManager(context, localBtManager, 65 mCachedDevices); 66 mCsipDeviceManager = new CsipDeviceManager(context, localBtManager, mCachedDevices); 67 } 68 getCachedDevicesCopy()69 public synchronized Collection<CachedBluetoothDevice> getCachedDevicesCopy() { 70 return new ArrayList<>(mCachedDevices); 71 } 72 onDeviceDisappeared(CachedBluetoothDevice cachedDevice)73 public static boolean onDeviceDisappeared(CachedBluetoothDevice cachedDevice) { 74 cachedDevice.setJustDiscovered(false); 75 return cachedDevice.getBondState() == BluetoothDevice.BOND_NONE; 76 } 77 onDeviceNameUpdated(BluetoothDevice device)78 public void onDeviceNameUpdated(BluetoothDevice device) { 79 CachedBluetoothDevice cachedDevice = findDevice(device); 80 if (cachedDevice != null) { 81 cachedDevice.refreshName(); 82 } 83 } 84 85 /** 86 * Search for existing {@link CachedBluetoothDevice} or return null 87 * if this device isn't in the cache. Use {@link #addDevice} 88 * to create and return a new {@link CachedBluetoothDevice} for 89 * a newly discovered {@link BluetoothDevice}. 90 * 91 * @param device the address of the Bluetooth device 92 * @return the cached device object for this device, or null if it has 93 * not been previously seen 94 */ findDevice(BluetoothDevice device)95 public synchronized CachedBluetoothDevice findDevice(BluetoothDevice device) { 96 for (CachedBluetoothDevice cachedDevice : mCachedDevices) { 97 if (cachedDevice.getDevice().equals(device)) { 98 return cachedDevice; 99 } 100 // Check the member devices for the coordinated set if it exists 101 final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice(); 102 if (!memberDevices.isEmpty()) { 103 for (CachedBluetoothDevice memberDevice : memberDevices) { 104 if (memberDevice.getDevice().equals(device)) { 105 return memberDevice; 106 } 107 } 108 } 109 // Check sub devices for hearing aid if it exists 110 CachedBluetoothDevice subDevice = cachedDevice.getSubDevice(); 111 if (subDevice != null && subDevice.getDevice().equals(device)) { 112 return subDevice; 113 } 114 } 115 116 return null; 117 } 118 119 /** 120 * Create and return a new {@link CachedBluetoothDevice}. This assumes 121 * that {@link #findDevice} has already been called and returned null. 122 * @param device the new Bluetooth device 123 * @return the newly created CachedBluetoothDevice object 124 */ addDevice(BluetoothDevice device)125 public CachedBluetoothDevice addDevice(BluetoothDevice device) { 126 return addDevice(device, /*leScanFilters=*/null); 127 } 128 129 /** 130 * Create and return a new {@link CachedBluetoothDevice}. This assumes 131 * that {@link #findDevice} has already been called and returned null. 132 * @param device the new Bluetooth device 133 * @param leScanFilters the BLE scan filters which the device matched 134 * @return the newly created CachedBluetoothDevice object 135 */ addDevice(BluetoothDevice device, List<ScanFilter> leScanFilters)136 public CachedBluetoothDevice addDevice(BluetoothDevice device, List<ScanFilter> leScanFilters) { 137 CachedBluetoothDevice newDevice; 138 final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager(); 139 synchronized (this) { 140 newDevice = findDevice(device); 141 if (newDevice == null) { 142 newDevice = new CachedBluetoothDevice(mContext, profileManager, device); 143 mCsipDeviceManager.initCsipDeviceIfNeeded(newDevice); 144 mHearingAidDeviceManager.initHearingAidDeviceIfNeeded(newDevice, leScanFilters); 145 if (!mCsipDeviceManager.setMemberDeviceIfNeeded(newDevice) 146 && !mHearingAidDeviceManager.setSubDeviceIfNeeded(newDevice)) { 147 mCachedDevices.add(newDevice); 148 mBtManager.getEventManager().dispatchDeviceAdded(newDevice); 149 } 150 } 151 } 152 153 return newDevice; 154 } 155 156 /** 157 * Returns device summary of the pair of the hearing aid / CSIP passed as the parameter. 158 * 159 * @param device the remote device 160 * @return Device summary, or if the pair does not exist or if it is not a hearing aid or 161 * a CSIP set member, then {@code null}. 162 */ getSubDeviceSummary(CachedBluetoothDevice device)163 public synchronized String getSubDeviceSummary(CachedBluetoothDevice device) { 164 final Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice(); 165 // TODO: check the CSIP group size instead of the real member device set size, and adjust 166 // the size restriction. 167 if (!memberDevices.isEmpty()) { 168 for (CachedBluetoothDevice memberDevice : memberDevices) { 169 if (memberDevice.isConnected()) { 170 return memberDevice.getConnectionSummary(); 171 } 172 } 173 } 174 CachedBluetoothDevice subDevice = device.getSubDevice(); 175 if (subDevice != null && subDevice.isConnected()) { 176 return subDevice.getConnectionSummary(); 177 } 178 return null; 179 } 180 181 /** 182 * Sync device status of the pair of the hearing aid if needed. 183 * 184 * @param device the remote device 185 */ syncDeviceWithinHearingAidSetIfNeeded(CachedBluetoothDevice device, int state, int profileId)186 public synchronized void syncDeviceWithinHearingAidSetIfNeeded(CachedBluetoothDevice device, 187 int state, int profileId) { 188 if (profileId == BluetoothProfile.HAP_CLIENT 189 || profileId == BluetoothProfile.HEARING_AID 190 || profileId == BluetoothProfile.CSIP_SET_COORDINATOR) { 191 if (state == BluetoothProfile.STATE_CONNECTED) { 192 mHearingAidDeviceManager.syncDeviceIfNeeded(device); 193 } 194 } 195 } 196 197 /** 198 * Notifies the connection status if device is hearing device. 199 * 200 * @param device The {@link CachedBluetoothDevice} need to be hearing device 201 */ notifyHearingDevicesConnectionStatusChangedIfNeeded( @onNull CachedBluetoothDevice device)202 public synchronized void notifyHearingDevicesConnectionStatusChangedIfNeeded( 203 @NonNull CachedBluetoothDevice device) { 204 if (!device.isHearingDevice()) { 205 return; 206 } 207 208 mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); 209 } 210 211 /** 212 * Search for existing sub device {@link CachedBluetoothDevice}. 213 * 214 * @param device the address of the Bluetooth device 215 * @return true for found sub / member device or false. 216 */ isSubDevice(BluetoothDevice device)217 public synchronized boolean isSubDevice(BluetoothDevice device) { 218 for (CachedBluetoothDevice cachedDevice : mCachedDevices) { 219 if (!cachedDevice.getDevice().equals(device)) { 220 // Check the member devices of the coordinated set if it exists 221 Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice(); 222 if (!memberDevices.isEmpty()) { 223 for (CachedBluetoothDevice memberDevice : memberDevices) { 224 if (memberDevice.getDevice().equals(device)) { 225 return true; 226 } 227 } 228 continue; 229 } 230 // Check sub devices of hearing aid if it exists 231 CachedBluetoothDevice subDevice = cachedDevice.getSubDevice(); 232 if (subDevice != null && subDevice.getDevice().equals(device)) { 233 return true; 234 } 235 } 236 } 237 return false; 238 } 239 240 /** 241 * Updates the Hearing Aid devices; specifically the HiSyncId's. This routine is called when the 242 * Hearing Aid Service is connected and the HiSyncId's are now available. 243 */ updateHearingAidsDevices()244 public synchronized void updateHearingAidsDevices() { 245 mHearingAidDeviceManager.updateHearingAidsDevices(); 246 } 247 248 /** 249 * Updates the Csip devices; specifically the GroupId's. This routine is called when the 250 * CSIS is connected and the GroupId's are now available. 251 */ updateCsipDevices()252 public synchronized void updateCsipDevices() { 253 mCsipDeviceManager.updateCsipDevices(); 254 } 255 256 /** 257 * Attempts to get the name of a remote device, otherwise returns the address. 258 * 259 * @param device The remote device. 260 * @return The name, or if unavailable, the address. 261 */ getName(BluetoothDevice device)262 public String getName(BluetoothDevice device) { 263 if (isOngoingPairByCsip(device)) { 264 CachedBluetoothDevice firstDevice = 265 mCsipDeviceManager.getFirstMemberDevice(mGroupIdOfLateBonding); 266 if (firstDevice != null && firstDevice.getName() != null) { 267 return firstDevice.getName(); 268 } 269 } 270 271 CachedBluetoothDevice cachedDevice = findDevice(device); 272 if (cachedDevice != null && cachedDevice.getName() != null) { 273 return cachedDevice.getName(); 274 } 275 276 String name = device.getAlias(); 277 if (name != null) { 278 return name; 279 } 280 281 return device.getAddress(); 282 } 283 clearNonBondedDevices()284 public synchronized void clearNonBondedDevices() { 285 clearNonBondedSubDevices(); 286 final List<CachedBluetoothDevice> removedCachedDevice = new ArrayList<>(); 287 mCachedDevices.stream() 288 .filter(cachedDevice -> cachedDevice.getBondState() == BluetoothDevice.BOND_NONE) 289 .forEach(cachedDevice -> { 290 cachedDevice.release(); 291 removedCachedDevice.add(cachedDevice); 292 }); 293 mCachedDevices.removeAll(removedCachedDevice); 294 } 295 clearNonBondedSubDevices()296 private void clearNonBondedSubDevices() { 297 for (int i = mCachedDevices.size() - 1; i >= 0; i--) { 298 CachedBluetoothDevice cachedDevice = mCachedDevices.get(i); 299 Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice(); 300 if (!memberDevices.isEmpty()) { 301 for (Object it : memberDevices.toArray()) { 302 CachedBluetoothDevice memberDevice = (CachedBluetoothDevice) it; 303 // Member device exists and it is not bonded 304 if (memberDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) { 305 cachedDevice.removeMemberDevice(memberDevice); 306 } 307 } 308 return; 309 } 310 CachedBluetoothDevice subDevice = cachedDevice.getSubDevice(); 311 if (subDevice != null 312 && subDevice.getDevice().getBondState() == BluetoothDevice.BOND_NONE) { 313 // Sub device exists and it is not bonded 314 subDevice.release(); 315 cachedDevice.setSubDevice(null); 316 } 317 } 318 } 319 onScanningStateChanged(boolean started)320 public synchronized void onScanningStateChanged(boolean started) { 321 if (!started) return; 322 // If starting a new scan, clear old visibility 323 // Iterate in reverse order since devices may be removed. 324 for (int i = mCachedDevices.size() - 1; i >= 0; i--) { 325 CachedBluetoothDevice cachedDevice = mCachedDevices.get(i); 326 cachedDevice.setJustDiscovered(false); 327 final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice(); 328 if (!memberDevices.isEmpty()) { 329 for (CachedBluetoothDevice memberDevice : memberDevices) { 330 memberDevice.setJustDiscovered(false); 331 } 332 return; 333 } 334 final CachedBluetoothDevice subDevice = cachedDevice.getSubDevice(); 335 if (subDevice != null) { 336 subDevice.setJustDiscovered(false); 337 } 338 } 339 } 340 onBluetoothStateChanged(int bluetoothState)341 public synchronized void onBluetoothStateChanged(int bluetoothState) { 342 // When Bluetooth is turning off, we need to clear the non-bonded devices 343 // Otherwise, they end up showing up on the next BT enable 344 if (bluetoothState == BluetoothAdapter.STATE_TURNING_OFF) { 345 for (int i = mCachedDevices.size() - 1; i >= 0; i--) { 346 CachedBluetoothDevice cachedDevice = mCachedDevices.get(i); 347 final Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice(); 348 if (!memberDevices.isEmpty()) { 349 for (CachedBluetoothDevice memberDevice : memberDevices) { 350 if (memberDevice.getBondState() != BluetoothDevice.BOND_BONDED) { 351 cachedDevice.removeMemberDevice(memberDevice); 352 } 353 } 354 } else { 355 CachedBluetoothDevice subDevice = cachedDevice.getSubDevice(); 356 if (subDevice != null) { 357 if (subDevice.getBondState() != BluetoothDevice.BOND_BONDED) { 358 cachedDevice.setSubDevice(null); 359 } 360 } 361 } 362 if (cachedDevice.getBondState() != BluetoothDevice.BOND_BONDED) { 363 cachedDevice.setJustDiscovered(false); 364 cachedDevice.release(); 365 mCachedDevices.remove(i); 366 } 367 } 368 369 // To clear the SetMemberPair flag when the Bluetooth is turning off. 370 mOngoingSetMemberPair = null; 371 mIsLateBonding = false; 372 mGroupIdOfLateBonding = BluetoothCsipSetCoordinator.GROUP_ID_INVALID; 373 } 374 } 375 removeDuplicateInstanceForIdentityAddress(BluetoothDevice device)376 synchronized void removeDuplicateInstanceForIdentityAddress(BluetoothDevice device) { 377 String identityAddress = device.getIdentityAddress(); 378 if (identityAddress == null || identityAddress.equals(device.getAddress())) { 379 return; 380 } 381 mCachedDevices.removeIf(d -> { 382 boolean shouldRemove = d.getDevice().getAddress().equals(identityAddress); 383 if (shouldRemove) { 384 Log.d(TAG, "Remove instance for identity address " + d); 385 } 386 return shouldRemove; 387 }); 388 } 389 onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice cachedDevice, int state, int profileId)390 public synchronized boolean onProfileConnectionStateChangedIfProcessed(CachedBluetoothDevice 391 cachedDevice, int state, int profileId) { 392 if (profileId == BluetoothProfile.HEARING_AID) { 393 return mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice, 394 state); 395 } 396 if (profileId == BluetoothProfile.HEADSET 397 || profileId == BluetoothProfile.A2DP 398 || profileId == BluetoothProfile.LE_AUDIO 399 || profileId == BluetoothProfile.CSIP_SET_COORDINATOR) { 400 return mCsipDeviceManager.onProfileConnectionStateChangedIfProcessed(cachedDevice, 401 state); 402 } 403 return false; 404 } 405 406 /** Handles when the device been set as active/inactive. */ onActiveDeviceChanged(CachedBluetoothDevice cachedBluetoothDevice)407 public synchronized void onActiveDeviceChanged(CachedBluetoothDevice cachedBluetoothDevice) { 408 if (cachedBluetoothDevice == null) { 409 return; 410 } 411 if (cachedBluetoothDevice.isHearingDevice()) { 412 mHearingAidDeviceManager.onActiveDeviceChanged(cachedBluetoothDevice); 413 if (Flags.hearingDeviceSetConnectionStatusReport()) { 414 mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); 415 } 416 } 417 } 418 onDeviceUnpaired(CachedBluetoothDevice device)419 public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) { 420 mHearingAidDeviceManager.clearLocalDataIfNeeded(device); 421 device.setGroupId(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); 422 CachedBluetoothDevice mainDevice = mCsipDeviceManager.findMainDevice(device); 423 // Should iterate through the cloned set to avoid ConcurrentModificationException 424 final Set<CachedBluetoothDevice> memberDevices = new HashSet<>(device.getMemberDevice()); 425 if (!memberDevices.isEmpty()) { 426 // Main device is unpaired, also unpair the member devices 427 for (CachedBluetoothDevice memberDevice : memberDevices) { 428 memberDevice.unpair(); 429 memberDevice.setGroupId(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); 430 device.removeMemberDevice(memberDevice); 431 } 432 } else if (mainDevice != null) { 433 // Member device is unpaired, also unpair the main device 434 mainDevice.unpair(); 435 } 436 mainDevice = mHearingAidDeviceManager.findMainDevice(device); 437 CachedBluetoothDevice subDevice = device.getSubDevice(); 438 if (subDevice != null) { 439 // Main device is unpaired, to unpair sub device 440 subDevice.unpair(); 441 device.setSubDevice(null); 442 } else if (mainDevice != null) { 443 // Sub device unpaired, to unpair main device 444 mainDevice.unpair(); 445 mainDevice.setSubDevice(null); 446 } 447 448 // TODO: b/386121967 - Should change to use isHearingDevice but mProfile get clear here. 449 // Need to consider where to put this logic when using isHearingDevice() 450 if (device.isHearingAidDevice()) { 451 if (Flags.hearingDeviceSetConnectionStatusReport()) { 452 mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged(); 453 } 454 } 455 } 456 457 /** 458 * Called when we found a set member of a group. The function will check the {@code groupId} if 459 * it exists and the bond state of the device is BOND_NOE, and if there isn't any ongoing pair 460 * , and then return {@code true} to pair the device automatically. 461 * 462 * @param device The found device 463 * @param groupId The group id of the found device 464 * 465 * @return {@code true}, if the device should pair automatically; Otherwise, return 466 * {@code false}. 467 */ shouldPairByCsip(BluetoothDevice device, int groupId)468 private synchronized boolean shouldPairByCsip(BluetoothDevice device, int groupId) { 469 boolean isOngoingSetMemberPair = mOngoingSetMemberPair != null; 470 int bondState = device.getBondState(); 471 boolean groupExists = mCsipDeviceManager.isExistedGroupId(groupId); 472 Log.d(TAG, 473 "isOngoingSetMemberPair=" + isOngoingSetMemberPair + ", bondState=" + bondState 474 + ", groupExists=" + groupExists + ", groupId=" + groupId); 475 476 if (isOngoingSetMemberPair || bondState != BluetoothDevice.BOND_NONE || !groupExists) { 477 return false; 478 } 479 return true; 480 } 481 checkLateBonding(int groupId)482 private synchronized boolean checkLateBonding(int groupId) { 483 CachedBluetoothDevice firstDevice = mCsipDeviceManager.getFirstMemberDevice(groupId); 484 if (firstDevice == null) { 485 Log.d(TAG, "No first device in group: " + groupId); 486 return false; 487 } 488 489 Timestamp then = firstDevice.getBondTimestamp(); 490 if (then == null) { 491 Log.d(TAG, "No bond timestamp"); 492 return true; 493 } 494 495 Timestamp now = new Timestamp(System.currentTimeMillis()); 496 497 long diff = (now.getTime() - then.getTime()); 498 Log.d(TAG, "Time difference to first bonding: " + diff + "ms"); 499 500 return diff > sLateBondingTimeoutMillis; 501 } 502 503 /** 504 * Called to check if there is an ongoing bonding for the device and it is late bonding. 505 * If the device is not matching the ongoing bonding device then false will be returned. 506 * 507 * @param device The device to check. 508 */ isLateBonding(BluetoothDevice device)509 public synchronized boolean isLateBonding(BluetoothDevice device) { 510 if (!isOngoingPairByCsip(device)) { 511 Log.d(TAG, "isLateBonding: pair not ongoing or not matching device"); 512 return false; 513 } 514 515 Log.d(TAG, "isLateBonding: " + mIsLateBonding); 516 return mIsLateBonding; 517 } 518 519 /** 520 * Called when we found a set member of a group. The function will check the {@code groupId} if 521 * it exists and the bond state of the device is BOND_NONE, and if there isn't any ongoing pair 522 * , and then pair the device automatically. 523 * 524 * @param device The found device 525 * @param groupId The group id of the found device 526 */ pairDeviceByCsip(BluetoothDevice device, int groupId)527 public synchronized void pairDeviceByCsip(BluetoothDevice device, int groupId) { 528 if (!shouldPairByCsip(device, groupId)) { 529 return; 530 } 531 Log.d(TAG, "Bond " + device.getAnonymizedAddress() + " groupId=" + groupId + " by CSIP "); 532 mOngoingSetMemberPair = device; 533 mIsLateBonding = checkLateBonding(groupId); 534 mGroupIdOfLateBonding = groupId; 535 syncConfigFromMainDevice(device, groupId); 536 if (!device.createBond(BluetoothDevice.TRANSPORT_LE)) { 537 Log.d(TAG, "Bonding could not be started"); 538 mOngoingSetMemberPair = null; 539 mIsLateBonding = false; 540 mGroupIdOfLateBonding = BluetoothCsipSetCoordinator.GROUP_ID_INVALID; 541 } 542 } 543 syncConfigFromMainDevice(BluetoothDevice device, int groupId)544 private void syncConfigFromMainDevice(BluetoothDevice device, int groupId) { 545 if (!isOngoingPairByCsip(device)) { 546 return; 547 } 548 CachedBluetoothDevice memberDevice = findDevice(device); 549 CachedBluetoothDevice mainDevice = mCsipDeviceManager.findMainDevice(memberDevice); 550 if (mainDevice == null) { 551 mainDevice = mCsipDeviceManager.getCachedDevice(groupId); 552 } 553 554 if (mainDevice == null || mainDevice.equals(memberDevice)) { 555 Log.d(TAG, "no mainDevice"); 556 return; 557 } 558 559 // The memberDevice set PhonebookAccessPermission 560 device.setPhonebookAccessPermission(mainDevice.getDevice().getPhonebookAccessPermission()); 561 } 562 563 /** 564 * Called when the bond state change. If the bond state change is related with the 565 * ongoing set member pair, the cachedBluetoothDevice will be created but the UI 566 * would not be updated. For the other case, return {@code false} to go through the normal 567 * flow. 568 * 569 * @param device The device 570 * @param bondState The new bond state 571 * 572 * @return {@code true}, if the bond state change for the device is handled inside this 573 * function, and would not like to update the UI. If not, return {@code false}. 574 */ onBondStateChangedIfProcess(BluetoothDevice device, int bondState)575 public synchronized boolean onBondStateChangedIfProcess(BluetoothDevice device, int bondState) { 576 if (!isOngoingPairByCsip(device)) { 577 return false; 578 } 579 580 if (bondState == BluetoothDevice.BOND_BONDING) { 581 return true; 582 } 583 584 mOngoingSetMemberPair = null; 585 mIsLateBonding = false; 586 mGroupIdOfLateBonding = BluetoothCsipSetCoordinator.GROUP_ID_INVALID; 587 if (bondState != BluetoothDevice.BOND_NONE) { 588 if (findDevice(device) == null) { 589 final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager(); 590 CachedBluetoothDevice newDevice = 591 new CachedBluetoothDevice(mContext, profileManager, device); 592 mCachedDevices.add(newDevice); 593 findDevice(device).connect(); 594 } 595 } 596 597 return true; 598 } 599 600 /** 601 * Check if the device is the one which is initial paired locally by CSIP. The setting 602 * would depned on it to accept the pairing request automatically 603 * 604 * @param device The device 605 * 606 * @return {@code true}, if the device is ongoing pair by CSIP. Otherwise, return 607 * {@code false}. 608 */ isOngoingPairByCsip(BluetoothDevice device)609 public boolean isOngoingPairByCsip(BluetoothDevice device) { 610 return mOngoingSetMemberPair != null && mOngoingSetMemberPair.equals(device); 611 } 612 613 @NonNull getHearingAidDeviceManager()614 public HearingAidDeviceManager getHearingAidDeviceManager() { 615 return mHearingAidDeviceManager; 616 } 617 log(String msg)618 private void log(String msg) { 619 if (DEBUG) { 620 Log.d(TAG, msg); 621 } 622 } 623 } 624