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