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