Android 9.0 以太网上网设置静态ip,解决拔插后才能更改ip地址的问题

在前面看过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<InetAddress> mDnsServers = new ArrayList<InetAddress>();
				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<String, IpConfiguration> 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地址了。希望能够有用。

今夕是何夕~

晚风过花庭~

猜你喜欢

转载自blog.csdn.net/qq_33796069/article/details/101307453
今日推荐