来电流程

<h2 align = "center">来电流程</h2>


概述:来电的过程,是在去电(拨号)的基础上分析的。

来电过程, 是由com.android.phone进程发起的,因为 com.android.phone 进程直接与Moderm层交互, com.android.phone 进程收到来来电消息后,发送消息给 system 进程, system 进程开始和com.android.phone 进程建立链接, 并通知 UI 进程 (com.android.dialer) 更新。大体上和拨号过程类似。

com.android.phone 进程

1.开机初始化的过程

PhoneApp 是 com.android.phone 进程的 Application 实例, 在它的 onCreate 中

public void onCreate() {
        if (UserHandle.myUserId() == 0) {

            // 创建 Phones RIL 等
            mPhoneGlobals = new PhoneGlobals(this);
            mPhoneGlobals.onCreate();

            mTelephonyGlobals = new TelephonyGlobals(this);

            // 主要是监听 phone 的状态变化
            mTelephonyGlobals.onCreate();
        }
    }

mTelephonyGlobals.onCreate() ==> TelecomAccountRegistry.setupOnBoot()

void setupOnBoot() {

    listenPhoneState();

}
private void listenPhoneState() {
        Phone[] phones = PhoneFactory.getPhones();
        for (Phone phone : phones) {
            int subscriptionId = phone.getSubId();
            if (subscriptionId >= 0 && !mPhoneStateListeners.containsKey(subscriptionId)) {
                // ...

                PhoneStateListener listener = new PhoneStateListener(subscriptionId) {
                    @Override
                    public void onServiceStateChanged(ServiceState serviceState) {
                        // ...
                        if (rebuildAccounts) {

                            tearDownAccounts();
                            // 只关注 setupAccounts
                            setupAccounts();
                            broadcastAccountChanged();
                        }

                    }
                };
                // 注册监听到 TelephonyRegistry, 如果在开机, 或者更换手机卡时, 这个listener将被触发
                mTelephonyManager.listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
                mPhoneStateListeners.put(subscriptionId, listener);
            }
        }
    }

为每个 Phone 设置 AccountEntry。

private void setupAccounts() {
        // Go through SIM-based phones and register ourselves -- registering an existing account
        // will cause the existing entry to be replaced.
        Phone[] phones = PhoneFactory.getPhones();
        Log.d(this, "Found %d phones.  Attempting to register.", phones.length);
        for (Phone phone : phones) {
            int subscriptionId = phone.getSubId();
            Log.d(this, "Phone with subscription id %d", subscriptionId);
            if (subscriptionId >= 0) {

                // 每一个 subscriptionId >= 0 的 phone 对应一个 AccountEntry
                mAccounts.add(new AccountEntry(phone, false /* emergency */, false /*isDummy*/));
            }
        }
        // ... 省略大量代码
    }

AccountEntry 的构造函数

AccountEntry(Phone phone, boolean isEmergency, boolean isDummy) {
            mPhone = phone;
            mAccount = registerPstnPhoneAccount(isEmergency, isDummy);

            // 来电通知监听。 核心过程
            mIncomingCallNotifier = new PstnIncomingCallNotifier((PhoneProxy) mPhone);
            mPhoneCapabilitiesNotifier = new PstnPhoneCapabilitiesNotifier((PhoneProxy) mPhone,
                    this);
        }

PstnIncomingCallNotifier 的构造函数 ==> registerForNotifications()

private void registerForNotifications() {
    Phone newPhone = mPhoneProxy.getActivePhone();
    if (newPhone != mPhoneBase) {
        unregisterForNotifications();

        if (newPhone != null) {
            Log.i(this, "Registering: %s", newPhone);
            mPhoneBase = newPhone;

            // 为 注册监听 来电事件
            mPhoneBase.registerForNewRingingConnection(
                    mHandler, EVENT_NEW_RINGING_CONNECTION, null);
            mPhoneBase.registerForCallWaiting(
                    mHandler, EVENT_CDMA_CALL_WAITING, null);
            mPhoneBase.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION,
                    null);

            mPhoneBase.registerForVoiceCallIncomingIndication(
                    mHandler, EVENT_VOICE_CALL_INCOMING_INDICATION , null);

        }
    }
}

2.收到来电消息,通知 system 进程

收到来电通知时, 会触发 mHandler.handleMessage, (句柄 EVENT_NEW_RINGING_CONNECTION), 直接调用 handleNewRingingConnection

private void handleNewRingingConnection(AsyncResult asyncResult) {
    Log.d(this, "handleNewRingingConnection");
    Connection connection = (Connection) asyncResult.result;
    if (connection != null) {
        Call call = connection.getCall();
        // Final verification of the ringing state before sending the intent to Telecom.
        if (call != null && call.getState().isRinging()) {
            // 流程将会进入这里
            sendIncomingCallIntent(connection);
        }
    }
}
private void sendIncomingCallIntent(Connection connection) {
    // ...
    PhoneAccountHandle handle = findCorrectPhoneAccountHandle();
    if (handle == null) {
        try {
            connection.hangup();
        } catch (CallStateException e) {
            // connection already disconnected. Do nothing
        }
    } else {
        // handle 不为空, 流程进入这里
        TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall(handle, extras);
    }
}

TelecomManager 通过 TelecomService 的跨进程接口 addNewIncomingCall , 给 system 进程发送消息。

3. RIL收到来电消息,通知Phone, Phone通知其监听者

有来电时, RIL 将会收到 RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED 消息, 因为 GsmCallTracker 监听了 RIL, GsmCallTracker 的 handleMessage(事件是EVENT_CALL_STATE_CHANGE) 被触发,接着调用 CallTracker.pollCallsWhenSafe 再次获取消息, 最后会进入 GsmCallTracker 的 handlePollCalls。

handlePollCalls(AsyncResult ar) {

    Connection newRinging = null; //or waiting

    // ...  省略大量处理代码, 如果有来电 newRinging 不为空

    if (newRinging != null) {
        if (DBG_POLL) log("notifyNewRingingConnection");

        // 通知 phone 的监听者, 由新来电。
        mPhone.notifyNewRingingConnection(newRinging);

    }

    // ...
}

system 进程

流程来到 TelecomServiceImpl 的 addNewIncomingCall

public void addNewIncomingCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
            synchronized (mLock) {
                Log.i(this, "Adding new incoming call with phoneAccountHandle %s",
                        phoneAccountHandle);
                if (phoneAccountHandle != null && phoneAccountHandle.getComponentName() != null) {

                    try {
                        Intent intent = new Intent(TelecomManager.ACTION_INCOMING_CALL);
                        intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
                            phoneAccountHandle);
                        intent.putExtra(CallIntentProcessor.KEY_IS_INCOMING_CALL, true);
                        if (extras != null) {
                            intent.putExtra(TelecomManager.EXTRA_INCOMING_CALL_EXTRAS, extras);
                        }

                        // 流程进入这里
                        CallIntentProcessor.processIncomingCallIntent(mCallsManager, intent);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    }
                } else {
                    Log.w(this,
                            "Null phoneAccountHandle. Ignoring request to add new incoming call");
                }
            }
        }

CallIntentProcessor.processIncomingCallIntent ==> callsManager.processIncomingCallIntent

void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) {
    Log.d(this, "processIncomingCallIntent");
    Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
    if (handle == null) {
        // Required for backwards compatibility
        handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER);
    }

    // 创建一个 call,这里不同于 去电,去电是调用 startOutgoingCall 创建 call 的
    Call call = new Call(
            mContext,
            this,
            mLock,
            mConnectionServiceRepository,
            mContactsAsyncHelper,
            mCallerInfoAsyncQueryFactory,
            handle,
            null /* gatewayInfo */,
            null /* connectionManagerPhoneAccount */,
            phoneAccountHandle,
            true /* isIncoming */,
            false /* isConference */);

    call.setIntentExtras(extras);
    /// M: For VoLTE @{
    if (TelecomVolteUtils.isConferenceInvite(extras)) {
        call.setIsIncomingFromConfServer(true);
    }
    /// @}

    // TODO: Move this to be a part of addCall()
    call.addListener(this);

    // 接下来是创建 和 com.android.phone 进程之间的链接的过程
    // 和 去电的过程是类似的。
    call.startCreateConnection(mPhoneAccountRegistrar);
}

由去电的流程知道,当CallsManager.addCall 被调用后, 会触发InCallActicity 启动。 去电和来电第一次调用 addCall 的时机不一样

  • 去电在新建Call的时候,就调用了addCall.
  • 来电则是在call.startCreateConnection 执行后, system 进程和 com.android.phone 进程建立链接成功后, 由 com.android.phone 进程告诉 system 进程 handleCreateConnectionSuccess, 然后经过层层回调,来到 Call.handleCreateConnectionSuccess
public void handleCreateConnectionSuccess(
            CallIdMapper idMapper,
            ParcelableConnection connection) {
        Log.v(this, "handleCreateConnectionSuccessful %s", connection);
        setTargetPhoneAccount(connection.getPhoneAccount());
        setHandle(connection.getHandle(), connection.getHandlePresentation());
        setCallerDisplayName(
                connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation());
        setConnectionCapabilities(connection.getConnectionCapabilities());
        setVideoProvider(connection.getVideoProvider());
        setVideoState(connection.getVideoState());
        setRingbackRequested(connection.isRingbackRequested());
        setIsVoipAudioMode(connection.getIsVoipAudioMode());
        setStatusHints(connection.getStatusHints());
        setExtras(connection.getExtras());

        mConferenceableCalls.clear();
        for (String id : connection.getConferenceableConnectionIds()) {
            mConferenceableCalls.add(idMapper.getCall(id));
        }
        // 未知电话, 通知 CallsManager.onSuccessfulUnknownCall
        if (mIsUnknown) {
            for (Listener l : mListeners) {
                l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState()));
            }
        // 来电, 由 mDirectToVoicemailRunnable.run 间接 通知 CallsManager.onSuccessfulIncomingCall
        } else if (mIsIncoming) {
            // We do not handle incoming calls immediately when they are verified by the connection
            // service. We allow the caller-info-query code to execute first so that we can read the
            // direct-to-voicemail property before deciding if we want to show the incoming call to
            // the user or if we want to reject the call.
            mDirectToVoicemailQueryPending = true;

            // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before
            // showing the user the incoming call screen.
            mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis(
                    mContext.getContentResolver()));
        // 去电, 通知 CallsManager.onSuccessfulOutgoingCall
        } else {
            /// M: ALPS02568075 @{
            // when perform ECC retry from dialing state
            // force to set as CONNECTING this time
            // in order to reset audio mode as normal in CallAudioManager.onCallStateChanged
            // then Ecc is changed to dialing again, can set audio mode once time.
            if (isEmergencyCall() && getState() == CallState.DIALING) {
                Log.v(this, "Change ecc state as connecting");
                for (Listener l : mListeners) {
                    l.onSuccessfulOutgoingCall(this, CallState.CONNECTING);
                }
            }
            /// @}
            for (Listener l : mListeners) {
                l.onSuccessfulOutgoingCall(this,
                        getStateFromConnectionState(connection.getState()));
            }
        }
    }

CallsManager.onSuccessfulIncomingCall

public void onSuccessfulIncomingCall(Call incomingCall) {
    Log.d(this, "onSuccessfulIncomingCall");
    setCallState(incomingCall, CallState.RINGING, "successful incoming call");

    if (hasEmergencyCall() || hasMaximumRingingCalls() || hasMaximumDialingCalls()
            || shouldBlockFor3GVT(incomingCall.isVideoCall())) {
        // ....
    } else {
        // 最终会allCall, 从而触发 InCallActicity 启动。
        addCall(incomingCall);
    }
}

GsmCallTracker.handlePollCalls 对于来电的处理, 和去电不一样

protected synchronized void
    handlePollCalls(AsyncResult ar) {


        for (int i = 0, curDC = 0, dcSize = polledCalls.size()
                ; i < mConnections.length; i++) {


            if (conn == null && dc != null) {
                // 新的电话
                if (mPendingMO != null && mPendingMO.compareTo(dc)) {
                   // 去电
                } else {
                   // 来电,创建一个GsmConnection, TelephonyConnection 中的 originalConnection 就是 GsmConnection
                   mConnections[i] = new GsmConnection(mPhone.getContext(), dc, this, i);
                }
            } else if (conn != null && dc == null) {
                // 旧的电话消失,可能时挂断


            } else if (conn != null && dc != null && !conn.compareTo(dc)) {

                // 旧的电话被替换
            } else if (conn != null && dc != null) {
                // 旧的电话更新, 正在打的电话时,状态更新
            }

        }
        //...
    }

在 GsmCallTracker 中有3种类型的 GsmCall

  • mRingingCall
  • mForegroundCall
  • mBackgroundCall

在 new GsmConnection 时, GsmConnection 会添加到对应的 Call 中, 也就是说,来电到来时, GsmConnection 已经准备好了,创建TelephonyConnection可以直接获取mRingingCall 中的GsmConnection。

TelephonyConnectionService.onCreateIncomingConnection 中

public Connection onCreateIncomingConnection(
            PhoneAccountHandle connectionManagerPhoneAccount,
            ConnectionRequest request) {

        // ....
        // ... 获取对应 的GSMPhone
        Phone phone = getPhoneForAccount(accountHandle, isEmergency);
        if (phone == null) {
            return Connection.createFailedConnection(
                    DisconnectCauseUtil.toTelecomDisconnectCause(
                            android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
                            "Phone is null"));
        }

        // 等于是获取GsmCallTracker里面的mRingingCall
        Call call = phone.getRingingCall();
        if (!call.getState().isRinging()) {
            Log.i(this, "onCreateIncomingConnection, no ringing call");
            return Connection.createFailedConnection(
                    DisconnectCauseUtil.toTelecomDisconnectCause(
                            android.telephony.DisconnectCause.INCOMING_MISSED,
                            "Found no ringing call"));
        }

        // 获取originalConnection
        com.android.internal.telephony.Connection originalConnection =
                call.getState() == Call.State.WAITING ?
                    call.getLatestConnection() : call.getEarliestConnection();
        if (isOriginalConnectionKnown(originalConnection)) {
            Log.i(this, "onCreateIncomingConnection, original connection already registered");
            return Connection.createCanceledConnection();
        }

        // 创建TelephonyConnection 与 GsmConnection(originalConnection) 绑定
        // 链接创建成功
        Connection connection =
                createConnectionFor(phone, originalConnection, false /* isOutgoing */,
                        request.getAccountHandle());
        if (connection == null) {
            return Connection.createCanceledConnection();
        } else {
            return connection;
        }
    }

因为接下来的流程和去电类似,请参考去电过程。

  • system 进程和 com.android.phone 绑定成功后, system 进程再和 com.android.dialer 进程绑定, 并启动 InCallActicity。
  • RIL 获取到更新后,通过 CallsManager 通知 system 进程
  • system 进程通知 com.android.dialer 进程, 然后 InCallPresenter 更新 UI
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 拨号流程 概述:拨号的过程,如果按照进程来分,可以分成3个模块。 com.android.dialer进程,即电话...
    SevChen阅读 2,497评论 4 2
  • afinalAfinal是一个android的ioc,orm框架 https://github.com/yangf...
    passiontim阅读 15,088评论 2 44
  • 我不知道自己想住什么样布局的房子,就在网上搜了不少四合院古典样式的房子,不禁啧啧称奇,竟有这样的设计,竟有那样的惊...
    过去瑾阅读 210评论 0 0
  • 第一句话写在最前面,虽然是句有点废的话,“外化的东西都需要内在的思考,而现实却少不了执行”。就像我经常跟我一个好朋...
    Addy_Zhou阅读 212评论 0 2