CallLog.java revision 457df5ff7e5d562b944b9f7544a02c956780f81a
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 458 TelecomManager tm = null; 459 try { 460 tm = TelecomManager.from(context); 461 } catch (UnsupportedOperationException e) {} 462 463 String accountAddress = null; 464 if (tm != null && accountHandle != null) { 465 PhoneAccount account = tm.getPhoneAccount(accountHandle); 466 if (account != null) { 467 Uri address = account.getSubscriptionAddress(); 468 if (address != null) { 469 accountAddress = address.getSchemeSpecificPart(); 470 } 471 } 472 } 473 474 // Remap network specified number presentation types 475 // PhoneConstants.PRESENTATION_xxx to calllog number presentation types 476 // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog 477 // from any future radio changes. 478 // If the number field is empty set the presentation type to Unknown. 479 if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { 480 numberPresentation = PRESENTATION_RESTRICTED; 481 } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { 482 numberPresentation = PRESENTATION_PAYPHONE; 483 } else if (TextUtils.isEmpty(number) 484 || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { 485 numberPresentation = PRESENTATION_UNKNOWN; 486 } 487 if (numberPresentation != PRESENTATION_ALLOWED) { 488 number = ""; 489 if (ci != null) { 490 ci.name = ""; 491 } 492 } 493 494 // accountHandle information 495 String accountComponentString = null; 496 String accountId = null; 497 if (accountHandle != null) { 498 accountComponentString = accountHandle.getComponentName().flattenToString(); 499 accountId = accountHandle.getId(); 500 } 501 502 ContentValues values = new ContentValues(6); 503 504 values.put(NUMBER, number); 505 values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation)); 506 values.put(TYPE, Integer.valueOf(callType)); 507 values.put(FEATURES, features); 508 values.put(DATE, Long.valueOf(start)); 509 values.put(DURATION, Long.valueOf(duration)); 510 if (dataUsage != null) { 511 values.put(DATA_USAGE, dataUsage); 512 } 513 values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString); 514 values.put(PHONE_ACCOUNT_ID, accountId); 515 values.put(PHONE_ACCOUNT_ADDRESS, accountAddress); 516 values.put(NEW, Integer.valueOf(1)); 517 518 if (callType == MISSED_TYPE) { 519 values.put(IS_READ, Integer.valueOf(0)); 520 } 521 522 if ((ci != null) && (ci.contactIdOrZero > 0)) { 523 // Update usage information for the number associated with the contact ID. 524 // We need to use both the number and the ID for obtaining a data ID since other 525 // contacts may have the same number. 526 527 final Cursor cursor; 528 529 // We should prefer normalized one (probably coming from 530 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others. 531 if (ci.normalizedNumber != null) { 532 final String normalizedPhoneNumber = ci.normalizedNumber; 533 cursor = resolver.query(Phone.CONTENT_URI, 534 new String[] { Phone._ID }, 535 Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?", 536 new String[] { String.valueOf(ci.contactIdOrZero), 537 normalizedPhoneNumber}, 538 null); 539 } else { 540 final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number; 541 cursor = resolver.query( 542 Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, 543 Uri.encode(phoneNumber)), 544 new String[] { Phone._ID }, 545 Phone.CONTACT_ID + " =?", 546 new String[] { String.valueOf(ci.contactIdOrZero) }, 547 null); 548 } 549 550 if (cursor != null) { 551 try { 552 if (cursor.getCount() > 0 && cursor.moveToFirst()) { 553 final String dataId = cursor.getString(0); 554 updateDataUsageStatForData(resolver, dataId); 555 if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS 556 && callType == Calls.OUTGOING_TYPE 557 && TextUtils.isEmpty(ci.normalizedNumber)) { 558 updateNormalizedNumber(context, resolver, dataId, number); 559 } 560 } 561 } finally { 562 cursor.close(); 563 } 564 } 565 } 566 567 Uri result = null; 568 569 if (addForAllUsers) { 570 // Insert the entry for all currently running users, in order to trigger any 571 // ContentObservers currently set on the call log. 572 final UserManager userManager = (UserManager) context.getSystemService( 573 Context.USER_SERVICE); 574 List<UserInfo> users = userManager.getUsers(true); 575 final int currentUserId = userManager.getUserHandle(); 576 final int count = users.size(); 577 for (int i = 0; i < count; i++) { 578 final UserInfo user = users.get(i); 579 final UserHandle userHandle = user.getUserHandle(); 580 if (userManager.isUserRunning(userHandle) 581 && !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, 582 userHandle) 583 && !user.isManagedProfile()) { 584 Uri uri = addEntryAndRemoveExpiredEntries(context, 585 ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values); 586 if (user.id == currentUserId) { 587 result = uri; 588 } 589 } 590 } 591 } else { 592 result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values); 593 } 594 595 return result; 596 } 597 598 /** 599 * Query the call log database for the last dialed number. 600 * @param context Used to get the content resolver. 601 * @return The last phone number dialed (outgoing) or an empty 602 * string if none exist yet. 603 */ 604 public static String getLastOutgoingCall(Context context) { 605 final ContentResolver resolver = context.getContentResolver(); 606 Cursor c = null; 607 try { 608 c = resolver.query( 609 CONTENT_URI, 610 new String[] {NUMBER}, 611 TYPE + " = " + OUTGOING_TYPE, 612 null, 613 DEFAULT_SORT_ORDER + " LIMIT 1"); 614 if (c == null || !c.moveToFirst()) { 615 return ""; 616 } 617 return c.getString(0); 618 } finally { 619 if (c != null) c.close(); 620 } 621 } 622 623 private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri, 624 ContentValues values) { 625 final ContentResolver resolver = context.getContentResolver(); 626 Uri result = resolver.insert(uri, values); 627 resolver.delete(uri, "_id IN " + 628 "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER 629 + " LIMIT -1 OFFSET 500)", null); 630 return result; 631 } 632 633 private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) { 634 final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() 635 .appendPath(dataId) 636 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 637 DataUsageFeedback.USAGE_TYPE_CALL) 638 .build(); 639 resolver.update(feedbackUri, new ContentValues(), null, null); 640 } 641 642 /* 643 * Update the normalized phone number for the given dataId in the ContactsProvider, based 644 * on the user's current country. 645 */ 646 private static void updateNormalizedNumber(Context context, ContentResolver resolver, 647 String dataId, String number) { 648 if (TextUtils.isEmpty(number) || TextUtils.isEmpty(dataId)) { 649 return; 650 } 651 final String countryIso = getCurrentCountryIso(context); 652 if (TextUtils.isEmpty(countryIso)) { 653 return; 654 } 655 final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, 656 getCurrentCountryIso(context)); 657 if (TextUtils.isEmpty(normalizedNumber)) { 658 return; 659 } 660 final ContentValues values = new ContentValues(); 661 values.put(Phone.NORMALIZED_NUMBER, normalizedNumber); 662 resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId}); 663 } 664 665 private static String getCurrentCountryIso(Context context) { 666 String countryIso = null; 667 final CountryDetector detector = (CountryDetector) context.getSystemService( 668 Context.COUNTRY_DETECTOR); 669 if (detector != null) { 670 final Country country = detector.detectCountry(); 671 if (country != null) { 672 countryIso = country.getCountryIso(); 673 } 674 } 675 return countryIso; 676 } 677 } 678} 679