本流程图基于MTK平台 Android 7.0,普通来电,本流程只作为沟通学习使用
本篇文章写作顺序是从modem上报消息,一层一层上报到incallUI,读者可以根据需求选择阅读顺序。
当对方拨打电话后,运营商就会通过基站向含有特定SIM卡手机的modem发送指令,当手机modem收到这个指令后就会发生变化,并将变化信息通过AT指令集的方式传送给RILC,RILC又会通过socket的方式将变化信息传递到RILJ,此时RILJ的RILReceiver就会处理这些消息,从而进入我们上图的流程当中。
第一次其实是RIL_UNSOL_INCOMING_CALL_INDICATION这个事件,mIncomingCallIndicationRegistrant这个观察者list是在BaseCommands中定义并实现了添加观察者的方法,在GsmCdmaCallTracker初始化的时候往里面加入了观察者,观察EVENT_INCOMING_CALL_INDICATION事件,所以,当RILC向RILJ传送UNSOL_INCOMING_CALL_INDICATION事件时,通过
mIncomingCallIndicationRegistrant.notifyRegistrant(new AsyncResult(null, ret, null));这个方法,让GsmCdmaCallTracker的 handleMessage 的 EVENT_INCOMING_CALL_INDICATION,这样就把消息通过观察者模式传递到了GsmCdmaCallTracker里面去了,在这里面做一些判断,比如提高PhoneAPP的优先级、多个来电拒绝最新的来电等.
12-30 14:57:45.564 D/RILJ ( 1452): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED [SUB0]
12-30 14:57:45.565 D/GsmCallTrackerHelper( 1452): handle EVENT_CALL_STATE_CHANGE (slot 0)
12-30 14:57:45.565 D/RILJ ( 1452): [4481]> GET_CURRENT_CALLS [SUB0]
12-30 14:57:45.584 D/RILJ ( 1452): [4481]< GET_CURRENT_CALLS {[id=1,INCOMING,toa=129,norm,mt,0,voc,nonvid,noevp,,cli=1,,1] } [SUB0]
12-30 14:57:45.586 D/GsmCallTrackerHelper( 1452): handle EVENT_POLL_CALLS_RESULT (slot 0)
12-30 14:57:45.586 D/GsmCdmaCallTracker( 1452): Event EVENT_POLL_CALLS_RESULT Received
这部分流程主要是做一些状态的上报,还有一些connection的初始化.
详细的介绍可以参考满哥的 http://blog.csdn.net/yihongyuelan/article/details/22044787 这篇文章,看完后应该会有个大致的了解,这里就不再细说了。
这个流程图主要介绍了Telecom这层对下面Telephony传上来的数据处理,大概是一下几步:
构造intent的额外信息
/**
* Sends the incoming call intent to telecom.
*/
private void sendIncomingCallIntent(Connection connection) {
/// M: ALPS02136977. Prints debug messages for telephony. @{
if (connection != null) {
FormattedLog formattedLog = new FormattedLog.Builder()
.setCategory("CC")
.setServiceName("Telephony")
.setOpType(FormattedLog.OpType.NOTIFY)
.setActionName("MT")
.setCallNumber(connection.getAddress())
.setCallId("")
.buildDebugMsg();
if (formattedLog != null) {
Log.d(this, formattedLog.toString());
}
}
/// @}
Bundle extras = new Bundle();
if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED &&
!TextUtils.isEmpty(connection.getAddress())) {
Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null);
extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri);
/// M: For one-key conference MT displayed as incoming conference call. @{
boolean isIncomingMpty = connection.isIncomingCallMultiparty();
Log.d(this, "isIncomingMpty: " + isIncomingMpty);
extras.putBoolean(TelecomManagerEx.EXTRA_VOLTE_CONF_CALL_INCOMING, isIncomingMpty);
/// @}
}
// Specifies the time the call was added. This is used by the dialer for analytics.
extras.putLong(TelecomManager.EXTRA_CALL_CREATED_TIME_MILLIS,
SystemClock.elapsedRealtime());
PhoneAccountHandle handle = findCorrectPhoneAccountHandle();
if (handle == null) {
try {
connection.hangup();
} catch (CallStateException e) {
// connection already disconnected. Do nothing
}
} else {
TelecomManager.from(mPhone.getContext()).addNewIncomingCall(handle, extras);
}
}
创建一个call
/**
* Starts the process to attach the call to a connection service.
*
* @param phoneAccountHandle The phone account which contains the component name of the
* connection service to use for this call.
* @param extras The optional extras Bundle passed with the intent used for the incoming call.
*/
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 call = new Call(
getNextCallId(),
mContext,
this,
mLock,
mConnectionServiceRepository,
mContactsAsyncHelper,
mCallerInfoAsyncQueryFactory,
handle,
null /* gatewayInfo */,
null /* connectionManagerPhoneAccount */,
phoneAccountHandle,
Call.CALL_DIRECTION_INCOMING /* callDirection */,
false /* forceAttachToExistingConnection */,
false /* isConference */
); //创建一个call
call.initAnalytics();
if (getForegroundCall() != null) {
getForegroundCall().getAnalytics().setCallIsInterrupted(true);
call.getAnalytics().setCallIsAdditional(true);
}
setIntentExtrasAndStartTime(call, extras);
/// M: For VoLTE @{
if (TelecomVolteUtils.isConferenceInvite(extras)) {
call.setIsIncomingFromConfServer(true);
}
/// @}
// TODO: Move this to be a part of addCall()
call.addListener(this);
call.startCreateConnection(mPhoneAccountRegistrar);
}
创建链接
/**
* This can be used by telecom to either create a new outgoing call or attach to an existing
* incoming call. In either case, telecom will cycle through a set of services and call
* createConnection util a connection service cancels the process or completes it successfully.
*/
private void createConnection(
final PhoneAccountHandle callManagerAccount,
final String callId,
final ConnectionRequest request,
boolean isIncoming,
boolean isUnknown) {
Log.d(this, "createConnection, callManagerAccount: %s, callId: %s, request: %s, " +
"isIncoming: %b, isUnknown: %b", callManagerAccount, callId, request,
isIncoming,
isUnknown);
/// M: ALPS02136977. Prints debug messages for MO. @{
if (!isIncoming) {
String callNumber = null;
if (request != null && request.getAddress() != null) {
callNumber = request.getAddress().getSchemeSpecificPart();
}
FormattedLog formattedLog = new FormattedLog.Builder()
.setCategory("CC")
.setServiceName(getConnectionServiceName())
.setOpType(FormattedLog.OpType.OPERATION)
.setActionName("Dial")
.setCallNumber(callNumber)
.setCallId("")
.buildDebugMsg();
if (formattedLog != null) {
Log.d(this, formattedLog.toString());
}
}
/// @}
Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request)
: isIncoming ? onCreateIncomingConnection(callManagerAccount, request)
: onCreateOutgoingConnection(callManagerAccount, request);//根据不同的状态创建不同类型的链接
Log.d(this, "createConnection, connection: %s", connection);
if (connection == null) {
connection = Connection.createFailedConnection(
new DisconnectCause(DisconnectCause.ERROR));
}
connection.setTelecomCallId(callId);
if (connection.getState() != Connection.STATE_DISCONNECTED) {
addConnection(callId, connection);
}
Uri address = connection.getAddress();
String number = address == null ? "null" : address.getSchemeSpecificPart();
Log.v(this, "createConnection, number: %s, state: %s, capabilities: %s, properties: %s",
Connection.toLogSafePhoneNumber(number),
Connection.stateToString(connection.getState()),
Connection.capabilitiesToString(connection.getConnectionCapabilities()),
Connection.propertiesToString(connection.getConnectionProperties()));
Log.d(this, "createConnection, calling handleCreateConnectionSuccessful %s", callId);
/// M: CC: Set PhoneAccountHandle for ECC @{
//[ALPS01794357]
PhoneAccountHandle handle = connection.getAccountHandle();
if (handle == null) {
handle = request.getAccountHandle();
} else {
Log.d(this, "createConnection, set back phone account:%s", handle);
}
//// @}
mAdapter.handleCreateConnectionComplete(
callId,
request,
new ParcelableConnection(
handle, /* M: CC: Set PhoneAccountHandle for ECC [ALPS01794357] */
connection.getState(),
connection.getConnectionCapabilities(),
connection.getConnectionProperties(),
connection.getAddress(),
connection.getAddressPresentation(),
connection.getCallerDisplayName(),
connection.getCallerDisplayNamePresentation(),
connection.getVideoProvider() == null ?
null : connection.getVideoProvider().getInterface(),
connection.getVideoState(),
connection.isRingbackRequested(),
connection.getAudioModeIsVoip(),
connection.getConnectTimeMillis(),
connection.getStatusHints(),
connection.getDisconnectCause(),
createIdList(connection.getConferenceables()),
connection.getExtras()));
if (isUnknown) {
triggerConferenceRecalculate();
}
/// M: CC: Proprietary CRSS handling @{
// [ALPS01956888] For FailureSignalingConnection, CastException JE will happen.
if (connection.getState() != Connection.STATE_DISCONNECTED) {
forceSuppMessageUpdate(connection);
}
/// @}
}
12-30 14:57:45.603 D/Telephony( 1452): PstnIncomingCallNotifier: handleNewRingingConnection
12-30 14:57:45.603 D/Telephony( 1452): PstnIncomingCallNotifier: [Debug][CC][Telephony][Notify][MT][13880118404][]
12-30 14:57:45.604 D/Telephony( 1452): PstnIncomingCallNotifier: isIncomingMpty: false
//判断来电是不是会议电话 这里会构造intent的extras放入号码、当前呼入时间、是否是会议通话,并且传给telecom PstnIncomingCallNotifier.sendIncomingCallIntent
12-30 14:57:45.608 V/Telecom ( 936): Logging: START_SESSION: TSI.aNIC@ApY
12-30 14:57:45.608 I/Telecom ( 936): : Adding new incoming call with phoneAccountHandle PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}}: TSI.aNIC@ApY
//这个方法里面会检查包名、检查phoneaccount、然后构造完整的intent:ACTION_INCOMING_CALL、KEY_IS_INCOMING_CALL等等 TelecomServiceImpl.addNewIncomingCall
12-30 14:57:45.618 D/Telecom ( 936): Telecom-LogUtils: [Debug][CC][Telecom][Notify][MT][][], Intent = Intent { act=android.telecom.action.INCOMING_CALL (has extras) }: TSI.aNIC@ApY
12-30 14:57:45.619 D/Telecom ( 936): Class: Processing incoming call from connection service [ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}]: TSI.aNIC@ApY
//这里会判断intent的EXTRA_PHONE_ACCOUNT_HANDLE 和EXTRA_INCOMING_CALL_EXTRAS是否为null CallIntentProcessor.processIncomingCallIntent
12-30 14:57:45.619 D/Telecom ( 936): CallsManager: processIncomingCallIntent: TSI.aNIC@ApY
//这里会构造一个call ,并打断前台电话,设置当前时间为telecom处理时间,CallsManager.processIncomingCallIntent
12-30 14:57:45.640 V/Telecom ( 936): CreateConnectionProcessor: CreateConnectionProcessor created for Call = [TC@5, NEW, null, tel:13880118404, A, childs(0), has_parent(false), [Capabilities:], [Properties:]]: TSI.aNIC@ApY
//创建链接处理器 CreateConnectionProcessor.process
12-30 14:57:45.643 V/Telecom ( 936): CreateConnectionProcessor: process: TSI.aNIC@ApY
12-30 14:57:45.658 V/Telecom ( 936): CreateConnectionProcessor: attemptNextPhoneAccount: TSI.aNIC@ApY
12-30 14:57:45.659 I/Telecom ( 936): CreateConnectionProcessor: Trying attempt CallAttemptRecord(PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}},PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}}): TSI.aNIC@ApY
//设置call的connectionmanager、connectionservice、Timeout、phoneaccount、等信息 CreateConnectionProcessor.attemptNextPhoneAccount
12-30 14:57:45.660 D/Telecom ( 936): ConnectionServiceWrapper: createConnection([TC@5, NEW, com.android.phone/com.android.services.telephony.TelephonyConnectionService, tel:13880118404, A, childs(0), has_parent(false), [Capabilities:], [Properties:]]) via ComponentInfo{com.android.phone/com.android.services.telephony.TelephonyConnectionService}.: TSI.aNIC@ApY
12-30 14:57:45.681 D/TelecomFramework( 1452): : Enqueueing pre-init request TC@5
12-30 14:57:45.697 D/TelecomFramework( 1452): TelephonyConnectionService: createConnection, callManagerAccount: PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}}, callId: TC@5, request: ConnectionRequest tel:13880118404 Bundle[mParcelledData.dataSize=584], isIncoming: true, isUnknown:
12-30 14:57:45.698 I/Telephony( 1452): TelephonyConnectionService: onCreateIncomingConnection, request: ConnectionRequest tel:13880118404 Bundle[mParcelledData.dataSize=584]
//真正的创建一个链接,根据类型的不同 有onCreateUnknownConnection、onCreateIncomingConnection、onCreateOutgoingConnection这些 ConnectionService.createConnection
12-30 14:57:45.712 V/TelecomFramework( 1452): TelephonyConnectionService: createConnection, number: 13880118404, state: RINGING, capabilities: [Capabilities: CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO CAPABILITY_SPEED_UP_MT_AUDIO CAPABILITY_SEPARATE_FROM_CONFERENCE CAPABILITY_DISCONNECT_FROM_CONFERENCE], properties: [Properties:]
12-30 14:57:45.712 D/TelecomFramework( 1452): TelephonyConnectionService: createConnection, calling handleCreateConnectionSuccessful TC@5
12-30 14:57:45.715 D/Telecom ( 936): Telecom-LogUtils: [Debug][CC][Telecom][Notify][CreateMtSuccess][13880118404][TC@5]ParcelableConnection [act:PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}}], state:2, capabilities:[Capabilities: CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO CAPABILITY_SPEED_UP_MT_AUDIO CAPABILITY_SEPARATE_FROM_CONFERENCE CAPABILITY_DISCONNECT_FROM_CONFERENCE], properties:[Properties:], extras:Bundle[mParcelledData.dataSize=104]: CSW.hCCC@Apo
12-30 14:57:45.715 D/Telecom ( 936): ConnectionServiceWrapper: ConnectionService -> Telecom: handleCreateConnectionComplete TC@5: CSW.hCCC@Apo
//MT成功 ConnectionServiceWrapper.ParcelableConnection
12-30 14:57:45.719 V/Telecom ( 936): Call: handleCreateConnectionSuccessful ParcelableConnection [act:PhoneAccountHandle{TelephonyConnectionService, 89860115881029413909, UserHandle{0}}], state:2, capabilities:[Capabilities: CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO CAPABILITY_SPEED_UP_MT_AUDIO CAPABILITY_SEPARATE_FROM_CONFERENCE CAPABILITY_DISCONNECT_FROM_CONFERENCE], properties:[Properties:], extras:Bundle[mParcelledData.dataSize=104]: CSW.hCCC@Apo
//这里会设置比较多的call的状态和信息,Call.handleCreateConnectionSuccess
12-30 14:57:45.745 D/Telecom ( 936): CallsManager: onSuccessfulIncomingCall: CSW.hCCC@Apo
//这里会设置很多的过滤器,并执行过滤 CallsManager.onSuccessfulIncomingCall
12-30 14:57:45.746 D/Telecom ( 936): IncomingCallFilter: [performFiltering], timeout = 5000: CSW.hCCC@Apo
12-30 14:57:45.811 V/Telecom ( 936): CallsManager: addCall([TC@5, RINGING, com.android.phone/com.android.services.telephony.TelephonyConnectionService, tel:13880118404, A, childs(0), has_parent(false), [Capabilities: CAPABILITY_SUPPORT_HOLD CAPABILITY_MUTE CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO CAPABILITY_SPEED_UP_MT_AUDIO CAPABILITY_SEPARATE_FROM_CONFERENCE CAPABILITY_DISCONNECT_FROM_CONFERENCE], [Properties:]])
12-30 14:57:45.831 I/Telecom ( 936): EmergencyInCallServiceConnection: Attempting to bind to InCall ComponentInfo{com.android.dialer/com.android.incallui.InCallServiceImpl}, with Intent { act=android.telecom.InCallService cmp=com.android.dialer/com.android.incallui.InCallServiceImpl }
//EmergencyInCallServiceConnection继承自InCallServiceBindingConnection 所以最终会回调到onServiceConnected
12-30 14:57:45.885 D/Telecom ( 936): : onServiceConnected: ComponentInfo{com.android.dialer/com.android.incallui.InCallServiceImpl} false true: ICSBC.oSC@Ap4 //YT
12-30 14:57:45.885 I/Telecom ( 936): InCallController: onConnected to ComponentInfo{com.android.dialer/com.android.incallui.InCallServiceImpl}: ICSBC.oSC@Ap4
12-30 14:57:45.886 I/Telecom ( 936): InCallController: Adding 1 calls to InCallService after onConnected: ComponentInfo{com.android.dialer/com.android.incallui.InCallServiceImpl}: ICSBC.oSC@Ap4
//后面会通过InCallServiceImpl 的onCallAdded方法调用到上层界面incallUI里面去
这里主要是把来电的消息转发到incallUI:
确定是来电后的一些处理逻辑
/**
* Called when there is a new incoming call.
*
* @param call
*/
@Override
public void onIncomingCall(Call call) {
/// M: for ALPS01945830. Force set theme colors. @{
setThemeColors();
/// @}
InCallState newState = startOrFinishUi(InCallState.INCOMING);
InCallState oldState = mInCallState;
Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
mInCallState = newState;
for (IncomingCallListener listener : mIncomingCallListeners) {
listener.onIncomingCall(oldState, mInCallState, call);
}
}
决定显示HeadsUp还是AnswerFragment
@Override
public void addNotification(StatusBarNotification notification, RankingMap ranking,
Entry oldEntry) {
if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
mNotificationData.updateRanking(ranking);
Entry shadeEntry = createNotificationViews(notification);
if (shadeEntry == null) {
return;
}
boolean isHeadsUped = shouldPeek(shadeEntry);
Log.d("michael", "isHeadsUped: " +isHeadsUped);
if (isHeadsUped) { //根据这个值确定是否显示HeadsUp true显示
mHeadsUpManager.showNotification(shadeEntry);
// Mark as seen immediately
setNotificationShown(notification);
}
if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
if (shouldSuppressFullScreenIntent(notification.getKey())) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + notification.getKey());
}
} else if (mNotificationData.getImportance(notification.getKey())
< NotificationListenerService.Ranking.IMPORTANCE_MAX) {
if (DEBUG) {
Log.d(TAG, "No Fullscreen intent: not important enough: "
+ notification.getKey());
}
} else {
// Stop screensaver if the notification has a full-screen intent.
// (like an incoming phone call)
awakenDreams();
// not immersive & a full-screen alert should be shown
if (DEBUG)
Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
notification.getKey());
notification.getNotification().fullScreenIntent.send();//显示AnswerFragment
shadeEntry.notifyFullScreenIntentLaunched();
MetricsLogger.count(mContext, "note_fullscreen", 1);
} catch (PendingIntent.CanceledException e) {
}
}
}
addNotificationViews(shadeEntry, ranking);
// Recalculate the position of the sliding windows and the titles.
setAreThereNotifications();
}
12-30 14:57:45.992 D/InCall ( 3633): CallList - onCallAdded: callState=4, incallui call id=Call_1, telcom call id=TC@5
12-30 14:57:45.992 I/InCall ( 3633): CallList - onIncoming - [Call_1, INCOMING, [Capabilities: CAPABILITY_SUPPORT_HOLD CAPABILITY_RESPOND_VIA_TEXT CAPABILITY_MUTE CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO CAPABILITY_SEPARATE_FROM_CONFERENCE CAPABILITY_DISCONNECT_FROM_CONFERENCE], children:[], parent:null, conferenceable:[], videoState:Audio Only, mSessionModificationState:0, VideoSettings:(CameraDir:-1)]
//Called when a single call has changed. call已经发生改变,更新一些值的状态 CallList.onIncoming
12-30 14:57:46.001 I/InCall ( 3633): InCallPresenter - Start Full Screen in call UI
12-30 14:57:46.105 I/InCall ( 3633): StatusBarNotifier - Displaying notification for 2
12-30 14:57:46.110 V/NotificationService( 936): enqueueNotificationInternal: pkg=com.android.dialer id=2 notification=Notification(pri=1 contentView=null vibrate=null sound=null defaults=0x0 flags=0x82 color=0xff00796b category=call actions=2 vis=PRIVATE publicVersion=Notification(pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0xff0288d1 vis=PRIVATE))
12-30 14:57:46.112 D/NotificationService( 936): EnqueueNotificationRunnable.run for: 0|com.android.dialer|2|null|10006
12-30 14:57:46.112 I/InCall ( 3633): InCallPresenter - Phone switching state: NO_CALLS -> INCOMING
12-30 14:57:46.126 D/InCall ( 3633): AnswerPresenter - onIncomingCall: com.android.incallui.AnswerPresenter@f23e1cd
12-30 14:57:46.136 D/StatusBar( 1190): onNotificationPosted: StatusBarNotification(pkg=com.android.dialer user=UserHandle{0} id=2 tag=null key=0|com.android.dialer|2|null|10006: Notification(pri=2 contentView=null vibrate=null sound=null defaults=0x0 flags=0x82 color=0xff00796b category=call actions=2 vis=PRIVATE publicVersion=Notification(pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x0 color=0xff0288d1 vis=PRIVATE)))
12-30 14:57:46.136 D/PhoneStatusBar( 1190): addNotification key=0|com.android.dialer|2|null|10006
完整流程图