以太网的功能是允许设备提供硬件接口通过插入网线的形式访问互联网的功能。接入网线之后,设备可以动态的获取IP,DNS,Gateway等一系列网络属性,我们也可以手动配置设备的网络属性,使用静态配置参数。Google已经有一套现成的机制使用有线网,但没有涉及有线网配置的功能,本文主要介绍如何Google现有机制的基础上实现静态网络的配置。本文基于高通MSM8953 Android 7.1平台进行开发,通过配置eth0网口的IP,DNS,Gateway三个参数,实现上网功能,若是其他平台或者非高通平台,可以当作参考。
此部分Google已经做好,当接入网线之后,在SystemBar中会出现有线网介入图标(<--->),此时设备已经接入有线网络,可以正常上网。
首先先来介绍一下相关java类:
(1)frameworks/base/core/java/android/net/IpConfiguration.java
IP状态配置,动态或者是静态,之后会介绍
(2)frameworks/base/core/java/android/net/StaticIpConfiguration.java
静态IP配置相关类,主要用于配置静态IP。
(3)frameworks/base/core/java/android/net/EthernetManager.java
上层配置IP的管理类,可以通过context.getSystemService(Context.ETHERNET_SERVICE)获得。
(4)frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetServiceImpl.java
通过实现IEthernetManager.aidl接口来处理一些远程的以太网请求。
(5)frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java
以太网网络链接的管理类。
具体介绍之前,先来看一张简单配置UML的流程图,方便接下来的讲解.
接下来对照流程图逐步进行讲解。
我们自己的项目中是通过配置eth0的IP,DNS,Gateway来配置静态网络参数的。可以自己开发相应界面,让用户手动输入相关信息即可。
这一步不涉及配置代码,仅仅是获取用户的想要设置的配置信息。
首先我们需要将相关配置信息转化为StaticIpConfiguration,转化之前,先介绍两个枚举类:
public enum IpAssignment {
/* Use statically configured IP settings. Configuration can be accessed
* with staticIpConfiguration */
STATIC,
/* Use dynamically configured IP settigns */
DHCP,
/* no IP details are assigned, this is used to indicate
* that any existing IP settings should be retained */
UNASSIGNED
}
public enum ProxySettings {
/* No proxy is to be used. Any existing proxy settings
* should be cleared. */
NONE,
/* Use statically configured proxy. Configuration can be accessed
* with httpProxy. */
STATIC,
/* no proxy details are assigned, this is used to indicate
* that any existing proxy settings should be retained */
UNASSIGNED,
/* Use a Pac based proxy.
*/
PAC
}
这两个枚举类型在IpConfiguration类中,具体作用上面代码部分的注释也写明了。下面是将配置信息转化为StaticIpConfiguration的方法:
private StaticIpConfiguration validateIpConfigFields(String ip,String dns,String gateway) {
StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
//analysis ip address
Inet4Address inetAddr = getIPv4Address(ip);
if (inetAddr == null || inetAddr.equals(Inet4Address.ANY)) {
return -1;
}
staticIpConfiguration.ipAddress = new LinkAddress(inetAddr, DEFAULT_PREFIX_LENGTH);
//analysis gateway address
InetAddress gatewayAddr = getIPv4Address(gateway);
if (gatewayAddr == null) {
return -1;
}
if (gatewayAddr.isMulticastAddress()) {
return -1;
}
staticIpConfiguration.gateway = gatewayAddr;
//analysis dns address
InetAddress dnsAddr = getIPv4Address(dns);
if (dnsAddr == null) {
return -1;
}
staticIpConfiguration.dnsServers.add(dnsAddr);
return staticIpConfiguration;
}
private Inet4Address getIPv4Address(String text) {
try {
return (Inet4Address) NetworkUtils.numericToInetAddress(text);
} catch (IllegalArgumentException | ClassCastException e) {
Log.e(TAG,"getIPv4Address fail");
return null;
}
}
其中DEFAULT_PREFIX_LENGTH默认值是24,参考来自于Wifi模块。至此,我们就将用户输入的IP,DNS,Gateway转化为需要的StaticIpConfiguration。
由于最终调用EthernetManager的setConfiguration函数时传递的参数类型是IpConfiguration,查看StaticIpConfiguration,发现StaticIpConfiguration并不是IpConfiguration的子类,所以我们需要在将StaticIpConfiguration转化为IpConfiguration,查看IpConfiguration代码,发现IpConfiguration的构造函数中含有StaticIpConfiguration参数,另外,我们可以通过setStaticIpConfiguration改变IpConfiguration。这里我们选择前者,直接使用StaticIpConfiguration传入IpConfiguration的构造函数创建IpConfiguration对象,先看一下IpConfiguration的构造函数:
private void init(IpAssignment ipAssignment,
ProxySettings proxySettings,
StaticIpConfiguration staticIpConfiguration,
ProxyInfo httpProxy) {
this.ipAssignment = ipAssignment;
this.proxySettings = proxySettings;
this.staticIpConfiguration = (staticIpConfiguration == null) ?
null : new StaticIpConfiguration(staticIpConfiguration);
this.httpProxy = (httpProxy == null) ?
null : new ProxyInfo(httpProxy);
}
public IpConfiguration() {
init(IpAssignment.UNASSIGNED, ProxySettings.UNASSIGNED, null, null);
}
public IpConfiguration(IpAssignment ipAssignment,
ProxySettings proxySettings,
StaticIpConfiguration staticIpConfiguration,
ProxyInfo httpProxy) {
init(ipAssignment, proxySettings, staticIpConfiguration, httpProxy);
}
可以看出,无论是有参的构造函数还是无参的构造函数,最终都会调用IpConfiguration的init函数进行初始化配置。我们使用的是IpConfiguration中有参的构造函数,其中参数IpAssignment和ProxySettings是枚举类型,我们需要配置静态地址,所以应该传入IpAssignment.STATIC和ProxySettings.STATIC,第三个参数传入StaticIpConfiguration,第四个参数ProxyInfo传入空即可,不需要设置代理。
mIpAssignment = IpAssignment.STATIC;
mProxySettings = ProxySettings.STATIC;
mStaticIpConfiguration = validateIpConfigFields(ip,dns,gateway); // 注意此处的参数应正确配置
IpConfiguration ipconfig = new IpConfiguration(mIpAssignment,mProxySettings,mStaticIpConfiguration,null);
获取IpConfiguration之后,我们就可以调用EthernetManager的setConfiguration开始进行静态网络配置:
/**
* Set Ethernet configuration.
*/
public void setConfiguration(IpConfiguration config) {
try {
mService.setConfiguration(config);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
查看设置代码,函数会调用mService的setConfiguration并且可能会抛出RemoteException。说明这一操作应该是远程aidl的调用,跟踪代码发现mService的类型为EthernetServiceImpl,并且实现了IEthernetManager.aidl接口。
/**
* 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);
}
}
}
代码中EthernetConfigStore将会把IpConfiguration的配置信息写入配置文件。进入EthernetConfigStore发现writeIpAndProxyConfigurations最后会调用EthernetConfigStore父类writeIpAndProxyConfigurations方法将配置信息写入配置文件。
之后判断当前地址是否跟配置地址一样,若不一样,则进行新地址的配置,由于配置信息已经通过EthernetConfigStore写入配置文件,mTracker也即EthernetNetworkFactory就会重启当前网络。这部分的逻辑代码都是Google已有的代码,这里不继续跟踪。
注:有时候EthernetNetworkFactory重启网络之后发现配置信息没有生效,我遇到这种情况后,发现此时需要重启eth0网口。重启网口的功能将会在下面的文章里介绍。
/**
* 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);
}
解释中说此接口可以接受以太网变化的通知。在实际应用时,发现插入网线和拔出网线确实能够接受到通知,说明这个接口正是我们需要的。查看代码发现,要使用这个接口,应该先调用addListener将实现该接口的子类加入到一个ArrayList的通知列表里面,这说明我们可以在不同的地方接受以太网状态变化的通知。
/**
* Adds a listener.
* @param listener A {@link Listener} to add.
* @throws IllegalArgumentException If the listener is null.
*/
public void addListener(Listener listener) {
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
mListeners.add(listener);
if (mListeners.size() == 1) {
try {
mService.addListener(mServiceListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
从上面代码可以看到,传递的参数被添加到了mListeners中,并且将mServiceListener添加到EthernetServiceImpl的远程监听接口中去。mServiceListener代码如下:
private final IEthernetServiceListener.Stub mServiceListener =
new IEthernetServiceListener.Stub() {
@Override
public void onAvailabilityChanged(boolean isAvailable) {
mHandler.obtainMessage(
MSG_AVAILABILITY_CHANGED, isAvailable ? 1 : 0, 0, null).sendToTarget();
}
};
若系统检测到以太网状态发生变化,则会通过调用mServiceListener来进行广播通知,接着在mHandler中会循坏便利mListeners列表中的监听对象,凡是注册了监听接口的类都会收到通知消息。
有时候我们需要在不拔出网线的同时关闭上网的功能,这个时候解决方安就是将网口关闭,等到允许上网时再打开网口,接下来就来介绍网口的打开与关闭操作。
查看EthernetNetworkFactory的代码可以发现有这样一个函数和之前一样,我们先到EthernetManager中查看有没有已经做好的功能可以供我们调用。很遗憾的是,EthernetManager并没有实现开关网口的功能。由于EthernetManager不是最终管理以太网的管理类,只是一个提供上层接口的一个中间类,所以要想查看以太网的所以功能,我们应该去查找以太网的管理类EthernetNetworkFactory。
查看EthernetNetworkFactory的代码可以发现有这样一个函数:
/**
* Updates interface state variables.
* Called on link state changes or on startup.
*/
private void updateInterfaceState(String iface, boolean up) {
Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down")+" , mIface : "+mIface);
if (!mIface.equals(iface)) {
return;
}
Log.d(TAG, "updateInterface: " + iface + " link " + (up ? "up" : "down"));
synchronized(this) {
mLinkUp = up;
mNetworkInfo.setIsAvailable(up);
if (!up) {
// Tell the agent we're disconnected. It will call disconnect().
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
stopIpProvisioningThreadLocked();
}
updateAgent();
// set our score lower than any network could go
// so we get dropped. TODO - just unregister the factory
// when link goes down.
mFactory.setScoreFilter(up ? NETWORK_SCORE : -1);
}
}
这正是我们想要的功能,所以这个功能其实也是已经做好的,但是他是private类型的函数,说明Google并不想将这个功能公开出来。函数的第一个参数是想要打开或关闭的网口名称,第二个参数表明是打开还是关闭,true表示打开,false表示关闭。
既然已经有了功能,我们只需要调用即可,具体如何调用,我们可以模仿配置静态IP的方法,从EthernetManager开始,到EthernetNetworkFactory结束,将这个过程做成一个标准的功能。
首先我们在 EthernetManager中添加一个函数updateIface: /*
* up and down eth0
*/
public void updateIface(String iface,boolean up){
try {
mService.updateIfaceState(iface,up);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
此时需要在EthernetServiceImpl中添加函数updateIfaceState:
@Override
public void updateIfaceState(String iface,boolean up){
mTracker.changeEthernetState(iface,up);
}
Override注解说明这是重写函数,EthernetServiceImpl继承自IEthernetManager.Stub,所以我们需要在对应的IEthernetManager.aidl接口文件中加入updateIfaceState声明,如下所示:
// IethernetManager.aidl
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);
}
这里之后会调用mTracker.changeEthernetState函数在EthernetNetworkFactory创建函数changeEthernetState:
public void changeEthernetState(String iface,boolean state){
Log.i(TAG,"changeEthernetState : iface : "+iface+" , state : "+state);
updateInterfaceState(iface,state);
}
到此结束,从EthernetManager到EthernetNetworkFactory中关于网口开关的功能就做完了,我们只需要调用EthernetManager中的updateIface函数就能实现网口的打开与关闭功能。
之前在第四节中介绍过监听网线的插拔功能,第五节中介绍了在不拔网线的情况下打开与关闭网口,这个时候就会遇到一个问题,那就是我无法正确的监听到网线的拔出。实际操作中会发现,打开与关闭网口是,监听器监听同样会被调用,但是此时我并没有拔出网线。换一个说法就是,网口的打开与关闭实际上模拟的就是网线的插拔功能。
那此时我就需要正确区分开网口的关闭与网线的拔出这两种情况。
查询EthernetNetworkFactory代码可以发现有一个内部类可以监听到网线的插入与拔出:
private class InterfaceObserver extends BaseNetworkObserver {
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
updateInterfaceState(iface, up);
}
@Override
public void interfaceAdded(String iface) {
maybeTrackInterface(iface);
}
@Override
public void interfaceRemoved(String iface) {
stopTrackingInterface(iface);
}
}
其中interfaceAdded表示网线的插入,interfaceRemoved表示网线的拔出。为了配合系统的原生代码结构,我们可以在EthernetManager的Listener接口中添加一个新函数声明,添加后的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);
/*
*Called when network wire take out
*/
public void onEthernetIfaceRemove();
}
监听的注册流程重EthernetManager的addListener,到EthernetServiceImpl中的addListener时,已经将其注册到了一个RemoteCallList的列表中,在通过构造mTacker是将监听列表传给了EthernetNetworkFactory。所以我们只需要在EthernetNetworkFactory实现通知网线拔出就可以了。
具体代码如下:private void notifyListenersRemoved(){
int n = mListeners.beginBroadcast();
Log.i("SIMCOMIP","notifyListenersRemoved state listener size : "+n);
for (int i = 0; i < n; i++) {
try {
mListeners.getBroadcastItem(i).onEthernetIfaceRemove();
} catch (RemoteException e) {
// Do nothing here.
}
}
mListeners.finishBroadcast();
}
首先,添加一个通知所有监听者网线拔出的函数,之后我们就可以在前面提到的InterfaceObserver的interfaceRemoved函数中调用一下就可以了:
private class InterfaceObserver extends BaseNetworkObserver {
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
updateInterfaceState(iface, up);
}
@Override
public void interfaceAdded(String iface) {
maybeTrackInterface(iface);
}
@Override
public void interfaceRemoved(String iface) {
stopTrackingInterface(iface);
notifyListenersRemoved();
}
}
上层的SystemUI显示图标及更新是通过NetworkControllerImpl.java文件完成,具体可以自己查看代码,这里不做解析了。
文章浏览阅读565次。本文主要介绍如何使用C#通过OPC方式连接PLC,并提供了相应的程序和学习资料,以便读者学习和使用。OPC服务器是一种软件,可以将PLC的数据转换为标准的OPC格式,允许其他软件通过标准接口读取或控制PLC的数据。此外,本文还提供了一些学习资料,包括OPC和PLC的基础知识,C#编程语言的教程和实例代码。这些资料可以帮助读者更好地理解和应用本文介绍的程序。1.该程序是通讯方式是CSharp通过OPC方式连接PLC,用这种方式连PLC不用考虑什么种类PLC,只要OPC服务器里有的PLC都可以连。_c#opc通信
文章浏览阅读1.6w次,点赞3次,收藏10次。实践环境物理机:Windows10教育版,操作系统版本 17763.914虚拟机:Ubuntu18.04.3桌面版在Hyper-V中的刚安装好Ubuntu虚拟机之后,会发现鼠标滑动很不顺畅,也不能向虚拟机中拖拽文件或者复制内容。在VMware中,可以通过安装VMware tools来使物理机和虚拟机之间达到更好的交互。在Hyper-V中,也有这样的工具。这款工具可以完成更好的鼠标交互,我的..._win10 hyper-v ubuntu18.04 文件拷贝
文章浏览阅读156次。前言互联网时代,瞬息万变。一个小小的走错,就有可能落后于别人。我们没办法去预测任何行业、任何职业未来十年会怎么样,因为未来谁都不能确定。只能说只要有互联网存在,程序员依然是个高薪热门行业。只要跟随着时代的脚步,学习新的知识。程序员是不可能会消失的,或者说不可能会没钱赚的。我们经常可以听到很多人说,程序员是一个吃青春饭的行当。因为大多数人认为这是一个需要高强度脑力劳动的工种,而30岁、40岁,甚至50岁的程序员身体机能逐渐弱化,家庭琐事缠身,已经不能再进行这样高强度的工作了。那么,这样的说法是对的么?_类初始化一个静态属性 为线程池
文章浏览阅读1w次,点赞13次,收藏43次。说来也是惭愧,一直以来,在装环境的时候都会从官网下载Maven。然后再在idea里配置Maven。以为从官网下载的Maven是必须的步骤,直到今天才得知,idea有捆绑的 Maven 我们只需要搞一个配置文件就行了无需再官网下载Maven包以后再在新电脑装环境的时候,只需要下载idea ,网上找一个Maven的配置文件 放到 默认的 包下面就可以了!也省得每次创建项目都要重新配一次Maven了。如果不想每次新建项目都要重新配置Maven,一种方法就是使用默认的配置,另一种方法就是配置 .._安装idea后是不是不需要安装maven了?
文章浏览阅读45次。家是我们一生中最重要的地方,小时候,我们在这里哭、在这里笑、在这里学习走路,在这里有我们最真实的时光,用相机把它记下吧。 很多家庭在拍摄孩子时有一个看法,认为儿童摄影团购必须是在风景秀丽的户外,即便是室内那也是像大酒店一样...
文章浏览阅读429次。Dockerfile介绍Dockerfile是构建镜像的指令文件,由一组指令组成,文件中每条指令对应linux中一条命令,在执行构建Docker镜像时,将读取Dockerfile中的指令,根据指令来操作生成指定Docker镜像。Dockerfile结构:主要由基础镜像信息、维护者信息、镜像操作指令、容器启动时执行指令。每行支持一条指令,每条指令可以携带多个参数。注释可以使用#开头。指令说明FROM 镜像 : 指定新的镜像所基于的镜像MAINTAINER 名字 : 说明新镜像的维护(制作)人,留下_rocker/r-base镜像
文章浏览阅读223次。该系统将提供便捷的信息发布、物业报修、社区互动等功能,为小区居民提供更加便利、高效的服务。引言: 随着城市化进程的加速,小区管理成为一个日益重要的任务。因此,设计一个基于微信小程序的小区管理系统成为了一项具有挑战性和重要性的毕设课题。本文将介绍该小区管理系统的设计思路和功能,以期为小区提供更便捷、高效的管理手段。四、总结与展望: 通过本次毕设项目,我们实现了一个基于微信小程序的小区管理系统,为小区居民提供了更加便捷、高效的服务。通过该系统的设计与实现,能够提高小区管理水平,提供更好的居住环境和服务。_ssm基于微信小程序的公寓生活管理系统
文章浏览阅读635次。文章来源i春秋入坑Ubuntu半年多了记得一开始学的时候基本一星期重装三四次=-= 尴尬了 觉得自己差不多可以的时候 就吧Windows10干掉了 c盘装Ubuntu 专心学习. 这里主要来说一下使用Ubuntu的正确姿势Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的开源GNU/Linux操作系统,Ubuntu 是基于DebianGNU/Linux,支..._ubuntu安装攻击工具包
文章浏览阅读335次。需求:C++中将BYTE型数组传递给Java中,考虑到内存释放问题,未采用通过返回值进行数据传递。public class demoClass{public native boolean getData(byte[] tempData);}JNIEXPORT jboolean JNICALL Java_com_core_getData(JNIEnv *env, jobject thisObj, jbyteArray tempData){ //resultsize为s..._jni引用byte[]
文章浏览阅读2.1k次,点赞5次,收藏30次。本教程代码开源:GitHub 欢迎star文章目录一、平面模型分割1. 代码2. 说明3. 运行二、圆柱模型分割1. 代码2. 说明3. 运行三、欧几里得聚类提取1. 代码2. 说明3. 运行四、区域生长分割1. 代码2. 说明3. 运行五、基于最小切割的分割1. 代码2. 说明3. 运行六、使用 ProgressiveMorphologicalFilter 分割地面1. 代码2. 说明3. 运行一、平面模型分割在本教程中,我们将学习如何对一组点进行简单的平面分割,即找到支持平面模型的点云中的所有._pclpy.pcl.pointcloud.pointxyzi转为numpy
文章浏览阅读141次。一 其实在 skyeye 上移植 arm-linux 并非难事,网上也有不少资料, 只是大都遗漏细节, 以致细微之处卡壳,所以本文力求详实清析, 希望能对大家有点用处。本文旨在将 arm-linux 在 skyeye 上搭建起来,并在 arm-linux 上能成功 mount NFS 为目标, 最终我们能在 arm-linux 里运行我们自己的应用程序. 二 安装 Sky..._nfs启动 arm
文章浏览阅读598次,点赞2次,收藏5次。00为了形成一个体系,想将前面学过的一些东西都拉来放在一起总结总结,方便学习,方便记忆。攻防世界 Pwn 新手攻防世界 Pwn 进阶 第一页01 4-ReeHY-main-100超详细的wp1超详细的wp203 format2栈迁移的两种作用之一:栈溢出太小,进行栈迁移从而能够写入更多shellcode,进行更多操作。栈迁移一篇搞定有个陌生的函数。C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 _pwn snprintf