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