Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

大家好,又见面了,我是全栈君。

本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处

本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉。


前置文章:


Android 4.4 Kitkat Phone工作流程浅析(一)__概要和学习计划

Android 4.4 Kitkat Phone工作流程浅析(二)__UI结构分析

Android 4.4 Kitkat Phone工作流程浅析(三)__MO(去电)流程分析

Android 4.4 Kitkat Phone工作流程浅析(四)__RILJ工作流程简析

Android 4.4 Kitkat Phone工作流程浅析(五)__MT(来电)流程分析

概述

        前面五篇文章,将Android 4.4 的MO/MT流程衔接了起来。同一时候包含了UI结构简单分析,以及RILJ的工作流程分析。本篇以及兴许关于Phone的文章,主要是对前面MO/MT主线的补充和说明,用以完好整个MO/MT流程。
        在整个MO/MT流程的分析过程中,遗漏了非常多细枝末节,在弄清楚整个MO/MT的始末之后,便发现有一些疑问疑问。因此打算陆陆续续分析一些遗漏的细节。本篇文章的发起缘由非常easy,就是想知道InCallActivity是怎样显示和更新的,通过分析后发现。如今Android 4.4 的界面更新也是从Modem状态改变发起,并不像曾经那样直接更新显示界面。
        从直观上来讲,当用户按下拨号键之后会立马显示正在呼叫的界面,也就是InCallActivity。在Android 4.2中,这个界面叫做InCallScreen这一点前面的文章已经有提过。InCallScreen会通过上层调用显示出来。

而在Android 4.4中。InCallActivity的显示则是由Call状态来决定的,Call的状态能够分为6种:ACTIVEHOLDINGDIALINGALERTINGINCOMINGWAITING。当发起MO流程而且对方还未接通这段时间。Call的状态是DIALING。

        这个DIALING状态是怎样反应到界面上的呢?InCallActivity是怎样知道当前Call的状态的呢?假设有看过MT流程的童鞋肯定知道,MT流程实际上就包括了Call状态的改变并从底层一路传递到上层,而DIALING的状态也是从底层反馈来的。因此整个流程和MT类似,分为三个部分:Telephony framework、TeleService、InCallUI。例如以下图:

Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程


Telephony Framework接收处理反馈

        通过前面的分析我们知道。全部的状态改变都是由底层Modem发起的,并将相关状态信息反馈到framework层的RILJ中。上层通过AT指令与Modem进行交互,关于AT的一些介绍信息能够看
这里
这里以及
这里,本文主要介绍的是InCallActivity的显示更新,那为什么会提到AT指令呢?这得从源头说起。
        当用户发起MO流程之后。告诉Modem运行Dial操作。此时Modem的状态就会随之改变,并将状态改变信息通知到上层,因此这里会涉及到一些系统的radio log,能够看到发起拨号操作的相关重要log信息例如以下:
01-01 18:11:47.039  1061  1061 D RILJ    :  RIL(1) :[0147]> DIAL
... ...省略
01-01 18:11:47.044   682   692 D use-Rlog/RLOG-AT: AT> ATD13800138000;
01-01 18:11:47.044   682   696 I use-Rlog/RLOG-RIL: RIL_URC2_PROXY wakeup
01-01 18:11:47.044   682   692 D use-Rlog/RLOG-AT: ATD13800138000;
... ...省略 
01-01 18:11:47.047   682   707 D use-Rlog/RLOG-AT: OK
01-01 18:11:47.047   682   707 D use-Rlog/RLOG-AT: AT< OK
01-01 18:11:47.047   682   707 D use-Rlog/RLOG-AT: RIL_CMD_READER_2:OK
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: 
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: +CIEV: 5, 1
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: 
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: +ECPI: 1,130,0,0,0,0,"13800138000",129,""
01-01 18:11:47.047   682   707 D use-Rlog/RLOG-AT: RIL_CMD_READER_2 Enter processLine
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: AT< +CIEV: 5, 1
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: RIL_URC_READER:+CIEV: 5, 1
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: RIL_URC_READER Enter processLine
01-01 18:11:47.047   682   707 I use-Rlog/RLOG-AT: AT read start
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-RIL: Nw URC:+CIEV: 5, 1
01-01 18:11:47.047   682   705 E use-Rlog/RLOG-RIL: Unhandled unsolicited result code: +CIEV: 5, 1
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: AT< +ECPI: 1,130,0,0,0,0,"13800138000",129,""
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: RIL_URC_READER:+ECPI: 1,130,0,0,0,0,"13800138000",129,""
01-01 18:11:47.047   682   692 D use-Rlog/RLOG-AT: response received on RIL_CMD_READER_2, tid:3086469296
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: RIL_URC_READER Enter processLine
01-01 18:11:47.047   682   705 D use-Rlog/RLOG-RIL: Nw URC:+ECPI: 1,130,0,0,0,0,"13800138000",129,""
... ...省略
01-01 18:11:47.048  1061  1274 D RILJ    :  RIL(1) :[0147]< DIAL 
01-01 18:11:47.048  1061  1274 V RILJ    :  RIL(1) :[UNSL RIL]< UNSOL_CALL_PROGRESS_INFO {1, 130, 0, 0, 0, 0, 13800138000, 129, }

这里简单的分析一下重要的log信息:

RIL(1) :[0147]> DIAL

这表示发起DIAL请求。紧接着运行ATD即AT拨号指令:

01-01 18:11:47.044   682   692 D use-Rlog/RLOG-AT: AT> ATD13800138000;

我们在《
Android 4.4 Kitkat Phone工作流程浅析(四)__RILJ工作流程简析》文章中有讲过。能够依据serial号查看AT指令的配对。同一时候也提到了Log中的“>”和“<”所代表的含义。即“>”表示request,“<”表示response,上面两条log信息能够解释为RILJ发起了两次request请求。

依据第一条AT指令的serial号”0147″我们能够在后面找到相应的response:

01-01 18:11:47.048  1061  1274 D RILJ    :  RIL(1) :[0147]< DIAL 

这表明整个拨号的request和response已经完毕,在此期间Modem主动返回了下面信息:

01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: AT< +ECPI: 1,130,0,0,0,0,"13800138000",129,""

该条AT指令+ECPI是MTK加入的。在标准AT指令中查询不到,详细含义例如以下:

ECPI即Call Progress Information的缩写,用于开启/禁用Call Progress Information。
+ECPI:<call_id>,<msg_type>,<is_ibt>,<is_tch>,<dir>,<call_mode>,[<number>,<type>],[<disc_cause>]

相应的解释例如以下图:

Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

图中黑色加粗部分为眼下MTK Android 4.4有使用的msg_type,这一点能够在GSMCallTracker中的handleCallProcessInfo()方法的凝视中找到。

相应着这个表我们来梳理一下

01-01 18:11:47.047   682   705 D use-Rlog/RLOG-AT: AT< +ECPI: 1,130,0,0,0,0,"13800138000",129,""

以上log重要信息例如以下:

call_id:1。call id 为1;
msg_type:130,表示CSMCC_CALL_ID_ASSIGN_MSG,也就是Dialing;
dir:0,CLCC_MO_CALL,表示MO操作;
call_mode:0,CLCC_VOICE_CALL,表示普通语音拨号;
number:138001380000,表示主叫号码。
type:129,National call,也就是本国电话。假设是145则表示国际电话。
在Modem完毕DIAL操作之后。紧接着返回了下面log:
01-01 18:11:47.048  1061  1274 V RILJ    :  RIL(1) :[UNSL RIL]< UNSOL_CALL_PROGRESS_INFO {1, 130, 0, 0, 0, 0, 13800138000, 129, }

这是一条UnSolicited response消息,处理方法是RIL.java中的
processUnsolicited(),类型为:UNSOL_PROGRESS_INFO。

        依据前文《
Android 4.4 Kitkat Phone工作流程浅析(五)__MT(来电)流程分析》能够知道,这里会開始在RILJ中运行ProcessUnSolicited()方法,而且相应类型为UNSOL_PROGRESS_INFO。

RIL.java将处理之后的消息通过notifyRegistrants()的方式传递给GsmCallTracker。在GsmCallTracker的handleCallProcessInfo()方法中能够看到是怎样定义Call状态为DIALING的。代码例如以下:

//... ... 省略部分代码
if (msgType == 132 || msgType == 6)
    dc.state = DriverCall.State.ACTIVE;
else if (msgType == 131)
    dc.state = DriverCall.State.HOLDING;
else if (msgType == 130 && callId != 254)
    //从log中能够看到msgType=130,call_id=1
    dc.state = DriverCall.State.DIALING;
else if (msgType == 2)
    dc.state = DriverCall.State.ALERTING;
else if (msgType == 0)
{
    for (j = 0; j < MAX_CONNECTIONS; j++) {
        if (mConnections[j] != null) {
            count ++;
        }
    }
    if (mState == PhoneConstants.State.IDLE || 
    (count == 0 &&  mForegroundCall.getState() == GsmCall.State.DIALING))
    {
    /* if the 2nd condition is true, that means we make a MO call, receiving +ECPI: 130, 
    * then receiving +ECPI: 133 immediately due to MT call (+ECPI: 0) is receiving*/
    if (count == 0 &&  mForegroundCall.getState() == GsmCall.State.DIALING)
        log("MO/MT conflict!!");
        dc.state = DriverCall.State.INCOMING;
    }
    else
        dc.state = DriverCall.State.WAITING;
}

当状态改变之后便会通过GsmPhone的
notifyPreciseCallStateChanged()方法发起响应。例如以下:

if ((hasNonHangupStateChanged || newRinging != null) && crssAction != CrssAction.SWAP && !(hasPendingReplaceRequest && msgType == 133)) {    log("notify precise call state changed");    mPhone.notifyPreciseCallStateChanged();}

之后会通过观察者模式方式调用到CallManager的
handleMessage()方法中,case为
EVENT_PRECISE_CALL_STATE_CHANGED。代码例如以下:

case EVENT_PRECISE_CALL_STATE_CHANGED://... ...省略部分代码    index = (msg.what - EVENT_PRECISE_CALL_STATE_CHANGED) / NOTIFICATION_ID_OFFSET;    mPreciseCallStateRegistrantsGemini[index].notifyRegistrants((AsyncResult) msg.obj);    mPreciseCallStateRegistrants.notifyRegistrants((AsyncResult) msg.obj);    handle3GSwitchLock();

这里会有MTK的Gemini处理,即双卡处理,通过相同的观察者模式将状态改变信息通过notifyRegistrants()方法发送到TeleService中。整个流程例如以下图:


Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

TeleService消息处理

        CallManager将Call状态改变的信息告诉TeleService中的
CallStateMonitor。通过这个名字能够非常easy知道它是用来监视Call状态的。

同一时候CallNotifierCallModeler均注冊了CallStateMonitor的状态改变回调。一旦Call状态改变便会通知CallNotifier和CallModeler。这里CallNotifier并没有做什么大的动作,仅仅是更新了近距离感应器的状态,推断是否接通假设接通则震动这类。跟界面相关的调用则在CallModeler中。

        在
CallModeler
onPhoneStateChanged()方法中。将信息传递到
CallHandlerServiceProxy
onUpdate()方法中:
if (!ignoreUpdate()) {
    if (updatedCalls.size() > 0) {
        for (int i = 0; i < mListeners.size(); ++i) {
            mListeners.get(i).onUpdate(updatedCalls);
        }
    }
}

这里会触发BluetoothManager、
CallHandlerServiceProxy、DTMFTonePlayer三个类中的
onUpdate()方法回调,这里我们查看CallHandlerServiceProxy中的onUpdate()方法就可以。对于上面的代码再多说几句,原生Android 4.4中,CallModeler的onPhoneStateChanged方法并没有
ignoreUpdate()方法,这是MTK增加的主要目的是用于推断是否忽略本次界面更新。用于自己主动拒接和高速挂断正在响铃的电话两种场景。代码例如以下:

    /*     * The function to judge whether should skip update calls to InCallUI,     * for auto reject case, or quick hang up ringing case.     * When 1A + 1R, if ringing call is hanged up while query(ringtone),     * CallNotifier will not notify InCallUI the onIncoming(), then we should ignore update calls to InCallUI;     * or will show callcard with ringing call information but no AnswerFragment shown.     */    private boolean ignoreUpdate() {        boolean shouldIgnore = false;        final boolean hasActiveFgCall = mCallManager.hasActiveFgCall();        final boolean hasActiveBgCall = mCallManager.hasActiveBgCall();        shouldIgnore = (hasActiveFgCall || hasActiveBgCall) && PhoneGlobals.getInstance().notifier.hasPendingCallerInfoQuery();        Log.i(TAG, "ignoreUpdate()... shouldIgnore: " + shouldIgnore);        return shouldIgnore;    }

通过代码能够非常清楚的知道,当CallerInfo没有查询完成时
hasPendingCallerInfoQuery()返回true,则忽略本次界面更新。

在CallHandlerServiceProxy的onUpdate()方法中,首先会去运行bindService操作也就是与InCallUI建立联系。代码例如以下:

@Overridepublic void onUpdate(List<Call> calls) {    synchronized (mServiceAndQueueLock) {        if (mCallHandlerServiceGuarded == null) {            //设置更新类型为METHOD_UPDATE            enqueueUpdate(calls);            //与CallHandlerService建立连接            setupServiceConnection();            return;        }    }    //运行更新    processUpdate(calls);}

更新类型包含:

QueueParams.METHOD_INCOMING:更新类型为INCOMING;
QueueParams.METHOD_UPDATE:更新类型为UPDATE;
QueueParams.METHOD_DISCONNECT:更新类型为DISCONNECT。
QueueParams.METHOD_VT_DIAL_SUCCESS:更新类型为VT_DIAL_SUCCESS,视屏通话拨号成功。
QueueParams.METHOD_VT_SETTING_PARAMS:更新类型为VT_SETTING_PARAMS。视屏通话设置參数;
QueueParams.METHOD_VT_STATE_CHANGE:更新类型为VT_STATE_CHANGE,视屏通话状态改变。
QueueParams.METHOD_UPDATE_RECORD_STATE:更新类型为UPDATE_RECORD_STATE,通话录音;

Android原生仅仅有前面三种类型,后面均由MTK加入。
        紧接着通过
setupServiceConnection()方法与InCallUI的CallHandlerService建立连接。这里须要注意在建立连接成功后会调用
onServiceConnected()方法。进一步调用
onCallHandlerServiceConnected()方法,并在该方法中继续调用
processQueue()方法,进而调用到终于跳转方法即
processUpdate()

这里大家可能会有疑问,
onUpdate()方法中的
processUpdate()方法什么时候调用呢?在第一次运行Dial操作时,TeleService和InCallUI还没有建立联系。因此须要先bindService。等连接建立成功后,兴许的更新则会直接调用
onUpdate()方法中的
processUpdate()方法。经过如此调用之后便会跳转到InCallUI中运行界面显示与更新。整个流程例如以下图:
Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

InCallUI显示/更新

        在TeleService的
processUpdate()方法中。使用AIDL的方式调用
mCallHandlerServiceGuarded.onUpdate()。终于跳转到InCallUI中的
CallHandlerService.onUpdate()方法中,代码例如以下:
@Override
public void onUpdate(List<Call> calls) {
    try {
        Log.i(TAG, "onUpdate: " + calls);
        //注意:这里的类型是ON_UPDATE_MULTI_CALL
        mMainHandler.sendMessage(mMainHandler.obtainMessage(ON_UPDATE_MULTI_CALL, calls));
    } catch (Exception e) {
        Log.e(TAG, "Error processing onUpdate() call.", e);
    }
}

这里非常奇怪的一点是。类型竟然是ON_UPDATE_MULTI_CALL,而代码中也有ON_UPDATE_CALL类型。这是google原生的。MTK没有改过。不知道是何用意。

后面继续跳转到
CallList
notifyListenersOfChange()方法:
    /**
     * Sends a generic notification to all listeners that something has changed.
     * It is up to the listeners to call back to determine what changed.
     */
    private void notifyListenersOfChange() {
        for (Listener listener : mListeners) {
            listener.onCallListChange(this);
        }
    }

这里会运行listener的回调,分别在
AnswerPresenter
InCallPresenter中。CallList仅仅是负责将状态改变通知到listener,是否处理则由listener们自己决定。AnswerPresenter仅仅有在incoming的时候才会处理。因此这里应该查看
InCallPresenter
onCallListChange()方法:

    @Override    public void onCallListChange(CallList callList) {        if (callList == null) {            return;        }        InCallState newState = getPotentialStateFromCallList(callList);        //这里去推断是否显示/关闭InCallActivity        newState = startOrFinishUi(newState);    //... ...省略部分代码    }

这里会跳转到
startOrFinishUi()中进行推断,由于是第一次启动
InCallActivity界面。最后会通过
startActivity()的方式启动InCallActivity,到此整个InCallActivity的界面显示流程就结束了。兴许Modem側状态改变则依据该流程传递到InCallPresenter,由InCallPresenter来响应不同的状态所须要启动/关闭的界面。整个流程例如以下图:


Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

小结

        在Android 4.2中界面显示是在placeCall返回之后调用的,但到了Android 4.4中。由于UI和Logic的分离,界面的显示全然依赖于Logic返回的状态信息。关于InCallActivity的显示和更新流程整体上和MT流程是类似的即分为:
Telephony framework
TeleService
InCallUI
        最后须要提一点。假设想主动显示或者更新InCallActivity界面,应该怎么办呢?在
CallModeler.java中有一个
updateCalls()
public方法,能够通过构造CallModeler的对象来调用updateCalls()方法完毕InCallActivity界面的显示和更新,代码例如以下:
    public void updateCalls() {        final List<Call> updatedCalls = Lists.newArrayList();        doUpdate(true, updatedCalls);        if (!ignoreUpdate()) {            if (updatedCalls.size() > 0) {                for (int i = 0; i < mListeners.size(); ++i) {                    mListeners.get(i).onUpdate(updatedCalls);                }            }        }    }

        注意:该方法是MTK自己加入的,原生AOSP并没有,假设想使用也能够仿照这样的方式加入。



        图片下载点:这里
        AT指令介绍文档点:这里
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/115823.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • C语言实现一个关机程序

    C语言实现一个关机程序#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>//system()#include<string.h>//strcmp()#include<windows.h>//shutdownintmain(){ charinput[20]={0}; //一个关机程序 //shutdown-s关机 //shutdown-s-t60设计关.

  • 二叉树的一些性质图解

    二叉树的一些性质图解树的介绍1.树的定义树是一种数据结构,它是由n(n&gt;=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:(01)每个节点有零个或多个子节点;(02)没有父节点的节点称为根节点;(03)每一个非根节点有且只有一个父节点;(04)除了根节点外,每个子节点可以分为多个不相交的子树。 2.树的基本术语若…

  • Microsoft Office Excel 不能访问文件 的解决办法

    Microsoft Office Excel 不能访问文件 的解决办法

  • Windows下openssl安装及使用「建议收藏」

    配置过程中需要生成一些mak文件,这些生成代码用perl脚本生成,所以要安装一个ActivePerl.网址:http://www.activestate.com/activeperl/下载后直接安装就行了!下载openssl网址http://www.openssl.org/我用的是openssl-0.9.8g版本,解压到c盘根目录。安装步骤:(可以参照o…

  • linux如何退出文件查看模式,linux vi保存退出指令(如何退出vi)

    linux如何退出文件查看模式,linux vi保存退出指令(如何退出vi)有很多方法退出Vi当编辑完文件准备退出Vi返回到shell时可以使用以下几种方法之一在命令模式中连按两次大写字母Z若当前编辑的文件曾被修改过则Vi保存该文件后退出返回到shell;若当前编辑的文件没被修改过则Vi直接退出返回到shell在末行模式下输入命令:wVi保存当前编辑文件但并不退出而是继续等待用户输入命令在使用w命令时可以再给编辑文件起一个新的文件名[例]:wnewfi…

  • python之sympy库–数学符号计算与绘图必备[通俗易懂]

    python之sympy库–数学符号计算与绘图必备[通俗易懂]在实际进行数学运算的时候,其实有两种运算模式,一种是数值运算,一种是符号运算(代数)。而我们日常使用计算机进行数值运算,尤其是比如除、开平方等运算时,往往只能得到其近似值,最终总会已一定的误差,如果使用符号运算模式,则可以完全避免此种问题。一、数学符号及符号表达式符号表达式,区别于常规的数值型数学表达式,常规数学表达式,比如x+y*2等,基本x和y是一个变量,且变量最终也会被赋值,由变量组成的表达式,最后得出的也是一个数值。而符号表达式,则真正的由符号组成,而符号无需提前赋值,由符号组成的表达式

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号