Add blanket offer to L2capNetworkProvider

Registers a blanket reservation offer which will receive reservation
requests filed using ConnectivityManager#reserveNetwork.

Test: TH
Change-Id: I2d37fe564a6c40f2c6dee1f6f7aa655c47628b85
diff --git a/service/src/com/android/server/L2capNetworkProvider.java b/service/src/com/android/server/L2capNetworkProvider.java
index 694a1df..463a76f 100644
--- a/service/src/com/android/server/L2capNetworkProvider.java
+++ b/service/src/com/android/server/L2capNetworkProvider.java
@@ -16,11 +16,23 @@
 
 package com.android.server;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.RES_ID_MATCH_ALL_RESERVATIONS;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
 import static android.content.pm.PackageManager.FEATURE_BLUETOOTH_LE;
 
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
 import android.net.NetworkProvider;
+import android.net.NetworkProvider.NetworkOfferCallback;
+import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.os.Handler;
 import android.os.Looper;
 
@@ -32,7 +44,46 @@
     private final Dependencies mDeps;
     private final Handler mHandler;
     private final NetworkProvider mProvider;
-    private final boolean mIsSupported;
+    private final BlanketReservationOffer mBlanketOffer;
+
+    /**
+     * The blanket reservation offer is used to create an L2CAP server network, i.e. a network
+     * based on a BluetoothServerSocket.
+     *
+     * Note that NetworkCapabilities matching semantics will cause onNetworkNeeded to be called for
+     * requests that do not have a NetworkSpecifier set.
+     */
+    private class BlanketReservationOffer implements NetworkOfferCallback {
+        // TODO: ensure that once the incoming request is satisfied, the blanket offer does not get
+        // unneeded. This means the blanket offer must always outscore the reserved offer. This
+        // might require setting the blanket offer as setTransportPrimary().
+        public static final NetworkScore SCORE = new NetworkScore.Builder().build();
+        // Note the missing NET_CAPABILITY_NOT_RESTRICTED marking the network as restricted.
+        public static final NetworkCapabilities CAPABILITIES;
+        static {
+            NetworkCapabilities caps = NetworkCapabilities.Builder.withoutDefaultCapabilities()
+                    .addTransportType(TRANSPORT_BLUETOOTH)
+                    .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+                    .addCapability(NET_CAPABILITY_NOT_METERED)
+                    .addCapability(NET_CAPABILITY_NOT_ROAMING)
+                    .addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+                    .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+                    .addCapability(NET_CAPABILITY_NOT_VPN)
+                    .build();
+            caps.setReservationId(RES_ID_MATCH_ALL_RESERVATIONS);
+            CAPABILITIES = caps;
+        }
+
+        @Override
+        public void onNetworkNeeded(NetworkRequest request) {
+            // TODO: implement
+        }
+
+        @Override
+        public void onNetworkUnneeded(NetworkRequest request) {
+            // TODO: implement
+        }
+    }
 
     @VisibleForTesting
     public static class Dependencies {
@@ -51,12 +102,15 @@
         mDeps = deps;
         mHandler = handler;
         mProvider = mDeps.getNetworkProvider(context, handler.getLooper());
+        mBlanketOffer = new BlanketReservationOffer();
 
-        mIsSupported = context.getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH_LE);
-        if (mIsSupported) {
+        final boolean isBleSupported =
+                context.getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH_LE);
+        if (isBleSupported) {
             context.getSystemService(ConnectivityManager.class).registerNetworkProvider(mProvider);
+            mProvider.registerNetworkOffer(BlanketReservationOffer.SCORE,
+                    BlanketReservationOffer.CAPABILITIES, mHandler::post, mBlanketOffer);
         }
     }
-
 }
 
diff --git a/tests/unit/java/com/android/server/L2capNetworkProviderTest.kt b/tests/unit/java/com/android/server/L2capNetworkProviderTest.kt
index fc0b815..bb7f21c 100644
--- a/tests/unit/java/com/android/server/L2capNetworkProviderTest.kt
+++ b/tests/unit/java/com/android/server/L2capNetworkProviderTest.kt
@@ -33,6 +33,7 @@
 import org.mockito.ArgumentMatchers.any
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
+import org.mockito.Mockito.any
 import org.mockito.Mockito.doReturn
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
@@ -71,6 +72,7 @@
     fun testNetworkProvider_registeredWhenSupported() {
         L2capNetworkProvider(deps, context, handler)
         verify(cm).registerNetworkProvider(eq(provider))
+        verify(provider).registerNetworkOffer(any(), any(), any(), any())
     }
 
     @Test