Merge "Only allow one pending bluetooth request at a time" into main am: 7fb9055ff3

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/3529935

Change-Id: I0fd7325fcc76ee549ae76e3c24dbcd43ac169139
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Quang Luong 2025-03-11 15:04:01 -07:00 • committed by Automerger Merge Worker
commit 61137d7d56
2 changed files with 49 additions and 18 deletions

View file

@ -290,7 +290,10 @@ public class Tethering {
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 @@ public class Tethering {
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 @@ public class Tethering {
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 @@ public class Tethering {
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 @@ public class Tethering {
// 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();

View file

@ -101,6 +101,7 @@ import static org.mockito.Matchers.anyString;
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 @@ public class TetheringTest {
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 @@ public class TetheringTest {
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);