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