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