• 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 ISO 3166-1 two letters country code of the country where the
141          * user received or made the call.
142          * <P>
143          * Type: TEXT
144          * </P>
145          *
146          * @hide
147          */
148         public static final String COUNTRY_ISO = "countryiso";
149 
150         /**
151          * The date the call occured, in milliseconds since the epoch
152          * <P>Type: INTEGER (long)</P>
153          */
154         public static final String DATE = "date";
155 
156         /**
157          * The duration of the call in seconds
158          * <P>Type: INTEGER (long)</P>
159          */
160         public static final String DURATION = "duration";
161 
162         /**
163          * Whether or not the call has been acknowledged
164          * <P>Type: INTEGER (boolean)</P>
165          */
166         public static final String NEW = "new";
167 
168         /**
169          * The cached name associated with the phone number, if it exists.
170          * This value is not guaranteed to be current, if the contact information
171          * associated with this number has changed.
172          * <P>Type: TEXT</P>
173          */
174         public static final String CACHED_NAME = "name";
175 
176         /**
177          * The cached number type (Home, Work, etc) associated with the
178          * phone number, if it exists.
179          * This value is not guaranteed to be current, if the contact information
180          * associated with this number has changed.
181          * <P>Type: INTEGER</P>
182          */
183         public static final String CACHED_NUMBER_TYPE = "numbertype";
184 
185         /**
186          * The cached number label, for a custom number type, associated with the
187          * phone number, if it exists.
188          * This value is not guaranteed to be current, if the contact information
189          * associated with this number has changed.
190          * <P>Type: TEXT</P>
191          */
192         public static final String CACHED_NUMBER_LABEL = "numberlabel";
193 
194         /**
195          * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}.
196          * <P>Type: TEXT</P>
197          * @hide
198          */
199         public static final String VOICEMAIL_URI = "voicemail_uri";
200 
201         /**
202          * Whether this item has been read or otherwise consumed by the user.
203          * <p>
204          * Unlike the {@link #NEW} field, which requires the user to have acknowledged the
205          * existence of the entry, this implies the user has interacted with the entry.
206          * <P>Type: INTEGER (boolean)</P>
207          */
208         public static final String IS_READ = "is_read";
209 
210         /**
211          * A geocoded location for the number associated with this call.
212          * <p>
213          * The string represents a city, state, or country associated with the number.
214          * <P>Type: TEXT</P>
215          * @hide
216          */
217         public static final String GEOCODED_LOCATION = "geocoded_location";
218 
219         /**
220          * The cached URI to look up the contact associated with the phone number, if it exists.
221          * This value is not guaranteed to be current, if the contact information
222          * associated with this number has changed.
223          * <P>Type: TEXT</P>
224          * @hide
225          */
226         public static final String CACHED_LOOKUP_URI = "lookup_uri";
227 
228         /**
229          * The cached phone number of the contact which matches this entry, if it exists.
230          * This value is not guaranteed to be current, if the contact information
231          * associated with this number has changed.
232          * <P>Type: TEXT</P>
233          * @hide
234          */
235         public static final String CACHED_MATCHED_NUMBER = "matched_number";
236 
237         /**
238          * The cached normalized version of the phone number, if it exists.
239          * This value is not guaranteed to be current, if the contact information
240          * associated with this number has changed.
241          * <P>Type: TEXT</P>
242          * @hide
243          */
244         public static final String CACHED_NORMALIZED_NUMBER = "normalized_number";
245 
246         /**
247          * The cached photo id of the picture associated with the phone number, if it exists.
248          * This value is not guaranteed to be current, if the contact information
249          * associated with this number has changed.
250          * <P>Type: INTEGER (long)</P>
251          * @hide
252          */
253         public static final String CACHED_PHOTO_ID = "photo_id";
254 
255         /**
256          * The cached formatted phone number.
257          * This value is not guaranteed to be present.
258          * <P>Type: TEXT</P>
259          * @hide
260          */
261         public static final String CACHED_FORMATTED_NUMBER = "formatted_number";
262 
263         /**
264          * Adds a call to the call log.
265          *
266          * @param ci the CallerInfo object to get the target contact from.  Can be null
267          * if the contact is unknown.
268          * @param context the context used to get the ContentResolver
269          * @param number the phone number to be added to the calls db
270          * @param presentation the number presenting rules set by the network for
271          *        "allowed", "payphone", "restricted" or "unknown"
272          * @param callType enumerated values for "incoming", "outgoing", or "missed"
273          * @param start time stamp for the call in milliseconds
274          * @param duration call duration in seconds
275          *
276          * {@hide}
277          */
addCall(CallerInfo ci, Context context, String number, int presentation, int callType, long start, int duration)278         public static Uri addCall(CallerInfo ci, Context context, String number,
279                 int presentation, int callType, long start, int duration) {
280             final ContentResolver resolver = context.getContentResolver();
281 
282             // If this is a private number then set the number to Private, otherwise check
283             // if the number field is empty and set the number to Unavailable
284             if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
285                 number = CallerInfo.PRIVATE_NUMBER;
286                 if (ci != null) ci.name = "";
287             } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
288                 number = CallerInfo.PAYPHONE_NUMBER;
289                 if (ci != null) ci.name = "";
290             } else if (TextUtils.isEmpty(number)
291                     || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
292                 number = CallerInfo.UNKNOWN_NUMBER;
293                 if (ci != null) ci.name = "";
294             }
295 
296             ContentValues values = new ContentValues(5);
297 
298             values.put(NUMBER, number);
299             values.put(TYPE, Integer.valueOf(callType));
300             values.put(DATE, Long.valueOf(start));
301             values.put(DURATION, Long.valueOf(duration));
302             values.put(NEW, Integer.valueOf(1));
303             if (callType == MISSED_TYPE) {
304                 values.put(IS_READ, Integer.valueOf(0));
305             }
306             if (ci != null) {
307                 values.put(CACHED_NAME, ci.name);
308                 values.put(CACHED_NUMBER_TYPE, ci.numberType);
309                 values.put(CACHED_NUMBER_LABEL, ci.numberLabel);
310             }
311 
312             if ((ci != null) && (ci.person_id > 0)) {
313                 // Update usage information for the number associated with the contact ID.
314                 // We need to use both the number and the ID for obtaining a data ID since other
315                 // contacts may have the same number.
316 
317                 final Cursor cursor;
318 
319                 // We should prefer normalized one (probably coming from
320                 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
321                 if (ci.normalizedNumber != null) {
322                     final String normalizedPhoneNumber = ci.normalizedNumber;
323                     cursor = resolver.query(Phone.CONTENT_URI,
324                             new String[] { Phone._ID },
325                             Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
326                             new String[] { String.valueOf(ci.person_id), normalizedPhoneNumber},
327                             null);
328                 } else {
329                     final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
330                     cursor = resolver.query(
331                             Uri.withAppendedPath(Callable.CONTENT_FILTER_URI,
332                                     Uri.encode(phoneNumber)),
333                             new String[] { Phone._ID },
334                             Phone.CONTACT_ID + " =?",
335                             new String[] { String.valueOf(ci.person_id) },
336                             null);
337                 }
338 
339                 if (cursor != null) {
340                     try {
341                         if (cursor.getCount() > 0 && cursor.moveToFirst()) {
342                             final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
343                                     .appendPath(cursor.getString(0))
344                                     .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
345                                                 DataUsageFeedback.USAGE_TYPE_CALL)
346                                     .build();
347                             resolver.update(feedbackUri, new ContentValues(), null, null);
348                         }
349                     } finally {
350                         cursor.close();
351                     }
352                 }
353             }
354 
355             Uri result = resolver.insert(CONTENT_URI, values);
356 
357             removeExpiredEntries(context);
358 
359             return result;
360         }
361 
362         /**
363          * Query the call log database for the last dialed number.
364          * @param context Used to get the content resolver.
365          * @return The last phone number dialed (outgoing) or an empty
366          * string if none exist yet.
367          */
getLastOutgoingCall(Context context)368         public static String getLastOutgoingCall(Context context) {
369             final ContentResolver resolver = context.getContentResolver();
370             Cursor c = null;
371             try {
372                 c = resolver.query(
373                     CONTENT_URI,
374                     new String[] {NUMBER},
375                     TYPE + " = " + OUTGOING_TYPE,
376                     null,
377                     DEFAULT_SORT_ORDER + " LIMIT 1");
378                 if (c == null || !c.moveToFirst()) {
379                     return "";
380                 }
381                 return c.getString(0);
382             } finally {
383                 if (c != null) c.close();
384             }
385         }
386 
removeExpiredEntries(Context context)387         private static void removeExpiredEntries(Context context) {
388             final ContentResolver resolver = context.getContentResolver();
389             resolver.delete(CONTENT_URI, "_id IN " +
390                     "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
391                     + " LIMIT -1 OFFSET 500)", null);
392         }
393     }
394 }
395