对于Android TV来说,运用以太网进行上网也是网络运用场景之一,可是Google一直都没有较为杰出的完成静态网络装备的办法,正好近期有Android S 的有线网络适配的需求,特此将一些适配办法以及疑问记载一下(如有缺乏,能够请各位大佬留言谈论补充纠错)。

Android 版别影响

首要便是Android 11 与 12 的有线网络版别差距仍是比较大的,所以假如之前只在11上面适配过
,那么对于12来说,适配仍是需求花费一点功夫,详细的差异在之后的部分会记载,可是12与13的有线网络差异就较小了,并且看起来,13的以太网接口以及逻辑比12来说更为完善。

#装备以太网所需求的类
装备以太网所需求的java类大概有以下几个,其实这几个类从Android9开始便是以太网装备的首要java类了

  • frameworks/base/core/java/android/net/EthernetManager.java

    EthernetManager.java此是上层办理以太网的类,咱们常通过context.getSystemService(Context.ETHERNET_SERVICE)取得他的实例对象

  • packages/modules/Connectivity/framework/src/android/net/IpConfiguration.java

    这个IpConfiguration.java类存在的目录就非常奇怪了,之前我有做过的Android 9 版别这个类分明在frameworks/base/core/java/android/net/IpConfiguration.java 这个途径才对,(可能是我没去官网看版别差异的原因)所以暂时将他列为Android12网络的一个差异吧,
    书回正题,这个类首要便是用来装备IP状态的,包括动态和静态

  • packages/modules/Connectivity/framework/src/android/net/StaticIpConfiguration.java

    望文生义,这个类首要便是用来装备静态IP的,这个类之前也是在frameworks/base/core/java/android/net/途径下,12里边也移到了packages/modules/Connectivity/framework/src/android/net/下

其实到这儿,对于咱们应用app来说现已能够包括了所有的以太网运用类了,可是对于framework开发有线网络来说,原生Android已有的装备办法,无法满足咱们的需求,所以还需求对Ethernet_service进行修正,以下是Ethernet_server所包括的类

  • Android/frameworks/base/core/java/android/net/IEthernetManager.aidl

    EthernetManager.java此类与EthernetServiceImpl.java通信的AIDL文件,类似于WMS的工作办法,其实需求一个service跨进程进行通信

  • Android/frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetServiceImpl.java

    AIDL完成的完成类,便是Ethernet_service的详细完成

  • Android/frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java

     以太网网络连接的办理类

  • Android/frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetTracker.java

    EthernetTracker中首要做了以下两件事 :
    1. 首要更新 ip config的,这个和静态ip相关;
    2. 根据iface,调用addInterface创建interface

获取IP参数修正记载

  通过查阅源码,发现,咱们设置IP后,所需求的网络参数如: DNS,gateway,IPaddress,netmask 这些都无法直接获取,所以,咱们首要的修正便是直接获取当前的ip参数,以及增加有线网络的开关

Android/frameworks/base/core/java/android/net/EthernetManager.java中增加

  /**
   * Indicates whether the interface is up.
   *
   * @param iface Ethernet interface name
   * @hide
   */
  @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
  public boolean isInterfaceup(String iface) {
      try {
          return mService.isInterfaceup(iface);
      } catch (RemoteException e) {
          throw e.rethrowFromSystemServer();
      }
  }
  /**
   * @hide
   */
  @UnsupportedAppUsage
  public String getIpAddress(String iface) {
      try {
          return mService.getIpAddress(iface);
      } catch (RemoteException e) {
          throw e.rethrowFromSystemServer();
      }
  }
  /**
   * @hide
   */
  @UnsupportedAppUsage
  public String getNetmask(String iface) {
      try {
          return mService.getNetmask(iface);
      } catch (RemoteException e) {
          throw e.rethrowFromSystemServer();
      }
  }
  /**
   * @hide
   */
  @UnsupportedAppUsage
  public String getGateway(String iface) {
      try {
          return mService.getGateway(iface);
      } catch (RemoteException e) {
          throw e.rethrowFromSystemServer();
      }
  }
  /**
   * @hide
   */
  @UnsupportedAppUsage
  public String getDns(String iface) {
      try {
          return mService.getDns(iface);
      } catch (RemoteException e) {
          throw e.rethrowFromSystemServer();
      }
  }

Android/frameworks/base/core/java/android/net/IEthernetManager.aidl 增加AIDL通信

    boolean isInterfaceup(String iface);
    String getIpAddress(String iface);
    String getNetmask(String iface);
    String getGateway(String iface);
    String getDns(String iface);

frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java  中增加getIpAddress,getNetmask,getGateway,getDns的完成逻辑。

diff --git a/Android/frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java b/Android/frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java
index 28b24f1fb1..2dc9b0f908 100644
--- a/Android/frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/Android/frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -22,10 +22,12 @@ import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.EthernetManager;
 import android.net.EthernetNetworkSpecifier;
 import android.net.IpConfiguration;
 import android.net.IpConfiguration.IpAssignment;
 import android.net.IpConfiguration.ProxySettings;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkAgent;
 import android.net.NetworkAgentConfig;
@@ -33,6 +35,7 @@ import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
+import android.net.RouteInfo;
 import android.net.ip.IIpClient;
 import android.net.ip.IpClientCallbacks;
 import android.net.ip.IpClientUtil;
@@ -47,11 +50,16 @@ import android.util.Log;
 import android.util.SparseArray;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.Inet4AddressUtils;
 import java.io.FileDescriptor;
+import java.lang.reflect.Method;
+import java.net.Inet4Address;
+import java.net.InetAddress;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
+
 /**
  * {@link NetworkFactory} that represents Ethernet networks.
  *
@@ -69,6 +77,9 @@ public class EthernetNetworkFactory extends NetworkFactory {
             new ConcurrentHashMap<>();
     private final Handler mHandler;
     private final Context mContext;
+    private EthernetManager mEthernetManager;
+
+    private static boolean[] mIfaceStatus = new boolean[2];
     public static class ConfigurationException extends AndroidRuntimeException {
         public ConfigurationException(String msg) {
@@ -83,6 +94,7 @@ public class EthernetNetworkFactory extends NetworkFactory {
         mContext = context;
         setScoreFilter(NETWORK_SCORE);
+        mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE);
     }
     @Override
@@ -104,8 +116,9 @@ public class EthernetNetworkFactory extends NetworkFactory {
         }
         if (++network.refCount == 1) {
-            network.start();
+            //network.start();
         }
+        Log.w(TAG, "needNetworkFor, network.refCount = " +network.refCount);
     }
     @Override
@@ -117,8 +130,9 @@ public class EthernetNetworkFactory extends NetworkFactory {
         }
         if (--network.refCount == 0) {
-            network.stop();
+            //network.stop();
         }
+        Log.w(TAG, "releaseNetworkFor, network.refCount = " +network.refCount);
     }
     /**
@@ -197,6 +211,14 @@ public class EthernetNetworkFactory extends NetworkFactory {
             Log.d(TAG, "updateInterfaceLinkState, iface: " + ifaceName + ", up: " + up);
         }
+        if (ifaceName.equals("eth0")) {
+			Log.i(TAG, " mIfaceStatus[0] = up");
+            mIfaceStatus[0] = up;
+		}
+        if (ifaceName.equals("eth1")){
+			Log.i(TAG, "mIfaceStatus[1] = up");
+            mIfaceStatus[1] = up;
+		}
         NetworkInterfaceState iface = mTrackingInterfaces.get(ifaceName);
         return iface.updateLinkState(up);
     }
@@ -204,6 +226,121 @@ public class EthernetNetworkFactory extends NetworkFactory {
     boolean hasInterface(String interfacName) {
         return mTrackingInterfaces.containsKey(interfacName);
     }
+	
+	// ADD BEGIN
+    boolean isInterfaceup(String interfacName) {
+        if (interfacName.equals("eth0"))
+            return mIfaceStatus[0];
+        else if (interfacName.equals("eth1"))
+            return mIfaceStatus[1];
+        else
+            return false;
+    }
+
+    String getIpAddress(String iface) {
+        IpConfiguration config = mEthernetManager.getConfiguration(iface);
+        if (config.getIpAssignment() == IpAssignment.STATIC) {
+            return config.getStaticIpConfiguration().getIpAddress().getAddress().getHostAddress();
+        } else {
+            NetworkInterfaceState netState = mTrackingInterfaces.get(iface);
+            if (null != netState) {
+                for (LinkAddress l : netState.mLinkProperties.getLinkAddresses()) {
+                    InetAddress source = l.getAddress();
+                    //Log.d(TAG, "getIpAddress: " + source.getHostAddress());
+                    if (source instanceof Inet4Address) {
+                        return source.getHostAddress();
+                    }
+                }
+            }
+        }
+        return "";
+    }
+
+    private String prefix2netmask(int prefix) {
+        // convert prefix to netmask
+        if (true) {
+            int mask = 0xFFFFFFFF << (32 - prefix);
+            //Log.d(TAG, "mask = " + mask + " prefix = " + prefix);
+            return ((mask>>>24) & 0xff) + "." + ((mask>>>16) & 0xff) + "." + ((mask>>>8) & 0xff) + "." + ((mask) & 0xff);
+        } else {
+            int hostAddress = Inet4AddressUtils.prefixLengthToV4NetmaskIntHTL(prefix);
+            return Inet4AddressUtils.intToInet4AddressHTL(hostAddress).getHostName();
+        }
+    }
+
+    String getNetmask(String iface) {
+        IpConfiguration config = mEthernetManager.getConfiguration(iface);
+        if (config.getIpAssignment() == IpAssignment.STATIC) {
+            return prefix2netmask(config.getStaticIpConfiguration().getIpAddress().getPrefixLength());
+        } else {
+            NetworkInterfaceState netState = mTrackingInterfaces.get(iface);
+            if (null != netState) {
+                for (LinkAddress l : netState.mLinkProperties.getLinkAddresses()) {
+                    InetAddress source = l.getAddress();
+                    if (source instanceof Inet4Address) {
+                        return prefix2netmask(l.getPrefixLength());
+                    }
+                }
+            }
+        }
+        return "";
+    }
+
+    String getGateway(String iface) {
+        IpConfiguration config = mEthernetManager.getConfiguration(iface);
+        if (config.getIpAssignment() == IpAssignment.STATIC) {
+            return config.getStaticIpConfiguration().getGateway().getHostAddress();
+        } else {
+            NetworkInterfaceState netState = mTrackingInterfaces.get(iface);
+            if (null != netState) {
+                for (RouteInfo route : netState.mLinkProperties.getRoutes()) {
+                    if (route.hasGateway()) {
+                        InetAddress gateway = route.getGateway();
+                        Object isIPv4Default = invokeMethodNoParameter(route, "isIPv4Default");
+                        if (null != isIPv4Default && (Boolean)isIPv4Default) {
+                            return gateway.getHostAddress();
+                        }
+                    }
+                }
+            }
+        }
+        return "";
+    }
+
+    private Object invokeMethodNoParameter(Object object, String methodName) {
+        try {
+            Method method = object.getClass().getDeclaredMethod(methodName);
+            method.setAccessible(true);
+            return method.invoke(object);
+            //return method.invoke(object, paramTypes);
+        } catch (Exception e) {
+            Log.e(TAG, "invokeMethod->methodName:" + methodName + ", " + e);
+        }
+        return null;
+    }
+
+    /*
+     * return dns format: "8.8.8.8,4.4.4.4"
+     */
+    String getDns(String iface) {
+        String dns = "";
+        IpConfiguration config = mEthernetManager.getConfiguration(iface);
+        if (config.getIpAssignment() == IpAssignment.STATIC) {
+            for (InetAddress nameserver : config.getStaticIpConfiguration().getDnsServers()) {
+                dns += nameserver.getHostAddress() + ",";
+            }
+        } else {
+            NetworkInterfaceState netState = mTrackingInterfaces.get(iface);
+            if (null != netState) {
+                for (InetAddress nameserver : netState.mLinkProperties.getDnsServers()) {
+                    dns += nameserver.getHostAddress() + ",";
+                }
+            }
+        }
+        return dns;
+    }
+	
+	//ADD END
     void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) {
         NetworkInterfaceState network = mTrackingInterfaces.get(iface);
@@ -382,7 +519,7 @@ public class EthernetNetworkFactory extends NetworkFactory {
         }
         void setIpConfig(IpConfiguration ipConfig) {
-            if (Objects.equals(this.mIpConfig, ipConfig)) {
+           /* if (Objects.equals(this.mIpConfig, ipConfig)) {
                 if (DBG) Log.d(TAG, "ipConfig have not changed,so ignore setIpConfig");
                 return;
             }
@@ -390,6 +527,9 @@ public class EthernetNetworkFactory extends NetworkFactory {
             if (mNetworkAgent != null) {
                 restart();
             }
+			*/
+			this.mIpConfig = ipConfig;
+            restart();
         }

frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetServiceImpl.java 中增加从EthernetTracker中获取参数的办法:

--- a/Android/frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/Android/frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetServiceImpl.java
@@ -141,6 +141,61 @@ public class EthernetServiceImpl extends IEthernetManager.Stub {
         return mTracker.isTrackingInterface(iface);
     }
+    @Override
+    public boolean isInterfaceup(String iface) {
+        enforceAccessPermission();
+
+        if (mTracker.isRestrictedInterface(iface)) {
+            enforceUseRestrictedNetworksPermission();
+        }
+
+        return mTracker.isInterfaceup(iface);
+    }
+
+    @Override
+    public String getIpAddress(String iface) {
+        enforceAccessPermission();
+
+        if (mTracker.isRestrictedInterface(iface)) {
+            enforceUseRestrictedNetworksPermission();
+        }
+
+        return mTracker.getIpAddress(iface);
+    }
+
+    @Override
+    public String getNetmask(String iface) {
+        enforceAccessPermission();
+
+        if (mTracker.isRestrictedInterface(iface)) {
+            enforceUseRestrictedNetworksPermission();
+        }
+
+        return mTracker.getNetmask(iface);
+    }
+
+    @Override
+    public String getGateway(String iface) {
+        enforceAccessPermission();
+
+        if (mTracker.isRestrictedInterface(iface)) {
+            enforceUseRestrictedNetworksPermission();
+        }
+
+        return mTracker.getGateway(iface);
+    }
+
+    @Override
+    public String getDns(String iface) {
+        enforceAccessPermission();
+
+        if (mTracker.isRestrictedInterface(iface)) {
+            enforceUseRestrictedNetworksPermission();
+        }
+
+        return mTracker.getDns(iface);
+    }
+

frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetTracker.java中完成对EthernetFactory的实例化,并完成getIP参数的办法

+    boolean isInterfaceup(String iface) {
+        return mFactory.isInterfaceup(iface);
+    }
+
+    String getIpAddress(String iface) {
+        return mFactory.getIpAddress(iface);
+    }
+
+    String getNetmask(String iface) {
+        return mFactory.getNetmask(iface);
+    }
+
+    String getGateway(String iface) {
+        return mFactory.getGateway(iface);
+    }
+
+    String getDns(String iface) {
+        return mFactory.getDns(iface);
+    }
+

做完以上过程后,咱们的EthernetManager中就有了isInterfaceup,getIpAddress,getNetmask,getGateway,getDns办法,能够直接获取动态或许静态的ip参数,就不需求再对当前的ipconfiguration进行剖析,便利许多。

增加以太网开关

安卓13的更改简略阅读

  阅读了Android 13 的代码,发现其实在13版别中现已增加了相关的代码,而且13更是将EthernetManager.java移动到了packages\modules\Connectivity\framework-t\src\android\net\EthernetManager.java这个途径下,与framework进行了别离,乃至wifi 、蓝牙、热点 之前 framework 的源码都移动到了下面的package目录:

    packages\modules\Connectivity\

可是12仍是在framework中,所以暂时没有深入研究,仅仅看到有开关的相关办法

@RequiresPermission(anyOf =   {NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,android.Manifest.permission.NETWORK_STACK,android.Manifest.permission.NETWORK_SETTINGS})
@SystemApi(client = MODULE_LIBRARIES)
public void setEthernetEnabled(boolean enabled) {
	try {
		mService.setEthernetEnabled(enabled);
	} catch (RemoteException e) {
		throw e.rethrowFromSystemServer();
	}
}

以此推测,EthernetServiceImpl.java中应该是有相关的开关办法,然后搜索EthernetServiceImpl.java,发现,这个类也换了位置->packages/modules/Connectivity/service-t/src/com/android/server/ethernet/EthernetTracker.java,/service-t/是用来存放AIDL的完成service的目录,详细完成开关的流程为:

graph TD
 mService.setEthernetEnabled--> mTracker.setEthernetEnabled--> trackAvailableInterfaces-->
 maybeTrackInterface-->addInterface-->NetdUtils.setInterfaceUp

大致的流程便是这样,最终仍是调用了之前就存在的NetdUtils.setInterfaceUp(mNetd, iface);办法

回过头来看12代码,由于没有开关,由此挑选什么做法就看自己决定了,首要榜首种办法便是直接比较暴力的开关(这个我也是看到有芯片厂商这样做,直接借鉴一下^^).

Ethernet以太网开关

1. 厂商的做法便是运用到这个AIDL INetworkManagementService.aidl,这个里边有

    /**
     * Set interface up
     */
    void setInterfaceUp(String iface);

咱们能够直接实例化这个aidl对象,然后在场景下调用这个办法,进行开关。

private INetworkManagementService mNMService;
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
                mNMService = INetworkManagementService.Stub.asInterface(b);
                try {
                    mNMService.setInterfaceDown(mIfaceName);
                    mNMService.setInterfaceUp(mIfaceName);
                } catch (RemoteException e) { // NwService throws runtime exceptions for errors
                    Log.e(TAG, "Settings: can't bring interface restart - " + e);
                }

2. 另一种做法便是在EthernetTracker.java 中运用此办法mNMService.setInterfaceUp("eth0");(由于咱们的产品只要一个网口,所以,固定是eth0,正常应该传参^^)

做完这些咱们的需求基本就完成了,至于UI部分以后有机会再说吧^^

问题

  做完这些之后,通过自测,发现设置静态IP居然在关机开机之后有概率读取不到IP信息 !!!可是开关一下自己写的以太网开关就又能读到了??回看代码觉得写的也没问题吧...后面发现,厂商在TVsettings里边设置静态IP之后开关了一下以太网接口,测验加上这个patch,问题算是处理了.(可是不知道为什么RK的不必开关啊??)

总结

其实许多芯片厂商都会把以太网的一整套办法写好(我见过的只要几个没有,各家的写法也都不尽相同,可是好在都有写),由于Android早就现已有完整的以太网流程,尽管没有开发出来配套的静态接口,可是现已算比较成熟的功用了。最首要便是了解EthernetServiceImpl&EthernetTracker&EthernetNetworkFactory这三个类的读取和装备就能够,至于一整套(从下到上)的流程,还需求去瞅瞅^^ 最终,感谢aospxref.com/