This article from http://blog.csdn.net/yihongyuelan reprint please be sure to indicate the source
The code in this article takes the MTK platform Android 4.4 as the analysis object, which is somewhat different from Google's native AOSP. Please be aware of it.
Pre-Article: Analysis of Working Process of Android 4.4 Kitkat Phone (1) _ overview and learning plan
Android 4.4 Kitkat Phone workflow analysis (ii) _ UI Structure Analysis
Android 4.4 Kitkat Phone workflow analysis (iii) _ MO (Power removal) Process Analysis
Analysis of Android 4.4 Kitkat Phone workflow (iv) _ RILJ Workflow
Android 4.4 Kitkat Phone workflow analysis (v) _ MT (incoming call) Process Analysis
Android 4.4 Kitkat Phone workflow analysis (6) _ InCallActivity display update process
Overview
This series of articles focuses on the MT/MO process and provides additional instructions on the details, such as the call ring process. The analysis of the MT process has covered the initiation and termination of the process. The ringing process described in this article begins with the initiation of the MT process, if you are not familiar with the MT process, see the article "Android 4.4 Kitkat Phone workflow analysis (v) _ MT (incoming call) process Analysis and Android 4.4 Kitkat Phone workflow analysis (6) _ InCallActivity display update process.
Android 4.4 has changed the logging process and released the logging trigger to TeleService. This also conforms to the 4.4 Phone design style, namely, separation of UI and Logic. When the incoming call process is initiated, capture the radio_log for analysis and you can see that the status of the entire incoming call process changes as follows:
HandleMessage (messages) // set the voice call flag handleMessage (event_new_ring_connection) // The new call flag handleMessage (messages) // handleMessage (EVENT_INCOMING_RING) when the status changes) // After receiving the AT command CLIP, update the call information (CLIP indicates the call display function) handleMessage (EVENT_INCOMING_RING) handleMessage (messages) handleMessage (EVENT_INCOMING_RING) handleMessage (handle) handleMessage (EVENT_INCOMING_RING) handleMessage (EVENT_CRSS_SUPP_SERVICE_NOTIFICATION) handleMessage (EVENT_DISCONNECT) // disconnect handleMessage (disconnect) // handleMessage (disconnect) when the status changes)
These logs are printed in CallManager. Of course, we can also view the corresponding logs in RIL. java.
After receiving the incoming message, the Modem side performs the following operations::
1. Set INCOMING_INDICATION Based on the incoming call type;
2. Initiate NEW_RINGING_CONNECTION, new call sign;
3. The CALL_STATE_CHANGED status is triggered, and the interface is updated when the status changes;
4. Initiate the ring notification INCOMING_RING, and the ring process is initiated accordingly;
5. Trigger CRSS_SUPP_SERVICE_NOTIFICATION Based on the returned information of CLIP. This is added by MTK and its function is to update the Call information based on the returned information of CLIP;
6. Loop the four or five steps above and keep ringing;
7. DISCONNECT. The MT process ends (not answered );
8. The CALL_STATE_CHANGED status is triggered, and the interface is updated when the status changes;
When the incoming call message arrives, the Telephony Framework sends it to the Telephony framework after being processed by the Modem and RILD, And the RILJ continues to initiate it. The flowchart is as follows:
RILJ
After the MT process is initiated, related unsolicited information will be fed back to RILJ. Here we mainly focus on the ringing_flow process, so we can find the log information returned by the following AT command:
01-01 02:46:01.154 4753 4768 D use-Rlog/RLOG-AT: +CRING: VOICE01-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,"",001-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: VOICE01-01 02:46:01.154 4753 4768 D use-Rlog/RLOG-AT: RIL_URC_READER:+CRING: VOICE01-01 02:46:01.154 4753 4768 D use-Rlog/RLOG-AT: RIL_URC_READER Enter processLine01-01 02:46:01.154 4753 4768 D use-Rlog/RLOG-RIL: Nw URC:+CRING: VOICE01-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!!!!!!
The + CRING: VOICE log is printed every 3877ms in the entire radio_log. There are five times before the call is suspended. According to the AT return information, the incoming call type is Voice and it is a ring event. Then the corresponding event RIL_UNSOL_CALL_RING is received in RILJ:
01-01 02:46:02.155 1443 1837 D RILJ : RIL(2) :[UNSL RIL]< UNSOL_CALL_RING [C@422899d0
Here is the UnSolicited event, which triggers the processUnsolicited method and runs it in the following code:
Case RIL_UNSOL_CALL_RING: if (RILJ_LOGD) unsljLogRet (response, ret); if (mRingRegistrant! = Null) {// observer mode mRingRegistrant. policyregistrant (new AsyncResult (null, ret, null ));}In the previous article, we have analyzed the trigger mode Registrant. Here, we will analyze the jump location after the policyregistrant () method is triggered.
First, we can see that the mRingRegistrant definition is in SourceCode/frameworks/opt/telephony/src/java/com/android/internal/telephony/BaseCommands. java, and the value is assignedSetOnCallRingMethod, as follows:
@Override public void setOnCallRing(Handler h, int what, Object obj) { mRingRegistrant = new Registrant (h, what, obj); }The setOnCallRing method is called in the construction method of SourceCode/frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneBase. java, as follows:
mCi.setOnCallRing(this, EVENT_CALL_RING, null);
MCi is the object of CommandsInterface, while BaseCommands implements CommandsInterface, that is, the mRingRegistrant value should be assigned when the PhoneBase constructor is called.
Pay attention to the following two points:
(1). PhoneBase extends Handler. Therefore, this in setOnCallRing represents PhoneBase itself. In other words, when policyregistrant is triggered, it will be called back to the handleMessage of PhoneBase;
(2) EVENT_CALL_RING in setOnCallRing indicates that when policyregistrant is triggered, the corresponding case in handleMessage is called back;
MRingRegistrant registration process
When the Phone is started for the first time, the makedefaphone Phone () method in PhoneFactory is executed to complete the Phone initialization. During the initialization process, the mRingRegistrant registration is completed.
After running the mRingRegistrant. yyregistrant () method, PhoneBase jumps to the handleMessage method of PhoneBase. The Code is as follows:
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 {// execute notifyIncomingRing ();} break here;Here we will continue to check the policyincomingring () method:
private void notifyIncomingRing() { if (!mIsVoiceCapable) return; AsyncResult ar = new AsyncResult(null, this, null); mIncomingRingRegistrants.notifyRegistrants(ar);}The observer mode is also used to find the corresponding registerXXX method, as follows:
@Override public void registerForIncomingRing( Handler h, int what, Object obj) { checkCorrectThread(h); mIncomingRingRegistrants.addUnique(h, what, obj); }You can find that the IncomingRing register method is called in the registerForPhoneStates method of CallManager:
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 is omitted. registerForIncomingRing (mHandler, EVENT_INCOMING_RING + offset, null );The MTK dual-card mechanism is involved here, and you may wonder how to determine whether it is called here? We can reverse-Think about it. When we find that the call is made here, we can reverse-look at where registerForPhoneStates was called and view it in sequence. In the end, we can see that these are called sequentially during Phone initialization. We just look for them in turn.
The code above in CallManager can be used to know:
(1). mHandler defines in CallManager and the related handleMessage can be found;
(2) case event: EVENT_INCOMING_RING;
MIncomingRingRegistrants registration process
The registration process of mIncomingRingRegistrants begins with the registerForPhoneStates () method in CallManager when the Phone is started and initialized. Why do I directly jump from CallManager to PhoneBase? In fact, the targetPhone object is passed through PhoneProxy, while PhoneProxy is the proxy of GSMPhone and CDMAPhone. Both GSMPhone and CDMAPhone inherit from PhoneBase, and the final implementation is also in PhoneBase, some jumps are omitted here. Please be aware of them.
CallManager
According to the previous analysis, the mIncomingRingRegistrants. policyregistrants () method is called in the notifyIncomingRing () method of PhoneBase. The handleMessage method and EVENT_INCOMING_RING corresponding to the event processing in CallManager can be found:
@ Overridepublic void handleMessage (Message msg) {int index; switch (msg. what ){//...... case EVENT_INCOMING_RING: case EVENT_INCOMING_RING + icationication_id_offset: case EVENT_INCOMING_RING + (icationication_id_offset * 2): case EVENT_INCOMING_RING + (icationication_id_offset * 3): if (! HasActiveFgCall () {index = (msg. what-EVENT_INCOMING_RING)/NOTIFICATION_ID_OFFSET; mIncomingRingRegistrantsGemini [index]. policyregistrants (AsyncResult) msg. obj); mIncomingRingRegistrants. policyregistrants (AsyncResult) msg. obj);} break;Check the registerXXX method of mIncomingRingRegistrantsGemini and mIncomingRingRegistrants. The Code is as follows:
// MIncomingRingRegistrantsGemini's registe method public void registerForIncomingRingEx (Handler h, int what, Object obj, int simId) {int index = getRegistrantsArrayIndex (simId); if (index! =-1) {mIncomingRingRegistrantsGemini [index]. addUnique (h, what, obj) ;}// regiiste method of mIncomingRingRegistrants public void registerForIncomingRing (Handler h, int what, Object obj) {mIncomingRingRegistrants. addUnique (h, what, obj );}The above method is called Based on the mobile phone system. If it is a dual-card, Gemini is called. You can find it in CallManagerWrapper:
Public static void registerForIncomingRing (Handler handler, int what, Object obj) {if (GeminiUtils. isGeminiSupport () {final int [] geminiSlots = GeminiUtils. getSlots (); for (int geminiSlot: geminiSlots) {// double card CallManager. getInstance (). registerForIncomingRingEx (handler, what, obj, geminiSlot) ;}} else {// single-card CallManager. getInstance (). registerForIncomingRing (handler, what, obj );}}The above method is also packaged in CallManagerWrapper:
public static void registerForIncomingRing(Handler handler, int what) { registerForIncomingRing(handler, what, null);}So where are the subsequent calls? We can find the following in the registerForNotifications () method of CallStateMonitor:
CallManagerWrapper.registerForIncomingRing(this, PHONE_INCOMING_RING);
The registerForNotifications () method is called when CallStateMonitor is initialized and the Radio status changes.
Through the above analysis, we can know:
(1). register the call callback in CallStateMonitor, that is, this. CallStateMonitor is inherited from Handler, then the yyregistrants () method in CallManager will jump to CallStateMonitor;
(2). The corresponding case event is: PHONE_INCOMING_RING;
MIncomingRingRegistrantsGemini registration process
The entire registration process starts when TeleService is started. TeleService listens for BOOT_COMPLETE broadcasts and starts the registration process after random startup. In step 2, you must note that the two-card CallManager. getInstance (). registerForIncomingRingEx () is executed, and the single-card CallManager. getInstance (). registerForIncomingRing () is executed ().
When TeleService handles a Ring Event, the Telephony Framework reports the Ring Event to CallStateMonitor, and starts the ring operation In TeleService. The flowchart is as follows:
After the CallStateMonitor is processed by the framework, the Ring Event is passed to the CallStateMonitor of TeleService. The preceding analysis shows that in the handleMessage of CallManager, The mIncomingRingRegistrantsGemini [index]. policyregistrants () method is used to jump to the handleMessage of CallStateMonitor, as shown below:
@Overridepublic void handleMessage(Message msg) { for (Handler handler : registeredHandlers) { handler.handleMessage(msg); }}The callback is triggered Based on registerHandler. During the analysis of the Call (MT) process, CallNotifier and CallModeler register the Handler callback of CallStateMonitor, only CallNotifier can process the PHONE_INCOMING_RING event. Therefore, we need to check how CallNotifier handles the Ring Event. CallNotifier finds the Processing Event of PHONE_INCOING_RING in the handleMessage method of CallNotifier as follows:
@ Overridepublic void handleMessage (Message msg) {// ...... omitting case CallStateMonitor. PHONE_INCOMING_RING: log ("PHONE_INCOMING_RING! "); If (msg. obj! = Null & (AsyncResult) msg. obj). result! = Null) {//... omitting if (provisioned &&! IsRespondViaSmsDialogShowing () {mRinger. ring () ;}} else {if (DBG) log ("RING before NEW_RING, skipping") ;}} break;Here, the ring () method of Ringer is called to start logging.
Ringer should prepare to play the ringtone here, but there are a series of things to be done in the ring () method:
Void ring () {synchronized (this ){//...... omit // create logoff thread, used to play/stop the ringtone, receives the play/Stop request makelovel () through handleMessage (); // if this is the first time the video is played, mFirstRingEventTime =-1 if (mFirstRingEventTime <0) {// obtain the time after the system is started, including sleep mFirstRingEventTime = SystemClock. elapsedRealtime (); if (mRingHandler! = Null) {// initiate the request for playing the ringtone mRingHandler. sendEmptyMessage (PLAY_RING_ONCE) ;}} else {// if it is not the first playback if (mFirstRingStartTime> 0) {if (mRingHandler! = Null) {// send a playback request with a latency of 133 ms (mRingHandler. sendEmptyMessageDelayed (PLAY_RING_ONCE, mFirstRingStartTime-mFirstRingEventTime) ;}} else {mFirstRingEventTime = SystemClock. elapsedRealtime ();}}}}Here, we should pay special attention to the fact that a logoff thread is created using the makelovel () method for playing/stopping ringtones. Why should we use logoff here? The sendEmptyMessage () and sendEmptyMessageDelayed () methods are used to initiate the request for playing the ringtone. Next, we will analyze the makelostrap () Construction and the reason for Logoff.
The makelo () method mainly creates a ringer thread for playing/stopping ringtones. The Code is as follows:
Private void makeLooper () {// if the first ring Bell mRingThread = null if (mRingThread = null) {// Worker implements the Runnable interface, in its constructor Worker (String name) // create and start a Worker thread named "ringer" mRingThread = new Worker ("ringer "); // if the logoff object of the ringer thread has not been obtained, if (mRingThread. getLooper () = null) {return;} // create a Looper object, mRingHandler = new Handler (mRingThread. getLooper () {@ Override public void handleMessage (Message m Sg) {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 (); // play the ringtone r. play (); synchronized (Ringer. this) {// assign the first playback time (the start time includes sleep) to mFirstRingStartTime if (mFirstRingStartTime <0) {mFirstRingStartTime = SystemClock. elapsedRealtime () ;}} break; case STOP_RING: if (DBG) log ("mRingHandler: STOP_RING... "); r = (Ringtone) msg. obj; if (r! = Null) {// stop the playing ringtone r. stop () ;}else {if (DBG) log ("-STOP_RING with null ringtone! Msg = "+ msg);} // exit logoff loop getLooper (). quit (); break;
MakeLooper () mainly does three things:
(1). Create and start the logoff thread named ringer;
(2) instantiate mHandler and attach it to ringer's logoff object;
(3) receive and process the Message of playing/stopping the ringtone;We can see that makelovel has done a very important thing, especially for the third point, the subsequent processing of the play/Stop ringtone will be in the handleMessage.
Next, let's take a look at logoff. We can see the implementation of the Worker class as follows:
Private class Worker implements Runnable {// create mLock private final Object mLock = new Object (); private lolock mlolock; Worker (String name) {// create and start a Thread named "name" Thread t = new Thread (null, this, name); t. start (); synchronized (mLock) {while (mLooper = null) {try {// blocking until the previous "name" thread has run successfully (the run method is executed ), the maximum blocking time is 5 s mLock. wait (5000) ;}catch (InterruptedException ex) {}}} public Looper getlotion () {// return the Looper object return mlotion of the "name" thread ;} public void run () {synchronized (mLock) {// enable Looper. prepare (); // return The logoff object of the current thread (that is, the sub-thread "name. mylogoff (); // wake up the lock and no longer block mLock. notifyAll ();} // enable logoff loop logoff. loop ();} public void quit () {// exit logoff loop mloit. quit ();}}Logoff is used to enable the message loop in the thread. By default, a common thread does not have a message loop. prepare () enables the message loop through logoff. loop () processes the message loop until the thread loop ends. We can call the logoff quit () method to exit the loop. (For the official explanation of logoff, the answer on StackOverFlow, or view the analysis by CSDN Users)
Back to the previous question, why should I use logoff here? Use a thread to play the ringtone. At the same time, you need to stop playing the ringtone according to different statuses, that is
When a thread is required to do something, and you want to control the start and stop of the task, you can use Logoff.
When the first request is received, enable the thread to start the Loop and play the ringtone. Through the analysis of Radio_log, RILJ receives a ringing request every 3.877s. At this time, the request is reported and uploaded to TeleService. If the playing of the ringtone is complete, ringtone is executed. play () method to start playing the ringtone. If the previous ringtone has not been played, ringtone will not be started again. play () method. If the caller or user hangs up at this time, the ring must be terminated immediately. In this case, you only need to process the STOP_RING method in the Child thread.
Summary The entire ring tones process is similar to that of laidian (MT), but the ring tones do not involve interface changes. In TeleService, the ringtone. play () method in media is called for playing the ringtones. Note that the observer mode is widely used throughout the process, and The logoff thread is used to play or stop the ringtone.
Point-free download of the sequence chart resources involved in this article. Click here.
The flowchart is as follows: