1. 程式人生 > >Android5.0 Telephony框架初步分析--telecomm

Android5.0 Telephony框架初步分析--telecomm

Android5.0在Telephony的變化又比較大,增加了一個Telecomm模組,它位於介面應用如InCallUI和Phone框架之間,其具體的設計意圖尚不明確,從程式碼分析上來看,流程比原來的架構複雜很多,可能是想把Phone程序獨立得更開一些,類似於RIL程序,給應用提供一個扁平的Phone介面,不希望像以前一樣,呼叫流程在Phone程序和應用程序的縱深過大,引起函式的耦合性太大,有些相同的函式會在不同的流程執行在不同的程序裡面。

其改變前後的關係可用下面的圖來簡述:


之前函式的呼叫流程在不同程序間的介面不夠平滑,現在新增新的Telecomm層後,整個軟體框架就比較有序和易於管理。

對於telephony相關的部分,從5.0和4.4的程式碼比較來看,

1)在framework下面,5.0新增了telecomm和ims部分的程式碼,截圖如下,



結合後面的程式碼分析,我們知道在framework部分的telecomm程式碼的作用是承上啟下的關係,它通過aidl介面,一方面和Phone進行互動,這一層是以InCallService、ConnectionService為代表,它的相關部分會執行在Phone程序;另一方面,和TelecommApp程序下的服務如TelecomService通訊,這一層以TelecomManager等為代表,但他們往往執行在應用程序裡面。

2)在Package目錄下,5.0的應用部分InCallUI下沒有了manifest檔案,Service目錄則多了MMS、Telecomm目錄,MMS部分為短彩信增加了新的服務流程,telecomm部分則添加了一些關鍵檔案,如作為程序載體的TelecomApp.java檔案,作為服務載體的TelecomServiceImpl.java,還有CallsManager、CallActivity等檔案,這些檔案的關係和作用將在後面逐步分解。。



新增加的Phone.java在 (frameworks\base\telecomm\java\android\telecom)目錄下,其功能是“A unified virtual device providing a means of voice (and other) communication on a device.”,即作為一個虛擬裝置提供通訊服務。其型別如下,

           public final class Phone {

它的方法的實現主要依賴3個輔助類,InCallAdapter、Listener、Call,即呼叫介面卡、監聽器、控制器(?),後續展開分析。

對於Phone的初始化,我們可以通過其方法的呼叫關係找到,例如查詢internalAddCall,我們就會發現呼叫者為InCallService,其中mPhone即為Phone的例項,

case MSG_ADD_CALL:

mPhone.internalAddCall((ParcelableCall) msg.obj);

在InCallService裡,當收到MSG_SET_IN_CALL_ADAPTER訊息時,會建立一個Phone例項,同時也順帶建立了一個InCallAdapter例項,

                case MSG_SET_IN_CALL_ADAPTER:

                    mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj));

                    onPhoneCreated(mPhone);

                    break;          

MSG_SET_IN_CALL_ADAPTER訊息是setInCallAdapter發出的,它是在InCallService裡實現的AIDL介面類的服務端方法,

    private final class InCallServiceBinder extends IInCallService.Stub {

        @Override

        public void setInCallAdapter(IInCallAdapter inCallAdapter) {

            mHandler.obtainMessage(MSG_SET_IN_CALL_ADAPTER, inCallAdapter).sendToTarget();

        }          

根據我們對service的瞭解,它在service類建立時建立binder,在service的onbind()方法裡將binder例項傳入,執行客戶端的ServiceConnection的onServiceConnected方法,所以在客戶端,onConnected被執行,之後就是客戶端InCallController裡的setInCallAdapter被呼叫,再間接呼叫前面提到的服務端的setInCallAdapter。

private class InCallServiceConnection implements ServiceConnection {

        /** {@inheritDoc} */

        @Override public void onServiceConnected(ComponentName name, IBinder service) {

            Log.d(this, "onServiceConnected: %s", name);

            onConnected(name, service);

        } 

    private void onConnected(ComponentName componentName, IBinder service) {

        ThreadUtil.checkOnMainThread();

        Log.i(this, "onConnected to %s", componentName);

        IInCallService inCallService = IInCallService.Stub.asInterface(service);

        try {

            inCallService.setInCallAdapter(new InCallAdapter(CallsManager.getInstance(),

                    mCallIdMapper));

            mInCallServices.put(componentName, inCallService);

        } catch (RemoteException e) {

            Log.e(this, e, "Failed to set the in-call adapter.");

            return;

        }

所以可以看出,Phone的例項就是在InCallService服務啟動過程中建立的。

前面提到的binder的客戶端InCallController位於(packages\services\telecomm\src \com\android\server\telecom)目錄,根據manifest檔案,它執行在TelecomApp這個應用裡,這是一個的5.0新程序。又根據android:persistent="true"這個屬性我們知道,TelecomApp是開機自啟動的。

InCallController是在CallsManager的建構函式裡建立的,CallsManager又是在TelecomApp的onCreate方法裡面建立的,

    public void onCreate() {

        super.onCreate();

        if (UserHandle.myUserId() == UserHandle.USER_OWNER) {

            // Note: This style of initialization mimics what will be performed once Telecom is

            // moved

            // to run in the system service. The emphasis is on ensuring that initialization of all

            // telecom classes happens in one place without relying on Singleton initialization.

            mMissedCallNotifier = new MissedCallNotifier(this);

            mPhoneAccountRegistrar = new PhoneAccountRegistrar(this);

            mCallsManager = new CallsManager(this, mMissedCallNotifier, mPhoneAccountRegistrar);

            CallsManager.initialize(mCallsManager);

            mTelecomService = new TelecomServiceImpl(mMissedCallNotifier, mPhoneAccountRegistrar,

                    mCallsManager, this);

            ServiceManager.addService(Context.TELECOM_SERVICE, mTelecomService);

            // Start the BluetoothPhoneService

            BluetoothPhoneService.start(this);

        }

    }          

所以,TelecomApp程序的啟動過程中,建立了InCallController和CallsManager兩個例項,這兩個類例項相互關聯。

實際上,並不存在TelecomService這個名字的檔案或類名,但存在一個這樣的服務。

還是在TelecomApp的onCreate方法裡(如上),建立了一個TelecomServiceImpl類例項,它對應於TelecomService服務的AIDL服務端,它就是以類例項為服務端,並不像service裡面bind的binder介面,並且,它被作為一個服務新增到serviceManager裡面。

mTelecomService = new TelecomServiceImpl(mMissedCallNotifier, mPhoneAccountRegistrar,

                    mCallsManager, this);

ServiceManager.addService(Context.TELECOM_SERVICE, mTelecomService);

public class TelecomServiceImpl extends ITelecomService.Stub {          

TelecomServiceImpl依賴CallsManager、PhoneAccountRegistrar、MissedCallNotifier這幾個類完成相應的功能。

TelecomService的客戶端是TelecomManager,它通過getTelecomService獲取到服務端介面,然後通過這個介面使用服務端的遠端介面,

     private ITelecomService getTelecomService() {

        return ITelecomService.Stub.asInterface(ServiceManager.getService(Context.TELECOM_SERVICE));

    }

TelecomManager在framework的android.telecom包裡面,它在ContextImpl被建立,並加入到註冊列表裡,屬於系統級的服務,

        registerService(TELECOM_SERVICE, new ServiceFetcher() {

                public Object createService(ContextImpl ctx) {

                    return new TelecomManager(ctx.getOuterContext());

                }});

應用要獲取其例項,可以通過其from方法,也可以直接通過context.getSystemService(Context.TELECOM_SERVICE)這個語句來獲取。

TelecomManager的功能則主要是對TelecomService提供的遠端介面的封裝,然後提供給應用使用,例如對於showInCallScreen方法,在TelecomServiceImpl提供服務端的遠端方法,TelecomManager提供客戶端訪問介面,DialpadFragment等應用元件使用這個介面,

 TelecomServiceImpl.java (packages\services\telecomm\src\com\android\server\telecom):    public void showInCallScreen(boolean showDialpad)

TelecomManager.java (frameworks\base\telecomm\java\android\telecom):    public void showInCallScreen(boolean showDialpad)

DialpadFragment.java (packages\apps\dialer\src\com\android\dialer\dialpad):        getTelecomManager().showInCallScreen(showDialpad);

DialtactsActivity.java (packages\apps\dialer\src\com\android\dialer):            getTelecomManager().showInCallScreen(false);

雖然TelecomServiceImpl是在packages目錄下,TelecomManager在frameworks目錄下,但前者作為一個服務,在單獨程序裡為後者提供服務,後者則為應用程序提供介面服務,再通過binder程序通訊訪問前者的服務,其關係如下,


InCallService是一個抽象類,繼承於service,它由四大部分組成,Handler、InCallServiceBinder、VideoCall以及自身的一些方法,其中InCallServiceBinder是aidl介面的服務端實現,對應於前面提到的InCallController,它主要是給當前服務傳送訊息,Handler接收並處理這些訊息,如前面提到,Handler會建立一個Telecom的Phone例項,然後使用Phone的介面處理應用請求,所以實際上,客戶端的請求實際上是Phone來完成的,至於Phone是如何實現功能的,請參見Phone的分析,這裡的幾個關鍵類的相互關係如下,


InCallService的主要功能是給應用提供管理Phone call的途徑,它的子類InCallServiceImpl完成真正服務例項的建立,當存在呼叫連線時,它bind到Telecomm,並接受呼叫狀態的更新。

服務例項的繫結過程是在InCallController裡面完成的,

     private void bind() {…

            Intent serviceIntent = new Intent(InCallService.SERVICE_INTERFACE);

            for (ResolveInfo entry : packageManager.queryIntentServices(serviceIntent, 0)) {

                    InCallServiceConnection inCallServiceConnection = new InCallServiceConnection();

                    ComponentName componentName = new ComponentName(serviceInfo.packageName,

                            serviceInfo.name);

                        Intent intent = new Intent(InCallService.SERVICE_INTERFACE);

                        intent.setComponent(componentName);

                        if (mContext.bindServiceAsUser(intent, inCallServiceConnection,

                                Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {

                            mServiceConnections.put(componentName, inCallServiceConnection);

                        }

…}

這個bind()是在onCallAdded()裡被呼叫的,onCallAdded被呼叫的地方有兩個:

CallsManager.java (packages\services\telecomm\src\com\android\server\telecom):            listener.onCallAdded(call);

Phone.java (frameworks\base\telecomm\java\android\telecom):            listener.onCallAdded(this, call);

他們都是通過listener的方式被呼叫的,通過分析兩個類的listener,發現Phone的監聽器主要在應用檔案中註冊,

CallList.java (packages\apps\incallui\src\com\android\incallui):        mPhone.addListener(mPhoneListener);

InCallPresenter.java (packages\apps\incallui\src\com\android\incallui):        mPhone.addListener(mPhoneListener);

CallsManager的監聽器在其建構函式中註冊,並且監聽器型別為CallsManagerListener,

    private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap(            new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1));

     CallsManager(Context context, MissedCallNotifier missedCallNotifier,

             PhoneAccountRegistrar phoneAccountRegistrar) {…

        mCallLogManager = new CallLogManager(context);

        mInCallController = new InCallController(context);

        mListeners.add(statusBarNotifier);

        mListeners.add(mCallLogManager);

        mListeners.add(mPhoneStateBroadcaster);

        mListeners.add(mInCallController);

        mListeners.add(mRinger);

…}

所以是CallsManager. addCall呼叫了InCallController的onCallAdded。addCall則會被來電、去電、會議電話等介面方法呼叫,如startOutgoingCall。

所以當有通話連線要產生時,會啟動InCallService服務,其大致過程如下:



如果覺得我的文章對您有用,請打賞。您的支援是對我莫大的認可