Stop advertising for reconect after timeout
Fixes: 151252960
Test: Unit tests and manually verify reconnect times out
Change-Id: Ie1de76c871b7d0bcfc4d05b6f5993e9573d57935
diff --git a/connected-device-lib/res/values/config.xml b/connected-device-lib/res/values/config.xml
index c052264..0f88ddc 100644
--- a/connected-device-lib/res/values/config.xml
+++ b/connected-device-lib/res/values/config.xml
@@ -25,4 +25,6 @@
<string name="car_secure_write_uuid" translatable="false">5e2a68a5-27be-43f9-8d1e-4546976fabd7</string>
<string name="connected_device_shared_preferences" translatable="false">com.android.car.connecteddevice</string>
+
+ <integer name="car_reconnect_timeout_sec">60</integer>
</resources>
diff --git a/connected-device-lib/src/com/android/car/connecteddevice/ConnectedDeviceManager.java b/connected-device-lib/src/com/android/car/connecteddevice/ConnectedDeviceManager.java
index 8218a94..f10bb85 100644
--- a/connected-device-lib/src/com/android/car/connecteddevice/ConnectedDeviceManager.java
+++ b/connected-device-lib/src/com/android/car/connecteddevice/ConnectedDeviceManager.java
@@ -107,6 +107,8 @@
private final AtomicBoolean mIsConnectingToUserDevice = new AtomicBoolean(false);
+ private final int mReconnectTimeoutSeconds;
+
private String mNameForAssociation;
private AssociationCallback mAssociationCallback;
@@ -147,7 +149,8 @@
UUID.fromString(context.getString(R.string.car_association_service_uuid)),
context.getString(R.string.car_bg_mask),
UUID.fromString(context.getString(R.string.car_secure_write_uuid)),
- UUID.fromString(context.getString(R.string.car_secure_read_uuid)));
+ UUID.fromString(context.getString(R.string.car_secure_read_uuid)),
+ context.getResources().getInteger(R.integer.car_reconnect_timeout_sec));
}
private ConnectedDeviceManager(
@@ -159,19 +162,21 @@
@NonNull UUID associationServiceUuid,
@NonNull String bgMask,
@NonNull UUID writeCharacteristicUuid,
- @NonNull UUID readCharacteristicUuid) {
+ @NonNull UUID readCharacteristicUuid,
+ int reconnectTimeoutSeconds) {
this(storage,
new CarBleCentralManager(context, bleCentralManager, storage, serviceUuid, bgMask,
writeCharacteristicUuid, readCharacteristicUuid),
new CarBlePeripheralManager(blePeripheralManager, storage, associationServiceUuid,
- writeCharacteristicUuid, readCharacteristicUuid));
+ writeCharacteristicUuid, readCharacteristicUuid), reconnectTimeoutSeconds);
}
@VisibleForTesting
ConnectedDeviceManager(
@NonNull ConnectedDeviceStorage storage,
@NonNull CarBleCentralManager centralManager,
- @NonNull CarBlePeripheralManager peripheralManager) {
+ @NonNull CarBlePeripheralManager peripheralManager,
+ int reconnectTimeoutSeconds) {
Executor callbackExecutor = Executors.newSingleThreadExecutor();
mStorage = storage;
mCentralManager = centralManager;
@@ -180,6 +185,7 @@
mPeripheralManager.registerCallback(generateCarBleCallback(peripheralManager),
callbackExecutor);
mStorage.setAssociatedDeviceCallback(mAssociatedDeviceCallback);
+ mReconnectTimeoutSeconds = reconnectTimeoutSeconds;
}
/**
@@ -296,7 +302,8 @@
}
EventLog.onStartDeviceSearchStarted();
mIsConnectingToUserDevice.set(true);
- mPeripheralManager.connectToDevice(UUID.fromString(userDevice.getDeviceId()));
+ mPeripheralManager.connectToDevice(UUID.fromString(userDevice.getDeviceId()),
+ mReconnectTimeoutSeconds);
} catch (Exception e) {
loge(TAG, "Exception while attempting connection with active user's device.", e);
}
diff --git a/connected-device-lib/src/com/android/car/connecteddevice/ble/CarBlePeripheralManager.java b/connected-device-lib/src/com/android/car/connecteddevice/ble/CarBlePeripheralManager.java
index 5dec98d..e3584d6 100644
--- a/connected-device-lib/src/com/android/car/connecteddevice/ble/CarBlePeripheralManager.java
+++ b/connected-device-lib/src/com/android/car/connecteddevice/ble/CarBlePeripheralManager.java
@@ -31,6 +31,8 @@
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.car.encryptionrunner.EncryptionRunnerFactory;
+import android.os.Handler;
+import android.os.Looper;
import android.os.ParcelUuid;
import com.android.car.connecteddevice.AssociationCallback;
@@ -79,6 +81,8 @@
private final BluetoothGattCharacteristic mReadCharacteristic;
+ private final Handler mTimeoutHandler;
+
// BLE default is 23, minus 3 bytes for ATT_PROTOCOL.
private int mWriteSize = 20;
@@ -117,6 +121,7 @@
| BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
mReadCharacteristic.addDescriptor(mDescriptor);
+ mTimeoutHandler = new Handler(Looper.getMainLooper());
}
@Override
@@ -143,8 +148,8 @@
mConnectedDevices.clear();
}
- /** Connect to device with provided id. */
- public void connectToDevice(@NonNull UUID deviceId) {
+ /** Attempt to connect to device with provided id within set timeout period. */
+ public void connectToDevice(@NonNull UUID deviceId, int timeoutSeconds) {
for (BleDevice device : mConnectedDevices) {
if (UUID.fromString(device.mDeviceId).equals(deviceId)) {
logd(TAG, "Already connected to device " + deviceId + ".");
@@ -160,11 +165,15 @@
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
- logd(TAG, "Successfully started advertising for device " + deviceId + ".");
+ mTimeoutHandler.postDelayed(mTimeoutRunnable,
+ TimeUnit.SECONDS.toMillis(timeoutSeconds));
+ logd(TAG, "Successfully started advertising for device " + deviceId
+ + " for " + timeoutSeconds + " seconds.");
}
};
mBlePeripheralManager.unregisterCallback(mAssociationPeripheralCallback);
mBlePeripheralManager.registerCallback(mReconnectPeripheralCallback);
+ mTimeoutHandler.removeCallbacks(mTimeoutRunnable);
startAdvertising(deviceId, mAdvertiseCallback, /* includeDeviceName = */ false);
}
@@ -314,6 +323,7 @@
private void addConnectedDevice(BluetoothDevice device, boolean isReconnect) {
EventLog.onDeviceConnected();
mBlePeripheralManager.stopAdvertising(mAdvertiseCallback);
+ mTimeoutHandler.removeCallbacks(mTimeoutRunnable);
mClientDeviceAddress = device.getAddress();
mClientDeviceName = device.getName();
if (mClientDeviceName == null) {
@@ -512,4 +522,12 @@
setDeviceId(deviceId);
}
};
+
+ private final Runnable mTimeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ logd(TAG, "Timeout period expired without a connection. Stopping advertisement.");
+ mBlePeripheralManager.stopAdvertising(mAdvertiseCallback);
+ }
+ };
}
diff --git a/connected-device-lib/tests/unit/src/com/android/car/connecteddevice/ConnectedDeviceManagerTest.java b/connected-device-lib/tests/unit/src/com/android/car/connecteddevice/ConnectedDeviceManagerTest.java
index 3d48578..579fe7d 100644
--- a/connected-device-lib/tests/unit/src/com/android/car/connecteddevice/ConnectedDeviceManagerTest.java
+++ b/connected-device-lib/tests/unit/src/com/android/car/connecteddevice/ConnectedDeviceManagerTest.java
@@ -22,6 +22,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mockitoSession;
import static org.mockito.Mockito.spy;
@@ -72,6 +73,8 @@
private static final String TEST_DEVICE_NAME = "TEST_DEVICE_NAME";
+ private static final int DEFAULT_RECONNECT_TIMEOUT = 5;
+
private final Executor mCallbackExecutor = Executors.newSingleThreadExecutor();
private final UUID mRecipientId = UUID.randomUUID();
@@ -100,7 +103,7 @@
ArgumentCaptor<AssociatedDeviceCallback> callbackCaptor = ArgumentCaptor
.forClass(AssociatedDeviceCallback.class);
mConnectedDeviceManager = new ConnectedDeviceManager(mMockStorage, mMockCentralManager,
- mMockPeripheralManager);
+ mMockPeripheralManager, DEFAULT_RECONNECT_TIMEOUT);
verify(mMockStorage).setAssociatedDeviceCallback(callbackCaptor.capture());
mAssociatedDeviceCallback = callbackCaptor.getValue();
mConnectedDeviceManager.start();
@@ -552,7 +555,7 @@
mConnectedDeviceManager.addConnectedDevice(deviceId, mMockPeripheralManager);
mConnectedDeviceManager.removeConnectedDevice(deviceId, mMockPeripheralManager);
verify(mMockPeripheralManager, timeout(1000))
- .connectToDevice(eq(UUID.fromString(deviceId)));
+ .connectToDevice(eq(UUID.fromString(deviceId)), anyInt());
}
@Test
@@ -568,7 +571,7 @@
mConnectedDeviceManager.addConnectedDevice(deviceId, mMockPeripheralManager);
mConnectedDeviceManager.removeConnectedDevice(deviceId, mMockPeripheralManager);
verify(mMockPeripheralManager, timeout(1000))
- .connectToDevice(eq(UUID.fromString(userDeviceId)));
+ .connectToDevice(eq(UUID.fromString(userDeviceId)), anyInt());
}
@Test
@@ -585,7 +588,7 @@
mConnectedDeviceManager.addConnectedDevice(userDeviceId, mMockCentralManager);
mConnectedDeviceManager.removeConnectedDevice(deviceId, mMockPeripheralManager);
verify(mMockPeripheralManager, timeout(1000).times(0))
- .connectToDevice(eq(UUID.fromString(userDeviceId)));
+ .connectToDevice(eq(UUID.fromString(userDeviceId)), anyInt());
}
@Test
diff --git a/connected-device-lib/tests/unit/src/com/android/car/connecteddevice/ble/CarBlePeripheralManagerTest.java b/connected-device-lib/tests/unit/src/com/android/car/connecteddevice/ble/CarBlePeripheralManagerTest.java
index adba67b..986f11e 100644
--- a/connected-device-lib/tests/unit/src/com/android/car/connecteddevice/ble/CarBlePeripheralManagerTest.java
+++ b/connected-device-lib/tests/unit/src/com/android/car/connecteddevice/ble/CarBlePeripheralManagerTest.java
@@ -92,7 +92,9 @@
@After
public void tearDown() {
- mCarBlePeripheralManager.stop();
+ if (mCarBlePeripheralManager != null) {
+ mCarBlePeripheralManager.stop();
+ }
if (mMockitoSession != null) {
mMockitoSession.finishMocking();
}
@@ -195,6 +197,18 @@
verify(callback).onAssociationError(eq(testErrorCode));
}
+ @Test
+ public void connectToDevice_stopsAdvertisingAfterTimeout() {
+ int timeoutSeconds = 2;
+ mCarBlePeripheralManager.connectToDevice(UUID.randomUUID(), timeoutSeconds);
+ ArgumentCaptor<AdvertiseCallback> callbackCaptor =
+ ArgumentCaptor.forClass(AdvertiseCallback.class);
+ verify(mMockPeripheralManager).startAdvertising(any(), any(), callbackCaptor.capture());
+ callbackCaptor.getValue().onStartSuccess(null);
+ verify(mMockPeripheralManager, timeout(TimeUnit.SECONDS.toMillis(timeoutSeconds + 1)))
+ .stopAdvertising(any(AdvertiseCallback.class));
+ }
+
private BlePeripheralManager.Callback startAssociation(AssociationCallback callback,
String deviceName) {
ArgumentCaptor<BlePeripheralManager.Callback> callbackCaptor =