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 */ addCall(CallerInfo ci, Context context, String number, int presentation, int callType, long start, int duration)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 */ getLastOutgoingCall(Context context)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 removeExpiredEntries(Context context)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