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