CFI通知流程

#注:本文基于Android 8.1

我们知道,当用户设置CFU(Call Forwarding Unconditional,无条件转移)后,会在通知栏显示当前号码被转移到另一个号码的通知,这就是Call Forwarding Indicator,简称CFI,而本文就主要讲述了CFI通知的更新流程。

首先,还是先从Telephony注册监听CFI事件说起,当PhoneGlobals被创建时,同时也会初始化CallNotifier对象。

PhoneGlobals.java

public void onCreate() {
        ......
        notifier = CallNotifier.init(this);
        ......
}

CallNotifier是一个单例的对象,下面是其创建代码。

static CallNotifier init(PhoneGlobals app) {
    synchronized (CallNotifier.class) {
        if (sInstance == null) {
            sInstance = new CallNotifier(app);
        } else {
            ......
        }
        return sInstance;
    }
}

我们现在只关心和CFI相关的内容,CallNotifier初始化时会向SubscriptionManager注册监听SubChange的事件,而SubChange的通知流程,我们这里暂不讨论。

private CallNotifier(PhoneGlobals app) {
    ......
    mSubscriptionManager.addOnSubscriptionsChangedListener(
            new OnSubscriptionsChangedListener() {
                @Override
                public void onSubscriptionsChanged() {
                    updatePhoneStateListeners();
                }
            });
}

当SubChange后,就会触发上面的updatePhoneStateListeners(),首先,会注销无效Sub的Listener,若此无效Sub之前已有CFI,也会被一并清楚;然后再对当前有效的Sub进行注册监听LISTEN_CALL_FORWARDING_INDICATOR。

public void updatePhoneStateListeners() {
    ......
    // Unregister phone listeners for inactive subscriptions.
    Iterator<Integer> itr = mPhoneStateListeners.keySet().iterator();
    while (itr.hasNext()) {
        int subId = itr.next();
        if (subInfos == null || !containsSubId(subInfos, subId)) {
            ......
            mApplication.notificationMgr.updateCfi(subId, false);

            // Listening to LISTEN_NONE removes the listener.
            mTelephonyManager.listen(
                    mPhoneStateListeners.get(subId), PhoneStateListener.LISTEN_NONE);
            ......
        }
    }
    ......
    // Register new phone listeners for active subscriptions.
    for (int i = 0; i < subInfos.size(); i++) {
        int subId = subInfos.get(i).getSubscriptionId();
        if (!mPhoneStateListeners.containsKey(subId)) {
            CallNotifierPhoneStateListener listener = new CallNotifierPhoneStateListener(subId);
            mTelephonyManager.listen(listener,
                    PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
                    | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR);
            mPhoneStateListeners.put(subId, listener);
        }
    }
}

至此,CFI的注册监听流程就完成了,接下来就等待用户设置CFU了。

CFU的设置流程将另起篇幅介绍,这里直接来看用户设置CFU后的流程。设置CFU后会在GsmMmiCode中处理EVENT_SET_CFF_COMPLETE事件,若无异常,就调用Phone设置CFF。

GsmMmiCode.java

handleMessage (Message msg) {
        ......
        case EVENT_SET_CFF_COMPLETE:
            ar = (AsyncResult) (msg.obj);

            /*
            * msg.arg1 = 1 means to set unconditional voice call forwarding
            * msg.arg2 = 1 means to enable voice call forwarding
            */
            if ((ar.exception == null) && (msg.arg1 == 1)) {
                boolean cffEnabled = (msg.arg2 == 1);
                if (mIccRecords != null) {
                    mPhone.setVoiceCallForwardingFlag(1, cffEnabled, mDialingNumber);
                }
            }
        ......
}

在Phone中,会先将CFF保存到SharedPreferences,然后再保存到SIM卡中。

Phone.java

public void setVoiceCallForwardingFlag(int line, boolean enable, String number) {
    setCallForwardingIndicatorInSharedPref(enable);
    IccRecords r = mIccRecords.get();
    if (r != null) {
        r.setVoiceCallForwardingFlag(line, enable, number);
    }
}

在SIMRecords中更新CFF时,会发出一个EVENT_CFI通知,当然还有一些其它处理,例如更新CFF到SIM卡,不过我们现在并不关心那些。

public void setVoiceCallForwardingFlag(int line, boolean enable, String dialNumber) {
    ......
    mRecordsEventsRegistrants.notifyResult(EVENT_CFI);
    ......
}

这个通知发出后,至于什么地方会收到,这就要看看此事件注册监听的流程了,也就是看谁调用了IccRecords.registerForRecordsEvents()。

public void registerForRecordsEvents(Handler h, int what, Object obj) {
    Registrant r = new Registrant (h, what, obj);
    mRecordsEventsRegistrants.add(r);
    ......
}

直接在代码里搜索方法名可发现,GsmCdmaPhone中进行了注册监听。


private void registerForIccRecordEvents() {
    ......
    if (isPhoneTypeGsm()) {
        ......
        r.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null);
    ......
}

而触发GsmCdmaPhone进行注册监听的动作,包括了切换PhoneType以及ICC_CHANGED,在此不作深入分析。
以ICC_CHANGED为例,下面是ICC_CHANGED发生后的简要流程:

new Phone() -> UiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null)
-> EVENT_ICC_CHANGED happened
-> UiccController.onGetIccCardStatusDone() -> mIccChangedRegistrants.notifyRegistrants()
-> Phone handle EVENT_ICC_CHANGED
-> GsmCdmaPhone.onUpdateIccAvailability() -> registerForIccRecordEvents()

通过以上分析可知,GsmCdmaPhone将接收并处理SIMRecords发出的EVENT_CFI通知。
由于注册时传入的是EVENT_ICC_RECORD_EVENTS,因此收到EVENT_CFI通知后也自当通过handleMessage()处理EVENT_ICC_RECORD_EVENTS事件。

public void handleMessage(Message msg) {
        ......
        case EVENT_ICC_RECORD_EVENTS:
            ar = (AsyncResult)msg.obj;
            processIccRecordEvents((Integer)ar.result);
            break;
        ......
}

private void processIccRecordEvents(int eventCode) {
    switch (eventCode) {
        case IccRecords.EVENT_CFI:
            logi("processIccRecordEvents: EVENT_CFI");
            notifyCallForwardingIndicator();
            break;
    }
}

public void notifyCallForwardingIndicator() {
    mNotifier.notifyCallForwardingChanged(this);
}

接下来就到了DefaultPhoneNotifier中,然后再经由TelephonyRegistry继续上报。

public void notifyCallForwardingChanged(Phone sender) {
            ......
            mRegistry.notifyCallForwardingChangedForSubscriber(subId,
                    sender.getCallForwardingIndicator());
            ......
}

到了TelephonyRegistry中,我们就离真相越来越近啦!

public void notifyCallForwardingChangedForSubscriber(int subId, boolean cfi) {
                ......
                if (r.matchPhoneStateListenerEvent(
                        PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) &&
                        idMatch(r.subId, subId, phoneId)) {
                    try {
                        r.callback.onCallForwardingIndicatorChanged(cfi);
                ......
}

我们在前面已经分析过,CallNotifier注册监听了LISTEN_CALL_FORWARDING_INDICATOR,因此,上面的回调会直接调用到CallNotifier.CallNotifierPhoneStateListener.onCallForwardingIndicatorChanged()。

public void onCallForwardingIndicatorChanged(boolean visible) {
    ......
    mApplication.notificationMgr.updateCfi(this.mSubId, visible);
}

最后就来到了NotificationMgr,这里面会控制通知栏是否显示CFI。

void updateCfi(int subId, boolean visible) {
    ......
    if (visible) { // 显示CFI
        ......
        mNotificationManager.notifyAsUser(
                Integer.toString(subId) /* tag */,
                CALL_FORWARD_NOTIFICATION,
                builder.build(),
                UserHandle.ALL);
    } else { // 隐藏CFI
        mNotificationManager.cancelAsUser(
                Integer.toString(subId) /* tag */,
                CALL_FORWARD_NOTIFICATION,
                UserHandle.ALL);
    }
}


简单总结一下:
GsmMmiCode处理Set CFU complete,
通过Phone将CFF保存到SIMRecords,
SIMRecords发出EVENT_CFI通知,
GsmCdmaPhone处理EVENT_CFI通知,通过DefaultPhoneNotifier上报到TelephonyRegistry,
TelephonyRegistry回调监听了LISTEN_CALL_FORWARDING_INDICATOR的Listener,
回调到CallNotifier,
最后通过NotificationMgr控制是否显示CFI

转载于:https://my.oschina.net/igiantpanda/blog/1609362

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值