Tell netd about local networks

This patch does the following :
• When a network is local but the system doesn't support it,
  throw in registerNetworkAgent.
• When a network is local, tell netd about it.
• Add tests for the above, as well as a test for callbacks
  being sent correctly when the caps are set and not when
  they aren't and that the new keep connected flag is
  respected.

Telling netd about the network being local has it add two
routes that do not exist if the network is not local :
• One at PRIORITY_LOCAL_NETWORK matching unmarked sockets
  and looking up the table for the interface.
20000: from all fwmark 0x0/0x10000 lookup xxxx
• One at PRIORITY_EXPLICIT_NETWORK matching explicitly on
  network 99, so that dnsmasq traffic and any OEM traffic
  marked for network 99 flowing there.
16000: from all fwmark 0x10063/0x1ffff iif lo lookup xxxx
IMPLICIT_NETWORK and EXPLICIT_NETWORK rules are installed
by connectivity service as a matter of course whether the
network is local or not.

See commit If8729fc6f3716a580c936584b851bc38000b5de5 for
implementation details of this mechanic.

There is no need to implement anything in particular for the
new connected reason, as the current implementation will
already keep it connected. A new test makes sure of that.

Test: FrameworksNetTests
      CtsNetTestCases
      FrameworksNetIntegrationTests
      NetworkStackTests
      NetworkStaticLibTests
      TetheringTests
      MtsTetheringTestLatestSdk
      TetheringIntegrationTests
Change-Id: I6fb7dfe4c232eea8cac9ac268897ddb36bb794d1
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 986d58b..ba0cad3 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
 import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
 import static android.content.pm.PackageManager.FEATURE_BLUETOOTH;
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
 import static android.content.pm.PackageManager.FEATURE_WATCH;
 import static android.content.pm.PackageManager.FEATURE_WIFI;
 import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
@@ -67,6 +68,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_ENTERPRISE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK;
 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_RESTRICTED;
@@ -5006,7 +5008,10 @@
                         !nai.networkAgentConfig.allowBypass /* secure */,
                         getVpnType(nai), nai.networkAgentConfig.excludeLocalRouteVpn);
             } else {
-                config = new NativeNetworkConfig(nai.network.getNetId(), NativeNetworkType.PHYSICAL,
+                final boolean hasLocalCap =
+                        nai.networkCapabilities.hasCapability(NET_CAPABILITY_LOCAL_NETWORK);
+                config = new NativeNetworkConfig(nai.network.getNetId(),
+                        hasLocalCap ? NativeNetworkType.PHYSICAL_LOCAL : NativeNetworkType.PHYSICAL,
                         getNetworkPermission(nai.networkCapabilities),
                         false /* secure */,
                         VpnManager.TYPE_VPN_NONE,
@@ -8056,6 +8061,18 @@
     }
 
     /**
+     * Returns whether local agents are supported on this device.
+     *
+     * Local agents are supported from U on TVs, and from V on all devices.
+     */
+    @VisibleForTesting
+    public boolean areLocalAgentsSupported() {
+        final PackageManager pm = mContext.getPackageManager();
+        // Local agents are supported starting on U on TVs and on V on everything else.
+        return mDeps.isAtLeastV() || (mDeps.isAtLeastU() && pm.hasSystemFeature(FEATURE_LEANBACK));
+    }
+
+    /**
      * Register a new agent with ConnectivityService to handle a network.
      *
      * @param na a reference for ConnectivityService to contact the agent asynchronously.
@@ -8084,6 +8101,12 @@
         } else {
             enforceNetworkFactoryPermission();
         }
+        final boolean hasLocalCap =
+                networkCapabilities.hasCapability(NET_CAPABILITY_LOCAL_NETWORK);
+        if (hasLocalCap && !areLocalAgentsSupported()) {
+            // Before U, netd doesn't support PHYSICAL_LOCAL networks so this can't work.
+            throw new IllegalArgumentException("Local agents are not supported in this version");
+        }
 
         final int uid = mDeps.getCallingUid();
         final long token = Binder.clearCallingIdentity();
@@ -9189,7 +9212,7 @@
             // are Type.LISTEN, but should not have NetworkCallbacks invoked.
             return;
         }
-        Bundle bundle = new Bundle();
+        final Bundle bundle = new Bundle();
         // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
         // TODO: check if defensive copies of data is needed.
         final NetworkRequest nrForCallback = nri.getNetworkRequestForCallback();
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 0f72cd4..8d0d711 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -64,7 +64,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.WakeupMessage;
-import com.android.modules.utils.build.SdkLevel;
 import com.android.server.ConnectivityService;
 
 import java.io.PrintWriter;
@@ -470,8 +469,8 @@
                     + networkCapabilities.getOwnerUid() + " to " + nc.getOwnerUid());
             nc.setOwnerUid(networkCapabilities.getOwnerUid());
         }
-        restrictCapabilitiesFromNetworkAgent(
-                nc, creatorUid, mHasAutomotiveFeature, carrierPrivilegeAuthenticator);
+        restrictCapabilitiesFromNetworkAgent(nc, creatorUid, mHasAutomotiveFeature,
+                mConnServiceDeps, carrierPrivilegeAuthenticator);
         return nc;
     }
 
@@ -601,6 +600,7 @@
     private static final String TAG = ConnectivityService.class.getSimpleName();
     private static final boolean VDBG = false;
     private final ConnectivityService mConnService;
+    private final ConnectivityService.Dependencies mConnServiceDeps;
     private final Context mContext;
     private final Handler mHandler;
     private final QosCallbackTracker mQosCallbackTracker;
@@ -628,6 +628,7 @@
         networkCapabilities = nc;
         networkAgentConfig = config;
         mConnService = connService;
+        mConnServiceDeps = deps;
         setScore(score); // uses members connService, networkCapabilities and networkAgentConfig
         clatd = new Nat464Xlat(this, netd, dnsResolver, deps);
         mContext = context;
@@ -1518,23 +1519,26 @@
      */
     public static void restrictCapabilitiesFromNetworkAgent(@NonNull final NetworkCapabilities nc,
             final int creatorUid, final boolean hasAutomotiveFeature,
+            @NonNull final ConnectivityService.Dependencies deps,
             @Nullable final CarrierPrivilegeAuthenticator authenticator) {
         if (nc.hasTransport(TRANSPORT_TEST)) {
             nc.restrictCapabilitiesForTestNetwork(creatorUid);
         }
-        if (!areAllowedUidsAcceptableFromNetworkAgent(nc, hasAutomotiveFeature, authenticator)) {
+        if (!areAllowedUidsAcceptableFromNetworkAgent(
+                nc, hasAutomotiveFeature, deps, authenticator)) {
             nc.setAllowedUids(new ArraySet<>());
         }
     }
 
     private static boolean areAllowedUidsAcceptableFromNetworkAgent(
             @NonNull final NetworkCapabilities nc, final boolean hasAutomotiveFeature,
+            @NonNull final ConnectivityService.Dependencies deps,
             @Nullable final CarrierPrivilegeAuthenticator carrierPrivilegeAuthenticator) {
         // NCs without access UIDs are fine.
         if (!nc.hasAllowedUids()) return true;
         // S and below must never accept access UIDs, even if an agent sends them, because netd
         // didn't support the required feature in S.
-        if (!SdkLevel.isAtLeastT()) return false;
+        if (!deps.isAtLeastT()) return false;
 
         // On a non-restricted network, access UIDs make no sense
         if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) return false;