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