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