Android5.1 Ethernet添加ip配置(Static DHCP)

博主从业经验不多,本次调试Ethernet参考了许多资料,走了很多弯路,现在也还有点问题,先记录下调试心得,文中提到的未解决的问题或写的错误得地方,还望各位看官大佬不吝赐教。

参考:

https://blog.csdn.net/Purple7826/article/details/80608172

https://blog.csdn.net/qq_34705828/article/details/76283929

https://www.jianshu.com/p/3d95347633a3

本文基于qcom msm8909平台,先以思维导图的形式展示下调试Ethernet修改或添加的文件。

Android5.1 Ethernet添加ip配置(Static DHCP)_第1张图片

然后在描述下settings app层基本实现思路:

Android5.1 Ethernet添加ip配置(Static DHCP)_第2张图片

实现步骤:

1:settings 部分

添加一级子菜单:packages/apps/Settings/res/xml/dashboard_categories.xml

修改string :packages/apps/Settings/res/values/strings.xml

Ethernet
Configure Ethernet device
Ethernet
Ethernet Devices:
Connection Type
DHCP
Static IP
DNS address
Gateway address
IP address
Ethernet
Turn on Ethernet
Ethernet configuration
Configure Ethernet devices
Netmask
Turn off Ethernet
Turn on Ethernet
Failed to set: Please enter the valid characters 0~255
can\'t be empty

Network prefix length
Cancle
Save

Setting主类添加:packages/apps/Settings/src/com/android/settings/SettingsActivity.java

import com.android.settings.ethernet.EthernetSettings;
R.id.ethernet_settings,
EthernetSettings.class.getName(),

添加空类:packages/apps/Settings/src/com/android/settings/Settings.java

public static class EthernetSettingsActivity extends SettingsActivity { /* empty */ }

 AndroidMainfest添加:packages/apps/Settings/AndroidManifest.xml


    
        
        
        

        
        
        
    

    
    
    

增加Ethernet主界面布局:packages/apps/Settings/res/xml/ethernet_settings.xml


    


添加Ethernet主类:packages/apps/Settings/src/com/android/settings/ethernet/EthernetSettings.java

至此:setting里面的界面布局等全部完成,逻辑处理等详情或代码就不具体详叙了。

效果图如下:

Android5.1 Ethernet添加ip配置(Static DHCP)_第3张图片

Android5.1 Ethernet添加ip配置(Static DHCP)_第4张图片

Android5.1 Ethernet添加ip配置(Static DHCP)_第5张图片

Android5.1 Ethernet添加ip配置(Static DHCP)_第6张图片

接下来分析下调用完setConfiguration()之后的流程,惯例,先上一张图。

Android5.1 Ethernet添加ip配置(Static DHCP)_第7张图片

当我们调用setConfiguration()之后,肯定在想这个方法里面干了些什么,那么我们继续往下走。

先看setConfiguration是通过EthernetManager调用的,我们先找到IEthernetManager.aidl这个通信接口,看看里面的内容。

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net;

import android.net.IpConfiguration;
import android.net.IEthernetServiceListener;

/**
 * Interface that answers queries about, and allows changing
 * ethernet configuration.
 */
/** {@hide} */
interface IEthernetManager
{
    IpConfiguration getConfiguration();
    void setConfiguration(in IpConfiguration config);
    boolean isAvailable();
    void addListener(in IEthernetServiceListener listener);
    void removeListener(in IEthernetServiceListener listener);
}

这里我们看到@hide这个参数,应该就会知道,这个接口被隐藏掉了,应用层的开发是不能调到这个接口的,但我们是在做系统app的调试,没什么关系,正常使用。

但是这里只有方法的定义,实现在哪里呢?简单,实现肯定在EthernetManager.java中,于是找到该文件frameworks/base/core/java/android/net/EthernetManager.java 找到setConfiguration()的实现,

如下:

/**
 * Set Ethernet configuration.
 */
public void setConfiguration(IpConfiguration config) {
    try {
        mService.setConfiguration(config);
    } catch (NullPointerException | RemoteException e) {
    }
}

咋的一看,咦,没干啥啊,实现很简单,原来,通过了aidl的实现,远程通信。

private final IEthernetManager mService;

那既然用了aidl,那接口对应的服务在哪呢,于是首先找到网口服务

frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetService.java

看看里面的实现

public final class EthernetService extends SystemService {

    private static final String TAG = "EthernetService";
    final EthernetServiceImpl mImpl;

    public EthernetService(Context context) {
        super(context);
        mImpl = new EthernetServiceImpl(context);
    }

    @Override
    public void onStart() {
        Log.i(TAG, "Registering service " + Context.ETHERNET_SERVICE);
        publishBinderService(Context.ETHERNET_SERVICE, mImpl);
    }

    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            mImpl.start();
        }
    }
}

实现很简单,看到构造方法中创建了一个EthernetServiceImpl实例,跟到EthernetServiceImpl.java看看,

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

 * @hide
 */
public class EthernetServiceImpl extends IEthernetManager.Stub {

如图,后面的没有展示出来,详细了解的请查看源码。

果然,EthernetServiceImpl.java继承了IEthernetManager接口并实现里面的方法。

那么现在就一目了然了。

找到setConfiguration的实现方法

/**
 * Set Ethernet configuration
 */
@Override
public void setConfiguration(IpConfiguration config) {
    if (!mStarted.get()) {
        Log.w(TAG, "System isn't ready enough to change ethernet configuration");
    }

    enforceChangePermission();
    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)) {
            Log.w(TAG, "!config.equals(mIpConfiguration)");
            mIpConfiguration = new IpConfiguration(config);
            mTracker.stop();
            mTracker.start(mContext, mHandler);
        }
    }
}

如图,方法里调用了frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetConfigStore.java

里面的writeIpAndProxyConfigurations(),这样就把配置写入指定的文件了。

可以看看该类的实现,实现也很简单,上个代码就好了。

public class EthernetConfigStore extends IpConfigStore {
    private static final String TAG = "EthernetConfigStore";

    private static final String ipConfigFile = Environment.getDataDirectory() +
            "/misc/ethernet/ipconfig.txt";

    public EthernetConfigStore() {
    }

    public IpConfiguration readIpAndProxyConfigurations() {
        SparseArray networks = readIpAndProxyConfigurations(ipConfigFile);

        Log.d(TAG, "readIpAndProxyConfigurations networks.size():"+networks.size());

        if (networks.size() == 0) {
            Log.w(TAG, "No Ethernet configuration found. Using default.");
            return new IpConfiguration(IpAssignment.DHCP, ProxySettings.NONE, null, null);
        }

        if (networks.size() > 1) {
            // Currently we only support a single Ethernet interface.
            Log.w(TAG, "Multiple Ethernet configurations detected. Only reading first one.");
        }

        return networks.valueAt(0);
    }

    public void writeIpAndProxyConfigurations(IpConfiguration config) {
        SparseArray networks = new SparseArray();
        networks.put(0, config);
        writeIpAndProxyConfigurations(ipConfigFile, networks);
    }
}

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

再回到setConfiguration()实现的地方,我们可以看到在实现写配置之后,进行了一个ip配置是否相同的判断,如果有修改了配置,那么就开了一个线程锁,里面对ipConfigure进行配置,并且启动了EthernetNetworkFactory中的start方法。该start方法主要功能就是监控连接。可以看看该方法的实现。

/**
 * Begin monitoring connectivity
 */
public synchronized void start(Context context, Handler target) {
    // The services we use.
    IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
    mNMService = INetworkManagementService.Stub.asInterface(b);
    mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE);

    // Interface match regex.
    mIfaceMatch = context.getResources().getString(
            com.android.internal.R.string.config_ethernet_iface_regex);

    // Create and register our NetworkFactory.
    mFactory = new LocalNetworkFactory(NETWORK_TYPE, context, target.getLooper());
    mFactory.setCapabilityFilter(mNetworkCapabilities);
    mFactory.setScoreFilter(-1); // this set high when we have an iface
    mFactory.register();

    mContext = context;

    // Start tracking interface change events.
    mInterfaceObserver = new InterfaceObserver();
    try {
        mNMService.registerObserver(mInterfaceObserver);
    } catch (RemoteException e) {
        Log.e(TAG, "Could not register InterfaceObserver " + e);
    }

    // If an Ethernet interface is already connected, start tracking that.
    // Otherwise, the first Ethernet interface to appear will be tracked.
    try {
        final String[] ifaces = mNMService.listInterfaces();
        for (String iface : ifaces) {
            synchronized(this) {
                if (maybeTrackInterface(iface)) {
                    // We have our interface. Track it.
                    // 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. Since we're already holding the lock,
                    // any real link up/down notification will only arrive
                    // after we've done this.
                    if (mNMService.getInterfaceConfig(iface).hasFlag("running")) {
                        updateInterfaceState(iface, true);
                    }
                    break;
                }
            }
        }
    } catch (RemoteException e) {
        Log.e(TAG, "Could not get list of interfaces " + e);
    }
}

至此,暂时分析到此处了。还有很多地方没分析到,逻辑感觉也不是很清晰,希望大佬们指点。

另外,博主在调试的时候发现,为了应用层更人性化,参照wifi的逻辑和上述提到的参考博客,对Ethernet的网络连接状态,物理接口,网线插拔进行了一些添加,并在systemui中进行一些实时的更新。

这里简单做些说明。

惯例,上图。

Android5.1 Ethernet添加ip配置(Static DHCP)_第8张图片

先回头看看EthernetManager中的方法里面有个Listener,

    /**
     * A listener interface to receive notification on changes in Ethernet.
     */
    public interface Listener {
        /**
         * Called when Ethernet port's availability is changed.
         * @param isAvailable {@code true} if one or more Ethernet port exists.
         */
        public void onAvailabilityChanged(boolean isAvailable);
    }

这个listener说能检测到一个或者多个网口事件,博主在settings应用层中添加listener后测试确实能够监听到网口事件,但好像不是对网线插拔的,仅仅是对于网口芯片是否接入的检测。

于是,我们得自己添加一个listener来监听网线的接入与拔出。

步骤如下:

1:在网络管理逻辑处理类EthernetNetworkFactory.java中添加一个全局变量用来标志网线的状态

private boolean isEthernetAdded = false; 

2:修改网络接口状态监听代码

ps:细心的人可能在刚刚介绍EthernetNetworkFactory.start()中看到了一个叫InterfaceObserver的内部类的使用

// Start tracking interface change events.
mInterfaceObserver = new InterfaceObserver();

这里就解释到这个类是跟踪接口更改事件的。

那既然如此我们刚刚定义的变量就放在这里操作。

private class InterfaceObserver extends BaseNetworkObserver {
    @Override
    public void interfaceLinkStateChanged(String iface, boolean up) {
        Log.i(TAG,"interfaceLinkStateChanged---iface:"+iface+"up:"+up);
        updateInterfaceState(iface, up);
    }

    @Override
    public void interfaceAdded(String iface) {
        Log.i(TAG,"interfaceAdded---iface:"+iface);
        maybeTrackInterface(iface);
    }

    @Override
    public void interfaceRemoved(String iface) {
        Log.i(TAG,"interfaceRemoved---iface:"+iface);
        stopTrackingInterface(iface);
    }
}

因为我这里只想监听eth0 的 usb net,所以我加了一个监听过滤,修改后如下:

private class InterfaceObserver extends BaseNetworkObserver {
    @Override
    public void interfaceLinkStateChanged(String iface, boolean up) {
        Log.i(TAG,"interfaceLinkStateChanged---iface:"+iface+"up:"+up);
        updateInterfaceState(iface, up);
        if("eth0".equals(iface)){
            Log.i(TAG,"interfaceLinkStateChanged---linked:"+up);
            isEthernetAdded = up;
            notifyEthernetInjected(isEthernetAdded); // 监听网线插拔
        }

    }

    @Override
    public void interfaceAdded(String iface) {
        Log.i(TAG,"interfaceAdded---iface:"+iface);
        maybeTrackInterface(iface);
        if("eth0".equals(iface)){
            Log.i(TAG,"interfaceAdded");
            isEthernetAdded = true;
            notifyEthernetInjected(isEthernetAdded);
        }

    }

    @Override
    public void interfaceRemoved(String iface) {
        Log.i(TAG,"interfaceRemoved---iface:"+iface);
        stopTrackingInterface(iface);
        if("eth0".equals(iface)){
            Log.i(TAG,"interfaceRemoved");
            isEthernetAdded = false;
            notifyEthernetInjected(isEthernetAdded);
        }

    }
}

通过改完后的代码可以看出,该类里面重写了三个方法。

interfaceLinkStateChanged:接口状态改变时调用

interfaceAdded:新接口接入时调用

interfaceRemoved:接口移除时调用

看到这里里面好像有个不眼熟的方法notifyEthernetInjected();

对,这个是我们添加用来通知所有注册的listener的方法。

private void notifyEthernetInjected(boolean isInjected){
    int n = mListeners.beginBroadcast();
    Log.i(TAG,"notifyEthernetInjected state listener size : "+n+",isInjected:"+isInjected);
    for (int i = 0; i < n; i++) {
        try {
            mListeners.getBroadcastItem(i).onEthernetInjected(isInjected);
        } catch (RemoteException e) {
            // Do nothing here.
        }
    }
    mListeners.finishBroadcast();
}

参照IEthernetManager.aidl中的isAvailable()方法,我们需要添加获取网线状态的接口isEthernetInjected();

public synchronized boolean isEthernetInjected(){
    return isEthernetAdded;
}

这里我们实现的isEthernetInjected()要想让EthernetManager.java调用,需要在IEthetnetManager.aidl中添加接口,并且在EthernetManager.java,EthernetServiceImpl.java中实现,

IEthetnetManager.aidl:

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net;

import android.net.IpConfiguration;
import android.net.IEthernetServiceListener;

/**
 * Interface that answers queries about, and allows changing
 * ethernet configuration.
 */
/** {@hide} */
interface IEthernetManager
{
    IpConfiguration getConfiguration();
    void setConfiguration(in IpConfiguration config);
    boolean isAvailable();
    void addListener(in IEthernetServiceListener listener);
    void removeListener(in IEthernetServiceListener listener);
    void updateIfaceState(String iface,boolean up);
    boolean isEthernetInjected();
}

EthernetManager.java:

public boolean isEthernetInjected() {
    try {
        return mService.isEthernetInjected();
    } catch (NullPointerException | RemoteException e) {
        return false;
    }
}

EthernetServiceImpl.java

@Override
public boolean isEthernetInjected() {
    enforceAccessPermission();
    return mTracker.isEthernetInjected();
}

到现在,我们还不能在应用层监听网线插拔的状态,因为我们还需要添加一个listener,还是先看IEthernetManager.aidl中的

void addListener(in IEthernetServiceListener listener);

看到参数是一个IEthernetServiceListener的类型,我们跟到IEthernetServiceListener.aidl中发现如下:

/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net;

/** @hide */
oneway interface IEthernetServiceListener
{
    void onAvailabilityChanged(boolean isAvailable);
}

发现刚刚在应用层添加的监听里面重写的方法原来在这里,那么好办了,我们添加的监听将要被重写的方法也加在这。

如下:

package android.net;

/** @hide */
oneway interface IEthernetServiceListener
{
    void onAvailabilityChanged(boolean isAvailable);
    void onEthernetInjected(boolean isInjected);
}

在这里添加后,我们得在EthernetManager.java中去实现它,找到

private final IEthernetServiceListener.Stub mServiceListener =
        new IEthernetServiceListener.Stub() {
            @Override
            public void onAvailabilityChanged(boolean isAvailable) {
                Log.i(TAG,"onEthernetInjected---isAvailable:"+isAvailable);
                mHandler.obtainMessage(
                        MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, null).sendToTarget();
            }
        };

参照onAvailabilityChanged,依葫芦画瓢添加onEthernetInjected

添加后如下:

private final IEthernetServiceListener.Stub mServiceListener =
        new IEthernetServiceListener.Stub() {
            @Override
            public void onAvailabilityChanged(boolean isAvailable) {
                Log.i(TAG,"onEthernetInjected---isAvailable:"+isAvailable);
                mHandler.obtainMessage(
                        MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, null).sendToTarget();
            }

            @Override
            public void onEthernetInjected(boolean isInjected) {
                Log.i(TAG,"onEthernetInjected---isInjected:"+isInjected);
                mHandler.obtainMessage(MSG_ETHERNET_INJECTION_CHANGED,isInjected).sendToTarget();
            }
        };

这里我们用handler发出了网线接入拔出的消息,那么得去处理他,还是在EthernetManager.java中

private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if (msg.what == MSG_AVAILABILITY_CHANGED) {
            boolean isAvailable = (msg.arg1 == 1);
            for (Listener listener : mListeners) {
                listener.onAvailabilityChanged(isAvailable);
            }
        }
    }
};

这里可以看到处理了onAvailabilityChanged发出来的消息,我们在这里继续加上onEthernetInjected发出的消息的处理。

private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if (msg.what == MSG_AVAILABILITY_CHANGED) {
            boolean isAvailable = (msg.arg1 == 1);
            for (Listener listener : mListeners) {
                listener.onAvailabilityChanged(isAvailable);
            }
        }
        else if(msg.what == MSG_ETHERNET_INJECTION_CHANGED){
            boolean isInjected = (boolean)msg.obj;
            for (EthernetListener listener : mEthernetListeners) {
                listener.onEthernetInjected(isInjected);
            }
        } 

    }
};

这里我们可以看到,在消息处理中有个EthernetListener的listener,mEthernetListeners是listener的ArrayList,这是是需要我们自己去实现的。

添加定义:

private final ArrayList mEthernetListeners = new ArrayList();  

添加listener接口,实现add 和 remove方法。

public interface EthernetListener {
    public void onEthernetInjected(boolean isEthernetInjected);
}

public void addEthernetListener(EthernetListener listener){
    if (listener == null) {
        throw new IllegalArgumentException("listener must not be null");
    }
    mEthernetListeners.add(listener);
}

public void removeEthernetListener(EthernetListener listener) {
    if (listener == null) {
        throw new IllegalArgumentException("listener must not be null");
    }
    mEthernetListeners.remove(listener);
}

至此,全部实现!

还有两点问题:

1:开机后默认使用DHCP,IP获取到了,但不能上网,需要插拔一次网线后才可以上网

2:应用层不知道怎么获取当前接入的Ethernet是否能够上网。

你可能感兴趣的:(android)