Android13 Ethernet and WIFI coexist

1. Introduction

In actual project development, especially when Ethernet-related functions are used, it often involves the situation where WiFi and Ethernet are turned on at the same time. Android has very little native compatibility for this. Only one will take effect, and Ethernet will be used first. Therefore, the system mechanism needs to be changed to make it compatible.

2. Implementation part

Idea: In fact, what we have to do is to make the bottom layer think that when the two coexist, it is enough to use wifi to access the Internet. Then we have to make a fuss about the network path, that is, to configure the “network coexistence routing table”, which is generally about The packages involved in the upper network are the Connectivity and netd packages in the system directory. We can start viewing the source codes of these two packages.

2.1. Upper system service part

1. Filter the detection of Ethernet and wifi in the unneeded method of the ConnectivityService.java class to enable coexistence.

 + + + b/packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java
@@ -322,11 + 322,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
     private static final String TRAFFICCONTROLLER_ARG = "trafficcontroller";
 
     private static final boolean DBG = true;
     private static final boolean DDBG = Log.isLoggable(TAG, Log.DEBUG);
     private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
 
     private static final boolean LOGD_BLOCKED_NETWORKINFO = true;

     /**
      * Default URL to use for {@link #getCaptivePortalServerUrl()}. This should not be changed
      * by OEMs for configuration purposes, as this value is overridden by
@@ -4601,6 + 4610,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
     private boolean unneeded(NetworkAgentInfo nai, UnneededFor reason) {
         ensureRunningOnConnectivityServiceThread();
 
 + // added by cgt ethernet and wifi coexist start
 + boolean needed = nai.isWIFI();
 + needed = nai.isETHERNET();
 + Log.d(TAG, "---> unneeded: needed=" + needed);
 + if (needed) {
 + return false;
 + }
 + // added by cgt ethernet and wifi coexist end

         if (!nai.everConnected || nai.isVPN() || nai.isInactive()
                 || nai.getScore().getKeepConnectedReason() != NetworkScore.KEEP_CONNECTED_NONE) {
             return false;

2. Add a method to determine whether the current network is Ethernet or wifi in the NetworkAgentInfo.java class

 + + + b/packages/modules/Connectivity/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -943,6 + 943,18 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo>, NetworkRa
         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
     }
 
 + // added by cgt ethernet and wifi coexist start
 + /** Whether this network is a WIFI. */
 + public boolean isWIFI() {
 + return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
 + }
 +
 + /** Whether this network is a ETHERNET. */
 + public boolean isETHERNET() {
 + return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET);
 + }
 + // added by cgt ethernet and wifi coexist end
 +
     /**
      * Whether this network should propagate the capabilities from its underlying networks.
      * Currently only true for VPNs.

3.Configure wifi priority in the NetworkRanker.java class and filter wifi and Ethernet in the mightBeat method

 + + + b/packages/modules/Connectivity/service/src/com/android/server/connectivity/NetworkRanker.java
@@ -46,10 + 46,18 @@ import java.util.Collection;
 import java.util.List;
 import java.util.function.Predicate;
 
 + // added by cgt ethernet and wifi coexist start
 + import android.util.Log;
 + // added by cgt ethernet and wifi coexist end
 +
 /**
  * A class that knows how to find the best network matching a request out of a list of networks.
  */
 public class NetworkRanker {
 + // added by cgt ethernet and wifi coexist start
 + private static final String TAG = "NetworkRanker";
 + // added by cgt ethernet and wifi coexist end
 +
     // Historically the legacy ints have been 0~100 in principle (though the highest score in
     // AOSP has always been 90). This is relied on by VPNs that send a legacy score of 101.
     public static final int LEGACY_INT_MAX = 100;
@@ -80,9 + 88,25 @@ public class NetworkRanker {
     }
 
     // Transport preference order, if it comes down to that.
 + // added by cgt ethernet and wifi coexist start
- private static final int[] PREFERRED_TRANSPORTS_ORDER = { TRANSPORT_ETHERNET, TRANSPORT_WIFI,
 + //private static final int[] PREFERRED_TRANSPORTS_ORDER = { TRANSPORT_ETHERNET, TRANSPORT_WIFI,
 + // TRANSPORT_BLUETOOTH, TRANSPORT_CELLULAR };
 + private static int[] PREFERRED_TRANSPORTS_ORDER = { TRANSPORT_ETHERNET, TRANSPORT_WIFI,
 + TRANSPORT_BLUETOOTH, TRANSPORT_CELLULAR };
 + private static int[] PREFERRED_TRANSPORTS_ORDER_COEXIST = { TRANSPORT_WIFI, TRANSPORT_ETHERNET,
             TRANSPORT_BLUETOOTH, TRANSPORT_CELLULAR };
 
 + static {
 + PREFERRED_TRANSPORTS_ORDER = PREFERRED_TRANSPORTS_ORDER_COEXIST;
 + }
 + // added by cgt ethernet and wifi coexist end
 +
     // Function used to partition a list into two working areas depending on whether they
     // satisfy a predicate. All items satisfying the predicate will be put in |positive|, all
     // items that don't will be put in |negative|.
@@ -310,6 + 334,20 @@ public class NetworkRanker {
         // is always better than no network.
         if (null == champion) return true;
         // If there is no champion, the offer can always beat.
 + // added by cgt ethernet and wifi coexist start
 + boolean needed = contestant.getCapsNoCopy().hasTransport(TRANSPORT_WIFI);
 + needed = contestant.getCapsNoCopy().hasTransport(TRANSPORT_ETHERNET);
 + Log.d(TAG, "---> mightBeat: needed=" + needed);
 + if (needed) {
 + return true;
 + }
 + // added by cgt ethernet and wifi coexist end
         // Otherwise rank them.
         final ArrayList<Scoreable> candidates = new ArrayList<>();
         candidates.add(champion);

2.2. Upper-layer netd service part

1. The Init method in the RouteController.cpp class configures a new pass-through routing information to replace the native configuration.

diff --git a/system/netd/server/RouteController.cpp b/system/netd/server/RouteController.cpp
index d2af9a375a2..22377105e1f 100644
--- a/system/netd/server/RouteController.cpp
 + + + b/system/netd/server/RouteController.cpp
@@ -40,6 + 40,10 @@
 #include "netid_client.h"
 #include "netutils/ifc.h"
 
 + // added by cgt ethernet and wifi coexist start
 + #include <android-base/properties.h>
 + // added by cgt ethernet and wifi coexist end
 +
 using android::base::StartsWith;
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
@@ -773,6 + 777,18 @@ int RouteController::configureDummyNetwork() {
     return 0;
 }
 
 + // added by cgt ethernet and wifi coexist start
 + // Add a new rule to look up the 'main' table, with the same selectors as the "default network"
 + // rule, but with a lower priority. We will never create routes in the main table; it should only be
 + // used for directly-connected routes implicitly created by the kernel when adding IP addresses.
 + // This is necessary, for example, when adding a route through a directly-connected gateway: in
 + // order to add the route, there must already be a directly-connected route that covers the gateway.
 + [[nodiscard]] static int addDirectlyConnectedRule() {
 + return modifyIpRule(RTM_NEWRULE, RULE_PRIORITY_DIRECTLY_CONNECTED, RT_TABLE_MAIN,
 + MARK_UNSET, MARK_UNSET, IIF_NONE, OIF_NONE, INVALID_UID, INVALID_UID);
 + }
 + // added by cgt ethernet and wifi coexist end
 +
 // Add an explicit unreachable rule close to the end of the prioriy list to make it clear that
 // relying on the kernel-default "from all lookup main" rule at priority 32766 is not intended
 // behaviour. We do flush the kernel-default rules at startup, but having an explicit unreachable
@@ -1259,6 + 1275,13 @@ int RouteController::Init(unsigned localNetId) {
     if (int ret = addLocalNetworkRules(localNetId)) {
         return ret;
     }
 + // added by cgt ethernet and wifi coexist start
 + if (int ret = addDirectlyConnectedRule()) {
 + return ret;
 + }
 + // added by cgt ethernet and wifi coexist end
     if (int ret = addUnreachableRule()) {
         return ret;
     }

2. Define a variable used in the RouteController.h class

diff --git a/system/netd/server/RouteController.h b/system/netd/server/RouteController.h
index ff41678d551..2604266995c 100644
--- a/system/netd/server/RouteController.h
 + + + b/system/netd/server/RouteController.h
@@ -82,6 + 82,9 @@ constexpr int32_t RULE_PRIORITY_UID_DEFAULT_NETWORK = 29000;
 // the network, it will not work. That will potentially cause a user-visible error.
 constexpr int32_t RULE_PRIORITY_UID_DEFAULT_UNREACHABLE = 30000;
 constexpr int32_t RULE_PRIORITY_DEFAULT_NETWORK = 31000;
 + // added by cgt ethernet and wifi coexist start
 + constexpr int32_t RULE_PRIORITY_DIRECTLY_CONNECTED = 9999;
 + // added by cgt ethernet and wifi coexist end
 constexpr int32_t RULE_PRIORITY_UNREACHABLE = 32000;
 // clang-format on
 

3. Conclusion

At this point, wifi and Ethernet coexistence and sharing are completed. In fact, it is the project that prompts us to implement these functions. Whenever there are new needs, we should not resist, but feel that we need to improve ourselves and keep learning to grow.