• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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 
17 
18 package android.provider;
19 
20 import android.content.ContentResolver;
21 import android.content.ContentValues;
22 import android.content.Context;
23 import android.database.Cursor;
24 import android.net.Uri;
25 import android.provider.ContactsContract.CommonDataKinds.Callable;
26 import android.provider.ContactsContract.CommonDataKinds.Phone;
27 import android.provider.ContactsContract.DataUsageFeedback;
28 import android.text.TextUtils;
29 
30 import com.android.internal.telephony.CallerInfo;
31 import com.android.internal.telephony.PhoneConstants;
32 
33 /**
34  * The CallLog provider contains information about placed and received calls.
35  */
36 public class CallLog {
37     public static final String AUTHORITY = "call_log";
38 
39     /**
40      * The content:// style URL for this provider
41      */
42     public static final Uri CONTENT_URI =
43         Uri.parse("content://" + AUTHORITY);
44 
45     /**
46      * Contains the recent calls.
47      */
48     public static class Calls implements BaseColumns {
49         /**
50          * The content:// style URL for this table
51          */
52         public static final Uri CONTENT_URI =
53                 Uri.parse("content://call_log/calls");
54 
55         /**
56          * The content:// style URL for filtering this table on phone numbers
57          */
58         public static final Uri CONTENT_FILTER_URI =
59                 Uri.parse("content://call_log/calls/filter");
60 
61         /**
62          * Query parameter used to limit the number of call logs returned.
63          * <p>
64          * TYPE: integer
65          */
66         public static final String LIMIT_PARAM_KEY = "limit";
67 
68         /**
69          * Query parameter used to specify the starting record to return.
70          * <p>
71          * TYPE: integer
72          */
73         public static final String OFFSET_PARAM_KEY = "offset";
74 
75         /**
76          * An optional URI parameter which instructs the provider to allow the operation to be
77          * applied to voicemail records as well.
78          * <p>
79          * TYPE: Boolean
80          * <p>
81          * Using this parameter with a value of {@code true} will result in a security error if the
82          * calling package does not have appropriate permissions to access voicemails.
83          *
84          * @hide
85          */
86         public static final String ALLOW_VOICEMAILS_PARAM_KEY = "allow_voicemails";
87 
88         /**
89          * Content uri with {@link #ALLOW_VOICEMAILS_PARAM_KEY} set. This can directly be used to
90          * access call log entries that includes voicemail records.
91          *
92          * @hide
93          */
94         public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon()
95                 .appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true")
96                 .build();
97 
98         /**
99          * The default sort order for this table
100          */
101         public static final String DEFAULT_SORT_ORDER = "date DESC";
102 
103         /**
104          * The MIME type of {@link #CONTENT_URI} and {@link #CONTENT_FILTER_URI}
105          * providing a directory of calls.
106          */
107         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/calls";
108 
109         /**
110          * The MIME type of a {@link #CONTENT_URI} sub-directory of a single
111          * call.
112          */
113         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls";
114 
115         /**
116          * The type of the call (incoming, outgoing or missed).
117          * <P>Type: INTEGER (int)</P>
118          */
119         public static final String TYPE = "type";
120 
121         /** Call log type for incoming calls. */
122         public static final int INCOMING_TYPE = 1;
123         /** Call log type for outgoing calls. */
124         public static final int OUTGOING_TYPE = 2;
125         /** Call log type for missed calls. */
126         public static final int MISSED_TYPE = 3;
127         /**
128          * Call log type for voicemails.
129          * @hide
130          */
131         public static final int VOICEMAIL_TYPE = 4;
132 
133         /**
134          * The phone number as the user entered it.
135          * <P>Type: TEXT</P>
136          */
137         public static final String NUMBER = "number";
138 
139         /**
140          * The number presenting rules set by the network.
141          *
142          * <p>
143          * Allowed values:
144          * <ul>
145          * <li>{@link #PRESENTATION_ALLOWED}</li>
146          * <li>{@link #PRESENTATION_RESTRICTED}</li>
147          * <li>{@link #PRESENTATION_UNKNOWN}</li>
148          * <li>{@link #PRESENTATION_PAYPHONE}</li>
149          * </ul>
150          * </p>
151          *
152          * <P>Type: INTEGER</P>
153          */
154         public static final String NUMBER_PRESENTATION = "presentation";
155 
156         /** Number is allowed to display for caller id. */
157         public static final int PRESENTATION_ALLOWED = 1;
158         /** Number is blocked by user. */
159         public static final int PRESENTATION_RESTRICTED = 2;
160         /** Number is not specified or unknown by network. */
161         public static final int PRESENTATION_UNKNOWN = 3;
162         /** Number is a pay phone. */
163         public static final int PRESENTATION_PAYPHONE = 4;
164 
165         /**
166          * The ISO 3166-1 two letters country code of the country where the
167          * user received or made the call.
168          * <P>
169          * Type: TEXT
170          * </P>
171          *
172          * @hide
173          */
174         public static final String COUNTRY_ISO = "countryiso";
175 
176         /**
177          * The date the call occured, in milliseconds since the epoch
178          * <P>Type: INTEGER (long)</P>
179          */
180         public static final String DATE = "date";
181 
182         /**
183          * The duration of the call in seconds
184          * <P>Type: INTEGER (long)</P>
185          */
186         public static final String DURATION = "duration";
187 
188         /**
189          * Whether or not the call has been acknowledged
190          * <P>Type: INTEGER (boolean)</P>
191          */
192         public static final String NEW = "new";
193 
194         /**
195          * The cached name associated with the phone number, if it exists.
196          * This value is not guaranteed to be current, if the contact information
197          * associated with this number has changed.
198          * <P>Type: TEXT</P>
199          */
200         public static final String CACHED_NAME = "name";
201 
202         /**
203          * The cached number type (Home, Work, etc) associated with the
204          * phone number, if it exists.
205          * This value is not guaranteed to be current, if the contact information
206          * associated with this number has changed.
207          * <P>Type: INTEGER</P>
208          */
209         public static final String CACHED_NUMBER_TYPE = "numbertype";
210 
211         /**
212          * The cached number label, for a custom number type, associated with the
213          * phone number, if it exists.
214          * This value is not guaranteed to be current, if the contact information
215          * associated with this number has changed.
216          * <P>Type: TEXT</P>
217          */
218         public static final String CACHED_NUMBER_LABEL = "numberlabel";
219 
220         /**
221          * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}.
222          * <P>Type: TEXT</P>
223          * @hide
224          */
225         public static final String VOICEMAIL_URI = "voicemail_uri";
226 
227         /**
228          * Whether this item has been read or otherwise consumed by the user.
229          * <p>
230          * Unlike the {@link #NEW} field, which requires the user to have acknowledged the
231          * existence of the entry, this implies the user has interacted with the entry.
232          * <P>Type: INTEGER (boolean)</P>
233          */
234         public static final String IS_READ = "is_read";
235 
236         /**
237          * A geocoded location for the number associated with this call.
238          * <p>
239          * The string represents a city, state, or country associated with the number.
240          * <P>Type: TEXT</P>
241          * @hide
242          */
243         public static final String GEOCODED_LOCATION = "geocoded_location";
244 
245         /**
246          * The cached URI to look up the contact associated with the phone number, if it exists.
247          * This value is not guaranteed to be current, if the contact information
248          * associated with this number has changed.
249          * <P>Type: TEXT</P>
250          * @hide
251          */
252         public static final String CACHED_LOOKUP_URI = "lookup_uri";
253 
254         /**
255          * The cached phone number of the contact which matches this entry, if it exists.
256          * This value is not guaranteed to be current, if the contact information
257          * associated with this number has changed.
258          * <P>Type: TEXT</P>
259          * @hide
260          */
261         public static final String CACHED_MATCHED_NUMBER = "matched_number";
262 
263         /**
264          * The cached normalized version of the phone number, if it exists.
265          * This value is not guaranteed to be current, if the contact information
266          * associated with this number has changed.
267          * <P>Type: TEXT</P>
268          * @hide
269          */
270         public static final String CACHED_NORMALIZED_NUMBER = "normalized_number";
271 
272         /**
273          * The cached photo id of the picture associated with the phone number, if it exists.
274          * This value is not guaranteed to be current, if the contact information
275          * associated with this number has changed.
276          * <P>Type: INTEGER (long)</P>
277          * @hide
278          */
279         public static final String CACHED_PHOTO_ID = "photo_id";
280 
281         /**
282          * The cached formatted phone number.
283          * This value is not guaranteed to be present.
284          * <P>Type: TEXT</P>
285          * @hide
286          */
287         public static final String CACHED_FORMATTED_NUMBER = "formatted_number";
288 
289         /**
290          * Adds a call to the call log.
291          *
292          * @param ci the CallerInfo object to get the target contact from.  Can be null
293          * if the contact is unknown.
294          * @param context the context used to get the ContentResolver
295          * @param number the phone number to be added to the calls db
296          * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
297          *        is set by the network and denotes the number presenting rules for
298          *        "allowed", "payphone", "restricted" or "unknown"
299          * @param callType enumerated values for "incoming", "outgoing", or "missed"
300          * @param start time stamp for the call in milliseconds
301          * @param duration call duration in seconds
302          *
303          * {@hide}
304          */
addCall(CallerInfo ci, Context context, String number, int presentation, int callType, long start, int duration)305         public static Uri addCall(CallerInfo ci, Context context, String number,
306                 int presentation, int callType, long start, int duration) {
307             final ContentResolver resolver = context.getContentResolver();
308             int numberPresentation = PRESENTATION_ALLOWED;
309 
310             // Remap network specified number presentation types
311             // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
312             // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
313             // from any future radio changes.
314             // If the number field is empty set the presentation type to Unknown.
315             if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
316                 numberPresentation = PRESENTATION_RESTRICTED;
317             } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
318                 numberPresentation = PRESENTATION_PAYPHONE;
319             } else if (TextUtils.isEmpty(number)
320                     || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
321                 numberPresentation = PRESENTATION_UNKNOWN;
322             }
323             if (numberPresentation != PRESENTATION_ALLOWED) {
324                 number = "";
325                 if (ci != null) {
326                     ci.name = "";
327                 }
328             }
329 
330             ContentValues values = new ContentValues(6);
331 
332             values.put(NUMBER, number);
333             values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
334             values.put(TYPE, Integer.valueOf(callType));
335             values.put(DATE, Long.valueOf(start));
336             values.put(DURATION, Long.valueOf(duration));
337             values.put(NEW, Integer.valueOf(1));
338             if (callType == MISSED_TYPE) {
339                 values.put(IS_READ, Integer.valueOf(0));
340             }
341             if (ci != null) {
342                 values.put(CACHED_NAME, ci.name);
343                 values.put(CACHED_NUMBER_TYPE, ci.numberType);
344                 values.put(CACHED_NUMBER_LABEL, ci.numberLabel);
345             }
346 
347             if ((ci != null) && (ci.person_id > 0)) {
348                 // Update usage information for the number associated with the contact ID.
349                 // We need to use both the number and the ID for obtaining a data ID since other
350                 // contacts may have the same number.
351 
352                 final Cursor cursor;
353 
354                 // We should prefer normalized one (probably coming from
355                 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
356                 if (ci.normalizedNumber != null) {
357                     final String normalizedPhoneNumber = ci.normalizedNumber;
358                     cursor = resolver.query(Phone.CONTENT_URI,
359                             new String[] { Phone._ID },
360                             Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
361                             new String[] { String.valueOf(ci.person_id), normalizedPhoneNumber},
362                             null);
363                 } else {
364                     final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
365                     cursor = resolver.query(
366                             Uri.withAppendedPath(Callable.CONTENT_FILTER_URI,
367                                     Uri.encode(phoneNumber)),
368                             new String[] { Phone._ID },
369                             Phone.CONTACT_ID + " =?",
370                             new String[] { String.valueOf(ci.person_id) },
371                             null);
372                 }
373 
374                 if (cursor != null) {
375                     try {
376                         if (cursor.getCount() > 0 && cursor.moveToFirst()) {
377                             final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
378                                     .appendPath(cursor.getString(0))
379                                     .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
380                                                 DataUsageFeedback.USAGE_TYPE_CALL)
381                                     .build();
382                             resolver.update(feedbackUri, new ContentValues(), null, null);
383                         }
384                     } finally {
385                         cursor.close();
386                     }
387                 }
388             }
389 
390             Uri result = resolver.insert(CONTENT_URI, values);
391 
392             removeExpiredEntries(context);
393 
394             return result;
395         }
396 
397         /**
398          * Query the call log database for the last dialed number.
399          * @param context Used to get the content resolver.
400          * @return The last phone number dialed (outgoing) or an empty
401          * string if none exist yet.
402          */
getLastOutgoingCall(Context context)403         public static String getLastOutgoingCall(Context context) {
404             final ContentResolver resolver = context.getContentResolver();
405             Cursor c = null;
406             try {
407                 c = resolver.query(
408                     CONTENT_URI,
409                     new String[] {NUMBER},
410                     TYPE + " = " + OUTGOING_TYPE,
411                     null,
412                     DEFAULT_SORT_ORDER + " LIMIT 1");
413                 if (c == null || !c.moveToFirst()) {
414                     return "";
415                 }
416                 return c.getString(0);
417             } finally {
418                 if (c != null) c.close();
419             }
420         }
421 
removeExpiredEntries(Context context)422         private static void removeExpiredEntries(Context context) {
423             final ContentResolver resolver = context.getContentResolver();
424             resolver.delete(CONTENT_URI, "_id IN " +
425                     "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
426                     + " LIMIT -1 OFFSET 500)", null);
427         }
428     }
429 }
430