blob: cf7e7aef767f17e3ec774feb077cd80480bcee63 [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;
Ihab Awad98a55602014-06-30 21:27:28 -070024import android.telecomm.PhoneAccount;
Yorke Leef98fb572014-03-05 10:56:55 -080025import android.telephony.PhoneNumberUtils;
26
Yorke Lee6f3f7af2014-07-11 10:59:46 -070027import com.android.internal.telephony.CallerInfo;
Yorke Leef98fb572014-03-05 10:56:55 -080028import com.android.internal.telephony.PhoneConstants;
29
30/**
31 * Helper class that provides functionality to write information about calls and their associated
32 * caller details to the call log. All logging activity will be performed asynchronously in a
33 * background thread to avoid blocking on the main thread.
34 */
Sailesh Nepal810735e2014-03-18 18:15:46 -070035final class CallLogManager extends CallsManagerListenerBase {
Yorke Leef98fb572014-03-05 10:56:55 -080036 /**
37 * Parameter object to hold the arguments to add a call in the call log DB.
38 */
39 private static class AddCallArgs {
40 /**
41 * @param contactInfo Caller details.
42 * @param number The phone number to be logged.
43 * @param presentation Number presentation of the phone number to be logged.
44 * @param callType The type of call (e.g INCOMING_TYPE). @see
45 * {@link android.provider.CallLog} for the list of values.
Tyler Gunn765c35c2014-07-10 08:17:53 -070046 * @param features The features of the call (e.g. FEATURES_VIDEO). @see
47 * {@link android.provider.CallLog} for the list of values.
Yorke Leef98fb572014-03-05 10:56:55 -080048 * @param creationDate Time when the call was created (milliseconds since epoch).
49 * @param durationInMillis Duration of the call (milliseconds).
Tyler Gunn765c35c2014-07-10 08:17:53 -070050 * @param dataUsage Data usage in bytes, or null if not applicable.
Yorke Leef98fb572014-03-05 10:56:55 -080051 */
Yorke Lee6f3f7af2014-07-11 10:59:46 -070052 public AddCallArgs(Context context, CallerInfo callerInfo, String number,
Tyler Gunn765c35c2014-07-10 08:17:53 -070053 int presentation, int callType, int features, PhoneAccount account,
54 long creationDate, long durationInMillis, Long dataUsage) {
Yorke Leef98fb572014-03-05 10:56:55 -080055 this.context = context;
Yorke Lee6f3f7af2014-07-11 10:59:46 -070056 this.callerInfo = callerInfo;
Yorke Leef98fb572014-03-05 10:56:55 -080057 this.number = number;
58 this.presentation = presentation;
59 this.callType = callType;
Tyler Gunn765c35c2014-07-10 08:17:53 -070060 this.features = features;
Ihab Awad98a55602014-06-30 21:27:28 -070061 this.mAccount = account;
Yorke Leef98fb572014-03-05 10:56:55 -080062 this.timestamp = creationDate;
63 this.durationInSec = (int)(durationInMillis / 1000);
Tyler Gunn765c35c2014-07-10 08:17:53 -070064 this.dataUsage = dataUsage;
Yorke Leef98fb572014-03-05 10:56:55 -080065 }
66 // Since the members are accessed directly, we don't use the
67 // mXxxx notation.
68 public final Context context;
Yorke Lee6f3f7af2014-07-11 10:59:46 -070069 public final CallerInfo callerInfo;
Yorke Leef98fb572014-03-05 10:56:55 -080070 public final String number;
71 public final int presentation;
72 public final int callType;
Tyler Gunn765c35c2014-07-10 08:17:53 -070073 public final int features;
Ihab Awad98a55602014-06-30 21:27:28 -070074 public final PhoneAccount mAccount;
Yorke Leef98fb572014-03-05 10:56:55 -080075 public final long timestamp;
76 public final int durationInSec;
Tyler Gunn765c35c2014-07-10 08:17:53 -070077 public final Long dataUsage;
Yorke Leef98fb572014-03-05 10:56:55 -080078 }
79
80 private static final String TAG = CallLogManager.class.getSimpleName();
81
82 private final Context mContext;
83
84 public CallLogManager(Context context) {
85 mContext = context;
86 }
87
Sailesh Nepal810735e2014-03-18 18:15:46 -070088 @Override
89 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
90 if (newState == CallState.DISCONNECTED || newState == CallState.ABORTED) {
91 int type;
92 if (!call.isIncoming()) {
93 type = Calls.OUTGOING_TYPE;
94 } else if (oldState == CallState.RINGING) {
95 type = Calls.MISSED_TYPE;
96 } else {
97 type = Calls.INCOMING_TYPE;
98 }
99 logCall(call, type);
100 }
Yorke Leef98fb572014-03-05 10:56:55 -0800101 }
102
103 /**
104 * Logs a call to the call log based on the {@link Call} object passed in.
105 *
106 * @param call The call object being logged
107 * @param callLogType The type of call log entry to log this call as. See:
108 * {@link android.provider.CallLog.Calls#INCOMING_TYPE}
109 * {@link android.provider.CallLog.Calls#OUTGOING_TYPE}
110 * {@link android.provider.CallLog.Calls#MISSED_TYPE}
111 */
112 private void logCall(Call call, int callLogType) {
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700113 final long creationTime = call.getCreationTimeMillis();
114 final long age = call.getAgeMillis();
Yorke Leef98fb572014-03-05 10:56:55 -0800115
Yorke Leef98fb572014-03-05 10:56:55 -0800116 final String logNumber = getLogNumber(call);
117
Yorke Lee33501632014-03-17 19:24:12 -0700118 Log.d(TAG, "logNumber set to: %s", Log.pii(logNumber));
Yorke Leef98fb572014-03-05 10:56:55 -0800119
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700120 final int presentation = getPresentation(call);
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700121 final PhoneAccount account = call.getPhoneAccount();
Yorke Leef98fb572014-03-05 10:56:55 -0800122
Tyler Gunn765c35c2014-07-10 08:17:53 -0700123 // TODO: Once features and data usage are available, wire them up here.
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700124 logCall(call.getCallerInfo(), logNumber, presentation, callLogType, Calls.FEATURES_NONE,
125 account, creationTime, age, null);
Yorke Leef98fb572014-03-05 10:56:55 -0800126 }
127
128 /**
129 * Inserts a call into the call log, based on the parameters passed in.
130 *
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700131 * @param callerInfo Caller details.
Yorke Leef98fb572014-03-05 10:56:55 -0800132 * @param number The number the call was made to or from.
133 * @param presentation
134 * @param callType The type of call.
Tyler Gunn765c35c2014-07-10 08:17:53 -0700135 * @param features The features of the call.
Yorke Leef98fb572014-03-05 10:56:55 -0800136 * @param start The start time of the call, in milliseconds.
137 * @param duration The duration of the call, in milliseconds.
Tyler Gunn765c35c2014-07-10 08:17:53 -0700138 * @param dataUsage The data usage for the call, null if not applicable.
Yorke Leef98fb572014-03-05 10:56:55 -0800139 */
140 private void logCall(
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700141 CallerInfo callerInfo,
Yorke Leef98fb572014-03-05 10:56:55 -0800142 String number,
143 int presentation,
144 int callType,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700145 int features,
Ihab Awad98a55602014-06-30 21:27:28 -0700146 PhoneAccount account,
Yorke Leef98fb572014-03-05 10:56:55 -0800147 long start,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700148 long duration,
149 Long dataUsage) {
Yorke Lee66255452014-06-05 08:09:24 -0700150 boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(mContext, number);
Yorke Leef98fb572014-03-05 10:56:55 -0800151
152 // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
153 // emergency calls to the Call Log. (This behavior is set on a per-product basis, based
154 // on carrier requirements.)
155 final boolean okToLogEmergencyNumber =
156 mContext.getResources().getBoolean(R.bool.allow_emergency_numbers_in_call_log);
157
158 // Don't log emergency numbers if the device doesn't allow it.
159 final boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber;
160
161 if (isOkToLogThisCall) {
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700162 Log.d(TAG, "Logging Calllog entry: " + callerInfo + ", "
Yorke Leef98fb572014-03-05 10:56:55 -0800163 + Log.pii(number) + "," + presentation + ", " + callType
164 + ", " + start + ", " + duration);
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700165 AddCallArgs args = new AddCallArgs(mContext, callerInfo, number, presentation,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700166 callType, features, account, start, duration, dataUsage);
Yorke Leef98fb572014-03-05 10:56:55 -0800167 logCallAsync(args);
168 } else {
169 Log.d(TAG, "Not adding emergency call to call log.");
170 }
171 }
172
173 /**
174 * Retrieve the phone number from the call, and then process it before returning the
175 * actual number that is to be logged.
176 *
177 * @param call The phone connection.
178 * @return the phone number to be logged.
179 */
180 private String getLogNumber(Call call) {
Yorke Lee33501632014-03-17 19:24:12 -0700181 Uri handle = call.getOriginalHandle();
Yorke Leef98fb572014-03-05 10:56:55 -0800182
183 if (handle == null) {
184 return null;
185 }
186
Yorke Lee060d1d62014-03-19 13:24:15 -0700187 String handleString = handle.getSchemeSpecificPart();
Sailesh Nepalce704b92014-03-17 18:31:43 -0700188 if (!PhoneNumberUtils.isUriNumber(handleString)) {
189 handleString = PhoneNumberUtils.stripSeparators(handleString);
Yorke Leef98fb572014-03-05 10:56:55 -0800190 }
Sailesh Nepalce704b92014-03-17 18:31:43 -0700191 return handleString;
Yorke Leef98fb572014-03-05 10:56:55 -0800192 }
193
194 /**
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700195 * Gets the presentation from the {@link Call}.
Yorke Leef98fb572014-03-05 10:56:55 -0800196 *
197 * TODO: There needs to be a way to pass information from
198 * Connection.getNumberPresentation() into a {@link Call} object. Until then, always return
199 * PhoneConstants.PRESENTATION_ALLOWED. On top of that, we might need to introduce
200 * getNumberPresentation to the ContactInfo object as well.
201 *
202 * @param call The call object to retrieve caller details from.
Yorke Leef98fb572014-03-05 10:56:55 -0800203 * @return The number presentation constant to insert into the call logs.
204 */
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700205 private int getPresentation(Call call) {
Yorke Leef98fb572014-03-05 10:56:55 -0800206 return PhoneConstants.PRESENTATION_ALLOWED;
207 }
208
209 /**
210 * Adds the call defined by the parameters in the provided AddCallArgs to the CallLogProvider
211 * using an AsyncTask to avoid blocking the main thread.
212 *
213 * @param args Prepopulated call details.
214 * @return A handle to the AsyncTask that will add the call to the call log asynchronously.
215 */
216 public AsyncTask<AddCallArgs, Void, Uri[]> logCallAsync(AddCallArgs args) {
217 return new LogCallAsyncTask().execute(args);
218 }
219
220 /**
221 * Helper AsyncTask to access the call logs database asynchronously since database operations
222 * can take a long time depending on the system's load. Since it extends AsyncTask, it uses
223 * its own thread pool.
224 */
225 private class LogCallAsyncTask extends AsyncTask<AddCallArgs, Void, Uri[]> {
226 @Override
227 protected Uri[] doInBackground(AddCallArgs... callList) {
228 int count = callList.length;
229 Uri[] result = new Uri[count];
230 for (int i = 0; i < count; i++) {
231 AddCallArgs c = callList[i];
232
233 try {
234 // May block.
Yorke Lee6f3f7af2014-07-11 10:59:46 -0700235 result[i] = Calls.addCall(c.callerInfo, c.context, c.number, c.presentation,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700236 c.callType, c.features, c.mAccount, c.timestamp, c.durationInSec,
237 c.dataUsage);
Yorke Leef98fb572014-03-05 10:56:55 -0800238 } catch (Exception e) {
239 // This is very rare but may happen in legitimate cases.
240 // E.g. If the phone is encrypted and thus write request fails, it may cause
241 // some kind of Exception (right now it is IllegalArgumentException, but this
242 // might change).
243 //
244 // We don't want to crash the whole process just because of that, so just log
245 // it instead.
246 Log.e(TAG, e, "Exception raised during adding CallLog entry.");
247 result[i] = null;
248 }
249 }
250 return result;
251 }
252
253 /**
254 * Performs a simple sanity check to make sure the call was written in the database.
255 * Typically there is only one result per call so it is easy to identify which one failed.
256 */
257 @Override
258 protected void onPostExecute(Uri[] result) {
259 for (Uri uri : result) {
260 if (uri == null) {
261 Log.w(TAG, "Failed to write call to the log.");
262 }
263 }
264 }
265 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800266}