Release ClientNetworkRequest before marking request unfulfillable

Otherwise, the tear down code that unregisters the NetworkAgent runs
after calling declareNetworkRequestUnfulfillable() which surpresses the
onLost() callback.

Test: CSL2capProviderTest
Change-Id: I447881bd4bd76ba492abf6cc3177143db626749e
This commit is contained in:
Patrick Rohr 2025-03-19 17:05:58 -07:00
commit 512b789a8d
2 changed files with 36 additions and 1 deletions

View file

@ -597,10 +597,12 @@ public class L2capNetworkProvider {
final ClientRequestInfo cri = mClientNetworkRequests.get(specifier);
if (cri == null) return;
// Release ClientNetworkRequest before sending onUnavailable() to ensure the app
// first receives an onLost() callback if a network had been created.
releaseClientNetworkRequest(cri);
for (NetworkRequest request : cri.requests) {
mProvider.declareNetworkRequestUnfulfillable(request);
}
releaseClientNetworkRequest(cri);
}
}

View file

@ -38,12 +38,14 @@ import android.net.NetworkRequest
import android.net.NetworkSpecifier
import android.net.RouteInfo
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.os.ParcelFileDescriptor
import com.android.server.net.L2capNetwork.L2capIpClient
import com.android.server.net.L2capPacketForwarder
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
import com.android.testutils.RecorderCallback.CallbackEntry.Reserved
import com.android.testutils.RecorderCallback.CallbackEntry.Unavailable
import com.android.testutils.TestableNetworkCallback
@ -59,6 +61,7 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.eq
import org.mockito.ArgumentMatchers.isNull
import org.mockito.Mockito.doAnswer
@ -394,4 +397,34 @@ class CSL2capProviderTest : CSTest() {
val cb2 = requestNetwork(nr)
cb2.expectAvailableCallbacks(anyNetwork(), validated = false)
}
/** Test to ensure onLost() is sent before onUnavailable() when the network is torn down. */
@Test
fun testClientNetwork_gracefulTearDown() {
val specifier = L2capNetworkSpecifier.Builder()
.setRole(ROLE_CLIENT)
.setHeaderCompression(HEADER_COMPRESSION_NONE)
.setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC))
.setPsm(PSM)
.build()
val nr = REQUEST.copyWithSpecifier(specifier)
val cb = requestNetwork(nr)
cb.expectAvailableCallbacks(anyNetwork(), validated = false)
// Capture the L2capPacketForwarder callback object to tear down the network.
val handlerCaptor = ArgumentCaptor.forClass(Handler::class.java)
val forwarderCbCaptor = ArgumentCaptor.forClass(L2capPacketForwarder.ICallback::class.java)
verify(providerDeps).createL2capPacketForwarder(
handlerCaptor.capture(), any(), any(), any(), forwarderCbCaptor.capture())
val handler = handlerCaptor.value
val forwarderCb = forwarderCbCaptor.value
// Trigger a forwarding error
handler.post { forwarderCb.onError() }
handler.waitForIdle(HANDLER_TIMEOUT_MS)
cb.expect<Lost>()
cb.expect<Unavailable>()
}
}