blob: a061e3a68dd4784e05998f7d6db362637408f95b [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) {
91 if (newState == CallState.DISCONNECTED || newState == CallState.ABORTED) {
92 int type;
93 if (!call.isIncoming()) {
94 type = Calls.OUTGOING_TYPE;
95 } else if (oldState == CallState.RINGING) {
96 type = Calls.MISSED_TYPE;
97 } else {
98 type = Calls.INCOMING_TYPE;
99 }
100 logCall(call, type);
101 }
Yorke Leef98fb572014-03-05 10:56:55 -0800102 }
103
104 /**
105 * Logs a call to the call log based on the {@link Call} object passed in.
106 *
107 * @param call The call object being logged
108 * @param callLogType The type of call log entry to log this call as. See:
109 * {@link android.provider.CallLog.Calls#INCOMING_TYPE}
110 * {@link android.provider.CallLog.Calls#OUTGOING_TYPE}
111 * {@link android.provider.CallLog.Calls#MISSED_TYPE}
112 */
113 private void logCall(Call call, int callLogType) {
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700114 final long creationTime = call.getCreationTimeMillis();
115 final long age = call.getAgeMillis();
Yorke Leef98fb572014-03-05 10:56:55 -0800116
Yorke Leef98fb572014-03-05 10:56:55 -0800117 final String logNumber = getLogNumber(call);
118
Yorke Lee33501632014-03-17 19:24:12 -0700119 Log.d(TAG, "logNumber set to: %s", Log.pii(logNumber));
Yorke Leef98fb572014-03-05 10:56:55 -0800120
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700121 final int presentation = getPresentation(call);
Evan Charlton94d01622014-07-20 12:32:05 -0700122 final PhoneAccountHandle accountHandle = call.getPhoneAccount();
Yorke Leef98fb572014-03-05 10:56:55 -0800123
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700124 // TODO(vt): Once data usage is available, wire it up here.
125 int callFeatures = getCallFeatures(call.getVideoStateHistory());
Evan Charlton94d01622014-07-20 12:32:05 -0700126 logCall(call.getCallerInfo(), logNumber, presentation, callLogType, callFeatures,
127 accountHandle, creationTime, age, null);
Yorke Leef98fb572014-03-05 10:56:55 -0800128 }
129
130 /**
131 * Inserts a call into the call log, based on the parameters passed in.
132 *
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700133 * @param callerInfo Caller details.
Yorke Leef98fb572014-03-05 10:56:55 -0800134 * @param number The number the call was made to or from.
135 * @param presentation
136 * @param callType The type of call.
Tyler Gunn765c35c2014-07-10 08:17:53 -0700137 * @param features The features of the call.
Yorke Leef98fb572014-03-05 10:56:55 -0800138 * @param start The start time of the call, in milliseconds.
139 * @param duration The duration of the call, in milliseconds.
Tyler Gunn765c35c2014-07-10 08:17:53 -0700140 * @param dataUsage The data usage for the call, null if not applicable.
Yorke Leef98fb572014-03-05 10:56:55 -0800141 */
142 private void logCall(
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700143 CallerInfo callerInfo,
Yorke Leef98fb572014-03-05 10:56:55 -0800144 String number,
145 int presentation,
146 int callType,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700147 int features,
Evan Charlton89176372014-07-19 18:23:09 -0700148 PhoneAccountHandle accountHandle,
Yorke Leef98fb572014-03-05 10:56:55 -0800149 long start,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700150 long duration,
151 Long dataUsage) {
Yorke Lee66255452014-06-05 08:09:24 -0700152 boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(mContext, number);
Yorke Leef98fb572014-03-05 10:56:55 -0800153
154 // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
155 // emergency calls to the Call Log. (This behavior is set on a per-product basis, based
156 // on carrier requirements.)
157 final boolean okToLogEmergencyNumber =
158 mContext.getResources().getBoolean(R.bool.allow_emergency_numbers_in_call_log);
159
160 // Don't log emergency numbers if the device doesn't allow it.
161 final boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber;
162
163 if (isOkToLogThisCall) {
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700164 Log.d(TAG, "Logging Calllog entry: " + callerInfo + ", "
Yorke Leef98fb572014-03-05 10:56:55 -0800165 + Log.pii(number) + "," + presentation + ", " + callType
166 + ", " + start + ", " + duration);
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700167 AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, presentation,
Evan Charlton89176372014-07-19 18:23:09 -0700168 callType, features, accountHandle, start, duration, dataUsage);
Yorke Leef98fb572014-03-05 10:56:55 -0800169 logCallAsync(args);
170 } else {
171 Log.d(TAG, "Not adding emergency call to call log.");
172 }
173 }
174
175 /**
Tyler Gunn0a388fc2014-07-17 12:21:17 -0700176 * Based on the video state of the call, determines the call features applicable for the call.
177 *
178 * @param videoState The video state.
179 * @return The call features.
180 */
181 private static int getCallFeatures(int videoState) {
182 if ((videoState & VideoCallProfile.VIDEO_STATE_TX_ENABLED)
183 == VideoCallProfile.VIDEO_STATE_TX_ENABLED) {
184 return Calls.FEATURES_VIDEO;
185 }
186 return Calls.FEATURES_NONE;
187 }
188
189 /**
Yorke Leef98fb572014-03-05 10:56:55 -0800190 * Retrieve the phone number from the call, and then process it before returning the
191 * actual number that is to be logged.
192 *
193 * @param call The phone connection.
194 * @return the phone number to be logged.
195 */
196 private String getLogNumber(Call call) {
Yorke Lee33501632014-03-17 19:24:12 -0700197 Uri handle = call.getOriginalHandle();
Yorke Leef98fb572014-03-05 10:56:55 -0800198
199 if (handle == null) {
200 return null;
201 }
202
Yorke Lee060d1d62014-03-19 13:24:15 -0700203 String handleString = handle.getSchemeSpecificPart();
Sailesh Nepalce704b92014-03-17 18:31:43 -0700204 if (!PhoneNumberUtils.isUriNumber(handleString)) {
205 handleString = PhoneNumberUtils.stripSeparators(handleString);
Yorke Leef98fb572014-03-05 10:56:55 -0800206 }
Sailesh Nepalce704b92014-03-17 18:31:43 -0700207 return handleString;
Yorke Leef98fb572014-03-05 10:56:55 -0800208 }
209
210 /**
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700211 * Gets the presentation from the {@link Call}.
Yorke Leef98fb572014-03-05 10:56:55 -0800212 *
213 * TODO: There needs to be a way to pass information from
214 * Connection.getNumberPresentation() into a {@link Call} object. Until then, always return
215 * PhoneConstants.PRESENTATION_ALLOWED. On top of that, we might need to introduce
216 * getNumberPresentation to the ContactInfo object as well.
217 *
218 * @param call The call object to retrieve caller details from.
Yorke Leef98fb572014-03-05 10:56:55 -0800219 * @return The number presentation constant to insert into the call logs.
220 */
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700221 private int getPresentation(Call call) {
Yorke Leef98fb572014-03-05 10:56:55 -0800222 return PhoneConstants.PRESENTATION_ALLOWED;
223 }
224
225 /**
226 * Adds the call defined by the parameters in the provided AddCallArgs to the CallLogProvider
227 * using an AsyncTask to avoid blocking the main thread.
228 *
229 * @param args Prepopulated call details.
230 * @return A handle to the AsyncTask that will add the call to the call log asynchronously.
231 */
232 public AsyncTask<AddCallArgs, Void, Uri[]> logCallAsync(AddCallArgs args) {
233 return new LogCallAsyncTask().execute(args);
234 }
235
236 /**
237 * Helper AsyncTask to access the call logs database asynchronously since database operations
238 * can take a long time depending on the system's load. Since it extends AsyncTask, it uses
239 * its own thread pool.
240 */
241 private class LogCallAsyncTask extends AsyncTask<AddCallArgs, Void, Uri[]> {
242 @Override
243 protected Uri[] doInBackground(AddCallArgs... callList) {
244 int count = callList.length;
245 Uri[] result = new Uri[count];
246 for (int i = 0; i < count; i++) {
247 AddCallArgs c = callList[i];
248
249 try {
250 // May block.
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700251 result[i] = Calls.addCall(c.callerInfo, c.context, c.number, c.presentation,
Evan Charlton89176372014-07-19 18:23:09 -0700252 c.callType, c.features, c.accountHandle, c.timestamp, c.durationInSec,
Yorke Leea38f3292014-07-16 17:31:02 -0700253 c.dataUsage, true /* addForAllUsers */);
Yorke Leef98fb572014-03-05 10:56:55 -0800254 } catch (Exception e) {
255 // This is very rare but may happen in legitimate cases.
256 // E.g. If the phone is encrypted and thus write request fails, it may cause
257 // some kind of Exception (right now it is IllegalArgumentException, but this
258 // might change).
259 //
260 // We don't want to crash the whole process just because of that, so just log
261 // it instead.
262 Log.e(TAG, e, "Exception raised during adding CallLog entry.");
263 result[i] = null;
264 }
265 }
266 return result;
267 }
268
269 /**
270 * Performs a simple sanity check to make sure the call was written in the database.
271 * Typically there is only one result per call so it is easy to identify which one failed.
272 */
273 @Override
274 protected void onPostExecute(Uri[] result) {
275 for (Uri uri : result) {
276 if (uri == null) {
277 Log.w(TAG, "Failed to write call to the log.");
278 }
279 }
280 }
281 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800282}