Merge changes Ic597ee91,I6a2b48aa,Ia71c79aa,I570dbff5,Ia063deea, ... into main

* changes:
  Get rid of volatile mIsAborted
  Get rid of volatile mIsRunning
  Add some more client network tests
  Add test for when BluetoothSocket#connect throws
  Add a test for when createInsecureL2capChannel throws
  Add a test that brings up an L2cap server network
  Move creating L2capIpClient to Dependencies
diff --git a/service/src/com/android/server/L2capNetworkProvider.java b/service/src/com/android/server/L2capNetworkProvider.java
index 814a068..9d070cd 100644
--- a/service/src/com/android/server/L2capNetworkProvider.java
+++ b/service/src/com/android/server/L2capNetworkProvider.java
@@ -61,6 +61,7 @@
 import com.android.net.module.util.HandlerUtils;
 import com.android.net.module.util.ServiceConnectivityJni;
 import com.android.server.net.L2capNetwork;
+import com.android.server.net.L2capNetwork.L2capIpClient;
 import com.android.server.net.L2capPacketForwarder;
 
 import java.io.IOException;
@@ -270,7 +271,6 @@
         private class AcceptThread extends Thread {
             private static final int TIMEOUT_MS = 500;
             private final BluetoothServerSocket mServerSocket;
-            private volatile boolean mIsRunning = true;
 
             public AcceptThread(BluetoothServerSocket serverSocket) {
                 super("L2capNetworkProvider-AcceptThread");
@@ -294,16 +294,17 @@
 
             @Override
             public void run() {
-                while (mIsRunning) {
+                while (true) {
                     final BluetoothSocket connectedSocket;
                     try {
                         connectedSocket = mServerSocket.accept();
                     } catch (IOException e) {
-                        // BluetoothServerSocket was closed().
-                        if (!mIsRunning) return;
-
-                        // Else, BluetoothServerSocket encountered exception.
-                        Log.e(TAG, "BluetoothServerSocket#accept failed", e);
+                        // Note calling BluetoothServerSocket#close() also triggers an IOException
+                        // which is indistinguishable from any other exceptional behavior.
+                        // postDestroyAndUnregisterReservedOffer() is always safe to call as it
+                        // first checks whether the offer still exists; so if the
+                        // BluetoothServerSocket was closed (i.e. on tearDown()) this is a noop.
+                        Log.w(TAG, "BluetoothServerSocket closed or #accept failed", e);
                         postDestroyAndUnregisterReservedOffer();
                         return; // stop running immediately on error
                     }
@@ -313,7 +314,6 @@
 
             public void tearDown() {
                 HandlerUtils.ensureRunningOnHandlerThread(mHandler);
-                mIsRunning = false;
                 try {
                     // BluetoothServerSocket.close() is thread-safe.
                     mServerSocket.close();
@@ -434,7 +434,6 @@
         private class ConnectThread extends Thread {
             private final L2capNetworkSpecifier mSpecifier;
             private final BluetoothSocket mSocket;
-            private volatile boolean mIsAborted = false;
 
             public ConnectThread(L2capNetworkSpecifier specifier, BluetoothSocket socket) {
                 super("L2capNetworkProvider-ConnectThread");
@@ -451,11 +450,12 @@
                         if (!success) closeBluetoothSocket(mSocket);
                     });
                 } catch (IOException e) {
-                    Log.e(TAG, "Failed to connect", e);
-                    if (mIsAborted) return;
-
+                    Log.w(TAG, "BluetoothSocket was closed or #connect failed", e);
+                    // It is safe to call BluetoothSocket#close() multiple times.
                     closeBluetoothSocket(mSocket);
                     mHandler.post(() -> {
+                        // Note that if the Socket was closed, this call is a noop as the
+                        // ClientNetworkRequest has already been removed.
                         declareAllNetworkRequestsUnfulfillable(mSpecifier);
                     });
                 }
@@ -463,7 +463,6 @@
 
             public void abort() {
                 HandlerUtils.ensureRunningOnHandlerThread(mHandler);
-                mIsAborted = true;
                 // Closing the BluetoothSocket is the only way to unblock connect() because it calls
                 // shutdown on the underlying (connected) SOCK_SEQPACKET.
                 // It is safe to call BluetoothSocket#close() multiple times.
@@ -680,6 +679,11 @@
                 L2capPacketForwarder.ICallback cb) {
             return new L2capPacketForwarder(handler, tunFd, socket, compressHeaders, cb);
         }
+
+        /** Create an L2capIpClient */
+        public L2capIpClient createL2capIpClient(String logTag, Context context, String ifname) {
+            return new L2capIpClient(logTag, context, ifname);
+        }
     }
 
     public L2capNetworkProvider(Context context) {
diff --git a/service/src/com/android/server/net/L2capNetwork.java b/service/src/com/android/server/net/L2capNetwork.java
index c7417f9..ca155db 100644
--- a/service/src/com/android/server/net/L2capNetwork.java
+++ b/service/src/com/android/server/net/L2capNetwork.java
@@ -52,7 +52,7 @@
      *
      * Note that the IpClient does not need to be stopped.
      */
-    private static class L2capIpClient extends IpClientCallbacks {
+    public static class L2capIpClient extends IpClientCallbacks {
         private final String mLogTag;
         private final ConditionVariable mOnIpClientCreatedCv = new ConditionVariable(false);
         private final ConditionVariable mOnProvisioningSuccessCv = new ConditionVariable(false);
@@ -61,7 +61,7 @@
         @Nullable
         private volatile LinkProperties mLinkProperties;
 
-        L2capIpClient(String logTag, Context context, String ifname) {
+        public L2capIpClient(String logTag, Context context, String ifname) {
             mLogTag = logTag;
             IpClientUtil.makeIpClient(context, ifname, this);
         }
@@ -157,7 +157,7 @@
         // LinkProperties) or fails (and returns null).
         // Note that since L2capNetwork is using IPv6 link-local provisioning the most likely
         // (only?) failure mode is due to the interface disappearing.
-        final LinkProperties lp = new L2capIpClient(logTag, context, ifname).start();
+        final LinkProperties lp = deps.createL2capIpClient(logTag, context, ifname).start();
         if (lp == null) return null;
 
         return new L2capNetwork(
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
index 489c3ad..babcba9 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
@@ -17,25 +17,37 @@
 package com.android.server
 
 import android.bluetooth.BluetoothAdapter
-import android.bluetooth.BluetoothManager
+import android.bluetooth.BluetoothDevice
 import android.bluetooth.BluetoothServerSocket
 import android.bluetooth.BluetoothSocket
+import android.net.INetworkMonitor
+import android.net.INetworkMonitorCallbacks
+import android.net.IpPrefix
 import android.net.L2capNetworkSpecifier
 import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN
 import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_NONE
+import android.net.L2capNetworkSpecifier.ROLE_CLIENT
 import android.net.L2capNetworkSpecifier.ROLE_SERVER
+import android.net.LinkAddress
+import android.net.LinkProperties
+import android.net.MacAddress
 import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED
 import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
 import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
 import android.net.NetworkRequest
 import android.net.NetworkSpecifier
+import android.net.RouteInfo
 import android.os.Build
 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.Reserved
 import com.android.testutils.RecorderCallback.CallbackEntry.Unavailable
 import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.anyNetwork
 import com.android.testutils.waitForIdle
 import java.io.IOException
 import java.util.Optional
@@ -47,10 +59,13 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isNull
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.doThrow
 import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
 
 private const val PSM = 0x85
 private val REMOTE_MAC = byteArrayOf(1, 2, 3, 4, 5, 6)
@@ -64,10 +79,17 @@
 @IgnoreUpTo(Build.VERSION_CODES.R)
 @DevSdkIgnoreRunner.MonitorThreadLeak
 class CSL2capProviderTest : CSTest() {
+    private val networkMonitor = mock<INetworkMonitor>()
+
     private val btAdapter = mock<BluetoothAdapter>()
+    private val btDevice = mock<BluetoothDevice>()
     private val btServerSocket = mock<BluetoothServerSocket>()
     private val btSocket = mock<BluetoothSocket>()
+    private val tunInterface = mock<ParcelFileDescriptor>()
+    private val l2capIpClient = mock<L2capIpClient>()
+    private val packetForwarder = mock<L2capPacketForwarder>()
     private val providerDeps = mock<L2capNetworkProvider.Dependencies>()
+
     // BlockingQueue does not support put(null) operations, as null is used as an internal sentinel
     // value. Therefore, use Optional<BluetoothSocket> where an empty optional signals the
     // BluetoothServerSocket#close() operation.
@@ -84,6 +106,8 @@
         doReturn(btAdapter).`when`(bluetoothManager).getAdapter()
         doReturn(btServerSocket).`when`(btAdapter).listenUsingInsecureL2capChannel()
         doReturn(PSM).`when`(btServerSocket).getPsm()
+        doReturn(btDevice).`when`(btAdapter).getRemoteDevice(eq(REMOTE_MAC))
+        doReturn(btSocket).`when`(btDevice).createInsecureL2capChannel(eq(PSM))
 
         doAnswer {
             val sock = acceptQueue.take()
@@ -96,6 +120,30 @@
         }.`when`(btServerSocket).close()
 
         doReturn(handlerThread).`when`(providerDeps).getHandlerThread()
+        doReturn(tunInterface).`when`(providerDeps).createTunInterface(any())
+        doReturn(packetForwarder).`when`(providerDeps)
+                .createL2capPacketForwarder(any(), any(), any(), any(), any())
+        doReturn(l2capIpClient).`when`(providerDeps).createL2capIpClient(any(), any(), any())
+
+        val lp = LinkProperties()
+        val ifname = "l2cap-tun0"
+        lp.setInterfaceName(ifname)
+        lp.addLinkAddress(LinkAddress("fe80::1/64"))
+        lp.addRoute(RouteInfo(IpPrefix("fe80::/64"), null /* nextHop */, ifname))
+        doReturn(lp).`when`(l2capIpClient).start()
+
+        // Note: In order to properly register a NetworkAgent, a NetworkMonitor must be created for
+        // the agent. CSAgentWrapper already does some of this, but requires adding additional
+        // Dependencies to the production code. Create a mocked NM inside this test instead.
+        doAnswer { i ->
+            val cb = i.arguments[2] as INetworkMonitorCallbacks
+            cb.onNetworkMonitorCreated(networkMonitor)
+        }.`when`(networkStack).makeNetworkMonitor(
+                any() /* network */,
+                isNull() /* name */,
+                any() /* callbacks */
+        )
+
         provider = L2capNetworkProvider(providerDeps, context)
         provider.start()
     }
@@ -241,4 +289,109 @@
         cb2.expect<Reserved>()
         cb2.assertNoCallback()
     }
+
+    @Test
+    fun testServerNetwork() {
+        val specifier = L2capNetworkSpecifier.Builder()
+                .setRole(ROLE_SERVER)
+                .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+                .build()
+        val nr = REQUEST.copyWithSpecifier(specifier)
+        val cb = reserveNetwork(nr)
+        cb.expect<Reserved>()
+
+        // Unblock BluetoothServerSocket#accept()
+        doReturn(true).`when`(btSocket).isConnected()
+        acceptQueue.put(Optional.of(btSocket))
+
+        cb.expectAvailableCallbacks(anyNetwork(), validated = false)
+        cb.assertNoCallback()
+        // Verify that packet forwarding was started.
+        // TODO: stop mocking L2capPacketForwarder.
+        verify(providerDeps).createL2capPacketForwarder(any(), any(), any(), any(), any())
+    }
+
+    @Test
+    fun testBluetoothException_createInsecureL2capChannelThrows() {
+        doThrow(IOException()).`when`(btDevice).createInsecureL2capChannel(any())
+
+        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.expect<Unavailable>()
+    }
+
+    @Test
+    fun testBluetoothException_bluetoothSocketConnectThrows() {
+        doThrow(IOException()).`when`(btSocket).connect()
+
+        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.expect<Unavailable>()
+    }
+
+    @Test
+    fun testClientNetwork() {
+        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)
+    }
+
+    @Test
+    fun testClientNetwork_headerCompressionMismatch() {
+        var specifier = L2capNetworkSpecifier.Builder()
+                .setRole(ROLE_CLIENT)
+                .setHeaderCompression(HEADER_COMPRESSION_NONE)
+                .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC))
+                .setPsm(PSM)
+                .build()
+        var nr = REQUEST.copyWithSpecifier(specifier)
+        val cb = requestNetwork(nr)
+        cb.expectAvailableCallbacks(anyNetwork(), validated = false)
+
+        specifier = L2capNetworkSpecifier.Builder()
+                .setRole(ROLE_CLIENT)
+                .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+                .setRemoteAddress(MacAddress.fromBytes(REMOTE_MAC))
+                .setPsm(PSM)
+                .build()
+        nr = REQUEST.copyWithSpecifier(specifier)
+        val cb2 = requestNetwork(nr)
+        cb2.expect<Unavailable>()
+    }
+
+    @Test
+    fun testClientNetwork_multipleRequests() {
+        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)
+
+        val cb2 = requestNetwork(nr)
+        cb2.expectAvailableCallbacks(anyNetwork(), validated = false)
+    }
 }