IMS(IP Multimedia Subsystem)被认为是下一代网络的核心技术,是解决移动与固网融合,引入语音、数据、视频三重融合等差异化业务的重要方式。Android作为移动网络终端的主要操作系统,也提供了对IMS的支持。
本篇博客的目的就是弄清楚Android中的IMS是如何完成开机初始化的。
一、监控IMS Service
之前的博客中已经分析过PhoneApp的启动过程(Android7.0 PhoneApp的启动),同时在分析数据业务基础类创建的过程时(Android7.0 数据业务基础类的创建),已经提到了PhoneFactory中的makeDefaultPhone函数。
makeDefaultPhone函数负责启动对IMS Service的监控,其中相关的代码如下:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">makeDefaultPhone</span>(Context context) { ........ <span class="hljs-comment">// Start monitoring after defaults have been made.</span> <span class="hljs-comment">// Default phone must be ready before ImsPhone is created</span> <span class="hljs-comment">// because ImsService might need it when it is being opened.</span> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i < numPhones; i++) { <span class="hljs-comment">//sPhones为静态数组,持有实际的GsmCdmaPhone对象</span> sPhones[i].startMonitoringImsService(); } ........ }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul>
startMonitoringImsService将启动对IMS Service的监控,该函数定义于GsmCdmaPhone的父对象Phone中:
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">startMonitoringImsService</span>() { <span class="hljs-comment">//正常情况下,应该是GSM或CDMA类型</span> <span class="hljs-keyword">if</span> (getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) { <span class="hljs-keyword">return</span>; } <span class="hljs-keyword">synchronized</span>(Phone.lockForRadioTechnologyChange) { IntentFilter filter = <span class="hljs-keyword">new</span> IntentFilter(); filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP); filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN); <span class="hljs-comment">//利用mImsIntentReceiver监听IMS服务启动和关闭的广播</span> mContext.registerReceiver(mImsIntentReceiver, filter); <span class="hljs-comment">// Monitor IMS service - but first poll to see if already up (could miss</span> <span class="hljs-comment">// intent)</span> <span class="hljs-comment">//1 、获取ImsManager</span> ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId()); <span class="hljs-comment">//2、 判断ImsService是否已经启动,初始时未启动,略过下文</span> <span class="hljs-keyword">if</span> (imsManager != <span class="hljs-keyword">null</span> && imsManager.isServiceAvailable()) { mImsServiceReady = <span class="hljs-keyword">true</span>; updateImsPhone(); ImsManager.updateImsServiceConfig(mContext, mPhoneId, <span class="hljs-keyword">false</span>); } } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li></ul>
从上面的代码可以看出,所谓的监控IMS Service,实际上指的是监听IMS Service启动和关闭时的广播。
1、获取ImsManager
我们先看看如何获取ImsManager:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ImsManager <span class="hljs-title">getInstance</span>(Context context, <span class="hljs-keyword">int</span> phoneId) { synchronized (sImsManagerInstances) { <span class="hljs-comment">//sImsManagerInstances为一个静态的HashMap</span> <span class="hljs-keyword">if</span> (sImsManagerInstances.containsKey(phoneId)) <span class="hljs-keyword">return</span> sImsManagerInstances.<span class="hljs-keyword">get</span>(phoneId); ImsManager mgr = <span class="hljs-keyword">new</span> ImsManager(context, phoneId); sImsManagerInstances.put(phoneId, mgr); <span class="hljs-keyword">return</span> mgr; } } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul>
从上面的代码来看,我们知道每个Phone都有对应的ImsManager对象。
跟进一下ImsManager的构造函数:
<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-title">ImsManager</span>(Context context, <span class="hljs-keyword">int</span> phoneId) { mContext = context; mPhoneId = phoneId; createImsService(<span class="hljs-keyword">true</span>); } <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">createImsService</span>(<span class="hljs-keyword">boolean</span> checkService) { <span class="hljs-keyword">if</span> (checkService) { <span class="hljs-comment">//如果ImsService启动了,就会将自己注册到service_manager进程中</span> IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); <span class="hljs-keyword">if</span> (binder == <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">return</span>; } <span class="hljs-comment">//如果ImsService已经注册,获取对应的Binder对象</span> IBinder b = ServiceManager.getService(getImsServiceName(mPhoneId)); <span class="hljs-keyword">if</span> (b != <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">try</span> { <span class="hljs-comment">//注册一个“讣告”观察者,当ImsService的Binder dead时,</span> <span class="hljs-comment">//mDeathRecipient将发送ACTION_IMS_SERVICE_DOWN的广播信息</span> b.linkToDeath(mDeathRecipient, <span class="hljs-number">0</span>); } <span class="hljs-keyword">catch</span> (RemoteException e) { } } <span class="hljs-comment">//将binder对象转化为ImsService的Proxy</span> mImsService = IImsService.Stub.asInterface(b); } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li></ul>
2、判断IMS Service是否可用
接下来跟进一下isServiceAvailable函数:
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isServiceAvailable</span>() { <span class="hljs-keyword">if</span> (mImsService != <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>; } IBinder binder = ServiceManager.checkService(getImsServiceName(mPhoneId)); <span class="hljs-keyword">if</span> (binder != <span class="hljs-keyword">null</span>) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>; } <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>; }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li></ul>
从代码可以看出,isServiceAvailable同样是根据ImsService是否注册到service manager进程,来判断ImsService是否可用。
二、IMS Service的结构
如上图所示,在Android的原生代码里,定义了抽象类ImsServiceBase,在该类中定义了内部类ImsServiceBinder。
ImsServiceBinder类实现了ImsService.aidl定义的接口,负责为ImsManager的调用提供服务。
实际上,ImsServiceBinder提供的所有函数并没有完成实际的功能,而是转调用ImsServiceBase中的定义的函数。按照Android的提供代码的结构,ImsServiceBase中定义的函数,将由其子类ImsService来实现。
也就是说经过几次转换,ImsManager的调用接口时,将由ImsService来提供具体的服务。
然而Android原生代码中并没有实现ImsService,仅仅定义了上面描述的继承和通信的结构。可能由于保密的原因,或者由于ImsService与HAL层强相关,ImsService一般由实际的芯片厂商来实现。
因此,在这里我们无法分析原生的ImsService启动过程。我们就以Qualcomm启动ImsService的流程为例,简单介绍一下整体的思路。
需要提出的是,在Qualcomm的设计中,ImsService并没有继承ImsServiceBase,而是直接用ImsService替换掉了ImsServiceBase,它的代码结构如下(MTK中ImsService直接继承ImsService.Stub,连ImsServiceBase.Java都去掉了):
在Qualcomm的代码中,在vendor目录下定义了一个IMS相关的APK。在该APK中,定义了一个常驻的BroadcastReceiver。
当该广播监听器,收到ACTION_BOOT_COMPLETED或ACTION_SIM_STATE_CHANGED消息时,就会发送Intent拉起Qualcomm定义的ImsService。
ImsService启动后,将自己注册到service manager进程,然后进一步创建IMS服务所需的对象,及注册一些观察者监听modem上报的事件,同时发送ACTION_IMS_SERVICE_UP和ACTION_IMS_SERVICE_DOWN的广播。
在第一部分”监控IMS Service”中,我们已经提到在Phone对象中创建了广播监听器,用于监听IMS服务的广播。
由于同时只有一个Phone具备IMS能力,因此IMS会发送两个广播并携带对应的PHONE_ID。
于是,一个Phone将处理ACTION_IMS_SERVICE_UP;另一个Phone将处理ACTION_IMS_SERVICE_DOWN。
以上的流程是属于Qualcomm的,并不是开源代码,为了遵守保密协议,这里就不附上实际的代码了。
三、ImsPhone的创建
ImsService发送广播后,我们回到Phone.java中看看对应的广播接收器如何处理:
<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> BroadcastReceiver mImsIntentReceiver = <span class="hljs-keyword">new</span> BroadcastReceiver() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onReceive</span>(Context context, Intent intent) { Rlog.d(LOG_TAG, <span class="hljs-string">"mImsIntentReceiver: action "</span> + intent.getAction()); <span class="hljs-keyword">if</span> (intent.hasExtra(ImsManager.EXTRA_PHONE_ID)) { <span class="hljs-comment">//取出Intent中的Phone_ID</span> <span class="hljs-keyword">int</span> extraPhoneId = intent.getIntExtra(ImsManager.EXTRA_PHONE_ID, SubscriptionManager.INVALID_PHONE_INDEX); Rlog.d(LOG_TAG, <span class="hljs-string">"mImsIntentReceiver: extraPhoneId = "</span> + extraPhoneId); <span class="hljs-comment">//Phone_ID与当前的Phone不符时,不做处理</span> <span class="hljs-keyword">if</span> (extraPhoneId == SubscriptionManager.INVALID_PHONE_INDEX || extraPhoneId != getPhoneId()) { <span class="hljs-keyword">return</span>; } } <span class="hljs-keyword">synchronized</span> (Phone.lockForRadioTechnologyChange) { <span class="hljs-comment">//按情况修改mImsServiceReady变量</span> <span class="hljs-keyword">if</span> (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) { mImsServiceReady = <span class="hljs-keyword">true</span>; updateImsPhone(); ImsManager.updateImsServiceConfig(mContext, mPhoneId, <span class="hljs-keyword">false</span>); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) { mImsServiceReady = <span class="hljs-keyword">false</span>; updateImsPhone(); } } } };</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li></ul>
从上面的代码来看收到广播后需要调用updateImsPhone函数;只有收到ACTION_IMS_SERVICE_UP消息时,才会调用updateImsServiceConfig函数。
接下来我们分别看看updateImsPhone和updateImsServiceConfig。
1、updateImsPhone
<code class="hljs lasso has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-literal">void</span> updateImsPhone() { <span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span> <span class="hljs-keyword">if</span> (mImsServiceReady <span class="hljs-subst">&&</span> (mImsPhone <span class="hljs-subst">==</span> <span class="hljs-built_in">null</span>)) { <span class="hljs-comment">//收到ACTION_IMS_SERVICE_UP时,创建ImsPhone</span> <span class="hljs-comment">//ImsPhone继承自ImsPhoneBase,后者继承自Phone</span> mImsPhone <span class="hljs-subst">=</span> PhoneFactory<span class="hljs-built_in">.</span>makeImsPhone(mNotifier, this); <span class="hljs-comment">//完成拨号相关的注册</span> CallManager<span class="hljs-built_in">.</span>getInstance()<span class="hljs-built_in">.</span>registerPhone(mImsPhone); mImsPhone<span class="hljs-built_in">.</span>registerForSilentRedial( this, EVENT_INITIATE_SILENT_REDIAL, <span class="hljs-built_in">null</span>); } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-subst">!</span>mImsServiceReady <span class="hljs-subst">&&</span> (mImsPhone <span class="hljs-subst">!=</span> <span class="hljs-built_in">null</span>)) { <span class="hljs-comment">//如果之前创建过ImsPhone的phone,收到ACTION_IMS_SERVICE_DOWN时</span> <span class="hljs-comment">//注销ImsPhone</span> CallManager<span class="hljs-built_in">.</span>getInstance()<span class="hljs-built_in">.</span>unregisterPhone(mImsPhone); mImsPhone<span class="hljs-built_in">.</span>unregisterForSilentRedial(this); mImsPhone<span class="hljs-built_in">.</span>dispose(); <span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-built_in">.</span> mImsPhone <span class="hljs-subst">=</span> <span class="hljs-built_in">null</span>; } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul>
我们跟进一下ImsPhone的创建过程:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Phone <span class="hljs-title">makeImsPhone</span>(PhoneNotifier phoneNotifier, Phone defaultPhone) { <span class="hljs-comment">//PhoneFactory实际上调用ImsPhoneFactory的静态函数</span> <span class="hljs-keyword">return</span> ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone); }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li></ul>
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ImsPhone <span class="hljs-title">makePhone</span>(Context context, PhoneNotifier phoneNotifier, Phone defaultPhone) { <span class="hljs-keyword">try</span> { <span class="hljs-comment">//ImsPhoneFactory直接调用ImsPhone的构造函数</span> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ImsPhone(context, phoneNotifier, defaultPhone); } <span class="hljs-keyword">catch</span> (Exception e) { Rlog.e(<span class="hljs-string">"VoltePhoneFactory"</span>, <span class="hljs-string">"makePhone"</span>, e); <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>; } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>
最后看一下ImsPhone的构造函数:
<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-title">ImsPhone</span>(Context context, PhoneNotifier notifier, Phone defaultPhone) { <span class="hljs-keyword">this</span>(context, notifier, defaultPhone, <span class="hljs-keyword">false</span>); } <span class="hljs-annotation">@VisibleForTesting</span> <span class="hljs-keyword">public</span> <span class="hljs-title">ImsPhone</span>(Context context, PhoneNotifier notifier, Phone defaultPhone, <span class="hljs-keyword">boolean</span> unitTestMode) { <span class="hljs-keyword">super</span>(<span class="hljs-string">"ImsPhone"</span>, context, notifier, unitTestMode); mDefaultPhone = defaultPhone; <span class="hljs-comment">//定义Ims拨号对应的Call Tracker</span> mCT = TelephonyComponentFactory.getInstance().makeImsPhoneCallTracker(<span class="hljs-keyword">this</span>); mExternalCallTracker = TelephonyComponentFactory.getInstance().makeImsExternalCallTracker(<span class="hljs-keyword">this</span>, mCT); <span class="hljs-keyword">try</span> { <span class="hljs-comment">//定义Call State Listener</span> mImsMultiEndpoint = mCT.getMultiEndpointInterface(); mImsMultiEndpoint.setExternalCallStateListener( mExternalCallTracker.getExternalCallStateListener()); } <span class="hljs-keyword">catch</span> (ImsException e) { Rlog.i(LOG_TAG, <span class="hljs-string">"ImsMultiEndpointInterface is not available."</span>); } <span class="hljs-comment">//将ImsPhone的service复位</span> mSS.setStateOff(); mPhoneId = mDefaultPhone.getPhoneId(); ........... <span class="hljs-comment">//ImsPhone利用对应的defaultPhone监听数据注册状态和无线技术的变化</span> <span class="hljs-keyword">if</span> (mDefaultPhone.getServiceStateTracker() != <span class="hljs-keyword">null</span>) { mDefaultPhone.getServiceStateTracker() .registerForDataRegStateOrRatChanged(<span class="hljs-keyword">this</span>, EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, <span class="hljs-keyword">null</span>); } <span class="hljs-comment">//利用defaultPhone更新mSS,得到数据服务的注册状态及无线技术</span> updateDataServiceState(); }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li></ul>
以上是ImsPhone创建的基本流程。
IMS依赖于数据业务,并为语音业务提供服务。今后分析语音业务时,会再进一步分析IMS。
2、updateImsServiceConfig
如上文所述,当Phone收到ACTION_IMS_SERVICE_UP时,会调用ImsManager的updateImsServiceConfig函数:
<code class="hljs java has-numbering"><span class="hljs-comment">//force的值为false</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">updateImsServiceConfig</span>(Context context, <span class="hljs-keyword">int</span> phoneId, <span class="hljs-keyword">boolean</span> force) { <span class="hljs-comment">//当不是强制启动时,必须保证对应的卡就绪</span> <span class="hljs-keyword">if</span> (!force) { <span class="hljs-keyword">if</span> (TelephonyManager.getDefault().getSimState() != TelephonyManager.SIM_STATE_READY) { log(<span class="hljs-string">"updateImsServiceConfig: SIM not ready"</span>); <span class="hljs-comment">// Don't disable IMS if SIM is not ready</span> <span class="hljs-keyword">return</span>; } } <span class="hljs-keyword">final</span> ImsManager imsManager = ImsManager.getInstance(context, phoneId); <span class="hljs-comment">//初始时ImsManager的mConfigUpdated为false</span> <span class="hljs-keyword">if</span> (imsManager != <span class="hljs-keyword">null</span> && (!imsManager.mConfigUpdated || force)) { <span class="hljs-keyword">try</span> { } <span class="hljs-keyword">catch</span> (ImsException e) { loge(<span class="hljs-string">"updateImsServiceConfig: "</span> + e); imsManager.mConfigUpdated = <span class="hljs-keyword">false</span>; } <span class="hljs-comment">//IMS包括语音VoLTE、视频VideoCall和WFC(WiFiCall)</span> <span class="hljs-comment">//当三种中的一种可用时,就可以使用IMS</span> <span class="hljs-comment">//以下是根据基本都是依据系统属性等,判断是否可用</span> <span class="hljs-keyword">boolean</span> isImsUsed = imsManager.updateVolteFeatureValue(); isImsUsed |= imsManager.updateVideoCallFeatureValue(); isImsUsed |= imsManager.updateWfcFeatureAndProvisionedValues(); <span class="hljs-keyword">if</span> (isImsUsed || !getBooleanCarrierConfig(context, CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)) { <span class="hljs-comment">// Turn on IMS if it is used.</span> <span class="hljs-comment">// Also, if turning off is not allowed for current carrier,</span> <span class="hljs-comment">// we need to turn IMS on because it might be turned off before</span> <span class="hljs-comment">// phone switched to current carrier.</span> imsManager.turnOnIms(); } <span class="hljs-keyword">else</span> { <span class="hljs-comment">// Turn off IMS if it is not used AND turning off is allowed for carrier.</span> imsManager.turnOffIms(); } imsManager.mConfigUpdated = <span class="hljs-keyword">true</span>; } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li></ul>
从updateImsServiceConfig函数的代码可以看出,它将根据IMS服务是否可用,选择开启或关闭IMS(这里指的是开启或关闭modem对IMS能力的支持,不是开启或关闭Java层的ImsService)。
但它在什么地方进行了update ImsService Config的操作呢?毕竟函数的名字已经这么取了。
2.1updateVolteFeatureValue
为了解释这个问题,我们以updateVolteFeatureValue为例进行分析:
<code class="hljs java has-numbering"><span class="hljs-comment">//定义于ImsManger中</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">updateVolteFeatureValue</span>() <span class="hljs-keyword">throws</span> ImsException { <span class="hljs-comment">//以下读取数据库、系统属性和资源文件得到对应字段的值</span> <span class="hljs-keyword">boolean</span> available = isVolteEnabledByPlatform(mContext); <span class="hljs-keyword">boolean</span> enabled = isEnhanced4gLteModeSettingEnabledByUser(mContext); <span class="hljs-keyword">boolean</span> isNonTty = isNonTtyOrTtyOnVolteEnabled(mContext); <span class="hljs-comment">//均为true时,feature才是on</span> <span class="hljs-keyword">boolean</span> isFeatureOn = available && enabled && isNonTty; <span class="hljs-comment">//打印log</span> ......... <span class="hljs-comment">//这里就是进行update IMS config的位置</span> getConfigInterface().setFeatureValue( ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE, TelephonyManager.NETWORK_TYPE_LTE, isFeatureOn ? ImsConfig.FeatureValueConstants.ON : ImsConfig.FeatureValueConstants.OFF, mImsConfigListener); <span class="hljs-keyword">return</span> isFeatureOn; }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li></ul>
我们先看看getConfigInterface函数:
<code class="hljs lasso has-numbering"><span class="hljs-keyword">public</span> ImsConfig getConfigInterface() throws ImsException { <span class="hljs-keyword">if</span> (mConfig <span class="hljs-subst">==</span> <span class="hljs-built_in">null</span>) { <span class="hljs-comment">//判断ImsService是否存活</span> checkAndThrowExceptionIfServiceUnavailable(); try { <span class="hljs-comment">//从ImsService获取一个Binder通信客户端</span> IImsConfig config <span class="hljs-subst">=</span> mImsService<span class="hljs-built_in">.</span>getConfigInterface(mPhoneId); <span class="hljs-keyword">if</span> (config <span class="hljs-subst">==</span> <span class="hljs-built_in">null</span>) { throw <span class="hljs-literal">new</span> ImsException(<span class="hljs-string">"getConfigInterface()"</span>, ImsReasonInfo<span class="hljs-built_in">.</span>CODE_LOCAL_SERVICE_UNAVAILABLE); } mConfig <span class="hljs-subst">=</span> <span class="hljs-literal">new</span> ImsConfig(config, mContext); } catch (RemoteException e) { <span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-built_in">..</span> } } <span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-built_in">.</span> <span class="hljs-keyword">return</span> mConfig; }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li></ul>
现在跟进ImsConfig的setFeatureValue函数:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setFeatureValue</span>(<span class="hljs-keyword">int</span> feature, <span class="hljs-keyword">int</span> network, <span class="hljs-keyword">int</span> <span class="hljs-keyword">value</span>, ImsConfigListener listener) throws ImsException { .......... <span class="hljs-keyword">try</span> { <span class="hljs-comment">//利用ImsService返回的Binder通信客户端,调用服务端的setFeatureValue函数</span> miConfig.setFeatureValue(feature, network, <span class="hljs-keyword">value</span>, listener); } <span class="hljs-keyword">catch</span> (RemoteException e) { ....... } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ul>
ImsConfig的实际功能由芯片厂商来提供,例如Qualcomm就定义了ImsConfigImpl的类作为Binder通信的服务端。
ImsConfigImpl类将利用RIL,将Config Feature信息发往modem。
按照上述代码,我们可以看出Android原生的逻辑定义了一个ImsConfigListener作为回调接口,
不过在Qualcomm修改过的代码中,传入的ImsConfigListener是null。
2.2 turnOnIms
接下来,我们看看turnOnIms是如何完成的:
<code class="hljs java has-numbering"><span class="hljs-comment">//定义于ImsManager中</span> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">turnOnIms</span>() <span class="hljs-keyword">throws</span> ImsException { checkAndThrowExceptionIfServiceUnavailable(); <span class="hljs-keyword">try</span> { <span class="hljs-comment">//依赖于ImsService</span> mImsService.turnOnIms(mPhoneId); } <span class="hljs-keyword">catch</span> (RemoteException e) { ...... } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li></ul>
如上代码所示,turnOnIms和turnOffIms将依赖于实际的ImsService类。
在QualComm的实现中:
ImsService下发开/关IMS的消息给modem;
然后,等待modem主动上报IMS服务状态改变;
AP侧收到状态改变信息后,再次主动向modem发送查询IMS服务状态的消息;
获得modem的返回结果后,AP进行逻辑处理,并发送广播给监听者,进行对应的操作,例如更新SystemUI等。
以上就是IMS开机初始化的主要流程,由于考虑到芯片厂商的利益,大量的代码不是开源的,因此重在理解IMS整体的通信结构。
四、获取IMS的注册状态
上文已经提到过,当ImsService启动后,发送广播给Phone对象。
Phone对象收到广播后,将创建出ImsPhone。在ImsPhone的构造函数中:
<code class="hljs java has-numbering"><span class="hljs-comment">//定义于ImsPhone.java</span> <span class="hljs-keyword">public</span> <span class="hljs-title">ImsPhone</span>(Context context, PhoneNotifier notifier, Phone defaultPhone, <span class="hljs-keyword">boolean</span> unitTestMode) { .......... <span class="hljs-comment">//创建了ImsPhoneCallTracker</span> mCT = TelephonyComponentFactory.getInstance().makeImsPhoneCallTracker(<span class="hljs-keyword">this</span>); ......... } <span class="hljs-comment">//定义于TelephonyComponentFactory.java</span> <span class="hljs-keyword">public</span> ImsPhoneCallTracker <span class="hljs-title">makeImsPhoneCallTracker</span>(ImsPhone imsPhone) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ImsPhoneCallTracker(imsPhone); }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li></ul>
我们跟进一下ImsPhoneCallTracker的构造函数:
<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-title">ImsPhoneCallTracker</span>(ImsPhone phone) { <span class="hljs-keyword">this</span>.mPhone = phone; ........... <span class="hljs-comment">//监听是否有来电</span> IntentFilter intentfilter = <span class="hljs-keyword">new</span> IntentFilter(); intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL); intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); mPhone.getContext().registerReceiver(mReceiver, intentfilter); <span class="hljs-comment">//由卡信息决定 </span> mAllowEmergencyVideoCalls = isEmergencyVtCallAllowed(mPhone.getSubId()); Thread t = <span class="hljs-keyword">new</span> Thread() { <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span>() { <span class="hljs-comment">//重头戏在这里</span> getImsService(); } }; t.start(); }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li></ul>
我们从代码可以看出,在ImsCallTracker的构造函数中启动了一个线程,调用getImsService:
<code class="hljs lasso has-numbering"><span class="hljs-keyword">private</span> <span class="hljs-literal">void</span> getImsService() { <span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-built_in">..</span> mImsManager <span class="hljs-subst">=</span> ImsManager<span class="hljs-built_in">.</span>getInstance(mPhone<span class="hljs-built_in">.</span>getContext(), mPhone<span class="hljs-built_in">.</span>getPhoneId()); try { mServiceId <span class="hljs-subst">=</span> mImsManager<span class="hljs-built_in">.</span>open(ImsServiceClass<span class="hljs-built_in">.</span>MMTEL, createIncomingCallPendingIntent(), <span class="hljs-comment">//传入了一个ConnectionStateListener,这就是获取IMS服务状态的接口</span> mImsConnectionStateListener); <span class="hljs-comment">//设置更新feature的回调接口;Qualcomm直接去掉了这句</span> <span class="hljs-comment">//原生的实现也就是打印了log</span> mImsManager<span class="hljs-built_in">.</span>setImsConfigListener(mImsConfigListener); <span class="hljs-comment">//处理EmergencyCabllback和TtyMode</span> <span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-built_in">.</span> } catch (ImsException e) { <span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-attribute">...</span><span class="hljs-built_in">.</span> } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li></ul>
上面调用ImsManger的open函数,实际上会利用ImsService进行实际的处理,依赖于芯片厂商实现,此处就不贴具体的源码了。
这里需要说明的是,不要被open函数的名称骗到了,它不会进行类似于turnOnIms的操作,仅仅用于从modem获取IMS服务当前的注册状态,同时将mImsConnectionStateListener注册到ImsService中(或其成员中)。
当获取到IMS服务的注册状态,或IMS服务状态发送改变时,均会回调mImsConnectionStateListener的接口。
最后我们看看ImsPhoneCallTracker中定义的ImsConnectionStateListener,目前重点关注IMS服务连接和断开的接口:
<code class="hljs java has-numbering"><span class="hljs-keyword">private</span> ImsConnectionStateListener mImsConnectionStateListener = <span class="hljs-keyword">new</span> ImsConnectionStateListener() { <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onImsConnected</span>() { <span class="hljs-keyword">if</span> (DBG) log(<span class="hljs-string">"onImsConnected"</span>); <span class="hljs-comment">//修改服务状态为STATE_IN_SERVICE</span> mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); <span class="hljs-comment">//IMS成功注册</span> mPhone.setImsRegistered(<span class="hljs-keyword">true</span>); mEventLog.writeOnImsConnectionState( TelephonyEventLog.IMS_CONNECTION_STATE_CONNECTED, <span class="hljs-keyword">null</span>); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onImsDisconnected</span>(ImsReasonInfo imsReasonInfo) { <span class="hljs-keyword">if</span> (DBG) log(<span class="hljs-string">"onImsDisconnected imsReasonInfo="</span> + imsReasonInfo); <span class="hljs-comment">//修改服务状态为STATE_OUT_OF_SERVICE</span> mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); <span class="hljs-comment">//IMS未注册</span> mPhone.setImsRegistered(<span class="hljs-keyword">false</span>); <span class="hljs-comment">//解析错误原因,会发广播</span> mPhone.processDisconnectReason(imsReasonInfo); mEventLog.writeOnImsConnectionState( TelephonyEventLog.IMS_CONNECTION_STATE_DISCONNECTED, imsReasonInfo); } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onImsProgressing</span>() { ........... } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onImsResumed</span>() { ........... } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onImsSuspended</span>() { ........ } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onFeatureCapabilityChanged</span>(<span class="hljs-keyword">int</span> serviceClass, <span class="hljs-keyword">int</span>[] enabledFeatures, <span class="hljs-keyword">int</span>[] disabledFeatures) { ......... } <span class="hljs-annotation">@Override</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onVoiceMessageCountChanged</span>(<span class="hljs-keyword">int</span> count) { ........ } }; }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li></ul>
从代码容易看出,mImsConnectionStateListener将调用ImsPhone的setServiceState函数更新服务状态:
<code class="hljs cs has-numbering"><span class="hljs-keyword">void</span> setServiceState(<span class="hljs-keyword">int</span> state) { <span class="hljs-comment">//更改ImsPhone持有的ServiceState的语音服务状态</span> mSS.setVoiceRegState(state); updateDataServiceState(); } <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">updateDataServiceState</span>() { <span class="hljs-keyword">if</span> (mSS != <span class="hljs-keyword">null</span> && mDefaultPhone.getServiceStateTracker() != <span class="hljs-keyword">null</span> && mDefaultPhone.getServiceStateTracker().mSS != <span class="hljs-keyword">null</span>) { <span class="hljs-comment">//更改对应GsmCdmaPhone持有的ServiceState的数据服务状态及无线技术</span> ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS; mSS.setDataRegState(ss.getDataRegState()); mSS.setRilDataRadioTechnology(ss.getRilDataRadioTechnology()); Rlog.d(LOG_TAG, <span class="hljs-string">"updateDataServiceState: defSs = "</span> + ss + <span class="hljs-string">" imsSs = "</span> + mSS); } }</code><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul><ul class="pre-numbering" style=""><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>五、总结
这篇博客中我们分析了Anroid中ImsService如何启动、ImsPhone如何创建及如何监听实际IMS服务的注册状态等。
由于IMS相关的代码并不开源,因此有些流程没法详细分析,知道Anroid中IMS相关的通信 架构即可。