#注:本文基于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