之前写过一篇《android 8.1 framework层修改以太网静态ip功能》的文章,这篇是在该基础上实现的。之前的功能,只在设备仅支持单个网口的情况下。当大佬拿来一个设备说:这个还可以外接底座,多网口,目前无法上网。我内心是崩溃的。于是我又开启了慢慢的framework源码阅读之旅。因为百度目前没有找到过相关资料,希望我这篇能补缺——我会很开心!

我这边直接从设置以太网开始讲解,之前该怎么做的,请看我上面链接的文章!

全篇涉及到的文件有:

frameworks\base\services\core\java\com\android\server\NetworkManagementService.java

frameworks\opt\net\ethernet\java\com\android\server\ethernet(底下全部文件)

frameworks\base\services\core\java\com\android\server\net\IpConfigStore

先从以太网大致流程说起,当设备开机时,启动服务

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);}@Overridepublic void onStart() {Log.i(TAG, "Registering service " + Context.ETHERNET_SERVICE);publishBinderService(Context.ETHERNET_SERVICE, mImpl);}@Overridepublic void onBootPhase(int phase) {if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {mImpl.start();}}
}

启动服务同时也实现了EthernetServiceImpl类,在该类的构造方法中初始化了EthernetConfigStore,去读取以太网配置文件,获取参数。主要代码:

 public EthernetServiceImpl(Context context) {mContext = context;Log.i(TAG, "Creating EthernetConfigStore");mEthernetConfigStore = new EthernetConfigStore();mIpConfiguration = mEthernetConfigStore.readIpAndProxyConfigurations();Log.i(TAG, "Read stored IP configuration: " + mIpConfiguration);mTracker = new EthernetNetworkFactory(mListeners);//这个注意,下面会用到}

再看EthernetConfigStore

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<IpConfiguration> networks = readIpAndProxyConfigurations(ipConfigFile);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<IpConfiguration> networks = new SparseArray<IpConfiguration>();networks.put(0, config);writeIpAndProxyConfigurations(ipConfigFile, networks);}
}

我们看到是他读取地址为Environment.getDataDirectory() + "/misc/ethernet/ipconfig.txt"的文件,我们也可以用adb pull出来看(用处不大)。

我们发现继承的是frameworks\base\services\core\java\com\android\server\net\IpConfigStore.java类,具体怎么读取的是在这里面实现的,可以看看,我就不贴代码了。

然后读取配置文件,调用mImpl.start();方法,我们再分析这个方法的流程走向。

 public void start() {Log.i(TAG, "Starting Ethernet service");HandlerThread handlerThread = new HandlerThread("EthernetServiceThread");handlerThread.start();mHandler = new Handler(handlerThread.getLooper());mTracker.start(mContext, mHandler);mStarted.set(true);}

我们看到mTracker = new EthernetNetworkFactory(mListeners);已经在上面EthernetServiceImpl类里面初始化了,所以,这个时候我们就要跳到EthernetNetworkFactory类里面查看是怎么实现的了。

 /*** Begin monitoring connectivity*/public void start(Context context, Handler handler) {mHandler = handler;// 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, mHandler.getLooper());mFactory.setCapabilityFilter(mNetworkCapabilities);mFactory.setScoreFilter(NETWORK_SCORE);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.mHandler.post(() -> trackFirstAvailableInterface());}

前面都是初始化获取manager对象,最后handler调用trackFirstAvailableInterface();

 public void trackFirstAvailableInterface() {try {//读取设备接口列表,包含各种接口final String[] ifaces = mNMService.listInterfaces();for (String iface : ifaces) {//这里排除其他不是eth\d的接口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.if (mNMService.getInterfaceConfig(iface).hasFlag("running")) {updateInterfaceState(iface, true);//只有都匹配了才能真正的上网了。}break;}}} catch (RemoteException|IllegalStateException e) {Log.e(TAG, "Could not get list of interfaces " + e);}}

请记住这个方法,等一下我们还要回来改,因为多个网口就是要修改这里面的代码!!!

我们还有特别注意上面那个筛选的判断方法

 private boolean maybeTrackInterface(String iface) {// If we don't already have an interface, and if this interface matches// our regex, start tracking it.if (!iface.matches(mIfaceMatch) || isTrackingInterface())return false;Log.d(TAG, "Started tracking interface " + iface);setInterfaceUp(iface);return true;}

这个是用来过滤只有eth\d的接口才需要走的方法。其他的直接return就行。如果只有一个以太网口,那么就只有eth0,如果是多个网口,那么就是eth0和eth1;

其中还有一个判断的方法

 public boolean isTrackingInterface() {return !TextUtils.isEmpty(mIface);}

当时我就很疑惑,为什么要判断mIface是否为空,而不是传来的iface。原来最终传给NetworkManagementService的是mIface这个字段,那么他在什么时候赋值的?我们往下面看。

接下来的一个更重要的方法:

private void setInterfaceUp(String iface) {// Bring up the interface so we get link status indications.try {mNMService.setInterfaceUp(iface);String hwAddr = null;InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);if (config == null) {Log.e(TAG, "Null interface config for " + iface + ". Bailing out.");return;}if (!isTrackingInterface()) {setInterfaceInfo(iface, config.getHardwareAddress());mNetworkInfo.setIsAvailable(true);mNetworkInfo.setExtraInfo(mHwAddr);} else {Log.e(TAG, "Interface unexpectedly changed from " + iface + " to " + mIface);mNMService.setInterfaceDown(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 " + mIface + ": " + e);}}

其中有一个mNMService.setInterfaceUp(iface);,这里面就涉及到了

frameworks\base\services\core\java\com\android\server\NetworkManagementService.java

这里就是我们最后能跟到的地方了,真正的设置参数连接上网实现,再细看下去我已经无能为力了。这个逻辑google已经写好了,所以我们看看就好。

我们就看看一个普通的方法:

@Overridepublic void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);Slog.d(TAG, "Enter setInterfaceConfig, iface=" + iface);LinkAddress linkAddr = cfg.getLinkAddress();if (linkAddr == null || linkAddr.getAddress() == null) {throw new IllegalStateException("Null LinkAddress given");}final Command cmd = new Command("interface", "setcfg", iface,linkAddr.getAddress().getHostAddress(),linkAddr.getPrefixLength());for (String flag : cfg.getFlags()) {cmd.appendArg(flag);}try {mConnector.execute(cmd);} catch (NativeDaemonConnectorException e) {throw e.rethrowAsParcelableException();}}

看了跟没看一样,这里面想看可以自己去深入看看。

我们继续看setInterfaceUp方法后往哪里走。

 /*** Set interface information and notify listeners if availability is changed.*/private void setInterfaceInfo(String iface, String hwAddr) {boolean oldAvailable = isTrackingInterface();mIface = iface;//这里面终于赋值了。。。。。。mHwAddr = hwAddr;boolean available = isTrackingInterface();mNetworkInfo.setExtraInfo(mHwAddr);mNetworkInfo.setIsAvailable(available);if (oldAvailable != available) {int n = mListeners.beginBroadcast();for (int i = 0; i < n; i++) {try {mListeners.getBroadcastItem(i).onAvailabilityChanged(available);} catch (RemoteException e) {// Do nothing here.}}mListeners.finishBroadcast();}}

我们看到mIface 被赋值了。最后设置网络信息值,发送广播这个整个判断流程就走完了,然后我们就回到了trackFirstAvailableInterface方法了,就是我们要记住的那个需要修改的方法,红字标记。

当所有条件都符合最后调用的就是

public void startIpManager() {if (DBG) {Log.d(TAG, String.format("starting IpManager(%s): mNetworkInfo=%s", mIface,mNetworkInfo));}LinkProperties linkProperties;IpConfiguration config = mEthernetManager.getConfiguration();if (config.getIpAssignment() == IpAssignment.STATIC) {if (!setStaticIpAddress(config.getStaticIpConfiguration())) {// We've already logged an error.return;}linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface);/*   } else {mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);IpManager.Callback ipmCallback = new IpManager.Callback() {@Overridepublic void onProvisioningSuccess(LinkProperties newLp) {mHandler.post(() -> onIpLayerStarted(newLp));}@Overridepublic void onProvisioningFailure(LinkProperties newLp) {mHandler.post(() -> onIpLayerStopped(newLp));}@Overridepublic void onLinkPropertiesChange(LinkProperties newLp) {mHandler.post(() -> updateLinkProperties(newLp));}};stopIpManager();mIpManager = new IpManager(mContext, mIface, ipmCallback); */if (config.getProxySettings() == ProxySettings.STATIC ||config.getProxySettings() == ProxySettings.PAC) {// mIpManager.setHttpProxy(config.getHttpProxy());linkProperties.setHttpProxy(config.getHttpProxy());}/* final String tcpBufferSizes = mContext.getResources().getString(com.android.internal.R.string.config_ethernet_tcp_buffers); */String tcpBufferSizes = mContext.getResources().getString(com.android.internal.R.string.config_ethernet_tcp_buffers);     /* if (!TextUtils.isEmpty(tcpBufferSizes)) {mIpManager.setTcpBufferSizes(tcpBufferSizes);} */if (TextUtils.isEmpty(tcpBufferSizes) == false) {linkProperties.setTcpBufferSizes(tcpBufferSizes);}} else {mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);}/*  final ProvisioningConfiguration provisioningConfiguration =mIpManager.buildProvisioningConfiguration().withProvisioningTimeoutMs(0).build();mIpManager.startProvisioning(provisioningConfiguration); */// }IpManager.Callback ipmCallback = new IpManager.Callback() {@Overridepublic void onProvisioningSuccess(LinkProperties newLp) {mHandler.post(() -> onIpLayerStarted(newLp));}@Overridepublic void onProvisioningFailure(LinkProperties newLp) {mHandler.post(() -> onIpLayerStopped(newLp));}@Overridepublic void onLinkPropertiesChange(LinkProperties newLp) {mHandler.post(() -> updateLinkProperties(newLp));}};stopIpManager();mIpManager = new IpManager(mContext, mIface, ipmCallback);//此处mIface,最后配置参数if (config.getProxySettings() == ProxySettings.STATIC ||config.getProxySettings() == ProxySettings.PAC) {mIpManager.setHttpProxy(config.getHttpProxy());}final String tcpBufferSizes = mContext.getResources().getString(com.android.internal.R.string.config_ethernet_tcp_buffers);if (!TextUtils.isEmpty(tcpBufferSizes)) {mIpManager.setTcpBufferSizes(tcpBufferSizes);}if (config.getIpAssignment() == IpAssignment.STATIC) {mIpManager.startProvisioning(config.getStaticIpConfiguration());} else {final ProvisioningConfiguration provisioningConfiguration =mIpManager.buildProvisioningConfiguration().withProvisioningTimeoutMs(0).build();mIpManager.startProvisioning(provisioningConfiguration);}}

。整个流程就走完了。好了,我好像还没有讲多个网口。。。。。最重要的来了

我们来看看那个标红的代码:

 public void trackFirstAvailableInterface() {try {//读取设备接口列表,包含各种接口final String[] ifaces = mNMService.listInterfaces();for (String iface : ifaces) {//这里排除其他不是eth\d的接口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.if (mNMService.getInterfaceConfig(iface).hasFlag("running")) {updateInterfaceState(iface, true);//只有都匹配了才能真正的上网了。}      break;}}} catch (RemoteException|IllegalStateException e) {Log.e(TAG, "Could not get list of interfaces " + e);}}

我们获取到所有接口时,有多个eth,当eth0走进判断,该方法已经被break了,循环结束,所以,后面你就是有100个网口他也不会再去配置上网的信息了。我上面提到最后传给NetworkManagementService的是mIface,而mIface是在maybeTrackInterface方法逻辑赋值的,我通过log发现,当第一次mIface被赋值以后,一直是eth0,当你eth0没有连接网线时,那么

public boolean isTrackingInterface() {
        return !TextUtils.isEmpty(mIface);
    }

这个判断方法就一直是true了。那么也不能走完设置上网参数的方法,所以我们最后的修改是在trackFirstAvailableInterface方法

public void trackFirstAvailableInterface() {try {final String[] ifaces = mNMService.listInterfaces();for (String iface : ifaces) {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.if (mNMService.getInterfaceConfig(iface).hasFlag("running")) {updateInterfaceState(iface, true);}else{mIface = "";}//  break;}}} catch (RemoteException|IllegalStateException e) {Log.e(TAG, "Could not get list of interfaces " + e);}}

这样我们就能让其他的网口也设置连接参数。并且当你有两个网口时,第一个网口不是running状态,将mIface置空,当eth1进来时重新赋值,这样就完美实现了eth1的上网了。

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

我看了几天源码,就修改了两行代码。。。。。。。刚接触framework层,比较生疏。

加油!

2019年9月4日18:21:48

今天修改一下上面改的一些导致的bug:

1:两个网口拔插切换还是有问题
2:修改后导致wifi连接后,以太网也会自动连接;

解决方案如果

@@ -172,7 +172,9 @@ class EthernetNetworkFactory {if (up) {maybeStartIpManager();} else {
-                       mIface = "";
+                       if(mIface.matches(mIfaceMatch)){
+                               mIface = "";
+                       }stopIpManager();}}
@@ -184,7 +186,7 @@ class EthernetNetworkFactory {mHandler.post(() -> {Log.d(TAG, "这里也进来了吗: " + iface+";什么情况啊=="+up);
-                               if(up){
+                               if(up && iface != null && iface.matches(mIfaceMatch)){mIface = iface;}updateInterfaceState(iface, up);

解释一下,因为wifi也会走这个类,他的事wlan/,而以太网是eth/

所以要做判断

希望对你有帮助!

今夕是何夕,晚风过花庭~~~~~

android 8.1 framework层Ethernet多个以太网口切换连接实现流程相关推荐

  1. 从Android应用层及Framework层的角度分析WakeLock锁机制

      从Android应用层及Framework层的角度分析WakeLock锁机制 本篇博客编写思路总结和关键点说明: 为了更加方便的读者阅读博客,通过导读思维图的形式将本博客的关键点列举出来,从而方便 ...

  2. android 8 修改ip,android 8.1 framework层修改以太网静态ip功能

    Android5.0基础上到Android7.0,Android都自带了以太网的设置功能,基本上都是将ip地址和网关之类的信息通过StaticIpConfiguration该对象实现联网功能.到了An ...

  3. 用Android Studio调试Framework层代码

    Android程序员不得不知的调试技巧. 本文以webview loadUrl和域名解析为例,介绍配合使用LLDB和Android Studio调试Framework代码的技巧. java 层调试 首 ...

  4. android四大组件在哪层,Android四大组件framework层

    activity https://www.kancloud.cn/alex_wsc/android-deep2/413484 当前Activity Activity向AMS发送StartActivit ...

  5. android应用框架 平台结构 源代码结构 事件处理流程 Framework层收到事件的处理过程 电话处理流程

    android应用框架 平台结构 第1层: Linux操作系统及驱动 C语言实现 第2层: 本地框架和Java运行环境 C和C++实现 第3层: Java框架(framework) Java实现 第4 ...

  6. Android高工必备:说说从手机开机到APP启动FrameWork层的整体执行流程

    引言 本文讲解从开机到app显示画面的流程,但不分析源码,如果想阅读源码请到参考文章中查阅. 纸上说来终觉浅,建议有时间的小伙伴去我的B站观看视频讲解:Android进阶:手机开机到APP启动中间Fr ...

  7. Binder框架在Framework层的C++中的使用

    Binder框架在Framework层的C++中的使用 转自:http://blog.csdn.net/a345017062/article/details/6175519 关于Binder在C++层 ...

  8. 了解Framework层对一名Android工程师的工作有什么帮助吗?

    了解Framework层对一名Android工程师的工作有什么帮助吗? 最近有一个朋友向我问了一个这样的问题: 作为一个应用开发工程师,在网上有看到过大家都说了解系统源码,例如四大组件启动流程及IPC ...

  9. android power 按键,Android Framework层Power键关机流程(一,Power长按键操作处理)

    一:Android处理Power按键长按操作 在Framework层中,Android4.x对Power键(KeyEvent.KEYCODE_POWER)的操作,我们从PhoneWindowManag ...

最新文章

  1. 分解原理_基于矩阵分解原理的推荐系统
  2. 构建现代产业体系 农民丰收节·林裕豪:从玉农业落实“链长制”1-09-27
  3. 涨知识!参加过将近三十届广交会的老鸟总结!
  4. 面具卡米怎么删模块_魔兽8.3咋肥事——面具带几个收益高?对小怪宝箱水晶有加成吗?...
  5. 二十五、Node中的Buffer缓冲器和EventEmitter事件触发器
  6. IE浏览器开发人员工具怎么使用
  7. 探索ring0之内核概述
  8. 后续:为LAMP添加XCache加速
  9. sql server xp_cmdshell 命令
  10. CodeForces405B - Jzzhu and Sequences 矩阵快速幂
  11. HDU_5688 (map)
  12. 2021华为软挑赛题_思路分析——实时更新,做多少更多少(一)
  13. windows10 64位 JDK1.8 下载
  14. Protel99se中PCB放置焊盘和设置焊盘大小
  15. android pak文件_Android 动态加载 (三) PAK 详解
  16. 一文搞懂Handler机制原理
  17. 从思维走向实践,数字化转型 IT 经营的成功路径
  18. 栈溢出 __stack_chk_fail
  19. 纯HTML5后台模板
  20. 冯诺伊曼计算机釆用二进制,大一新生计算机考试重点复习资料(完整版).docx...

热门文章

  1. 一款强大到没朋友的图片编辑插件,爱了爱了!
  2. 7-59 打印菱形图案 (15 分)
  3. Docker failed to initialize
  4. 融合计费打造全业务核心竞争力
  5. restful重定向
  6. 关于两个串口停止位不一样还能通信的问题
  7. [IOS APP]【现代流行唱法】零基础入门学唱歌
  8. HTMLCSS——利用CSS定位背景图片 background-position
  9. 关于战略策划的一点认识
  10. T1103陶陶摘苹果(信息学一本通C++)