在前面看过Android7.0与8.0的以太网后,对9.0的以太网解决起来更得心应手了。在添加以太网后,可以顺利设置静态ip.但是当我设置静态后,出现需要将网线拔插一次更新网络后才能刷新ip地址的bug。当时以为只要像7.0和8.0那样新增就没有问题了,没想到这次9.0又改变了代码结构。下面我将详细介绍9.0的网络步骤。
以下是设计到的文件极其目录:
packages\apps\Settings\src\com\android\settings\ethernet\EthernetConfigDialog.java-----------设置静态ip,自己新增
frameworks\base\core\java\android\net\EthernetManager.java
frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetServiceImpl.java
frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetTracker.java ————区别其他版本,Google新增的类
frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetNetworkFactory.java
为了能够在不看其他版本或者不了解之前Android版本以太网流程情况下尽快熟知,我这里还是需要啰嗦的讲一下以太网的流程。
以下仅设置静态ip流程。
1:主动调用设置静态ip方法
try {
StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
InetAddress mIpAddr = NetworkUtils.numericToInetAddress(mIpaddr.getText().toString());//ip地址
String[] strs = mMask.getText().toString().split("\\.");//子网掩码参数
int count = 0;
for(String str : strs){
if(str.equals("255")){
count++;
}
}
int prefixLength = count*8;
LinkAddress mIpAddress = new LinkAddress(mIpAddr,prefixLength);
InetAddress mGateway = NetworkUtils.numericToInetAddress(mGw.getText().toString());//网关地址
ArrayList mDnsServers = new ArrayList();
mDnsServers.add(NetworkUtils.numericToInetAddress(mDns1.getText().toString()));//dns1
//与dns2的值
mDnsServers.add(NetworkUtils.numericToInetAddress(mDns2.getText().toString()));
staticIpConfiguration.ipAddress = mIpAddress;
staticIpConfiguration.gateway = mGateway;
staticIpConfiguration.dnsServers.addAll(mDnsServers);
config = new IpConfiguration(IpAssignment.STATIC, ProxySettings.NONE, staticIpConfiguration, ProxyInfo.buildDirectProxy(null,0));
mEthManager.setConfiguration("eth0",config);//此处区别//于其他Android版本修改,多了一个参数需要传端口名
}catch (Exception e) {
e.printStackTrace();
}
注意一点事mEthManager.setConfiguration();Android9.0版本变成两个参数了。
通过设置好静态ip地址所需要的参数后,调用EthernetManager类里面的setConfiguration方法进行参数配置。我们看一下这个方法具体做了什么操作。
/**
* Set Ethernet configuration.
*/
public void setConfiguration(String iface, IpConfiguration config) {
try {
mService.setConfiguration(iface, config);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
发现又是通过mService.setConfiguration()方法。那么这个mService是哪里传来的呢,我找到了构造方法
/**
* Create a new EthernetManager instance.
* Applications will almost always want to use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
* the standard {@link android.content.Context#ETHERNET_SERVICE Context.ETHERNET_SERVICE}.
*/
public EthernetManager(Context context, IEthernetManager service) {
mContext = context;
mService = service;
}
其中IEthernetManager这个看似接口实例化的对象,我通过找到发现在
public class EthernetServiceImpl extends IEthernetManager.Stub{}类中运用到。也就是EthernetServiceImpl 继承了IEthernetManager.Stub。Android的Binder机制。
在EthernetServiceImpl 中找到方法
/**
* Set Ethernet configuration
*/
@Override
public void setConfiguration(String iface, IpConfiguration config) {
if (!mStarted.get()) {
Log.w(TAG, "System isn't ready enough to change ethernet configuration");
}
enforceConnectivityInternalPermission();
if (mTracker.isRestrictedInterface(iface)) {
enforceUseRestrictedNetworksPermission();
}
// TODO: this does not check proxy settings, gateways, etc.
// Fix this by making IpConfiguration a complete representation of static configuration.
mTracker.updateIpConfiguration(iface, new IpConfiguration(config));
}
上面是9.0中的方法实现。从这里开始已经跟8.0之前的版本不同了。我们看一下8.0之前的版本是这个方法是怎么写的。
Android8.1中的实现方式
/**
* Set Ethernet configuration
*/
@Override
public void setConfiguration(IpConfiguration config) {
if (!mStarted.get()) {
Log.w(TAG, "System isn't ready enough to change ethernet configuration");
}
enforceConnectivityInternalPermission();
synchronized (mIpConfiguration) {
mEthernetConfigStore.writeIpAndProxyConfigurations(config);
// TODO: this does not check proxy settings, gateways, etc.
// Fix this by making IpConfiguration a complete representation of static configuration.
if (!config.equals(mIpConfiguration)) {
mIpConfiguration = new IpConfiguration(config);
mTracker.stop();
mTracker.start(mContext, mHandler);
}
}
}
我们也是看着都是mTracker对象调用方法进行设置。其实这两个mTracker是不同的对象。在9.0之前的版本,这个mTracker对象都是EthernetNetworkFactory的实例对象。因为后续的设置参数进行连接网络判断端口等一系列操作都在这个EthernetNetworkFactory类中完成。而在9.0后,Google将他们抽离出来了,对于监听以太网切换、以太网判断当前网络是否可用等一些列操作抽离到一个EthernetTracker类中。那么9.0的EthernetNetworkFactory只需要关心拿到参数进行连接上网操作就可以了。
我们现在只关心9.0是怎么走的。找到EthernetTracker类的具体实现,
void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) {
if (DBG) {
Log.i(TAG, "updateIpConfiguration, iface: " + iface + ", cfg: " + ipConfiguration);
}
mConfigStore.write(iface, ipConfiguration);
mIpConfigurations.put(iface, ipConfiguration);
mHandler.post(() -> mFactory.updateIpConfiguration(iface, ipConfiguration));
}
最后是调用EthernetNetworkFactory中的方法,而其实这个方法并没有做任何上网的操作。
void updateIpConfiguration(String iface, IpConfiguration ipConfiguration) {
NetworkInterfaceState network = mTrackingInterfaces.get(iface);
if (network != null) {
network.setIpConfig(ipConfiguration);
}
}
他做的操作仅仅只是将设置的ip地址等以太网参数保存下来。
说道这里我想大家已经知道了在9.0不能立马实现修改ip的问题所在了。如果我们做到这一步,已经可以通过拔插网线实现设置静态ip上网。那么接下来,我们看看正常流程怎么走。
还是继续看EthernetTracker类。因为判断能不能连接网络的条件都在这里实现。
EthernetTracker(Context context, Handler handler) {
mHandler = handler;
// The services we use.
IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
mNMService = INetworkManagementService.Stub.asInterface(b);
// Interface match regex.
mIfaceMatch = context.getResources().getString(
com.android.internal.R.string.config_ethernet_iface_regex);
// Read default Ethernet interface configuration from resources
final String[] interfaceConfigs = context.getResources().getStringArray(
com.android.internal.R.array.config_ethernet_interfaces);
for (String strConfig : interfaceConfigs) {
parseEthernetConfig(strConfig);
}
mConfigStore = new EthernetConfigStore();
NetworkCapabilities nc = createNetworkCapabilities(true /* clear default capabilities */);
mFactory = new EthernetNetworkFactory(handler, context, nc);
mFactory.register();
}
void start() {
mConfigStore.read();
// Default interface is just the first one we want to track.
mIpConfigForDefaultInterface = mConfigStore.getIpConfigurationForDefaultInterface();
final ArrayMap configs = mConfigStore.getIpConfigurations();
for (int i = 0; i < configs.size(); i++) {
mIpConfigurations.put(configs.keyAt(i), configs.valueAt(i));
}
try {
mNMService.registerObserver(new InterfaceObserver());
} catch (RemoteException e) {
Log.e(TAG, "Could not register InterfaceObserver " + e);
}
mHandler.post(this::trackAvailableInterfaces);
}
这个start()方法,就是正常上网的第一个被调用的方法,一般我们连接网线后,这个方法就会被调用。trackAvailableInterfaces是接下来要走的方法
private void trackAvailableInterfaces() {
try {
final String[] ifaces = mNMService.listInterfaces();
for (String iface : ifaces) {
maybeTrackInterface(iface);
}
} catch (RemoteException | IllegalStateException e) {
Log.e(TAG, "Could not get list of interfaces " + e);
}
}
private void maybeTrackInterface(String iface) {
if (DBG) Log.i(TAG, "maybeTrackInterface " + iface);
// If we don't already track this interface, and if this interface matches
// our regex, start tracking it.
if (!iface.matches(mIfaceMatch) || mFactory.hasInterface(iface)) {
return;
}
if (mIpConfigForDefaultInterface != null) {
updateIpConfiguration(iface, mIpConfigForDefaultInterface);
mIpConfigForDefaultInterface = null;
}
addInterface(iface);
}
private void addInterface(String iface) {
InterfaceConfiguration config = null;
// Bring up the interface so we get link status indications.
try {
mNMService.setInterfaceUp(iface);
config = mNMService.getInterfaceConfig(iface);
} catch (RemoteException | IllegalStateException e) {
// Either the system is crashing or the interface has disappeared. Just ignore the
// error; we haven't modified any state because we only do that if our calls succeed.
Log.e(TAG, "Error upping interface " + iface, e);
}
if (config == null) {
Log.e(TAG, "Null interface config for " + iface + ". Bailing out.");
return;
}
final String hwAddress = config.getHardwareAddress();
NetworkCapabilities nc = mNetworkCapabilities.get(iface);
if (nc == null) {
// Try to resolve using mac address
nc = mNetworkCapabilities.get(hwAddress);
if (nc == null) {
nc = createDefaultNetworkCapabilities();
}
}
IpConfiguration ipConfiguration = mIpConfigurations.get(iface);
if (ipConfiguration == null) {
ipConfiguration = createDefaultIpConfiguration();
}
Log.d(TAG, "Started tracking interface " + iface);
mFactory.addInterface(iface, hwAddress, nc, ipConfiguration);
// Note: if the interface already has link (e.g., if we crashed and got
// restarted while it was running), we need to fake a link up notification so we
// start configuring it.
if (config.hasFlag("running")) {
updateInterfaceState(iface, true);
}
}
一些列判断成功后就通过mFactory去调用连接网络的操作了。
所以,当我们主动设置静态ip时需要在frameworks\opt\net\ethernet\java\com\android\server\ethernet\EthernetServiceImpl.java类中添加下面修改:
/**
* Set Ethernet configuration
*/
@Override
public void setConfiguration(String iface, IpConfiguration config) {
if (!mStarted.get()) {
Log.w(TAG, "System isn't ready enough to change ethernet configuration");
}
enforceConnectivityInternalPermission();
if (mTracker.isRestrictedInterface(iface)) {
enforceUseRestrictedNetworksPermission();
}
// TODO: this does not check proxy settings, gateways, etc.
// Fix this by making IpConfiguration a complete representation of static configuration.
mTracker.updateIpConfiguration(iface, new IpConfiguration(config));
mTracker.start();//重新连接网络
}
我们重新调用start()方法去连接一下网络。我以为这样就可以了,如果是8.0确实好像可以。然后出现了问题。最后一步一步判断发现在EthernetTracker的方法。(请看中文注释)
private void maybeTrackInterface(String iface) {
if (DBG) Log.i(TAG, "maybeTrackInterface " + iface);
// If we don't already track this interface, and if this interface matches
// our regex, start tracking it.
//此处的判断需要注意,mFactory.hasInterface(iface)并不同于8.0以前的判断。我们具体看一下
if (!iface.matches(mIfaceMatch) || mFactory.hasInterface(iface)) {
return;
}
if (mIpConfigForDefaultInterface != null) {
updateIpConfiguration(iface, mIpConfigForDefaultInterface);
mIpConfigForDefaultInterface = null;
}
addInterface(iface);
}
这个判断的方法是这样的
boolean hasInterface(String interfacName) {
return mTrackingInterfaces.containsKey(interfacName);//eth0
}
而这个集合是添加的端口记录的
void addInterface(String ifaceName, String hwAddress, NetworkCapabilities capabilities,
IpConfiguration ipConfiguration) {
if (mTrackingInterfaces.containsKey(ifaceName)) {
Log.e(TAG, "Interface with name " + ifaceName + " already exists.");
return;
}
if (DBG) {
Log.d(TAG, "addInterface, iface: " + ifaceName + ", capabilities: " + capabilities);
}
NetworkInterfaceState iface = new NetworkInterfaceState(
ifaceName, hwAddress, mHandler, mContext, capabilities);
iface.setIpConfig(ipConfiguration);
mTrackingInterfaces.put(ifaceName, iface);//看这里看这里看这里看这里看这里
updateCapabilityFilter();
}
所以,当连接网线后,如果是同一个端口已经上网了,那么集合里面已经包含了该端口名了。所以我们在重新连接的时候,需要先清空我们的端口名。
最后
/**
* Set Ethernet configuration
*/
@Override
public void setConfiguration(String iface, IpConfiguration config) {
if (!mStarted.get()) {
Log.w(TAG, "System isn't ready enough to change ethernet configuration");
}
enforceConnectivityInternalPermission();
if (mTracker.isRestrictedInterface(iface)) {
enforceUseRestrictedNetworksPermission();
}
// TODO: this does not check proxy settings, gateways, etc.
// Fix this by making IpConfiguration a complete representation of static configuration.
mTracker.updateIpConfiguration(iface, new IpConfiguration(config));
mTracker.removeInterface(iface);//清除当前端口
mTracker.start();
}
好了用过上面的修改,我们就可以实现9.0修改以太网静态ip地址了。希望能够有用。
今夕是何夕~
晚风过花庭~