blob: de9b301b8af4187de7e5154bc6e587badd17df45 [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;
23import android.telephony.PhoneNumberUtils;
24
25import com.android.internal.telephony.PhoneConstants;
26
27/**
28 * Helper class that provides functionality to write information about calls and their associated
29 * caller details to the call log. All logging activity will be performed asynchronously in a
30 * background thread to avoid blocking on the main thread.
31 */
Ben Gilad9f2bed32013-12-12 17:43:26 -080032class CallLogManager {
Yorke Leef98fb572014-03-05 10:56:55 -080033 /**
34 * Parameter object to hold the arguments to add a call in the call log DB.
35 */
36 private static class AddCallArgs {
37 /**
38 * @param contactInfo Caller details.
39 * @param number The phone number to be logged.
40 * @param presentation Number presentation of the phone number to be logged.
41 * @param callType The type of call (e.g INCOMING_TYPE). @see
42 * {@link android.provider.CallLog} for the list of values.
43 * @param creationDate Time when the call was created (milliseconds since epoch).
44 * @param durationInMillis Duration of the call (milliseconds).
45 */
46 public AddCallArgs(Context context, ContactInfo contactInfo, String number,
47 int presentation, int callType, long creationDate, long durationInMillis) {
48 this.context = context;
49 this.contactInfo = contactInfo;
50 this.number = number;
51 this.presentation = presentation;
52 this.callType = callType;
53 this.timestamp = creationDate;
54 this.durationInSec = (int)(durationInMillis / 1000);
55 }
56 // Since the members are accessed directly, we don't use the
57 // mXxxx notation.
58 public final Context context;
59 public final ContactInfo contactInfo;
60 public final String number;
61 public final int presentation;
62 public final int callType;
63 public final long timestamp;
64 public final int durationInSec;
65 }
66
67 private static final String TAG = CallLogManager.class.getSimpleName();
68
69 private final Context mContext;
70
71 public CallLogManager(Context context) {
72 mContext = context;
73 }
74
75 void logDisconnectedCall(Call call) {
76 // TODO: Until we add more state to the Call object to track whether this disconnected
77 // call orginated as an incoming or outgoing call, always log it as an incoming call.
78 // See b/13420887.
79 logCall(call, Calls.INCOMING_TYPE);
80 }
81
82 void logFailedOutgoingCall(Call call) {
83 logCall(call, Calls.OUTGOING_TYPE);
84 }
85
86 void logMissedCall(Call call) {
87 logCall(call, Calls.MISSED_TYPE);
88 }
89
90 /**
91 * Logs a call to the call log based on the {@link Call} object passed in.
92 *
93 * @param call The call object being logged
94 * @param callLogType The type of call log entry to log this call as. See:
95 * {@link android.provider.CallLog.Calls#INCOMING_TYPE}
96 * {@link android.provider.CallLog.Calls#OUTGOING_TYPE}
97 * {@link android.provider.CallLog.Calls#MISSED_TYPE}
98 */
99 private void logCall(Call call, int callLogType) {
100 String number = call.getHandle();
101 final long creationTime = call.getCreationTimeInMilliseconds();
102 final long age = call.getAgeInMilliseconds();
103
104 final ContactInfo contactInfo = call.getContactInfo(); // May be null.
105 final String logNumber = getLogNumber(call);
106
107 Log.d(TAG, "logNumber set to:" + Log.pii(logNumber) + ", number set to: "
108 + Log.pii(number));
109
110 final int presentation = getPresentation(call, contactInfo);
111
112 logCall(contactInfo, logNumber, presentation, callLogType, creationTime, age);
113 }
114
115 /**
116 * Inserts a call into the call log, based on the parameters passed in.
117 *
118 * @param contactInfo Caller details.
119 * @param number The number the call was made to or from.
120 * @param presentation
121 * @param callType The type of call.
122 * @param start The start time of the call, in milliseconds.
123 * @param duration The duration of the call, in milliseconds.
124 */
125 private void logCall(
126 ContactInfo contactInfo,
127 String number,
128 int presentation,
129 int callType,
130 long start,
131 long duration) {
132 boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(number, mContext);
133
134 // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
135 // emergency calls to the Call Log. (This behavior is set on a per-product basis, based
136 // on carrier requirements.)
137 final boolean okToLogEmergencyNumber =
138 mContext.getResources().getBoolean(R.bool.allow_emergency_numbers_in_call_log);
139
140 // Don't log emergency numbers if the device doesn't allow it.
141 final boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber;
142
143 if (isOkToLogThisCall) {
144 Log.d(TAG, "Logging Calllog entry: " + contactInfo + ", "
145 + Log.pii(number) + "," + presentation + ", " + callType
146 + ", " + start + ", " + duration);
147 AddCallArgs args = new AddCallArgs(mContext, contactInfo, number, presentation,
148 callType, start, duration);
149 logCallAsync(args);
150 } else {
151 Log.d(TAG, "Not adding emergency call to call log.");
152 }
153 }
154
155 /**
156 * Retrieve the phone number from the call, and then process it before returning the
157 * actual number that is to be logged.
158 *
159 * @param call The phone connection.
160 * @return the phone number to be logged.
161 */
162 private String getLogNumber(Call call) {
163 String handle = call.getHandle();
164
165 if (handle == null) {
166 return null;
167 }
168
169 if (!PhoneNumberUtils.isUriNumber(handle)) {
170 handle = PhoneNumberUtils.stripSeparators(handle);
171 }
172 return handle;
173 }
174
175 /**
176 * Gets the presentation from the {@link ContactInfo} if not null. Otherwise, gets it from the
177 * {@link Call}.
178 *
179 * TODO: There needs to be a way to pass information from
180 * Connection.getNumberPresentation() into a {@link Call} object. Until then, always return
181 * PhoneConstants.PRESENTATION_ALLOWED. On top of that, we might need to introduce
182 * getNumberPresentation to the ContactInfo object as well.
183 *
184 * @param call The call object to retrieve caller details from.
185 * @param contactInfo The CallerInfo. May be null.
186 * @return The number presentation constant to insert into the call logs.
187 */
188 private int getPresentation(Call call, ContactInfo contactInfo) {
189 return PhoneConstants.PRESENTATION_ALLOWED;
190 }
191
192 /**
193 * Adds the call defined by the parameters in the provided AddCallArgs to the CallLogProvider
194 * using an AsyncTask to avoid blocking the main thread.
195 *
196 * @param args Prepopulated call details.
197 * @return A handle to the AsyncTask that will add the call to the call log asynchronously.
198 */
199 public AsyncTask<AddCallArgs, Void, Uri[]> logCallAsync(AddCallArgs args) {
200 return new LogCallAsyncTask().execute(args);
201 }
202
203 /**
204 * Helper AsyncTask to access the call logs database asynchronously since database operations
205 * can take a long time depending on the system's load. Since it extends AsyncTask, it uses
206 * its own thread pool.
207 */
208 private class LogCallAsyncTask extends AsyncTask<AddCallArgs, Void, Uri[]> {
209 @Override
210 protected Uri[] doInBackground(AddCallArgs... callList) {
211 int count = callList.length;
212 Uri[] result = new Uri[count];
213 for (int i = 0; i < count; i++) {
214 AddCallArgs c = callList[i];
215
216 try {
217 // May block.
218 result[i] = Calls.addCall(null, c.context, c.number, c.presentation,
219 c.callType, c.timestamp, c.durationInSec);
220 } catch (Exception e) {
221 // This is very rare but may happen in legitimate cases.
222 // E.g. If the phone is encrypted and thus write request fails, it may cause
223 // some kind of Exception (right now it is IllegalArgumentException, but this
224 // might change).
225 //
226 // We don't want to crash the whole process just because of that, so just log
227 // it instead.
228 Log.e(TAG, e, "Exception raised during adding CallLog entry.");
229 result[i] = null;
230 }
231 }
232 return result;
233 }
234
235 /**
236 * Performs a simple sanity check to make sure the call was written in the database.
237 * Typically there is only one result per call so it is easy to identify which one failed.
238 */
239 @Override
240 protected void onPostExecute(Uri[] result) {
241 for (Uri uri : result) {
242 if (uri == null) {
243 Log.w(TAG, "Failed to write call to the log.");
244 }
245 }
246 }
247 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800248}