Android5.1拨号时通话记录的生成以及显示,这里以紧急拨号为例(正常拨号也是如此):
概述:首先拨号,会走正常的拨号流程,可参考另外一篇文章,除此之外,拨号除了会将当前通话加入到CallList中,同时会利用状态变化通知将这路通话保存到通话记录数据库等待下次显示调用,但是注意,只有在挂断时才去通知存储通话记录(也有可能是创建连接成功后)。
1.拨号键盘拨号时触发DialpadFragment.java(packages/apps/Dialer)中的handleDialButtonPressed方法,调用:
final Intent intent = CallUtil.getCallIntent(number,
(getActivity() instanceof DialtactsActivity ?
((DialtactsActivity) getActivity()).getCallOrigin() : null));
DialerUtils.startActivityWithErrorToast(getActivity(), intent);
hideAndClearDialpad(false);
前面的逻辑主要处理了号码不符合要求的情况,在这里跟踪代码即可知道,得到的action是CALL_PRIVILEGED,最后进入的是telecomm层的CallActivity.java,在这里做了一个中转,然后进入CallReceiver.java中的processOutgoingCallIntent()中,在这里要注意进入的不是outgoingBroadcaster.java,5.0/5.1之后都是走的telecomm来处理通话;
2.拨号流程中从CallActivity.java接收到广播开始转发到CallReceiver.java中接收到拨号请求:
Call call = getCallsManager().startOutgoingCall(handle, phoneAccountHandle, clientExtras);
if (call != null) {
NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
context, getCallsManager(), call, intent, isDefaultDialer);
final int result = broadcaster.processIntent();
final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
if (!success && call != null) {
disconnectCallAndShowErrorDialog(context, call, result);
}
}
这里最后会转入到CallsManager.java(packages/services/Telecomm)中,同时发出了 NewOutgoingCallIntentBroadcaster,我们看在这里做了什么,进入NewOutgoingCallIntentBroadcaster .java,主要方法是processIntent():
if (Intent.ACTION_CALL.equals(action)
|| Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
// Voicemail calls will be handled directly by the telephony connection manager
Log.i(this, "Placing call immediately instead of waiting for "
+ " OutgoingCallBroadcastReceiver: %s", intent);
boolean speakerphoneOn = mIntent.getBooleanExtra(
TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, false);
mCallsManager.placeOutgoingCall(mCall, handle, null, speakerphoneOn,
VideoProfile.VideoState.AUDIO_ONLY);
return DisconnectCause.NOT_DISCONNECTED;
} else {
Log.i(this, "Unhandled intent %s. Ignoring and not placing call.", intent);
return DisconnectCause.OUTGOING_CANCELED;
}
看关键的一行:
mCallsManager.placeOutgoingCall(mCall, handle, null, speakerphoneOn,
VideoProfile.VideoState.AUDIO_ONLY);
3.这里做了什么呢?其实跟进去既可以看到,在CallsManager中的 placeOutgoingCall()方法中:
call.setHandle(uriHandle);
call.setGatewayInfo(gatewayInfo);
call.setStartWithSpeakerphoneOn(speakerphoneOn);
call.setVideoState(videoState);
boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext,
call.getHandle());
if (isEmergencyCall) {
// Emergency -- CreateConnectionProcessor will choose accounts automatically
call.setTargetPhoneAccount(null);
}
if (call.getTargetPhoneAccount() != null || isEmergencyCall) {
// If the account has been set, proceed to place the outgoing call.
// Otherwise the connection will be initiated when the account is set by the user.
call.startCreateConnection(mPhoneAccountRegistrar);
}
这里先拿到call的实例,然后对这个实例做一些配置工作,比如设置状态,对方号码,即将进入拨号,不过,这里出现了非常重要的一行代码:
call.startCreateConnection(mPhoneAccountRegistrar);
我们知道拨打电话,这一路通话必须要建立一个连接Connection,这个连接就好比一个通道,只有将连接建立好我们的电话才有可能到达对方端,才能够拨打电话成功,我们继续跟踪,看看这个连接是如何建立起来的,进入Call.java(packages/services/Telecomm)中查看方法startCreateConnection():
Preconditions.checkState(mCreateConnectionProcessor == null);
mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this,
phoneAccountRegistrar, mContext);
mCreateConnectionProcessor.process();
直接查看CreateConnectionProcessor.java(packages/services/Telecomm)中的方法process():
clearTimeout();
mAttemptRecords = new ArrayList<>();
if (mCall.getTargetPhoneAccount() != null) {
mAttemptRecords.add(new CallAttemptRecord(
mCall.getTargetPhoneAccount(), mCall.getTargetPhoneAccount()));
}
adjustAttemptsForConnectionManager();
adjustAttemptsForEmergency();
mAttemptRecordIterator = mAttemptRecords.iterator();
attemptNextPhoneAccount();
这个CreateConnectionProcessor就是建立通话连接的类,看它的 attemptNextPhoneAccount()方法:
ConnectionServiceWrapper service =
mRepository.getService(
phoneAccount.getComponentName(),
phoneAccount.getUserHandle());
if (service == null) {
Log.i(this, "Found no connection service for attempt %s", attempt);
attemptNextPhoneAccount();
} else {
mCall.setConnectionManagerPhoneAccount(attempt.connectionManagerPhoneAccount);
mCall.setTargetPhoneAccount(attempt.targetPhoneAccount);
mCall.setConnectionService(service);
setTimeoutIfNeeded(service, attempt);
Log.i(this, "Attempting to call from %s", service.getComponentName());
service.createConnection(mCall, new Response(service));
}
}
看代码看关键地方,我们看到这里先拿到了ConnectionServiceWrapper的一个实例service,然后:
mCall.setConnectionService(service);
service.createConnection(mCall, new Response(service));
这里调用了Call.java(packages/services/Telecomm)中的 setConnectionService方法保存service实例,然后调用service实例也就是ConnectionServiceWrapper类的createConnection方法,在这个方法中才最终创建了connection,创建成功后会调用当前类的handleCreateConnectionComplete方法然后去调用Call.java的handleCreateConnectionSuccess方法:
for (Listener l : mListeners) {
l.onSuccessfulOutgoingCall(this,
getStateFromConnectionState(connection.getState()));
}
在这里,最后调用了CallsManager.java中的onSuccessfulOutgoingCall方法;
4.至此,整个通话连接的创建过程结束了,不过通话记录是如何存储的,到此还没有结束,回调onSuccessfulOutgoingCall方法后,此方法中关键的一句:
setCallState(call, callState);
查看此方法关键地方:
if (mCalls.contains(call)) {
for (CallsManagerListener listener : mListeners) {
if (Log.SYSTRACE_DEBUG) {
Trace.beginSection(listener.getClass().toString() + " onCallStateChanged");
}
listener.onCallStateChanged(call, oldState, newState);
if (Log.SYSTRACE_DEBUG) {
Trace.endSection();
}
}
updateCallsManagerState();
}
上面逻辑中我们看到:
listener.onCallStateChanged(call, oldState, newState);
这里的listener是什么实例?跟踪即可得知是CallLogManager.java(packages/services/Telecomm),这个类继承了CallsManagerListenerBase,而CallsManagerListenerBase中定义了 onCallStateChanged,接下来我们看看 onCallStateChanged()方法中做了什么:
if (isNewlyDisconnected &&
(oldState != CallState.PRE_DIAL_WAIT &&
!call.isConference() &&
!isCallCanceled)) {
int type;
if (!call.isIncoming()) {
type = Calls.OUTGOING_TYPE;
//huanghonglin,[HQ01415857],2015-9-29,14:50:51,start
//} else if (disconnectCause == DisconnectCause.MISSED || disconnectCause == DisconnectCause.REJECTED) {
} else if (disconnectCause == DisconnectCause.MISSED){
type = Calls.MISSED_TYPE;
} else {
type = Calls.INCOMING_TYPE;
}
logCall(call, type);
}
在这里,首先会判断通话的类型,是来电去电还是未接电话,接着调用logCall(call, type),只查看此方法关键处:
logCall(call.getCallerInfo(), logNumber, call.getHandlePresentation(),
callLogType, callFeatures, accountHandle, creationTime, age, null);
看到最后会调用上面方法:
boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(mContext, number);
// On some devices, to avoid accidental redialing of emergency numbers, we *never* log
// emergency calls to the Call Log. (This behavior is set on a per-product basis, based
// on carrier requirements.)
final boolean okToLogEmergencyNumber =
mContext.getResources().getBoolean(R.bool.allow_emergency_numbers_in_call_log);
// Don't log emergency numbers if the device doesn't allow it.
final boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber;
sendAddCallBroadcast(callType, duration);
if (isOkToLogThisCall) {
Log.d(TAG, "Logging Calllog entry: " + callerInfo + ", "
+ Log.pii(number) + "," + presentation + ", " + callType
+ ", " + start + ", " + duration);
AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, presentation,
callType, features, accountHandle, start, duration, dataUsage);
logCallAsync(args);
} else {
Log.d(TAG, "Not adding emergency call to call log.");
}
上面首先会判断是不是紧急拨号,事实上,是不是紧急通话都会进入到if逻辑中,因为 okToLogEmergencyNumber始终为true,在这里将一路通话的信息组合成 args,代入到 logLndAsync()中:
return new LogLndAsyncTask().execute(args);
可以想到,此处必然调用异步线程去处理数据的存储,进入到 LogLndAsyncTask:
private class LogLndAsyncTask extends AsyncTask<AddCallArgs, Void, Void> {
@Override
protected Void doInBackground(AddCallArgs... callList) {
int count = callList.length;
for (int i = 0; i < count; i++) {
AddCallArgs c = callList[i];
try {
ContentValues contentValues = new ContentValues();
if (c.callerInfo.name == null) {
contentValues.put(IccUriUtils.NAME, c.number);
} else {
contentValues.put(IccUriUtils.NAME, c.number);
}
contentValues.put(IccUriUtils.NUMBER, c.number);
Uri uri = c.context.getContentResolver().insert(
IccUriUtils.getIccURI("lnd", c.accountHandle.getId()), contentValues);
c.accountHandle.getId() + ", uri : " + uri);
} catch (Exception e) {
Log.e(TAG, e, "Exception raised during adding CallLog entry.");
}}
return null;}
}
在这里,我们看到了真正操作数据库的地方,使用 ContentValues包装数据最后 调用到insert方法去做数据的插入,这样一路通话记录就存入到数据表中了,通话记录的插入我们看CallLogProvider.java中的insert()方法即可了解整个数据写入过程。
最后在显示通话记录时,我们可以查到使用的Adapter是CallLogAdapter.java(packages/apps/Dialer),定位到bindView(),如果需要改动显示的紧急号码,将其改成”Emergency Number”字符串,之需要修改:
String name = info.name;
重新对name赋值即可:
if(PhoneNumberUtils.isEmergencyNumber(number)){
name = "Emergency number";
}
顺便提一下,要将通话界面中的紧急通话 Emergency Number上面一行去掉只需要修改CallCardFragment.java中的setPrimary()方法中:
mCallStateButton.setVisibility(View.INVISIBLE);
即可。
2.以上是拨号中的通话记录存储,如果是来电是由CallsManager.java中的onSuccessfulIncomingCall方法来处理,同样是在挂断时进行保存处理。
问题:在CallNotifier.java(packages/services/Telephony)中的onDisconnect()方法中还调用了logCall,这个有何作用?如下:
mCallLogger.logCall(c);//mCallLogger是CallLogger实例
查看CallLogger.java,看到其中的logcall貌似没有具体实现,猜测可能是废弃的类,不过查看CallController.java(packages/services/Telephony)中的placeCallInternal()方法,其中调用到了:
// Log failed call.
// Note: Normally, many of these values we gather from the Connection object but
// since no such object is created for unconnected calls, we have to build them
// manually.
// TODO: Try to restructure code so that we can handle failure-
// condition call logging in a single place (placeCall()) that also has access to
// the number we attempted to dial (not placeCall()).
mCallLogger.logCall(null /* callerInfo */, number, 0 /* presentation */,
Calls.OUTGOING_TYPE, System.currentTimeMillis(), 0 /* duration */);
不过看其注释,应该是存储拨打未成功的那路通话,暂时不用去关心。
备注拨号流程log:
I/yyj ( 1596): +++++++dial==Intent { act=android.intent.action.CALL_PRIVILEGED dat=tel:xxxx-x (has extras) }
I/Telecom ( 2517): yyj: ++++++++++++++++++callactivity++Intent { act=android.intent.action.CALL_PRIVILEGED dat=tel:xxxx-x flg=0x800000 cmp=com.android.server.telecom/.PrivilegedCallActivity (has extras) }
I/Telecom ( 529): yyj: ++++++++++callreveiverIntent { act=android.intent.action.CALL_PRIVILEGED dat=tel:xxxx-x flg=0x10000010 cmp=com.android.server.telecom/.CallReceiver (has extras) }
I/Telecom ( 529): yyj: ++++++++++callreveiver OutgoingIntent { act=android.intent.action.CALL_PRIVILEGED dat=tel:xxxx-x flg=0x10000010 cmp=com.android.server.telecom/.CallReceiver (has extras) }
I/Telecom ( 529): yyj: ++++++++++++onStartOutgoingCall+++++
I/Telecom ( 529): yyj: +++++++++++++NEWOC processIntent+++++++++++Intent { act=android.intent.action.CALL dat=tel:xxxx-x flg=0x10000010 cmp=com.android.server.telecom/.CallReceiver (has extras) }
I/Telecom ( 529): yyj: +++++++++++++NEWOC onReceive+++++++++++Intent { act=android.intent.action.NEW_OUTGOING_CALL flg=0x10000010 (has extras) }
I/Telecom ( 529): yyj: ++++++++++++setConnectionService=com.android.server.telecom.ConnectionServiceWrapper@2fe73187
I/Telecom ( 529): yyj: ++++++++call=[843600016, CONNECTING, com.android.phone/com.android.services.telephony.TelephonyConnectionService, tel:10086, 0, childs(0), has_parent(false), [[Capabilities:]]
I/InCall ( 1596): yyj - +++++++++++incallanctivity internalResolveIntent++++++++++++Intent { act=android.intent.action.MAIN flg=0x10840000 cmp=com.android.dialer/com.android.incallui.InCallActivity (has extras) }
I/InCall ( 1596): yyj - ++++++++++++callcardfragment+++++
I/InCall ( 1596): yyj - ++++++++++++callcardPresenter+++++
a.拨号过程:
CallActivity-->CallReceiver-->CallsManager.startOutgoingCall
|
-->NewOutgoingCallIntentBroadcaster-->CallsManager.placeOutgoingCall(创建连接connection)