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 number presenting rules set by the network.
    141          *
    142          * <p>
    143          * Allowed values:
    144          * <ul>
    145          * <li>{@link #PRESENTATION_ALLOWED}</li>
    146          * <li>{@link #PRESENTATION_RESTRICTED}</li>
    147          * <li>{@link #PRESENTATION_UNKNOWN}</li>
    148          * <li>{@link #PRESENTATION_PAYPHONE}</li>
    149          * </ul>
    150          * </p>
    151          *
    152          * <P>Type: INTEGER</P>
    153          */
    154         public static final String NUMBER_PRESENTATION = "presentation";
    155 
    156         /** Number is allowed to display for caller id. */
    157         public static final int PRESENTATION_ALLOWED = 1;
    158         /** Number is blocked by user. */
    159         public static final int PRESENTATION_RESTRICTED = 2;
    160         /** Number is not specified or unknown by network. */
    161         public static final int PRESENTATION_UNKNOWN = 3;
    162         /** Number is a pay phone. */
    163         public static final int PRESENTATION_PAYPHONE = 4;
    164 
    165         /**
    166          * The ISO 3166-1 two letters country code of the country where the
    167          * user received or made the call.
    168          * <P>
    169          * Type: TEXT
    170          * </P>
    171          *
    172          * @hide
    173          */
    174         public static final String COUNTRY_ISO = "countryiso";
    175 
    176         /**
    177          * The date the call occured, in milliseconds since the epoch
    178          * <P>Type: INTEGER (long)</P>
    179          */
    180         public static final String DATE = "date";
    181 
    182         /**
    183          * The duration of the call in seconds
    184          * <P>Type: INTEGER (long)</P>
    185          */
    186         public static final String DURATION = "duration";
    187 
    188         /**
    189          * Whether or not the call has been acknowledged
    190          * <P>Type: INTEGER (boolean)</P>
    191          */
    192         public static final String NEW = "new";
    193 
    194         /**
    195          * The cached name associated with the phone number, if it exists.
    196          * This value is not guaranteed to be current, if the contact information
    197          * associated with this number has changed.
    198          * <P>Type: TEXT</P>
    199          */
    200         public static final String CACHED_NAME = "name";
    201 
    202         /**
    203          * The cached number type (Home, Work, etc) associated with the
    204          * phone number, if it exists.
    205          * This value is not guaranteed to be current, if the contact information
    206          * associated with this number has changed.
    207          * <P>Type: INTEGER</P>
    208          */
    209         public static final String CACHED_NUMBER_TYPE = "numbertype";
    210 
    211         /**
    212          * The cached number label, for a custom number type, associated with the
    213          * phone number, if it exists.
    214          * This value is not guaranteed to be current, if the contact information
    215          * associated with this number has changed.
    216          * <P>Type: TEXT</P>
    217          */
    218         public static final String CACHED_NUMBER_LABEL = "numberlabel";
    219 
    220         /**
    221          * URI of the voicemail entry. Populated only for {@link #VOICEMAIL_TYPE}.
    222          * <P>Type: TEXT</P>
    223          * @hide
    224          */
    225         public static final String VOICEMAIL_URI = "voicemail_uri";
    226 
    227         /**
    228          * Whether this item has been read or otherwise consumed by the user.
    229          * <p>
    230          * Unlike the {@link #NEW} field, which requires the user to have acknowledged the
    231          * existence of the entry, this implies the user has interacted with the entry.
    232          * <P>Type: INTEGER (boolean)</P>
    233          */
    234         public static final String IS_READ = "is_read";
    235 
    236         /**
    237          * A geocoded location for the number associated with this call.
    238          * <p>
    239          * The string represents a city, state, or country associated with the number.
    240          * <P>Type: TEXT</P>
    241          * @hide
    242          */
    243         public static final String GEOCODED_LOCATION = "geocoded_location";
    244 
    245         /**
    246          * The cached URI to look up the contact associated with the phone number, if it exists.
    247          * This value is not guaranteed to be current, if the contact information
    248          * associated with this number has changed.
    249          * <P>Type: TEXT</P>
    250          * @hide
    251          */
    252         public static final String CACHED_LOOKUP_URI = "lookup_uri";
    253 
    254         /**
    255          * The cached phone number of the contact which matches this entry, if it exists.
    256          * This value is not guaranteed to be current, if the contact information
    257          * associated with this number has changed.
    258          * <P>Type: TEXT</P>
    259          * @hide
    260          */
    261         public static final String CACHED_MATCHED_NUMBER = "matched_number";
    262 
    263         /**
    264          * The cached normalized version of the phone number, if it exists.
    265          * This value is not guaranteed to be current, if the contact information
    266          * associated with this number has changed.
    267          * <P>Type: TEXT</P>
    268          * @hide
    269          */
    270         public static final String CACHED_NORMALIZED_NUMBER = "normalized_number";
    271 
    272         /**
    273          * The cached photo id of the picture associated with the phone number, if it exists.
    274          * This value is not guaranteed to be current, if the contact information
    275          * associated with this number has changed.
    276          * <P>Type: INTEGER (long)</P>
    277          * @hide
    278          */
    279         public static final String CACHED_PHOTO_ID = "photo_id";
    280 
    281         /**
    282          * The cached formatted phone number.
    283          * This value is not guaranteed to be present.
    284          * <P>Type: TEXT</P>
    285          * @hide
    286          */
    287         public static final String CACHED_FORMATTED_NUMBER = "formatted_number";
    288 
    289         /**
    290          * Adds a call to the call log.
    291          *
    292          * @param ci the CallerInfo object to get the target contact from.  Can be null
    293          * if the contact is unknown.
    294          * @param context the context used to get the ContentResolver
    295          * @param number the phone number to be added to the calls db
    296          * @param presentation enum value from PhoneConstants.PRESENTATION_xxx, which
    297          *        is set by the network and denotes the number presenting rules for
    298          *        "allowed", "payphone", "restricted" or "unknown"
    299          * @param callType enumerated values for "incoming", "outgoing", or "missed"
    300          * @param start time stamp for the call in milliseconds
    301          * @param duration call duration in seconds
    302          *
    303          * {@hide}
    304          */
    305         public static Uri addCall(CallerInfo ci, Context context, String number,
    306                 int presentation, int callType, long start, int duration) {
    307             final ContentResolver resolver = context.getContentResolver();
    308             int numberPresentation = PRESENTATION_ALLOWED;
    309 
    310             // Remap network specified number presentation types
    311             // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
    312             // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
    313             // from any future radio changes.
    314             // If the number field is empty set the presentation type to Unknown.
    315             if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
    316                 numberPresentation = PRESENTATION_RESTRICTED;
    317             } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
    318                 numberPresentation = PRESENTATION_PAYPHONE;
    319             } else if (TextUtils.isEmpty(number)
    320                     || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
    321                 numberPresentation = PRESENTATION_UNKNOWN;
    322             }
    323             if (numberPresentation != PRESENTATION_ALLOWED) {
    324                 number = "";
    325                 if (ci != null) {
    326                     ci.name = "";
    327                 }
    328             }
    329 
    330             ContentValues values = new ContentValues(6);
    331 
    332             values.put(NUMBER, number);
    333             values.put(NUMBER_PRESENTATION, Integer.valueOf(numberPresentation));
    334             values.put(TYPE, Integer.valueOf(callType));
    335             values.put(DATE, Long.valueOf(start));
    336             values.put(DURATION, Long.valueOf(duration));
    337             values.put(NEW, Integer.valueOf(1));
    338             if (callType == MISSED_TYPE) {
    339                 values.put(IS_READ, Integer.valueOf(0));
    340             }
    341             if (ci != null) {
    342                 values.put(CACHED_NAME, ci.name);
    343                 values.put(CACHED_NUMBER_TYPE, ci.numberType);
    344                 values.put(CACHED_NUMBER_LABEL, ci.numberLabel);
    345             }
    346 
    347             if ((ci != null) && (ci.person_id > 0)) {
    348                 // Update usage information for the number associated with the contact ID.
    349                 // We need to use both the number and the ID for obtaining a data ID since other
    350                 // contacts may have the same number.
    351 
    352                 final Cursor cursor;
    353 
    354                 // We should prefer normalized one (probably coming from
    355                 // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
    356                 if (ci.normalizedNumber != null) {
    357                     final String normalizedPhoneNumber = ci.normalizedNumber;
    358                     cursor = resolver.query(Phone.CONTENT_URI,
    359                             new String[] { Phone._ID },
    360                             Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
    361                             new String[] { String.valueOf(ci.person_id), normalizedPhoneNumber},
    362                             null);
    363                 } else {
    364                     final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
    365                     cursor = resolver.query(
    366                             Uri.withAppendedPath(Callable.CONTENT_FILTER_URI,
    367                                     Uri.encode(phoneNumber)),
    368                             new String[] { Phone._ID },
    369                             Phone.CONTACT_ID + " =?",
    370                             new String[] { String.valueOf(ci.person_id) },
    371                             null);
    372                 }
    373 
    374                 if (cursor != null) {
    375                     try {
    376                         if (cursor.getCount() > 0 && cursor.moveToFirst()) {
    377                             final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
    378                                     .appendPath(cursor.getString(0))
    379                                     .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
    380                                                 DataUsageFeedback.USAGE_TYPE_CALL)
    381                                     .build();
    382                             resolver.update(feedbackUri, new ContentValues(), null, null);
    383                         }
    384                     } finally {
    385                         cursor.close();
    386                     }
    387                 }
    388             }
    389 
    390             Uri result = resolver.insert(CONTENT_URI, values);
    391 
    392             removeExpiredEntries(context);
    393 
    394             return result;
    395         }
    396 
    397         /**
    398          * Query the call log database for the last dialed number.
    399          * @param context Used to get the content resolver.
    400          * @return The last phone number dialed (outgoing) or an empty
    401          * string if none exist yet.
    402          */
    403         public static String getLastOutgoingCall(Context context) {
    404             final ContentResolver resolver = context.getContentResolver();
    405             Cursor c = null;
    406             try {
    407                 c = resolver.query(
    408                     CONTENT_URI,
    409                     new String[] {NUMBER},
    410                     TYPE + " = " + OUTGOING_TYPE,
    411                     null,
    412                     DEFAULT_SORT_ORDER + " LIMIT 1");
    413                 if (c == null || !c.moveToFirst()) {
    414                     return "";
    415                 }
    416                 return c.getString(0);
    417             } finally {
    418                 if (c != null) c.close();
    419             }
    420         }
    421 
    422         private static void removeExpiredEntries(Context context) {
    423             final ContentResolver resolver = context.getContentResolver();
    424             resolver.delete(CONTENT_URI, "_id IN " +
    425                     "(SELECT _id FROM calls ORDER BY " + DEFAULT_SORT_ORDER
    426                     + " LIMIT -1 OFFSET 500)", null);
    427         }
    428     }
    429 }
    430