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