Android Telephony Call分析

關於Call對象

一共4個
./packages/apps/Dialer/java/com/android/incallui/call/DialerCall.java
./frameworks/base/telecomm/java/android/telecom/Call.java
./packages/services/Telecomm/src/com/android/server/telecom/Call.java
./frameworks/opt/telephony/src/java/com/android/internal/telephony/Call.java

 

  • frameworks/opt/telephony/src/java/com/android/internal/telephony/Call.java沒有使用,看樣子google打算放棄使用,轉用packages/services這個了。
  • Call狀態最終都是通過CallsManager來廣播出去的,CallsManager承上啓下。通過不同Call的轉換,最終在ui上體現出來。InCallService是UI和telecom的接口。InCallController綁定InCallService。
  • 從下往上,Call的傳到是:
    com.android.server.telecom.call (系統進程)--> android.telecom.call(framgwork) --> com.android.incallui.DialerCall(Dialer進程)
  • com.android.server.telecom.call對象是撥打電話的時候(CallsManger.startOutgoingCall),或者收到來電intent的時候創建的(CallsManger.processIncomingCallIntent)。inCallController中的toParcelableCall函數會new ParcelableCall對象,參數是com.androidservice.telecom.call對象。toParcelableCall有兩個地方可以調到,一個是onConnected的時候,就是inCallController綁定到incallService後,如果這個時候call不爲空,就會走;還有一個地方是CallsManager回調onCallAdded的時候,也會調用。android.telecom.call就是通過這個ParcelableCall對象的相關信息來創建的。即完成了com.android.server.telecom.call到android.telecom.call的映射。
  • incallservice會在綁定的時候,把自己註冊到phone的listener中。當incallservice的客戶端(InCallController)調用addcall的時候,會傳過來一個parcelableCall的對象,incallservice會調用phone.internalAddCall來處理,在internalAddCall中根據這個parcelableCall對象生成telecomCall對象,然後phone通過fireCallAdded把這個call對象通過回調傳到incallservice中,incallservice調用onCallAdded,這個實現在incallui裏面,即完成了android.telecom.call到 com.android.incallui.call的映射。
  • android.telecom.call作爲構造參數創建com.android.incallui.DialerCall,同時incallui.DialerCall註冊telecommCall的回調函數,這樣有變化的時候,telecommCall會通知incallui.DialerCall來更新UI.

一,Diale裏面的Call ,InCallUi  DialerCall.java

在以前沒有吧InCallUi合進Dialer中的時InCallUi中只有一個Call.java,現在重新構建了一遍,吧InCallUi放入Dialer中(要看商場怎麼分離)。

我們來看一下DialerCall的構造方法:

public DialerCall(
      Context context,DialerCallDelegate dialerCallDelegate,Call telecomCall,LatencyReport latencyReport,boolean registerCallback) {
    Assert.isNotNull(context);
   .....
}

這裏的Call是framework telecomm  Call中傳入的,調用邏輯圖:

大致邏輯  Call先是從系統服務InCallContrller.java中通過AIDL傳給framework中的InCallSerivce.java中在通過InCallSerive調用子類方法進行傳入。

Dialer創建是在:CallList.java中onCallAdded初始化:

public void onCallAdded(
      final Context context, final android.telecom.Call telecomCall, LatencyReport latencyReport) {
    Trace.beginSection("onCallAdded");
    final DialerCall call =
        new DialerCall(context, this, telecomCall, latencyReport, true /* registerCallback */);
  
......
if (call.getState() == DialerCall.State.INCOMING
        || call.getState() == DialerCall.State.CALL_WAITING) {
      onIncoming(call);  //來電主核心
    } else {
      dialerCallListener.onDialerCallUpdate();
    }
}

這個onCallAdded是InCallServiceImpl中調用的,而InCallSerivceImpl中的onCallAdded方法是其父類直接調用,他的父類就是framewok裏面的InCallSerivce.java其實他是一個服務,等待系統服務調用。

二,Serivce.Telecomm 中的Call 

Telecom Call  
framework/base/telecomm/src/java/android/telecomm/Phone.java

public final class Call {}

他是一個被定義成final類型的類,它是在Phone.java中被創建的internalAddCall()方法中被創建的.interanalAddCall()他是在InCallService中的Hanlder(MSG_ADD_CALL)調用,來電或去點都會被調用這個方法.

 final void internalAddCall(ParcelableCall parcelableCall) {//parcelabeCall是通過AIDL進行傳輸的,所有需要轉換
        //從ParcelableCall中取出信息用於new Telecom Call
        Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
                parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
        mCallByTelecomCallId.put(parcelableCall.getId(), call);
        //添加調集合中
        mCalls.add(call);
        checkCallTree(parcelableCall);
        call.internalUpdate(parcelableCall, mCallByTelecomCallId);
        fireCallAdded(call);
     }

這裏的ParcelableCall是一箇中間者的角色,在InCallController.java中先將Telecom Service中的Call轉換成ParcelableCall傳入.

  ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall(
                        call,
                        videoProviderChanged /* includeVideoProvider */,
                        mCallsManager.getPhoneAccountRegistrar(),
                        info.isExternalCallsSupported(),
                        rttInfoChanged && info.equals(mInCallServiceConnection.getInfo()));
                ComponentName componentName = info.getComponentName();
                IInCallService inCallService = entry.getValue();
                componentsUpdated.add(componentName);

       inCallService.updateCall(parcelableCall);//通過AIDL調用Framework中的數據

總結:然後通過ParcelableCallUtils進行轉換,轉換成Telecom Call, 這樣子就實現了serviceTelecomm傳入給frameworkTelecomm

 

三,Service Telecomm 系統中Telecomm

路徑:package/service/telecomm/src/com/android/service/telecomm/Call.java

public class Call implements CreateConnectionResponse, EventManager.Loggable,
        ConnectionServiceFocusManager.CallFocus {}

Call實現了CreateConnectionResponse接口,說明他負責與Connection創建之後處理一些事件,比如創建Connection之後需要UI界面的一些刷新以及更新。

Service Telecom Call是通話流程中最重要的Call對象,他擁有管理一通電話的能力,(answer,reject,hold,disconnect等等),他由CallsManager創建管理

在通過過程中,CallsManager是這樣管理的:

CallsManager.java
來電創建Call對象 processInComingCallIntent()
去電創建Call對象

startOutgoingCall()

發起撥號請求 placeOutgoingCall()
設置狀態 setCallState()
主動掛斷 disconnectCall()

 

四,framework opt中的Call

framework/opt/telephony/src/android/internel/Call.java

public abstract class Call {
}

他是一個抽象類。

繼承結構

我們這裏關注一下GsmCdmaCall的關係:

 

GsmCdmaPhone初始化了GsmCdmaCallTracker,GsmCdmaCallTracker是負責管理GsmCdmaCall和GsmCdmaConnection的操作類,GsmCdmaCallTracker裏面有一個GsmCdmaConnection的數組:

public GsmCdmaConnection[] mConnections;

並且有常量控制着mConnections數組數組的大小,一個GsmCdmaConnection代表着一通電話,說明GSM最大允許同時存在19通,CDMA最大同時存在8通。

    public static final int MAX_CONNECTIONS_GSM = 19;   //7 allowed in GSM + 12 from IMS for SRVCC
    private static final int MAX_CONNECTIONS_PER_CALL_GSM = 5; //only 5 connections allowed per call

    private static final int MAX_CONNECTIONS_CDMA = 8;
    private static final int MAX_CONNECTIONS_PER_CALL_CDMA = 1; //only 1 connection allowed per call

同時,GsmCdmaCallTracker的內部也會創建三個GsmCdmaCall(GsmCdmaCall僅僅會在GsmCdmaCallTracker中被創建,創建之後不會再被重新賦值):

 

    public GsmCdmaCall mRingingCall = new GsmCdmaCall(this);
    // A call that is ringing or (call) waiting
    public GsmCdmaCall mForegroundCall = new GsmCdmaCall(this);
    public GsmCdmaCall mBackgroundCall = new GsmCdmaCall(this);

Telephony Framework Call的狀態有9種:

    public enum State {
        IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;

        public boolean isAlive() {
            return !(this == IDLE || this == DISCONNECTED || this == DISCONNECTING);
        }

        public boolean isRinging() {
            return this == INCOMING || this == WAITING;
        }

        public boolean isDialing() {
            return this == DIALING || this == ALERTING;
        }
    }

問題1:那麼mRingingCall,mForegroundCall,mBackgroundCall分別對應Call的什麼狀態呢?

由於Telephony Framework Call的”ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING”這六種狀態跟DriverCall.State是一一對應的
 

public static State
    stateFromDCState (DriverCall.State dcState) {
        switch (dcState) {
            case ACTIVE:        return State.ACTIVE;
            case HOLDING:       return State.HOLDING;
            case DIALING:       return State.DIALING;
            case ALERTING:      return State.ALERTING;
            case INCOMING:      return State.INCOMING;
            case WAITING:       return State.WAITING;
            default:            throw new RuntimeException ("illegal call state:" + dcState);
        }
    }

在GsmCdmaConnection中有依據DriverCall.State將GsmCdmaCall分類的方法,根據state來返回相應的對象

private GsmCdmaCall
    parentFromDCState (DriverCall.State state) {
        switch (state) {
            case ACTIVE:
            case DIALING:
            case ALERTING:
                return mOwner.mForegroundCall;
            //break;

            case HOLDING:
                return mOwner.mBackgroundCall;
            //break;

            case INCOMING:
            case WAITING:
                return mOwner.mRingingCall;
            //break;

            default:
                throw new RuntimeException("illegal call state: " + state);
        }
    }

所以mRingingCall,mForegroundCall,mBackgroundCall與GsmCdmaCall.mState的關係如下:

mRingingCall INCOMING,WAITING
mForegroundCall ACTIVE,DIALING,ALERTING
mBackgroundCall HOLDING

GsmCdmaCallTracker在初始化的時候就註冊監聽了Call狀態變化的消息,

    public GsmCdmaCallTracker (GsmCdmaPhone phone) {
        this.mPhone = phone;
        mCi = phone.mCi;
        mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);//註冊EVENT_CALL_STATE_CHANGE 狀態
        mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
        mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);

        // Register receiver for ECM exit
        IntentFilter filter = new IntentFilter();
        filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
        mPhone.getContext().registerReceiver(mEcmExitReceiver, filter);

        updatePhoneType(true);
    }

所以當modem中Call狀態發生變化後,便會通知到GsmCdmaCallTracker,GsmCdmaCallTracker通過調用RILJ的getCurrentCalls()方法發起查詢modem當前的Call狀態列表,modem返回來的結果是DriverCall 集合。
再由GsmCdmaCallTracker的handlePollCalls()方法來對比自身mConnections集合與DriverCall 集合的差異,進而依據DriverCall的信息跟新這對應GsmCdmaCall(mRingingCall,mForegroundCall,mBackgroundCall)的狀態,同時將當前GsmCdmaConnection與對應的GsmCdmaCall綁定。
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章