Add a routing coordinator class

Test: In followup changesĀ : RoutingCoordinatorManagerTest
Change-Id: Ia6811d614e02123a072c7638291828745abae051
diff --git a/framework/Android.bp b/framework/Android.bp
index 182c558..02c678a 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -292,17 +292,20 @@
 // Library providing limited APIs within the connectivity module, so that R+ components like
 // Tethering have a controlled way to depend on newer components like framework-connectivity that
 // are not loaded on R.
+// Note that this target needs to have access to hidden classes, and as such needs to list
+// the full libraries instead of the .impl lib (which only expose API classes).
 java_library {
     name: "connectivity-internal-api-util",
     sdk_version: "module_current",
     libs: [
         "androidx.annotation_annotation",
-        "framework-connectivity.impl",
+        "framework-connectivity-pre-jarjar",
     ],
     jarjar_rules: ":framework-connectivity-jarjar-rules",
     srcs: [
-        // Files listed here MUST all be annotated with @RequiresApi(Build.VERSION_CODES.TIRAMISU),
-        // so that API checks are enforced for R+ users of this library
+        // Files listed here MUST all be annotated with @RequiresApi(Build.VERSION_CODES.S)
+        // or above as appropriate so that API checks are enforced for R+ users of this library
+        "src/android/net/RoutingCoordinatorManager.java",
         "src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java",
     ],
     visibility: [
diff --git a/framework/jarjar-excludes.txt b/framework/jarjar-excludes.txt
index 1ac5e8e..bc3c8d1 100644
--- a/framework/jarjar-excludes.txt
+++ b/framework/jarjar-excludes.txt
@@ -14,6 +14,15 @@
 # TODO: move files to android.net.connectivity.visiblefortesting
 android\.net\.IConnectivityDiagnosticsCallback(\$.+)?
 
+# Classes used by tethering as a hidden API are compiled as a lib in target
+# connectivity-internal-api-util. Because it's used by tethering, it can't
+# be jarjared. Classes in android.net.connectivity are exempt from being
+# listed here because they are already in the target package and as such
+# are already not jarjared.
+# Because Tethering can be installed on R without Connectivity, any use
+# of these classes must be protected by a check for >= S SDK.
+# It's unlikely anybody else declares a hidden class with this name ?
+android\.net\.RoutingCoordinatorManager(\$.+)?
 
 # KeepaliveUtils is used by ConnectivityManager CTS
 # TODO: move into service-connectivity so framework-connectivity stops using
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 915c20d..5cc1968 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -29,6 +29,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresApi;
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -6174,4 +6175,24 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    private static final Object sRoutingCoordinatorManagerLock = new Object();
+    @GuardedBy("sRoutingCoordinatorManagerLock")
+    private static RoutingCoordinatorManager sRoutingCoordinatorManager = null;
+    /** @hide */
+    @RequiresApi(Build.VERSION_CODES.S)
+    public RoutingCoordinatorManager getRoutingCoordinatorManager() {
+        try {
+            synchronized (sRoutingCoordinatorManagerLock) {
+                if (null == sRoutingCoordinatorManager) {
+                    sRoutingCoordinatorManager = new RoutingCoordinatorManager(mContext,
+                            IRoutingCoordinator.Stub.asInterface(
+                                    mService.getRoutingCoordinatorService()));
+                }
+                return sRoutingCoordinatorManager;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index fe27773..e495448 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -259,4 +259,6 @@
     void setVpnNetworkPreference(String session, in UidRange[] ranges);
 
     void setTestLowTcpPollingTimerForKeepalive(long timeMs);
+
+    IBinder getRoutingCoordinatorService();
 }
diff --git a/framework/src/android/net/IRoutingCoordinator.aidl b/framework/src/android/net/IRoutingCoordinator.aidl
new file mode 100644
index 0000000..a5cda98
--- /dev/null
+++ b/framework/src/android/net/IRoutingCoordinator.aidl
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.RouteInfo;
+
+/** @hide */
+interface IRoutingCoordinator {
+   /**
+    * Add a route for specific network
+    *
+    * @param netId the network to add the route to
+    * @param route the route to add
+    * @throws ServiceSpecificException in case of failure, with an error code indicating the
+    *         cause of the failure.
+    */
+    void addRoute(int netId, in RouteInfo route);
+
+   /**
+    * Remove a route for specific network
+    *
+    * @param netId the network to remove the route from
+    * @param route the route to remove
+    * @throws ServiceSpecificException in case of failure, with an error code indicating the
+    *         cause of the failure.
+    */
+    void removeRoute(int netId, in RouteInfo route);
+
+    /**
+    * Update a route for specific network
+    *
+    * @param netId the network to update the route for
+    * @param route parcelable with route information
+    * @throws ServiceSpecificException in case of failure, with an error code indicating the
+    *         cause of the failure.
+    */
+    void updateRoute(int netId, in RouteInfo route);
+
+    /**
+     * Adds an interface to a network. The interface must not be assigned to any network, including
+     * the specified network.
+     *
+     * @param netId the network to add the interface to.
+     * @param iface the name of the interface to add.
+     *
+     * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+     *         unix errno.
+     */
+    void addInterfaceToNetwork(int netId, in String iface);
+
+    /**
+     * Removes an interface from a network. The interface must be assigned to the specified network.
+     *
+     * @param netId the network to remove the interface from.
+     * @param iface the name of the interface to remove.
+     *
+     * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+     *         unix errno.
+     */
+     void removeInterfaceFromNetwork(int netId, in String iface);
+}
diff --git a/framework/src/android/net/RouteInfo.java b/framework/src/android/net/RouteInfo.java
index df5f151..e8ebf81 100644
--- a/framework/src/android/net/RouteInfo.java
+++ b/framework/src/android/net/RouteInfo.java
@@ -584,7 +584,7 @@
             }
             RouteKey p = (RouteKey) o;
             // No need to do anything special for scoped addresses. Inet6Address#equals does not
-            // consider the scope ID, but the netd route IPCs (e.g., INetd#networkAddRouteParcel)
+            // consider the scope ID, but the route IPCs (e.g., RoutingCoordinatorManager#addRoute)
             // and the kernel ignore scoped addresses both in the prefix and in the nexthop and only
             // look at RTA_OIF.
             return Objects.equals(p.mDestination, mDestination)
diff --git a/framework/src/android/net/RoutingCoordinatorManager.java b/framework/src/android/net/RoutingCoordinatorManager.java
new file mode 100644
index 0000000..5576cb0
--- /dev/null
+++ b/framework/src/android/net/RoutingCoordinatorManager.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+
+/**
+ * A manager class for talking to the routing coordinator service.
+ *
+ * This class should only be used by the connectivity and tethering module. This is enforced
+ * by the build rules. Do not change build rules to gain access to this class from elsewhere.
+ * @hide
+ */
+@RequiresApi(Build.VERSION_CODES.S)
+public class RoutingCoordinatorManager {
+    @NonNull final Context mContext;
+    @NonNull final IRoutingCoordinator mService;
+
+    public RoutingCoordinatorManager(@NonNull final Context context,
+            @NonNull final IRoutingCoordinator service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Add a route for specific network
+     *
+     * @param netId the network to add the route to
+     * @param route the route to add
+     * @throws ServiceSpecificException in case of failure, with an error code indicating the
+     *         cause of the failure.
+     */
+    public void addRoute(final int netId, final RouteInfo route) {
+        try {
+            mService.addRoute(netId, route);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Remove a route for specific network
+     *
+     * @param netId the network to remove the route from
+     * @param route the route to remove
+     * @throws ServiceSpecificException in case of failure, with an error code indicating the
+     *         cause of the failure.
+     */
+    public void removeRoute(final int netId, final RouteInfo route) {
+        try {
+            mService.removeRoute(netId, route);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Update a route for specific network
+     *
+     * @param netId the network to update the route for
+     * @param route parcelable with route information
+     * @throws ServiceSpecificException in case of failure, with an error code indicating the
+     *         cause of the failure.
+     */
+    public void updateRoute(final int netId, final RouteInfo route) {
+        try {
+            mService.updateRoute(netId, route);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Adds an interface to a network. The interface must not be assigned to any network, including
+     * the specified network.
+     *
+     * @param netId the network to add the interface to.
+     * @param iface the name of the interface to add.
+     *
+     * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+     *         unix errno.
+     */
+    public void addInterfaceToNetwork(final int netId, final String iface) {
+        try {
+            mService.addInterfaceToNetwork(netId, iface);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Removes an interface from a network. The interface must be assigned to the specified network.
+     *
+     * @param netId the network to remove the interface from.
+     * @param iface the name of the interface to remove.
+     *
+     * @throws ServiceSpecificException in case of failure, with an error code corresponding to the
+     *         unix errno.
+     */
+    public void removeInterfaceFromNetwork(final int netId, final String iface) {
+        try {
+            mService.removeInterfaceFromNetwork(netId, iface);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/framework/src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java b/framework/src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java
index d65858f..c2d75d2 100644
--- a/framework/src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java
+++ b/framework/src/android/net/connectivity/TiramisuConnectivityInternalApiUtil.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.RoutingCoordinatorManager;
 import android.os.Build;
 import android.os.IBinder;
 
@@ -34,15 +35,28 @@
  * linter).
  * @hide
  */
-@RequiresApi(Build.VERSION_CODES.TIRAMISU)
+// TODO : rename this so that it doesn't reference "Tiramisu" since it can be used in S.
+@RequiresApi(Build.VERSION_CODES.S)
 public class TiramisuConnectivityInternalApiUtil {
 
     /**
      * Get a service binder token for
      * {@link com.android.server.connectivity.wear.CompanionDeviceManagerProxyService}.
      */
+    @RequiresApi(Build.VERSION_CODES.TIRAMISU)
     public static IBinder getCompanionDeviceManagerProxyService(Context ctx) {
         final ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
         return cm.getCompanionDeviceManagerProxyService();
     }
+
+    /**
+     * Obtain a routing coordinator manager from a context, possibly cross-module.
+     * @param ctx the context
+     * @return an instance of the coordinator manager
+     */
+    @RequiresApi(Build.VERSION_CODES.S)
+    public static RoutingCoordinatorManager getRoutingCoordinatorManager(Context ctx) {
+        final ConnectivityManager cm = ctx.getSystemService(ConnectivityManager.class);
+        return cm.getRoutingCoordinatorManager();
+    }
 }