diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java index 1249e85b19..c6dad69200 100644 --- a/Tethering/src/com/android/networkstack/tethering/Tethering.java +++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java @@ -132,7 +132,6 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; -import android.util.Pair; import android.util.SparseArray; import androidx.annotation.NonNull; @@ -292,7 +291,7 @@ public class Tethering { private SettingsObserver mSettingsObserver; private BluetoothPan mBluetoothPan; private PanServiceListener mBluetoothPanListener; - private ArrayList> mPendingPanRequests; + private final ArrayList mPendingPanRequestListeners; // AIDL doesn't support Set. 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 @@ -312,7 +311,7 @@ public class Tethering { // 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. - mPendingPanRequests = new ArrayList<>(); + mPendingPanRequestListeners = new ArrayList<>(); mTetherStates = new ArrayMap<>(); mConnectedClientsTracker = new ConnectedClientsTracker(); @@ -862,12 +861,19 @@ public class Tethering { return; } - // The reference of IIntResultListener should only exist when application want to start - // tethering but tethering is not bound to pan service yet. Even if the calling process - // dies, the referenice of IIntResultListener would still keep in mPendingPanRequests. Once - // tethering bound to pan service (onServiceConnected) or bluetooth just crash - // (onServiceDisconnected), all the references from mPendingPanRequests would be cleared. - mPendingPanRequests.add(new Pair(enable, listener)); + 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, + TETHERING_BLUETOOTH); + } + mPendingPanRequestListeners.clear(); + sendTetherResult(listener, TETHER_ERROR_NO_ERROR, TETHERING_BLUETOOTH); + return; + } + mPendingPanRequestListeners.add(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 @@ -893,10 +899,11 @@ public class Tethering { mBluetoothPan = (BluetoothPan) proxy; mIsConnected = true; - for (Pair request : mPendingPanRequests) { - setBluetoothTetheringSettings(mBluetoothPan, request.first, request.second); + for (IIntResultListener pendingListener : mPendingPanRequestListeners) { + setBluetoothTetheringSettings(mBluetoothPan, true /* enable */, + pendingListener); } - mPendingPanRequests.clear(); + mPendingPanRequestListeners.clear(); }); } @@ -907,11 +914,11 @@ public class Tethering { // reachable before next onServiceConnected. mIsConnected = false; - for (Pair request : mPendingPanRequests) { - sendTetherResult(request.second, TETHER_ERROR_SERVICE_UNAVAIL, + for (IIntResultListener pendingListener : mPendingPanRequestListeners) { + sendTetherResult(pendingListener, TETHER_ERROR_SERVICE_UNAVAIL, TETHERING_BLUETOOTH); } - mPendingPanRequests.clear(); + mPendingPanRequestListeners.clear(); 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 f7a44f1c56..e1c2db90e3 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -93,7 +93,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.notNull; @@ -2787,6 +2786,10 @@ public class TetheringTest { public void assertHasResult() { if (!mHasResult) fail("No callback result"); } + + public void assertDoesNotHaveResult() { + if (mHasResult) fail("Has callback result"); + } } @Test @@ -3395,10 +3398,9 @@ public class TetheringTest { } @Test + @IgnoreUpTo(Build.VERSION_CODES.S_V2) public void testBluetoothTethering() throws Exception { initTetheringOnTestThread(); - // Switch to @IgnoreUpTo(Build.VERSION_CODES.S_V2) when it is available for AOSP. - assumeTrue(isAtLeastT()); final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR); mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */); @@ -3432,10 +3434,9 @@ public class TetheringTest { } @Test + @IgnoreAfter(Build.VERSION_CODES.S_V2) public void testBluetoothTetheringBeforeT() throws Exception { initTetheringOnTestThread(); - // Switch to @IgnoreAfter(Build.VERSION_CODES.S_V2) when it is available for AOSP. - assumeFalse(isAtLeastT()); final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR); mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */); @@ -3515,6 +3516,23 @@ public class TetheringTest { verifyNetdCommandForBtTearDown(); } + @Test + public void testPendingPanEnableRequestFailedUponDisableRequest() throws Exception { + initTetheringOnTestThread(); + + mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */); + final ResultListener failedEnable = new ResultListener(TETHER_ERROR_SERVICE_UNAVAIL); + mTethering.startTethering(createTetheringRequest(TETHERING_BLUETOOTH), + TEST_CALLER_PKG, failedEnable); + mLooper.dispatchAll(); + failedEnable.assertDoesNotHaveResult(); + + // Stop tethering before the pan service connects. This should fail the enable request. + mTethering.stopTethering(TETHERING_BLUETOOTH); + mLooper.dispatchAll(); + failedEnable.assertHasResult(); + } + private void mockBluetoothSettings(boolean bluetoothOn, boolean tetheringOn) { when(mBluetoothAdapter.isEnabled()).thenReturn(bluetoothOn); when(mBluetoothPan.isTetheringOn()).thenReturn(tetheringOn);