CallLog.java revision edb8450b567cd7ae9f13255960b46f155865b163
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        public static final String TYPE = "type";
158
159        /** Call log type for incoming calls. */
160        public static final int INCOMING_TYPE = 1;
161        /** Call log type for outgoing calls. */
162        public static final int OUTGOING_TYPE = 2;
163        /** Call log type for missed calls. */
164        public static final int MISSED_TYPE = 3;
165        /** Call log type for voicemails. */
166        public static final int VOICEMAIL_TYPE = 4;
167
168        /**
169         * Bit-mask describing features of the call (e.g. video).
170         *
171         * <P>Type: INTEGER (int)</P>
172         */
173        public static final String FEATURES = "features";
174
175        /** Call had video. */
176        public static final int FEATURES_VIDEO = 0x1;
177
178        /**
179         * The phone number as the user entered it.
180         * <P>Type: TEXT</P>
181         */
182        public static final String NUMBER = "number";
183
184        /**
185         * The number presenting rules set by the network.
186         *
187         * <p>
188         * Allowed values:
189         * <ul>
190         * <li>{@link #PRESENTATION_ALLOWED}</li>
191         * <li>{@link #PRESENTATION_RESTRICTED}</li>
192         * <li>{@link #PRESENTATION_UNKNOWN}</li>
193         * <li>{@link #PRESENTATION_PAYPHONE}</li>
194         * </ul>
195         * </p>
196         *
197         * <P>Type: INTEGER</P>
198         */
199        public static final String NUMBER_PRESENTATION = "presentation";
200
201        /** Number is allowed to display for caller id. */
202        public static final int PRESENTATION_ALLOWED = 1;
203        /** Number is blocked by user. */
204        public static final int PRESENTATION_RESTRICTED = 2;
205        /** Number is not specified or unknown by network. */
206        public static final int PRESENTATION_UNKNOWN = 3;
207        /** Number is a pay phone. */
208        public static final int PRESENTATION_PAYPHONE = 4;
209
210        /**
211         * The ISO 3166-1 two letters country code of the country where the
212         * user received or made the call.
213         * <P>
214         * Type: TEXT
215         * </P>
216         */
217        public static final String COUNTRY_ISO = "countryiso";
218
219        /**
220         * The date the call occured, in milliseconds since the epoch
221         * <P>Type: INTEGER (long)</P>
222         */
223        public static final String DATE = "date";
224
225        /**
226         * The duration of the call in seconds
227         * <P>Type: INTEGER (long)</P>
228         */
229        public static final String DURATION = "duration";
230
231        /**
232         * The data usage of the call in bytes.
233         * <P>Type: INTEGER (long)</P>
234         */
235        public static final String DATA_USAGE = "data_usage";
236
237        /**
238         * Whether or not the call has been acknowledged
239         * <P>Type: INTEGER (boolean)</P>
240         */
241        public static final String NEW = "new";
242
243        /**
244         * The cached name associated with the 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: TEXT</P>
248         */
249        public static final String CACHED_NAME = "name";
250
251        /**
252         * The cached number type (Home, Work, etc) 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: INTEGER</P>
257         */
258        public static final String CACHED_NUMBER_TYPE = "numbertype";
259
260        /**
261         * The cached number label, for a custom number type, associated with the
262         * phone number, if it exists.
263         * This value is not guaranteed to be current, if the contact information
264         * associated with this number has changed.
265         * <P>Type: TEXT</P>
266         */
267        public static final String CACHED_NUMBER_LABEL = "numberlabel";
268
269        /**
270         * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}.
271         * <P>Type: TEXT</P>
272         */
273        public static final String VOICEMAIL_URI = "voicemail_uri";
274
275        /**
276         * Transcription of the call or voicemail entry. This will only be populated for call log
277         * entries of type {@link #VOICEMAIL_TYPE} that have valid transcriptions.
278         */
279        public static final String TRANSCRIPTION = "transcription";
280
281        /**
282         * Whether this item has been read or otherwise consumed by the user.
283         * <p>
284         * Unlike the {@link #NEW} field, which requires the user to have acknowledged the
285         * existence of the entry, this implies the user has interacted with the entry.
286         * <P>Type: INTEGER (boolean)</P>
287         */
288        public static final String IS_READ = "is_read";
289
290        /**
291         * A geocoded location for the number associated with this call.
292         * <p>
293         * The string represents a city, state, or country associated with the number.
294         * <P>Type: TEXT</P>
295         */
296        public static final String GEOCODED_LOCATION = "geocoded_location";
297
298        /**
299         * The cached URI to look up the contact associated with the phone number, if it exists.
300         * This value may not be current if the contact information associated with this number
301         * has changed.
302         * <P>Type: TEXT</P>
303         */
304        public static final String CACHED_LOOKUP_URI = "lookup_uri";
305
306        /**
307         * The cached phone number of the contact which matches this entry, if it exists.
308         * This value may not be current if the contact information associated with this number
309         * has changed.
310         * <P>Type: TEXT</P>
311         */
312        public static final String CACHED_MATCHED_NUMBER = "matched_number";
313
314        /**
315         * The cached normalized(E164) version of 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_NORMALIZED_NUMBER = "normalized_number";
321
322        /**
323         * The cached photo id of the picture associated with the phone number, if it exists.
324         * This value may not be current if the contact information associated with this number
325         * has changed.
326         * <P>Type: INTEGER (long)</P>
327         */
328        public static final String CACHED_PHOTO_ID = "photo_id";
329
330        /**
331         * The cached photo URI of the picture associated with 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 (URI)</P>
335         */
336        public static final String CACHED_PHOTO_URI = "photo_uri";
337
338        /**
339         * The cached phone number, formatted with formatting rules based on the country the
340         * user was in when the call was made or received.
341         * This value is not guaranteed to be present, and may not be current if the contact
342         * information associated with this number
343         * has changed.
344         * <P>Type: TEXT</P>
345         */
346        public static final String CACHED_FORMATTED_NUMBER = "formatted_number";
347
348        // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming
349        // that was encoded into call log databases.
350
351        /**
352         * The component name of the account used to place or receive the call; in string form.
353         * <P>Type: TEXT</P>
354         */
355        public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
356
357        /**
358         * The identifier for the account used to place or receive the call.
359         * <P>Type: TEXT</P>
360         */
361        public static final String PHONE_ACCOUNT_ID = "subscription_id";
362
363        /**
364         * The address associated with the account used to place or receive the call; in string
365         * form. For SIM-based calls, this is the user's own phone number.
366         * <P>Type: TEXT</P>
367         *
368         * @hide
369         */
370        public static final String PHONE_ACCOUNT_ADDRESS = "phone_account_address";
371
372        /**
373         * Indicates that the entry will be hidden from all queries until the associated
374         * {@link android.telecom.PhoneAccount} is registered with the system.
375         * <P>Type: INTEGER</P>
376         *
377         * @hide
378         */
379        public static final String PHONE_ACCOUNT_HIDDEN = "phone_account_hidden";
380
381        /**
382         * The subscription ID used to place this call.  This is no longer used and has been
383         * replaced with PHONE_ACCOUNT_COMPONENT_NAME/PHONE_ACCOUNT_ID.
384         * For ContactsProvider internal use only.
385         * <P>Type: INTEGER</P>
386         *
387         * @Deprecated
388         * @hide
389         */
390        public static final String SUB_ID = "sub_id";
391
392        /**
393         * If a successful call is made that is longer than this duration, update the phone number
394         * in the ContactsProvider with the normalized version of the number, based on the user's
395         * current country code.
396         */
397        private static final int MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS = 1000 * 10;
398
399        /**
400         * Adds a call to the call log.
401         *
402         * @param ci the CallerInfo object to get the target contact from.  Can be null
403         * if the contact is unknown.
404         * @param context the context used to get the ContentResolver
405         * @param number the phone number to be added to the calls db
406         * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
407         *        is set by the network and denotes the number presenting rules for
408         *        "allowed", "payphone", "restricted" or "unknown"
409         * @param callType enumerated values for "incoming", "outgoing", or "missed"
410         * @param features features of the call (e.g. Video).
411         * @param accountHandle The accountHandle object identifying the provider of the call
412         * @param start time stamp for the call in milliseconds
413         * @param duration call duration in seconds
414         * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
415         *                  the call.
416         * @result The URI of the call log entry belonging to the user that made or received this
417         *        call.
418         * {@hide}
419         */
420        public static Uri addCall(CallerInfo ci, Context context, String number,
421                int presentation, int callType, int features, PhoneAccountHandle accountHandle,
422                long start, int duration, Long dataUsage) {
423            return addCall(ci, context, number, presentation, callType, features, accountHandle,
424                    start, duration, dataUsage, false);
425        }
426
427
428        /**
429         * Adds a call to the call log.
430         *
431         * @param ci the CallerInfo object to get the target contact from.  Can be null
432         * if the contact is unknown.
433         * @param context the context used to get the ContentResolver
434         * @param number the phone number to be added to the calls db
435         * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
436         *        is set by the network and denotes the number presenting rules for
437         *        "allowed", "payphone", "restricted" or "unknown"
438         * @param callType enumerated values for "incoming", "outgoing", or "missed"
439         * @param features features of the call (e.g. Video).
440         * @param accountHandle The accountHandle object identifying the provider of the call
441         * @param start time stamp for the call in milliseconds
442         * @param duration call duration in seconds
443         * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
444         *                  the call.
445         * @param addForAllUsers If true, the call is added to the call log of all currently
446         *        running users. The caller must have the MANAGE_USERS permission if this is true.
447         *
448         * @result The URI of the call log entry belonging to the user that made or received this
449         *        call.
450         * {@hide}
451         */
452        public static Uri addCall(CallerInfo ci, Context context, String number,
453                int presentation, int callType, int features, PhoneAccountHandle accountHandle,
454                long start, int duration, Long dataUsage, boolean addForAllUsers) {
455            final ContentResolver resolver = context.getContentResolver();
456            int numberPresentation = PRESENTATION_ALLOWED;
457            boolean isHidden = false;
458
459            TelecomManager tm = null;
460            try {
461                tm = TelecomManager.from(context);
462            } catch (UnsupportedOperationException e) {}
463
464            String accountAddress = null;
465            if (tm != null && accountHandle != null) {
466                PhoneAccount account = tm.getPhoneAccount(accountHandle);
467                if (account != null) {
468                    Uri address = account.getSubscriptionAddress();
469                    if (address != null) {
470                        accountAddress = address.getSchemeSpecificPart();
471                    }
472                } else {
473                    // We could not find the account through telecom. For call log entries that
474                    // are added with a phone account which is not registered, we automatically
475                    // mark them as hidden. They are unhidden once the account is registered.
476                    Log.i(LOG_TAG, "Marking call log entry as hidden.");
477                    isHidden = true;
478                }
479            }
480
481            // Remap network specified number presentation types
482            // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
483            // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
484            // from any future radio changes.
485            // If the number field is empty set the presentation type to Unknown.
486            if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
487                numberPresentation = PRESENTATION_RESTRICTED;
488            } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
489                numberPresentation = PRESENTATION_PAYPHONE;
490            } else if (TextUtils.isEmpty(number)
491                    || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
492                numberPresentation = PRESENTATION_UNKNOWN;
493            }
494            if (numberPresentation != PRESENTATION_ALLOWED) {
495                number = "";
496                if (ci != null) {
497                    ci.name = "";
498                }
499            }
500
501            // accountHandle information
502            String accountComponentString = null;
503            String accountId = null;
504            if (accountHandle != null) {
505                accountComponentString = accountHandle.getComponentName().flattenToString();
506                accountId = accountHandle.getId();
507            }
508
509            ContentValues values = new ContentValues(6);
510
511            values.put(NUMBER, number);
512            values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
513            values.put(TYPE, Integer.valueOf(callType));
514            values.put(FEATURES, features);
515            values.put(DATE, Long.valueOf(start));
516            values.put(DURATION, Long.valueOf(duration));
517            if (dataUsage != null) {
518                values.put(DATA_USAGE, dataUsage);
519            }
520            values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
521            values.put(PHONE_ACCOUNT_ID, accountId);
522            values.put(PHONE_ACCOUNT_ADDRESS, accountAddress);
523            values.put(PHONE_ACCOUNT_HIDDEN, Integer.valueOf(isHidden ? 1 : 0));
524            values.put(NEW, Integer.valueOf(1));
525
526            if (callType == MISSED_TYPE) {
527                values.put(IS_READ, Integer.valueOf(0));
528            }
529            if (ci != null) {
530                values.put(CACHED_NAME, ci.name);
531                values.put(CACHED_NUMBER_TYPE, ci.numberType);
532                values.put(CACHED_NUMBER_LABEL, ci.numberLabel);
533            }
534
535            if ((ci != null) && (ci.contactIdOrZero > 0)) {
536                // Update usage information for the number associated with the contact ID.
537                // We need to use both the number and the ID for obtaining a data ID since other
538                // contacts may have the same number.
539
540                final Cursor cursor;
541
542                // We should prefer normalized one (probably coming from
543                // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
544                if (ci.normalizedNumber != null) {
545                    final String normalizedPhoneNumber = ci.normalizedNumber;
546                    cursor = resolver.query(Phone.CONTENT_URI,
547                            new String[] { Phone._ID },
548                            Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
549                            new String[] { String.valueOf(ci.contactIdOrZero),
550                                    normalizedPhoneNumber},
551                            null);
552                } else {
553                    final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
554                    cursor = resolver.query(
555                            Uri.withAppendedPath(Callable.CONTENT_FILTER_URI,
556                                    Uri.encode(phoneNumber)),
557                            new String[] { Phone._ID },
558                            Phone.CONTACT_ID + " =?",
559                            new String[] { String.valueOf(ci.contactIdOrZero) },
560                            null);
561                }
562
563                if (cursor != null) {
564                    try {
565                        if (cursor.getCount() > 0 && cursor.moveToFirst()) {
566                            final String dataId = cursor.getString(0);
567                            updateDataUsageStatForData(resolver, dataId);
568                            if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS
569                                    && callType == Calls.OUTGOING_TYPE
570                                    && TextUtils.isEmpty(ci.normalizedNumber)) {
571                                updateNormalizedNumber(context, resolver, dataId, number);
572                            }
573                        }
574                    } finally {
575                        cursor.close();
576                    }
577                }
578            }
579
580            Uri result = null;
581
582            if (addForAllUsers) {
583                // Insert the entry for all currently running users, in order to trigger any
584                // ContentObservers currently set on the call log.
585                final UserManager userManager = (UserManager) context.getSystemService(
586                        Context.USER_SERVICE);
587                List<UserInfo> users = userManager.getUsers(true);
588                final int currentUserId = userManager.getUserHandle();
589                final int count = users.size();
590                for (int i = 0; i < count; i++) {
591                    final UserInfo user = users.get(i);
592                    final UserHandle userHandle = user.getUserHandle();
593                    if (userManager.isUserRunning(userHandle)
594                            && !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
595                                    userHandle)
596                            && !user.isManagedProfile()) {
597                        Uri uri = addEntryAndRemoveExpiredEntries(context,
598                                ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values);
599                        if (user.id == currentUserId) {
600                            result = uri;
601                        }
602                    }
603                }
604            } else {
605                result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values);
606            }
607
608            return result;
609        }
610
611        /**
612         * Query the call log database for the last dialed number.
613         * @param context Used to get the content resolver.
614         * @return The last phone number dialed (outgoing) or an empty
615         * string if none exist yet.
616         */
617        public static String getLastOutgoingCall(Context context) {
618            final ContentResolver resolver = context.getContentResolver();
619            Cursor c = null;
620            try {
621                c = resolver.query(
622                    CONTENT_URI,
623                    new String[] {NUMBER},
624                    TYPE + " = " + OUTGOING_TYPE,
625                    null,
626                    DEFAULT_SORT_ORDER + " LIMIT 1");
627                if (c == null || !c.moveToFirst()) {
628                    return "";
629                }
630                return c.getString(0);
631            } finally {
632                if (c != null) c.close();
633            }
634        }
635
636        private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri,
637                ContentValues values) {
638            final ContentResolver resolver = context.getContentResolver();
639            Uri result = resolver.insert(uri, values);
640            resolver.delete(uri, "_id IN " +
641                    "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
642                    + " LIMIT -1 OFFSET 500)", null);
643            return result;
644        }
645
646        private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) {
647            final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
648                    .appendPath(dataId)
649                    .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
650                                DataUsageFeedback.USAGE_TYPE_CALL)
651                    .build();
652            resolver.update(feedbackUri, new ContentValues(), null, null);
653        }
654
655        /*
656         * Update the normalized phone number for the given dataId in the ContactsProvider, based
657         * on the user's current country.
658         */
659        private static void updateNormalizedNumber(Context context, ContentResolver resolver,
660                String dataId, String number) {
661            if (TextUtils.isEmpty(number) || TextUtils.isEmpty(dataId)) {
662                return;
663            }
664            final String countryIso = getCurrentCountryIso(context);
665            if (TextUtils.isEmpty(countryIso)) {
666                return;
667            }
668            final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number,
669                    getCurrentCountryIso(context));
670            if (TextUtils.isEmpty(normalizedNumber)) {
671                return;
672            }
673            final ContentValues values = new ContentValues();
674            values.put(Phone.NORMALIZED_NUMBER, normalizedNumber);
675            resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId});
676        }
677
678        private static String getCurrentCountryIso(Context context) {
679            String countryIso = null;
680            final CountryDetector detector = (CountryDetector) context.getSystemService(
681                    Context.COUNTRY_DETECTOR);
682            if (detector != null) {
683                final Country country = detector.detectCountry();
684                if (country != null) {
685                    countryIso = country.getCountryIso();
686                }
687            }
688            return countryIso;
689        }
690    }
691}
692