赞
踩
对于Android TV来说,使用以太网进行上网也是网络使用场景之一,但是Google一直都没有较为良好的实现静态网络配置的方法,正好近期有Android S 的有线网络适配的需求,特此将一些适配方式以及疑问记录一下(如有不足,可以请各位大佬留言评论补充纠错)。
首先就是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后,所需要的网络参数如: 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进行分析,方便很多。
浏览了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的目录,具体实现开关的流程为:
大致的流程就是这样,最后还是调用了之前就存在的NetdUtils.setInterfaceUp(mNetd, iface);方法
回过头来看12代码,因为没有开关,由此选择什么做法就看自己决定了,首先第一种方法就是直接比较暴力的开关(这个我也是看到有芯片厂商这样做,直接借鉴一下^^).
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这三个类的读取和配置就可以,至于一整套(从下到上)的流程,还需要去瞅瞅^^ 最后,感谢http://aospxref.com/
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。