CallLog.java revision 509e1f1a97d65ca3356794ac9a85b5c38329c2ea
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 with {@link #ALLOW_VOICEMAILS_PARAM_KEY} set. This can directly be used to 90 * access call log entries that includes voicemail records. 91 * 92 * @hide 93 */ 94 public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon() 95 .appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true") 96 .build(); 97 98 /** 99 * The default sort order for this table 100 */ 101 public static final String DEFAULT_SORT_ORDER = "date DESC"; 102 103 /** 104 * The MIME type of {@link #CONTENT_URI} and {@link #CONTENT_FILTER_URI} 105 * providing a directory of calls. 106 */ 107 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/calls"; 108 109 /** 110 * The MIME type of a {@link #CONTENT_URI} sub-directory of a single 111 * call. 112 */ 113 public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls"; 114 115 /** 116 * The type of the call (incoming, outgoing or missed). 117 * <P>Type: INTEGER (int)</P> 118 */ 119 public static final String TYPE = "type"; 120 121 /** Call log type for incoming calls. */ 122 public static final int INCOMING_TYPE = 1; 123 /** Call log type for outgoing calls. */ 124 public static final int OUTGOING_TYPE = 2; 125 /** Call log type for missed calls. */ 126 public static final int MISSED_TYPE = 3; 127 /** 128 * Call log type for voicemails. 129 * @hide 130 */ 131 public static final int VOICEMAIL_TYPE = 4; 132 133 /** 134 * The phone number as the user entered it. 135 * <P>Type: TEXT</P> 136 */ 137 public static final String NUMBER = "number"; 138 139 /** 140 * The ISO 3166-1 two letters country code of the country where the 141 * user received or made the call. 142 * <P> 143 * Type: TEXT 144 * </P> 145 * 146 * @hide 147 */ 148 public static final String COUNTRY_ISO = "countryiso"; 149 150 /** 151 * The date the call occured, in milliseconds since the epoch 152 * <P>Type: INTEGER (long)</P> 153 */ 154 public static final String DATE = "date"; 155 156 /** 157 * The duration of the call in seconds 158 * <P>Type: INTEGER (long)</P> 159 */ 160 public static final String DURATION = "duration"; 161 162 /** 163 * Whether or not the call has been acknowledged 164 * <P>Type: INTEGER (boolean)</P> 165 */ 166 public static final String NEW = "new"; 167 168 /** 169 * The cached name associated with the phone number, if it exists. 170 * This value is not guaranteed to be current, if the contact information 171 * associated with this number has changed. 172 * <P>Type: TEXT</P> 173 */ 174 public static final String CACHED_NAME = "name"; 175 176 /** 177 * The cached number type (Home, Work, etc) associated with the 178 * phone number, if it exists. 179 * This value is not guaranteed to be current, if the contact information 180 * associated with this number has changed. 181 * <P>Type: INTEGER</P> 182 */ 183 public static final String CACHED_NUMBER_TYPE = "numbertype"; 184 185 /** 186 * The cached number label, for a custom number type, associated with the 187 * phone number, if it exists. 188 * This value is not guaranteed to be current, if the contact information 189 * associated with this number has changed. 190 * <P>Type: TEXT</P> 191 */ 192 public static final String CACHED_NUMBER_LABEL = "numberlabel"; 193 194 /** 195 * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}. 196 * <P>Type: TEXT</P> 197 * @hide 198 */ 199 public static final String VOICEMAIL_URI = "voicemail_uri"; 200 201 /** 202 * Whether this item has been read or otherwise consumed by the user. 203 * <p> 204 * Unlike the {@link #NEW} field, which requires the user to have acknowledged the 205 * existence of the entry, this implies the user has interacted with the entry. 206 * <P>Type: INTEGER (boolean)</P> 207 */ 208 public static final String IS_READ = "is_read"; 209 210 /** 211 * A geocoded location for the number associated with this call. 212 * <p> 213 * The string represents a city, state, or country associated with the number. 214 * <P>Type: TEXT</P> 215 * @hide 216 */ 217 public static final String GEOCODED_LOCATION = "geocoded_location"; 218 219 /** 220 * The cached URI to look up the contact associated with the phone number, if it exists. 221 * This value is not guaranteed to be current, if the contact information 222 * associated with this number has changed. 223 * <P>Type: TEXT</P> 224 * @hide 225 */ 226 public static final String CACHED_LOOKUP_URI = "lookup_uri"; 227 228 /** 229 * The cached phone number of the contact which matches this entry, 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 * @hide 234 */ 235 public static final String CACHED_MATCHED_NUMBER = "matched_number"; 236 237 /** 238 * The cached normalized version of the 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: TEXT</P> 242 * @hide 243 */ 244 public static final String CACHED_NORMALIZED_NUMBER = "normalized_number"; 245 246 /** 247 * The cached photo id of the picture associated with the 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: INTEGER (long)</P> 251 * @hide 252 */ 253 public static final String CACHED_PHOTO_ID = "photo_id"; 254 255 /** 256 * The cached formatted phone number. 257 * This value is not guaranteed to be present. 258 * <P>Type: TEXT</P> 259 * @hide 260 */ 261 public static final String CACHED_FORMATTED_NUMBER = "formatted_number"; 262 263 /** 264 * Adds a call to the call log. 265 * 266 * @param ci the CallerInfo object to get the target contact from. Can be null 267 * if the contact is unknown. 268 * @param context the context used to get the ContentResolver 269 * @param number the phone number to be added to the calls db 270 * @param presentation the number presenting rules set by the network for 271 * "allowed", "payphone", "restricted" or "unknown" 272 * @param callType enumerated values for "incoming", "outgoing", or "missed" 273 * @param start time stamp for the call in milliseconds 274 * @param duration call duration in seconds 275 * 276 * {@hide} 277 */ 278 public static Uri addCall(CallerInfo ci, Context context, String number, 279 int presentation, int callType, long start, int duration) { 280 final ContentResolver resolver = context.getContentResolver(); 281 282 // If this is a private number then set the number to Private, otherwise check 283 // if the number field is empty and set the number to Unavailable 284 if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { 285 number = CallerInfo.PRIVATE_NUMBER; 286 if (ci != null) ci.name = ""; 287 } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { 288 number = CallerInfo.PAYPHONE_NUMBER; 289 if (ci != null) ci.name = ""; 290 } else if (TextUtils.isEmpty(number) 291 || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { 292 number = CallerInfo.UNKNOWN_NUMBER; 293 if (ci != null) ci.name = ""; 294 } 295 296 ContentValues values = new ContentValues(5); 297 298 values.put(NUMBER, number); 299 values.put(TYPE, Integer.valueOf(callType)); 300 values.put(DATE, Long.valueOf(start)); 301 values.put(DURATION, Long.valueOf(duration)); 302 values.put(NEW, Integer.valueOf(1)); 303 if (callType == MISSED_TYPE) { 304 values.put(IS_READ, Integer.valueOf(0)); 305 } 306 if (ci != null) { 307 values.put(CACHED_NAME, ci.name); 308 values.put(CACHED_NUMBER_TYPE, ci.numberType); 309 values.put(CACHED_NUMBER_LABEL, ci.numberLabel); 310 } 311 312 if ((ci != null) && (ci.person_id > 0)) { 313 // Update usage information for the number associated with the contact ID. 314 // We need to use both the number and the ID for obtaining a data ID since other 315 // contacts may have the same number. 316 317 final Cursor cursor; 318 319 // We should prefer normalized one (probably coming from 320 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others. 321 if (ci.normalizedNumber != null) { 322 final String normalizedPhoneNumber = ci.normalizedNumber; 323 cursor = resolver.query(Phone.CONTENT_URI, 324 new String[] { Phone._ID }, 325 Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?", 326 new String[] { String.valueOf(ci.person_id), normalizedPhoneNumber}, 327 null); 328 } else { 329 final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number; 330 cursor = resolver.query( 331 Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, 332 Uri.encode(phoneNumber)), 333 new String[] { Phone._ID }, 334 Phone.CONTACT_ID + " =?", 335 new String[] { String.valueOf(ci.person_id) }, 336 null); 337 } 338 339 if (cursor != null) { 340 try { 341 if (cursor.getCount() > 0 && cursor.moveToFirst()) { 342 final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() 343 .appendPath(cursor.getString(0)) 344 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 345 DataUsageFeedback.USAGE_TYPE_CALL) 346 .build(); 347 resolver.update(feedbackUri, new ContentValues(), null, null); 348 } 349 } finally { 350 cursor.close(); 351 } 352 } 353 } 354 355 Uri result = resolver.insert(CONTENT_URI, values); 356 357 removeExpiredEntries(context); 358 359 return result; 360 } 361 362 /** 363 * Query the call log database for the last dialed number. 364 * @param context Used to get the content resolver. 365 * @return The last phone number dialed (outgoing) or an empty 366 * string if none exist yet. 367 */ 368 public static String getLastOutgoingCall(Context context) { 369 final ContentResolver resolver = context.getContentResolver(); 370 Cursor c = null; 371 try { 372 c = resolver.query( 373 CONTENT_URI, 374 new String[] {NUMBER}, 375 TYPE + " = " + OUTGOING_TYPE, 376 null, 377 DEFAULT_SORT_ORDER + " LIMIT 1"); 378 if (c == null || !c.moveToFirst()) { 379 return ""; 380 } 381 return c.getString(0); 382 } finally { 383 if (c != null) c.close(); 384 } 385 } 386 387 private static void removeExpiredEntries(Context context) { 388 final ContentResolver resolver = context.getContentResolver(); 389 resolver.delete(CONTENT_URI, "_id IN " + 390 "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER 391 + " LIMIT -1 OFFSET 500)", null); 392 } 393 } 394} 395