CallLog.java revision 7a5e5726ab7b2940d45b0651acfa848db57d284c
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.net.Uri; 28import android.os.UserHandle; 29import android.os.UserManager; 30import android.provider.ContactsContract.CommonDataKinds.Callable; 31import android.provider.ContactsContract.CommonDataKinds.Phone; 32import android.provider.ContactsContract.DataUsageFeedback; 33import android.telecomm.PhoneAccountHandle; 34import android.text.TextUtils; 35 36import com.android.internal.telephony.CallerInfo; 37import com.android.internal.telephony.PhoneConstants; 38 39import java.util.List; 40 41/** 42 * The CallLog provider contains information about placed and received calls. 43 */ 44public class CallLog { 45 public static final String AUTHORITY = "call_log"; 46 47 /** 48 * The content:// style URL for this provider 49 */ 50 public static final Uri CONTENT_URI = 51 Uri.parse("content://" + AUTHORITY); 52 53 /** 54 * Contains the recent calls. 55 */ 56 public static class Calls implements BaseColumns { 57 /** 58 * The content:// style URL for this table 59 */ 60 public static final Uri CONTENT_URI = 61 Uri.parse("content://call_log/calls"); 62 63 /** 64 * The content:// style URL for filtering this table on phone numbers 65 */ 66 public static final Uri CONTENT_FILTER_URI = 67 Uri.parse("content://call_log/calls/filter"); 68 69 /** 70 * Query parameter used to limit the number of call logs returned. 71 * <p> 72 * TYPE: integer 73 */ 74 public static final String LIMIT_PARAM_KEY = "limit"; 75 76 /** 77 * Query parameter used to specify the starting record to return. 78 * <p> 79 * TYPE: integer 80 */ 81 public static final String OFFSET_PARAM_KEY = "offset"; 82 83 /** 84 * An optional URI parameter which instructs the provider to allow the operation to be 85 * applied to voicemail records as well. 86 * <p> 87 * TYPE: Boolean 88 * <p> 89 * Using this parameter with a value of {@code true} will result in a security error if the 90 * calling package does not have appropriate permissions to access voicemails. 91 * 92 * @hide 93 */ 94 public static final String ALLOW_VOICEMAILS_PARAM_KEY = "allow_voicemails"; 95 96 /** 97 * An optional extra used with {@link #CONTENT_TYPE Calls.CONTENT_TYPE} and 98 * {@link Intent#ACTION_VIEW} to specify that the presented list of calls should be 99 * filtered for a particular call type. 100 * 101 * Applications implementing a call log UI should check for this extra, and display a 102 * filtered list of calls based on the specified call type. If not applicable within the 103 * application's UI, it should be silently ignored. 104 * 105 * <p> 106 * The following example brings up the call log, showing only missed calls. 107 * <pre> 108 * Intent intent = new Intent(Intent.ACTION_VIEW); 109 * intent.setType(CallLog.Calls.CONTENT_TYPE); 110 * intent.putExtra(CallLog.Calls.EXTRA_CALL_TYPE_FILTER, CallLog.Calls.MISSED_TYPE); 111 * startActivity(intent); 112 * </pre> 113 * </p> 114 */ 115 public static final String EXTRA_CALL_TYPE_FILTER = "extra_call_type_filter"; 116 117 /** 118 * Content uri used to access call log entries, including voicemail records. You must have 119 * the READ_CALL_LOG and WRITE_CALL_LOG permissions to read and write to the call log, as 120 * well as READ_VOICEMAIL and WRITE_VOICEMAIL permissions to read and write voicemails. 121 */ 122 public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon() 123 .appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true") 124 .build(); 125 126 /** 127 * The default sort order for this table 128 */ 129 public static final String DEFAULT_SORT_ORDER = "date DESC"; 130 131 /** 132 * The MIME type of {@link #CONTENT_URI} and {@link #CONTENT_FILTER_URI} 133 * providing a directory of calls. 134 */ 135 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/calls"; 136 137 /** 138 * The MIME type of a {@link #CONTENT_URI} sub-directory of a single 139 * call. 140 */ 141 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls"; 142 143 /** 144 * The type of the call (incoming, outgoing or missed). 145 * <P>Type: INTEGER (int)</P> 146 */ 147 public static final String TYPE = "type"; 148 149 /** Call log type for incoming calls. */ 150 public static final int INCOMING_TYPE = 1; 151 /** Call log type for outgoing calls. */ 152 public static final int OUTGOING_TYPE = 2; 153 /** Call log type for missed calls. */ 154 public static final int MISSED_TYPE = 3; 155 /** Call log type for voicemails. */ 156 public static final int VOICEMAIL_TYPE = 4; 157 158 /** 159 * Bit-mask describing features of the call (e.g. video). 160 * 161 * <P>Type: INTEGER (int)</P> 162 */ 163 public static final String FEATURES = "features"; 164 165 /** Call had no associated features (e.g. voice-only). */ 166 public static final int FEATURES_NONE = 0x0; 167 /** Call had video. */ 168 public static final int FEATURES_VIDEO = 0x1; 169 170 /** 171 * The phone number as the user entered it. 172 * <P>Type: TEXT</P> 173 */ 174 public static final String NUMBER = "number"; 175 176 /** 177 * The number presenting rules set by the network. 178 * 179 * <p> 180 * Allowed values: 181 * <ul> 182 * <li>{@link #PRESENTATION_ALLOWED}</li> 183 * <li>{@link #PRESENTATION_RESTRICTED}</li> 184 * <li>{@link #PRESENTATION_UNKNOWN}</li> 185 * <li>{@link #PRESENTATION_PAYPHONE}</li> 186 * </ul> 187 * </p> 188 * 189 * <P>Type: INTEGER</P> 190 */ 191 public static final String NUMBER_PRESENTATION = "presentation"; 192 193 /** Number is allowed to display for caller id. */ 194 public static final int PRESENTATION_ALLOWED = 1; 195 /** Number is blocked by user. */ 196 public static final int PRESENTATION_RESTRICTED = 2; 197 /** Number is not specified or unknown by network. */ 198 public static final int PRESENTATION_UNKNOWN = 3; 199 /** Number is a pay phone. */ 200 public static final int PRESENTATION_PAYPHONE = 4; 201 202 /** 203 * The ISO 3166-1 two letters country code of the country where the 204 * user received or made the call. 205 * <P> 206 * Type: TEXT 207 * </P> 208 */ 209 public static final String COUNTRY_ISO = "countryiso"; 210 211 /** 212 * The date the call occured, in milliseconds since the epoch 213 * <P>Type: INTEGER (long)</P> 214 */ 215 public static final String DATE = "date"; 216 217 /** 218 * The duration of the call in seconds 219 * <P>Type: INTEGER (long)</P> 220 */ 221 public static final String DURATION = "duration"; 222 223 /** 224 * The data usage of the call in bytes. 225 * <P>Type: INTEGER (long)</P> 226 */ 227 public static final String DATA_USAGE = "data_usage"; 228 229 /** 230 * Whether or not the call has been acknowledged 231 * <P>Type: INTEGER (boolean)</P> 232 */ 233 public static final String NEW = "new"; 234 235 /** 236 * The cached name associated with the phone number, if it exists. 237 * This value is not guaranteed to be current, if the contact information 238 * associated with this number has changed. 239 * <P>Type: TEXT</P> 240 */ 241 public static final String CACHED_NAME = "name"; 242 243 /** 244 * The cached number type (Home, Work, etc) associated with the 245 * phone number, if it exists. 246 * This value is not guaranteed to be current, if the contact information 247 * associated with this number has changed. 248 * <P>Type: INTEGER</P> 249 */ 250 public static final String CACHED_NUMBER_TYPE = "numbertype"; 251 252 /** 253 * The cached number label, for a custom number type, associated with the 254 * phone number, if it exists. 255 * This value is not guaranteed to be current, if the contact information 256 * associated with this number has changed. 257 * <P>Type: TEXT</P> 258 */ 259 public static final String CACHED_NUMBER_LABEL = "numberlabel"; 260 261 /** 262 * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}. 263 * <P>Type: TEXT</P> 264 */ 265 public static final String VOICEMAIL_URI = "voicemail_uri"; 266 267 /** 268 * Transcription of the call or voicemail entry. This will only be populated for call log 269 * entries of type {@link #VOICEMAIL_TYPE} that have valid transcriptions. 270 */ 271 public static final String TRANSCRIPTION = "transcription"; 272 273 /** 274 * Whether this item has been read or otherwise consumed by the user. 275 * <p> 276 * Unlike the {@link #NEW} field, which requires the user to have acknowledged the 277 * existence of the entry, this implies the user has interacted with the entry. 278 * <P>Type: INTEGER (boolean)</P> 279 */ 280 public static final String IS_READ = "is_read"; 281 282 /** 283 * A geocoded location for the number associated with this call. 284 * <p> 285 * The string represents a city, state, or country associated with the number. 286 * <P>Type: TEXT</P> 287 */ 288 public static final String GEOCODED_LOCATION = "geocoded_location"; 289 290 /** 291 * The cached URI to look up the contact associated with the phone number, if it exists. 292 * This value may not be current if the contact information associated with this number 293 * has changed. 294 * <P>Type: TEXT</P> 295 */ 296 public static final String CACHED_LOOKUP_URI = "lookup_uri"; 297 298 /** 299 * The cached phone number of the contact which matches this entry, 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_MATCHED_NUMBER = "matched_number"; 305 306 /** 307 * The cached normalized(E164) version of the phone number, 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_NORMALIZED_NUMBER = "normalized_number"; 313 314 /** 315 * The cached photo id of the picture 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: INTEGER (long)</P> 319 */ 320 public static final String CACHED_PHOTO_ID = "photo_id"; 321 322 /** 323 * The cached phone number, formatted with formatting rules based on the country the 324 * user was in when the call was made or received. 325 * This value is not guaranteed to be present, and may not be current if the contact 326 * information associated with this number 327 * has changed. 328 * <P>Type: TEXT</P> 329 */ 330 public static final String CACHED_FORMATTED_NUMBER = "formatted_number"; 331 332 // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming 333 // that was encoded into call log databases. 334 335 /** 336 * The component name of the account in string form. 337 * <P>Type: TEXT</P> 338 */ 339 public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name"; 340 341 /** 342 * The identifier of a account that is unique to a specified component. 343 * <P>Type: TEXT</P> 344 */ 345 public static final String PHONE_ACCOUNT_ID = "subscription_id"; 346 347 /** 348 * Adds a call to the call log. 349 * 350 * @param ci the CallerInfo object to get the target contact from. Can be null 351 * if the contact is unknown. 352 * @param context the context used to get the ContentResolver 353 * @param number the phone number to be added to the calls db 354 * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which 355 * is set by the network and denotes the number presenting rules for 356 * "allowed", "payphone", "restricted" or "unknown" 357 * @param callType enumerated values for "incoming", "outgoing", or "missed" 358 * @param features features of the call (e.g. Video). 359 * @param accountHandle The accountHandle object identifying the provider of the call 360 * @param start time stamp for the call in milliseconds 361 * @param duration call duration in seconds 362 * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for 363 * the call. 364 * @result The URI of the call log entry belonging to the user that made or received this 365 * call. 366 * {@hide} 367 */ 368 public static Uri addCall(CallerInfo ci, Context context, String number, 369 int presentation, int callType, int features, PhoneAccountHandle accountHandle, 370 long start, int duration, Long dataUsage) { 371 return addCall(ci, context, number, presentation, callType, features, accountHandle, 372 start, duration, dataUsage, false); 373 } 374 375 /** 376 * Adds a call to the call log. 377 * 378 * @param ci the CallerInfo object to get the target contact from. Can be null 379 * if the contact is unknown. 380 * @param context the context used to get the ContentResolver 381 * @param number the phone number to be added to the calls db 382 * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which 383 * is set by the network and denotes the number presenting rules for 384 * "allowed", "payphone", "restricted" or "unknown" 385 * @param callType enumerated values for "incoming", "outgoing", or "missed" 386 * @param features features of the call (e.g. Video). 387 * @param accountHandle The accountHandle object identifying the provider of the call 388 * @param start time stamp for the call in milliseconds 389 * @param duration call duration in seconds 390 * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for 391 * the call. 392 * @param addForAllUsers If true, the call is added to the call log of all currently 393 * running users. The caller must have the MANAGE_USERS permission if this is true. 394 * 395 * @result The URI of the call log entry belonging to the user that made or received this 396 * call. 397 * {@hide} 398 */ 399 public static Uri addCall(CallerInfo ci, Context context, String number, 400 int presentation, int callType, int features, PhoneAccountHandle accountHandle, 401 long start, int duration, Long dataUsage, boolean addForAllUsers) { 402 final ContentResolver resolver = context.getContentResolver(); 403 int numberPresentation = PRESENTATION_ALLOWED; 404 405 // Remap network specified number presentation types 406 // PhoneConstants.PRESENTATION_xxx to calllog number presentation types 407 // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog 408 // from any future radio changes. 409 // If the number field is empty set the presentation type to Unknown. 410 if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { 411 numberPresentation = PRESENTATION_RESTRICTED; 412 } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { 413 numberPresentation = PRESENTATION_PAYPHONE; 414 } else if (TextUtils.isEmpty(number) 415 || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { 416 numberPresentation = PRESENTATION_UNKNOWN; 417 } 418 if (numberPresentation != PRESENTATION_ALLOWED) { 419 number = ""; 420 if (ci != null) { 421 ci.name = ""; 422 } 423 } 424 425 // accountHandle information 426 String accountComponentString = null; 427 String accountId = null; 428 if (accountHandle != null) { 429 accountComponentString = accountHandle.getComponentName().flattenToString(); 430 accountId = accountHandle.getId(); 431 } 432 433 ContentValues values = new ContentValues(6); 434 435 values.put(NUMBER, number); 436 values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation)); 437 values.put(TYPE, Integer.valueOf(callType)); 438 values.put(FEATURES, features); 439 values.put(DATE, Long.valueOf(start)); 440 values.put(DURATION, Long.valueOf(duration)); 441 if (dataUsage != null) { 442 values.put(DATA_USAGE, dataUsage); 443 } 444 values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString); 445 values.put(PHONE_ACCOUNT_ID, accountId); 446 values.put(NEW, Integer.valueOf(1)); 447 if (callType == MISSED_TYPE) { 448 values.put(IS_READ, Integer.valueOf(0)); 449 } 450 if (ci != null) { 451 values.put(CACHED_NAME, ci.name); 452 values.put(CACHED_NUMBER_TYPE, ci.numberType); 453 values.put(CACHED_NUMBER_LABEL, ci.numberLabel); 454 } 455 456 if ((ci != null) && (ci.contactIdOrZero > 0)) { 457 // Update usage information for the number associated with the contact ID. 458 // We need to use both the number and the ID for obtaining a data ID since other 459 // contacts may have the same number. 460 461 final Cursor cursor; 462 463 // We should prefer normalized one (probably coming from 464 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others. 465 if (ci.normalizedNumber != null) { 466 final String normalizedPhoneNumber = ci.normalizedNumber; 467 cursor = resolver.query(Phone.CONTENT_URI, 468 new String[] { Phone._ID }, 469 Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?", 470 new String[] { String.valueOf(ci.contactIdOrZero), 471 normalizedPhoneNumber}, 472 null); 473 } else { 474 final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number; 475 cursor = resolver.query( 476 Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, 477 Uri.encode(phoneNumber)), 478 new String[] { Phone._ID }, 479 Phone.CONTACT_ID + " =?", 480 new String[] { String.valueOf(ci.contactIdOrZero) }, 481 null); 482 } 483 484 if (cursor != null) { 485 try { 486 if (cursor.getCount() > 0 && cursor.moveToFirst()) { 487 final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() 488 .appendPath(cursor.getString(0)) 489 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 490 DataUsageFeedback.USAGE_TYPE_CALL) 491 .build(); 492 resolver.update(feedbackUri, new ContentValues(), null, null); 493 } 494 } finally { 495 cursor.close(); 496 } 497 } 498 } 499 500 Uri result = null; 501 502 if (addForAllUsers) { 503 // Insert the entry for all currently running users, in order to trigger any 504 // ContentObservers currently set on the call log. 505 final UserManager userManager = (UserManager) context.getSystemService( 506 Context.USER_SERVICE); 507 List<UserInfo> users = userManager.getUsers(true); 508 final int currentUserId = userManager.getUserHandle(); 509 final int count = users.size(); 510 for (int i = 0; i < count; i++) { 511 final UserInfo user = users.get(i); 512 final UserHandle userHandle = user.getUserHandle(); 513 if (userManager.isUserRunning(userHandle) && 514 !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, 515 userHandle)) { 516 Uri uri = addEntryAndRemoveExpiredEntries(context, 517 ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values); 518 if (user.id == currentUserId) { 519 result = uri; 520 } 521 } 522 } 523 } else { 524 result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values); 525 } 526 527 return result; 528 } 529 530 /** 531 * Query the call log database for the last dialed number. 532 * @param context Used to get the content resolver. 533 * @return The last phone number dialed (outgoing) or an empty 534 * string if none exist yet. 535 */ 536 public static String getLastOutgoingCall(Context context) { 537 final ContentResolver resolver = context.getContentResolver(); 538 Cursor c = null; 539 try { 540 c = resolver.query( 541 CONTENT_URI, 542 new String[] {NUMBER}, 543 TYPE + " = " + OUTGOING_TYPE, 544 null, 545 DEFAULT_SORT_ORDER + " LIMIT 1"); 546 if (c == null || !c.moveToFirst()) { 547 return ""; 548 } 549 return c.getString(0); 550 } finally { 551 if (c != null) c.close(); 552 } 553 } 554 555 private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri, 556 ContentValues values) { 557 final ContentResolver resolver = context.getContentResolver(); 558 Uri result = resolver.insert(uri, values); 559 resolver.delete(uri, "_id IN " + 560 "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER 561 + " LIMIT -1 OFFSET 500)", null); 562 return result; 563 } 564 } 565} 566