分析通话记录信息是通过什么写入的 android 源码 保存通话记录
16lz
2021-01-26
当来电或去电时,通过记录里的信息是怎么保存下来的呢?
在CallNotifier.java代码里有这样一个方法:
private void onDisconnect(AsyncResult r) { if (VDBG) log("onDisconnect()... CallManager state: " + mCM.getState()); Connection c = (Connection) r.result;mDisconnectNumber = c.getAddress();if (DBG) log("mDisconnectNumber:" + mDisconnectNumber);if (DBG && c != null) { log("- onDisconnect: cause = " + c.getDisconnectCause() + ", incoming = " + c.isIncoming() + ", date = " + c.getCreateTime()); } mCdmaVoicePrivacyState = false; int autoretrySetting = 0; if ((c != null) && (c.getCall().getPhone().getPhoneType() == Phone.PHONE_TYPE_CDMA)) { autoretrySetting = android.provider.Settings.System.getInt(mApplication. getContentResolver(),android.provider.Settings.System.CALL_AUTO_RETRY, 0); } if ((c != null) && (c.getCall().getPhone().getPhoneType() == Phone.PHONE_TYPE_CDMA)) { // Stop any signalInfo tone being played when a call gets ended stopSignalInfoTone(); // Resetting the CdmaPhoneCallState members mApplication.cdmaPhoneCallState.resetCdmaPhoneCallState(); // Remove Call waiting timers removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE); removeMessages(CALLWAITING_ADDCALL_DISABLE_TIMEOUT); } // Stop the ringer if it was ringing (for an incoming call that // either disconnected by itself, or was rejected by the user.) // // TODO: We technically *shouldn't* stop the ringer if the // foreground or background call disconnects while an incoming call // is still ringing, but that's a really rare corner case. // It's safest to just unconditionally stop the ringer here. // CDMA: For Call collision cases i.e. when the user makes an out going call // and at the same time receives an Incoming Call, the Incoming Call is given // higher preference. At this time framework sends a disconnect for the Out going // call connection hence we should *not* be stopping the ringer being played for // the Incoming Call Call ringingCall = mCM.getFirstActiveRingingCall(); if (ringingCall.getPhone().getPhoneType() == Phone.PHONE_TYPE_CDMA) { if (PhoneUtils.isRealIncomingCall(ringingCall.getState())) { // Also we need to take off the "In Call" icon from the Notification // area as the Out going Call never got connected if (DBG) log("cancelCallInProgressNotification()... (onDisconnect)"); NotificationMgr.getDefault().cancelCallInProgressNotification(); } else { if (DBG) log("stopRing()... (onDisconnect)"); mRinger.stopRing(); } } else { // GSM if (DBG) log("stopRing()... (onDisconnect)"); mRinger.stopRing(); } // stop call waiting tone if needed when disconnecting if (mCallWaitingTonePlayer != null) { mCallWaitingTonePlayer.stopTone(); mCallWaitingTonePlayer = null; } // Check for the various tones we might need to play (thru the // earpiece) after a call disconnects. int toneToPlay = InCallTonePlayer.TONE_NONE; // The "Busy" or "Congestion" tone is the highest priority: if (c != null) { Connection.DisconnectCause cause = c.getDisconnectCause(); if (cause == Connection.DisconnectCause.BUSY) { if (DBG) log("- need to play BUSY tone!"); toneToPlay = InCallTonePlayer.TONE_BUSY; } else if (cause == Connection.DisconnectCause.CONGESTION) { if (DBG) log("- need to play CONGESTION tone!"); toneToPlay = InCallTonePlayer.TONE_CONGESTION; } else if (((cause == Connection.DisconnectCause.NORMAL) || (cause == Connection.DisconnectCause.LOCAL)) && (mApplication.isOtaCallInActiveState())) { if (DBG) log("- need to play OTA_CALL_END tone!"); toneToPlay = InCallTonePlayer.TONE_OTA_CALL_END; } else if (cause == Connection.DisconnectCause.CDMA_REORDER) { if (DBG) log("- need to play CDMA_REORDER tone!"); toneToPlay = InCallTonePlayer.TONE_REORDER; } else if (cause == Connection.DisconnectCause.CDMA_INTERCEPT) { if (DBG) log("- need to play CDMA_INTERCEPT tone!"); toneToPlay = InCallTonePlayer.TONE_INTERCEPT; } else if (cause == Connection.DisconnectCause.CDMA_DROP) { if (DBG) log("- need to play CDMA_DROP tone!"); toneToPlay = InCallTonePlayer.TONE_CDMA_DROP; } else if (cause == Connection.DisconnectCause.OUT_OF_SERVICE) { if (DBG) log("- need to play OUT OF SERVICE tone!"); toneToPlay = InCallTonePlayer.TONE_OUT_OF_SERVICE; } else if (cause == Connection.DisconnectCause.UNOBTAINABLE_NUMBER) { if (DBG) log("- need to play TONE_UNOBTAINABLE_NUMBER tone!"); toneToPlay = InCallTonePlayer.TONE_UNOBTAINABLE_NUMBER; } else if (cause == Connection.DisconnectCause.ERROR_UNSPECIFIED) { if (DBG) log("- DisconnectCause is ERROR_UNSPECIFIED: play TONE_CALL_ENDED!"); toneToPlay = InCallTonePlayer.TONE_CALL_ENDED; } } // If we don't need to play BUSY or CONGESTION, then play the // "call ended" tone if this was a "regular disconnect" (i.e. a // normal call where one end or the other hung up) *and* this // disconnect event caused the phone to become idle. (In other // words, we *don't* play the sound if one call hangs up but // there's still an active call on the other line.) // TODO: We may eventually want to disable this via a preference. if ((toneToPlay == InCallTonePlayer.TONE_NONE) && (mCM.getState() == Phone.State.IDLE) && (c != null)) { Connection.DisconnectCause cause = c.getDisconnectCause(); if ((cause == Connection.DisconnectCause.NORMAL) // remote hangup || (cause == Connection.DisconnectCause.LOCAL)) { // local hangup if (VDBG) log("- need to play CALL_ENDED tone!"); toneToPlay = InCallTonePlayer.TONE_CALL_ENDED; mIsCdmaRedialCall = false; } } if (mCM.getState() == Phone.State.IDLE) { // Don't reset the audio mode or bluetooth/speakerphone state // if we still need to let the user hear a tone through the earpiece. if (toneToPlay == InCallTonePlayer.TONE_NONE) { int currentMode = mAudioManager.getMode(); if(AudioManager.MODE_IN_VT_CALL == currentMode){ if(!isVTConnected){ resetAudioStateAfterDisconnect(); }else{ isNeedResetAudio = true; sendEmptyMessageDelayed(DELAY_RESET_AUDIO_MODE, 5000); } }else{ resetAudioStateAfterDisconnect(); } } NotificationMgr.getDefault().cancelCallInProgressNotification(); // If the InCallScreen is *not* in the foreground, forcibly // dismiss it to make sure it won't still be in the activity // history. (But if it *is* in the foreground, don't mess // with it; it needs to be visible, displaying the "Call // ended" state.) if (!mApplication.isShowingCallScreen()) { if (VDBG) log("onDisconnect: force InCallScreen to finish()"); mApplication.dismissCallScreen(); } else { if (VDBG) log("onDisconnect: In call screen. Set short timeout."); mApplication.clearUserActivityTimeout(); } } if (c != null) { final String number = c.getAddress(); final long date = c.getCreateTime(); final long duration = c.getDurationMillis(); final Connection.DisconnectCause cause = c.getDisconnectCause(); final Phone phone = c.getCall().getPhone(); final int dialType; if (PhoneApp.getInstance().isVTCall()) dialType = CallLog.Calls.VIDEO_DIAL; else dialType = CallLog.Calls.VOICE_DIAL; // Set the "type" to be displayed in the call log (see constants in CallLog.Calls) final int callLogType; if (c.isIncoming()) { callLogType = (cause == Connection.DisconnectCause.INCOMING_MISSED ? Calls.MISSED_TYPE : Calls.INCOMING_TYPE); } else { callLogType = Calls.OUTGOING_TYPE; } if (VDBG) log("- callLogType: " + callLogType + ", UserData: " + c.getUserData()); { final CallerInfo ci = getCallerInfoFromConnection(c); // May be null. final String logNumber = getLogNumber(c, ci); if (DBG) log("- onDisconnect(): logNumber set to: " + /*logNumber*/ "xxxxxxx"); // TODO: In getLogNumber we use the presentation from // the connection for the CNAP. Should we use the one // below instead? (comes from caller info) // For international calls, 011 needs to be logged as + final int presentation = getPresentation(c, ci); if (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) { if ((PhoneNumberUtils.isEmergencyNumber(number)) && (mCurrentEmergencyToneState != EMERGENCY_TONE_OFF)) { if (mEmergencyTonePlayerVibrator != null) { mEmergencyTonePlayerVibrator.stop(); } } } // To prevent accidental redial of emergency numbers // (carrier requirement) the quickest solution is to // not log the emergency number. We gate on CDMA // (ugly) when we actually mean carrier X. // TODO: Clean this up and come up with a unified strategy. final boolean shouldNotlogEmergencyNumber = (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA); // Don't call isOtaSpNumber on GSM phones. final boolean isOtaNumber = (phone.getPhoneType() == Phone.PHONE_TYPE_CDMA) && phone.isOtaSpNumber(number); final boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(number); // Don't put OTA or CDMA Emergency calls into call logif (!(isOtaNumber || isEmergencyNumber && shouldNotlogEmergencyNumber ||mIsPhoneNumberInBlackList)) {CallLogAsync.AddCallArgs args = new CallLogAsync.AddCallArgs( mApplication, ci, logNumber, presentation, callLogType, date, duration, dialType); mCallLog.addCall(args); mIsPhoneNumberInBlackList = false; } } if (callLogType == Calls.MISSED_TYPE) { // Show the "Missed call" notification. // (Note we *don't* do this if this was an incoming call that // the user deliberately rejected.) showMissedCallNotification(c, date); } // Possibly play a "post-disconnect tone" thru the earpiece. // We do this here, rather than from the InCallScreen // activity, since we need to do this even if you're not in // the Phone UI at the moment the connection ends. if (toneToPlay != InCallTonePlayer.TONE_NONE) { if (VDBG) log("- starting post-disconnect tone (" + toneToPlay + ")..."); new InCallTonePlayer(toneToPlay).start(); // TODO: alternatively, we could start an InCallTonePlayer // here with an "unlimited" tone length, // and manually stop it later when this connection truly goes // away. (The real connection over the network was closed as soon // as we got the BUSY message. But our telephony layer keeps the // connection open for a few extra seconds so we can show the // "busy" indication to the user. We could stop the busy tone // when *that* connection's "disconnect" event comes in.) } if (mCM.getState() == Phone.State.IDLE) { // Release screen wake locks if the in-call screen is not // showing. Otherwise, let the in-call screen handle this because // it needs to show the call ended screen for a couple of // seconds. if (!mApplication.isShowingCallScreen()) { if (VDBG) log("- NOT showing in-call screen; releasing wake locks!"); mApplication.setScreenTimeout(PhoneApp.ScreenTimeoutDuration.DEFAULT); mApplication.requestWakeState(PhoneApp.WakeState.SLEEP); } else { if (VDBG) log("- still showing in-call screen; not releasing wake locks."); } } else { if (VDBG) log("- phone still in use; not releasing wake locks."); } if (((mPreviousCdmaCallState == Call.State.DIALING) || (mPreviousCdmaCallState == Call.State.ALERTING)) && (!PhoneNumberUtils.isEmergencyNumber(number)) && (cause != Connection.DisconnectCause.INCOMING_MISSED ) && (cause != Connection.DisconnectCause.NORMAL) && (cause != Connection.DisconnectCause.LOCAL) && (cause != Connection.DisconnectCause.INCOMING_REJECTED)) { if (!mIsCdmaRedialCall) { if (autoretrySetting == InCallScreen.AUTO_RETRY_ON) { // TODO: (Moto): The contact reference data may need to be stored and use // here when redialing a call. For now, pass in NULL as the URI parameter. PhoneUtils.placeCall(phone, number, null); mIsCdmaRedialCall = true; } else { mIsCdmaRedialCall = false; } } else { mIsCdmaRedialCall = false; } } } }
CDMA情况:
/** * Performs Call logging based on Timeout or Ignore Call Waiting Call for CDMA, * and finally calls Hangup on the Call Waiting connection. * * This method should be called only from the UI thread. * @see sendCdmaCallWaitingReject() */ private void onCdmaCallWaitingReject() { final Call ringingCall = mCM.getFirstActiveRingingCall(); // Call waiting timeout scenario if (ringingCall.getState() == Call.State.WAITING) { // Code for perform Call logging and missed call notification Connection c = ringingCall.getLatestConnection(); if (c != null) { String number = c.getAddress(); int presentation = c.getNumberPresentation(); final long date = c.getCreateTime(); final long duration = c.getDurationMillis(); final int callLogType = mCallWaitingTimeOut ? Calls.MISSED_TYPE : Calls.INCOMING_TYPE; // get the callerinfo object and then log the call with it. Object o = c.getUserData(); final CallerInfo ci; if ((o == null) || (o instanceof CallerInfo)) { ci = (CallerInfo) o; } else { ci = ((PhoneUtils.CallerInfoToken) o).currentInfo; }// add this to judge dial typefinal int dialType;if (PhoneApp.getInstance().isVTCall())dialType = CallLog.Calls.VIDEO_DIAL;elsedialType = CallLog.Calls.VOICE_DIAL; // Do final CNAP modifications of logNumber prior to logging [mimicking // onDisconnect()] final String logNumber = PhoneUtils.modifyForSpecialCnapCases( mApplication, ci, number, presentation); final int newPresentation = (ci != null) ? ci.numberPresentation : presentation; if (DBG) log("- onCdmaCallWaitingReject(): logNumber set to: " + logNumber + ", newPresentation value is: " + newPresentation); CallLogAsync.AddCallArgs args = new CallLogAsync.AddCallArgs( mApplication, ci, logNumber, presentation, callLogType, date, duration, dialType); mCallLog.addCall(args); if (callLogType == Calls.MISSED_TYPE) { // Add missed call notification showMissedCallNotification(c, date); } else { // Remove Call waiting 20 second display timer in the queue removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE); } // Hangup the RingingCall connection for CW PhoneUtils.hangup(c); } //Reset the mCallWaitingTimeOut boolean mCallWaitingTimeOut = false; } }
通过上述的红色部分,调用了:CallLogAsync.AddCallArgs的AddCallArgs方法,而CallLogAsync.java是管理通讯录的接口。
/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.android.phone;import android.content.Context;import android.net.Uri;import android.os.AsyncTask;import android.os.Looper;import android.provider.CallLog.Calls;import android.util.Log;import com.android.internal.telephony.CallerInfo;/** * Class to access the call logs database asynchronously since * database ops can take a long time depending on the system's load. * It uses AsyncTask which has its own thread pool. * * <pre class="prettyprint"> * Typical usage: * ============== * * // From an activity... * String mLastNumber = ""; * * CallLogAsync log = new CallLogAsync(); * * CallLogAsync.AddCallArgs addCallArgs = new CallLogAsync.AddCallArgs( * this, ci, number, presentation, type, timestamp, duration); * * log.addCall(addCallArgs); * * CallLogAsync.GetLastOutgoingCallArgs lastCallArgs = new CallLogAsync.GetLastOutgoingCallArgs( * this, new CallLogAsync.OnLastOutgoingCallComplete() { * public void lastOutgoingCall(String number) { mLastNumber = number; } * }); * log.getLastOutgoingCall(lastCallArgs); * </pre> * */public class CallLogAsync { private static final String TAG = "CallLogAsync"; /** * Parameter object to hold the args to add a call in the call log DB. */ public static class AddCallArgs { /** * @param ci CallerInfo. * @param number To be logged. * @param presentation Of the number. * @param callType The type of call (e.g INCOMING_TYPE). @see * android.provider.CallLog for the list of values. * @param timestamp Of the call (millisecond since epoch). * @param durationInMillis Of the call (millisecond). */ public AddCallArgs(Context context, CallerInfo ci, String number, int presentation, int callType, long timestamp, long durationInMillis, int dialType) { // Note that the context is passed each time. We could // have stored it in a member but we've run into a bunch // of memory leaks in the past that resulted from storing // references to contexts in places that were long lived // when the contexts were expected to be short lived. For // example, if you initialize this class with an Activity // instead of an Application the Activity can't be GCed // until this class can, and Activities tend to hold // references to large amounts of RAM for things like the // bitmaps in their views. // // Having hit more than a few of those bugs in the past // we've grown cautious of storing references to Contexts // when it's not very clear that the thing holding the // references is tightly tied to the Context, for example // Views the Activity is displaying. this.context = context; this.ci = ci; this.number = number; this.presentation = presentation; this.callType = callType; this.timestamp = timestamp; this.durationInSec = (int)(durationInMillis / 1000); this.dialType = dialType;} // Since the members are accessed directly, we don't use the // mXxxx notation. public final Context context; public final CallerInfo ci; public final String number; public final int presentation; public final int callType; public final long timestamp; public final int durationInSec;public final int dialType; } /** * Parameter object to hold the args to get the last outgoing call * from the call log DB. */ public static class GetLastOutgoingCallArgs { public GetLastOutgoingCallArgs(Context context, OnLastOutgoingCallComplete callback) { this.context = context; this.callback = callback; } public final Context context; public final OnLastOutgoingCallComplete callback; } /** * Non blocking version of CallLog.addCall(...) */ public AsyncTask addCall(AddCallArgs args) { assertUiThread(); return new AddCallTask().execute(args); } /** Interface to retrieve the last dialed number asynchronously. */ public interface OnLastOutgoingCallComplete { /** @param number The last dialed number or an empty string if * none exists yet. */ void lastOutgoingCall(String number); } /** * CallLog.getLastOutgoingCall(...) */ public AsyncTask getLastOutgoingCall(GetLastOutgoingCallArgs args) { assertUiThread(); return new GetLastOutgoingCallTask(args.callback).execute(args); } /** * AsyncTask to save calls in the DB. */ private class AddCallTask extends AsyncTask<AddCallArgs, Void, Uri[]> { @Override protected Uri[] doInBackground(AddCallArgs... callList) { int count = callList.length; Uri[] result = new Uri[count]; //Add by kylin 2012.02.14 try { for (int i = 0; i < count; i++) { AddCallArgs c = callList[i]; // May block. result[i] = Calls.addCall( c.ci, c.context, c.number, c.presentation, c.callType, c.timestamp, c.durationInSec, c.dialType); }} catch (Exception e) {// TODO: handle exception} //end return result; } // Perform a simple sanity check to make sure the call was // written in the database. Typically there is only one result // per call so it is easy to identify which one failed. @Override protected void onPostExecute(Uri[] result) { for (Uri uri : result) { if (uri == null) { Log.e(TAG, "Failed to write call to the log."); } } } } /** * AsyncTask to get the last outgoing call from the DB. */ private class GetLastOutgoingCallTask extends AsyncTask<GetLastOutgoingCallArgs, Void, String> { private final OnLastOutgoingCallComplete mCallback; private String mNumber; public GetLastOutgoingCallTask(OnLastOutgoingCallComplete callback) { mCallback = callback; } // Happens on a background thread. We cannot run the callback // here because only the UI thread can modify the view // hierarchy (e.g enable/disable the dial button). The // callback is ran rom the post execute method. @Override protected String doInBackground(GetLastOutgoingCallArgs... list) { int count = list.length; String number = ""; for (GetLastOutgoingCallArgs args : list) { // May block. Select only the last one. number = Calls.getLastOutgoingCall(args.context); } return number; // passed to the onPostExecute method. } // Happens on the UI thread, it is safe to run the callback // that may do some work on the views. @Override protected void onPostExecute(String number) { assertUiThread(); mCallback.lastOutgoingCall(number); } } private void assertUiThread() { if (!Looper.getMainLooper().equals(Looper.myLooper())) { throw new RuntimeException("Not on the UI thread!"); } }}
更多相关文章
- android事件4-onTouchEvent, onClick及onLongClick的调用机制
- android 移动图片
- Android(安卓)Studio 安装No JVM Installation found. Please in
- Android打电话的流程
- andorid中的html.fromhtml方法
- android 自定义view中onMeasure()
- Android类参考---Fragment(五)
- Listview中Button抢占焦点的解决方法
- Android(安卓)中Parcelable的作用