CallLog.java revision c66f3baa42b8a732952abf1967c68f77d3e26131
185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian/* 285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * Copyright (C) 2006 The Android Open Source Project 385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * 485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * Licensed under the Apache License, Version 2.0 (the "License"); 585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * you may not use this file except in compliance with the License. 685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * You may obtain a copy of the License at 785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * 885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * http://www.apache.org/licenses/LICENSE-2.0 985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * 1085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * Unless required by applicable law or agreed to in writing, software 1185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * distributed under the License is distributed on an "AS IS" BASIS, 1285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * See the License for the specific language governing permissions and 1485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * limitations under the License. 1585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian */ 1685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 1785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 1885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianpackage android.provider; 1985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 2085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.ContentProvider; 2185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.ContentResolver; 2285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.ContentValues; 2385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.Context; 2485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.Intent; 2585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.pm.UserInfo; 2685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.database.Cursor; 2785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.location.Country; 2885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.location.CountryDetector; 2985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.net.Uri; 3085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.os.UserHandle; 3185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.os.UserManager; 3285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.provider.ContactsContract.CommonDataKinds.Callable; 336547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennisimport android.provider.ContactsContract.CommonDataKinds.Phone; 346547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennisimport android.provider.ContactsContract.Data; 3585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.provider.ContactsContract.DataUsageFeedback; 3685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.telecom.PhoneAccount; 3785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.telecom.PhoneAccountHandle; 3885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.telecom.TelecomManager; 3985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.telephony.PhoneNumberUtils; 4085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.text.TextUtils; 4185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.util.Log; 4285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 4385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport com.android.internal.telephony.CallerInfo; 4485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport com.android.internal.telephony.PhoneConstants; 4585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 4685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport java.util.List; 4785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 4885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian/** 4985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * The CallLog provider contains information about placed and received calls. 5085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian */ 5185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianpublic class CallLog { 5285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian private static final String LOG_TAG = "CallLog"; 5385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 5485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian public static final String AUTHORITY = "call_log"; 5585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 5685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian /** 5785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * The content:// style URL for this provider 5885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian */ 5985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian public static final Uri CONTENT_URI = 6085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian Uri.parse("content://" + AUTHORITY); 6185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 6285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian /** 6385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * Contains the recent calls. 6485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian */ 6585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian public static class Calls implements BaseColumns { 6685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian /** 6785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * The content:// style URL for this table 6885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian */ 6985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian public static final Uri CONTENT_URI = 7085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian Uri.parse("content://call_log/calls"); 7185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 7285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian /** 7385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * The content:// style URL for filtering this table on phone numbers 7485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian */ 7585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian public static final Uri CONTENT_FILTER_URI = 766547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennis Uri.parse("content://call_log/calls/filter"); 776547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennis 786547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennis /** 7985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * Query parameter used to limit the number of call logs returned. 8085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * <p> 8185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * TYPE: integer 8285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian */ 8385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian public static final String LIMIT_PARAM_KEY = "limit"; 8485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian 8585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian /** 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 phone number, formatted with formatting rules based on the country the 332 * user was in when the call was made or received. 333 * This value is not guaranteed to be present, and may not be current if the contact 334 * information associated with this number 335 * has changed. 336 * <P>Type: TEXT</P> 337 */ 338 public static final String CACHED_FORMATTED_NUMBER = "formatted_number"; 339 340 // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming 341 // that was encoded into call log databases. 342 343 /** 344 * The component name of the account used to place or receive the call; in string form. 345 * <P>Type: TEXT</P> 346 */ 347 public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name"; 348 349 /** 350 * The identifier for the account used to place or receive the call. 351 * <P>Type: TEXT</P> 352 */ 353 public static final String PHONE_ACCOUNT_ID = "subscription_id"; 354 355 /** 356 * The address associated with the account used to place or receive the call; in string 357 * form. For SIM-based calls, this is the user's own phone number. 358 * <P>Type: TEXT</P> 359 * 360 * @hide 361 */ 362 public static final String PHONE_ACCOUNT_ADDRESS = "phone_account_address"; 363 364 /** 365 * Indicates that the entry will be hidden from all queries until the associated 366 * {@link android.telecom.PhoneAccount} is registered with the system. 367 * <P>Type: INTEGER</P> 368 * 369 * @hide 370 */ 371 public static final String PHONE_ACCOUNT_HIDDEN = "phone_account_hidden"; 372 373 /** 374 * The subscription ID used to place this call. This is no longer used and has been 375 * replaced with PHONE_ACCOUNT_COMPONENT_NAME/PHONE_ACCOUNT_ID. 376 * For ContactsProvider internal use only. 377 * <P>Type: INTEGER</P> 378 * 379 * @Deprecated 380 * @hide 381 */ 382 public static final String SUB_ID = "sub_id"; 383 384 /** 385 * If a successful call is made that is longer than this duration, update the phone number 386 * in the ContactsProvider with the normalized version of the number, based on the user's 387 * current country code. 388 */ 389 private static final int MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS = 1000 * 10; 390 391 /** 392 * Adds a call to the call log. 393 * 394 * @param ci the CallerInfo object to get the target contact from. Can be null 395 * if the contact is unknown. 396 * @param context the context used to get the ContentResolver 397 * @param number the phone number to be added to the calls db 398 * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which 399 * is set by the network and denotes the number presenting rules for 400 * "allowed", "payphone", "restricted" or "unknown" 401 * @param callType enumerated values for "incoming", "outgoing", or "missed" 402 * @param features features of the call (e.g. Video). 403 * @param accountHandle The accountHandle object identifying the provider of the call 404 * @param start time stamp for the call in milliseconds 405 * @param duration call duration in seconds 406 * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for 407 * the call. 408 * @result The URI of the call log entry belonging to the user that made or received this 409 * call. 410 * {@hide} 411 */ 412 public static Uri addCall(CallerInfo ci, Context context, String number, 413 int presentation, int callType, int features, PhoneAccountHandle accountHandle, 414 long start, int duration, Long dataUsage) { 415 return addCall(ci, context, number, presentation, callType, features, accountHandle, 416 start, duration, dataUsage, false); 417 } 418 419 420 /** 421 * Adds a call to the call log. 422 * 423 * @param ci the CallerInfo object to get the target contact from. Can be null 424 * if the contact is unknown. 425 * @param context the context used to get the ContentResolver 426 * @param number the phone number to be added to the calls db 427 * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which 428 * is set by the network and denotes the number presenting rules for 429 * "allowed", "payphone", "restricted" or "unknown" 430 * @param callType enumerated values for "incoming", "outgoing", or "missed" 431 * @param features features of the call (e.g. Video). 432 * @param accountHandle The accountHandle object identifying the provider of the call 433 * @param start time stamp for the call in milliseconds 434 * @param duration call duration in seconds 435 * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for 436 * the call. 437 * @param addForAllUsers If true, the call is added to the call log of all currently 438 * running users. The caller must have the MANAGE_USERS permission if this is true. 439 * 440 * @result The URI of the call log entry belonging to the user that made or received this 441 * call. 442 * {@hide} 443 */ 444 public static Uri addCall(CallerInfo ci, Context context, String number, 445 int presentation, int callType, int features, PhoneAccountHandle accountHandle, 446 long start, int duration, Long dataUsage, boolean addForAllUsers) { 447 final ContentResolver resolver = context.getContentResolver(); 448 int numberPresentation = PRESENTATION_ALLOWED; 449 boolean isHidden = false; 450 451 TelecomManager tm = null; 452 try { 453 tm = TelecomManager.from(context); 454 } catch (UnsupportedOperationException e) {} 455 456 String accountAddress = null; 457 if (tm != null && accountHandle != null) { 458 PhoneAccount account = tm.getPhoneAccount(accountHandle); 459 if (account != null) { 460 Uri address = account.getSubscriptionAddress(); 461 if (address != null) { 462 accountAddress = address.getSchemeSpecificPart(); 463 } 464 } else { 465 // We could not find the account through telecom. For call log entries that 466 // are added with a phone account which is not registered, we automatically 467 // mark them as hidden. They are unhidden once the account is registered. 468 Log.i(LOG_TAG, "Marking call log entry as hidden."); 469 isHidden = true; 470 } 471 } 472 473 // Remap network specified number presentation types 474 // PhoneConstants.PRESENTATION_xxx to calllog number presentation types 475 // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog 476 // from any future radio changes. 477 // If the number field is empty set the presentation type to Unknown. 478 if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { 479 numberPresentation = PRESENTATION_RESTRICTED; 480 } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { 481 numberPresentation = PRESENTATION_PAYPHONE; 482 } else if (TextUtils.isEmpty(number) 483 || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { 484 numberPresentation = PRESENTATION_UNKNOWN; 485 } 486 if (numberPresentation != PRESENTATION_ALLOWED) { 487 number = ""; 488 if (ci != null) { 489 ci.name = ""; 490 } 491 } 492 493 // accountHandle information 494 String accountComponentString = null; 495 String accountId = null; 496 if (accountHandle != null) { 497 accountComponentString = accountHandle.getComponentName().flattenToString(); 498 accountId = accountHandle.getId(); 499 } 500 501 ContentValues values = new ContentValues(6); 502 503 values.put(NUMBER, number); 504 values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation)); 505 values.put(TYPE, Integer.valueOf(callType)); 506 values.put(FEATURES, features); 507 values.put(DATE, Long.valueOf(start)); 508 values.put(DURATION, Long.valueOf(duration)); 509 if (dataUsage != null) { 510 values.put(DATA_USAGE, dataUsage); 511 } 512 values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString); 513 values.put(PHONE_ACCOUNT_ID, accountId); 514 values.put(PHONE_ACCOUNT_ADDRESS, accountAddress); 515 values.put(PHONE_ACCOUNT_HIDDEN, Integer.valueOf(isHidden ? 1 : 0)); 516 values.put(NEW, Integer.valueOf(1)); 517 518 if (callType == MISSED_TYPE) { 519 values.put(IS_READ, Integer.valueOf(0)); 520 } 521 if (ci != null) { 522 values.put(CACHED_NAME, ci.name); 523 values.put(CACHED_NUMBER_TYPE, ci.numberType); 524 values.put(CACHED_NUMBER_LABEL, ci.numberLabel); 525 } 526 527 if ((ci != null) && (ci.contactIdOrZero > 0)) { 528 // Update usage information for the number associated with the contact ID. 529 // We need to use both the number and the ID for obtaining a data ID since other 530 // contacts may have the same number. 531 532 final Cursor cursor; 533 534 // We should prefer normalized one (probably coming from 535 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others. 536 if (ci.normalizedNumber != null) { 537 final String normalizedPhoneNumber = ci.normalizedNumber; 538 cursor = resolver.query(Phone.CONTENT_URI, 539 new String[] { Phone._ID }, 540 Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?", 541 new String[] { String.valueOf(ci.contactIdOrZero), 542 normalizedPhoneNumber}, 543 null); 544 } else { 545 final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number; 546 cursor = resolver.query( 547 Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, 548 Uri.encode(phoneNumber)), 549 new String[] { Phone._ID }, 550 Phone.CONTACT_ID + " =?", 551 new String[] { String.valueOf(ci.contactIdOrZero) }, 552 null); 553 } 554 555 if (cursor != null) { 556 try { 557 if (cursor.getCount() > 0 && cursor.moveToFirst()) { 558 final String dataId = cursor.getString(0); 559 updateDataUsageStatForData(resolver, dataId); 560 if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS 561 && callType == Calls.OUTGOING_TYPE 562 && TextUtils.isEmpty(ci.normalizedNumber)) { 563 updateNormalizedNumber(context, resolver, dataId, number); 564 } 565 } 566 } finally { 567 cursor.close(); 568 } 569 } 570 } 571 572 Uri result = null; 573 574 if (addForAllUsers) { 575 // Insert the entry for all currently running users, in order to trigger any 576 // ContentObservers currently set on the call log. 577 final UserManager userManager = (UserManager) context.getSystemService( 578 Context.USER_SERVICE); 579 List<UserInfo> users = userManager.getUsers(true); 580 final int currentUserId = userManager.getUserHandle(); 581 final int count = users.size(); 582 for (int i = 0; i < count; i++) { 583 final UserInfo user = users.get(i); 584 final UserHandle userHandle = user.getUserHandle(); 585 if (userManager.isUserRunning(userHandle) 586 && !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, 587 userHandle) 588 && !user.isManagedProfile()) { 589 Uri uri = addEntryAndRemoveExpiredEntries(context, 590 ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values); 591 if (user.id == currentUserId) { 592 result = uri; 593 } 594 } 595 } 596 } else { 597 result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values); 598 } 599 600 return result; 601 } 602 603 /** 604 * Query the call log database for the last dialed number. 605 * @param context Used to get the content resolver. 606 * @return The last phone number dialed (outgoing) or an empty 607 * string if none exist yet. 608 */ 609 public static String getLastOutgoingCall(Context context) { 610 final ContentResolver resolver = context.getContentResolver(); 611 Cursor c = null; 612 try { 613 c = resolver.query( 614 CONTENT_URI, 615 new String[] {NUMBER}, 616 TYPE + " = " + OUTGOING_TYPE, 617 null, 618 DEFAULT_SORT_ORDER + " LIMIT 1"); 619 if (c == null || !c.moveToFirst()) { 620 return ""; 621 } 622 return c.getString(0); 623 } finally { 624 if (c != null) c.close(); 625 } 626 } 627 628 private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri, 629 ContentValues values) { 630 final ContentResolver resolver = context.getContentResolver(); 631 Uri result = resolver.insert(uri, values); 632 resolver.delete(uri, "_id IN " + 633 "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER 634 + " LIMIT -1 OFFSET 500)", null); 635 return result; 636 } 637 638 private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) { 639 final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() 640 .appendPath(dataId) 641 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 642 DataUsageFeedback.USAGE_TYPE_CALL) 643 .build(); 644 resolver.update(feedbackUri, new ContentValues(), null, null); 645 } 646 647 /* 648 * Update the normalized phone number for the given dataId in the ContactsProvider, based 649 * on the user's current country. 650 */ 651 private static void updateNormalizedNumber(Context context, ContentResolver resolver, 652 String dataId, String number) { 653 if (TextUtils.isEmpty(number) || TextUtils.isEmpty(dataId)) { 654 return; 655 } 656 final String countryIso = getCurrentCountryIso(context); 657 if (TextUtils.isEmpty(countryIso)) { 658 return; 659 } 660 final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, 661 getCurrentCountryIso(context)); 662 if (TextUtils.isEmpty(normalizedNumber)) { 663 return; 664 } 665 final ContentValues values = new ContentValues(); 666 values.put(Phone.NORMALIZED_NUMBER, normalizedNumber); 667 resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId}); 668 } 669 670 private static String getCurrentCountryIso(Context context) { 671 String countryIso = null; 672 final CountryDetector detector = (CountryDetector) context.getSystemService( 673 Context.COUNTRY_DETECTOR); 674 if (detector != null) { 675 final Country country = detector.detectCountry(); 676 if (country != null) { 677 countryIso = country.getCountryIso(); 678 } 679 } 680 return countryIso; 681 } 682 } 683} 684