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