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