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