Android以太网框架情景分析之NetworkFactory与NetworkAgent深入分析
前言
在前面的章节中Android以太网框架情景分析之启动简介我们介绍了以太网框架的整体启动流程,然后说到了EthernetNetworkFactory类是Ethernet的核心管理类,几乎包含了Ethernet所有的网络管理操作,这其中就包括各种网络的注册网络以及和ConnectifyService服务的交互(主要是通知网络状态的变化,评分机制的变化)。那么本章节将会重点来讲解EthernetNetworkFactory,而这其中的重重之重是NetworkFactory与NetworkAgent和ConnectifyService的通信机制的建立。
本篇章演示的源码是在Android 7.1 msm8953平台上,其中涉及的源码路径如下所示:
frameworks/base/services/java/com/android/server/
---ConnectivityService.java
---INativeDaemonConnectorCallbacks.java
---NativeDaemonConnector.java
---NetworkManagementService.java
---SystemServer.java
---SystemServiceManager.java
frameworks/base/core/java/android/net/
---ConnectivityManager.java
---EthernetManager.java
---IConnectivityManager.aidl
---IEthernetManager.aidl
---LinkProperties.java
---NetworkPolicy.java
---NetworkAgent.java
---NetworkFactory.java
---NetworkInfo.java
---ProxyInfo.java
frameworks/opt/net/ethernet/java/com/android/server/ethernet/
---EthernetConfigStore.java
---EthernetNetworkFactory.java
---EthernetServiceImpl.java
---EthernetService.java
在正式开始本篇的介绍前,还是附上祖传的以太网框架相关类调用关系图,这样能让大伙先在整体流程上有个概括,这样在分析代码走神的时候还可以回来看看,自己到那里了,将要到那里去。
一. EthernetNetworkFactory的初始化
在前面我们扯了一大推的理论知识和前期准备,从现在开始就是开始真正的源码分析了,在前面的篇章中我们分析到了EthernetNetworkFactory,那么我们就接着上篇继续分析了,开干,各位小伙伴们系好安全带,开始发车了。
1.1 EthernetNetworkFactory.EthernetNetworkFactory
分析Java中的一个类,一般从构造方法开始,这里我们也不能免俗也从构造方法开始,这里的该构造函数比较简单,主要就是初始化一些成员变量信息,这里不是我们重点关注的。
class EthernetNetworkFactory {
EthernetNetworkFactory(RemoteCallbackList<IEthernetServiceListener> listeners) {
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORK_TYPE, "");//设置网络基本信息,如网络类型,网络别名
mLinkProperties = new LinkProperties();
initNetworkCapabilities();
mListeners = listeners;
}
}
}
1.2 EthernetNetworkFactory.start
class EthernetNetworkFactory {
/**
* 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);//获取NetworkManagementService服务端代理,在前期知识储备中我们有讲过NetworkManagementService主要用于和Netd通信
mEthernetManager = (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE);//获取EthernetManager服务端代理
// 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();//向ConnectivityService注册自己
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|IllegalStateException e) {
Log.e(TAG, "Could not get list of interfaces " + e);
}
}
这里可以看到start方法接收一个Handler对象作为参数,对于该参数我们回溯一下是从EthernetServiceImpl.start()中传入的,它新建了一个HandlerThread对象,并传入作为参数,这个Handler对象很重要,因为它会有多重的传递,串联起了以太网的网络管理。并且在代码中创建了一个LocalNetworkFactory对象,而我们的LocalNetworkFactory继承自NetworkFactory。
二. NetworkFactory网络工厂类详解及注册流程分析
我们知道各个具有网络连接的对象(WIFI,BT,PHONE)都需要向ConnectivityService注册自己,并把自己所提供的网络的分值告诉ConnectivityService。而Android为了ConnectivityService便于统一管理,每一个具备提供网络服务的对象都需要创建一个NetworkFactory的子类对象,并利用该对象注册自己,以及提供自己的分值。并且我们可以通过搜索发现确实WIFI/BT/PHONE/ETHERNET都有实现了NetworkFactory工厂类。如下所示,而我们的LocalNetworkFactory的也在此行列中,那先让我们看看NetworkFactory究竟是如何定义的。
XXX/frameworks$ grep -nr "extends NetworkFactory" ./
./base/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java:430: private static class MockNetworkFactory extends NetworkFactory {
./opt/telephony/src/java/com/android/internal/telephony/PhoneSwitcher.java:222: private static class PhoneSwitcherNetworkRequestListener extends NetworkFactory {
./opt/telephony/src/java/com/android/internal/telephony/dataconnection/TelephonyNetworkFactory.java:42:public class TelephonyNetworkFactory extends NetworkFactory {
./opt/net/ethernet/java/com/android/server/ethernet/EthernetNetworkFactory.java:127: private class LocalNetworkFactory extends NetworkFactor {
./opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java:4083: private class WifiNetworkFactory extends NetworkFactory {
./opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java:4116: private class UntrustedWifiNetworkFactory extends NetworkFactory {
XXX/packages$ grep -nr "extends NetworkFactory" ./
./apps/Bluetooth/src/com/android/bluetooth/pan/BluetoothTetheringNetworkFactory.java:49:public class BluetoothTetheringNetworkFactory extends NetworkFactory {
NetworkFactory网络注册的时序图如下所示:
2.1 NetworkFactory类简介
好吗,前面空洞的介绍了NetworkFactory的功能,下面得实打实的来点干货分析分析了,不然没有料不是!
/**
* A NetworkFactory is an entity that creates NetworkAgent objects.
* The bearers register with ConnectivityService using {@link #register} and
* their factory will start receiving scored NetworkRequests. NetworkRequests
* can be filtered 3 ways: by NetworkCapabilities, by score and more complexly by
* overridden function. All of these can be dynamic - changing NetworkCapabilities
* or score forces re-evaluation of all current requests.
*
* If any requests pass the filter some overrideable functions will be called.
* If the bearer only cares about very simple start/stopNetwork callbacks, those
* functions can be overridden. If the bearer needs more interaction, it can
* override addNetworkRequest and removeNetworkRequest which will give it each
* request that passes their current filters.
* @hide
**/
public class NetworkFactory extends Handler {
public NetworkFactory(Looper looper, Context context, String logTag,
NetworkCapabilities filter) {
super(looper);
LOG_TAG = logTag;
mContext = context;
mCapabilityFilter = filter;
}
//将当前网络注册到ConnectivityService
public void register() {
if (DBG) log("Registering NetworkFactory");
if (mMessenger == null) {
mMessenger = new Messenger(this);
ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG);
}
}
//处理网络请求,用于打开或者释放当前连接,一般在有新的网络接入时会触发此处
protected void handleAddRequest(NetworkRequest request, int score) {
NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
if (n == null) {
if (DBG) log("got request " + request + " with score " + score);
n = new NetworkRequestInfo(request, score);
mNetworkRequests.put(n.request.requestId, n);
} else {
if (VDBG) log("new score " + score + " for exisiting request " + request);
n.score = score;
}
if (VDBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter);
evalRequest(n);
}
//被子类实现
protected void startNetwork() { }
protected void stopNetwork() { }
//更新当前网络的分值
public void setScoreFilter(int score) {
sendMessage(obtainMessage(CMD_SET_SCORE, score, 0));
}
}
可以看到,这里的NetworkFactory 可以分为如下三个知识点:
- 这里我们可以看到NetworkFactory 继承于Handler,从该类的字面理解我们可以把它当作一个工厂类,对相关网络的请求和终止操作都通过该对象进行处理。并且从注释可知NetworkFactory与NetworkAgent对象有密切关系,至于为什么说它们有密切的关系,这个先不点破,大伙可以跟着代码分析来自行体会。
- 而且可知NetworkFactory会通过register()方法向ConnectivityService进行注册。
- 其中它的方法start/stopNetwork()是提供的回调函数 ,我们可以通过这两个函数来请求或终止网络:
2.2 NetworkFactory注册到ConnectivityService服务
既然这里提到了注册,通常注册到目的一般是想通过注册到目的端,然后当目的端的状态发生变化的时候回调发起端的相关方法或者接口,而我们这里的注册也不能免俗!通过后面的分析你会分析原来也是依照这个套路来进行的。
//NetworkFactory.java
public void register() {
if (DBG) log("Registering NetworkFactory");
if (mMessenger == null) {
mMessenger = new Messenger(this);//注意这里的是Messenger,其参数为NetworkFactory本身,且Messenger是可序列的所以可以跨进程Binder传递
ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG);//
}
}
在正式开始分析前,有必要认识一下Messenger类,这个可不是Message,不是,不是!如果小伙伴们对Messenger和AsyncChannel不熟悉,强烈建议小伙伴们抽出一点时间参见AsyncChannel原理分析以及实操演练的篇章,里面有具体讲解了Messenger和AsyncChannle,如果不搞懂上述两个内容,后续的相关知识点小伙伴可能会一头雾水了!这里我们可以用一句话来概括Messenger主要是为了实现AsyncChannel通信而封装的一个类,而AsyncChannle又是为了实现Handler跨进程或者相同进程通信而封装的一个类。
而我们在前面的篇章知道,我们在创建LocalNetworkFactory对象时,我们给它指定了一个特定线程的Looper对象,而这个Looper得最终来源是EthernetServiceImpl的start方法,如下所示:
//EthernetServiceImpl.java
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);
}
结合前面的两点,我们再来捋一捋!这里我们的registerNetworkFactory方法参数Messenger对象是一个可以跨进程传递的实例对象,你可以认为它代表一个Handler对象,既然是Handler对象,那么我们可以像使用Handler对象一样,使用Messenger对象来发送消息了,而此处该引用指向mNetworkFactory这个Handler对象,它绑定的Looper属于一个特定的线程,它在EthernetServiceImpl中创建。
然后们接着继续分析registerNetworkFactory方法,如下所示:
//ConnectivityManager.java
public static ConnectivityManager from(Context context) {
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
public void registerNetworkFactory(Messenger messenger, String name) {
try {
mService.registerNetworkFactory(messenger, name);//详见章节2.3
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
一看就是老套路,获取CONNECTIVITY_SERVICE服务,这个服务就是我们的ConnectivityService服务,然后通过Binder调用ConnectivityService服务的registerNetworkFactory方法,对于Binder不熟悉的小伙伴们可以看看Android Binder指南让你对Android的Binder有一个比较深入的认识和了解。
2.3 ConnectivityService.registerNetworkFactory
历经重重险阻,万水千山的终于来到了ConnectivityService的世界,让我们紧跟源码继续分析!
//ConnectivityService.java
private static class NetworkFactoryInfo {
public final String name;
public final Messenger messenger;
public final AsyncChannel asyncChannel;
public NetworkFactoryInfo(String name, Messenger messenger, AsyncChannel asyncChannel) {
this.name = name;//要注册的网络名称,譬如Ethernet,wifi,bt等
this.messenger = messenger;//
this.asyncChannel = asyncChannel;
}
}
public void registerNetworkFactory(Messenger messenger, String name) {
enforceConnectivityInternalPermission();//权限检测忽略
NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel());//内部类,对相关的信息进行封装
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi));//InternalHandler对象用于处理内部事务
}
这里的NetworkFactoryInfo 是一个ConnectivityService的内部类,用来封装一些变量信息,而我们这里用它保存了将要注册的网络信息名称(Ethernet)以及特定的Messenger对象messenger,并且将新创建的匿名AsyncChannel通道类对象也保存到其中了。
接着通过InternalHandler对象mHandler向自己发送了一条信息EVENT_REGISTER_NETWORK_FACTORY并且携带了NetworkFactoryInfo相关信息,这里的InternalHandler是ConnectivityService的内部Handler主要用于其内部一些事务的处理流程,我们直接直捣黄龙看其对EVENT_REGISTER_NETWORK_FACTORY的处理流程,如下所示:
//ConnectivityService.java
private class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
......
case EVENT_REGISTER_NETWORK_FACTORY: {
handleRegisterNetworkFactory((NetworkFactoryInfo)msg.obj);//详见章节2.4
break;
......
}
}
}
}
2.4 ConnectivityService.handleRegisterNetworkFactory
//ConnectivityService.java
private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) {
if (DBG) log("Got NetworkFactory Messenger for " + nfi.name);
mNetworkFactoryInfos.put(nfi.messenger, nfi);
nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger);//是不是有种似曾识相的感觉
}
这里的mNetworkFactoryInfos是一个HashMap对象,在此处将前面创建的NetworkFactoryInfo对象nfi的参数Messenger位key,以NetworkFactoryInfo对象nfi为value添加到mNetworkFactoryInfos中。接着调用前面创建的匿名AsyncChannel对象建立连接,对于这个知识点还是建议大伙抽出一定的时间参阅博客AsyncChannel原理分析以及实操演练,这里我还是简单的分析下调用connect的后续流程,如下所示:
//AsyncChannel.java
public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
connected(srcContext, srcHandler, dstMessenger);
replyHalfConnected(STATUS_SUCCESSFUL);
}
public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
mSrcContext = srcContext;
mSrcHandler = srcHandler;
mSrcMessenger = new Messenger(mSrcHandler);
mDstMessenger = dstMessenger;
linkToDeathMonitor();
}
private void replyHalfConnected(int status) {
Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
msg.arg1 = status;
msg.obj = this;
msg.replyTo = mDstMessenger;
if (!linkToDeathMonitor()) {
msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
}
mSrcHandler.sendMessage(msg);
}
通过上面的connect流程简要分析,我们可以得到如下两点结论:
-
这里通过调用connect方法带入参数,对AsyncChannel中的变量进行相关的初始化,初始化之后mSrcHandler指向了ConnectivityService中的NetworkStateTrackerHandler对象mTrackerHandler,它主要负责接收网络模块发送的消息,并进行网络更新;并且这里的mSrcMessenger表示了对该Handler对象的一个引用;最后的mDstMessenger 指向了EthernetNetworkFactory中的LocalNetworkFactory对象,该对象也是一个Handler。在这里要特别关注Handler和Messenger的来源和出处,不是说自古英雄不问出处吗,看来有时候古人还是欺我啊。
-
最后通过mSrcHandler发送了CMD_CHANNEL_HALF_CONNECTED的相关指令,将会在ConnectivityService中对象中的mTrackerHandler被处理,处理逻辑如下:
//ConnectivityService.java
private class NetworkStateTrackerHandler extends Handler {
public NetworkStateTrackerHandler(Looper looper) {
super(looper);
}
private boolean maybeHandleAsyncChannelMessage(Message msg) {
switch (msg.what) {
default:
return false;
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
handleAsyncChannelHalfConnect(msg);//此处处理,详见章节2.5
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai != null) nai.asyncChannel.disconnect();
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
handleAsyncChannelDisconnected(msg);
break;
}
}
return true;
}
2.5 ConnectivityService.handleAsyncChannelHalfConnect
//ConnectivityService.java
private void handleAsyncChannelHalfConnect(Message msg) {
AsyncChannel ac = (AsyncChannel) msg.obj;
if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {//又见到老熟人了mNetworkFactoryInfos
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkFactory connected");
// A network factory has connected. Send it all current NetworkRequests.
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (nri.request.isListen()) continue;
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);//详见章节2.6
}
} else {
loge("Error connecting NetworkFactory");
mNetworkFactoryInfos.remove(msg.obj);
}
} else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {//NetworkAgent注册的时候会走这个分支
......
}
}
还记得大明湖畔的夏雨荷吗!错了,还记得2.4章节的mNetworkFactoryInfos吗,在该章节里面我们会将NetworkFactoryInfo添加到其中,而此时我们会从其中取出来进行匹配,既然我们已经添加了当然能匹配到了,所以最终会调用到如下的逻辑流程:
AsyncChannel ac = (AsyncChannel) msg.obj;
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
if (nri.request.isListen()) continue;
NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK,
(nai != null ? nai.getCurrentScore() : 0), 0, nri.request);//详见章节2.6
}
这里最终会调用到AsyncChannel对象ac的mDstMessenger发送CMD_REQUEST_NETWORK消息,而通过前面的分析我们知道mDstMessenger是指向了EthernetNetworkFactory中的LocalNetworkFactory,从字面理解该请求可以理解为向LocalNetworkFactory请求一个网络, 而此时还处于初始化阶段还没有为以太网创建NetworkRequest请求,所以mNetworkForRequestId.get取出的内容为空,从而sendMessage发送的第二个参数arg1为为0这个很重要。
2.6 NetworkFactory.handleAddRequest
我找遍LocalNetworkFactory也没有看到Handler的处理消息的方法handleMessage,好吗它的处理方法在其父类LocalNetworkFactory中实现的,那我们就来看看它是怎么处理CMD_REQUEST_NETWORK消息的:
//NetworkFactory.java
public void handleMessage(Message msg) {
switch (msg.what) {
case CMD_REQUEST_NETWORK: {
handleAddRequest((NetworkRequest)msg.obj, msg.arg1);//处理请求
break;
}
case CMD_CANCEL_REQUEST: {
handleRemoveRequest((NetworkRequest) msg.obj);
break;
}
case CMD_SET_SCORE: {
handleSetScore(msg.arg1);
break;
}
case CMD_SET_FILTER: {
handleSetFilter((NetworkCapabilities) msg.obj);
break;
}
}
}
protected void handleAddRequest(NetworkRequest request, int score) {
NetworkRequestInfo n = mNetworkRequests.get(request.requestId);
if (n == null) {
if (DBG) log("got request " + request + " with score " + score);
n = new NetworkRequestInfo(request, score);
mNetworkRequests.put(n.request.requestId, n);
} else {
if (VDBG) log("new score " + score + " for exisiting request " + request);
n.score = score;
}
if (VDBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter);
evalRequest(n);//详见章节2.7
}
在这里由于是第一次接收到CMD_REQUEST_NETWORK的请求,因此NetworkRequestInfo 对象n为null,所以将会在NetworkFactory中创建NetworkRequestInfo的对象并且将其存储到列表mNetworkRequests中供后续使用,然后调用方法evalRequest进入网络评价过程。
2.7 NetworkFactory.evalRequest
//NetworkFactory.java
private void evalRequest(NetworkRequestInfo n) {
if (VDBG) log("evalRequest");
if (n.requested == false && n.score < mScore &&
n.request.networkCapabilities.satisfiedByNetworkCapabilities(
mCapabilityFilter) && acceptRequest(n.request, n.score)) {
if (VDBG) log(" needNetworkFor");
needNetworkFor(n.request, n.score);//详见章节2.8
n.requested = true;
} else if (n.requested == true &&
(n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(
mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {
if (VDBG) log(" releaseNetworkFor");
releaseNetworkFor(n.request);
n.requested = false;
} else {
if (VDBG) log(" done");
}
}
该逻辑就是整个网络注册系统最关键的地方,其逻辑分为如下两条线路:
- 如果NetworkRequestInfo没有被requested过,并且其分值(n.score)小于当前NetworkFactory自己的分值(mScore),那么就说明,当前NetworkFactory所处的网络优先级高于其他网络的优先级,就会触发当前NetworkFactory所在网络的needNetworkFor()流程,也就是连接建立流程,并将标记NetworkRequestInfo.requested=true。
- 当NetworkRequestInfo被requested过(也就是当前网络被needNetworkFor过),此时如果再次收到请求,并且携带的新score大于当前NetworkFactory所处网络的mScore,那么就说明当前NetworkFactory所在网络优先级已经不是最高,需要将其releaseNetworkFor掉,并标记NetworkRequestInfo.requested=false。
对于初始化流程来说,由于NetworkRequestInfo是刚才在handleAddRequest新创建的,所以其requested状态必然为false,而且我们前面提到,ConnectivityService发送CMD_REQUEST_NETWORK时携带的分值参数为0,并且对于以太网网络来说,其mScore=70(至于这个70在什么地方赋值的,这个先忽略,后续的篇章中会讲到此处),因此此时的判定状态将会是:n.requested=false 且n.score < mScore,那么在这种环境下,对于以太网网络环境初始化过程来说,将会满足第一个if判断,进入needNetworkFor流程,也就是触发以太网网络的建立。
这里插播一个知识点,假如客户提出网络共存需求,即有客户提出,也就是机器中所有的物理传输介质(WiFi、Mobil、Ethernet)都必须可以同时可以进行网络通信工作,不能互斥。通过设置不同主机IP地址使用不同的网络类型,设置规则发送给Netd,而实现需求。而在这里我们可见,同一时间只能存在一种网络通信方式,即优先级。也就是WiFi、Mobil、以太网他们之间是互斥的,同一时间只有一种通信方式在线提供服务。那要怎么操作呢?
其实这里就涉及到了evalRequest的评分机制了,要想WiFi、Mobil、Ethernet网络共存,我们可以通过修改评分回调的逻辑实现共存,也就是当进入评分互斥的逻辑时不调用网络释放接口,就能实现网络共存不互斥的需求。也就是通过控制传入的score值大小,可以是设置为40,最终走到needNetworkFor的这个逻辑中去就能实现网络共存。
2.8 NetworkFactory.evalRequest
//NetworkFactory.java
protected void needNetworkFor(NetworkRequest networkRequest, int score) {
if (++mRefCount == 1) startNetwork();
}
//EthernetNetworkFactory.java
private class LocalNetworkFactory extends NetworkFactory {
protected void startNetwork() {
onRequestNetwork();//详见章节2.9
}
}
兜兜转转最后走到了EthernetNetworkFactory中的onRequestNetwork来处理Ethernet的连接操作,包括对静态IP和DHCP的处理。到这里onRequestNetwork处理完成,一个NetworkFactory对象的注册过程就结束了。
2.9 EthernetNetworkFactory.onRequestNetwork
//EthernetNetworkFactory.java
public void onRequestNetwork() {
synchronized(EthernetNetworkFactory.this) {
if (mIpProvisioningThread != null) {
return;
}
}
final Thread ipProvisioningThread = new Thread(new Runnable() {
public void run() {
if (DBG) {
Log.d(TAG, String.format("starting ipProvisioningThread(%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.
mIpProvisioningThread = null;
return;
}
if (PAX_ETHERNET) {
StaticStatus = true;
}
linkProperties = config.getStaticIpConfiguration().toLinkProperties(mIface);
if (config.getProxySettings() == ProxySettings.STATIC ||
config.getProxySettings() == ProxySettings.PAC) {
linkProperties.setHttpProxy(config.getHttpProxy());
}
} else {
mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
WaitForProvisioningCallback ipmCallback = new WaitForProvisioningCallback() {
@Override
public void onLinkPropertiesChange(LinkProperties newLp) {
synchronized(EthernetNetworkFactory.this) {
if (mNetworkAgent != null && mNetworkInfo.isConnected()) {
mLinkProperties = newLp;
mNetworkAgent.sendLinkProperties(newLp);
}
}
}
};
synchronized(EthernetNetworkFactory.this) {
stopIpManagerLocked();
mIpManager = new IpManager(mContext, mIface, ipmCallback);
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);
}
final ProvisioningConfiguration provisioningConfiguration =
mIpManager.buildProvisioningConfiguration()
.withProvisioningTimeoutMs(0)
.build();
mIpManager.startProvisioning(provisioningConfiguration);
}
linkProperties = ipmCallback.waitForProvisioning();
if (linkProperties == null ) {
Log.e(TAG, "IP provisioning error");
// set our score lower than any network could go
// so we get dropped.
mFactory.setScoreFilter(-1);
synchronized(EthernetNetworkFactory.this) {
stopIpManagerLocked();
}
mIpProvisioningThread = null;
return;
}
}
synchronized(EthernetNetworkFactory.this) {
if (mNetworkAgent != null) {
Log.e(TAG, "Already have a NetworkAgent - aborting new request");
stopIpManagerLocked();
mIpProvisioningThread = null;
return;
}
mLinkProperties = linkProperties;
mNetworkInfo.setIsAvailable(true);
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
// Create our NetworkAgent.
mNetworkAgent = new NetworkAgent(mFactory.getLooper(), mContext,
NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties,
NETWORK_SCORE) {
public void unwanted() {
synchronized(EthernetNetworkFactory.this) {
if (this == mNetworkAgent) {
stopIpManagerLocked();
mLinkProperties.clear();
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null,
mHwAddr);
updateAgent();
mNetworkAgent = null;
try {
mNMService.clearInterfaceAddresses(mIface);
} catch (Exception e) {
Log.e(TAG, "Failed to clear addresses or disable ipv6" + e);
}
} else {
Log.d(TAG, "Ignoring unwanted as we have a more modern " +
"instance");
}
}
};
};
mIpProvisioningThread = null;
}
if (DBG) {
Log.d(TAG, String.format("exiting ipProvisioningThread(%s): mNetworkInfo=%s",
mIface, mNetworkInfo));
}
}
});
synchronized(EthernetNetworkFactory.this) {
if (mIpProvisioningThread == null) {
mIpProvisioningThread = ipProvisioningThread;
mIpProvisioningThread.start();
}
}
}
尼玛这个方法的逻辑代码也太多了,我们牛逼的谷歌工程师就不知道拆分拆分吗,这一头看不都尾巴的代码,这个各位在分析的时候可以不要硬啃,我们这里只用关心NetworkAgent。通过前面的分析我们可知NetworkFactory将自己注册到ConnectivityService,然后在EthernetNetworkFactory中快速响应ConnectivityService的网络请求,但是这种通道请求时单向的,只能由ConnectivityService流向NetworkFactory而不能反过来。那此时就一个问题了假如我们的EthernetNetworkFactory需要和ConnectivityService进行双向通信呢,这就轮到了我们的NetworkAgent上场了。
三. NetworkAgent网络代理类详解及注册流程分析
通过我们第二章节的分析可知NetworkFactory可以被看做是ConnectivityService向链路网络(以太网,蓝牙,WIFI)请求的统一接口.,那么NetworkAgent网络代理则可以认为是是ConnectivityService和链路网络管理者(如EthernetNetworkFactory)之间的双向信使,在NetworkFactory和ConnectivityService建立连接并调用onRequestNetwork之后通过NetworkAgent,EthernetNetworkFactory可以向ConnectivityService执行如下操作:
- 更新网络状态 NetworkInfo(断开、连接中、已连接等)
- 更新链路配置 LinkProperties(本机网口、IP、DNS、路由信息等)
- 更新网络能力 NetworkCapabilities(信号强度、是否收费等)
ConnectivityService可以向EthernetNetworkFactory执行如下操作,但是遗憾的是EthernetNetworkFactory都放任没有处理:
- 更新网络有效性(即NetworkMonitor的网络检测结果)
- 禁止自动连接
- 由于网络不可上网等原因主动断开网络
到这里我们应该知道了,NetworkAgent提供了ConnectivityService和EthernetNetworkFactory之间双向通信的能力。原理类似NetworkFactory,也是使用了AsyncChannel和Messenger,其时序图如下红色框标记所示:
3.1 NetWorkFactory和NetworkAgent之间的关系究竟是啥
还记得在章节2.1的时候留下的一个小小疑问点吗!我们说NetWorkFactory和NetworkAgent之间存在着紧密的联系,那这个紧密联系是什么呢!通过从前面的代码分析我们可以知道NetworkAgent是被NetworkFactory创建的(通过前面的时序图也可以看到),这里的创建并不是说在NetworkFactory内部创建NetworkAgent,而是说,在NetworkFactory这个环境就绪之后,网络提供者才可以创建NetworkAgent。并且在一个NetworkFactory中可以创建不同的NetworkAgent,他们拥有不同的Capabilities等参数而他们之间还有一个区别就是NetworkFactory是在系统初始化时就被创建,而NetworkAgent是在真正接入网络时才会创建。
我们可以用运营商之间的关系来比喻他们的关系。
NetworkFactory相当于不同的运营商,比如中国电信、铁通、移动,他们具备联通互联网的能力,当用户入网时就决定了自己的运营商(即完成NetworkFactory初始化)。但同时在每个运营商内部又创建各个不同的接入点,比如对于中国电信来说,还分为上海电信、河北电信等,只有当用户打开电脑真正上网的时候,才会被分配具体的接入点(即完成NetworkAgent初始化)。
也就是说,同一个NetworkFactory可以在不同的时刻根据需要创建不同的NetworkAgent,比如使用数据上网时,会根据当前的需要(发送MMS还是IMS,或者单纯上网)来创建不同参数的NetworkAgent(不同的APN参数)对象,然后将其注册到ConnectivityService中,而同理在以太网络环境中也是如此可能当前联网是DHCP动态获取或者静态设置IP或者是以太网络代码,也会来创建不同参数的NetworkAgent然后注册到ConnectivityService中。
3.2 NetworkAgent类简介
前面从理论阶段介绍了NetworkAgent的基本情况,现在得现场相亲熟悉熟悉了,媒婆介绍得吹得再好还是得现场感受下不!下面得上点干货来点实际的东西了。翠花上酸菜!
public abstract class NetworkAgent extends Handler {
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
super(looper);
LOG_TAG = logTag;
mContext = context;
if (ni == null || nc == null || lp == null) {
throw new IllegalArgumentException();
}
if (VDBG) log("Registering NetworkAgent");
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);//注册到ConnectivityService中
}
@Override
public void handleMessage(Message msg) {
}
abstract protected void unwanted();//虚方法
}
这里NetworkAgent 的是Hnadler的一个子类,通常Handler吗大多情况是用来处理消息的这里也不例外。而它的unwanted()是一个虚方法,必须在子类中被实现,而在章节二的后面我们在初始化NetworkAgent 的时候实现了它,当ConnectivityService不再需要当前网络连接时调用它(看来ConnectivityService也是个负心汉啊)。除了清除掉网络配置信息,还会把mNetworkAgent置为null。从代码中可以看出mNetworkAgent是否为null标志了当前是否该类型的网络连接正在使用,如果有则不会处理新的网络请求。
//EthernetNetworkFactory.java
mNetworkAgent = new NetworkAgent(mFactory.getLooper(), mContext,
NETWORK_TYPE, mNetworkInfo, mNetworkCapabilities, mLinkProperties,
NETWORK_SCORE) {
public void unwanted() {
synchronized(EthernetNetworkFactory.this) {
if (this == mNetworkAgent) {
stopIpManagerLocked();
mLinkProperties.clear();
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null,
mHwAddr);
updateAgent();
mNetworkAgent = null;
try {
mNMService.clearInterfaceAddresses(mIface);
} catch (Exception e) {
Log.e(TAG, "Failed to clear addresses or disable ipv6" + e);
}
} else {
Log.d(TAG, "Ignoring unwanted as we have a more modern " +
"instance");
}
}
};
};
3.3 NetworkAgent注册到ConnectivityService
前面我们在口口声声的说NetworkAgent注册到ConnectivityService中,可是却怎么也找不到和NetworkFactory类似的register的方法,好吗NetworkAgent没有专门提供注册接口,而是直接在构造方法中注册了,具体逻辑如下:
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
super(looper);
LOG_TAG = logTag;
mContext = context;
if (ni == null || nc == null || lp == null) {
throw new IllegalArgumentException();
}
if (VDBG) log("Registering NetworkAgent");
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);
}
这个注册方法还是老套路和前面NetworkFactory的注册类似,其中的第一个参数是Messenge对象,这个也是实现双向通信的关键!这里最终通过Binder调用到了ConnectivityService中。
3.4 ConnectivityService.registerNetworkAgent
历经重重险阻,万水千山的终于来到了ConnectivityService的世界,让我们紧跟源码继续分析!
public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
int currentScore, NetworkMisc networkMisc) {
enforceConnectivityInternalPermission();
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest.
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new Network(reserveNetId()), new NetworkInfo(networkInfo), new LinkProperties(
linkProperties), new NetworkCapabilities(networkCapabilities), currentScore,
mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);//通过传递过来的参数,初始化NetworkAgentInfo对象nai
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
addValidationLogs(nai.networkMonitor.getValidationLogs(), nai.network,
networkInfo.getExtraInfo());
if (DBG) log("registerNetworkAgent " + nai);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
return nai.network.netId;
}
这里的传入的参数Messenger对象messenger是前面NetworkAgent的引用,这个也是实现NetworkAgent和ConnectivityService双向跨进程通信的关键,然后在ConnectivityService的内部创建了一个新的对象NetworkAgentInfo,该对象中保留了传递进来的一系列参数,包括NetworkAgent的Messenger对象、NetworkInfo、NetworkCapabilities、score以及创建了一个用于通讯的AsyncChannel通道。
然后就把当前创建的NetworkAgentInfo对象放入EVENT_REGISTER_NETWORK_AGENT消息中,发送给Handler处理:
//ConnectivityService.java
private class InternalHandler extends Handler {
public void handleMessage(Message msg) {
NetworkInfo info;
switch (msg.what) {
case EVENT_REGISTER_NETWORK_AGENT: {
handleRegisterNetworkAgent((NetworkAgentInfo)msg.obj);//详见章节3.5
break;
}
}
}
3.5 ConnectivityService.handleRegisterNetworkAgent
//ConnectivityService.java
private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
if (VDBG) log("Got NetworkAgent Messenger");
mNetworkAgentInfos.put(na.messenger, na);//将NetworkAgentInfo保存到mNetworkAgentInfos列表中
synchronized (mNetworkForNetId) {
mNetworkForNetId.put(na.network.netId, na);
}
//发送单向通道连接请求
na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
NetworkInfo networkInfo = na.networkInfo;
na.networkInfo = null;
updateNetworkInfo(na, networkInfo);//更新最新的NetworkInfo信息
}
在该阶段,ConnectivityService主要干了三件事情:
- 将新要注册的NetworkAgentInfo信息保存到HashMap列表mNetworkAgentInfos中
- 利用NetworkAgentInfo对象na的AsyncChannle工具类向NetWrokAgent发起单向通道连接
- 更新最新的NetworkAgentInfo状态
如果对AsyncChannel还有不清楚的小伙伴,墙烈建议参见篇章AsyncChannel看这就对了,此时我们可以看到AsyncChannel通过connect方法发起的单向通道连接,按照AsyncChannel通信的逻辑将会在mTrackerHandler收到CMD_CHANNEL_HALF_CONNECTED的消息,历史和其的相似啊和NetWorkFactory注册的过程:
//ConnectivityService.java
private class NetworkStateTrackerHandler extends Handler {
public NetworkStateTrackerHandler(Looper looper) {
super(looper);
}
private boolean maybeHandleAsyncChannelMessage(Message msg) {
switch (msg.what) {
default:
return false;
case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
handleAsyncChannelHalfConnect(msg);
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
if (nai != null) nai.asyncChannel.disconnect();
break;
}
case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
handleAsyncChannelDisconnected(msg);
break;
}
}
return true;
}
//ConnectivityService.java
private void handleAsyncChannelHalfConnect(Message msg) {
AsyncChannel ac = (AsyncChannel) msg.obj;
if (mNetworkFactoryInfos.containsKey(msg.replyTo)) {//NetWorkFactory注册走的是此通道
......
} else if (mNetworkAgentInfos.containsKey(msg.replyTo)) {//NetworkAgent注册走此通道
if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
if (VDBG) log("NetworkAgent connected");
// A network agent has requested a connection. Establish the connection.
mNetworkAgentInfos.get(msg.replyTo).asyncChannel.
sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
} else {
......
}
}
}
因为此时发起的是NetworkAgent注册流程所以会在第二个分支,因为我们前面已经将要注册的NetWorkAgent信息存储到了mNetworkAgentInfos中。这里我们可以看到走入该分支以后,此时ConnectivityService与NetworkAgent之间单向通道建立完成后,然后又通过sendMessage发起了双向通道的请求,此时在NetworkAgent端,将会收到CMD_CHANNEL_FULL_CONNECTION的消息。
3.6 NetWorkAgent.handleMessage
//NetworkAgent.java
public void handleMessage(Message msg) {
switch (msg.what) {
case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
if (mAsyncChannel != null) {
} else {
AsyncChannel ac = new AsyncChannel();
ac.connected(null, this, msg.replyTo);
//告知ConnectivityService双向连接成功
ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, AsyncChannel.STATUS_SUCCESSFUL);
synchronized (mPreConnectedQueue) {
mAsyncChannel = ac;
for (Message m : mPreConnectedQueue) {
//如果有缓存消息,则发送出去
ac.sendMessage(m);
}
mPreConnectedQueue.clear();
}
}
break;
}
}
}
至此ConnectivityService和NetWrokAgent的双向通道建立完成,随后通过AsyncChannel发送CMD_CHANNEL_FULLY_CONNECTED消息,表示整个AsyncChannel的连接成功,可以进行通信了。同时,还会遍历mPreConnectedQueue集合,这个集合中保存了当mAsyncChannel为null时的所有与更新网络信息相关的message,通过ac.sendMessage()向CS发送所有的message进行状态更新(要注意,ac对象的mSrcHanlder为当前NetworkAgent,mDstMessenger指向NetworkStateTrackerHandler)。
3.7 NetWorkAgent通知ConnectivityService网络状态的变化
还记得在章节三的开端说的我们可以通过NetworkAgent向ConnectivityService报告网络的变化,通知它进行网络状态的更新吗。而在EthernetNetworkFactory中通过updateAgen完成此项工作(至于怎么触发updateAgen这个本篇不予讨论,这个就牵涉到其它的知识点了),逻辑如下:
//EthernetNetworkFactory.java
public void updateAgent() {
synchronized (EthernetNetworkFactory.this) {
if (mNetworkAgent == null) return;
if (DBG) {
Log.i(TAG, "Updating mNetworkAgent with: " +
mNetworkCapabilities + ", " +
mNetworkInfo + ", " +
mLinkProperties);
}
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
mNetworkAgent.sendLinkProperties(mLinkProperties);
// never set the network score below 0.
mNetworkAgent.sendNetworkScore(mLinkUp? NETWORK_SCORE : 0);
}
}
我们这里以sendNetworkScore为例说明:
//NetWorkAgent.java
public void sendNetworkScore(int score) {
if (score < 0) {
throw new IllegalArgumentException("Score must be >= 0");
}
queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new Integer(score));
}
private void queueOrSendMessage(Message msg) {
synchronized (mPreConnectedQueue) {
if (mAsyncChannel != null) {
mAsyncChannel.sendMessage(msg);
} else {
mPreConnectedQueue.add(msg);
}
}
}
这个应该又是老套路了吗,通过已经建立的AsyncChannel连接向ConnectifyService发送消息,并附带需要更新的NetworkInfo对象,这样ConnectivityService中NetworkStateTrackerHandler就可以收到消息,并进行网络状态更新了。
结语
修行至此,恭喜读者你已经对EthernetNetworkFactory类的功能了然于心了,但是这个还只是个开始,关于Android以太网框架的情景分析还有涉及到和netd的交互逻辑,这个我会在后续的章节继续讲解的。
写在最后
各位读者看官朋友们,Android以太网框架情景分析之NetworkFactory与NetworkAgent深入分析到这里结束了,希望能吸引你,激发你的学习欲望和斗志。在最后麻烦读者朋友们如果本篇对你有帮助,关注和点赞一下,当然如果有错误和不足的地方也可以拍砖。
特别鸣谢如下博客:
https://blog.csdn.net/u010961631/article/details/48971651
https://blog.csdn.net/qq_14978113/article/details/89182253