From 1a2b238bc1466e7d0a252ab38b959d4df918aeeb Mon Sep 17 00:00:00 2001 From: Quang Anh Luong Date: Tue, 3 Jun 2025 14:59:07 +0900 Subject: [PATCH] Remove pending BT request when service disconnects If the PAN service disconnects before the BT iface is available, the BT TetheringRequest will be stuck pending and prevent any new BT startTethering attempts. This may happen if the user turns BT tethering on and turns off BT without connecting to any device. Fix this by always removing the pending BT request when the PAN service disconnects. Flag: EXEMPT minor bugfix Bug: 420412916 Test: atest TetheringTest, manually verify turning BT tethering on, turning off BT, and turning BT tethering on again. Change-Id: I6bc96d6e4016d83af6cfd8e549d474ce651a44fc --- .../networkstack/tethering/Tethering.java | 16 +++--- .../networkstack/tethering/TetheringTest.java | 53 ++++++++++++++++--- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 771a9ff7b0..00103d68af 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -960,13 +960,15 @@ public class Tethering { // reachable before next onServiceConnected. mIsConnected = false; - if (mPendingPanRequestListener != null) { - sendTetherResultAndRemoveOnError( - mRequestTracker.getOrCreatePendingRequest(TETHERING_BLUETOOTH), - mPendingPanRequestListener, - TETHER_ERROR_SERVICE_UNAVAIL); - } - mPendingPanRequestListener = null; + // Remove any pending request even if bluetooth tethering is not started. + // Call sendTetherResultAndRemoveOnError for convenience (and to update the metrics) + // even though mPendingPanRequestListener is guaranteed to be null here (when the + // service connects onServiceConnected sets it to null, and whenever the service is + // connected, it's never set to non-null). + sendTetherResultAndRemoveOnError( + mRequestTracker.getOrCreatePendingRequest(TETHERING_BLUETOOTH), + null /* listener */, + TETHER_ERROR_SERVICE_UNAVAIL); 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 a68bc51be9..5614a35219 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -3292,6 +3292,10 @@ public class TetheringTest { @Override public void onResult(final int resultCode) { + if (mHasResult) { + fail("Received result: " + resultCode + ", but we already have a result: " + + mExpectedResult); + } mHasResult = true; if (resultCode != mExpectedResult) { fail("expected result: " + mExpectedResult + " but actual result: " + resultCode); @@ -3314,11 +3318,9 @@ public class TetheringTest { final LinkAddress serverLinkAddr = new LinkAddress("192.168.20.1/24"); final LinkAddress clientLinkAddr = new LinkAddress("192.168.20.42/24"); final String serverAddr = "192.168.20.1"; - final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR); - final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR); - final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR); // Enable USB tethering and check that Tethering starts USB. + final ResultListener firstResult = new ResultListener(TETHER_ERROR_NO_ERROR); mTethering.startTethering(createTetheringRequest(TETHERING_USB), TEST_CALLER_PKG, firstResult); mLooper.dispatchAll(); @@ -3327,6 +3329,7 @@ public class TetheringTest { verifyNoMoreInteractions(mUsbManager); // Enable USB tethering again with the same request and expect no change to USB. + final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR); mTethering.startTethering(createTetheringRequest(TETHERING_USB), TEST_CALLER_PKG, secondResult); mLooper.dispatchAll(); @@ -3336,22 +3339,24 @@ public class TetheringTest { // Enable USB tethering again with the same request but different uid/package and expect no // change to USB. + final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR); TetheringRequest differentUidPackage = createTetheringRequest(TETHERING_USB); differentUidPackage.setUid(TEST_CALLER_UID_2); differentUidPackage.setPackageName(TEST_CALLER_PKG_2); - mTethering.startTethering(differentUidPackage, TEST_CALLER_PKG_2, secondResult); + mTethering.startTethering(differentUidPackage, TEST_CALLER_PKG_2, thirdResult); mLooper.dispatchAll(); - secondResult.assertHasResult(); + thirdResult.assertHasResult(); verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE); reset(mUsbManager); // Enable USB tethering with a different request and expect that USB is stopped and // started. + final ResultListener fourthResult = new ResultListener(TETHER_ERROR_NO_ERROR); mTethering.startTethering(createTetheringRequest(TETHERING_USB, serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL, null), - TEST_CALLER_PKG, thirdResult); + TEST_CALLER_PKG, fourthResult); mLooper.dispatchAll(); - thirdResult.assertHasResult(); + fourthResult.assertHasResult(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); @@ -3988,6 +3993,38 @@ public class TetheringTest { verifyNetdCommandForBtTearDown(); } + @Test + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + public void testPendingBluetoothRequestRemovedWhenPanServiceDisconnectsBeforeIfaceAvailable() + throws Exception { + initTetheringOnTestThread(); + + final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR); + mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */); + mTethering.startTethering(createTetheringRequest(TETHERING_BLUETOOTH), + TEST_CALLER_PKG, result); + mLooper.dispatchAll(); + ServiceListener serviceListener = + verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */); + result.assertHasResult(); + + // Mock BT turning off + serviceListener.onServiceDisconnected(BluetoothProfile.PAN); + mLooper.dispatchAll(); + + // Pending request should be removed + assertTrue(mTethering.getPendingTetheringRequests().isEmpty()); + // Mock BT tethering started again should succeed + final ResultListener result2 = new ResultListener(TETHER_ERROR_NO_ERROR); + mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */); + mTethering.startTethering(createTetheringRequest(TETHERING_BLUETOOTH), + TEST_CALLER_PKG, result2); + serviceListener.onServiceConnected(BluetoothProfile.PAN, mBluetoothPan); + mLooper.dispatchAll(); + verifySetBluetoothTethering(true /* enable */, false /* bindToPanService */); + result2.assertHasResult(); + } + @Test @IgnoreAfter(Build.VERSION_CODES.S_V2) public void testBluetoothTetheringBeforeT() throws Exception { @@ -4203,7 +4240,7 @@ public class TetheringTest { } verify(mBluetoothPan).isTetheringOn(); verifyNoMoreInteractions(mBluetoothAdapter, mBluetoothPan); - reset(mBluetoothAdapter, mBluetoothPan); + reset(mBluetoothAdapter, mBluetoothPan, mBluetoothPanShim); return listener; }