通话记录保存

4.3 通话记录保存

在通话到结束的整个过程中,为了保存通话信息,包括通话号码,通话时间等。在通话完全结束后,就会将这些信息插入到通话记录的数据库中。

1,插入通话记录

在前面通话状态更新一节中,Telecom进程的CallsManager的setCallState方法会调用监听器的onCallStateChanged方法,

for (CallsManagerListener listener : mListeners) {
    •••
    listener.onCallStateChanged(call, oldState, newState);
    •••
}

当然,监听器不止一个对象。在CallsManager的构造方法中,会将CallsManagerListener保存在mListeners 中,

mListeners.add(statusBarNotifier);
mListeners.add(mCallLogManager);
mListeners.add(mPhoneStateBroadcaster);
mListeners.add(mInCallController);
mListeners.add(mRinger);
•••

这个小节主要论述通话记录插入数据库的过程,因此,主要看CallLogManager。

CallLogManager的onCallStateChanged方法调用流程图如下,


onCallStateChanged方法逻辑如下,

1,获取通话状态以及相关信息,

int disconnectCause = call.getDisconnectCause().getCode(); //通话断开原因
//判断通话是否结束
boolean isNewlyDisconnected =
   newState == CallState.DISCONNECTED || newState == CallState.ABORTED;
//通话取消
boolean isCallCanceled = isNewlyDisconnected && disconnectCause == DisconnectCause.CANCELED;

2,只有在通话结束的情况下才会添加通话信息,

if (isNewlyDisconnected &&
                (oldState != CallState.SELECT_PHONE_ACCOUNT &&
                 !call.isConference() &&
                 !isCallCanceled)) {

3,获取通话的类型,是去电,未接来电还是来电,一共只有三种类型,

int type;
if (!call.isIncoming()) {
   type = Calls.OUTGOING_TYPE;
} else if (disconnectCause == DisconnectCause.MISSED) {
    type = Calls.MISSED_TYPE;
} else {
     type = Calls.INCOMING_TYPE;
}

CallLog的内部类Calls中定义了4中类型的通话,对应的值如下,

public static final int INCOMING_TYPE = 1; //来电
public static final int OUTGOING_TYPE = 2; //去电
public static final int MISSED_TYPE = 3;  //未接来电
public static final int VOICEMAIL_TYPE = 4; //语音邮箱电话

4,最后调用logCall方法,

logCall(call, type);

logCall方法主要逻辑如下,

1,首先获取通话信息,比如通话号码,通话时间等等,

final long creationTime = call.getCreationTimeMillis();
final long age = call.getAgeMillis();
final String logNumber = getLogNumber(call);

2,调用logCall方法继续处理,

int callFeatures = getCallFeatures(call.getVideoStateHistory());
logCall(call.getCallerInfo(), logNumber, call.getHandlePresentation(),
                toPreciseLogType(call,callLogType), callFeatures, accountHandle,
                creationTime, age, null, call.isEmergencyCall());

CallLogManager中有2个logCall方法,只是参数不同而已。

第二个logCall方法主要逻辑如下,

1,根据系统的bool值判断是否保存紧急拨号的信息,

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 = !isEmergency || okToLogEmergencyNumber;

2,将通话的通话类型和时间通过广播发送出去,接收该广播需要相关权限,

sendAddCallBroadcast(callType, duration);

sendAddCallBroadcast方法如下,

Intent callAddIntent = new Intent(ACTION_CALLS_TABLE_ADD_ENTRY);
callAddIntent.putExtra(CALL_TYPE, callType); //类型
callAddIntent.putExtra(CALL_DURATION, duration); //时间
mContext.sendBroadcast(callAddIntent, PERMISSION_PROCESS_CALLLOG_INFO);

3,最后将这些通话信息封装为AddCallArgs对象,调用logCallAsync方法继续处理,

AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, presentation,
     callType, features, accountHandle, start, duration, dataUsage);
logCallAsync(args);

AddCallArgs是CallLogManager的一个很简单的内部类, AddCallArgs里面有10个变量,分别保存通话信息。

logCallAsync方法如下,

public AsyncTask<AddCallArgs, Void, Uri[]> logCallAsync(AddCallArgs args) {
   return new LogCallAsyncTask().execute(args);
}

LogCallAsyncTask是CallLogManager的内部类,继承AsyncTask并实现了doInBackground方法, LogCallAsyncTask定义如下,

private class LogCallAsyncTask extends AsyncTask<AddCallArgs, Void, Uri[]> {

AsyncTask 原理在此就不论述了。反正一条, execute方法之后,会在子线程中调用doInBackground方法, doInBackground方法逻辑如下,

获取封装通话信息的AddCallArgs对象,然后逐个调用Calls的addCall方法,

int count = callList.length;
Uri[] result = new Uri[count];
for (int i = 0; i < count; i++) {
    AddCallArgs c = callList[i];
try {
        // May block.
        result[i] = Calls.addCall(c.callerInfo, c.context, c.number, c.presentation,
             c.callType, c.features, c.accountHandle, c.timestamp, c.durationInSec,
             c.dataUsage, true /* addForAllUsers */);
•••

CallLog的内部类Calls有3个addCall方法,最后插入数据库的addCall方法将AddCallArgs对象包含的通话信息封装成ContentValues对象,

ContentValues values = new ContentValues(6);

values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
values.put(TYPE, Integer.valueOf(callType));
values.put(FEATURES, features);
•••

然后调用addEntryAndRemoveExpiredEntries方法来实现插入数据库的,该方法如下,

final ContentResolver resolver = context.getContentResolver();
Uri result = resolver.insert(uri, values);
resolver.delete(uri, "_id IN " +
    "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
    + " LIMIT -1 OFFSET 500)", null);

通话记录对应的Uri为,

public static final Uri CONTENT_URI = Uri.parse("content://call_log/calls");

小结:

1,更新通话记录时机:在通话完成之后。

2,利用AsyncTask 异步进行插入数据库的过程。

2,通话记录数据库

真实的数据库在系统中的data/data路径下,详细的路径如下,


Contacts2.db这个数据库里面多达30个表单,但是和通话记录相关的只有一个calls表单。


每一次通话就会在calls表单中创建一条通话记录信息,每条信息包含的通话信息项比较多,部分如下,


对应操作数据库的代码为CallLogProvider.java,路径如下,

packages\providers\ContactsProvider\src\com\android\providers\contacts

AndroidManifest.xml中有关CallLogProvider的信息如下,

<provider android:name="CallLogProvider"
   android:authorities="call_log"
   android:syncable="false" android:multiprocess="false"
   android:exported="true"
   android:readPermission="android.permission.READ_CALL_LOG"
   android:writePermission="android.permission.WRITE_CALL_LOG">
</provider>

操作该数据库的关键字为 call_log ,另外需要读写的权限。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值