Fix stop Ble scan logic
Can not simplly stop ble scan if one of the device is not connected yet.
We also need to check if any devices has been already reported ble
presence since there's a chance CDM need to report `ble disappeared`
After device is bt disconnected, wait for 10 seconds, if a device
pending to report BLE device lost and also it was BLE presence before,
it is likely the BLE is lost and trigger the onDeviceEvent callback with
EVENT_BLE_DISAPPEARED event.
Bug: 326652934
Test: cts
Change-Id: I5c75647284cc877f7d53e29d899788842c696398
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index 7e30790..0d1269e 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -168,7 +168,7 @@
void startScan() {
enforceInitialized();
- if (DEBUG) Log.i(TAG, "startScan()");
+ Slog.i(TAG, "startBleScan()");
// This method should not be called if scan is already in progress.
if (mScanning) {
Slog.w(TAG, "Scan is already in progress.");
@@ -228,7 +228,7 @@
void stopScanIfNeeded() {
enforceInitialized();
- if (DEBUG) Log.i(TAG, "stopScan()");
+ Slog.i(TAG, "stopBleScan()");
if (!mScanning) {
if (DEBUG) Log.d(TAG, " > not scanning.");
return;
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
index 54a4692..f252b52 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -41,7 +41,9 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.companion.AssociationStore;
import com.android.server.companion.ObservableUuid;
import com.android.server.companion.ObservableUuidStore;
@@ -102,12 +104,24 @@
private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>();
private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
private final @NonNull Set<ParcelUuid> mConnectedUuidDevices = new HashSet<>();
+ @GuardedBy("mBtDisconnectedDevices")
+ private final @NonNull Set<Integer> mBtDisconnectedDevices = new HashSet<>();
+
+ // A map to track device presence within 10 seconds of Bluetooth disconnection.
+ // The key is the association ID, and the boolean value indicates if the device
+ // was detected again within that time frame.
+ @GuardedBy("mBtDisconnectedDevices")
+ private final @NonNull SparseBooleanArray mBtDisconnectedDevicesBlePresence =
+ new SparseBooleanArray();
// Tracking "simulated" presence. Used for debugging and testing only.
private final @NonNull Set<Integer> mSimulated = new HashSet<>();
private final SimulatedDevicePresenceSchedulerHelper mSchedulerHelper =
new SimulatedDevicePresenceSchedulerHelper();
+ private final BleDeviceDisappearedScheduler mBleDeviceDisappearedScheduler =
+ new BleDeviceDisappearedScheduler();
+
public CompanionDevicePresenceMonitor(UserManager userManager,
@NonNull AssociationStore associationStore,
@NonNull ObservableUuidStore observableUuidStore, @NonNull Callback callback) {
@@ -229,13 +243,24 @@
@Override
public void onBluetoothCompanionDeviceConnected(int associationId) {
- Slog.i(TAG, "onBluetoothCompanionDeviceConnected: "
- + "associationId( " + associationId + " )");
- onDevicePresenceEvent(mConnectedBtDevices, associationId, EVENT_BT_CONNECTED);
- // Stop scanning for BLE devices when this device is connected
- // and there are no other devices to connect to.
- if (canStopBleScan()) {
- mBleScanner.stopScanIfNeeded();
+ synchronized (mBtDisconnectedDevices) {
+ // A device is considered reconnected within 10 seconds if a pending BLE lost report is
+ // followed by a detected Bluetooth connection.
+ boolean isReconnected = mBtDisconnectedDevices.contains(associationId);
+ if (isReconnected) {
+ Slog.i(TAG, "Device ( " + associationId + " ) is reconnected within 10s.");
+ mBleDeviceDisappearedScheduler.unScheduleDeviceDisappeared(associationId);
+ }
+
+ Slog.i(TAG, "onBluetoothCompanionDeviceConnected: "
+ + "associationId( " + associationId + " )");
+ onDevicePresenceEvent(mConnectedBtDevices, associationId, EVENT_BT_CONNECTED);
+
+ // Stop the BLE scan if all devices report BT connected status and BLE was present.
+ if (canStopBleScan()) {
+ mBleScanner.stopScanIfNeeded();
+ }
+
}
}
@@ -247,6 +272,14 @@
mBleScanner.startScan();
onDevicePresenceEvent(mConnectedBtDevices, associationId, EVENT_BT_DISCONNECTED);
+ // If current device is BLE present but BT is disconnected , means it will be
+ // potentially out of range later. Schedule BLE disappeared callback.
+ if (isBlePresent(associationId)) {
+ synchronized (mBtDisconnectedDevices) {
+ mBtDisconnectedDevices.add(associationId);
+ }
+ mBleDeviceDisappearedScheduler.scheduleBleDeviceDisappeared(associationId);
+ }
}
@Override
@@ -283,6 +316,12 @@
@Override
public void onBleCompanionDeviceFound(int associationId) {
onDevicePresenceEvent(mNearbyBleDevices, associationId, EVENT_BLE_APPEARED);
+ synchronized (mBtDisconnectedDevices) {
+ final boolean isCurrentPresent = mBtDisconnectedDevicesBlePresence.get(associationId);
+ if (mBtDisconnectedDevices.contains(associationId) && isCurrentPresent) {
+ mBleDeviceDisappearedScheduler.unScheduleDeviceDisappeared(associationId);
+ }
+ }
}
@Override
@@ -353,6 +392,16 @@
switch (event) {
case EVENT_BLE_APPEARED:
+ synchronized (mBtDisconnectedDevices) {
+ // If a BLE device is detected within 10 seconds after BT is disconnected,
+ // flag it as BLE is present.
+ if (mBtDisconnectedDevices.contains(associationId)) {
+ Slog.i(TAG, "Device ( " + associationId + " ) is present,"
+ + " do not need to send the callback with event ( "
+ + EVENT_BLE_APPEARED + " ).");
+ mBtDisconnectedDevicesBlePresence.append(associationId, true);
+ }
+ }
case EVENT_BT_CONNECTED:
case EVENT_SELF_MANAGED_APPEARED:
final boolean added = presentDevicesForSource.add(associationId);
@@ -405,6 +454,8 @@
mNearbyBleDevices.remove(id);
mReportedSelfManagedDevices.remove(id);
mSimulated.remove(id);
+ mBtDisconnectedDevices.remove(id);
+ mBtDisconnectedDevicesBlePresence.delete(id);
// Do NOT call mCallback.onDeviceDisappeared()!
// CompanionDeviceManagerService will know that the association is removed, and will do
@@ -427,14 +478,21 @@
throw new SecurityException("Caller is neither Shell nor Root");
}
+ /**
+ * The BLE scan can be only stopped if all the devices have been reported
+ * BT connected and BLE presence and are not pending to report BLE lost.
+ */
private boolean canStopBleScan() {
for (AssociationInfo ai : mAssociationStore.getAssociations()) {
int id = ai.getId();
- // The BLE scan cannot be stopped if there's a device is not yet connected.
- if (ai.isNotifyOnDeviceNearby() && !isBtConnected(id)) {
- Slog.i(TAG, "The BLE scan cannot be stopped, "
- + "device( " + id + " ) is not yet connected");
- return false;
+ synchronized (mBtDisconnectedDevices) {
+ if (ai.isNotifyOnDeviceNearby() && !(isBtConnected(id)
+ && isBlePresent(id) && mBtDisconnectedDevices.isEmpty())) {
+ Slog.i(TAG, "The BLE scan cannot be stopped, "
+ + "device( " + id + " ) is not yet connected "
+ + "OR the BLE is not current present Or is pending to report BLE lost");
+ return false;
+ }
}
}
return true;
@@ -514,4 +572,51 @@
}
}
}
+
+ private class BleDeviceDisappearedScheduler extends Handler {
+ BleDeviceDisappearedScheduler() {
+ super(Looper.getMainLooper());
+ }
+
+ void scheduleBleDeviceDisappeared(int associationId) {
+ if (hasMessages(associationId)) {
+ removeMessages(associationId);
+ }
+ Slog.i(TAG, "scheduleBleDeviceDisappeared for Device: ( " + associationId + " ).");
+ sendEmptyMessageDelayed(associationId, 10 * 1000 /* 10 seconds */);
+ }
+
+ void unScheduleDeviceDisappeared(int associationId) {
+ if (hasMessages(associationId)) {
+ Slog.i(TAG, "unScheduleDeviceDisappeared for Device( " + associationId + " )");
+ synchronized (mBtDisconnectedDevices) {
+ mBtDisconnectedDevices.remove(associationId);
+ mBtDisconnectedDevicesBlePresence.delete(associationId);
+ }
+
+ removeMessages(associationId);
+ }
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ final int associationId = msg.what;
+ synchronized (mBtDisconnectedDevices) {
+ final boolean isCurrentPresent = mBtDisconnectedDevicesBlePresence.get(
+ associationId);
+ // If a device hasn't reported after 10 seconds and is not currently present,
+ // assume BLE is lost and trigger the onDeviceEvent callback with the
+ // EVENT_BLE_DISAPPEARED event.
+ if (mBtDisconnectedDevices.contains(associationId)
+ && !isCurrentPresent) {
+ Slog.i(TAG, "Device ( " + associationId + " ) is likely BLE out of range, "
+ + "sending callback with event ( " + EVENT_BLE_DISAPPEARED + " )");
+ onDevicePresenceEvent(mNearbyBleDevices, associationId, EVENT_BLE_DISAPPEARED);
+ }
+
+ mBtDisconnectedDevices.remove(associationId);
+ mBtDisconnectedDevicesBlePresence.delete(associationId);
+ }
+ }
+ }
}