Android N (7.0) 数据拨号前的准备工作
2017-09-14 14:38
253 查看
背景
在介绍PhoneApp的创建过程时,我们知道为了支持双卡手机,PhoneFactory创建了两个Phone对象。
然而由于通信制式、功耗等的限制,目前底层的芯片厂商规定modem工作于DSDS模式下,于是同一时间内只有一个Phone具有上网的能力。
本文旨在揭示激活Phone拨号能力的过程,即讲述数据拨号前的准备工作。
版本
android 7.0
1 TelephonyProvider的启动
数据业务在建立之前,必须有可用的APN,因此我们首先看看Android 7.0中APN加载的过程。
之前分析PhoneApp启动过程时,我们知道PhoneApp的onCreate函数是靠ActivityThread.java中的handleBindApplication函数调用的。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[/code]
从上面的代码,我们知道在PhoneApp的onCreate被调用前,先加载了PhoneApp中的ContentProvider(PM解析xml得到这种包含关系)。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[/code]
从TelephonyProvider的AndroidManifest.xml,我们知道TelephonyProvider就是运行在Phone进程中的,因此结合上面的代码,可以得出结论:
TelephonyProvider将在PhoneApp的onCreate被调用前被加载。
Android7.0 TelephonyProvider启动后的过程,与Android6.0中一致。
之前的blog分析Android6.0中APN加载过程时,已经做过描述了,其实就是创建数据库、解析xml文件并存入数据库的过程,此处不再赘述。
2 设定具有拨号能力的Phone
我们回忆一下PhoneApp启动后,利用PhoneFactory的makeDefaultPhone创建对象的代码,其中:
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
在刚开机时,不插卡的情况下,上面代码提及的PhoneSwitcher和TelephonyNetworkFactory将决定具有拨号能力的Phone。
我们在这里先分析不插卡情况下的流程,主要原因是:
框架对卡信息有记录,将会根据记录信息改变具有拨号能力的Phone。这个过程是通过调用Phone进程提供的接口完成的,我们以后再做分析。
2.1 PhoneSwitcher
我们先来看看PhoneSwitcher:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[/code]
上面的代码涉及到了很多内容,以后有机会再分析,此时我们仅需要有一个映像就是:
PhoneSwitcher创建了一个能够处理所有Network Request的NetworkFactory(实际上是子类),并调用了register方法。
我们来看看NetworkFactory的register方法:
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
继续跟进ConnectivityManager中的函数:
2
3
4
5
6
7
8
9
10
11
12
[/code]
mService的类型为IConnectivityManager,其实是ConnectivityService的Binder通信的服务端代理,因此上述代码最终将调用到ConnectivityService的registerNetworkFactory函数。
ConnectivityService是开机时,由SystemServer加载的,这里我们不分析ConnectivityService启动的过程,以后有机会单独介绍ConnectivityService。这里我们只需要知道ConnectivityService主要负责管理Android中的各种网络。
我们看看ConnectivityService的registerNetworkFactory函数:
2
3
4
5
6
7
8
9
10
[/code]
ConnectivityService内部的handler将调用handleRegisterNetworkFactory处理EVENT_REGISTER_NETWORK_FACTORY消息:
2
3
4
5
6
[/code]
2
3
4
5
6
7
[/code]
我们看看AsyncChannel的connect方法:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[/code]
看到connnected函数,我们知道每个NetworkFactory注册后,ConnectivityService将维护对应的NetworkFactoryInfo,对应的键值为NetworkFactory中的Messenger对象。
每个NetworkFactoryInfo中有一个AsyncChannel对象,该对象的源端Messenger包裹着ConnectivityService的mTrackerHandler,目的端Messenger为注册NetworkFactory的Messenger。
ConnectivityService这么做的目的是:简化ConnectivityService与每个NetworkFactory通信时的函数调用。
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
根据上面的代码,我们知道接下来应该是ConnectivityService的mTrackerHandler处理CMD_CHANNEL_HALF_CONNECTED事件。
mTrackerHandler的类型为ConnectivityService的内部类NetworkStateTrackerHandler:
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
在这里我们看看maybeHandleAsyncChannelMessage:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[/code]
上面的代码其实就是发消息给NetworkFactory。这里存在的疑点是:
1、开机时是否有NetworkRequest?
2、发送CMD_REQUEST_NETWORK时为什么要携带分数?
我们先来分析第一个问题:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[/code]
从上面的代码,我们知道了开机后,ConnectivityService中默认就会存在一个NetworkRequest,于是每当一个新的NetworkFactory注册到ConnectivityService后,都会处理这个默认的NetworkRequest。
现在我们再来回答第二个问题,为什么发送CMD_REQUEST_NETWORK时,需要携带分数。
这个问题其实涉及了Android框架,管理整个网络的架构。具体的情况,今后介绍ConnectivityService时,会详细介绍。
在这里直接抛出近似的答案:Android通过ConnectivityService管理所有的网络,每一种网络都有各自的NetworkFactory。当NetworkFactory收到NetworkRequest后,将创建实际的网络对象。ConnectivityService用NetworkAgent和NetworkAgentInfo来抽象实际的网络对象的一些特性(还有其它的对象,例如Network,LinkProperties等共同抽象网络)。
然而,同一个设备在同一时间内不需要连接多个网络。比如说,用户的目的就是上网,但WiFi和移动网络均能满足用户上网的需求,因此设备没有必要同时连接WiFi和移动网络。因此,Android需要一种机制来权衡各种网络的是否应该保留。
为此android引入了分数的概念,当两个网络能同时满足一个NetworkRequest时,分数高者就留下。根据这个规则,我们其实也知道如果两个网络分别满足不同的NetworkRequest时,是可以共存的。
以上是基本的结论,今后分析ConnectivityService时,会分析对应的源码。
知道原理后,
现在回过头来看看发送CMD_REQUEST_NETWORK时,为什么要携带分数。
当消息发送给NetworkFactory后,NetworkFactory如果能满足NetworkRequest的需求,需要生成对应当NetworkAgent注册到ConnectivityService。如果这个新生成的NetworkAgent的分数,比之前满足NetworkRequest的已存在的NetworkAgent分数低,那么这个NetworkAgent将被处理掉。
因此,与其浪费资源生成一个无用的NetworkAgent,不如一开始就让NetworkFactory通过比较分数,不处理高分NetworkRequest。
这种设计类似于从声源阻止噪声吧!
回答完问题后,让我们继续回到之前ConnectivityService的流程,接下来看看AsyncChannel如何发送消息:
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
至此,消息终于又发回了PhoneSwitcher中创建的NetworkFactory,消息的处理由父类NetworkFactory处理:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
[/code]
绕了一圈,我们现在可以看看PhoneSwitcher中创建的NetworkFactory的needNetworkFor函数:
2
3
4
5
6
7
8
[/code]
2
3
4
5
6
7
8
9
10
11
12
[/code]
这里的唯一的问题就是“根据优先级排序”的概念。
我们花点时间来看看DcRequest这个类:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
[/code]
从上面的代码,我们知道DcRequest其实就是根据xml中的配置文件得到每个Apn对应的优先级;然后,根据每个NetworkRequest需要的NetworkCapabilities得到对应的apnId;最终,根据apnId的优先级,完成对NetworkRequest的优先级排序。
其中,networkAttributes定义于frameworks/base/core/res/res/values/config.xml中:
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
根据NetworkConfig的构造函数,我们看出每一行的第4位表示优先级。结合DcReqeust中的compareTo函数,我们知道数字越大,优先级越高,例如mobile_mms的优先级要高于mobile:
2
3
4
5
6
7
8
9
[/code]
现在继续回到PhoneSwitcher处理NetworkRequest的流程中来,PhoneSwitcher根据优先级对NetworkRequest排序后,将调用onEvaluate方法:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
[/code]
最后,我们来看看deactivate和activate函数:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[/code]
PhoneSwitcher的上述流程,最终激活了一个Phone的数据拨号能力,完成拨号前的第一部分准备工作。
2.2 TelephonyNetworkFactory
PhoneSwitcher只是决定了那一个Phone具有拨号能力,在实际拨号前还需要激活可用的APN。这一部分工作需要TelephonyNetworkFactory来主动承担。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[/code]
从上面的代码我们知道,在初始时每个Phone对应的TelephonyNetworkFactory的激活态均是false,并且均注册到了ConnectivityService。于是,与PhoneSwitcher一样,TelephonyNetworkFactory也会收到ConnectivityService发送的NetworkReqeust请求。
由于ConnectivityService创建的DefaultRequest只要求Internet能力,并且在没有建立其它网络的条件下(没有连接WiFi等网络),DefaultRequest的分数为0,因此两个Phone对应的TelephonyNetworkFactory均会处理ConnectivityService发送的NetworkReqeust请求。
与PhoneSwitcher一样,我们看看TelephonyNetworkFactory的needNetworkFor函数:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
[/code]
从上面的代码我们知道,在TelephonyNetworkFactory被激活前,它收到NetworkRequest的请求,但不会进行实际操作,直到PhoneSwitcher完成了激活Phone拨号能力的操作。
在TelephonyNetworkFactory的构造函数中,向PhoneSwitcher注册了观察对象,当发现Phone的激活态改变后,将向内部handler发送EVENT_ACTIVE_PHONE_SWITCH消息。内部handler收到消息后,将调用onActivePhoneSwitch进行处理。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[/code]
从上面的代码,可以看出当PhoneSwitcher改变TelephonyNetworkFactory的激活状态后,TelephonyNetworkFactory将根据各自的状态,调用applyRequests函数:
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
根据上面的代码,我们知道了流程走到了DcTracker。
DcTracker是PhoneFactory创建GsmCdmaPhone时,在GsmCdmaPhone内部创建的,与TelephonyNetworkFactory一样,每个phone对象持有一个。
2
3
4
5
[/code]
TelephonyComponentFactory是一个单例对象:
2
3
4
5
6
7
8
9
10
[/code]
我们回到拨号前的准备流程,看看DcTracker的requestNetwork函数:
2
3
4
5
6
7
8
9
[/code]
上面的代码中唯一的疑点是mApnContextsById是如何定义的?这就必须提及DcTracker的构造函数了。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[/code]
从上面的代码可以看出,mApnContextsById中的Apn记录也是从config.xml中读取出来的,因此ApnContext的incRefCount方法可以得到执行:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[/code]
最后我们来看看DcTracker的setEnabled函数。
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[/code]
至此,TelephonyNetworkFactory再次完成了一部分数据拨号前的准备工作,激活了一个类型的APN。
3 DcTracker装载卡对应的APN信息
从这一节的标题就知道,我们要开始分析插入卡后的流程了,毕竟拨号是离不开SIM卡的(网络运营商提供的虚拟卡暂不考虑)。
我们先看看框架是如何知道手机中插入了卡。
卡实际上可以看做一种硬件设备,当卡插入手机后,会产生相应的电信号,触发相应的驱动工作。当然,这不是我们关注的重点,我们只需要知道的是,当检测到卡后,modem将主动上报RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED事件。
RIL收到该事件后,将通知其观察者:
2
3
4
5
6
7
[/code]
那么谁是该事件的观察者呢?
我们知道在PhoneFactory的makeDefaultPhone中,创建过UiccController,看看UiccController构造函数的片段:
2
3
4
5
6
7
8
9
10
[/code]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[/code]
UiccController的观察者比较多,其中就有我们关注的DcTracker,在DcTracker的构造函数中,就有如下片段:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[/code]
当DcTracker监听到卡信息载入完成后,处理EVENT_RECORDS_LOADED事件:
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[/code]
我们看看createAllApnList函数:
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
[/code]
至此,我们做好了数据拨号前的所有工作,用户点击数据业务开关后,就可以开始拨号流程了。
当然上述过程过于繁杂,涉及的细节比较多,因此还是需要整理一下流程图和类图:
流程图对应链接
类图对应链接
结束语
以上的流程图和类图都是比较简易的。
实际终端的开机过程,可能和流程图存在一定时序上的偏差,但大致流程应该一致;类图并没有严格分析每个类之间的关系,仅仅描述了主要类的关系。
最后需要说明的是,目前的逻辑都是按照Android 7.0原生流程来分析的,实际终端的软件版本应该与此有些差异。不论MTK还是Qualcomm应该都会根据自己的芯片能力,对原生流程进行修改。
举例来说,原生流程中,PhoneSwitcher直接通过setDataAllowed激活Phone的数据拨号能力,然后立马就通知TelephonyNetworkFactory。
这实在是高估了底层芯片的能力,自己目前还没有拿到Qualcomm和MTK的代码,但觉得这里应该会像Android6.0一样,必须等底层芯片返回数据能力设置成功的信息后,才会通知TelephonyNetworkFactory。甚至考虑到底层的异常,厂商应该还是会引入状态机进行控制。
原文地址:http://blog.csdn.net/gaugamela/article/details/52325196
在介绍PhoneApp的创建过程时,我们知道为了支持双卡手机,PhoneFactory创建了两个Phone对象。
然而由于通信制式、功耗等的限制,目前底层的芯片厂商规定modem工作于DSDS模式下,于是同一时间内只有一个Phone具有上网的能力。
本文旨在揭示激活Phone拨号能力的过程,即讲述数据拨号前的准备工作。
版本
android 7.0
1 TelephonyProvider的启动
数据业务在建立之前,必须有可用的APN,因此我们首先看看Android 7.0中APN加载的过程。
之前分析PhoneApp启动过程时,我们知道PhoneApp的onCreate函数是靠ActivityThread.java中的handleBindApplication函数调用的。
private void handleBindApplication(AppBindData data) { ........ try { Application app = data.info.makeApplication(data.restrictedBackupMode, null); mInitialApplication = app; if (!data.restrictedBackupMode) { if (!ArrayUtils.isEmpty(data.providers)) { //加载App中的provider installContentProviders(app, data.providers); ........... } } try { mInstrumentation.onCreate(data.instrumentationArgs); } catch (Exception e) { ......... } try { //调用App的onCreate函数 mInstrumentation.callApplicationOnCreate(app); } catch (Exception e) { .............. } } finally { ............. } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[/code]
从上面的代码,我们知道在PhoneApp的onCreate被调用前,先加载了PhoneApp中的ContentProvider(PM解析xml得到这种包含关系)。
private void installContentProviders(Context context, List<ProviderInfo> providers) { ........ for (ProviderInfo cpi : providers) { .......... //依次安装provider IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi, false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/); ............ } try { //发布provider ActivityManagerNative.getDefault().publishContentProviders( getApplicationThread(), results); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[/code]
从TelephonyProvider的AndroidManifest.xml,我们知道TelephonyProvider就是运行在Phone进程中的,因此结合上面的代码,可以得出结论:
TelephonyProvider将在PhoneApp的onCreate被调用前被加载。
Android7.0 TelephonyProvider启动后的过程,与Android6.0中一致。
之前的blog分析Android6.0中APN加载过程时,已经做过描述了,其实就是创建数据库、解析xml文件并存入数据库的过程,此处不再赘述。
2 设定具有拨号能力的Phone
我们回忆一下PhoneApp启动后,利用PhoneFactory的makeDefaultPhone创建对象的代码,其中:
......... //创建了一个PhoneSwitcher sPhoneSwitcher = new PhoneSwitcher(MAX_ACTIVE_PHONES, numPhones, sContext, sc, Looper.myLooper(), tr, sCommandsInterfaces, sPhones); ......... sTelephonyNetworkFactories = new TelephonyNetworkFactory[numPhones]; for (int i = 0; i < numPhones; i++) { //创建了两个TelephonyNetworkFactory,分别与每个Phone关联起来 sTelephonyNetworkFactories[i] = new TelephonyNetworkFactory( sPhoneSwitcher, sc, sSubscriptionMonitor, Looper.myLooper(), sContext, i, sPhones[i].mDcTracker); }1
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
在刚开机时,不插卡的情况下,上面代码提及的PhoneSwitcher和TelephonyNetworkFactory将决定具有拨号能力的Phone。
我们在这里先分析不插卡情况下的流程,主要原因是:
框架对卡信息有记录,将会根据记录信息改变具有拨号能力的Phone。这个过程是通过调用Phone进程提供的接口完成的,我们以后再做分析。
2.1 PhoneSwitcher
我们先来看看PhoneSwitcher:
//PhoneSwitcher继承自Handler public class PhoneSwitcher extends Handler { ...... public PhoneSwitcher(......) { ........ //创建NetworkCapabilities,为创建NetworkFactory作准备 //NetworkCapabilities决定了NetworkFactory可以处理的Network Request种类 NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_IA); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); netCap.setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER); //创建了一个NetworkFactory对象 NetworkFactory networkFactory = new PhoneSwitcherNetworkRequestListener(looper, context, netCap, this); //每个NetworkFactory仅能处理分数比自己低的NetworkRequest //这里设置分数为101,将能够处理所有的request networkFactory.setScoreFilter(101); //注册该NetworkFactory对象 networkFactory.register(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[/code]
上面的代码涉及到了很多内容,以后有机会再分析,此时我们仅需要有一个映像就是:
PhoneSwitcher创建了一个能够处理所有Network Request的NetworkFactory(实际上是子类),并调用了register方法。
我们来看看NetworkFactory的register方法:
//NetworkFactory继承Handler public class NetworkFactory extends Handler { ........ public void register() { if (DBG) log("Registering NetworkFactory"); if (mMessenger == null) { //构造Messenger对象,其中包含Handler mMessenger = new Messenger(this); //调用ConnectivityManager的registerNetworkFactory方法 ConnectivityManager.from(mContext).registerNetworkFactory(mMessenger, LOG_TAG); } } .......... }1
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
继续跟进ConnectivityManager中的函数:
public static ConnectivityManager from(Context context) { return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); } public void registerNetworkFactory(Messenger messenger, String name) { try { //可以看到将调用用mService的registerNetworkFactory函数 mService.registerNetworkFactory(messenger, name); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }1
2
3
4
5
6
7
8
9
10
11
12
[/code]
mService的类型为IConnectivityManager,其实是ConnectivityService的Binder通信的服务端代理,因此上述代码最终将调用到ConnectivityService的registerNetworkFactory函数。
ConnectivityService是开机时,由SystemServer加载的,这里我们不分析ConnectivityService启动的过程,以后有机会单独介绍ConnectivityService。这里我们只需要知道ConnectivityService主要负责管理Android中的各种网络。
我们看看ConnectivityService的registerNetworkFactory函数:
@Override public void registerNetworkFactory(Messenger messenger, String name) { //权限检查 enforceConnectivityInternalPermission(); //构造NetworkFactoryInfo, 该变量用于在ConnectivityService中代表注册的NetworkFactory //注意Messager中,包含注册NetworkFactory内部的Handler NetworkFactoryInfo nfi = new NetworkFactoryInfo(name, messenger, new AsyncChannel()); //发送消息给给内部的Handler处理,将并行的接口调用变为串行的消息处理 mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_FACTORY, nfi)); }1
2
3
4
5
6
7
8
9
10
[/code]
ConnectivityService内部的handler将调用handleRegisterNetworkFactory处理EVENT_REGISTER_NETWORK_FACTORY消息:
............ case EVENT_REGISTER_NETWORK_FACTORY: { handleRegisterNetworkFactory((NetworkFactoryInfo)msg.obj); break; } ...........1
2
3
4
5
6
[/code]
private void handleRegisterNetworkFactory(NetworkFactoryInfo nfi) { if (DBG) log("Got NetworkFactory Messenger for " + nfi.name); //以键值对的方式,存储NetworkFactory的messenger和对应的NetworkFactoryInfo mNetworkFactoryInfos.put(nfi.messenger, nfi); //调用AsyncChannel的connect方法,将ConnectivityService的TrackerHandler与NetworkFactory的Messenger联系起来 nfi.asyncChannel.connect(mContext, mTrackerHandler, nfi.messenger); }1
2
3
4
5
6
7
[/code]
我们看看AsyncChannel的connect方法:
//此处传递的srcHandler是ConnectivityService的mTrackerHandler //dstMessenger为NetworkFactory内部的Messenger public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) { if (DBG) log("connect srcHandler to the dstMessenger E"); // We are connected connected(srcContext, srcHandler, dstMessenger); // Tell source we are half connected replyHalfConnected(STATUS_SUCCESSFUL); if (DBG) log("connect srcHandler to the dstMessenger X"); } public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) { if (DBG) log("connected srcHandler to the dstMessenger E"); // Initialize source fields mSrcContext = srcContext; mSrcHandler = srcHandler; mSrcMessenger = new Messenger(mSrcHandler); // Initialize destination fields mDstMessenger = dstMessenger; //监听目的端断开 linkToDeathMonitor(); if (DBG) log("connected srcHandler to the dstMessenger X"); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[/code]
看到connnected函数,我们知道每个NetworkFactory注册后,ConnectivityService将维护对应的NetworkFactoryInfo,对应的键值为NetworkFactory中的Messenger对象。
每个NetworkFactoryInfo中有一个AsyncChannel对象,该对象的源端Messenger包裹着ConnectivityService的mTrackerHandler,目的端Messenger为注册NetworkFactory的Messenger。
ConnectivityService这么做的目的是:简化ConnectivityService与每个NetworkFactory通信时的函数调用。
//这里发送CMD_CHANNEL_HALF_CONNECTED给ConnectivityService的mTrackerHandler //replyTo NetworkFactory中的Messenger private void replyHalfConnected(int status) { Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED); msg.arg1 = status; msg.obj = this; msg.replyTo = mDstMessenger; if (!linkToDeathMonitor()) { // Override status to indicate failure msg.arg1 = STATUS_BINDING_UNSUCCESSFUL; } mSrcHandler.sendMessage(msg); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
根据上面的代码,我们知道接下来应该是ConnectivityService的mTrackerHandler处理CMD_CHANNEL_HALF_CONNECTED事件。
mTrackerHandler的类型为ConnectivityService的内部类NetworkStateTrackerHandler:
private class NetworkStateTrackerHandler extends Handler { ........... @Override public void handleMessage(Message msg) { //这里的代码写法还是很赞的,特意看了Android6.0之前的代码 //在Android6.0之前,是在一个handleMessage中,根据msg.what使用大量的switch-case语句 //现在这样写,虽然本质上仍是大量的switch-case,但至少作了个分类 if (!maybeHandleAsyncChannelMessage(msg) && !maybeHandleNetworkMonitorMessage(msg)) { maybeHandleNetworkAgentMessage(msg); } } ....... }1
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
在这里我们看看maybeHandleAsyncChannelMessage:
private boolean maybeHandleAsyncChannelMessage(Message msg) { switch (msg.what) { //说实话这个代码写的辣眼睛 //自己写switch-case,一直将default写在最后,导致自己认为default是可以匹配所有的case //实际上,当剩余的case匹配不上时,default才会去匹配,default写在之前之后,没有关系 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; } private void handleAsyncChannelHalfConnect(Message msg) { AsyncChannel ac = (AsyncChannel) msg.obj; //从前面的代码,我们知道msg.replyTo为NetworkFactory对应的Messenger if (mNetworkFactoryInfos.containsKey(msg.replyTo)) { if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { if (VDBG) log("NetworkFactory connected"); // A network factory has connected. Send it all current NetworkRequests. //这里的NetworkRequest等下说明 for (NetworkRequestInfo nri : mNetworkRequests.values()) { //NetworkRequest分为监听网络用的,和申请网络用的 //仅处理申请网络用的 if (!nri.isRequest()) continue; //判断NetworkRequest是否有对应的NetworkAgentInfo //当一个网络建立后,将会生成对应的NetworkAgent注册到ConnectivityService //这里是取出NetworkRequest对应的NetworkAgentInfo NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId); //将CMD_REQUEST_NETWORK的消息发送给NetworkFactory,同时指定该request的分数 ac.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, (nai != null ? nai.getCurrentScore() : 0), 0, nri.request); } } else { loge("Error connecting NetworkFactory"); mNetworkFactoryInfos.remove(msg.obj); } } else if (mNetworkAgentInfos.containsKey(msg.replyTo)) { //这里是发送给NetworkAgent的消息,我们暂时不关注 ......... } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[/code]
上面的代码其实就是发消息给NetworkFactory。这里存在的疑点是:
1、开机时是否有NetworkRequest?
2、发送CMD_REQUEST_NETWORK时为什么要携带分数?
我们先来分析第一个问题:
//构造函数 public ConnectivityService(.......) { if (DBG) log("ConnectivityService starting up"); //初始时创建了DefaultRequest mDefaultRequest = createInternetRequestForTransport(-1); //创建对应的NetworkRequestInfo NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder(), NetworkRequestType.REQUEST); //按键值对存储 mNetworkRequests.put(mDefaultRequest, defaultNRI); mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI); ............ } //初始时,ConnectivityService创建NetworkRequest传入的参数为-1 private NetworkRequest createInternetRequestForTransport(int transportType) { NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED); if (transportType > -1) { netCap.addTransportType(transportType); } return new NetworkRequest(netCap, TYPE_NONE, nextNetworkRequestId()); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[/code]
从上面的代码,我们知道了开机后,ConnectivityService中默认就会存在一个NetworkRequest,于是每当一个新的NetworkFactory注册到ConnectivityService后,都会处理这个默认的NetworkRequest。
现在我们再来回答第二个问题,为什么发送CMD_REQUEST_NETWORK时,需要携带分数。
这个问题其实涉及了Android框架,管理整个网络的架构。具体的情况,今后介绍ConnectivityService时,会详细介绍。
在这里直接抛出近似的答案:Android通过ConnectivityService管理所有的网络,每一种网络都有各自的NetworkFactory。当NetworkFactory收到NetworkRequest后,将创建实际的网络对象。ConnectivityService用NetworkAgent和NetworkAgentInfo来抽象实际的网络对象的一些特性(还有其它的对象,例如Network,LinkProperties等共同抽象网络)。
然而,同一个设备在同一时间内不需要连接多个网络。比如说,用户的目的就是上网,但WiFi和移动网络均能满足用户上网的需求,因此设备没有必要同时连接WiFi和移动网络。因此,Android需要一种机制来权衡各种网络的是否应该保留。
为此android引入了分数的概念,当两个网络能同时满足一个NetworkRequest时,分数高者就留下。根据这个规则,我们其实也知道如果两个网络分别满足不同的NetworkRequest时,是可以共存的。
以上是基本的结论,今后分析ConnectivityService时,会分析对应的源码。
知道原理后,
现在回过头来看看发送CMD_REQUEST_NETWORK时,为什么要携带分数。
当消息发送给NetworkFactory后,NetworkFactory如果能满足NetworkRequest的需求,需要生成对应当NetworkAgent注册到ConnectivityService。如果这个新生成的NetworkAgent的分数,比之前满足NetworkRequest的已存在的NetworkAgent分数低,那么这个NetworkAgent将被处理掉。
因此,与其浪费资源生成一个无用的NetworkAgent,不如一开始就让NetworkFactory通过比较分数,不处理高分NetworkRequest。
这种设计类似于从声源阻止噪声吧!
回答完问题后,让我们继续回到之前ConnectivityService的流程,接下来看看AsyncChannel如何发送消息:
//一层层的sendMessage调用,最后会调用到这个sendMessage public void sendMessage(Message msg) { //根据前面的分析,我们知道ConnectivityService中NetworkFactoryInfo的AsyncChannel中 //mSrcMessenger中包裹的是mTrackerHandler msg.replyTo = mSrcMessenger; try { //DstMessenger中包裹的是NetworkFactory中的handler //binder通信,发往了NetworkFactory mDstMessenger.send(msg); } catch (RemoteException e) { replyDisconnected(STATUS_SEND_UNSUCCESSFUL); } }1
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
至此,消息终于又发回了PhoneSwitcher中创建的NetworkFactory,消息的处理由父类NetworkFactory处理:
@Override 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; } } } @VisibleForTesting protected void handleAddRequest(NetworkRequest request, int score) { //判断之前是否处理过该NetworkRequest 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); //对于处理过的NetworkRequest,仅更新器分数 //原因是:ConnectivityService中匹配该NetworkRequest的NetworkAgent可能变化,所以需要更新分数 n.score = score; } if (VDBG) log(" my score=" + mScore + ", my filter=" + mCapabilityFilter); //评估NetworkRequest evalRequest(n); } private void evalRequest(NetworkRequestInfo n) { if (VDBG) log("evalRequest"); //n.requested == false表示该request未被处理过 //n.score < mScore表示request的分数小于NetworkFactory的分数(子类定义) //satisfiedByNetworkCapabilities是为了判断NetworkFactory(子类)的网络能力能满足NetworkRequest //acceptRequest函数衡返回true,这是留给子类覆盖用的吧,一般没有覆盖 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); 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"); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
[/code]
绕了一圈,我们现在可以看看PhoneSwitcher中创建的NetworkFactory的needNetworkFor函数:
@Override protected void needNetworkFor(NetworkRequest networkRequest, int score) { if (VDBG) log("needNetworkFor " + networkRequest + ", " + score); Message msg = mPhoneSwitcher.obtainMessage(EVENT_REQUEST_NETWORK); msg.obj = networkRequest; //发送消息给自己的handler处理,将调用PhoneSwitcher中的onRequestNetwork处理 msg.sendToTarget(); }1
2
3
4
5
6
7
8
[/code]
private void onRequestNetwork(NetworkRequest networkRequest) { //利用NetworkRequest构造DcRequest final DcRequest dcRequest = new DcRequest(networkRequest, mContext); //之前没有处理过的dcRequest,才会进行处理 if (mPrioritizedDcRequests.contains(dcRequest) == false) { mPrioritizedDcRequests.add(dcRequest); //根据优先级进行排序 Collections.sort(mPrioritizedDcRequests); //评估,REQUESTS_CHANGED的值为true onEvaluate(REQUESTS_CHANGED, "netRequest"); } }1
2
3
4
5
6
7
8
9
10
11
12
[/code]
这里的唯一的问题就是“根据优先级排序”的概念。
我们花点时间来看看DcRequest这个类:
public class DcRequest implements Comparable<DcRequest> { .......... public DcRequest(NetworkRequest nr, Context context) { //初始化优先级排序规则 initApnPriorities(context); networkRequest = nr; //从NetworkReqeust得出对应的匹配APN id apnId = apnIdForNetworkRequest(networkRequest); //得到这个NetworkReqeust的优先级 priority = priorityForApnId(apnId); } ......... private void initApnPriorities(Context context) { synchronized (sApnPriorityMap) { //初始化优先级配置仅用执行一次 if (sApnPriorityMap.isEmpty()) { //解析xml文件,得到配置信息 String[] networkConfigStrings = context.getResources().getStringArray( com.android.internal.R.array.networkAttributes); for (String networkConfigString : networkConfigStrings) { NetworkConfig networkConfig = new NetworkConfig(networkConfigString); //从配置信息中,得到apnId final int apnId = ApnContext.apnIdForType(networkConfig.type); //以键值对的方式,将apnId和优先级信息放入到sApnPriorityMap中 sApnPriorityMap.put(apnId, networkConfig.priority); } } } } ......... private int apnIdForNetworkRequest(NetworkRequest nr) { NetworkCapabilities nc = nr.networkCapabilities; // For now, ignore the bandwidth stuff if (nc.getTransportTypes().length > 0 && nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == false) { return APN_INVALID_ID; } int apnId = APN_INVALID_ID; boolean error = false; //其实就是根据NetworkRequest申请的NetworkCapbility,来决定对应的apnId if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { if (apnId != APN_INVALID_ID) error = true; apnId = APN_DEFAULT_ID; } if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_MMS)) { if (apnId != APN_INVALID_ID) error = true; apnId = APN_MMS_ID; } ...... return apnId; } .......... public int compareTo(DcRequest o) { return o.priority - priority; } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
[/code]
从上面的代码,我们知道DcRequest其实就是根据xml中的配置文件得到每个Apn对应的优先级;然后,根据每个NetworkRequest需要的NetworkCapabilities得到对应的apnId;最终,根据apnId的优先级,完成对NetworkRequest的优先级排序。
其中,networkAttributes定义于frameworks/base/core/res/res/values/config.xml中:
<string-array translatable="false" name="networkAttributes"> <item>"wifi,1,1,1,-1,true"</item> <item>"mobile,0,0,0,-1,true"</item> <item>"mobile_mms,2,0,2,60000,true"</item> <item>"mobile_supl,3,0,2,60000,true"</item> <item>"mobile_hipri,5,0,3,60000,true"</item> <item>"mobile_fota,10,0,2,60000,true"</item> <item>"mobile_ims,11,0,2,60000,true"</item> <item>"mobile_cbs,12,0,2,60000,true"</item> <item>"wifi_p2p,13,1,0,-1,true"</item> <item>"mobile_ia,14,0,2,-1,true"</item> <item>"mobile_emergency,15,0,2,-1,true"</item> </string-array>1
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
根据NetworkConfig的构造函数,我们看出每一行的第4位表示优先级。结合DcReqeust中的compareTo函数,我们知道数字越大,优先级越高,例如mobile_mms的优先级要高于mobile:
public NetworkConfig(String init) { String fragments[] = init.split(","); name = fragments[0].trim().toLowerCase(Locale.ROOT); type = Integer.parseInt(fragments[1]); radio = Integer.parseInt(fragments[2]); priority = Integer.parseInt(fragments[3]); restoreTime = Integer.parseInt(fragments[4]); dependencyMet = Boolean.parseBoolean(fragments[5]); }1
2
3
4
5
6
7
8
9
[/code]
现在继续回到PhoneSwitcher处理NetworkRequest的流程中来,PhoneSwitcher根据优先级对NetworkRequest排序后,将调用onEvaluate方法:
private void onEvaluate(boolean requestsChanged, String reason) { ........ //此时,传入的参数为true boolean diffDetected = requestsChanged; //检测卡信息是否发生变化,我们此时分析不插卡的流程,暂不关注这个 //即使插了卡,也不影响当前流程 ........ if (diffDetected) { List<Integer> newActivePhones = new ArrayList<Integer>(); //按优先级依次处理DcRequest for (DcRequest dcRequest : mPrioritizedDcRequests) { //根据networkRequest中NetworkCapabilities携带的Specifier参数,决定phoneId int phoneIdForRequest = phoneIdForRequest(dcRequest.networkRequest); if (phoneIdForRequest == INVALID_PHONE_INDEX) continue; if (newActivePhones.contains(phoneIdForRequest)) continue; newActivePhones.add(phoneIdForRequest); //目前mMaxActivePhones的值为1,意味者找到优先级最高的DcRequest对应phone后,结束循环 if (newActivePhones.size() >= mMaxActivePhones) break; } .......... for (int phoneId = 0; phoneId < mNumPhones; phoneId++) { if (newActivePhones.contains(phoneId) == false) { //去激活 无法处理最高优先级DcRequest的phone 的数据拨号能力 deactivate(phoneId); } } // only activate phones up to the limit for (int phoneId : newActivePhones) { //激活 能处理最高优先级DcRequest的phone 的数据拨号能力 activate(phoneId); } } } private int phoneIdForRequest(NetworkRequest netRequest) { //在此流程中,specifier的值为空 //一般发送彩信,建立彩信网络时,才会指定specifier为对应卡的subId String specifier = netRequest.networkCapabilities.getNetworkSpecifier(); int subId; if (TextUtils.isEmpty(specifier)) { //为空时,取subId为默认数据卡的subId //我们分析不插卡的流程时,mDefaultDataSubscription为初始值0 subId = mDefaultDataSubscription; } else { subId = Integer.parseInt(specifier); } int phoneId = INVALID_PHONE_INDEX; if (subId == INVALID_SUBSCRIPTION_ID) return phoneId; for (int i = 0 ; i < mNumPhones; i++) { if (mPhoneSubscriptions[i] == subId) { //我们分析不插卡的流程时,phoneId最后取0 //实际插卡情况,将有specifier来具体决定 phoneId = i; break; } } return phoneId; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
[/code]
最后,我们来看看deactivate和activate函数:
private void deactivate(int phoneId) { PhoneState state = mPhoneStates[phoneId]; if (state.active == false) return; //记录对应phone的激活状态 state.active = false; log("deactivate " + phoneId); state.lastRequested = System.currentTimeMillis(); //mCommandsInterfaces[phoneId]其实就是RIL对象 //这里其实就是通过RIL发送消息给modem,取消对应phone的数据拨号能力 mCommandsInterfaces[phoneId].setDataAllowed(false, null); //通知观察者 mActivePhoneRegistrants[phoneId].notifyRegistrants(); } private void activate(int phoneId) { PhoneState state = mPhoneStates[phoneId]; if (state.active == true) return; state.active = true; log("activate " + phoneId); state.lastRequested = System.currentTimeMillis(); //同样,通过RIL发送消息 mCommandsInterfaces[phoneId].setDataAllowed(true, null); //通知观察者 mActivePhoneRegistrants[phoneId].notifyRegistrants(); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[/code]
PhoneSwitcher的上述流程,最终激活了一个Phone的数据拨号能力,完成拨号前的第一部分准备工作。
2.2 TelephonyNetworkFactory
PhoneSwitcher只是决定了那一个Phone具有拨号能力,在实际拨号前还需要激活可用的APN。这一部分工作需要TelephonyNetworkFactory来主动承担。
public class TelephonyNetworkFactory extends NetworkFactory { .......... public TelephonyNetworkFactory(.....) { super(looper, context, "TelephonyNetworkFactory[" + phoneId + "]", null); //创建内部的handler mInternalHandler = new InternalHandler(looper); //设定TelephonyNetworkFactory的网络能力,决定了它能处理的NetworkReqeust setCapabilityFilter(makeNetworkFilter(subscriptionController, phoneId)); //NetworkFactory的分数为50 setScoreFilter(TELEPHONY_NETWORK_SCORE); ............ //创建时,TelephonyNetworkFactory是非激活的 mIsActive = false; //是PhoneSwitcher的观察者 mPhoneSwitcher.registerForActivePhoneSwitch(mPhoneId, mInternalHandler, EVENT_ACTIVE_PHONE_SWITCH, null); //监听卡变化的情况 ............ //同样注册到ConnectivityService register(); } private NetworkCapabilities makeNetworkFilter(SubscriptionController subscriptionController, int phoneId) { final int subscriptionId = subscriptionController.getSubIdUsingPhoneId(phoneId); return makeNetworkFilter(subscriptionId); } //指定了TelephonyNetworkFactory的NetworkCapabilities private NetworkCapabilities makeNetworkFilter(int subscriptionId) { NetworkCapabilities nc = new NetworkCapabilities(); nc.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_MMS); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_SUPL); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_FOTA); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_CBS); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_IA); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_RCS); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_XCAP); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_EIMS); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); nc.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); nc.setNetworkSpecifier(String.valueOf(subscriptionId)); return nc; } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[/code]
从上面的代码我们知道,在初始时每个Phone对应的TelephonyNetworkFactory的激活态均是false,并且均注册到了ConnectivityService。于是,与PhoneSwitcher一样,TelephonyNetworkFactory也会收到ConnectivityService发送的NetworkReqeust请求。
由于ConnectivityService创建的DefaultRequest只要求Internet能力,并且在没有建立其它网络的条件下(没有连接WiFi等网络),DefaultRequest的分数为0,因此两个Phone对应的TelephonyNetworkFactory均会处理ConnectivityService发送的NetworkReqeust请求。
与PhoneSwitcher一样,我们看看TelephonyNetworkFactory的needNetworkFor函数:
@Override public void needNetworkFor(NetworkRequest networkRequest, int score) { //发送消息,由onNeedNetworkFor函数处理 Message msg = mInternalHandler.obtainMessage(EVENT_NETWORK_REQUEST); msg.obj = networkRequest; msg.sendToTarget(); } private void onNeedNetworkFor(Message msg) { NetworkRequest networkRequest = (NetworkRequest)msg.obj; boolean isApplicable = false; LocalLog localLog = null; //当NetworkReqeust的specifier为空时 if (TextUtils.isEmpty(networkRequest.networkCapabilities.getNetworkSpecifier())) { // request only for the default network localLog = mDefaultRequests.get(networkRequest); if (localLog == null) { localLog = new LocalLog(REQUEST_LOG_SIZE); localLog.log("created for " + networkRequest); mDefaultRequests.put(networkRequest, localLog); //只有default phone才能处理这个NetworkReqeust isApplicable = mIsDefault; } } else { //当NetworkReqeust的specifier不为空时,只要之前没有处理过,就可以处理 //因为在收到NetworkRequest时,父类的evalRequest中,已经通过NetworkCapabilities匹配过了 localLog = mSpecificRequests.get(networkRequest); if (localLog == null) { localLog = new LocalLog(REQUEST_LOG_SIZE); mSpecificRequests.put(networkRequest, localLog); isApplicable = true; } } //只有TelephonyNetworkFactory处于激活态,并且能够处理时,才进入该分支 if (mIsActive && isApplicable) { String s = "onNeedNetworkFor"; localLog.log(s); log(s + " " + networkRequest); mDcTracker.requestNetwork(networkRequest, localLog); } else { String s = "not acting - isApp=" + isApplicable + ", isAct=" + mIsActive; localLog.log(s); log(s + " " + networkRequest); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
[/code]
从上面的代码我们知道,在TelephonyNetworkFactory被激活前,它收到NetworkRequest的请求,但不会进行实际操作,直到PhoneSwitcher完成了激活Phone拨号能力的操作。
在TelephonyNetworkFactory的构造函数中,向PhoneSwitcher注册了观察对象,当发现Phone的激活态改变后,将向内部handler发送EVENT_ACTIVE_PHONE_SWITCH消息。内部handler收到消息后,将调用onActivePhoneSwitch进行处理。
private void onActivePhoneSwitch() { //根据PhoneSwitcher中记录的phoneState决定自己是否active final boolean newIsActive = mPhoneSwitcher.isPhoneActive(mPhoneId); //自己的active状态改变后,处理request if (mIsActive != newIsActive) { mIsActive = newIsActive; String logString = "onActivePhoneSwitch(" + mIsActive + ", " + mIsDefault + ")"; if (DBG) log(logString); //mIsActive的值,决定了requestNetwork,还是releaseNetwork //android7.0中,defaultRequest与specificRequest分别装在了不同的map中 if (mIsDefault) { applyRequests(mDefaultRequests, (mIsActive ? REQUEST : RELEASE), logString); } applyRequests(mSpecificRequests, (mIsActive ? REQUEST : RELEASE), logString); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[/code]
从上面的代码,可以看出当PhoneSwitcher改变TelephonyNetworkFactory的激活状态后,TelephonyNetworkFactory将根据各自的状态,调用applyRequests函数:
private void applyRequests(HashMap<NetworkRequest, LocalLog> requestMap, boolean action, String logStr) { for (NetworkRequest networkRequest : requestMap.keySet()) { LocalLog localLog = requestMap.get(networkRequest); localLog.log(logStr); if (action == REQUEST) { //我们关注active phone的操作 mDcTracker.requestNetwork(networkRequest, localLog); } else { mDcTracker.releaseNetwork(networkRequest, localLog); } } }1
2
3
4
5
6
7
8
9
10
11
12
13
[/code]
根据上面的代码,我们知道了流程走到了DcTracker。
DcTracker是PhoneFactory创建GsmCdmaPhone时,在GsmCdmaPhone内部创建的,与TelephonyNetworkFactory一样,每个phone对象持有一个。
public GsmCdmaPhone(.....) { ........ mDcTracker = mTelephonyComponentFactory.makeDcTracker(this); ........ }1
2
3
4
5
[/code]
TelephonyComponentFactory是一个单例对象:
public static TelephonyComponentFactory getInstance() { if (sInstance == null) { sInstance = new TelephonyComponentFactory(); } return sInstance; } public DcTracker makeDcTracker(Phone phone) { return new DcTracker(phone); }1
2
3
4
5
6
7
8
9
10
[/code]
我们回到拨号前的准备流程,看看DcTracker的requestNetwork函数:
public void requestNetwork(NetworkRequest networkRequest, LocalLog log) { //与上文中定义APN优先级一样,根据NetworkRequest的NetworkCapabilities得到对应的ApnId final int apnId = ApnContext.apnIdForNetworkRequest(networkRequest); //判断DcTracker能否处理该APN final ApnContext apnContext = mApnContextsById.get(apnId); log.log("DcTracker.requestNetwork for " + networkRequest + " found " + apnContext); //能够处理时,就调用ApnContext的incRefCount方法 if (apnContext != null) apnContext.incRefCount(log); }1
2
3
4
5
6
7
8
9
[/code]
上面的代码中唯一的疑点是mApnContextsById是如何定义的?这就必须提及DcTracker的构造函数了。
public DcTracker(Phone phone) { ....... //初始化APN Context initApnContexts(); ....... } private void initApnContexts() { String[] networkConfigStrings = mPhone.getContext().getResources().getStringArray( com.android.internal.R.array.networkAttributes); for (String networkConfigString : networkConfigStrings) { NetworkConfig networkConfig = new NetworkConfig(networkConfigString); ApnContext apnContext = null; switch (networkConfig.type) { case ConnectivityManager.TYPE_MOBILE: apnContext = addApnContext(PhoneConstants.APN_TYPE_DEFAULT, networkConfig); break; case ConnectivityManager.TYPE_MOBILE_MMS: apnContext = addApnContext(PhoneConstants.APN_TYPE_MMS, networkConfig); break; ...... } .......... } private ApnContext addApnContext(String type, NetworkConfig networkConfig) { ApnContext apnContext = new ApnContext(mPhone, type, LOG_TAG, networkConfig, this); mApnContexts.put(type, apnContext); mApnContextsById.put(ApnContext.apnIdForApnName(type), apnContext); mPrioritySortedApnContexts.add(apnContext); return apnContext; }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[/code]
从上面的代码可以看出,mApnContextsById中的Apn记录也是从config.xml中读取出来的,因此ApnContext的incRefCount方法可以得到执行:
public void incRefCount(LocalLog log) { synchronized (mRefCountLock) { if (mLocalLogs.contains(log)) { log.log("ApnContext.incRefCount has duplicate add - " + mRefCount); } else { mLocalLogs.add(log); log.log("ApnContext.incRefCount - " + mRefCount); } //每种APN类型只激活一次 if (mRefCount++ == 0) { //这里重新调用DcTracker的setEnabled函数,传入的参数之一是可用的ApnId //也就是说这一类APN是可以使用的,例如如果激活的ApnId为DctConstants.APN_DEFAULT_ID //那么所有具有default属性的APN均可以使用了 mDcTracker.setEnabled(apnIdForApnName(mApnType), true); } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[/code]
最后我们来看看DcTracker的setEnabled函数。
public void setEnabled(int id, boolean enable) { //发送消息给内部handler处理,将调用onEnableApn进行处理 Message msg = obtainMessage(DctConstants.EVENT_ENABLE_NEW_APN); msg.arg1 = id; msg.arg2 = (enable ? DctConstants.ENABLED : DctConstants.DISABLED); sendMessage(msg); } private void onEnableApn(int apnId, int enabled) { ApnContext apnContext = mApnContextsById.get(apnId); if (apnContext == null) { loge("onEnableApn(" + apnId + ", " + enabled + "): NO ApnContext"); return; } // TODO change our retry manager to use the appropriate numbers for the new APN if (DBG) log("onEnableApn: apnContext=" + apnContext + " call applyNewState"); //更新apnContext的state //DependencyMet由config.xml得到,均为true applyNewState(apnContext, enabled == DctConstants.ENABLED, apnContext.getDependencyMet()); } private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) { boolean cleanup = false; boolean trySetup = false; ........ //初始时,apnContext not ready if (apnContext.isReady()) { ...... } else { //传入参数中,enabled与met均为true if (enabled && met) { if (apnContext.isEnabled()) { apnContext.setReason(Phone.REASON_DATA_DEPENDENCY_MET); } else { apnContext.setReason(Phone.REASON_DATA_ENABLED); } if (apnContext.getState() == DctConstants.State.FAILED) { apnContext.setState(DctConstants.State.IDLE); } trySetup = true; } } //完成准备工作!!!拨号依赖于此条件 apnContext.setEnabled(enabled); apnContext.setDependencyMet(met); if (cleanup) cleanUpConnection(true, apnContext); if (trySetup) { apnContext.resetErrorCodeRetries(); //会触发拨号操作,当然此时没插卡,数据业务开关也没打开,不会通过RIL发送消息给modem trySetupData(apnContext); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[/code]
至此,TelephonyNetworkFactory再次完成了一部分数据拨号前的准备工作,激活了一个类型的APN。
3 DcTracker装载卡对应的APN信息
从这一节的标题就知道,我们要开始分析插入卡后的流程了,毕竟拨号是离不开SIM卡的(网络运营商提供的虚拟卡暂不考虑)。
我们先看看框架是如何知道手机中插入了卡。
卡实际上可以看做一种硬件设备,当卡插入手机后,会产生相应的电信号,触发相应的驱动工作。当然,这不是我们关注的重点,我们只需要知道的是,当检测到卡后,modem将主动上报RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED事件。
RIL收到该事件后,将通知其观察者:
case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: if (RILJ_LOGD) unsljLog(response); if (mIccStatusChangedRegistrants != null) { mIccStatusChangedRegistrants.notifyRegistrants(); } break;1
2
3
4
5
6
7
[/code]
那么谁是该事件的观察者呢?
我们知道在PhoneFactory的makeDefaultPhone中,创建过UiccController,看看UiccController构造函数的片段:
private UiccController(Context c, CommandsInterface []ci) { .......... for (int i = 0; i < mCis.length; i++) { Integer index = new Integer(i); //从这里可以看出UiccController将监听IccStatusChanged事件 //监听到IccStatusChanged发送EVENT_ICC_STATUS_CHANGED消息给自己处理 mCis[i].registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, index); ............. } }1
2
3
4
5
6
7
8
9
10
[/code]
@Override public void handleMessage (Message msg) { ....... switch (msg.what) { case EVENT_ICC_STATUS_CHANGED: if (DBG) log("Received EVENT_ICC_STATUS_CHANGED, calling getIccCardStatus"); //利用RIL从modem获取卡信息,获取到卡信息后,发送EVENT_GET_ICC_STATUS_DONE给自己处理 mCis[index].getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE, index)); break; case EVENT_GET_ICC_STATUS_DONE: if (DBG) log("Received EVENT_GET_ICC_STATUS_DONE"); //获取到卡信息后,进行处理 onGetIccCardStatusDone(ar, index); break; ...... } ....... } private synchronized void onGetIccCardStatusDone(AsyncResult ar, Integer index) { ........ IccCardStatus status = (IccCardStatus)ar.result; //利用获取的卡信息,形成抽象的卡对象 if (mUiccCards[index] == null) { //Create new card mUiccCards[index] = new UiccCard(mContext, mCis[index], status, index); } else { //Update already existing card mUiccCards[index].update(mContext, mCis[index] , status); } if (DBG) log("Notifying IccChangedRegistrants"); //通知自己的观察者 mIccChangedRegistrants.notifyRegistrants(new AsyncResult(null, index, null)); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[/code]
UiccController的观察者比较多,其中就有我们关注的DcTracker,在DcTracker的构造函数中,就有如下片段:
public DcTracker(Phone phone) { ....... mUiccController = UiccController.getInstance(); //收到UiccController的通知后,将发送EVENT_ICC_CHANGED给自己处理,调用函数为onUpdateIcc mUiccController.registerForIccChanged(this, DctConstants.EVENT_ICC_CHANGED, null); ....... } private void onUpdateIcc() { if (mUiccController == null ) { return; } //利用UiccController获取卡信息 IccRecords newIccRecords = getUiccRecords(UiccController.APP_FAM_3GPP); IccRecords r = mIccRecords.get(); if (r != newIccRecords) { if (r != null) { log("Removing stale icc objects."); r.unregisterForRecordsLoaded(this); mIccRecords.set(null); } if (newIccRecords != null) { if (SubscriptionManager.isValidSubscriptionId(mPhone.getSubId())) { log("New records found."); mIccRecords.set(newIccRecords); //监听卡信息是否载入完成,IccRecords的子类负责通知 newIccRecords.registerForRecordsLoaded( this, DctConstants.EVENT_RECORDS_LOADED, null); SubscriptionController.getInstance().setSimProvisioningStatus( SubscriptionManager.SIM_PROVISIONED, mPhone.getSubId()); } } else { onSimNotReady(); } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[/code]
当DcTracker监听到卡信息载入完成后,处理EVENT_RECORDS_LOADED事件:
.......... case DctConstants.EVENT_RECORDS_LOADED: // If onRecordsLoadedOrSubIdChanged() is not called here, it should be called on // onSubscriptionsChanged() when a valid subId is available. int subId = mPhone.getSubId(); //卡载入完成,应该已经有了有效的subId if (SubscriptionManager.isValidSubscriptionId(subId)) { //调用该函数 onRecordsLoadedOrSubIdChanged(); } else { log("Ignoring EVENT_RECORDS_LOADED as subId is not valid: " + subId); } break; ........1
2
3
4
5
6
7
8
9
10
11
12
13
14
[/code]
private void onRecordsLoadedOrSubIdChanged() { ............ //创建APN list createAllApnList(); //从所有可用APN中,选择一个APN通过RIL发送给modem //选择顺序是IA(支持IA类型的APN)、prefer(用户选择的)、 default(支持default类型的)、 first(mApnSettings中的第一个) setInitialAttachApn(); //判断modem的radio是否处于开启状态 if (mPhone.mCi.getRadioState().isOn()) { if (DBG) log("onRecordsLoadedOrSubIdChanged: notifying data availability"); //发送一个通知而已 notifyOffApnsOfAvailability(Phone.REASON_SIM_LOADED); } //进行数据拨号,注意此时数据业务开关如果是开的,才能利用RIL发送信息给modem setupDataOnConnectableApns(Phone.REASON_SIM_LOADED); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[/code]
我们看看createAllApnList函数:
private void createAllApnList() { ....... mAllApnSettings = new ArrayList<ApnSetting>(); IccRecords r = mIccRecords.get(); String operator = (r != null) ? r.getOperatorNumeric() : ""; String operator = (r != null) ? r.getOperatorNumeric() : ""; if (operator != null) { //根据运营商构造selection语句 String selection = "numeric = '" + operator + "'"; String orderBy = "_id"; //查询数据库 Cursor cursor = mPhone.getContext().getContentResolver().query( Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy); if (cursor != null) { if (cursor.getCount() > 0) { //该函数内部利用makeApnSettings函数,根据数据库字段,构造当前卡可以使用的Apn mAllApnSettings = createApnList(cursor); } cursor.close(); } //增加紧急拨号类型的APN addEmergencyApnSetting(); //删除重复的APN dedupeApnSettings(); if (mAllApnSettings.isEmpty()) { mPreferredApn = null; } else { //获取prefer APN(即优先使用的APN) //根据subId匹配,将数据库中对应的APN按名称ASC(升序)排列后,取出数据库中第一个APN的id //轮询当前卡的AllApnSettings,匹配id并且能满足NetworkRequest要求的APN就是prefer APN //用户不设置的话,初始时应该是没有的 mPreferredApn = getPreferredApn(); //优选APN不适用于当期运营商的话,则不设定优选APN if (mPreferredApn != null && !mPreferredApn.numeric.equals(operator)) { mPreferredApn = null; setPreferredApn(-1); } } } //调用RIL的接口setDataProfile,向modem发送dataProfie,即发送当期卡可用的APN信息给modem setDataProfilesAsNeeded(); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
[/code]
至此,我们做好了数据拨号前的所有工作,用户点击数据业务开关后,就可以开始拨号流程了。
当然上述过程过于繁杂,涉及的细节比较多,因此还是需要整理一下流程图和类图:
流程图对应链接
类图对应链接
结束语
以上的流程图和类图都是比较简易的。
实际终端的开机过程,可能和流程图存在一定时序上的偏差,但大致流程应该一致;类图并没有严格分析每个类之间的关系,仅仅描述了主要类的关系。
最后需要说明的是,目前的逻辑都是按照Android 7.0原生流程来分析的,实际终端的软件版本应该与此有些差异。不论MTK还是Qualcomm应该都会根据自己的芯片能力,对原生流程进行修改。
举例来说,原生流程中,PhoneSwitcher直接通过setDataAllowed激活Phone的数据拨号能力,然后立马就通知TelephonyNetworkFactory。
这实在是高估了底层芯片的能力,自己目前还没有拿到Qualcomm和MTK的代码,但觉得这里应该会像Android6.0一样,必须等底层芯片返回数据能力设置成功的信息后,才会通知TelephonyNetworkFactory。甚至考虑到底层的异常,厂商应该还是会引入状态机进行控制。
原文地址:http://blog.csdn.net/gaugamela/article/details/52325196
相关文章推荐
- Android7.0 数据拨号前的准备工作
- Android7.0 数据拨号前的准备工作
- 【Android浏览器插件开发准备工作之NPAPI】使用Visual Studio 2008 运行Firefox浏览器插件实例npruntime
- android项目开发准备工作
- android系统开发(九)-Audio部分准备工作alsa移植
- 亮仔的 Android 笔记第一章第零节:Android Application 开发的准备工作
- Android日历开发:日历显示需要准备哪些数据
- JDBC--数据的查询准备工作
- Android 7.0 Gallery图库源码分析3 - 数据加载及显示流程
- 【Android】用Eclipse和robotium-solo测试Android APP前的准备工作
- Android 7.0 BroadcastReceiver接收pendingIntent 传递过来序列化数据
- Android 7.0 Vold工作流程
- Android项目开发前准备工作(二)
- Android编译准备工作
- Android开发(11)-利用listview控件显示person表中的所有数据(并实现点击触发拨号程序)
- android系统开发(九)-Audio部分准备工作alsa移植
- Android 7.0 以上保留数据去除锁屏密码的方法
- Android开发(11)-利用listview控件显示person表中的所有数据(并实现点击触发拨号程序)