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