CallLog.java revision 4efaf4ba84187a9e25c081a2d1c0d409cbd94911
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         * A geocoded location for the number associated with this call.
197         * <p>
198         * The string represents a city, state, or country associated with the number.
199         * <P>Type: TEXT</P>
200         * @hide
201         */
202        public static final String GEOCODED_LOCATION = "geocoded_location";
203
204        /**
205         * Adds a call to the call log.
206         *
207         * @param ci the CallerInfo object to get the target contact from.  Can be null
208         * if the contact is unknown.
209         * @param context the context used to get the ContentResolver
210         * @param number the phone number to be added to the calls db
211         * @param presentation the number presenting rules set by the network for
212         *        "allowed", "payphone", "restricted" or "unknown"
213         * @param callType enumerated values for "incoming", "outgoing", or "missed"
214         * @param start time stamp for the call in milliseconds
215         * @param duration call duration in seconds
216         *
217         * {@hide}
218         */
219        public static Uri addCall(CallerInfo ci, Context context, String number,
220                int presentation, int callType, long start, int duration) {
221            final ContentResolver resolver = context.getContentResolver();
222
223            // If this is a private number then set the number to Private, otherwise check
224            // if the number field is empty and set the number to Unavailable
225            if (presentation == Connection.PRESENTATION_RESTRICTED) {
226                number = CallerInfo.PRIVATE_NUMBER;
227                if (ci != null) ci.name = "";
228            } else if (presentation == Connection.PRESENTATION_PAYPHONE) {
229                number = CallerInfo.PAYPHONE_NUMBER;
230                if (ci != null) ci.name = "";
231            } else if (TextUtils.isEmpty(number)
232                    || presentation == Connection.PRESENTATION_UNKNOWN) {
233                number = CallerInfo.UNKNOWN_NUMBER;
234                if (ci != null) ci.name = "";
235            }
236
237            ContentValues values = new ContentValues(5);
238
239            values.put(NUMBER, number);
240            values.put(TYPE, Integer.valueOf(callType));
241            values.put(DATE, Long.valueOf(start));
242            values.put(DURATION, Long.valueOf(duration));
243            values.put(NEW, Integer.valueOf(1));
244            if (callType == MISSED_TYPE) {
245                values.put(IS_READ, Integer.valueOf(0));
246            }
247            if (ci != null) {
248                values.put(CACHED_NAME, ci.name);
249                values.put(CACHED_NUMBER_TYPE, ci.numberType);
250                values.put(CACHED_NUMBER_LABEL, ci.numberLabel);
251            }
252
253            if ((ci != null) && (ci.person_id > 0)) {
254                // Update usage information for the number associated with the contact ID.
255                // We need to use both the number and the ID for obtaining a data ID since other
256                // contacts may have the same number.
257
258                final Cursor cursor;
259
260                // We should prefer normalized one (probably coming from
261                // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
262                if (ci.normalizedNumber != null) {
263                    final String normalizedPhoneNumber = ci.normalizedNumber;
264                    cursor = resolver.query(Phone.CONTENT_URI,
265                            new String[] { Phone._ID },
266                            Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
267                            new String[] { String.valueOf(ci.person_id), normalizedPhoneNumber},
268                            null);
269                } else {
270                    final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
271                    cursor = resolver.query(Phone.CONTENT_URI,
272                            new String[] { Phone._ID },
273                            Phone.CONTACT_ID + " =? AND " + Phone.NUMBER + " =?",
274                            new String[] { String.valueOf(ci.person_id), phoneNumber},
275                            null);
276                }
277
278                if (cursor != null) {
279                    try {
280                        if (cursor.getCount() > 0 && cursor.moveToFirst()) {
281                            final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
282                                    .appendPath(cursor.getString(0))
283                                    .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
284                                                DataUsageFeedback.USAGE_TYPE_CALL)
285                                    .build();
286                            resolver.update(feedbackUri, new ContentValues(), null, null);
287                        }
288                    } finally {
289                        cursor.close();
290                    }
291                }
292            }
293
294            Uri result = resolver.insert(CONTENT_URI, values);
295
296            removeExpiredEntries(context);
297
298            return result;
299        }
300
301        /**
302         * Query the call log database for the last dialed number.
303         * @param context Used to get the content resolver.
304         * @return The last phone number dialed (outgoing) or an empty
305         * string if none exist yet.
306         */
307        public static String getLastOutgoingCall(Context context) {
308            final ContentResolver resolver = context.getContentResolver();
309            Cursor c = null;
310            try {
311                c = resolver.query(
312                    CONTENT_URI,
313                    new String[] {NUMBER},
314                    TYPE + " = " + OUTGOING_TYPE,
315                    null,
316                    DEFAULT_SORT_ORDER + " LIMIT 1");
317                if (c == null || !c.moveToFirst()) {
318                    return "";
319                }
320                return c.getString(0);
321            } finally {
322                if (c != null) c.close();
323            }
324        }
325
326        private static void removeExpiredEntries(Context context) {
327            final ContentResolver resolver = context.getContentResolver();
328            resolver.delete(CONTENT_URI, "_id IN " +
329                    "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
330                    + " LIMIT -1 OFFSET 500)", null);
331        }
332    }
333}
334