From 496f770dd8859a3c4fe38ddda07362cfe053c49c Mon Sep 17 00:00:00 2001
From: Handa Wang <handaw@google.com>
Date: Tue, 7 Nov 2023 12:32:29 +0800
Subject: [PATCH] update ThreadNetworkControllerService to support border
 routing

This CL lets ThreadNetworkControllerService calls the LocalNetworkConfig
API to set up border routing between Thread and infrastructure network.

Bug: 299552948
Bug: 305861411
Test: Verified on Cuttlefish that forwarding is set up.

Change-Id: I8b2f015d9d6248bfb1cd9ac806dc8664970a26d2
---
 thread/service/Android.bp                     |   3 +
 .../ThreadNetworkControllerService.java       | 136 +++++++++++++++++-
 thread/service/proguard.flags                 |   4 +
 3 files changed, 141 insertions(+), 2 deletions(-)
 create mode 100644 thread/service/proguard.flags

diff --git a/thread/service/Android.bp b/thread/service/Android.bp
index bd265e60c1..35ae3c277d 100644
--- a/thread/service/Android.bp
+++ b/thread/service/Android.bp
@@ -43,6 +43,9 @@ java_library {
         "ot-daemon-aidl-java",
     ],
     apex_available: ["com.android.tethering"],
+    optimize: {
+        proguard_flags_files: ["proguard.flags"],
+    },
 }
 
 cc_library_shared {
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 1f9270067c..60c97bfe35 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -55,16 +55,20 @@ import android.Manifest.permission;
 import android.annotation.NonNull;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.LocalNetworkConfig;
 import android.net.MulticastRoutingConfig;
+import android.net.LocalNetworkInfo;
+import android.net.Network;
 import android.net.NetworkAgent;
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkProvider;
 import android.net.NetworkRequest;
 import android.net.NetworkScore;
+import android.net.RouteInfo;
 import android.net.thread.ActiveOperationalDataset;
 import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
 import android.net.thread.IActiveOperationalDatasetReceiver;
@@ -92,6 +96,7 @@ import com.android.server.thread.openthread.IOtDaemonCallback;
 import com.android.server.thread.openthread.IOtStatusReceiver;
 import com.android.server.thread.openthread.Ipv6AddressInfo;
 import com.android.server.thread.openthread.OtDaemonState;
+import com.android.server.thread.openthread.BorderRouterConfigurationParcel;
 
 import java.io.IOException;
 import java.net.Inet6Address;
@@ -139,9 +144,14 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
 
     private IOtDaemon mOtDaemon;
     private NetworkAgent mNetworkAgent;
-    private final NetworkRequest mUpstreamNetworkRequest;
     private MulticastRoutingConfig mUpstreamMulticastRoutingConfig = CONFIG_FORWARD_NONE;
     private MulticastRoutingConfig mDownstreamMulticastRoutingConfig = CONFIG_FORWARD_NONE;
+    private Network mUpstreamNetwork;
+    private final NetworkRequest mUpstreamNetworkRequest;
+    private final HashMap<Network, String> mNetworkToInterface;
+    private final LocalNetworkConfig mLocalNetworkConfig;
+
+    private BorderRouterConfigurationParcel mBorderRouterConfig;
 
     @VisibleForTesting
     ThreadNetworkControllerService(
@@ -158,7 +168,18 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
         mOtDaemonSupplier = otDaemonSupplier;
         mConnectivityManager = connectivityManager;
         mTunIfController = tunIfController;
-        mUpstreamNetworkRequest = null; // to be updated aosp/2823311
+        mUpstreamNetworkRequest =
+                new NetworkRequest.Builder()
+                        .clearCapabilities()
+                        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                        .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
+                        .build();
+        mLocalNetworkConfig =
+                new LocalNetworkConfig.Builder()
+                        .setUpstreamSelector(mUpstreamNetworkRequest)
+                        .build();
+        mNetworkToInterface = new HashMap<Network, String>();
+        mBorderRouterConfig = new BorderRouterConfigurationParcel();
     }
 
     public static ThreadNetworkControllerService newInstance(Context context) {
@@ -179,6 +200,7 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
     private static NetworkCapabilities newNetworkCapabilities() {
         return new NetworkCapabilities.Builder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_THREAD)
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK)
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
                 .build();
     }
@@ -260,11 +282,77 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
                     mLinkProperties.setInterfaceName(TUN_IF_NAME);
                     mLinkProperties.setMtu(TunInterfaceController.MTU);
                     mConnectivityManager.registerNetworkProvider(mNetworkProvider);
+                    requestUpstreamNetwork();
 
                     initializeOtDaemon();
                 });
     }
 
+    private void requestUpstreamNetwork() {
+        mConnectivityManager.registerNetworkCallback(
+                mUpstreamNetworkRequest,
+                new ConnectivityManager.NetworkCallback() {
+                    @Override
+                    public void onAvailable(@NonNull Network network) {
+                        Log.i(TAG, "onAvailable: " + network);
+                    }
+
+                    @Override
+                    public void onLost(@NonNull Network network) {
+                        Log.i(TAG, "onLost: " + network);
+                    }
+
+                    @Override
+                    public void onLinkPropertiesChanged(
+                            @NonNull Network network, @NonNull LinkProperties linkProperties) {
+                        Log.i(
+                                TAG,
+                                String.format(
+                                        "onLinkPropertiesChanged: {network: %s, interface: %s}",
+                                        network, linkProperties.getInterfaceName()));
+                        mNetworkToInterface.put(network, linkProperties.getInterfaceName());
+                        if (network.equals(mUpstreamNetwork)) {
+                            enableBorderRouting(mNetworkToInterface.get(mUpstreamNetwork));
+                        }
+                    }
+                },
+                mHandler);
+    }
+
+    private final class ThreadNetworkCallback extends ConnectivityManager.NetworkCallback {
+        @Override
+        public void onAvailable(@NonNull Network network) {
+            Log.i(TAG, "onAvailable: Thread network Available");
+        }
+
+        @Override
+        public void onLocalNetworkInfoChanged(
+                @NonNull Network network, @NonNull LocalNetworkInfo localNetworkInfo) {
+            Log.i(TAG, "onLocalNetworkInfoChanged: " + localNetworkInfo);
+            if (localNetworkInfo.getUpstreamNetwork() == null) {
+                mUpstreamNetwork = null;
+                return;
+            }
+            if (!localNetworkInfo.getUpstreamNetwork().equals(mUpstreamNetwork)) {
+                mUpstreamNetwork = localNetworkInfo.getUpstreamNetwork();
+                if (mNetworkToInterface.containsKey(mUpstreamNetwork)) {
+                    enableBorderRouting(mNetworkToInterface.get(mUpstreamNetwork));
+                }
+            }
+        }
+    }
+
+    private void requestThreadNetwork() {
+        mConnectivityManager.registerNetworkCallback(
+                new NetworkRequest.Builder()
+                        .clearCapabilities()
+                        .addTransportType(NetworkCapabilities.TRANSPORT_THREAD)
+                        .removeForbiddenCapability(NetworkCapabilities.NET_CAPABILITY_LOCAL_NETWORK)
+                        .build(),
+                new ThreadNetworkCallback(),
+                mHandler);
+    }
+
     private void registerThreadNetwork() {
         if (mNetworkAgent != null) {
             return;
@@ -274,6 +362,7 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
                 new NetworkScore.Builder()
                         .setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_LOCAL_NETWORK)
                         .build();
+        requestThreadNetwork();
         mNetworkAgent =
                 new NetworkAgent(
                         mContext,
@@ -281,6 +370,7 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
                         TAG,
                         netCaps,
                         mLinkProperties,
+                        mLocalNetworkConfig,
                         score,
                         new NetworkAgentConfig.Builder().build(),
                         mNetworkProvider) {};
@@ -320,10 +410,19 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
     }
 
     private void updateNetworkLinkProperties(LinkAddress linkAddress, boolean isAdded) {
+        RouteInfo routeInfo =
+                new RouteInfo(
+                        new IpPrefix(linkAddress.getAddress(), 64),
+                        null,
+                        TUN_IF_NAME,
+                        RouteInfo.RTN_UNICAST,
+                        TunInterfaceController.MTU);
         if (isAdded) {
             mLinkProperties.addLinkAddress(linkAddress);
+            mLinkProperties.addRoute(routeInfo);
         } else {
             mLinkProperties.removeLinkAddress(linkAddress);
+            mLinkProperties.removeRoute(routeInfo);
         }
 
         // The Thread daemon can send link property updates before the networkAgent is
@@ -573,6 +672,39 @@ final class ThreadNetworkControllerService extends IThreadNetworkController.Stub
         }
     }
 
+    private void enableBorderRouting(String infraIfName) {
+        if (mBorderRouterConfig.isBorderRoutingEnabled
+                && infraIfName.equals(mBorderRouterConfig.infraInterfaceName)) {
+            return;
+        }
+        Log.i(TAG, "enableBorderRouting on AIL: " + infraIfName);
+        try {
+            mBorderRouterConfig.infraInterfaceName = infraIfName;
+            mBorderRouterConfig.infraInterfaceIcmp6Socket =
+                    InfraInterfaceController.createIcmp6Socket(infraIfName);
+            mBorderRouterConfig.isBorderRoutingEnabled = true;
+
+            mOtDaemon.configureBorderRouter(
+                    mBorderRouterConfig,
+                    new IOtStatusReceiver.Stub() {
+                        @Override
+                        public void onSuccess() {
+                            Log.i(TAG, "configure border router successfully");
+                        }
+
+                        @Override
+                        public void onError(int i, String s) {
+                            Log.w(
+                                    TAG,
+                                    String.format(
+                                            "failed to configure border router: %d %s", i, s));
+                        }
+                    });
+        } catch (Exception e) {
+            Log.w(TAG, "enableBorderRouting failed: " + e);
+        }
+    }
+
     private void handleThreadInterfaceStateChanged(boolean isUp) {
         try {
             mTunIfController.setInterfaceUp(isUp);
diff --git a/thread/service/proguard.flags b/thread/service/proguard.flags
new file mode 100644
index 0000000000..5028982537
--- /dev/null
+++ b/thread/service/proguard.flags
@@ -0,0 +1,4 @@
+# Ensure the callback methods are not stripped
+-keepclassmembers class **.ThreadNetworkControllerService$ThreadNetworkCallback {
+    *;
+}
-- 
GitLab