浅谈Android的ConnectivityService网络连接服务

目录

1、概述

2、原理

3、网络工厂

4、网络类型注册

5、网络链接请求

6、网络评分机制

总结

8、附表

表1:代码路径

表2:网络能力

表3:网络透传能力

9、扩展:如何进行第二路拨号并访问网络

9.1 拨号

9.2 访问


1、概述

ConnectivityService在 Android 系统中是一个核心的服务,它主要负责管理和监控网络连接的状态。这个服务是 Android 框架的一部分,为应用程序和系统服务提供了关于网络连接(如 wifi、mobile network、ethernet、bt-pan)的详细信息。

ConnectivityService作为管理员身份,每种网络都会去向它注册,网络的使用权全靠它来分配。并实现了网络评分机制确保了系统能够最出作优的网络连接,从而为用户提供更好的网络体验。

2、原理

相关类的功能说明如下:

  • ConnectivityService是个服务,用来管理网络链接。
  • ConnectivityManager是ConnectivityService的代理,代理意思是调用者不要关心具体接口的实现,只管调用,所以才设计此模式。所以ConnectivityManager作用是对外提供ConnectivityService的相关接口。
  • TelephonyNetworkFatory是个移动的网络工厂,父类是NetworkFactory,用于SIM卡业务的数据链接。
  • WifiNetworkFatory是个Wifi的网络工厂,父类是NetworkFactory,用于WIFI模块的数据链接。
  • EthernetNetworkFatory是个以太网的网络工厂,父类是NetworkFactory,用于以太网业务的数据链接。

ConnectivityService链接服务从箭头来看分为2个流程,分别为注册和网络选择。

  • 注册:见红色箭头,在设备开机时,相关的网络工厂会向ConnectivityService进行注册register,ConnectivityManager是个代理,ConnectivityService对外提供的接口都由ConnectivityManager提供,最后在register函数里通过调用registerNetworkProvider,ConnectivityService就拿到了各种类型的网络工厂,比如移动,Wifi,以太网等。
  • 网络选择:见绿色箭头,如果存在多种网络的情况下,ConnectivityService根据evalRequest逻辑进行评分,分数高的网络工厂会优先使用,这叫做网络评分机制。从流程图可以看出,当应用调用请求网络requestNetwork时(此函数是发起一个网络链接请求),最终根据evalRequest函数找到分数最高的网络工厂然后调用它的needNetworkFor来完成网络链接请求。

3、网络工厂

NetworkFactory网络工厂其实是通过工厂模式生成各自特有功能的网络类型。

上图的圆圈是网络工厂的相关类图,TelephonyNetworkFatory、WifiNetworkFatory、EthernetNetworkFatory网络类型继承NetworkFatory,NetworkFatory的重点override接口如下:

  • needNetworkFor:请求网络链接接口
  • releaseNetworkFor:释放网络链接接口
  • setScoreFilter(int score): 设置分数,除了初始评分外,各个网络的评分还会根据实时状态进行调整。

例如,以太网会根据网卡的up和down状态,把分值设置为70(当网卡up时)或0(当网卡down时)。Wi-Fi的分值还跟信号状态、当前数据速率等一系列因素有关,在WifiStateMachine.java的calculateWifiScore函数中进行计算,初始计算的基础分值为56,然后根据wifi网络的状态进行小的加减,最后如果分值大于60,就把分值设置为60。

  • register(): 向ConnectivityService注册,这样ConnectivityService就能拿到各类型的网络。用于后面选择评分高的网络进行链接。

NetworkProvider直译是网络提供者,和对应的网络工厂绑定,用于告知ConnectivityService,方便异步调用网络提供者。

4、网络类型注册

在开机时,各网络类型通过调用register向ConnectivityService进行注册,函数代码如下:

1.	public void register() {  
2.	    if (mProvider != null) {  
3.	        throw new IllegalStateException("A NetworkFactory must only be registered once");  
4.	    }  
5.	    if (DBG) log("Registering NetworkFactory");  
6.	  
7.	    mProvider = new NetworkProvider(mContext, NetworkFactory.this.getLooper(), LOG_TAG) {  
8.	        @Override  
9.	        public void onNetworkRequested(@NonNull NetworkRequest request, int score,  
10.	                int servingProviderId) {  
11.	            handleAddRequest(request, score, servingProviderId);  
12.	        }  
13.	  
14.	        @Override  
15.	        public void onNetworkRequestWithdrawn(@NonNull NetworkRequest request) {  
16.	            handleRemoveRequest(request);  
17.	        }  
18.	    };  
19.	  
20.	    ((ConnectivityManager) mContext.getSystemService(  
21.	        Context.CONNECTIVITY_SERVICE)).registerNetworkProvider(mProvider);  
22.	}  

从如上代码可以看出,先创建一个NetworkProvider的对象mProvider,此对象之前说过,方便ConnectivityService进行异步调用,也就是NetworkProvider有相关的Messenger实现。

最后调用ConnectivityService的registerNetworkProvider函数完成注册,把NetworkProvider放到mNetworkProviderInfos这个成员数组里。其实ConnectivityService最终拿到的是NetworkFactory的NetworkProvider相关接口。

5、网络链接请求

应用层如何发起网络链接请求,本文以TelephonyNetworkFatory为例说明。

ConnectivityManager对外提供了一个requestNetwork接口,所以应用层调用此接口即可。

1.	NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();  
2.	networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL);  
3.	networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);  
4.	// During an emergency call, and when we have cached the Active Sub Id, we set the  
5.	// Network Specifier so that the network request goes to the correct Sub Id  
6.	if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {  
7.	    if (DEBUG) Log.d(TAG, "Adding Network Specifier: " + Integer.toString(mActiveSubId));  
8.	    networkRequestBuilder.setNetworkSpecifier(Integer.toString(mActiveSubId));  
9.	}  
10.	NetworkRequest networkRequest = networkRequestBuilder.build();  
11.	mConnMgr.requestNetwork( 
12.	        networkRequest,  
13.	        mSuplConnectivityCallback,  
14.	        mHandler,  
15.	        SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS);  

requestNetwork函数有2个重要的参数:

  1. NetworkRequest:网络请求封装,如通过什么能力及什么方式透传。

addCapability:通过此函数设置网络能力,见表2。它用于向网络请求添加特定的网络能力,具体来说,addCapability用于指定一个网络请求所需或支持的网络特性,跟APN有关。本示例的网络能力是安全用户平面定位(SUPL),用于GPS辅助定位服务。

addTransportType:函数用于指定网络连接所使用的传输类型,见表3。传输类型决定了网络请求所使用的底层传输技术,例如Wi-Fi、蜂窝数据等。本示例的传输类型是移动网络。

  1. NetworkCallback:网络请求的回调,具体是ConnectivityManager.NetworkCallback,回调的接口有onAvailableonLostonUnavailable

关于requestNetwork接下来如何调用,请参考时序图,这里不再讲解。

6、网络评分机制

  触发网络评分机制有好多种方法,如修改分数,下发网络请求等。这些最终都会触发evalRequest函数的调用,代码如下:

1.	private void evalRequest(NetworkRequestInfo n) {  
2.	    if (VDBG) {  
3.	        log("evalRequest");  
4.	        log(" n.requests = " + n.requested);  
5.	        log(" n.score = " + n.score);  
6.	        log(" mScore = " + mScore);  
7.	        log(" request.providerId = " + n.providerId);  
8.	        log(" mProvider.id = " + mProvider.getProviderId());  
9.	    }  
10.	    if (shouldNeedNetworkFor(n)) {  
11.	        if (VDBG) log("  needNetworkFor");  
12.	        needNetworkFor(n.request, n.score);  
13.	        n.requested = true;  
14.	    } else if (shouldReleaseNetworkFor(n)) {  
15.	        if (VDBG) log("  releaseNetworkFor");  
16.	        releaseNetworkFor(n.request);  
17.	        n.requested = false;  
18.	    } else {  
19.	        if (VDBG) log("  done");  
20.	    }  21.	} 

shouldNeedNetworkFor函数用于确认是否进行网络链接请求,shouldReleaseNetworkFor函数用于释放网络链接请求。

shouldNeedNetworkFor的函数代码如下,具体逻辑是:如果NetworkRequestInfo没有被requested过,并且其分值(n.score)小于当前NetworkFactory自己的分值(mScore),那么就说明,当前NetworkFactory所处的网络优先级高于其他网络的优先级,就会触发当前NetworkFactory所在网络的needNetworkFor()流程,也就是连接建立流程,并将标记NetworkRequestInfo.requested=true。当NetworkRequestInfo被requested过(也就是当前网络被needNetworkFor过),此时如果再次收到请求,并且携带的新score大于当前NetworkFactory所处网络的mScore,那么就说明当前NetworkFactory所在网络优先级已经不是最高,需要将其releaseNetworkFor掉,并标记NetworkRequestInfo.requested=false。

1.	private boolean shouldNeedNetworkFor(NetworkRequestInfo n) {  
2.	        // If this request is already tracked, it doesn't qualify for need  
3.	        return !n.requested  
4.	            // If the score of this request is higher or equal to that of this factory and some  
5.	            // other factory is responsible for it, then this factory should not track the request  
6.	            // because it has no hope of satisfying it.  
7.	            && (n.score < mScore || n.providerId == mProvider.getProviderId())  
8.	            // If this factory can't satisfy the capability needs of this request, then it  
9.	            // should not be tracked.  
10.	            && n.request.canBeSatisfiedBy(mCapabilityFilter)  
11.	            // Finally if the concrete implementation of the factory rejects the request, then  
12.	            // don't track it.  
13.	            && acceptRequest(n.request, n.score);  
14.	    }  

总结

  本文大概讲了ConnectivityService框架及接口使用说明,涉及的还不是很深。后面有问题再具体总结分析这块的内容。

8、附表

表1:代码路径

路径

frameworks\opt\telephony\src\java\com\android\internal\telephony\dataconnection\TelephonyNetworkFactory.java

frameworks\libs\net\common\src_servicescommon\android\net\NetworkFactory.java

frameworks\base\core\java\android\net\NetworkCapabilities.java

frameworks\base\core\java\android\net\NetworkRequest.java

frameworks\base\core\java\android\net\ConnectivityManager.java

frameworks\base\core\java\android\net\ Network.java

frameworks\base\core\java\android\net\ NetworkProvider.java

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

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

表2:网络能力

NetworkCapabilities

ApnSetting

能力说明

NET_CAPABILITY_MMS

TYPE_MMS

多媒体消息服务(MMS),用于发送和接收彩信。

NET_CAPABILITY_SUPL

TYPE_SUPL

安全用户平面定位(SUPL),用于GPS辅助定位服务。

NET_CAPABILITY_DUN

TYPE_DUN

拨号上网(DUN),允许设备通过移动网络为其他设备提供网络连接。

NET_CAPABILITY_FOTA

TYPE_FOTA

固件空中升级(FOTA),允许设备通过移动网络接收固件更新。

NET_CAPABILITY_IMS

TYPE_IMS

IP多媒体子系统(IMS),用于支持VoIP等多媒体通信服务。

NET_CAPABILITY_CBS

TYPE_CBS

小区广播服务(CBS),用于向手机用户发送小区内的短消息。

NET_CAPABILITY_IA

TYPE_IA

这个名称不太常见,可能表示某种特定类型的网络访问或功能。

NET_CAPABILITY_EIMS

TYPE_EMERGENCY

紧急IMS,可能用于紧急情况下的多媒体通信。

NET_CAPABILITY_INTERNET

TYPE_DEFAULT

表示网络可以访问互联网。

NET_CAPABILITY_WIFI_P2P

Wi-Fi直连(P2P),允许设备之间直接通过Wi-Fi进行通信,无需接入点。

NET_CAPABILITY_RCS

丰富的呼叫服务(RCS),用于增强语音呼叫的功能,如视频通话、即时消息等。

NET_CAPABILITY_XCAP

XML配置访问协议(XCAP),一种用于存储和检索XML文档的协议。

NET_CAPABILITY_NOT_METERED

表示网络流量不计费。

NET_CAPABILITY_NOT_RESTRICTED

表示网络没有访问限制。

NET_CAPABILITY_TRUSTED

表示网络是可信的。

表3:网络透传能力

TransportType

能力名称

能力说明

TRANSPORT_CELLULAR:

蜂窝数据网络

如2G、3G、4G(LTE)、5G等移动网络。

TRANSPORT_WIFI

Wi-Fi网络

通过无线局域网连接到互联网。

TRANSPORT_BLUETOOTH

蓝牙网络

虽然蓝牙通常不用于互联网连接,但在某些特定场景下(如蓝牙PAN)可能用作数据传输。

TRANSPORT_ETHERNET

以太网网络

这通常指的是有线网络连接,但在Android设备上较为罕见,因为移动设备主要使用无线连接。

TRANSPORT_VPN

虚拟私人网络(VPN)

这是一种通过公共网络(如互联网)建立加密通道的技术,用于在远程服务器上安全地发送和接收数据。

9、扩展:如何进行第二路拨号并访问网络

9.1 拨号

在使用移动网络进行第二跑拨号时,要注意不能与当前的apn相同,比如当前是default的apn,那么第二路只能使用其他apn类型拨号了,否则会报如下错误。

ConnectivityService: NetReassign [53 : null → 100]

使用APN为mms的拨号示例代码如下:

1.	import android.net.ConnectivityManager;
2.	import android.net.Network;
3.	import android.net.NetworkCapabilities;
4.	import android.net.NetworkInfo;
5.	import android.net.NetworkRequest;
6.	
7.	private ConnectivityManager.NetworkCallback createConnectivityCallback(){  
8.	    return new ConnectivityManager.NetworkCallback() {  
9.	        @Override  
10.	        public void onAvailable(Network network) {  
11.	            // Specific to a change to a SUPL enabled network becoming ready  
12.	            Log.d(LOG_TAG, "test network connection available.");  
13.	        }  
14.	  
15.	        @Override  
16.	        public void onLost(Network network) {  
17.	            Log.d(LOG_TAG, "test network connection lost.");  
18.	        }  
19.	  
20.	        @Override  
21.	        public void onUnavailable() {  
22.	            Log.d(LOG_TAG, "test network connection request timed out.");  
23.	            // Could not setup the connection to the network in the specified time duration.  
24.	  
25.	        }  
26.	    };  
27.	}  
28.	//发起移动网络拨号
29.	private void startSecDataCall()  
30.	{  
31.	    ConnectivityManager mTConnMgr;  
32.	    ConnectivityManager.NetworkCallback mConnectivityCallback = createConnectivityCallback();  
33.	    mTConnMgr = (ConnectivityManager) getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);  
34.	      
35.	    NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();  
36.	    networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS);  
37.	    networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);  
38.	  
39.	    NetworkRequest networkRequest = networkRequestBuilder.build();  
40.	    mTConnMgr.requestNetwork(  
41.	            networkRequest,  
42.	            mConnectivityCallback,  
43.	            20 * 1000);  
44.	}  

拨号成功后,可使用ifconfig指令查询网卡有没有识别出来。

9.2 访问

使用第二路访问网络代码:

1.	import java.net.URL;  
2.	import java.net.MalformedURLException;  
3.	import java.io.IOException;  
4.	import java.net.URLConnection;  
5.	import java.io.ByteArrayOutputStream;  
6.	  
7.	private String getTest(URLConnection urlConnection) throws IOException {  
8.	        //URL url = new URL(pacUri.toString());  
9.	        //URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);  
10.	        long MAX_PAC_SIZE = 20 * 1000 * 1000;  
11.	        long contentLength = -1;  
12.	        try {  
13.	            contentLength = Long.parseLong(urlConnection.getHeaderField("Content-Length"));  
14.	        } catch (NumberFormatException e) {  
15.	            // Ignore  
16.	        }  
17.	        if (contentLength > MAX_PAC_SIZE) {  
18.	            throw new IOException("PAC too big: " + contentLength + " bytes");  
19.	        }  
20.	        ByteArrayOutputStream bytes = new ByteArrayOutputStream();  
21.	        byte[] buffer = new byte[1024];  
22.	        int count;  
23.	        while ((count = urlConnection.getInputStream().read(buffer)) != -1) {  
24.	            bytes.write(buffer, 0, count);  
25.	            if (bytes.size() > MAX_PAC_SIZE) {  
26.	                throw new IOException("PAC too big");  
27.	            }  
28.	        }  
29.	        Log.d(LOG_TAG, "bytes.size() = " + bytes.size());  
30.	        return bytes.toString();  
31.	    }  
32.	  
33.	private ConnectivityManager.NetworkCallback createConnectivityCallback(){  
34.	        return new ConnectivityManager.NetworkCallback() {  
35.	            @Override  
36.	            public void onAvailable(Network network) {  
37.	                // Specific to a change to a SUPL enabled network becoming ready  
38.	                Log.d(LOG_TAG, "test network connection available");  
39.	                URL mUrl;  
40.	  
41.	                try {  
42.	                    mUrl = new URL("https://www.baidu.com/");  
43.	                    //Log.d(LOG_TAG, "test network mUrl = ", mUrl);  
44.	                    getTest(network.openConnection(mUrl));  
45.	                } catch (IOException e) {  
46.	                    //throw new MalformedURLException("open()", e, ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN);  
47.	                    Log.d(LOG_TAG, "test network 11");  
48.	                }             
49.	            }  
50.	  
51.	            @Override  
52.	            public void onLost(Network network) {  
53.	                Log.d(LOG_TAG, "test network connection lost.");  
54.	            }  
55.	  
56.	            @Override  
57.	            public void onUnavailable() {  
58.	                Log.d(LOG_TAG, "test network connection request timed out.");  
59.	                // Could not setup the connection to the network in the specified time duration.  
60.	  
61.	            }  
62.	        };  
63.	    }  

可通过查看对应网卡的RX TX属性,确认是否从该网卡收发,见如下图。

  • 12
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值