CallLog.java revision 3b444db1d6cbc539a3c4ed653e4e675eb42d63bf
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 * Indicates that the entry will be hidden from all queries until the associated 365 * {@link android.telecom.PhoneAccount} is registered with the system. 366 * <P>Type: INTEGER</P> 367 * 368 * @Deprecated 369 * @hide 370 */ 371 public static final String PHONE_ACCOUNT_HIDDEN = "phone_account_hidden"; 372 373 /** 374 * The address associated with the account used to place or receive the call; in string 375 * form. For SIM-based calls, this is the user's own phone number. 376 * <P>Type: TEXT</P> 377 * 378 * @hide 379 */ 380 public static final String PHONE_ACCOUNT_ADDRESS = "phone_account_address"; 381 382 /** 383 * The subscription ID used to place this call. This is no longer used and has been 384 * replaced with PHONE_ACCOUNT_COMPONENT_NAME/PHONE_ACCOUNT_ID. 385 * For ContactsProvider internal use only. 386 * <P>Type: INTEGER</P> 387 * 388 * @Deprecated 389 * @hide 390 */ 391 public static final String SUB_ID = "sub_id"; 392 393 /** 394 * If a successful call is made that is longer than this duration, update the phone number 395 * in the ContactsProvider with the normalized version of the number, based on the user's 396 * current country code. 397 */ 398 private static final int MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS = 1000 * 10; 399 400 /** 401 * Adds a call to the call log. 402 * 403 * @param ci the CallerInfo object to get the target contact from. Can be null 404 * if the contact is unknown. 405 * @param context the context used to get the ContentResolver 406 * @param number the phone number to be added to the calls db 407 * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which 408 * is set by the network and denotes the number presenting rules for 409 * "allowed", "payphone", "restricted" or "unknown" 410 * @param callType enumerated values for "incoming", "outgoing", or "missed" 411 * @param features features of the call (e.g. Video). 412 * @param accountHandle The accountHandle object identifying the provider of the call 413 * @param start time stamp for the call in milliseconds 414 * @param duration call duration in seconds 415 * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for 416 * the call. 417 * @result The URI of the call log entry belonging to the user that made or received this 418 * call. 419 * {@hide} 420 */ 421 public static Uri addCall(CallerInfo ci, Context context, String number, 422 int presentation, int callType, int features, PhoneAccountHandle accountHandle, 423 long start, int duration, Long dataUsage) { 424 return addCall(ci, context, number, presentation, callType, features, accountHandle, 425 start, duration, dataUsage, false); 426 } 427 428 429 /** 430 * Adds a call to the call log. 431 * 432 * @param ci the CallerInfo object to get the target contact from. Can be null 433 * if the contact is unknown. 434 * @param context the context used to get the ContentResolver 435 * @param number the phone number to be added to the calls db 436 * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which 437 * is set by the network and denotes the number presenting rules for 438 * "allowed", "payphone", "restricted" or "unknown" 439 * @param callType enumerated values for "incoming", "outgoing", or "missed" 440 * @param features features of the call (e.g. Video). 441 * @param accountHandle The accountHandle object identifying the provider of the call 442 * @param start time stamp for the call in milliseconds 443 * @param duration call duration in seconds 444 * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for 445 * the call. 446 * @param addForAllUsers If true, the call is added to the call log of all currently 447 * running users. The caller must have the MANAGE_USERS permission if this is true. 448 * 449 * @result The URI of the call log entry belonging to the user that made or received this 450 * call. 451 * {@hide} 452 */ 453 public static Uri addCall(CallerInfo ci, Context context, String number, 454 int presentation, int callType, int features, PhoneAccountHandle accountHandle, 455 long start, int duration, Long dataUsage, boolean addForAllUsers) { 456 final ContentResolver resolver = context.getContentResolver(); 457 int numberPresentation = PRESENTATION_ALLOWED; 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 accountAddress = account.getSubscriptionAddress().getSchemeSpecificPart(); 469 } 470 } 471 472 // Remap network specified number presentation types 473 // PhoneConstants.PRESENTATION_xxx to calllog number presentation types 474 // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog 475 // from any future radio changes. 476 // If the number field is empty set the presentation type to Unknown. 477 if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { 478 numberPresentation = PRESENTATION_RESTRICTED; 479 } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { 480 numberPresentation = PRESENTATION_PAYPHONE; 481 } else if (TextUtils.isEmpty(number) 482 || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { 483 numberPresentation = PRESENTATION_UNKNOWN; 484 } 485 if (numberPresentation != PRESENTATION_ALLOWED) { 486 number = ""; 487 if (ci != null) { 488 ci.name = ""; 489 } 490 } 491 492 // accountHandle information 493 String accountComponentString = null; 494 String accountId = null; 495 if (accountHandle != null) { 496 accountComponentString = accountHandle.getComponentName().flattenToString(); 497 accountId = accountHandle.getId(); 498 } 499 500 ContentValues values = new ContentValues(6); 501 502 values.put(NUMBER, number); 503 values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation)); 504 values.put(TYPE, Integer.valueOf(callType)); 505 values.put(FEATURES, features); 506 values.put(DATE, Long.valueOf(start)); 507 values.put(DURATION, Long.valueOf(duration)); 508 if (dataUsage != null) { 509 values.put(DATA_USAGE, dataUsage); 510 } 511 values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString); 512 values.put(PHONE_ACCOUNT_ID, accountId); 513 values.put(PHONE_ACCOUNT_ADDRESS, accountAddress); 514 values.put(NEW, Integer.valueOf(1)); 515 516 if (callType == MISSED_TYPE) { 517 values.put(IS_READ, Integer.valueOf(0)); 518 } 519 if (ci != null) { 520 values.put(CACHED_NAME, ci.name); 521 values.put(CACHED_NUMBER_TYPE, ci.numberType); 522 values.put(CACHED_NUMBER_LABEL, ci.numberLabel); 523 } 524 525 if ((ci != null) && (ci.contactIdOrZero > 0)) { 526 // Update usage information for the number associated with the contact ID. 527 // We need to use both the number and the ID for obtaining a data ID since other 528 // contacts may have the same number. 529 530 final Cursor cursor; 531 532 // We should prefer normalized one (probably coming from 533 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others. 534 if (ci.normalizedNumber != null) { 535 final String normalizedPhoneNumber = ci.normalizedNumber; 536 cursor = resolver.query(Phone.CONTENT_URI, 537 new String[] { Phone._ID }, 538 Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?", 539 new String[] { String.valueOf(ci.contactIdOrZero), 540 normalizedPhoneNumber}, 541 null); 542 } else { 543 final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number; 544 cursor = resolver.query( 545 Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, 546 Uri.encode(phoneNumber)), 547 new String[] { Phone._ID }, 548 Phone.CONTACT_ID + " =?", 549 new String[] { String.valueOf(ci.contactIdOrZero) }, 550 null); 551 } 552 553 if (cursor != null) { 554 try { 555 if (cursor.getCount() > 0 && cursor.moveToFirst()) { 556 final String dataId = cursor.getString(0); 557 updateDataUsageStatForData(resolver, dataId); 558 if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS 559 && callType == Calls.OUTGOING_TYPE 560 && TextUtils.isEmpty(ci.normalizedNumber)) { 561 updateNormalizedNumber(context, resolver, dataId, number); 562 } 563 } 564 } finally { 565 cursor.close(); 566 } 567 } 568 } 569 570 Uri result = null; 571 572 if (addForAllUsers) { 573 // Insert the entry for all currently running users, in order to trigger any 574 // ContentObservers currently set on the call log. 575 final UserManager userManager = (UserManager) context.getSystemService( 576 Context.USER_SERVICE); 577 List<UserInfo> users = userManager.getUsers(true); 578 final int currentUserId = userManager.getUserHandle(); 579 final int count = users.size(); 580 for (int i = 0; i < count; i++) { 581 final UserInfo user = users.get(i); 582 final UserHandle userHandle = user.getUserHandle(); 583 if (userManager.isUserRunning(userHandle) 584 && !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, 585 userHandle) 586 && !user.isManagedProfile()) { 587 Uri uri = addEntryAndRemoveExpiredEntries(context, 588 ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values); 589 if (user.id == currentUserId) { 590 result = uri; 591 } 592 } 593 } 594 } else { 595 result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values); 596 } 597 598 return result; 599 } 600 601 /** 602 * Query the call log database for the last dialed number. 603 * @param context Used to get the content resolver. 604 * @return The last phone number dialed (outgoing) or an empty 605 * string if none exist yet. 606 */ 607 public static String getLastOutgoingCall(Context context) { 608 final ContentResolver resolver = context.getContentResolver(); 609 Cursor c = null; 610 try { 611 c = resolver.query( 612 CONTENT_URI, 613 new String[] {NUMBER}, 614 TYPE + " = " + OUTGOING_TYPE, 615 null, 616 DEFAULT_SORT_ORDER + " LIMIT 1"); 617 if (c == null || !c.moveToFirst()) { 618 return ""; 619 } 620 return c.getString(0); 621 } finally { 622 if (c != null) c.close(); 623 } 624 } 625 626 private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri, 627 ContentValues values) { 628 final ContentResolver resolver = context.getContentResolver(); 629 Uri result = resolver.insert(uri, values); 630 resolver.delete(uri, "_id IN " + 631 "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER 632 + " LIMIT -1 OFFSET 500)", null); 633 return result; 634 } 635 636 private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) { 637 final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() 638 .appendPath(dataId) 639 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 640 DataUsageFeedback.USAGE_TYPE_CALL) 641 .build(); 642 resolver.update(feedbackUri, new ContentValues(), null, null); 643 } 644 645 /* 646 * Update the normalized phone number for the given dataId in the ContactsProvider, based 647 * on the user's current country. 648 */ 649 private static void updateNormalizedNumber(Context context, ContentResolver resolver, 650 String dataId, String number) { 651 if (TextUtils.isEmpty(number) || TextUtils.isEmpty(dataId)) { 652 return; 653 } 654 final String countryIso = getCurrentCountryIso(context); 655 if (TextUtils.isEmpty(countryIso)) { 656 return; 657 } 658 final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, 659 getCurrentCountryIso(context)); 660 if (TextUtils.isEmpty(normalizedNumber)) { 661 return; 662 } 663 final ContentValues values = new ContentValues(); 664 values.put(Phone.NORMALIZED_NUMBER, normalizedNumber); 665 resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId}); 666 } 667 668 private static String getCurrentCountryIso(Context context) { 669 String countryIso = null; 670 final CountryDetector detector = (CountryDetector) context.getSystemService( 671 Context.COUNTRY_DETECTOR); 672 if (detector != null) { 673 final Country country = detector.detectCountry(); 674 if (country != null) { 675 countryIso = country.getCountryIso(); 676 } 677 } 678 return countryIso; 679 } 680 } 681} 682