CallLog.java revision c66f3baa42b8a732952abf1967c68f77d3e26131
185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian/*
285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * Copyright (C) 2006 The Android Open Source Project
385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian *
485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * Licensed under the Apache License, Version 2.0 (the "License");
585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * you may not use this file except in compliance with the License.
685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * You may obtain a copy of the License at
785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian *
885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian *      http://www.apache.org/licenses/LICENSE-2.0
985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian *
1085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * Unless required by applicable law or agreed to in writing, software
1185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * distributed under the License is distributed on an "AS IS" BASIS,
1285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * See the License for the specific language governing permissions and
1485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * limitations under the License.
1585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian */
1685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
1785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
1885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianpackage android.provider;
1985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
2085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.ContentProvider;
2185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.ContentResolver;
2285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.ContentValues;
2385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.Context;
2485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.Intent;
2585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.content.pm.UserInfo;
2685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.database.Cursor;
2785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.location.Country;
2885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.location.CountryDetector;
2985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.net.Uri;
3085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.os.UserHandle;
3185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.os.UserManager;
3285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.provider.ContactsContract.CommonDataKinds.Callable;
336547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennisimport android.provider.ContactsContract.CommonDataKinds.Phone;
346547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennisimport android.provider.ContactsContract.Data;
3585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.provider.ContactsContract.DataUsageFeedback;
3685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.telecom.PhoneAccount;
3785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.telecom.PhoneAccountHandle;
3885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.telecom.TelecomManager;
3985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.telephony.PhoneNumberUtils;
4085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.text.TextUtils;
4185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport android.util.Log;
4285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
4385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport com.android.internal.telephony.CallerInfo;
4485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport com.android.internal.telephony.PhoneConstants;
4585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
4685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianimport java.util.List;
4785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
4885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian/**
4985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian * The CallLog provider contains information about placed and received calls.
5085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian */
5185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopianpublic class CallLog {
5285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian    private static final String LOG_TAG = "CallLog";
5385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
5485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian    public static final String AUTHORITY = "call_log";
5585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
5685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian    /**
5785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian     * The content:// style URL for this provider
5885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian     */
5985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian    public static final Uri CONTENT_URI =
6085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian        Uri.parse("content://" + AUTHORITY);
6185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
6285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian    /**
6385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian     * Contains the recent calls.
6485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian     */
6585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian    public static class Calls implements BaseColumns {
6685cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian        /**
6785cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian         * The content:// style URL for this table
6885cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian         */
6985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian        public static final Uri CONTENT_URI =
7085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian                Uri.parse("content://call_log/calls");
7185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
7285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian        /**
7385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian         * The content:// style URL for filtering this table on phone numbers
7485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian         */
7585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian        public static final Uri CONTENT_FILTER_URI =
766547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennis                Uri.parse("content://call_log/calls/filter");
776547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennis
786547ff4327aa320fbc9635668d3fc66db7dd78f6Jamie Gennis        /**
7985cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian         * Query parameter used to limit the number of call logs returned.
8085cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian         * <p>
8185cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian         * TYPE: integer
8285cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian         */
8385cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian        public static final String LIMIT_PARAM_KEY = "limit";
8485cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian
8585cce376cbeecb185ba485c69643bfabe72fe794Mathias Agopian        /**
86         * Query parameter used to specify the starting record to return.
87         * <p>
88         * TYPE: integer
89         */
90        public static final String OFFSET_PARAM_KEY = "offset";
91
92        /**
93         * An optional URI parameter which instructs the provider to allow the operation to be
94         * applied to voicemail records as well.
95         * <p>
96         * TYPE: Boolean
97         * <p>
98         * Using this parameter with a value of {@code true} will result in a security error if the
99         * calling package does not have appropriate permissions to access voicemails.
100         *
101         * @hide
102         */
103        public static final String ALLOW_VOICEMAILS_PARAM_KEY = "allow_voicemails";
104
105        /**
106         * An optional extra used with {@link #CONTENT_TYPE Calls.CONTENT_TYPE} and
107         * {@link Intent#ACTION_VIEW} to specify that the presented list of calls should be
108         * filtered for a particular call type.
109         *
110         * Applications implementing a call log UI should check for this extra, and display a
111         * filtered list of calls based on the specified call type. If not applicable within the
112         * application's UI, it should be silently ignored.
113         *
114         * <p>
115         * The following example brings up the call log, showing only missed calls.
116         * <pre>
117         * Intent intent = new Intent(Intent.ACTION_VIEW);
118         * intent.setType(CallLog.Calls.CONTENT_TYPE);
119         * intent.putExtra(CallLog.Calls.EXTRA_CALL_TYPE_FILTER, CallLog.Calls.MISSED_TYPE);
120         * startActivity(intent);
121         * </pre>
122         * </p>
123         */
124        public static final String EXTRA_CALL_TYPE_FILTER =
125                "android.provider.extra.CALL_TYPE_FILTER";
126
127        /**
128         * Content uri used to access call log entries, including voicemail records. You must have
129         * the READ_CALL_LOG and WRITE_CALL_LOG permissions to read and write to the call log, as
130         * well as READ_VOICEMAIL and WRITE_VOICEMAIL permissions to read and write voicemails.
131         */
132        public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon()
133                .appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true")
134                .build();
135
136        /**
137         * The default sort order for this table
138         */
139        public static final String DEFAULT_SORT_ORDER = "date DESC";
140
141        /**
142         * The MIME type of {@link #CONTENT_URI} and {@link #CONTENT_FILTER_URI}
143         * providing a directory of calls.
144         */
145        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/calls";
146
147        /**
148         * The MIME type of a {@link #CONTENT_URI} sub-directory of a single
149         * call.
150         */
151        public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls";
152
153        /**
154         * The type of the call (incoming, outgoing or missed).
155         * <P>Type: INTEGER (int)</P>
156         */
157        public static final String TYPE = "type";
158
159        /** Call log type for incoming calls. */
160        public static final int INCOMING_TYPE = 1;
161        /** Call log type for outgoing calls. */
162        public static final int OUTGOING_TYPE = 2;
163        /** Call log type for missed calls. */
164        public static final int MISSED_TYPE = 3;
165        /** Call log type for voicemails. */
166        public static final int VOICEMAIL_TYPE = 4;
167
168        /**
169         * Bit-mask describing features of the call (e.g. video).
170         *
171         * <P>Type: INTEGER (int)</P>
172         */
173        public static final String FEATURES = "features";
174
175        /** Call had video. */
176        public static final int FEATURES_VIDEO = 0x1;
177
178        /**
179         * The phone number as the user entered it.
180         * <P>Type: TEXT</P>
181         */
182        public static final String NUMBER = "number";
183
184        /**
185         * The number presenting rules set by the network.
186         *
187         * <p>
188         * Allowed values:
189         * <ul>
190         * <li>{@link #PRESENTATION_ALLOWED}</li>
191         * <li>{@link #PRESENTATION_RESTRICTED}</li>
192         * <li>{@link #PRESENTATION_UNKNOWN}</li>
193         * <li>{@link #PRESENTATION_PAYPHONE}</li>
194         * </ul>
195         * </p>
196         *
197         * <P>Type: INTEGER</P>
198         */
199        public static final String NUMBER_PRESENTATION = "presentation";
200
201        /** Number is allowed to display for caller id. */
202        public static final int PRESENTATION_ALLOWED = 1;
203        /** Number is blocked by user. */
204        public static final int PRESENTATION_RESTRICTED = 2;
205        /** Number is not specified or unknown by network. */
206        public static final int PRESENTATION_UNKNOWN = 3;
207        /** Number is a pay phone. */
208        public static final int PRESENTATION_PAYPHONE = 4;
209
210        /**
211         * The ISO 3166-1 two letters country code of the country where the
212         * user received or made the call.
213         * <P>
214         * Type: TEXT
215         * </P>
216         */
217        public static final String COUNTRY_ISO = "countryiso";
218
219        /**
220         * The date the call occured, in milliseconds since the epoch
221         * <P>Type: INTEGER (long)</P>
222         */
223        public static final String DATE = "date";
224
225        /**
226         * The duration of the call in seconds
227         * <P>Type: INTEGER (long)</P>
228         */
229        public static final String DURATION = "duration";
230
231        /**
232         * The data usage of the call in bytes.
233         * <P>Type: INTEGER (long)</P>
234         */
235        public static final String DATA_USAGE = "data_usage";
236
237        /**
238         * Whether or not the call has been acknowledged
239         * <P>Type: INTEGER (boolean)</P>
240         */
241        public static final String NEW = "new";
242
243        /**
244         * The cached name associated with the phone number, if it exists.
245         * This value is not guaranteed to be current, if the contact information
246         * associated with this number has changed.
247         * <P>Type: TEXT</P>
248         */
249        public static final String CACHED_NAME = "name";
250
251        /**
252         * The cached number type (Home, Work, etc) associated with the
253         * phone number, if it exists.
254         * This value is not guaranteed to be current, if the contact information
255         * associated with this number has changed.
256         * <P>Type: INTEGER</P>
257         */
258        public static final String CACHED_NUMBER_TYPE = "numbertype";
259
260        /**
261         * The cached number label, for a custom number type, associated with the
262         * phone number, if it exists.
263         * This value is not guaranteed to be current, if the contact information
264         * associated with this number has changed.
265         * <P>Type: TEXT</P>
266         */
267        public static final String CACHED_NUMBER_LABEL = "numberlabel";
268
269        /**
270         * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}.
271         * <P>Type: TEXT</P>
272         */
273        public static final String VOICEMAIL_URI = "voicemail_uri";
274
275        /**
276         * Transcription of the call or voicemail entry. This will only be populated for call log
277         * entries of type {@link #VOICEMAIL_TYPE} that have valid transcriptions.
278         */
279        public static final String TRANSCRIPTION = "transcription";
280
281        /**
282         * Whether this item has been read or otherwise consumed by the user.
283         * <p>
284         * Unlike the {@link #NEW} field, which requires the user to have acknowledged the
285         * existence of the entry, this implies the user has interacted with the entry.
286         * <P>Type: INTEGER (boolean)</P>
287         */
288        public static final String IS_READ = "is_read";
289
290        /**
291         * A geocoded location for the number associated with this call.
292         * <p>
293         * The string represents a city, state, or country associated with the number.
294         * <P>Type: TEXT</P>
295         */
296        public static final String GEOCODED_LOCATION = "geocoded_location";
297
298        /**
299         * The cached URI to look up the contact associated with the phone number, if it exists.
300         * This value may not be current if the contact information associated with this number
301         * has changed.
302         * <P>Type: TEXT</P>
303         */
304        public static final String CACHED_LOOKUP_URI = "lookup_uri";
305
306        /**
307         * The cached phone number of the contact which matches this entry, if it exists.
308         * This value may not be current if the contact information associated with this number
309         * has changed.
310         * <P>Type: TEXT</P>
311         */
312        public static final String CACHED_MATCHED_NUMBER = "matched_number";
313
314        /**
315         * The cached normalized(E164) version of the phone number, if it exists.
316         * This value may not be current if the contact information associated with this number
317         * has changed.
318         * <P>Type: TEXT</P>
319         */
320        public static final String CACHED_NORMALIZED_NUMBER = "normalized_number";
321
322        /**
323         * The cached photo id of the picture associated with the phone number, if it exists.
324         * This value may not be current if the contact information associated with this number
325         * has changed.
326         * <P>Type: INTEGER (long)</P>
327         */
328        public static final String CACHED_PHOTO_ID = "photo_id";
329
330        /**
331         * The cached phone number, formatted with formatting rules based on the country the
332         * user was in when the call was made or received.
333         * This value is not guaranteed to be present, and may not be current if the contact
334         * information associated with this number
335         * has changed.
336         * <P>Type: TEXT</P>
337         */
338        public static final String CACHED_FORMATTED_NUMBER = "formatted_number";
339
340        // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming
341        // that was encoded into call log databases.
342
343        /**
344         * The component name of the account used to place or receive the call; in string form.
345         * <P>Type: TEXT</P>
346         */
347        public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
348
349        /**
350         * The identifier for the account used to place or receive the call.
351         * <P>Type: TEXT</P>
352         */
353        public static final String PHONE_ACCOUNT_ID = "subscription_id";
354
355        /**
356         * The address associated with the account used to place or receive the call; in string
357         * form. For SIM-based calls, this is the user's own phone number.
358         * <P>Type: TEXT</P>
359         *
360         * @hide
361         */
362        public static final String PHONE_ACCOUNT_ADDRESS = "phone_account_address";
363
364        /**
365         * Indicates that the entry will be hidden from all queries until the associated
366         * {@link android.telecom.PhoneAccount} is registered with the system.
367         * <P>Type: INTEGER</P>
368         *
369         * @hide
370         */
371        public static final String PHONE_ACCOUNT_HIDDEN = "phone_account_hidden";
372
373        /**
374         * The subscription ID used to place this call.  This is no longer used and has been
375         * replaced with PHONE_ACCOUNT_COMPONENT_NAME/PHONE_ACCOUNT_ID.
376         * For ContactsProvider internal use only.
377         * <P>Type: INTEGER</P>
378         *
379         * @Deprecated
380         * @hide
381         */
382        public static final String SUB_ID = "sub_id";
383
384        /**
385         * If a successful call is made that is longer than this duration, update the phone number
386         * in the ContactsProvider with the normalized version of the number, based on the user's
387         * current country code.
388         */
389        private static final int MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS = 1000 * 10;
390
391        /**
392         * Adds a call to the call log.
393         *
394         * @param ci the CallerInfo object to get the target contact from.  Can be null
395         * if the contact is unknown.
396         * @param context the context used to get the ContentResolver
397         * @param number the phone number to be added to the calls db
398         * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
399         *        is set by the network and denotes the number presenting rules for
400         *        "allowed", "payphone", "restricted" or "unknown"
401         * @param callType enumerated values for "incoming", "outgoing", or "missed"
402         * @param features features of the call (e.g. Video).
403         * @param accountHandle The accountHandle object identifying the provider of the call
404         * @param start time stamp for the call in milliseconds
405         * @param duration call duration in seconds
406         * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
407         *                  the call.
408         * @result The URI of the call log entry belonging to the user that made or received this
409         *        call.
410         * {@hide}
411         */
412        public static Uri addCall(CallerInfo ci, Context context, String number,
413                int presentation, int callType, int features, PhoneAccountHandle accountHandle,
414                long start, int duration, Long dataUsage) {
415            return addCall(ci, context, number, presentation, callType, features, accountHandle,
416                    start, duration, dataUsage, false);
417        }
418
419
420        /**
421         * Adds a call to the call log.
422         *
423         * @param ci the CallerInfo object to get the target contact from.  Can be null
424         * if the contact is unknown.
425         * @param context the context used to get the ContentResolver
426         * @param number the phone number to be added to the calls db
427         * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
428         *        is set by the network and denotes the number presenting rules for
429         *        "allowed", "payphone", "restricted" or "unknown"
430         * @param callType enumerated values for "incoming", "outgoing", or "missed"
431         * @param features features of the call (e.g. Video).
432         * @param accountHandle The accountHandle object identifying the provider of the call
433         * @param start time stamp for the call in milliseconds
434         * @param duration call duration in seconds
435         * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
436         *                  the call.
437         * @param addForAllUsers If true, the call is added to the call log of all currently
438         *        running users. The caller must have the MANAGE_USERS permission if this is true.
439         *
440         * @result The URI of the call log entry belonging to the user that made or received this
441         *        call.
442         * {@hide}
443         */
444        public static Uri addCall(CallerInfo ci, Context context, String number,
445                int presentation, int callType, int features, PhoneAccountHandle accountHandle,
446                long start, int duration, Long dataUsage, boolean addForAllUsers) {
447            final ContentResolver resolver = context.getContentResolver();
448            int numberPresentation = PRESENTATION_ALLOWED;
449            boolean isHidden = false;
450
451            TelecomManager tm = null;
452            try {
453                tm = TelecomManager.from(context);
454            } catch (UnsupportedOperationException e) {}
455
456            String accountAddress = null;
457            if (tm != null && accountHandle != null) {
458                PhoneAccount account = tm.getPhoneAccount(accountHandle);
459                if (account != null) {
460                    Uri address = account.getSubscriptionAddress();
461                    if (address != null) {
462                        accountAddress = address.getSchemeSpecificPart();
463                    }
464                } else {
465                    // We could not find the account through telecom. For call log entries that
466                    // are added with a phone account which is not registered, we automatically
467                    // mark them as hidden. They are unhidden once the account is registered.
468                    Log.i(LOG_TAG, "Marking call log entry as hidden.");
469                    isHidden = true;
470                }
471            }
472
473            // Remap network specified number presentation types
474            // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
475            // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
476            // from any future radio changes.
477            // If the number field is empty set the presentation type to Unknown.
478            if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
479                numberPresentation = PRESENTATION_RESTRICTED;
480            } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
481                numberPresentation = PRESENTATION_PAYPHONE;
482            } else if (TextUtils.isEmpty(number)
483                    || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
484                numberPresentation = PRESENTATION_UNKNOWN;
485            }
486            if (numberPresentation != PRESENTATION_ALLOWED) {
487                number = "";
488                if (ci != null) {
489                    ci.name = "";
490                }
491            }
492
493            // accountHandle information
494            String accountComponentString = null;
495            String accountId = null;
496            if (accountHandle != null) {
497                accountComponentString = accountHandle.getComponentName().flattenToString();
498                accountId = accountHandle.getId();
499            }
500
501            ContentValues values = new ContentValues(6);
502
503            values.put(NUMBER, number);
504            values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
505            values.put(TYPE, Integer.valueOf(callType));
506            values.put(FEATURES, features);
507            values.put(DATE, Long.valueOf(start));
508            values.put(DURATION, Long.valueOf(duration));
509            if (dataUsage != null) {
510                values.put(DATA_USAGE, dataUsage);
511            }
512            values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
513            values.put(PHONE_ACCOUNT_ID, accountId);
514            values.put(PHONE_ACCOUNT_ADDRESS, accountAddress);
515            values.put(PHONE_ACCOUNT_HIDDEN, Integer.valueOf(isHidden ? 1 : 0));
516            values.put(NEW, Integer.valueOf(1));
517
518            if (callType == MISSED_TYPE) {
519                values.put(IS_READ, Integer.valueOf(0));
520            }
521            if (ci != null) {
522                values.put(CACHED_NAME, ci.name);
523                values.put(CACHED_NUMBER_TYPE, ci.numberType);
524                values.put(CACHED_NUMBER_LABEL, ci.numberLabel);
525            }
526
527            if ((ci != null) && (ci.contactIdOrZero > 0)) {
528                // Update usage information for the number associated with the contact ID.
529                // We need to use both the number and the ID for obtaining a data ID since other
530                // contacts may have the same number.
531
532                final Cursor cursor;
533
534                // We should prefer normalized one (probably coming from
535                // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
536                if (ci.normalizedNumber != null) {
537                    final String normalizedPhoneNumber = ci.normalizedNumber;
538                    cursor = resolver.query(Phone.CONTENT_URI,
539                            new String[] { Phone._ID },
540                            Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
541                            new String[] { String.valueOf(ci.contactIdOrZero),
542                                    normalizedPhoneNumber},
543                            null);
544                } else {
545                    final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
546                    cursor = resolver.query(
547                            Uri.withAppendedPath(Callable.CONTENT_FILTER_URI,
548                                    Uri.encode(phoneNumber)),
549                            new String[] { Phone._ID },
550                            Phone.CONTACT_ID + " =?",
551                            new String[] { String.valueOf(ci.contactIdOrZero) },
552                            null);
553                }
554
555                if (cursor != null) {
556                    try {
557                        if (cursor.getCount() > 0 && cursor.moveToFirst()) {
558                            final String dataId = cursor.getString(0);
559                            updateDataUsageStatForData(resolver, dataId);
560                            if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS
561                                    && callType == Calls.OUTGOING_TYPE
562                                    && TextUtils.isEmpty(ci.normalizedNumber)) {
563                                updateNormalizedNumber(context, resolver, dataId, number);
564                            }
565                        }
566                    } finally {
567                        cursor.close();
568                    }
569                }
570            }
571
572            Uri result = null;
573
574            if (addForAllUsers) {
575                // Insert the entry for all currently running users, in order to trigger any
576                // ContentObservers currently set on the call log.
577                final UserManager userManager = (UserManager) context.getSystemService(
578                        Context.USER_SERVICE);
579                List<UserInfo> users = userManager.getUsers(true);
580                final int currentUserId = userManager.getUserHandle();
581                final int count = users.size();
582                for (int i = 0; i < count; i++) {
583                    final UserInfo user = users.get(i);
584                    final UserHandle userHandle = user.getUserHandle();
585                    if (userManager.isUserRunning(userHandle)
586                            && !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
587                                    userHandle)
588                            && !user.isManagedProfile()) {
589                        Uri uri = addEntryAndRemoveExpiredEntries(context,
590                                ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values);
591                        if (user.id == currentUserId) {
592                            result = uri;
593                        }
594                    }
595                }
596            } else {
597                result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values);
598            }
599
600            return result;
601        }
602
603        /**
604         * Query the call log database for the last dialed number.
605         * @param context Used to get the content resolver.
606         * @return The last phone number dialed (outgoing) or an empty
607         * string if none exist yet.
608         */
609        public static String getLastOutgoingCall(Context context) {
610            final ContentResolver resolver = context.getContentResolver();
611            Cursor c = null;
612            try {
613                c = resolver.query(
614                    CONTENT_URI,
615                    new String[] {NUMBER},
616                    TYPE + " = " + OUTGOING_TYPE,
617                    null,
618                    DEFAULT_SORT_ORDER + " LIMIT 1");
619                if (c == null || !c.moveToFirst()) {
620                    return "";
621                }
622                return c.getString(0);
623            } finally {
624                if (c != null) c.close();
625            }
626        }
627
628        private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri,
629                ContentValues values) {
630            final ContentResolver resolver = context.getContentResolver();
631            Uri result = resolver.insert(uri, values);
632            resolver.delete(uri, "_id IN " +
633                    "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
634                    + " LIMIT -1 OFFSET 500)", null);
635            return result;
636        }
637
638        private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) {
639            final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
640                    .appendPath(dataId)
641                    .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
642                                DataUsageFeedback.USAGE_TYPE_CALL)
643                    .build();
644            resolver.update(feedbackUri, new ContentValues(), null, null);
645        }
646
647        /*
648         * Update the normalized phone number for the given dataId in the ContactsProvider, based
649         * on the user's current country.
650         */
651        private static void updateNormalizedNumber(Context context, ContentResolver resolver,
652                String dataId, String number) {
653            if (TextUtils.isEmpty(number) || TextUtils.isEmpty(dataId)) {
654                return;
655            }
656            final String countryIso = getCurrentCountryIso(context);
657            if (TextUtils.isEmpty(countryIso)) {
658                return;
659            }
660            final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number,
661                    getCurrentCountryIso(context));
662            if (TextUtils.isEmpty(normalizedNumber)) {
663                return;
664            }
665            final ContentValues values = new ContentValues();
666            values.put(Phone.NORMALIZED_NUMBER, normalizedNumber);
667            resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId});
668        }
669
670        private static String getCurrentCountryIso(Context context) {
671            String countryIso = null;
672            final CountryDetector detector = (CountryDetector) context.getSystemService(
673                    Context.COUNTRY_DETECTOR);
674            if (detector != null) {
675                final Country country = detector.detectCountry();
676                if (country != null) {
677                    countryIso = country.getCountryIso();
678                }
679            }
680            return countryIso;
681        }
682    }
683}
684