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