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 18 package android.provider; 19 20 import android.content.ContentResolver; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.database.Cursor; 24 import android.net.Uri; 25 import android.provider.ContactsContract.CommonDataKinds.Callable; 26 import android.provider.ContactsContract.CommonDataKinds.Phone; 27 import android.provider.ContactsContract.DataUsageFeedback; 28 import android.text.TextUtils; 29 30 import com.android.internal.telephony.CallerInfo; 31 import com.android.internal.telephony.PhoneConstants; 32 33 /** 34 * The CallLog provider contains information about placed and received calls. 35 */ 36 public 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 number presenting rules set by the network. 141 * 142 * <p> 143 * Allowed values: 144 * <ul> 145 * <li>{@link #PRESENTATION_ALLOWED}</li> 146 * <li>{@link #PRESENTATION_RESTRICTED}</li> 147 * <li>{@link #PRESENTATION_UNKNOWN}</li> 148 * <li>{@link #PRESENTATION_PAYPHONE}</li> 149 * </ul> 150 * </p> 151 * 152 * <P>Type: INTEGER</P> 153 */ 154 public static final String NUMBER_PRESENTATION = "presentation"; 155 156 /** Number is allowed to display for caller id. */ 157 public static final int PRESENTATION_ALLOWED = 1; 158 /** Number is blocked by user. */ 159 public static final int PRESENTATION_RESTRICTED = 2; 160 /** Number is not specified or unknown by network. */ 161 public static final int PRESENTATION_UNKNOWN = 3; 162 /** Number is a pay phone. */ 163 public static final int PRESENTATION_PAYPHONE = 4; 164 165 /** 166 * The ISO 3166-1 two letters country code of the country where the 167 * user received or made the call. 168 * <P> 169 * Type: TEXT 170 * </P> 171 * 172 * @hide 173 */ 174 public static final String COUNTRY_ISO = "countryiso"; 175 176 /** 177 * The date the call occured, in milliseconds since the epoch 178 * <P>Type: INTEGER (long)</P> 179 */ 180 public static final String DATE = "date"; 181 182 /** 183 * The duration of the call in seconds 184 * <P>Type: INTEGER (long)</P> 185 */ 186 public static final String DURATION = "duration"; 187 188 /** 189 * Whether or not the call has been acknowledged 190 * <P>Type: INTEGER (boolean)</P> 191 */ 192 public static final String NEW = "new"; 193 194 /** 195 * The cached name associated with the phone number, if it exists. 196 * This value is not guaranteed to be current, if the contact information 197 * associated with this number has changed. 198 * <P>Type: TEXT</P> 199 */ 200 public static final String CACHED_NAME = "name"; 201 202 /** 203 * The cached number type (Home, Work, etc) associated with the 204 * phone number, if it exists. 205 * This value is not guaranteed to be current, if the contact information 206 * associated with this number has changed. 207 * <P>Type: INTEGER</P> 208 */ 209 public static final String CACHED_NUMBER_TYPE = "numbertype"; 210 211 /** 212 * The cached number label, for a custom number type, associated with the 213 * phone number, if it exists. 214 * This value is not guaranteed to be current, if the contact information 215 * associated with this number has changed. 216 * <P>Type: TEXT</P> 217 */ 218 public static final String CACHED_NUMBER_LABEL = "numberlabel"; 219 220 /** 221 * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}. 222 * <P>Type: TEXT</P> 223 * @hide 224 */ 225 public static final String VOICEMAIL_URI = "voicemail_uri"; 226 227 /** 228 * Whether this item has been read or otherwise consumed by the user. 229 * <p> 230 * Unlike the {@link #NEW} field, which requires the user to have acknowledged the 231 * existence of the entry, this implies the user has interacted with the entry. 232 * <P>Type: INTEGER (boolean)</P> 233 */ 234 public static final String IS_READ = "is_read"; 235 236 /** 237 * A geocoded location for the number associated with this call. 238 * <p> 239 * The string represents a city, state, or country associated with the number. 240 * <P>Type: TEXT</P> 241 * @hide 242 */ 243 public static final String GEOCODED_LOCATION = "geocoded_location"; 244 245 /** 246 * The cached URI to look up the contact associated with the phone number, if it exists. 247 * This value is not guaranteed to be current, if the contact information 248 * associated with this number has changed. 249 * <P>Type: TEXT</P> 250 * @hide 251 */ 252 public static final String CACHED_LOOKUP_URI = "lookup_uri"; 253 254 /** 255 * The cached phone number of the contact which matches this entry, if it exists. 256 * This value is not guaranteed to be current, if the contact information 257 * associated with this number has changed. 258 * <P>Type: TEXT</P> 259 * @hide 260 */ 261 public static final String CACHED_MATCHED_NUMBER = "matched_number"; 262 263 /** 264 * The cached normalized version of the phone number, if it exists. 265 * This value is not guaranteed to be current, if the contact information 266 * associated with this number has changed. 267 * <P>Type: TEXT</P> 268 * @hide 269 */ 270 public static final String CACHED_NORMALIZED_NUMBER = "normalized_number"; 271 272 /** 273 * The cached photo id of the picture associated with the phone number, if it exists. 274 * This value is not guaranteed to be current, if the contact information 275 * associated with this number has changed. 276 * <P>Type: INTEGER (long)</P> 277 * @hide 278 */ 279 public static final String CACHED_PHOTO_ID = "photo_id"; 280 281 /** 282 * The cached formatted phone number. 283 * This value is not guaranteed to be present. 284 * <P>Type: TEXT</P> 285 * @hide 286 */ 287 public static final String CACHED_FORMATTED_NUMBER = "formatted_number"; 288 289 /** 290 * Adds a call to the call log. 291 * 292 * @param ci the CallerInfo object to get the target contact from. Can be null 293 * if the contact is unknown. 294 * @param context the context used to get the ContentResolver 295 * @param number the phone number to be added to the calls db 296 * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which 297 * is set by the network and denotes the number presenting rules for 298 * "allowed", "payphone", "restricted" or "unknown" 299 * @param callType enumerated values for "incoming", "outgoing", or "missed" 300 * @param start time stamp for the call in milliseconds 301 * @param duration call duration in seconds 302 * 303 * {@hide} 304 */ 305 public static Uri addCall(CallerInfo ci, Context context, String number, 306 int presentation, int callType, long start, int duration) { 307 final ContentResolver resolver = context.getContentResolver(); 308 int numberPresentation = PRESENTATION_ALLOWED; 309 310 // Remap network specified number presentation types 311 // PhoneConstants.PRESENTATION_xxx to calllog number presentation types 312 // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog 313 // from any future radio changes. 314 // If the number field is empty set the presentation type to Unknown. 315 if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) { 316 numberPresentation = PRESENTATION_RESTRICTED; 317 } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) { 318 numberPresentation = PRESENTATION_PAYPHONE; 319 } else if (TextUtils.isEmpty(number) 320 || presentation == PhoneConstants.PRESENTATION_UNKNOWN) { 321 numberPresentation = PRESENTATION_UNKNOWN; 322 } 323 if (numberPresentation != PRESENTATION_ALLOWED) { 324 number = ""; 325 if (ci != null) { 326 ci.name = ""; 327 } 328 } 329 330 ContentValues values = new ContentValues(6); 331 332 values.put(NUMBER, number); 333 values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation)); 334 values.put(TYPE, Integer.valueOf(callType)); 335 values.put(DATE, Long.valueOf(start)); 336 values.put(DURATION, Long.valueOf(duration)); 337 values.put(NEW, Integer.valueOf(1)); 338 if (callType == MISSED_TYPE) { 339 values.put(IS_READ, Integer.valueOf(0)); 340 } 341 if (ci != null) { 342 values.put(CACHED_NAME, ci.name); 343 values.put(CACHED_NUMBER_TYPE, ci.numberType); 344 values.put(CACHED_NUMBER_LABEL, ci.numberLabel); 345 } 346 347 if ((ci != null) && (ci.person_id > 0)) { 348 // Update usage information for the number associated with the contact ID. 349 // We need to use both the number and the ID for obtaining a data ID since other 350 // contacts may have the same number. 351 352 final Cursor cursor; 353 354 // We should prefer normalized one (probably coming from 355 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others. 356 if (ci.normalizedNumber != null) { 357 final String normalizedPhoneNumber = ci.normalizedNumber; 358 cursor = resolver.query(Phone.CONTENT_URI, 359 new String[] { Phone._ID }, 360 Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?", 361 new String[] { String.valueOf(ci.person_id), normalizedPhoneNumber}, 362 null); 363 } else { 364 final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number; 365 cursor = resolver.query( 366 Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, 367 Uri.encode(phoneNumber)), 368 new String[] { Phone._ID }, 369 Phone.CONTACT_ID + " =?", 370 new String[] { String.valueOf(ci.person_id) }, 371 null); 372 } 373 374 if (cursor != null) { 375 try { 376 if (cursor.getCount() > 0 && cursor.moveToFirst()) { 377 final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() 378 .appendPath(cursor.getString(0)) 379 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 380 DataUsageFeedback.USAGE_TYPE_CALL) 381 .build(); 382 resolver.update(feedbackUri, new ContentValues(), null, null); 383 } 384 } finally { 385 cursor.close(); 386 } 387 } 388 } 389 390 Uri result = resolver.insert(CONTENT_URI, values); 391 392 removeExpiredEntries(context); 393 394 return result; 395 } 396 397 /** 398 * Query the call log database for the last dialed number. 399 * @param context Used to get the content resolver. 400 * @return The last phone number dialed (outgoing) or an empty 401 * string if none exist yet. 402 */ 403 public static String getLastOutgoingCall(Context context) { 404 final ContentResolver resolver = context.getContentResolver(); 405 Cursor c = null; 406 try { 407 c = resolver.query( 408 CONTENT_URI, 409 new String[] {NUMBER}, 410 TYPE + " = " + OUTGOING_TYPE, 411 null, 412 DEFAULT_SORT_ORDER + " LIMIT 1"); 413 if (c == null || !c.moveToFirst()) { 414 return ""; 415 } 416 return c.getString(0); 417 } finally { 418 if (c != null) c.close(); 419 } 420 } 421 422 private static void removeExpiredEntries(Context context) { 423 final ContentResolver resolver = context.getContentResolver(); 424 resolver.delete(CONTENT_URI, "_id IN " + 425 "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER 426 + " LIMIT -1 OFFSET 500)", null); 427 } 428 } 429 } 430