blob: 1bb3c128e5322b6a896dcb8897df1f076dc69d7d [file] [log] [blame]
Yorke Leef98fb572014-03-05 10:56:55 -08001/*
2 * Copyright 2014, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Ben Gilad9f2bed32013-12-12 17:43:26 -080017package com.android.telecomm;
18
Yorke Leef98fb572014-03-05 10:56:55 -080019import android.content.Context;
20import android.net.Uri;
21import android.os.AsyncTask;
22import android.provider.CallLog.Calls;
Sailesh Nepal810735e2014-03-18 18:15:46 -070023import android.telecomm.CallState;
Evan Charlton89176372014-07-19 18:23:09 -070024import android.telecomm.PhoneAccountHandle;
Tyler Gunn0a388fc2014-07-17 12:21:17 -070025import android.telecomm.VideoCallProfile;
Yorke Leef98fb572014-03-05 10:56:55 -080026import android.telephony.PhoneNumberUtils;
27
Yorke Lee6f3f7af2014-07-11 10:59:46 -070028import com.android.internal.telephony.CallerInfo;
Yorke Leef98fb572014-03-05 10:56:55 -080029import com.android.internal.telephony.PhoneConstants;
30
31/**
32 * Helper class that provides functionality to write information about calls and their associated
33 * caller details to the call log. All logging activity will be performed asynchronously in a
34 * background thread to avoid blocking on the main thread.
35 */
Sailesh Nepal810735e2014-03-18 18:15:46 -070036final class CallLogManager extends CallsManagerListenerBase {
Yorke Leef98fb572014-03-05 10:56:55 -080037 /**
38 * Parameter object to hold the arguments to add a call in the call log DB.
39 */
40 private static class AddCallArgs {
41 /**
Evan Charlton89176372014-07-19 18:23:09 -070042 * @param callerInfo Caller details.
Yorke Leef98fb572014-03-05 10:56:55 -080043 * @param number The phone number to be logged.
44 * @param presentation Number presentation of the phone number to be logged.
45 * @param callType The type of call (e.g INCOMING_TYPE). @see
46 * {@link android.provider.CallLog} for the list of values.
Tyler Gunn765c35c2014-07-10 08:17:53 -070047 * @param features The features of the call (e.g. FEATURES_VIDEO). @see
48 * {@link android.provider.CallLog} for the list of values.
Yorke Leef98fb572014-03-05 10:56:55 -080049 * @param creationDate Time when the call was created (milliseconds since epoch).
50 * @param durationInMillis Duration of the call (milliseconds).
Tyler Gunn765c35c2014-07-10 08:17:53 -070051 * @param dataUsage Data usage in bytes, or null if not applicable.
Yorke Leef98fb572014-03-05 10:56:55 -080052 */
Yorke Lee6f3f7af2014-07-11 10:59:46 -070053 public AddCallArgs(Context context, CallerInfo callerInfo, String number,
Evan Charlton89176372014-07-19 18:23:09 -070054 int presentation, int callType, int features, PhoneAccountHandle accountHandle,
Tyler Gunn765c35c2014-07-10 08:17:53 -070055 long creationDate, long durationInMillis, Long dataUsage) {
Yorke Leef98fb572014-03-05 10:56:55 -080056 this.context = context;
Yorke Lee6f3f7af2014-07-11 10:59:46 -070057 this.callerInfo = callerInfo;
Yorke Leef98fb572014-03-05 10:56:55 -080058 this.number = number;
59 this.presentation = presentation;
60 this.callType = callType;
Tyler Gunn765c35c2014-07-10 08:17:53 -070061 this.features = features;
Evan Charlton89176372014-07-19 18:23:09 -070062 this.accountHandle = accountHandle;
Yorke Leef98fb572014-03-05 10:56:55 -080063 this.timestamp = creationDate;
64 this.durationInSec = (int)(durationInMillis / 1000);
Tyler Gunn765c35c2014-07-10 08:17:53 -070065 this.dataUsage = dataUsage;
Yorke Leef98fb572014-03-05 10:56:55 -080066 }
67 // Since the members are accessed directly, we don't use the
68 // mXxxx notation.
69 public final Context context;
Yorke Lee6f3f7af2014-07-11 10:59:46 -070070 public final CallerInfo callerInfo;
Yorke Leef98fb572014-03-05 10:56:55 -080071 public final String number;
72 public final int presentation;
73 public final int callType;
Tyler Gunn765c35c2014-07-10 08:17:53 -070074 public final int features;
Evan Charlton89176372014-07-19 18:23:09 -070075 public final PhoneAccountHandle accountHandle;
Yorke Leef98fb572014-03-05 10:56:55 -080076 public final long timestamp;
77 public final int durationInSec;
Tyler Gunn765c35c2014-07-10 08:17:53 -070078 public final Long dataUsage;
Yorke Leef98fb572014-03-05 10:56:55 -080079 }
80
81 private static final String TAG = CallLogManager.class.getSimpleName();
82
83 private final Context mContext;
84
85 public CallLogManager(Context context) {
86 mContext = context;
87 }
88
Sailesh Nepal810735e2014-03-18 18:15:46 -070089 @Override
90 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
Nancy Chenaa561dd2014-07-24 14:32:29 -070091 if ((newState == CallState.DISCONNECTED || newState == CallState.ABORTED) &&
92 oldState != CallState.PRE_DIAL_WAIT) {
Sailesh Nepal810735e2014-03-18 18:15:46 -070093 int type;
94 if (!call.isIncoming()) {
95 type = Calls.OUTGOING_TYPE;
96 } else if (oldState == CallState.RINGING) {
97 type = Calls.MISSED_TYPE;
98 } else {
99 type = Calls.INCOMING_TYPE;
100 }
101 logCall(call, type);
102 }
Yorke Leef98fb572014-03-05 10:56:55 -0800103 }
104
105 /**
106 * Logs a call to the call log based on the {@link Call} object passed in.
107 *
108 * @param call The call object being logged
109 * @param callLogType The type of call log entry to log this call as. See:
110 * {@link android.provider.CallLog.Calls#INCOMING_TYPE}
111 * {@link android.provider.CallLog.Calls#OUTGOING_TYPE}
112 * {@link android.provider.CallLog.Calls#MISSED_TYPE}
113 */
114 private void logCall(Call call, int callLogType) {
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700115 final long creationTime = call.getCreationTimeMillis();
116 final long age = call.getAgeMillis();
Yorke Leef98fb572014-03-05 10:56:55 -0800117
Yorke Leef98fb572014-03-05 10:56:55 -0800118 final String logNumber = getLogNumber(call);
119
Yorke Lee33501632014-03-17 19:24:12 -0700120 Log.d(TAG, "logNumber set to: %s", Log.pii(logNumber));
Yorke Leef98fb572014-03-05 10:56:55 -0800121
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700122 final int presentation = getPresentation(call);
Evan Charlton94d01622014-07-20 12:32:05 -0700123 final PhoneAccountHandle accountHandle = call.getPhoneAccount();
Yorke Leef98fb572014-03-05 10:56:55 -0800124
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700125 // TODO(vt): Once data usage is available, wire it up here.
126 int callFeatures = getCallFeatures(call.getVideoStateHistory());
Evan Charlton94d01622014-07-20 12:32:05 -0700127 logCall(call.getCallerInfo(), logNumber, presentation, callLogType, callFeatures,
128 accountHandle, creationTime, age, null);
Yorke Leef98fb572014-03-05 10:56:55 -0800129 }
130
131 /**
132 * Inserts a call into the call log, based on the parameters passed in.
133 *
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700134 * @param callerInfo Caller details.
Yorke Leef98fb572014-03-05 10:56:55 -0800135 * @param number The number the call was made to or from.
136 * @param presentation
137 * @param callType The type of call.
Tyler Gunn765c35c2014-07-10 08:17:53 -0700138 * @param features The features of the call.
Yorke Leef98fb572014-03-05 10:56:55 -0800139 * @param start The start time of the call, in milliseconds.
140 * @param duration The duration of the call, in milliseconds.
Tyler Gunn765c35c2014-07-10 08:17:53 -0700141 * @param dataUsage The data usage for the call, null if not applicable.
Yorke Leef98fb572014-03-05 10:56:55 -0800142 */
143 private void logCall(
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700144 CallerInfo callerInfo,
Yorke Leef98fb572014-03-05 10:56:55 -0800145 String number,
146 int presentation,
147 int callType,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700148 int features,
Evan Charlton89176372014-07-19 18:23:09 -0700149 PhoneAccountHandle accountHandle,
Yorke Leef98fb572014-03-05 10:56:55 -0800150 long start,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700151 long duration,
152 Long dataUsage) {
Yorke Lee66255452014-06-05 08:09:24 -0700153 boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(mContext, number);
Yorke Leef98fb572014-03-05 10:56:55 -0800154
155 // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
156 // emergency calls to the Call Log. (This behavior is set on a per-product basis, based
157 // on carrier requirements.)
158 final boolean okToLogEmergencyNumber =
159 mContext.getResources().getBoolean(R.bool.allow_emergency_numbers_in_call_log);
160
161 // Don't log emergency numbers if the device doesn't allow it.
162 final boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber;
163
164 if (isOkToLogThisCall) {
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700165 Log.d(TAG, "Logging Calllog entry: " + callerInfo + ", "
Yorke Leef98fb572014-03-05 10:56:55 -0800166 + Log.pii(number) + "," + presentation + ", " + callType
167 + ", " + start + ", " + duration);
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700168 AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, presentation,
Evan Charlton89176372014-07-19 18:23:09 -0700169 callType, features, accountHandle, start, duration, dataUsage);
Yorke Leef98fb572014-03-05 10:56:55 -0800170 logCallAsync(args);
171 } else {
172 Log.d(TAG, "Not adding emergency call to call log.");
173 }
174 }
175
176 /**
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700177 * Based on the video state of the call, determines the call features applicable for the call.
178 *
179 * @param videoState The video state.
180 * @return The call features.
181 */
182 private static int getCallFeatures(int videoState) {
Andrew Lee1d90b552014-07-28 14:36:59 -0700183 if ((videoState & VideoCallProfile.VideoState.TX_ENABLED)
184 == VideoCallProfile.VideoState.TX_ENABLED) {
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700185 return Calls.FEATURES_VIDEO;
186 }
187 return Calls.FEATURES_NONE;
188 }
189
190 /**
Yorke Leef98fb572014-03-05 10:56:55 -0800191 * Retrieve the phone number from the call, and then process it before returning the
192 * actual number that is to be logged.
193 *
194 * @param call The phone connection.
195 * @return the phone number to be logged.
196 */
197 private String getLogNumber(Call call) {
Yorke Lee33501632014-03-17 19:24:12 -0700198 Uri handle = call.getOriginalHandle();
Yorke Leef98fb572014-03-05 10:56:55 -0800199
200 if (handle == null) {
201 return null;
202 }
203
Yorke Lee060d1d62014-03-19 13:24:15 -0700204 String handleString = handle.getSchemeSpecificPart();
Sailesh Nepalce704b92014-03-17 18:31:43 -0700205 if (!PhoneNumberUtils.isUriNumber(handleString)) {
206 handleString = PhoneNumberUtils.stripSeparators(handleString);
Yorke Leef98fb572014-03-05 10:56:55 -0800207 }
Sailesh Nepalce704b92014-03-17 18:31:43 -0700208 return handleString;
Yorke Leef98fb572014-03-05 10:56:55 -0800209 }
210
211 /**
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700212 * Gets the presentation from the {@link Call}.
Yorke Leef98fb572014-03-05 10:56:55 -0800213 *
214 * TODO: There needs to be a way to pass information from
215 * Connection.getNumberPresentation() into a {@link Call} object. Until then, always return
216 * PhoneConstants.PRESENTATION_ALLOWED. On top of that, we might need to introduce
217 * getNumberPresentation to the ContactInfo object as well.
218 *
219 * @param call The call object to retrieve caller details from.
Yorke Leef98fb572014-03-05 10:56:55 -0800220 * @return The number presentation constant to insert into the call logs.
221 */
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700222 private int getPresentation(Call call) {
Yorke Leef98fb572014-03-05 10:56:55 -0800223 return PhoneConstants.PRESENTATION_ALLOWED;
224 }
225
226 /**
227 * Adds the call defined by the parameters in the provided AddCallArgs to the CallLogProvider
228 * using an AsyncTask to avoid blocking the main thread.
229 *
230 * @param args Prepopulated call details.
231 * @return A handle to the AsyncTask that will add the call to the call log asynchronously.
232 */
233 public AsyncTask<AddCallArgs, Void, Uri[]> logCallAsync(AddCallArgs args) {
234 return new LogCallAsyncTask().execute(args);
235 }
236
237 /**
238 * Helper AsyncTask to access the call logs database asynchronously since database operations
239 * can take a long time depending on the system's load. Since it extends AsyncTask, it uses
240 * its own thread pool.
241 */
242 private class LogCallAsyncTask extends AsyncTask<AddCallArgs, Void, Uri[]> {
243 @Override
244 protected Uri[] doInBackground(AddCallArgs... callList) {
245 int count = callList.length;
246 Uri[] result = new Uri[count];
247 for (int i = 0; i < count; i++) {
248 AddCallArgs c = callList[i];
249
250 try {
251 // May block.
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700252 result[i] = Calls.addCall(c.callerInfo, c.context, c.number, c.presentation,
Evan Charlton89176372014-07-19 18:23:09 -0700253 c.callType, c.features, c.accountHandle, c.timestamp, c.durationInSec,
Yorke Leea38f3292014-07-16 17:31:02 -0700254 c.dataUsage, true /* addForAllUsers */);
Yorke Leef98fb572014-03-05 10:56:55 -0800255 } catch (Exception e) {
256 // This is very rare but may happen in legitimate cases.
257 // E.g. If the phone is encrypted and thus write request fails, it may cause
258 // some kind of Exception (right now it is IllegalArgumentException, but this
259 // might change).
260 //
261 // We don't want to crash the whole process just because of that, so just log
262 // it instead.
263 Log.e(TAG, e, "Exception raised during adding CallLog entry.");
264 result[i] = null;
265 }
266 }
267 return result;
268 }
269
270 /**
271 * Performs a simple sanity check to make sure the call was written in the database.
272 * Typically there is only one result per call so it is easy to identify which one failed.
273 */
274 @Override
275 protected void onPostExecute(Uri[] result) {
276 for (Uri uri : result) {
277 if (uri == null) {
278 Log.w(TAG, "Failed to write call to the log.");
279 }
280 }
281 }
282 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800283}