zoukankan      html  css  js  c++  java
  • Android 4.4 Kitkat Phone工作流程浅析(七)__来电(MT)响铃流程


    本文来自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 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程

    概述

            本系列文章以MT/MO为主线流程。并对当中的细枝末节进行补充说明,比方来电响铃流程。在MT流程的分析中已经涵盖了流程的发起与终止,本文所描写叙述的响铃流程始于MT流程的发起。如对MT流程不熟悉的童鞋请查看文章《Android 4.4 Kitkat Phone工作流程浅析(五)__MT(来电)流程分析》以及《Android 4.4 Kitkat Phone工作流程浅析(六)__InCallActivity显示更新流程》。

            Android 4.4对于响铃流程有所修改。把响铃触发放到了TeleService中,这也符合4.4 Phone的设计风格即UI和Logic分离。当来电流程发起时。抓取radio_log进行分析后,能够看到整个来电过程状态改变例如以下:

    handleMessage (EVENT_VOICE_CALL_INCOMING_INDICATION) //设置voice call的标志
    handleMessage (EVENT_NEW_RINGING_CONNECTION)         //新来电标志
    handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)     //状态改变
    handleMessage (EVENT_INCOMING_RING)                  //响铃
    handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION) //收到AT指令CLIP后,更新通话信息(CLIP即来电显示作用)
    handleMessage (EVENT_INCOMING_RING)
    handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION)
    handleMessage (EVENT_INCOMING_RING)
    handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION)
    handleMessage (EVENT_INCOMING_RING)
    handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION)
    handleMessage (EVENT_INCOMING_RING)
    handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION)
    handleMessage (EVENT_DISCONNECT)                     //断开连接
    handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)     //状态改变
    handleMessage (EVENT_PRECISE_CALL_STATE_CHANGED)
    这些log是CallManager中打印出来的,当然我们也能够在RIL.java中去查看相应的log。

    当Modem側收到来电消息后所做的操作例如以下

    1. 依据来电类型设置INCOMING_INDICATION;

    2. 发起NEW_RINGING_CONNECTION,新来电标志

    3. 触发CALL_STATE_CHANGED状态,状态改变促使界面更新;

    4. 发起响铃通知INCOMING_RING。响铃流程由此发起;

    5. 依据CLIP返回信息触发CRSS_SUPP_SERVICE_NOTIFICATION。这是由MTK增加的,其作用是依据CLIP的返回更新Call的信息;

    6. 循环上面4和5步,持续响铃;

    7. 断开连接DISCONNECT,本次MT流程结束(未接听);

    8. 触发CALL_STATE_CHANGED状态。状态改变促使界面更新。

    Telephony Framework处理响铃事件

            当来电消息到来时,经过Modem以及RILD的处理后便传递到Telephony framework中,并由RILJ继续发起。流程图例如以下:

    RILJ

            在MT流程发起后,会有相关的unsolicited信息反馈到RILJ中,这里主要关注响铃流程(ringing_flow),因此能够找到下面AT指令返回的log信息:

    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: +CRING: VOICE
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: 
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: +CLIP: "13800138000",0,"",0,"",0
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: 
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: +ECPI: 1,4,0,1,1,0,"13800138000",129,""
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: AT< +CRING: VOICE
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: RIL_URC_READER:+CRING: VOICE
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-AT: RIL_URC_READER Enter processLine
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-RIL: Nw URC:+CRING: VOICE
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-RIL: receiving RING!!!!!!
    01-01 02:46:01.154  4753  4768 D use-Rlog/RLOG-RIL: receiving first RING!!!!!!

    整个radio_log中每隔3877ms便会打印一次+CRING: VOICE的log,通话挂断前一共同拥有5次。

    依据AT返回信息能够知道,来电类型为Voice,而且是一次响铃事件。

    紧接着在RILJ中收到了与之相应的事件RIL_UNSOL_CALL_RING:

    01-01 02:46:02.155 1443 1837 D RILJ : RIL(2) :[UNSL RIL]< UNSOL_CALL_RING [C@422899d0

    这里是UnSolicited事件,触发processUnsolicited方法,并运行到下面代码处:

    case RIL_UNSOL_CALL_RING:
        if (RILJ_LOGD) unsljLogRet(response, ret);
        if (mRingRegistrant != null) {
    	//观察者模式
            mRingRegistrant.notifyRegistrant(
                    new AsyncResult (null, ret, null));
        }
    前面的文章中我们已经分析过Registrant这样的触发模式,这里再次分析下notifyRegistrant()方法触发后的跳转地点。

    首先查看到mRingRegistrant的定义在SourceCode/frameworks/opt/telephony/src/java/com/android/internal/telephony/BaseCommands.java中。且赋值在setOnCallRing方法中,例如以下:

        @Override
        public void setOnCallRing(Handler h, int what, Object obj) {
            mRingRegistrant = new Registrant (h, what, obj);
        }
    这里的setOnCallRing方法在SourceCode/frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneBase.java的构造方法中调用,例如以下:

    mCi.setOnCallRing(this, EVENT_CALL_RING, null);
    mCi是CommandsInterface的对象,而BaseCommands implements CommandsInterface。也就是说在PhoneBase的构造方法被调用时,就应经完毕了对mRingRegistrant的赋值。
    须要注意两点:

    (1). PhoneBase extends Handler,因此setOnCallRing中的this即代表了PhoneBase本身。换句话说。当触发notifyRegistrant时,便会回调到PhoneBase的handleMessage中;

    (2). setOnCallRing中的EVENT_CALL_RING表示当触发notifyRegistrant时。回调handleMessage中的对应case。

    mRingRegistrant注冊流程


            当Phone第一次启动时便会运行PhoneFactory中的makeDefaultPhone()方法,用于完毕Phone的初始化,在初始化过程中即完毕了mRingRegistrant的注冊。

    PhoneBase

            当运行mRingRegistrant.notifyRegistrant()方法后,跳转到PhoneBase的handleMessage方法中。代码例如以下:

    case EVENT_CALL_RING:
        ar = (AsyncResult)msg.obj;
        if (ar.exception == null) {
            PhoneConstants.State state = getState();
            if ((!mDoesRilSendMultipleCallRing)
                    && ((state == PhoneConstants.State.RINGING) ||
                            (state == PhoneConstants.State.IDLE))) {
                mCallRingContinueToken += 1;
                sendIncomingCallRingNotification(mCallRingContinueToken);
            } else {
                //运行这里
                notifyIncomingRing();
            }
        }
        break;
    这里继续查看notifyIncomingRing()方法:
    private void notifyIncomingRing() {
        if (!mIsVoiceCapable)
            return;
        AsyncResult ar = new AsyncResult(null, this, null);
        mIncomingRingRegistrants.notifyRegistrants(ar);
    }
    相同使用了观察者模式,查找对应的registerXXX方法,例如以下:
        @Override
        public void registerForIncomingRing(
                Handler h, int what, Object obj) {
            checkCorrectThread(h);
            mIncomingRingRegistrants.addUnique(h, what, obj);
        }
    能够找到在CallManager的registerForPhoneStates方法中,调用了IncomingRing的register方法:
    if (FeatureOption.MTK_GEMINI_SUPPORT == true && !(phone instanceof SipPhone)) {
        if(phone instanceof GeminiPhone) {
            int offset;        	
            int count = (MAXIMUM_SIM_COUNT < PhoneConstants.GEMINI_SIM_NUM) ? MAXIMUM_SIM_COUNT : PhoneConstants.GEMINI_SIM_NUM;
            Phone targetPhone;
            for (int i = 0; i < count; i++) {
                offset = i * NOTIFICATION_ID_OFFSET;
                targetPhone = ((GeminiPhone)phone).getPhonebyId(PhoneConstants.GEMINI_SIM_1 + i);    
                //... ...省略
                targetPhone.registerForIncomingRing(mHandler, EVENT_INCOMING_RING + offset, null);
    这里涉及到MTK的双卡机制,同一时候可能大家会有疑惑怎样断定是这里调用的呢?我们能够反向思考。当发现这里调用之后。我们反过来看是哪里调用了registerForPhoneStates,然后依次查看。

    终于我们能够看到这些都是在Phone初始化时候顺序调用的,我们仅仅是反过来查找而已。

    通过上面CallManager中的代码能够知道:

    (1). mHandler在CallManager中定义。相关handleMessage就可以找到;

    (2). case相应事件为:EVENT_INCOMING_RING;

    mIncomingRingRegistrants注冊流程


            mIncomingRingRegistrants的注冊流程始于Phone启动并初始化时,须要关注的一点是CallManager中的registerForPhoneStates()方法。

    为什么这里直接从CallManager跳转到PhoneBase呢?实际上targetPhone对象是通过PhoneProxy传递过来的,而PhoneProxy是GSMPhone和CDMAPhone的代理。GSMPhone和CDMAPhone都继承自PhoneBase,终于的实现也在PhoneBase中。这里省略了部分跳转,请读者知悉。

    CallManager

            依据前面的分析,在PhoneBase的notifyIncomingRing()方法中会调用mIncomingRingRegistrants.notifyRegistrants()方法。能够找到在CallManager中相应的handleMessage方法以及相应的处理事件EVENT_INCOMING_RING:

    @Override
    public void handleMessage(Message msg) {
        int index;
        switch (msg.what) {
    	    //... ...省略
            case EVENT_INCOMING_RING:
            case EVENT_INCOMING_RING + NOTIFICATION_ID_OFFSET:
            case EVENT_INCOMING_RING + (NOTIFICATION_ID_OFFSET * 2):
            case EVENT_INCOMING_RING + (NOTIFICATION_ID_OFFSET * 3):
                if (!hasActiveFgCall()) {
                   index = (msg.what - EVENT_INCOMING_RING) / NOTIFICATION_ID_OFFSET;
                   mIncomingRingRegistrantsGemini[index].notifyRegistrants((AsyncResult) msg.obj);
                   mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);
                }
            break;
    看到这里继续查看mIncomingRingRegistrantsGemini和mIncomingRingRegistrants的registerXXX方法。代码例如以下:
    //mIncomingRingRegistrantsGemini的registe方法
    public void registerForIncomingRingEx(Handler h, int what, Object obj, int simId){
    	  int index = getRegistrantsArrayIndex(simId);
    	  if (index != -1) {
            mIncomingRingRegistrantsGemini[index].addUnique(h, what, obj);
        }
    }
    //mIncomingRingRegistrants的registe方法
    public void registerForIncomingRing(Handler h, int what, Object obj){
        mIncomingRingRegistrants.addUnique(h, what, obj);
    }
    以上方法会依据手机制式来调用,假设是双卡则调用Gemini,在CallManagerWrapper中能够找到:
    public static void registerForIncomingRing(Handler handler, int what, Object obj) {
        if (GeminiUtils.isGeminiSupport()) {
            final int[] geminiSlots = GeminiUtils.getSlots();
            for (int geminiSlot : geminiSlots) {
    	    //双卡
                CallManager.getInstance().registerForIncomingRingEx(handler, what, obj,
                        geminiSlot);
            }
        } else {
    	//单卡
            CallManager.getInstance().registerForIncomingRing(handler, what, obj);
        }
    }
    而以上方法在CallManagerWrapper中还经过了一层包装:
    public static void registerForIncomingRing(Handler handler, int what) {
        registerForIncomingRing(handler, what, null);
    }
    那么兴许调用是在哪里呢?我们能够在CallStateMonitor的registerForNotifications()方法中找到:
    CallManagerWrapper.registerForIncomingRing(this, PHONE_INCOMING_RING);
    而registerForNotifications()方法在CallStateMonitor初始化以及Radio状态改变的时候会调用。

    通过以上分析我们能够知道:

    (1). 在CallStateMonitor中注冊了来电响铃回调,也就是这里的this。

    CallStateMonitor继承自Handler,那么CallManager中的notifyRegistrants()方法会跳转到CallStateMonitor中;

    (2). 相应的case事件为:PHONE_INCOMING_RING。

    mIncomingRingRegistrantsGemini注冊流程

            整个注冊流程是从TeleService启动时開始的,TeleService有监听BOOT_COMPLETE的广播,在随机启动之后便開始了整个注冊流程。

    在第8步须要注意。双卡运行CallManager.getInstance().registerForIncomingRingEx()。单卡则运行CallManager.getInstance().registerForIncomingRing()。

    TeleService处理响铃

            当Telephony Framework处理完响铃事件之后,会将响铃事件上报到CallStateMonitor中。并终于在TeleService中发起响铃操作,流程图例如以下:

    CallStateMonitor

            经过framework的层层处理之后,响铃事件传递到了TeleService的CallStateMonitor中。通过前面的分析能够知道,在CallManager的handleMessage中。通过mIncomingRingRegistrantsGemini[index].notifyRegistrants()方法跳转到了CallStateMonitor的handleMessage中。例如以下:
    @Override
    public void handleMessage(Message msg) {
        for (Handler handler : registeredHandlers) {
            handler.handleMessage(msg);
        }
    }
    这里会依据registerHandler触发相应的回调。在前面来电(MT)流程的分析过程有看到,CallNotifier和CallModeler注冊了CallStateMonitor的Handler回调,但通过进一步分析后发现,仅仅有CallNotifier中才处理了PHONE_INCOMING_RING的事件,所以接着我们须要查看CallNotifier对响铃事件的处理。

    CallNotifier

            在CallNotifier的handleMessage方法中找到PHONE_INCOING_RING的处理事件例如以下:
    @Override
    public void handleMessage(Message msg) {
        //... ...省略
        case CallStateMonitor.PHONE_INCOMING_RING:
            log("PHONE_INCOMING_RING !");
            if (msg.obj != null && ((AsyncResult) msg.obj).result != null) {
                    //... ...省略
                    if (provisioned && !isRespondViaSmsDialogShowing()) {
                        mRinger.ring();
                    }
                } else {
                    if (DBG) log("RING before NEW_RING, skipping");
                }
            }
        break;
    通过这里会调用Ringer的ring()方法,从而開始响铃。


    Ringer

            到这里就应该准备播放铃声了,但ring()方法中另一系列事情须要处理:
    void ring() {
        synchronized (this) {
            //... ...省略
            //创建Looper线程,用于播放/停止铃声,通过handleMessage接收播放/停止请求
            makeLooper();
            //假设是第一次播放则mFirstRingEventTime = -1
            if (mFirstRingEventTime < 0) {
    	    //这里获取系统开机后经过的时间,包含休眠
                mFirstRingEventTime = SystemClock.elapsedRealtime();
                if (mRingHandler != null) {
    	        //发起播放铃声的请求
                    mRingHandler.sendEmptyMessage(PLAY_RING_ONCE);
                }
            } else {
                //假设不是第一次播放
                if (mFirstRingStartTime > 0) {
                    if (mRingHandler != null) {
         //延迟发送播放请求。延迟时间为第一次启动播放时间减去第一次发送PLAY_RING_ONCE的时间(133ms)
                        mRingHandler.sendEmptyMessageDelayed(PLAY_RING_ONCE,
                            mFirstRingStartTime - mFirstRingEventTime);
                    }
                } else {
                    mFirstRingEventTime = SystemClock.elapsedRealtime();
                }
            }
        }
    }
    在这里就要特别注意了,通过makeLooper()方法创建了一个Looper线程,用于播放/停止铃声。那这里为什么要用Looper呢?
    后面通过sendEmptyMessage()和sendEmptyMessageDelayed()方法发起播放铃声的请求,接下来分析一下makeLooper()的构造以及使用Looper的缘由。

            makeLooper()方法主要创建一个ringer的线程,用于播放/停止铃声,代码例如以下:
    private void makeLooper() {
        //假设第一响铃mRingThread==null
        if (mRingThread == null) {
            //Worker实现了Runnable接口,在其构造方法Worker(String name)
            //中创建并启动了名为"ringer"的工作线程
            mRingThread = new Worker("ringer");
            //若还未获取到ringer线程的Looper对象则返回
            if (mRingThread.getLooper() == null) {
                return ;
            }
            //创建Handler并依附于ringer线程的Looper对象
            mRingHandler = new Handler(mRingThread.getLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    Ringtone r = null;
                    switch (msg.what) {
                        case PLAY_RING_ONCE:
                            if (DBG) log("mRingHandler: PLAY_RING_ONCE...");
                            if (mRingtone == null && !hasMessages(STOP_RING)) {
                                // create the ringtone with the uri
                                if (DBG) log("creating ringtone: " + mCustomRingtoneUri);
                                r = RingtoneManager.getRingtone(mContext, mCustomRingtoneUri);
                                synchronized (Ringer.this) {
                                    if (!hasMessages(STOP_RING)) {
                                        mRingtone = r;
                                    }
                                }
                            }
                            r = mRingtone;
                            if (r != null && !hasMessages(STOP_RING) && !r.isPlaying()) {
                                PhoneLog.d(LOG_TAG, "play ringtone... ");
                                PhoneUtils.setAudioMode();
                                //播放铃声
                                r.play();
                                synchronized (Ringer.this) {
                   //将第一次播放时间(开机后的时间包含休眠)赋值给mFirstRingStartTime
                                    if (mFirstRingStartTime < 0) {
                                        mFirstRingStartTime = SystemClock.elapsedRealtime();
                                    }
                                }
                            }
                            break;
                        case STOP_RING:
                            if (DBG) log("mRingHandler: STOP_RING...");
                            r = (Ringtone) msg.obj;
                            if (r != null) {
                                //停止播放铃声
                                r.stop();
                            } else {
                                if (DBG) log("- STOP_RING with null ringtone!  msg = " + msg);
                            }
                            //退出Looper循环
                            getLooper().quit();
                            break;

    makeLooper()主要做了三件事:
    (1). 创建并启动名为ringer的Looper线程。
    (2). 实例化mHandler并依附于ringer的Looper对象。
    (3). 接收并处理播放/停止铃声的Message;
    能够看到makeLooper做了相当重要的事情,特别是对于第三点,兴许处理播/停止铃声便在handleMessage中。


    接下来须要看看Looper,我们看到Worker类的实现,例如以下:
    private class Worker implements Runnable {
        //创建mLock锁
        private final Object mLock = new Object();
        private Looper mLooper;
    
        Worker(String name) {
    	//创建并启动名为"name"的线程
            Thread t = new Thread(null, this, name);
            t.start();
            synchronized (mLock) {
                while (mLooper == null) {
                    try {
    	            //堵塞直到前面的"name"线程已成功执行(执行了run方法),最大堵塞时间5s
                        mLock.wait(5000);
                    } catch (InterruptedException ex) {
                    }   
                }   
            }   
        }
        public Looper getLooper() {
    	//返回"name"线程的Looper对象
            return mLooper;
        }
        public void run() {
            synchronized (mLock) {
    	    //启用Looper
                Looper.prepare();
    	    //返回当前线程(也就是这里的子线程"name")的Looper对象
                mLooper = Looper.myLooper();
    	    //唤醒锁,不再堵塞
                mLock.notifyAll();
            }
    	//开启Looper循环
            Looper.loop();
        }
        public void quit() {
    	//退出Looper循环
            mLooper.quit();
        }   
    }
    Looper用于在线程中开启消息循环,普通线程默认是没有消息循环的。在线程中通过调用Looper.prepare()开启消息循环。通过Looper.loop()处理消息循环直到线程循环终止,我们能够调用Looper的quit()方法退出循环。

    (关于Looper官方解释。StackOverFlow上的回答,或者查看CSDN网友分析)

            回到前面的问题,这里为什么要使用Looper呢?使用线程来播放铃声。同一时候又须要依据不同的状态,停止铃声的播放。也就是说当持续须要线程来做某件事情,同一时候又想控制该事情的启动与停止,这便能够使用Looper

            当第一次收到响铃请求时。开启线程启动Loop,并播放铃声。通过对Radio_log的分析,RILJ每隔3.877s收到一次响铃请求,此时上报并传到到TeleService中,假设此时铃声已经播放完成,那么则继续运行ringtone.play()方法启动铃声的播放,假设之前的铃声并未播放完成,则不会再次启动ringtone.play()方法。假设此时对方/己方挂断了来电。那么响铃须要马上终止,此时仅仅需在子线程中处理STOP_RING方法就可以。

    总结

            整个响铃流程跟来电(MT)流程类似,但响铃并未涉及到界面的变换,在TeleService中调用到media中的ringtone.play()方法进行铃声的播放。

    特别须要注意的是整个过程中。大量的使用了观察者模式,最后使用Looper线程来做铃声播放/停止操作。


           文中涉及的时序图资源免积分下载。戳这里

    整个流程图例如以下:

  • 相关阅读:
    Appium 服务命令行参数
    DC 输入 输出 时钟 PVT设置
    .synopsys_dc.setup编写
    Excel VBA编程常用语句300句
    C# 泛型单例工厂
    C# Winform与JS交互
    SQL分析“聚集索引、非聚集索引”区别
    C# ClassHelper动态创建程序集和类, 添加/删除类属性
    从30个角度对比 PostgreSQL 和 MySQL
    C# 常用类和命名空间
  • 原文地址:https://www.cnblogs.com/yjbjingcha/p/8279991.html
Copyright © 2011-2022 走看看