Merge "Only allow one pending bluetooth request at a time" into main
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 1589509..03bd52f 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -290,7 +290,10 @@
private SettingsObserver mSettingsObserver;
private BluetoothPan mBluetoothPan;
private PanServiceListener mBluetoothPanListener;
- private final ArrayList<IIntResultListener> mPendingPanRequestListeners;
+ // Pending listener for starting Bluetooth tethering before the PAN service is connected. Once
+ // the service is connected, the bluetooth iface will be requested and the listener will be
+ // called.
+ private IIntResultListener mPendingPanRequestListener;
// AIDL doesn't support Set<Integer>. Maintain a int bitmap here. When the bitmap is passed to
// TetheringManager, TetheringManager would convert it to a set of Integer types.
// mSupportedTypeBitmap should always be updated inside tethering internal thread but it may be
@@ -308,11 +311,6 @@
mTetheringMetrics = mDeps.makeTetheringMetrics(mContext);
mRequestTracker = new RequestTracker();
- // This is intended to ensrure that if something calls startTethering(bluetooth) just after
- // bluetooth is enabled. Before onServiceConnected is called, store the calls into this
- // list and handle them as soon as onServiceConnected is called.
- mPendingPanRequestListeners = new ArrayList<>();
-
mTetherStates = new ArrayMap<>();
mConnectedClientsTracker = new ConnectedClientsTracker();
@@ -862,15 +860,21 @@
if (!enable) {
// The service is not connected. If disabling tethering, there's no point starting
// the service just to stop tethering since tethering is not started. Just remove
- // any pending requests to enable tethering, and notify them that they have failed.
- for (IIntResultListener pendingListener : mPendingPanRequestListeners) {
- sendTetherResult(pendingListener, TETHER_ERROR_SERVICE_UNAVAIL,
+ // any pending request to enable tethering, and notify them that they have failed.
+ if (mPendingPanRequestListener != null) {
+ sendTetherResult(mPendingPanRequestListener, TETHER_ERROR_SERVICE_UNAVAIL,
TETHERING_BLUETOOTH);
}
- mPendingPanRequestListeners.clear();
+ mPendingPanRequestListener = null;
return TETHER_ERROR_NO_ERROR;
}
- mPendingPanRequestListeners.add(listener);
+
+ // Only allow one pending request at a time.
+ if (mPendingPanRequestListener != null) {
+ return TETHER_ERROR_SERVICE_UNAVAIL;
+ }
+
+ mPendingPanRequestListener = listener;
// Bluetooth tethering is not a popular feature. To avoid bind to bluetooth pan service all
// the time but user never use bluetooth tethering. mBluetoothPanListener is created first
@@ -897,12 +901,12 @@
mBluetoothPan = (BluetoothPan) proxy;
mIsConnected = true;
- for (IIntResultListener pendingListener : mPendingPanRequestListeners) {
+ if (mPendingPanRequestListener != null) {
final int result = setBluetoothTetheringSettings(mBluetoothPan,
true /* enable */);
- sendTetherResult(pendingListener, result, TETHERING_BLUETOOTH);
+ sendTetherResult(mPendingPanRequestListener, result, TETHERING_BLUETOOTH);
}
- mPendingPanRequestListeners.clear();
+ mPendingPanRequestListener = null;
});
}
@@ -913,11 +917,11 @@
// reachable before next onServiceConnected.
mIsConnected = false;
- for (IIntResultListener pendingListener : mPendingPanRequestListeners) {
- sendTetherResult(pendingListener, TETHER_ERROR_SERVICE_UNAVAIL,
+ if (mPendingPanRequestListener != null) {
+ sendTetherResult(mPendingPanRequestListener, TETHER_ERROR_SERVICE_UNAVAIL,
TETHERING_BLUETOOTH);
}
- mPendingPanRequestListeners.clear();
+ mPendingPanRequestListener = null;
mBluetoothIfaceRequest = null;
mBluetoothCallback = null;
maybeDisableBluetoothIpServing();
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 51efaf8..2a22c6d 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -101,6 +101,7 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
@@ -3571,6 +3572,32 @@
failedEnable.assertHasResult();
}
+ @Test
+ public void testStartBluetoothTetheringFailsWhenTheresAnExistingRequestWaitingForPanService()
+ throws Exception {
+ initTetheringOnTestThread();
+
+ mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
+ final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mTethering.startTethering(createTetheringRequest(TETHERING_BLUETOOTH),
+ TEST_CALLER_PKG, firstResult);
+ mLooper.dispatchAll();
+ firstResult.assertDoesNotHaveResult();
+
+ // Second request should fail.
+ final ResultListener secondResult = new ResultListener(TETHER_ERROR_SERVICE_UNAVAIL);
+ mTethering.startTethering(createTetheringRequest(TETHERING_BLUETOOTH),
+ TEST_CALLER_PKG, secondResult);
+ mLooper.dispatchAll();
+ secondResult.assertHasResult();
+ firstResult.assertDoesNotHaveResult();
+
+ // Bind to PAN service should succeed for first listener only. If the second result is
+ // called with TETHER_ERROR_NO_ERROR, ResultListener will fail an assertion.
+ verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
+ firstResult.assertHasResult();
+ }
+
private void mockBluetoothSettings(boolean bluetoothOn, boolean tetheringOn) {
when(mBluetoothAdapter.isEnabled()).thenReturn(bluetoothOn);
when(mBluetoothPan.isTetheringOn()).thenReturn(tetheringOn);
@@ -3618,7 +3645,7 @@
private ServiceListener verifySetBluetoothTethering(final boolean enable,
final boolean bindToPanService) throws Exception {
ServiceListener listener = null;
- verify(mBluetoothAdapter).isEnabled();
+ verify(mBluetoothAdapter, atLeastOnce()).isEnabled();
if (bindToPanService) {
final ArgumentCaptor<ServiceListener> listenerCaptor =
ArgumentCaptor.forClass(ServiceListener.class);