目录
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个重要的参数:
- NetworkRequest:网络请求封装,如通过什么能力及什么方式透传。
addCapability:通过此函数设置网络能力,见表2。它用于向网络请求添加特定的网络能力,具体来说,addCapability用于指定一个网络请求所需或支持的网络特性,跟APN有关。本示例的网络能力是安全用户平面定位(SUPL),用于GPS辅助定位服务。
addTransportType:函数用于指定网络连接所使用的传输类型,见表3。传输类型决定了网络请求所使用的底层传输技术,例如Wi-Fi、蜂窝数据等。本示例的传输类型是移动网络。
- NetworkCallback:网络请求的回调,具体是ConnectivityManager.NetworkCallback,回调的接口有onAvailable,onLost,onUnavailable。
关于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属性,确认是否从该网卡收发,见如下图。