OSDN Git Service

am fe5e7e92: Merge "docs: Fix issue with onCreate() method declaration in file backup...
[android-x86/frameworks-base.git] / core / java / android / provider / CallLog.java
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
18 package android.provider;
19
20 import android.content.ContentProvider;
21 import android.content.ContentResolver;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.pm.UserInfo;
26 import android.database.Cursor;
27 import android.location.Country;
28 import android.location.CountryDetector;
29 import android.net.Uri;
30 import android.os.UserHandle;
31 import android.os.UserManager;
32 import android.provider.ContactsContract.CommonDataKinds.Callable;
33 import android.provider.ContactsContract.CommonDataKinds.Phone;
34 import android.provider.ContactsContract.Data;
35 import android.provider.ContactsContract.DataUsageFeedback;
36 import android.telecom.PhoneAccountHandle;
37 import android.telephony.PhoneNumberUtils;
38 import android.text.TextUtils;
39
40 import com.android.internal.telephony.CallerInfo;
41 import com.android.internal.telephony.PhoneConstants;
42
43 import java.util.List;
44
45 /**
46  * The CallLog provider contains information about placed and received calls.
47  */
48 public class CallLog {
49     public static final String AUTHORITY = "call_log";
50
51     /**
52      * The content:// style URL for this provider
53      */
54     public static final Uri CONTENT_URI =
55         Uri.parse("content://" + AUTHORITY);
56
57     /**
58      * Contains the recent calls.
59      */
60     public static class Calls implements BaseColumns {
61         /**
62          * The content:// style URL for this table
63          */
64         public static final Uri CONTENT_URI =
65                 Uri.parse("content://call_log/calls");
66
67         /**
68          * The content:// style URL for filtering this table on phone numbers
69          */
70         public static final Uri CONTENT_FILTER_URI =
71                 Uri.parse("content://call_log/calls/filter");
72
73         /**
74          * Query parameter used to limit the number of call logs returned.
75          * <p>
76          * TYPE: integer
77          */
78         public static final String LIMIT_PARAM_KEY = "limit";
79
80         /**
81          * Query parameter used to specify the starting record to return.
82          * <p>
83          * TYPE: integer
84          */
85         public static final String OFFSET_PARAM_KEY = "offset";
86
87         /**
88          * An optional URI parameter which instructs the provider to allow the operation to be
89          * applied to voicemail records as well.
90          * <p>
91          * TYPE: Boolean
92          * <p>
93          * Using this parameter with a value of {@code true} will result in a security error if the
94          * calling package does not have appropriate permissions to access voicemails.
95          *
96          * @hide
97          */
98         public static final String ALLOW_VOICEMAILS_PARAM_KEY = "allow_voicemails";
99
100         /**
101          * An optional extra used with {@link #CONTENT_TYPE Calls.CONTENT_TYPE} and
102          * {@link Intent#ACTION_VIEW} to specify that the presented list of calls should be
103          * filtered for a particular call type.
104          *
105          * Applications implementing a call log UI should check for this extra, and display a
106          * filtered list of calls based on the specified call type. If not applicable within the
107          * application's UI, it should be silently ignored.
108          *
109          * <p>
110          * The following example brings up the call log, showing only missed calls.
111          * <pre>
112          * Intent intent = new Intent(Intent.ACTION_VIEW);
113          * intent.setType(CallLog.Calls.CONTENT_TYPE);
114          * intent.putExtra(CallLog.Calls.EXTRA_CALL_TYPE_FILTER, CallLog.Calls.MISSED_TYPE);
115          * startActivity(intent);
116          * </pre>
117          * </p>
118          */
119         public static final String EXTRA_CALL_TYPE_FILTER =
120                 "android.provider.extra.CALL_TYPE_FILTER";
121
122         /**
123          * Content uri used to access call log entries, including voicemail records. You must have
124          * the READ_CALL_LOG and WRITE_CALL_LOG permissions to read and write to the call log, as
125          * well as READ_VOICEMAIL and WRITE_VOICEMAIL permissions to read and write voicemails.
126          */
127         public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon()
128                 .appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true")
129                 .build();
130
131         /**
132          * The default sort order for this table
133          */
134         public static final String DEFAULT_SORT_ORDER = "date DESC";
135
136         /**
137          * The MIME type of {@link #CONTENT_URI} and {@link #CONTENT_FILTER_URI}
138          * providing a directory of calls.
139          */
140         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/calls";
141
142         /**
143          * The MIME type of a {@link #CONTENT_URI} sub-directory of a single
144          * call.
145          */
146         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/calls";
147
148         /**
149          * The type of the call (incoming, outgoing or missed).
150          * <P>Type: INTEGER (int)</P>
151          */
152         public static final String TYPE = "type";
153
154         /** Call log type for incoming calls. */
155         public static final int INCOMING_TYPE = 1;
156         /** Call log type for outgoing calls. */
157         public static final int OUTGOING_TYPE = 2;
158         /** Call log type for missed calls. */
159         public static final int MISSED_TYPE = 3;
160         /** Call log type for voicemails. */
161         public static final int VOICEMAIL_TYPE = 4;
162
163         /**
164          * Bit-mask describing features of the call (e.g. video).
165          *
166          * <P>Type: INTEGER (int)</P>
167          */
168         public static final String FEATURES = "features";
169
170         /** Call had video. */
171         public static final int FEATURES_VIDEO = 0x1;
172
173         /**
174          * The phone number as the user entered it.
175          * <P>Type: TEXT</P>
176          */
177         public static final String NUMBER = "number";
178
179         /**
180          * The number presenting rules set by the network.
181          *
182          * <p>
183          * Allowed values:
184          * <ul>
185          * <li>{@link #PRESENTATION_ALLOWED}</li>
186          * <li>{@link #PRESENTATION_RESTRICTED}</li>
187          * <li>{@link #PRESENTATION_UNKNOWN}</li>
188          * <li>{@link #PRESENTATION_PAYPHONE}</li>
189          * </ul>
190          * </p>
191          *
192          * <P>Type: INTEGER</P>
193          */
194         public static final String NUMBER_PRESENTATION = "presentation";
195
196         /** Number is allowed to display for caller id. */
197         public static final int PRESENTATION_ALLOWED = 1;
198         /** Number is blocked by user. */
199         public static final int PRESENTATION_RESTRICTED = 2;
200         /** Number is not specified or unknown by network. */
201         public static final int PRESENTATION_UNKNOWN = 3;
202         /** Number is a pay phone. */
203         public static final int PRESENTATION_PAYPHONE = 4;
204
205         /**
206          * The ISO 3166-1 two letters country code of the country where the
207          * user received or made the call.
208          * <P>
209          * Type: TEXT
210          * </P>
211          */
212         public static final String COUNTRY_ISO = "countryiso";
213
214         /**
215          * The date the call occured, in milliseconds since the epoch
216          * <P>Type: INTEGER (long)</P>
217          */
218         public static final String DATE = "date";
219
220         /**
221          * The duration of the call in seconds
222          * <P>Type: INTEGER (long)</P>
223          */
224         public static final String DURATION = "duration";
225
226         /**
227          * The data usage of the call in bytes.
228          * <P>Type: INTEGER (long)</P>
229          */
230         public static final String DATA_USAGE = "data_usage";
231
232         /**
233          * Whether or not the call has been acknowledged
234          * <P>Type: INTEGER (boolean)</P>
235          */
236         public static final String NEW = "new";
237
238         /**
239          * The cached name associated with the phone number, if it exists.
240          * This value is not guaranteed to be current, if the contact information
241          * associated with this number has changed.
242          * <P>Type: TEXT</P>
243          */
244         public static final String CACHED_NAME = "name";
245
246         /**
247          * The cached number type (Home, Work, etc) associated with the
248          * phone number, if it exists.
249          * This value is not guaranteed to be current, if the contact information
250          * associated with this number has changed.
251          * <P>Type: INTEGER</P>
252          */
253         public static final String CACHED_NUMBER_TYPE = "numbertype";
254
255         /**
256          * The cached number label, for a custom number type, associated with the
257          * phone number, if it exists.
258          * This value is not guaranteed to be current, if the contact information
259          * associated with this number has changed.
260          * <P>Type: TEXT</P>
261          */
262         public static final String CACHED_NUMBER_LABEL = "numberlabel";
263
264         /**
265          * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}.
266          * <P>Type: TEXT</P>
267          */
268         public static final String VOICEMAIL_URI = "voicemail_uri";
269
270         /**
271          * Transcription of the call or voicemail entry. This will only be populated for call log
272          * entries of type {@link #VOICEMAIL_TYPE} that have valid transcriptions.
273          */
274         public static final String TRANSCRIPTION = "transcription";
275
276         /**
277          * Whether this item has been read or otherwise consumed by the user.
278          * <p>
279          * Unlike the {@link #NEW} field, which requires the user to have acknowledged the
280          * existence of the entry, this implies the user has interacted with the entry.
281          * <P>Type: INTEGER (boolean)</P>
282          */
283         public static final String IS_READ = "is_read";
284
285         /**
286          * A geocoded location for the number associated with this call.
287          * <p>
288          * The string represents a city, state, or country associated with the number.
289          * <P>Type: TEXT</P>
290          */
291         public static final String GEOCODED_LOCATION = "geocoded_location";
292
293         /**
294          * The cached URI to look up the contact associated with the phone number, if it exists.
295          * This value may not be current if the contact information associated with this number
296          * has changed.
297          * <P>Type: TEXT</P>
298          */
299         public static final String CACHED_LOOKUP_URI = "lookup_uri";
300
301         /**
302          * The cached phone number of the contact which matches this entry, if it exists.
303          * This value may not be current if the contact information associated with this number
304          * has changed.
305          * <P>Type: TEXT</P>
306          */
307         public static final String CACHED_MATCHED_NUMBER = "matched_number";
308
309         /**
310          * The cached normalized(E164) version of the phone number, if it exists.
311          * This value may not be current if the contact information associated with this number
312          * has changed.
313          * <P>Type: TEXT</P>
314          */
315         public static final String CACHED_NORMALIZED_NUMBER = "normalized_number";
316
317         /**
318          * The cached photo id of the picture associated with the phone number, if it exists.
319          * This value may not be current if the contact information associated with this number
320          * has changed.
321          * <P>Type: INTEGER (long)</P>
322          */
323         public static final String CACHED_PHOTO_ID = "photo_id";
324
325         /**
326          * The cached phone number, formatted with formatting rules based on the country the
327          * user was in when the call was made or received.
328          * This value is not guaranteed to be present, and may not be current if the contact
329          * information associated with this number
330          * has changed.
331          * <P>Type: TEXT</P>
332          */
333         public static final String CACHED_FORMATTED_NUMBER = "formatted_number";
334
335         // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming
336         // that was encoded into call log databases.
337
338         /**
339          * The component name of the account in string form.
340          * <P>Type: TEXT</P>
341          */
342         public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
343
344         /**
345          * The identifier of a account that is unique to a specified component.
346          * <P>Type: TEXT</P>
347          */
348         public static final String PHONE_ACCOUNT_ID = "subscription_id";
349
350         /**
351          * The identifier of a account that is unique to a specified component. Equivalent value
352          * to {@link #PHONE_ACCOUNT_ID}. For ContactsProvider internal use only.
353          * <P>Type: INTEGER</P>
354          *
355          * @hide
356          */
357         public static final String SUB_ID = "sub_id";
358
359         /**
360          * If a successful call is made that is longer than this duration, update the phone number
361          * in the ContactsProvider with the normalized version of the number, based on the user's
362          * current country code.
363          */
364         private static final int MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS = 1000 * 10;
365
366         /**
367          * Adds a call to the call log.
368          *
369          * @param ci the CallerInfo object to get the target contact from.  Can be null
370          * if the contact is unknown.
371          * @param context the context used to get the ContentResolver
372          * @param number the phone number to be added to the calls db
373          * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
374          *        is set by the network and denotes the number presenting rules for
375          *        "allowed", "payphone", "restricted" or "unknown"
376          * @param callType enumerated values for "incoming", "outgoing", or "missed"
377          * @param features features of the call (e.g. Video).
378          * @param accountHandle The accountHandle object identifying the provider of the call
379          * @param start time stamp for the call in milliseconds
380          * @param duration call duration in seconds
381          * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
382          *                  the call.
383          * @result The URI of the call log entry belonging to the user that made or received this
384          *        call.
385          * {@hide}
386          */
387         public static Uri addCall(CallerInfo ci, Context context, String number,
388                 int presentation, int callType, int features, PhoneAccountHandle accountHandle,
389                 long start, int duration, Long dataUsage) {
390             return addCall(ci, context, number, presentation, callType, features, accountHandle,
391                     start, duration, dataUsage, false);
392         }
393
394         /**
395          * Adds a call to the call log.
396          *
397          * @param ci the CallerInfo object to get the target contact from.  Can be null
398          * if the contact is unknown.
399          * @param context the context used to get the ContentResolver
400          * @param number the phone number to be added to the calls db
401          * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
402          *        is set by the network and denotes the number presenting rules for
403          *        "allowed", "payphone", "restricted" or "unknown"
404          * @param callType enumerated values for "incoming", "outgoing", or "missed"
405          * @param features features of the call (e.g. Video).
406          * @param accountHandle The accountHandle object identifying the provider of the call
407          * @param start time stamp for the call in milliseconds
408          * @param duration call duration in seconds
409          * @param dataUsage data usage for the call in bytes, null if data usage was not tracked for
410          *                  the call.
411          * @param addForAllUsers If true, the call is added to the call log of all currently
412          *        running users. The caller must have the MANAGE_USERS permission if this is true.
413          *
414          * @result The URI of the call log entry belonging to the user that made or received this
415          *        call.
416          * {@hide}
417          */
418         public static Uri addCall(CallerInfo ci, Context context, String number,
419                 int presentation, int callType, int features, PhoneAccountHandle accountHandle,
420                 long start, int duration, Long dataUsage, boolean addForAllUsers) {
421             final ContentResolver resolver = context.getContentResolver();
422             int numberPresentation = PRESENTATION_ALLOWED;
423
424             // Remap network specified number presentation types
425             // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
426             // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
427             // from any future radio changes.
428             // If the number field is empty set the presentation type to Unknown.
429             if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
430                 numberPresentation = PRESENTATION_RESTRICTED;
431             } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
432                 numberPresentation = PRESENTATION_PAYPHONE;
433             } else if (TextUtils.isEmpty(number)
434                     || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
435                 numberPresentation = PRESENTATION_UNKNOWN;
436             }
437             if (numberPresentation != PRESENTATION_ALLOWED) {
438                 number = "";
439                 if (ci != null) {
440                     ci.name = "";
441                 }
442             }
443
444             // accountHandle information
445             String accountComponentString = null;
446             String accountId = null;
447             if (accountHandle != null) {
448                 accountComponentString = accountHandle.getComponentName().flattenToString();
449                 accountId = accountHandle.getId();
450             }
451
452             ContentValues values = new ContentValues(6);
453
454             values.put(NUMBER, number);
455             values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
456             values.put(TYPE, Integer.valueOf(callType));
457             values.put(FEATURES, features);
458             values.put(DATE, Long.valueOf(start));
459             values.put(DURATION, Long.valueOf(duration));
460             if (dataUsage != null) {
461                 values.put(DATA_USAGE, dataUsage);
462             }
463             values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
464             values.put(PHONE_ACCOUNT_ID, accountId);
465             values.put(NEW, Integer.valueOf(1));
466             if (callType == MISSED_TYPE) {
467                 values.put(IS_READ, Integer.valueOf(0));
468             }
469             if (ci != null) {
470                 values.put(CACHED_NAME, ci.name);
471                 values.put(CACHED_NUMBER_TYPE, ci.numberType);
472                 values.put(CACHED_NUMBER_LABEL, ci.numberLabel);
473             }
474
475             if ((ci != null) && (ci.contactIdOrZero > 0)) {
476                 // Update usage information for the number associated with the contact ID.
477                 // We need to use both the number and the ID for obtaining a data ID since other
478                 // contacts may have the same number.
479
480                 final Cursor cursor;
481
482                 // We should prefer normalized one (probably coming from
483                 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
484                 if (ci.normalizedNumber != null) {
485                     final String normalizedPhoneNumber = ci.normalizedNumber;
486                     cursor = resolver.query(Phone.CONTENT_URI,
487                             new String[] { Phone._ID },
488                             Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
489                             new String[] { String.valueOf(ci.contactIdOrZero),
490                                     normalizedPhoneNumber},
491                             null);
492                 } else {
493                     final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
494                     cursor = resolver.query(
495                             Uri.withAppendedPath(Callable.CONTENT_FILTER_URI,
496                                     Uri.encode(phoneNumber)),
497                             new String[] { Phone._ID },
498                             Phone.CONTACT_ID + " =?",
499                             new String[] { String.valueOf(ci.contactIdOrZero) },
500                             null);
501                 }
502
503                 if (cursor != null) {
504                     try {
505                         if (cursor.getCount() > 0 && cursor.moveToFirst()) {
506                             final String dataId = cursor.getString(0);
507                             updateDataUsageStatForData(resolver, dataId);
508                             if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS
509                                     && callType == Calls.OUTGOING_TYPE
510                                     && TextUtils.isEmpty(ci.normalizedNumber)) {
511                                 updateNormalizedNumber(context, resolver, dataId, number);
512                             }
513                         }
514                     } finally {
515                         cursor.close();
516                     }
517                 }
518             }
519
520             Uri result = null;
521
522             if (addForAllUsers) {
523                 // Insert the entry for all currently running users, in order to trigger any
524                 // ContentObservers currently set on the call log.
525                 final UserManager userManager = (UserManager) context.getSystemService(
526                         Context.USER_SERVICE);
527                 List<UserInfo> users = userManager.getUsers(true);
528                 final int currentUserId = userManager.getUserHandle();
529                 final int count = users.size();
530                 for (int i = 0; i < count; i++) {
531                     final UserInfo user = users.get(i);
532                     final UserHandle userHandle = user.getUserHandle();
533                     if (userManager.isUserRunning(userHandle)
534                             && !userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS,
535                                     userHandle)
536                             && !user.isManagedProfile()) {
537                         Uri uri = addEntryAndRemoveExpiredEntries(context,
538                                 ContentProvider.maybeAddUserId(CONTENT_URI, user.id), values);
539                         if (user.id == currentUserId) {
540                             result = uri;
541                         }
542                     }
543                 }
544             } else {
545                 result = addEntryAndRemoveExpiredEntries(context, CONTENT_URI, values);
546             }
547
548             return result;
549         }
550
551         /**
552          * Query the call log database for the last dialed number.
553          * @param context Used to get the content resolver.
554          * @return The last phone number dialed (outgoing) or an empty
555          * string if none exist yet.
556          */
557         public static String getLastOutgoingCall(Context context) {
558             final ContentResolver resolver = context.getContentResolver();
559             Cursor c = null;
560             try {
561                 c = resolver.query(
562                     CONTENT_URI,
563                     new String[] {NUMBER},
564                     TYPE + " = " + OUTGOING_TYPE,
565                     null,
566                     DEFAULT_SORT_ORDER + " LIMIT 1");
567                 if (c == null || !c.moveToFirst()) {
568                     return "";
569                 }
570                 return c.getString(0);
571             } finally {
572                 if (c != null) c.close();
573             }
574         }
575
576         private static Uri addEntryAndRemoveExpiredEntries(Context context, Uri uri,
577                 ContentValues values) {
578             final ContentResolver resolver = context.getContentResolver();
579             Uri result = resolver.insert(uri, values);
580             resolver.delete(uri, "_id IN " +
581                     "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
582                     + " LIMIT -1 OFFSET 500)", null);
583             return result;
584         }
585
586         private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) {
587             final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
588                     .appendPath(dataId)
589                     .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
590                                 DataUsageFeedback.USAGE_TYPE_CALL)
591                     .build();
592             resolver.update(feedbackUri, new ContentValues(), null, null);
593         }
594
595         /**
596          * Update the normalized phone number for the given dataId in the ContactsProvider, based
597          * on the user's current country.
598          */
599         private static void updateNormalizedNumber(Context context, ContentResolver resolver,
600                 String dataId, String number) {
601             if (TextUtils.isEmpty(number) || TextUtils.isEmpty(dataId)) {
602                 return;
603             }
604
605             final String countryIso = getCurrentCountryIso(context);
606             if (TextUtils.isEmpty(countryIso)) {
607                 return;
608             }
609
610             final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number,
611                     getCurrentCountryIso(context));
612             if (TextUtils.isEmpty(normalizedNumber)) {
613                 return;
614             }
615
616             final ContentValues values = new ContentValues();
617             values.put(Phone.NORMALIZED_NUMBER, normalizedNumber);
618             resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId});
619         }
620
621         private static String getCurrentCountryIso(Context context) {
622             String countryIso = null;
623             final CountryDetector detector = (CountryDetector) context.getSystemService(
624                     Context.COUNTRY_DETECTOR);
625             if (detector != null) {
626                 final Country country = detector.detectCountry();
627                 if (country != null) {
628                     countryIso = country.getCountryIso();
629                 }
630             }
631             return countryIso;
632         }
633     }
634 }