android 8.1 framework层Ethernet多个以太网口切换连接实现流程
之前写过一篇《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多个以太网口切换连接实现流程相关推荐
- 从Android应用层及Framework层的角度分析WakeLock锁机制
从Android应用层及Framework层的角度分析WakeLock锁机制 本篇博客编写思路总结和关键点说明: 为了更加方便的读者阅读博客,通过导读思维图的形式将本博客的关键点列举出来,从而方便 ...
- android 8 修改ip,android 8.1 framework层修改以太网静态ip功能
Android5.0基础上到Android7.0,Android都自带了以太网的设置功能,基本上都是将ip地址和网关之类的信息通过StaticIpConfiguration该对象实现联网功能.到了An ...
- 用Android Studio调试Framework层代码
Android程序员不得不知的调试技巧. 本文以webview loadUrl和域名解析为例,介绍配合使用LLDB和Android Studio调试Framework代码的技巧. java 层调试 首 ...
- android四大组件在哪层,Android四大组件framework层
activity https://www.kancloud.cn/alex_wsc/android-deep2/413484 当前Activity Activity向AMS发送StartActivit ...
- android应用框架 平台结构 源代码结构 事件处理流程 Framework层收到事件的处理过程 电话处理流程
android应用框架 平台结构 第1层: Linux操作系统及驱动 C语言实现 第2层: 本地框架和Java运行环境 C和C++实现 第3层: Java框架(framework) Java实现 第4 ...
- Android高工必备:说说从手机开机到APP启动FrameWork层的整体执行流程
引言 本文讲解从开机到app显示画面的流程,但不分析源码,如果想阅读源码请到参考文章中查阅. 纸上说来终觉浅,建议有时间的小伙伴去我的B站观看视频讲解:Android进阶:手机开机到APP启动中间Fr ...
- Binder框架在Framework层的C++中的使用
Binder框架在Framework层的C++中的使用 转自:http://blog.csdn.net/a345017062/article/details/6175519 关于Binder在C++层 ...
- 了解Framework层对一名Android工程师的工作有什么帮助吗?
了解Framework层对一名Android工程师的工作有什么帮助吗? 最近有一个朋友向我问了一个这样的问题: 作为一个应用开发工程师,在网上有看到过大家都说了解系统源码,例如四大组件启动流程及IPC ...
- android power 按键,Android Framework层Power键关机流程(一,Power长按键操作处理)
一:Android处理Power按键长按操作 在Framework层中,Android4.x对Power键(KeyEvent.KEYCODE_POWER)的操作,我们从PhoneWindowManag ...
最新文章
- 分解原理_基于矩阵分解原理的推荐系统
- 构建现代产业体系 农民丰收节·林裕豪:从玉农业落实“链长制”1-09-27
- 涨知识!参加过将近三十届广交会的老鸟总结!
- 面具卡米怎么删模块_魔兽8.3咋肥事——面具带几个收益高?对小怪宝箱水晶有加成吗?...
- 二十五、Node中的Buffer缓冲器和EventEmitter事件触发器
- IE浏览器开发人员工具怎么使用
- 探索ring0之内核概述
- 后续:为LAMP添加XCache加速
- sql server xp_cmdshell 命令
- CodeForces405B - Jzzhu and Sequences 矩阵快速幂
- HDU_5688 (map)
- 2021华为软挑赛题_思路分析——实时更新,做多少更多少(一)
- windows10 64位 JDK1.8 下载
- Protel99se中PCB放置焊盘和设置焊盘大小
- android pak文件_Android 动态加载 (三) PAK 详解
- 一文搞懂Handler机制原理
- 从思维走向实践,数字化转型 IT 经营的成功路径
- 栈溢出 __stack_chk_fail
- 纯HTML5后台模板
- 冯诺伊曼计算机釆用二进制,大一新生计算机考试重点复习资料(完整版).docx...