CallLog.java revision 8dc2c62821c4e3fbec5d1528874bd244651a632e
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
18package android.provider;
19
20import android.content.ContentProvider;
21import android.content.ContentResolver;
22import android.content.ContentValues;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.UserInfo;
26import android.database.Cursor;
27import android.net.Uri;
28import android.os.UserHandle;
29import android.os.UserManager;
30import android.provider.ContactsContract.CommonDataKinds.Callable;
31import android.provider.ContactsContract.CommonDataKinds.Phone;
32import android.provider.ContactsContract.DataUsageFeedback;
33import android.telecomm.PhoneAccountHandle;
34import android.text.TextUtils;
35
36import com.android.internal.telephony.CallerInfo;
37import com.android.internal.telephony.PhoneConstants;
38
39import java.util.List;
40
41/**
42 * The CallLog provider contains information about placed and received calls.
43 */
44public class CallLog {
45    public static final String AUTHORITY = "call_log";
46
47    /**
48     * The content:// style URL for this provider
49     */
50    public static final Uri CONTENT_URI =
51        Uri.parse("content://" + AUTHORITY);
52
53    /**
54     * Contains the recent calls.
55     */
56    public static class Calls implements BaseColumns {
57        /**
58         * The content:// style URL for this table
59         */
60        public static final Uri CONTENT_URI =
61                Uri.parse("content://call_log/calls");
62
63        /**
64         * The content:// style URL for filtering this table on phone numbers
65         */
66        public static final Uri CONTENT_FILTER_URI =
67                Uri.parse("content://call_log/calls/filter");
68
69        /**
70         * Query parameter used to limit the number of call logs returned.
71         * <p>
72         * TYPE: integer
73         */
74        public static final String LIMIT_PARAM_KEY = "limit";
75
76        /**
77         * Query parameter used to specify the starting record to return.
78         * <p>
79         * TYPE: integer
80         */
81        public static final String OFFSET_PARAM_KEY = "offset";
82
83        /**
84         * An optional URI parameter which instructs the provider to allow the operation to be
85         * applied to voicemail records as well.
86         * <p>
87         * TYPE: Boolean
88         * <p>
89         * Using this parameter with a value of {@code true} will result in a security error if the
90         * calling package does not have appropriate permissions to access voicemails.
91         *
92         * @hide
93         */
94        public static final String ALLOW_VOICEMAILS_PARAM_KEY = "allow_voicemails";
95
96        /**
97         * An optional extra used with {@link #CONTENT_TYPE Calls.CONTENT_TYPE} and
98         * {@link Intent#ACTION_VIEW} to specify that the presented list of calls should be
99         * filtered for a particular call type.
100         *
101         * Applications implementing a call log UI should check for this extra, and display a
102         * filtered list of calls based on the specified call type. If not applicable within the
103         * application's UI, it should be silently ignored.
104         *
105         * <p>
106         * The following example brings up the call log, showing only missed calls.
107         * <pre>
108         * Intent intent = new Intent(Intent.ACTION_VIEW);
109         * intent.setType(CallLog.Calls.CONTENT_TYPE);
110         * intent.putExtra(CallLog.Calls.EXTRA_CALL_TYPE_FILTER, CallLog.Calls.MISSED_TYPE);
111         * startActivity(intent);
112         * </pre>
113         * </p>
114         */
115        public static final String EXTRA_CALL_TYPE_FILTER =
116                "android.provider.extra.CALL_TYPE_FILTER";
117
118        /**
119         * Content uri used to access call log entries, including voicemail records. You must have
120         * the READ_CALL_LOG and WRITE_CALL_LOG permissions to read and write to the call log, as
121         * well as READ_VOICEMAIL and WRITE_VOICEMAIL permissions to read and write voicemails.
122         */
123        public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon()
124                .appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true")
125                .build();
126
127        /**
128         * The default sort order for this table
129         */
130        public static final String DEFAULT_SORT_ORDER = "date DESC";
131
132        /**
133         * The MIME type of {@link #CONTENT_URI} and {@link #CONTENT_FILTER_URI}
134         * providing a directory of calls.
135         */
136        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/calls";
137
138        /**
139         * The MIME type of a {@link #CONTENT_URI} sub-directory of a single
140         * call.
141         */
142        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls";
143
144        /**
145         * The type of the call (incoming, outgoing or missed).
146         * <P>Type: INTEGER (int)</P>
147         */
148        public static final String TYPE = "type";
149
150        /** Call log type for incoming calls. */
151        public static final int INCOMING_TYPE = 1;
152        /** Call log type for outgoing calls. */
153        public static final int OUTGOING_TYPE = 2;
154        /** Call log type for missed calls. */
155        public static final int MISSED_TYPE = 3;
156        /** Call log type for voicemails. */
157        public static final int VOICEMAIL_TYPE = 4;
158
159        /**
160         * Bit-mask describing features of the call (e.g. video).
161         *
162         * <P>Type: INTEGER (int)</P>
163         */
164        public static final String FEATURES = "features";
165
166        /** Call had video. */
167        public static final int FEATURES_VIDEO = 0x1;
168
169        /**
170         * The phone number as the user entered it.
171         * <P>Type: TEXT</P>
172         */
173        public static final String NUMBER = "number";
174
175        /**
176         * The number presenting rules set by the network.
177         *
178         * <p>
179         * Allowed values:
180         * <ul>
181         * <li>{@link #PRESENTATION_ALLOWED}</li>
182         * <li>{@link #PRESENTATION_RESTRICTED}</li>
183         * <li>{@link #PRESENTATION_UNKNOWN}</li>
184         * <li>{@link #PRESENTATION_PAYPHONE}</li>
185         * </ul>
186         * </p>
187         *
188         * <P>Type: INTEGER</P>
189         */
190        public static final String NUMBER_PRESENTATION = "presentation";
191
192        /** Number is allowed to display for caller id. */
193        public static final int PRESENTATION_ALLOWED = 1;
194        /** Number is blocked by user. */
195        public static final int PRESENTATION_RESTRICTED = 2;
196        /** Number is not specified or unknown by network. */
197        public static final int PRESENTATION_UNKNOWN = 3;
198        /** Number is a pay phone. */
199        public static final int PRESENTATION_PAYPHONE = 4;
200
201        /**
202         * The ISO 3166-1 two letters country code of the country where the
203         * user received or made the call.
204         * <P>
205         * Type: TEXT
206         * </P>
207         */
208        public static final String COUNTRY_ISO = "countryiso";
209
210        /**
211         * The date the call occured, in milliseconds since the epoch
212         * <P>Type: INTEGER (long)</P>
213         */
214        public static final String DATE = "date";
215
216        /**
217         * The duration of the call in seconds
218         * <P>Type: INTEGER (long)</P>
219         */
220        public static final String DURATION = "duration";
221
222        /**
223         * The data usage of the call in bytes.
224         * <P>Type: INTEGER (long)</P>
225         */
226        public static final String DATA_USAGE = "data_usage";
227
228        /**
229         * Whether or not the call has been acknowledged
230         * <P>Type: INTEGER (boolean)</P>
231         */
232        public static final String NEW = "new";
233
234        /**
235         * The cached name associated with the phone number, if it exists.
236         * This value is not guaranteed to be current, if the contact information
237         * associated with this number has changed.
238         * <P>Type: TEXT</P>
239         */
240        public static final String CACHED_NAME = "name";
241
242        /**
243         * The cached number type (Home, Work, etc) associated with the
244         * phone number, if it exists.
245         * This value is not guaranteed to be current, if the contact information
246         * associated with this number has changed.
247         * <P>Type: INTEGER</P>
248         */
249        public static final String CACHED_NUMBER_TYPE = "numbertype";
250
251        /**
252         * The cached number label, for a custom number type, associated with the
253         * phone number, if it exists.
254         * This value is not guaranteed to be current, if the contact information
255         * associated with this number has changed.
256         * <P>Type: TEXT</P>
257         */
258        public static final String CACHED_NUMBER_LABEL = "numberlabel";
259
260        /**
261         * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}.
262         * <P>Type: TEXT</P>
263         */
264        public static final String VOICEMAIL_URI = "voicemail_uri";
265
266        /**
267         * Transcription of the call or voicemail entry. This will only be populated for call log
268         * entries of type {@link #VOICEMAIL_TYPE} that have valid transcriptions.
269         */
270        public static final String TRANSCRIPTION = "transcription";
271
272        /**
273         * Whether this item has been read or otherwise consumed by the user.
274         * <p>
275         * Unlike the {@link #NEW} field, which requires the user to have acknowledged the
276         * existence of the entry, this implies the user has interacted with the entry.
277         * <P>Type: INTEGER (boolean)</P>
278         */
279        public static final String IS_READ = "is_read";
280
281        /**
282         * A geocoded location for the number associated with this call.
283         * <p>
284         * The string represents a city, state, or country associated with the number.
285         * <P>Type: TEXT</P>
286         */
287        public static final String GEOCODED_LOCATION = "geocoded_location";
288
289        /**
290         * The cached URI to look up the contact associated with the phone number, if it exists.
291         * This value may not be current if the contact information associated with this number
292         * has changed.
293         * <P>Type: TEXT</P>
294         */
295        public static final String CACHED_LOOKUP_URI = "lookup_uri";
296
297        /**
298         * The cached phone number of the contact which matches this entry, if it exists.
299         * This value may not be current if the contact information associated with this number
300         * has changed.
301         * <P>Type: TEXT</P>
302         */
303        public static final String CACHED_MATCHED_NUMBER = "matched_number";
304
305        /**
306         * The cached normalized(E164) version of the phone number, if it exists.
307         * This value may not be current if the contact information associated with this number
308         * has changed.
309         * <P>Type: TEXT</P>
310         */
311        public static final String CACHED_NORMALIZED_NUMBER = "normalized_number";
312
313        /**
314         * The cached photo id of the picture associated with the phone number, if it exists.
315         * This value may not be current if the contact information associated with this number
316         * has changed.
317         * <P>Type: INTEGER (long)</P>
318         */
319        public static final String CACHED_PHOTO_ID = "photo_id";
320
321        /**
322         * The cached phone number, formatted with formatting rules based on the country the
323         * user was in when the call was made or received.
324         * This value is not guaranteed to be present, and may not be current if the contact
325         * information associated with this number
326         * has changed.
327         * <P>Type: TEXT</P>
328         */
329        public static final String CACHED_FORMATTED_NUMBER = "formatted_number";
330
331        // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming
332        // that was encoded into call log databases.
333
334        /**
335         * The component name of the account in string form.
336         * <P>Type: TEXT</P>
337         */
338        public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
339
340        /**
341         * The identifier of a account that is unique to a specified component.
342         * <P>Type: TEXT</P>
343         */
344        public static final String PHONE_ACCOUNT_ID = "subscription_id";
345
346        /**
347         * The identifier of a account that is unique to a specified component. Equivalent value
348         * to {@link #PHONE_ACCOUNT_ID}. For ContactsProvider internal use only.
349         * <P>Type: INTEGER</P>
350         *
351         * @hide
352         */
353        public static final String SUB_ID = "sub_id";
354
355        /**
356         * If a successful call is made that is longer than this duration, update the phone number
357         * in the ContactsProvider with the normalized version of the number, based on the user's
358         * current country code.
359         */
360        private static final int MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS = 1000 * 10;
361
362        /**
363         * Adds a call to the call log.
364         *
365         * @param ci the CallerInfo object to get the target contact from.  Can be null
366         * if the contact is unknown.
367         * @param context the context used to get the ContentResolver
368         * @param number the phone number to be added to the calls db
369         * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
370         *        is set by the network and denotes the number presenting rules for
371         *        "allowed", "payphone", "restricted" or "unknown"
372         * @param callType enumerated values for "incoming", "outgoing", or "missed"
373         * @param features features of the call (e.g. Video).
374         * @param accountHandle The accountHandle object identifying the provider of the call
375         * @param start time stamp for the call in milliseconds
376         * @param duration call duration in seconds
377         * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
378         *                  the call.
379         * @result The URI of the call log entry belonging to the user that made or received this
380         *        call.
381         * {@hide}
382         */
383        public static Uri addCall(CallerInfo ci, Context context, String number,
384                int presentation, int callType, int features, PhoneAccountHandle accountHandle,
385                long start, int duration, Long dataUsage) {
386            // FIXME using -1 as subId instead of SubscriptionManager.INVALID_SUB_ID
387            return addCall(ci, context, number, presentation, callType, features, accountHandle,
388                    start, duration, dataUsage, false);
389        }
390
391
392        /**
393         * Adds a call to the call log.
394         *
395         * @param ci the CallerInfo object to get the target contact from.  Can be null
396         * if the contact is unknown.
397         * @param context the context used to get the ContentResolver
398         * @param number the phone number to be added to the calls db
399         * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
400         *        is set by the network and denotes the number presenting rules for
401         *        "allowed", "payphone", "restricted" or "unknown"
402         * @param callType enumerated values for "incoming", "outgoing", or "missed"
403         * @param features features of the call (e.g. Video).
404         * @param accountHandle The accountHandle object identifying the provider of the call
405         * @param start time stamp for the call in milliseconds
406         * @param duration call duration in seconds
407         * @param subId the subscription id.
408         * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
409         *                  the call.
410         * @param addForAllUsers If true, the call is added to the call log of all currently
411         *        running users. The caller must have the MANAGE_USERS permission if this is true.
412         *
413         * @result The URI of the call log entry belonging to the user that made or received this
414         *        call.
415         * {@hide}
416         */
417        public static Uri addCall(CallerInfo ci, Context context, String number,
418                int presentation, int callType, int features, PhoneAccountHandle accountHandle,
419                long start, int duration, Long dataUsage, boolean addForAllUsers) {
420            final ContentResolver resolver = context.getContentResolver();
421            int numberPresentation = PRESENTATION_ALLOWED;
422
423            // Remap network specified number presentation types
424            // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
425            // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
426            // from any future radio changes.
427            // If the number field is empty set the presentation type to Unknown.
428            if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
429                numberPresentation = PRESENTATION_RESTRICTED;
430            } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
431                numberPresentation = PRESENTATION_PAYPHONE;
432            } else if (TextUtils.isEmpty(number)
433                    || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
434                numberPresentation = PRESENTATION_UNKNOWN;
435            }
436            if (numberPresentation != PRESENTATION_ALLOWED) {
437                number = "";
438                if (ci != null) {
439                    ci.name = "";
440                }
441            }
442
443            // accountHandle information
444            String accountComponentString = null;
445            String accountId = null;
446            if (accountHandle != null) {
447                accountComponentString = accountHandle.getComponentName().flattenToString();
448                accountId = accountHandle.getId();
449            }
450
451            ContentValues values = new ContentValues(6);
452
453            values.put(NUMBER, number);
454            values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
455            values.put(TYPE, Integer.valueOf(callType));
456            values.put(FEATURES, features);
457            values.put(DATE, Long.valueOf(start));
458            values.put(DURATION, Long.valueOf(duration));
459            if (dataUsage != null) {
460                values.put(DATA_USAGE, dataUsage);
461            }
462            values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
463            values.put(PHONE_ACCOUNT_ID, accountId);
464            values.put(NEW, Integer.valueOf(1));
465
466            if (callType == MISSED_TYPE) {
467                values.put(IS_READ, Integer.valueOf(0));
468            }
469            if (ci != null) {
470                values.put(CACHED_NAME, ci.name);
471                values.put(CACHED_NUMBER_TYPE, ci.numberType);
472                values.put(CACHED_NUMBER_LABEL, ci.numberLabel);
473            }
474
475            if ((ci != null) && (ci.contactIdOrZero > 0)) {
476                // Update usage information for the number associated with the contact ID.
477                // We need to use both the number and the ID for obtaining a data ID since other
478                // contacts may have the same number.
479
480                final Cursor cursor;
481
482                // We should prefer normalized one (probably coming from
483                // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
484                if (ci.normalizedNumber != null) {
485                    final String normalizedPhoneNumber = ci.normalizedNumber;
486                    cursor = resolver.query(Phone.CONTENT_URI,
487                            new String[] { Phone._ID },
488                            Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
489                            new String[] { String.valueOf(ci.contactIdOrZero),
490                                    normalizedPhoneNumber},
491                            null);
492                } else {
493                    final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
494                    cursor = resolver.query(
495                            Uri.withAppendedPath(Callable.CONTENT_FILTER_URI,
496                                    Uri.encode(phoneNumber)),
497                            new String[] { Phone._ID },
498                            Phone.CONTACT_ID + " =?",
499                            new String[] { String.valueOf(ci.contactIdOrZero) },
500                            null);
501                }
502
503                if (cursor != null) {
504                    try {
505                        if (cursor.getCount() > 0 && cursor.moveToFirst()) {
506                            final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
507                                    .appendPath(cursor.getString(0))
508                                    .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
509                                                DataUsageFeedback.USAGE_TYPE_CALL)
510                                    .build();
511                            resolver.update(feedbackUri, new ContentValues(), null, null);
512                        }
513                    } finally {
514                        cursor.close();
515                    }
516                }
517            }
518
519            Uri result = null;
520
521            if (addForAllUsers) {
522                // Insert the entry for all currently running users, in order to trigger any
523                // ContentObservers currently set on the call log.
524                final UserManager userManager = (UserManager) context.getSystemService(
525                        Context.USER_SERVICE);
526                List<UserInfo> users = userManager.getUsers(true);
527                final int currentUserId = userManager.getUserHandle();
528                final int count = users.size();
529                for (int i = 0; i < count; i++) {
530                    final UserInfo user = users.get(i);
531                    final UserHandle userHandle = user.getUserHandle();
532                    if (userManager.isUserRunning(userHandle)
533                            && !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
534                                    userHandle)
535                            && !user.isManagedProfile()) {
536                        Uri uri = addEntryAndRemoveExpiredEntries(context,
537                                ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values);
538                        if (user.id == currentUserId) {
539                            result = uri;
540                        }
541                    }
542                }
543            } else {
544                result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values);
545            }
546
547            return result;
548        }
549
550        /**
551         * Query the call log database for the last dialed number.
552         * @param context Used to get the content resolver.
553         * @return The last phone number dialed (outgoing) or an empty
554         * string if none exist yet.
555         */
556        public static String getLastOutgoingCall(Context context) {
557            final ContentResolver resolver = context.getContentResolver();
558            Cursor c = null;
559            try {
560                c = resolver.query(
561                    CONTENT_URI,
562                    new String[] {NUMBER},
563                    TYPE + " = " + OUTGOING_TYPE,
564                    null,
565                    DEFAULT_SORT_ORDER + " LIMIT 1");
566                if (c == null || !c.moveToFirst()) {
567                    return "";
568                }
569                return c.getString(0);
570            } finally {
571                if (c != null) c.close();
572            }
573        }
574
575        private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri,
576                ContentValues values) {
577            final ContentResolver resolver = context.getContentResolver();
578            Uri result = resolver.insert(uri, values);
579            resolver.delete(uri, "_id IN " +
580                    "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
581                    + " LIMIT -1 OFFSET 500)", null);
582            return result;
583        }
584    }
585}
586