blob: 712551ea12f21bac5b2c76c0ff11f368a49c6412 [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
27import com.android.internal.telephony.PhoneConstants;
28
29/**
30 * Helper class that provides functionality to write information about calls and their associated
31 * caller details to the call log. All logging activity will be performed asynchronously in a
32 * background thread to avoid blocking on the main thread.
33 */
Sailesh Nepal810735e2014-03-18 18:15:46 -070034final class CallLogManager extends CallsManagerListenerBase {
Yorke Leef98fb572014-03-05 10:56:55 -080035 /**
36 * Parameter object to hold the arguments to add a call in the call log DB.
37 */
38 private static class AddCallArgs {
39 /**
40 * @param contactInfo Caller details.
41 * @param number The phone number to be logged.
42 * @param presentation Number presentation of the phone number to be logged.
43 * @param callType The type of call (e.g INCOMING_TYPE). @see
44 * {@link android.provider.CallLog} for the list of values.
Tyler Gunn765c35c2014-07-10 08:17:53 -070045 * @param features The features of the call (e.g. FEATURES_VIDEO). @see
46 * {@link android.provider.CallLog} for the list of values.
Yorke Leef98fb572014-03-05 10:56:55 -080047 * @param creationDate Time when the call was created (milliseconds since epoch).
48 * @param durationInMillis Duration of the call (milliseconds).
Tyler Gunn765c35c2014-07-10 08:17:53 -070049 * @param dataUsage Data usage in bytes, or null if not applicable.
Yorke Leef98fb572014-03-05 10:56:55 -080050 */
51 public AddCallArgs(Context context, ContactInfo contactInfo, String number,
Tyler Gunn765c35c2014-07-10 08:17:53 -070052 int presentation, int callType, int features, PhoneAccount account,
53 long creationDate, long durationInMillis, Long dataUsage) {
Yorke Leef98fb572014-03-05 10:56:55 -080054 this.context = context;
55 this.contactInfo = contactInfo;
56 this.number = number;
57 this.presentation = presentation;
58 this.callType = callType;
Tyler Gunn765c35c2014-07-10 08:17:53 -070059 this.features = features;
Ihab Awad98a55602014-06-30 21:27:28 -070060 this.mAccount = account;
Yorke Leef98fb572014-03-05 10:56:55 -080061 this.timestamp = creationDate;
62 this.durationInSec = (int)(durationInMillis / 1000);
Tyler Gunn765c35c2014-07-10 08:17:53 -070063 this.dataUsage = dataUsage;
Yorke Leef98fb572014-03-05 10:56:55 -080064 }
65 // Since the members are accessed directly, we don't use the
66 // mXxxx notation.
67 public final Context context;
68 public final ContactInfo contactInfo;
69 public final String number;
70 public final int presentation;
71 public final int callType;
Tyler Gunn765c35c2014-07-10 08:17:53 -070072 public final int features;
Ihab Awad98a55602014-06-30 21:27:28 -070073 public final PhoneAccount mAccount;
Yorke Leef98fb572014-03-05 10:56:55 -080074 public final long timestamp;
75 public final int durationInSec;
Tyler Gunn765c35c2014-07-10 08:17:53 -070076 public final Long dataUsage;
Yorke Leef98fb572014-03-05 10:56:55 -080077 }
78
79 private static final String TAG = CallLogManager.class.getSimpleName();
80
81 private final Context mContext;
82
83 public CallLogManager(Context context) {
84 mContext = context;
85 }
86
Sailesh Nepal810735e2014-03-18 18:15:46 -070087 @Override
88 public void onCallStateChanged(Call call, CallState oldState, CallState newState) {
89 if (newState == CallState.DISCONNECTED || newState == CallState.ABORTED) {
90 int type;
91 if (!call.isIncoming()) {
92 type = Calls.OUTGOING_TYPE;
93 } else if (oldState == CallState.RINGING) {
94 type = Calls.MISSED_TYPE;
95 } else {
96 type = Calls.INCOMING_TYPE;
97 }
98 logCall(call, type);
99 }
Yorke Leef98fb572014-03-05 10:56:55 -0800100 }
101
102 /**
103 * Logs a call to the call log based on the {@link Call} object passed in.
104 *
105 * @param call The call object being logged
106 * @param callLogType The type of call log entry to log this call as. See:
107 * {@link android.provider.CallLog.Calls#INCOMING_TYPE}
108 * {@link android.provider.CallLog.Calls#OUTGOING_TYPE}
109 * {@link android.provider.CallLog.Calls#MISSED_TYPE}
110 */
111 private void logCall(Call call, int callLogType) {
Sailesh Nepal8c85dee2014-04-07 22:21:40 -0700112 final long creationTime = call.getCreationTimeMillis();
113 final long age = call.getAgeMillis();
Yorke Leef98fb572014-03-05 10:56:55 -0800114
Santos Cordonfd71f4a2014-05-28 13:59:49 -0700115 // TODO(santoscordon): Replace with use of call.getCallerInfo() or similar.
116 final ContactInfo contactInfo = null;
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
121 final int presentation = getPresentation(call, contactInfo);
Sailesh Nepalb0ba0872014-07-08 22:09:50 -0700122 final PhoneAccount account = call.getPhoneAccount();
Yorke Leef98fb572014-03-05 10:56:55 -0800123
Tyler Gunn765c35c2014-07-10 08:17:53 -0700124 // TODO: Once features and data usage are available, wire them up here.
125 logCall(contactInfo, logNumber, presentation, callLogType, Calls.FEATURES_NONE, account,
126 creationTime, age, null);
Yorke Leef98fb572014-03-05 10:56:55 -0800127 }
128
129 /**
130 * Inserts a call into the call log, based on the parameters passed in.
131 *
132 * @param contactInfo Caller details.
133 * @param number The number the call was made to or from.
134 * @param presentation
135 * @param callType The type of call.
Tyler Gunn765c35c2014-07-10 08:17:53 -0700136 * @param features The features of the call.
Yorke Leef98fb572014-03-05 10:56:55 -0800137 * @param start The start time of the call, in milliseconds.
138 * @param duration The duration of the call, in milliseconds.
Tyler Gunn765c35c2014-07-10 08:17:53 -0700139 * @param dataUsage The data usage for the call, null if not applicable.
Yorke Leef98fb572014-03-05 10:56:55 -0800140 */
141 private void logCall(
142 ContactInfo contactInfo,
143 String number,
144 int presentation,
145 int callType,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700146 int features,
Ihab Awad98a55602014-06-30 21:27:28 -0700147 PhoneAccount account,
Yorke Leef98fb572014-03-05 10:56:55 -0800148 long start,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700149 long duration,
150 Long dataUsage) {
Yorke Lee66255452014-06-05 08:09:24 -0700151 boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(mContext, number);
Yorke Leef98fb572014-03-05 10:56:55 -0800152
153 // On some devices, to avoid accidental redialing of emergency numbers, we *never* log
154 // emergency calls to the Call Log. (This behavior is set on a per-product basis, based
155 // on carrier requirements.)
156 final boolean okToLogEmergencyNumber =
157 mContext.getResources().getBoolean(R.bool.allow_emergency_numbers_in_call_log);
158
159 // Don't log emergency numbers if the device doesn't allow it.
160 final boolean isOkToLogThisCall = !isEmergencyNumber || okToLogEmergencyNumber;
161
162 if (isOkToLogThisCall) {
163 Log.d(TAG, "Logging Calllog entry: " + contactInfo + ", "
164 + Log.pii(number) + "," + presentation + ", " + callType
165 + ", " + start + ", " + duration);
166 AddCallArgs args = new AddCallArgs(mContext, contactInfo, number, presentation,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700167 callType, features, account, start, duration, dataUsage);
Yorke Leef98fb572014-03-05 10:56:55 -0800168 logCallAsync(args);
169 } else {
170 Log.d(TAG, "Not adding emergency call to call log.");
171 }
172 }
173
174 /**
175 * Retrieve the phone number from the call, and then process it before returning the
176 * actual number that is to be logged.
177 *
178 * @param call The phone connection.
179 * @return the phone number to be logged.
180 */
181 private String getLogNumber(Call call) {
Yorke Lee33501632014-03-17 19:24:12 -0700182 Uri handle = call.getOriginalHandle();
Yorke Leef98fb572014-03-05 10:56:55 -0800183
184 if (handle == null) {
185 return null;
186 }
187
Yorke Lee060d1d62014-03-19 13:24:15 -0700188 String handleString = handle.getSchemeSpecificPart();
Sailesh Nepalce704b92014-03-17 18:31:43 -0700189 if (!PhoneNumberUtils.isUriNumber(handleString)) {
190 handleString = PhoneNumberUtils.stripSeparators(handleString);
Yorke Leef98fb572014-03-05 10:56:55 -0800191 }
Sailesh Nepalce704b92014-03-17 18:31:43 -0700192 return handleString;
Yorke Leef98fb572014-03-05 10:56:55 -0800193 }
194
195 /**
196 * Gets the presentation from the {@link ContactInfo} if not null. Otherwise, gets it from the
197 * {@link Call}.
198 *
199 * TODO: There needs to be a way to pass information from
200 * Connection.getNumberPresentation() into a {@link Call} object. Until then, always return
201 * PhoneConstants.PRESENTATION_ALLOWED. On top of that, we might need to introduce
202 * getNumberPresentation to the ContactInfo object as well.
203 *
204 * @param call The call object to retrieve caller details from.
205 * @param contactInfo The CallerInfo. May be null.
206 * @return The number presentation constant to insert into the call logs.
207 */
208 private int getPresentation(Call call, ContactInfo contactInfo) {
209 return PhoneConstants.PRESENTATION_ALLOWED;
210 }
211
212 /**
213 * Adds the call defined by the parameters in the provided AddCallArgs to the CallLogProvider
214 * using an AsyncTask to avoid blocking the main thread.
215 *
216 * @param args Prepopulated call details.
217 * @return A handle to the AsyncTask that will add the call to the call log asynchronously.
218 */
219 public AsyncTask<AddCallArgs, Void, Uri[]> logCallAsync(AddCallArgs args) {
220 return new LogCallAsyncTask().execute(args);
221 }
222
223 /**
224 * Helper AsyncTask to access the call logs database asynchronously since database operations
225 * can take a long time depending on the system's load. Since it extends AsyncTask, it uses
226 * its own thread pool.
227 */
228 private class LogCallAsyncTask extends AsyncTask<AddCallArgs, Void, Uri[]> {
229 @Override
230 protected Uri[] doInBackground(AddCallArgs... callList) {
231 int count = callList.length;
232 Uri[] result = new Uri[count];
233 for (int i = 0; i < count; i++) {
234 AddCallArgs c = callList[i];
235
236 try {
237 // May block.
238 result[i] = Calls.addCall(null, c.context, c.number, c.presentation,
Tyler Gunn765c35c2014-07-10 08:17:53 -0700239 c.callType, c.features, c.mAccount, c.timestamp, c.durationInSec,
240 c.dataUsage);
Yorke Leef98fb572014-03-05 10:56:55 -0800241 } catch (Exception e) {
242 // This is very rare but may happen in legitimate cases.
243 // E.g. If the phone is encrypted and thus write request fails, it may cause
244 // some kind of Exception (right now it is IllegalArgumentException, but this
245 // might change).
246 //
247 // We don't want to crash the whole process just because of that, so just log
248 // it instead.
249 Log.e(TAG, e, "Exception raised during adding CallLog entry.");
250 result[i] = null;
251 }
252 }
253 return result;
254 }
255
256 /**
257 * Performs a simple sanity check to make sure the call was written in the database.
258 * Typically there is only one result per call so it is easy to identify which one failed.
259 */
260 @Override
261 protected void onPostExecute(Uri[] result) {
262 for (Uri uri : result) {
263 if (uri == null) {
264 Log.w(TAG, "Failed to write call to the log.");
265 }
266 }
267 }
268 }
Ben Gilad9f2bed32013-12-12 17:43:26 -0800269}