Home | History | Annotate | Download | only in presence
      1 /*
      2  * Copyright (c) 2015, Motorola Mobility LLC
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *     - Redistributions of source code must retain the above copyright
      8  *       notice, this list of conditions and the following disclaimer.
      9  *     - Redistributions in binary form must reproduce the above copyright
     10  *       notice, this list of conditions and the following disclaimer in the
     11  *       documentation and/or other materials provided with the distribution.
     12  *     - Neither the name of Motorola Mobility nor the
     13  *       names of its contributors may be used to endorse or promote products
     14  *       derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
     26  * DAMAGE.
     27  */
     28 
     29 package com.android.service.ims.presence;
     30 
     31 import android.content.Context;
     32 import android.content.ContentResolver;
     33 import android.content.ContentUris;
     34 import android.content.ContentValues;
     35 import android.content.Context;
     36 import android.database.Cursor;
     37 import android.database.CursorWrapper;
     38 import android.database.Cursor;
     39 import android.database.DatabaseUtils;
     40 import android.net.Uri;
     41 import android.text.format.Time;
     42 import android.text.TextUtils;
     43 
     44 import com.android.ims.internal.EABContract;
     45 import com.android.ims.internal.ContactNumberUtils;
     46 import com.android.ims.RcsPresenceInfo;
     47 import com.android.ims.internal.Logger;
     48 
     49 import java.util.ArrayList;
     50 import java.util.List;
     51 
     52 public class EABContactManager {
     53     private Logger logger = Logger.getLogger(this.getClass().getName());
     54 
     55     /**
     56      * An identifier for a particular EAB contact number, unique across the system.
     57      * Clients use this ID to make subsequent calls related to the contact.
     58      */
     59     public final static String COLUMN_ID = Contacts.Impl._ID;
     60 
     61     /**
     62      * Timestamp when the presence was last updated, in {@link System#currentTimeMillis
     63      * System.currentTimeMillis()} (wall clock time in UTC).
     64      */
     65     public final static String COLUMN_LAST_UPDATED_TIMESTAMP =
     66             Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP;
     67 
     68     /**
     69      * columns to request from EABProvider.
     70      * @hide
     71      */
     72     public static final String[] CONTACT_COLUMNS = new String[] {
     73         Contacts.Impl._ID,
     74         Contacts.Impl.CONTACT_NUMBER,
     75         Contacts.Impl.CONTACT_NAME,
     76         Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP,
     77         Contacts.Impl.VOLTE_CALL_SERVICE_CONTACT_ADDRESS,
     78         Contacts.Impl.VOLTE_CALL_CAPABILITY,
     79         Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP,
     80         Contacts.Impl.VOLTE_CALL_AVAILABILITY,
     81         Contacts.Impl.VOLTE_CALL_AVAILABILITY_TIMESTAMP,
     82         Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS,
     83         Contacts.Impl.VIDEO_CALL_CAPABILITY,
     84         Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP,
     85         Contacts.Impl.VIDEO_CALL_AVAILABILITY,
     86         Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP,
     87         Contacts.Impl.VOLTE_STATUS
     88     };
     89 
     90     /**
     91      * Look up the formatted number and Data ID
     92      */
     93     private static final String[] DATA_QUERY_PROJECTION = new String[] {
     94         Contacts.Impl._ID,
     95         Contacts.Impl.FORMATTED_NUMBER,
     96         EABContract.EABColumns.DATA_ID
     97     };
     98     // Data Query Columns, which match the DATA_QUERY_PROJECTION
     99     private static final int DATA_QUERY_ID = 0;
    100     private static final int DATA_QUERY_FORMATTED_NUMBER = 1;
    101     private static final int DATA_QUERY_DATA_ID = 2;
    102 
    103 
    104     /**
    105      * This class contains all the information necessary to request a new contact.
    106      */
    107     public static class Request {
    108         private long mId = -1;
    109         private String mContactNumber = null;
    110         private String mContactName = null;
    111 
    112         private int mVolteCallCapability = -1;
    113         private long mVolteCallCapabilityTimeStamp = -1;
    114         private int mVolteCallAvailability = -1;
    115         private long mVolteCallAvailabilityTimeStamp = -1;
    116         private String mVolteCallServiceContactAddress = null;
    117 
    118         private int mVideoCallCapability = -1;
    119         private long mVideoCallCapabilityTimeStamp = -1;
    120         private int mVideoCallAvailability = -1;
    121         private long mVideoCallAvailabilityTimeStamp = -1;
    122         private String mVideoCallServiceContactAddress = null;
    123 
    124         private long mContactLastUpdatedTimeStamp = -1;
    125         private int mFieldUpdatedFlags = 0;
    126 
    127         private static int sVolteCallCapabilityFlag = 0x0001;
    128         private static int sVolteCallCapabilityTimeStampFlag = 0x0002;
    129         private static int sVolteCallAvailabilityFlag = 0x0004;
    130         private static int sVolteCallAvailabilityTimeStampFlag = 0x0008;
    131         private static int sVolteCallServiceContactAddressFlag = 0x0010;
    132         private static int sVideoCallCapabilityFlag = 0x0020;
    133         private static int sVideoCallCapabilityTimeStampFlag = 0x0040;
    134         private static int sVideoCallAvailabilityFlag = 0x0080;
    135         private static int sVideoCallAvailabilityTimeStampFlag = 0x0100;
    136         private static int sVideoCallServiceContactAddressFlag = 0x0200;
    137         private static int sContactLastUpdatedTimeStampFlag = 0x0400;
    138 
    139         /**
    140          * @param id the contact id.
    141          */
    142         public Request(long id) {
    143             if (id < 0) {
    144                 throw new IllegalArgumentException(
    145                         "Can't update EAB presence item with id: " + id);
    146             }
    147 
    148             mId = id;
    149         }
    150 
    151         public Request(String number) {
    152             if (TextUtils.isEmpty(number)) {
    153                 throw new IllegalArgumentException(
    154                         "Can't update EAB presence item with number: " + number);
    155             }
    156 
    157             mContactNumber = number;
    158         }
    159 
    160         public long getContactId() {
    161             return mId;
    162         }
    163 
    164         public String getContactNumber() {
    165             return mContactNumber;
    166         }
    167 
    168         /**
    169          * Set Volte call service contact address.
    170          * @param address contact from NOTIFY
    171          * @return this object
    172          */
    173         public Request setVolteCallServiceContactAddress(String address) {
    174             mVolteCallServiceContactAddress = address;
    175             mFieldUpdatedFlags |= sVolteCallServiceContactAddressFlag;
    176             return this;
    177         }
    178 
    179         /**
    180          * Set Volte call capability.
    181          * @param b wheter volte call is supported or not
    182          * @return this object
    183          */
    184         public Request setVolteCallCapability(boolean b) {
    185             mVolteCallCapability = b ? 1 : 0;
    186             mFieldUpdatedFlags |= sVolteCallCapabilityFlag;
    187             return this;
    188         }
    189 
    190         public Request setVolteCallCapability(int val) {
    191             mVolteCallCapability = val;
    192             mFieldUpdatedFlags |= sVolteCallCapabilityFlag;
    193             return this;
    194         }
    195 
    196         /**
    197          * Set Volte call availability.
    198          * @param b wheter volte call is available or not
    199          * @return this object
    200          */
    201         public Request setVolteCallAvailability(boolean b) {
    202             mVolteCallAvailability = b ? 1 : 0;
    203             mFieldUpdatedFlags |= sVolteCallAvailabilityFlag;
    204             return this;
    205         }
    206 
    207         public Request setVolteCallAvailability(int val) {
    208             mVolteCallAvailability = val;
    209             mFieldUpdatedFlags |= sVolteCallAvailabilityFlag;
    210             return this;
    211         }
    212 
    213         /**
    214          * Set Video call service contact address.
    215          * @param address contact from NOTIFY.
    216          * @return this object
    217          */
    218         public Request setVideoCallServiceContactAddress(String address) {
    219             mVideoCallServiceContactAddress = address;
    220             mFieldUpdatedFlags |= sVideoCallServiceContactAddressFlag;
    221             return this;
    222         }
    223 
    224         /**
    225          * Set Video call capability.
    226          * @param b wheter volte call is supported or not
    227          * @return this object
    228          */
    229         public Request setVideoCallCapability(boolean b) {
    230             mVideoCallCapability = b ? 1 : 0;
    231             mFieldUpdatedFlags |= sVideoCallCapabilityFlag;
    232             return this;
    233         }
    234 
    235         public Request setVideoCallCapability(int val) {
    236             mVideoCallCapability = val;
    237             mFieldUpdatedFlags |= sVideoCallCapabilityFlag;
    238             return this;
    239         }
    240 
    241         /**
    242          * Set Video call availability.
    243          * @param b wheter volte call is available or not
    244          * @return this object
    245          */
    246         public Request setVideoCallAvailability(boolean b) {
    247             mVideoCallAvailability = b ? 1 : 0;
    248             mFieldUpdatedFlags |= sVideoCallAvailabilityFlag;
    249             return this;
    250         }
    251 
    252         public Request setVideoCallAvailability(int val) {
    253             mVideoCallAvailability = val;
    254             mFieldUpdatedFlags |= sVideoCallAvailabilityFlag;
    255             return this;
    256         }
    257 
    258         /**
    259          * Set the update timestamp.
    260          * @param long timestamp the last update timestamp
    261          * @return this object
    262          */
    263         public Request setLastUpdatedTimeStamp(long timestamp) {
    264             mContactLastUpdatedTimeStamp = timestamp;
    265             mFieldUpdatedFlags |= sContactLastUpdatedTimeStampFlag;
    266             return this;
    267         }
    268 
    269         public Request setVolteCallCapabilityTimeStamp(long timestamp) {
    270             mVolteCallCapabilityTimeStamp = timestamp;
    271             mFieldUpdatedFlags |= sVolteCallCapabilityTimeStampFlag;
    272             return this;
    273         }
    274 
    275         public Request setVolteCallAvailabilityTimeStamp(long timestamp) {
    276             mVolteCallAvailabilityTimeStamp = timestamp;
    277             mFieldUpdatedFlags |= sVolteCallAvailabilityTimeStampFlag;
    278             return this;
    279         }
    280 
    281         public Request setVideoCallCapabilityTimeStamp(long timestamp) {
    282             mVideoCallCapabilityTimeStamp = timestamp;
    283             mFieldUpdatedFlags |= sVideoCallCapabilityTimeStampFlag;
    284             return this;
    285         }
    286 
    287         public Request setVideoCallAvailabilityTimeStamp(long timestamp) {
    288             mVideoCallAvailabilityTimeStamp = timestamp;
    289             mFieldUpdatedFlags |= sVideoCallAvailabilityTimeStampFlag;
    290             return this;
    291         }
    292 
    293         public Request reset() {
    294             mVolteCallCapability = -1;
    295             mVolteCallCapabilityTimeStamp = -1;
    296             mVolteCallAvailability = -1;
    297             mVolteCallAvailabilityTimeStamp = -1;
    298             mVolteCallServiceContactAddress = null;
    299 
    300             mVideoCallCapability = -1;
    301             mVideoCallCapabilityTimeStamp = -1;
    302             mVideoCallAvailability = -1;
    303             mVideoCallAvailabilityTimeStamp = -1;
    304             mVideoCallServiceContactAddress = null;
    305 
    306             mContactLastUpdatedTimeStamp = -1;
    307             mFieldUpdatedFlags = 0;
    308             return this;
    309         }
    310 
    311         /**
    312          * @return ContentValues to be passed to EABProvider.update()
    313          */
    314         ContentValues toContentValues() {
    315             ContentValues values = new ContentValues();
    316 
    317             if ((mFieldUpdatedFlags & sVolteCallCapabilityFlag) > 0) {
    318                 values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY,
    319                         mVolteCallCapability);
    320             }
    321             if ((mFieldUpdatedFlags & sVolteCallCapabilityTimeStampFlag) > 0) {
    322                 values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP,
    323                         mVolteCallCapabilityTimeStamp);
    324             }
    325             if ((mFieldUpdatedFlags & sVolteCallAvailabilityFlag) > 0) {
    326                 values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY,
    327                         mVolteCallAvailability);
    328             }
    329             if ((mFieldUpdatedFlags & sVolteCallAvailabilityTimeStampFlag) > 0) {
    330                 values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY_TIMESTAMP,
    331                         mVolteCallAvailabilityTimeStamp);
    332             }
    333             if ((mFieldUpdatedFlags & sVolteCallServiceContactAddressFlag) > 0) {
    334                 values.put(Contacts.Impl.VOLTE_CALL_SERVICE_CONTACT_ADDRESS,
    335                         mVolteCallServiceContactAddress);
    336             }
    337 
    338             if ((mFieldUpdatedFlags & sVideoCallCapabilityFlag) > 0) {
    339                 values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY,
    340                         mVideoCallCapability);
    341             }
    342             if ((mFieldUpdatedFlags & sVideoCallCapabilityTimeStampFlag) > 0) {
    343                 values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP,
    344                         mVideoCallCapabilityTimeStamp);
    345             }
    346             if ((mFieldUpdatedFlags & sVideoCallAvailabilityFlag) > 0) {
    347                 values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY,
    348                         mVideoCallAvailability);
    349             }
    350             if ((mFieldUpdatedFlags & sVideoCallAvailabilityTimeStampFlag) > 0) {
    351                 values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP,
    352                         mVideoCallAvailabilityTimeStamp);
    353             }
    354             if ((mFieldUpdatedFlags & sVideoCallServiceContactAddressFlag) > 0) {
    355                 values.put(Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS,
    356                         mVideoCallServiceContactAddress);
    357             }
    358 
    359             if ((mFieldUpdatedFlags & sContactLastUpdatedTimeStampFlag) > 0 ) {
    360                 values.put(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP,
    361                         mContactLastUpdatedTimeStamp);
    362             }
    363 
    364             return values;
    365         }
    366 
    367         @Override
    368         public String toString() {
    369             StringBuilder sb = new StringBuilder(512);
    370             sb.append("EABContactManager.Request { ");
    371             if (mId != -1) {
    372                 sb.append("\nId: " + mId);
    373             }
    374             if (!TextUtils.isEmpty(mContactNumber)) {
    375                 sb.append("\nContact Number: " + mContactNumber);
    376             }
    377             if (!TextUtils.isEmpty(mContactName)) {
    378                 sb.append("\nContact Name: " + mContactName);
    379             }
    380 
    381             if ((mFieldUpdatedFlags & sVolteCallCapabilityFlag) > 0) {
    382                 sb.append("\nVolte call capability: " + mVolteCallCapability);
    383             }
    384             if ((mFieldUpdatedFlags & sVolteCallCapabilityTimeStampFlag) > 0) {
    385                 sb.append("\nVolte call capability timestamp: " + mVolteCallCapabilityTimeStamp
    386                         + "(" + getTimeString(mVolteCallCapabilityTimeStamp) + ")");
    387             }
    388             if ((mFieldUpdatedFlags & sVolteCallAvailabilityFlag) > 0) {
    389                 sb.append("\nVolte call availability: " + mVolteCallAvailability);
    390             }
    391             if ((mFieldUpdatedFlags & sVolteCallAvailabilityTimeStampFlag) > 0) {
    392                 sb.append("\nVolte call availablity timestamp: " + mVolteCallAvailabilityTimeStamp
    393                         + "(" + getTimeString(mVolteCallAvailabilityTimeStamp) + ")");
    394             }
    395             if ((mFieldUpdatedFlags & sVolteCallServiceContactAddressFlag) > 0) {
    396                 sb.append("\nVolte Call Service address: " + mVolteCallServiceContactAddress);
    397             }
    398 
    399             if ((mFieldUpdatedFlags & sVideoCallCapabilityFlag) > 0) {
    400                 sb.append("\nVideo call capability: " + mVideoCallCapability);
    401             }
    402             if ((mFieldUpdatedFlags & sVideoCallCapabilityTimeStampFlag) > 0) {
    403                 sb.append("\nVideo call capability timestamp: " + mVideoCallCapabilityTimeStamp
    404                         + "(" + getTimeString(mVideoCallCapabilityTimeStamp) + ")");
    405             }
    406             if ((mFieldUpdatedFlags & sVideoCallAvailabilityFlag) > 0) {
    407                 sb.append("\nVideo call availability: " + mVideoCallAvailability);
    408             }
    409             if ((mFieldUpdatedFlags & sVideoCallAvailabilityTimeStampFlag) > 0) {
    410                 sb.append("\nVideo call availablity timestamp: " + mVideoCallAvailabilityTimeStamp
    411                         + "(" + getTimeString(mVideoCallAvailabilityTimeStamp) + ")");
    412             }
    413             if ((mFieldUpdatedFlags & sVideoCallServiceContactAddressFlag) > 0) {
    414                 sb.append("\nVideo Call Service address: " + mVideoCallServiceContactAddress);
    415             }
    416 
    417             if ((mFieldUpdatedFlags & sContactLastUpdatedTimeStampFlag) > 0 ) {
    418                 sb.append("\nContact last update time: " + mContactLastUpdatedTimeStamp
    419                         + "(" + getTimeString(mContactLastUpdatedTimeStamp) + ")");
    420             }
    421 
    422             sb.append(" }");
    423             return sb.toString();
    424         }
    425     }
    426 
    427     /**
    428      * This class may be used to filter EABProvider queries.
    429      */
    430     public static class Query {
    431         /**
    432          * Constant for use with {@link #orderBy}
    433          * @hide
    434          */
    435         public static final int ORDER_ASCENDING = 1;
    436 
    437         /**
    438          * Constant for use with {@link #orderBy}
    439          * @hide
    440          */
    441         public static final int ORDER_DESCENDING = 2;
    442 
    443         private long[] mIds = null;
    444         private String mContactNumber = null;
    445         private List<String> mTimeFilters = null;
    446         private String mOrderByColumn = COLUMN_LAST_UPDATED_TIMESTAMP;
    447         private int mOrderDirection = ORDER_ASCENDING;
    448 
    449         /**
    450          * Include only the contacts with the given IDs.
    451          * @return this object
    452          */
    453         public Query setFilterById(long... ids) {
    454             mIds = ids;
    455             return this;
    456         }
    457 
    458         /**
    459          * Include only the contacts with the given number.
    460          * @return this object
    461          */
    462         public Query setFilterByNumber(String number) {
    463             mContactNumber = number;
    464             return this;
    465         }
    466 
    467         /**
    468          * Include the contacts that meet the specified time condition.
    469          * @return this object
    470          */
    471         public Query setFilterByTime(String selection) {
    472             if (mTimeFilters == null) {
    473                 mTimeFilters = new ArrayList<String>();
    474             }
    475 
    476             mTimeFilters.add(selection);
    477             return this;
    478         }
    479 
    480         /**
    481          * Include only the contacts that has not been updated before the last time.
    482          * @return this object
    483          */
    484         public Query setFilterByTime(String column, long last) {
    485             if (mTimeFilters == null) {
    486                 mTimeFilters = new ArrayList<String>();
    487             }
    488 
    489             mTimeFilters.add(column + "<='" + last + "'");
    490             return this;
    491         }
    492 
    493         /**
    494          * Include only the contacts that has not been updated after the eariest time.
    495          * @return this object
    496          */
    497         public Query setFilterByEarliestTime(String column, long earliest) {
    498             if (mTimeFilters == null) {
    499                 mTimeFilters = new ArrayList<String>();
    500             }
    501 
    502             mTimeFilters.add(column + ">='" + earliest + "'");
    503             return this;
    504         }
    505 
    506         /**
    507          * Change the sort order of the returned Cursor.
    508          *
    509          * @param column one of the COLUMN_* constants; currently, only
    510          *         {@link #COLUMN_LAST_MODIFIED_TIMESTAMP} and {@link #COLUMN_TOTAL_SIZE_BYTES} are
    511          *         supported.
    512          * @param direction either {@link #ORDER_ASCENDING} or {@link #ORDER_DESCENDING}
    513          * @return this object
    514          * @hide
    515          */
    516         public Query orderBy(String column, int direction) {
    517             if (direction != ORDER_ASCENDING && direction != ORDER_DESCENDING) {
    518                 throw new IllegalArgumentException("Invalid direction: " + direction);
    519             }
    520 
    521             if (column.equals(COLUMN_ID)) {
    522                 mOrderByColumn = Contacts.Impl._ID;
    523             } else if (column.equals(COLUMN_LAST_UPDATED_TIMESTAMP)) {
    524                 mOrderByColumn = Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP;
    525             } else {
    526                 throw new IllegalArgumentException("Cannot order by " + column);
    527             }
    528             mOrderDirection = direction;
    529             return this;
    530         }
    531 
    532         /**
    533          * Run this query using the given ContentResolver.
    534          * @param projection the projection to pass to ContentResolver.query()
    535          * @return the Cursor returned by ContentResolver.query()
    536          */
    537         Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) {
    538             Uri uri = baseUri;
    539             List<String> selectionParts = new ArrayList<String>();
    540             String[] selectionArgs = null;
    541 
    542             if (mIds != null) {
    543                 selectionParts.add(getWhereClauseForIds(mIds));
    544                 selectionArgs = getWhereArgsForIds(mIds);
    545             }
    546 
    547             if (!TextUtils.isEmpty(mContactNumber)) {
    548                 String number = mContactNumber;
    549                 if (number.startsWith("tel:")) {
    550                     number = number.substring(4);
    551                 }
    552                 String escapedPhoneNumber = DatabaseUtils.sqlEscapeString(number);
    553                 String cselection = "(" + Contacts.Impl.CONTACT_NUMBER + "=" + escapedPhoneNumber;
    554                 cselection += " OR PHONE_NUMBERS_EQUAL(" + Contacts.Impl.CONTACT_NUMBER + ", ";
    555                 cselection += escapedPhoneNumber + ", 0))";
    556 
    557                 selectionParts.add(cselection);
    558             }
    559 
    560             if (mTimeFilters != null) {
    561                 String cselection = joinStrings(" OR ", mTimeFilters);
    562                 int size = mTimeFilters.size();
    563                 if (size > 1) {
    564                     cselection = "(" + cselection + ")";
    565                 }
    566                 selectionParts.add(cselection);
    567             }
    568 
    569             String selection = joinStrings(" AND ", selectionParts);
    570             String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC" : "DESC");
    571             String orderBy = mOrderByColumn + " " + orderDirection;
    572 
    573             return resolver.query(uri, projection, selection, selectionArgs, orderBy);
    574         }
    575 
    576         private String joinStrings(String joiner, Iterable<String> parts) {
    577             StringBuilder builder = new StringBuilder();
    578             boolean first = true;
    579             for (String part : parts) {
    580                 if (!first) {
    581                     builder.append(joiner);
    582                 }
    583                 builder.append(part);
    584                 first = false;
    585             }
    586             return builder.toString();
    587         }
    588 
    589         @Override
    590         public String toString() {
    591             List<String> selectionParts = new ArrayList<String>();
    592             String[] selectionArgs = null;
    593 
    594             if (mIds != null) {
    595                 selectionParts.add(getWhereClauseForIds(mIds));
    596                 selectionArgs = getWhereArgsForIds(mIds);
    597             }
    598 
    599             if (!TextUtils.isEmpty(mContactNumber)) {
    600                 String number = mContactNumber;
    601                 if (number.startsWith("tel:")) {
    602                     number = number.substring(4);
    603                 }
    604                 String escapedPhoneNumber = DatabaseUtils.sqlEscapeString(number);
    605                 String cselection = "(" + Contacts.Impl.CONTACT_NUMBER + "=" + escapedPhoneNumber;
    606                 cselection += " OR PHONE_NUMBERS_EQUAL(" + Contacts.Impl.CONTACT_NUMBER + ", ";
    607                 cselection += escapedPhoneNumber + ", 0))";
    608 
    609                 selectionParts.add(cselection);
    610             }
    611 
    612             if (mTimeFilters != null) {
    613                 String cselection = joinStrings(" OR ", mTimeFilters);
    614                 int size = mTimeFilters.size();
    615                 if (size > 1) {
    616                     cselection = "(" + cselection + ")";
    617                 }
    618                 selectionParts.add(cselection);
    619             }
    620 
    621             String selection = joinStrings(" AND ", selectionParts);
    622             String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC" : "DESC");
    623             String orderBy = mOrderByColumn + " " + orderDirection;
    624 
    625             StringBuilder sb = new StringBuilder(512);
    626             sb.append("EABContactManager.Query { ");
    627             sb.append("\nSelection: " + selection);
    628             sb.append("\nSelectionArgs: " + selectionArgs);
    629             sb.append("\nOrderBy: " + orderBy);
    630             sb.append(" }");
    631             return sb.toString();
    632         }
    633     }
    634 
    635     private ContentResolver mResolver;
    636     private String mPackageName;
    637     private Uri mBaseUri = Contacts.Impl.CONTENT_URI;
    638 
    639     /**
    640      * @hide
    641      */
    642     public EABContactManager(ContentResolver resolver, String packageName) {
    643         mResolver = resolver;
    644         mPackageName = packageName;
    645     }
    646 
    647     /**
    648      * Query the presence manager about contacts that have been requested.
    649      * @param query parameters specifying filters for this query
    650      * @return a Cursor over the result set of contacts, with columns consisting of all the
    651      * COLUMN_* constants.
    652      */
    653     public Cursor query(Query query) {
    654         Cursor underlyingCursor = query.runQuery(mResolver, CONTACT_COLUMNS, mBaseUri);
    655         if (underlyingCursor == null) {
    656             return null;
    657         }
    658 
    659         return new CursorTranslator(underlyingCursor, mBaseUri);
    660     }
    661 
    662     /**
    663      * Update a contact presence status.
    664      *
    665      * @param request the parameters specifying this contact presence
    666      * @return an ID for the contact, unique across the system.  This ID is used to make future
    667      * calls related to this contact.
    668      */
    669     public int update(Request request) {
    670         if (request == null) {
    671             return 0;
    672         }
    673 
    674         long id = request.getContactId();
    675         String number = request.getContactNumber();
    676         if ((id == -1) && TextUtils.isEmpty(number)) {
    677             throw new IllegalArgumentException("invalid request for contact update.");
    678         }
    679 
    680         ContentValues values = request.toContentValues();
    681         if (id != -1) {
    682             logger.debug("Update contact " + id + " with request: " + values);
    683             return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values,
    684                     null, null);
    685         } else {
    686             Query query = new Query().setFilterByNumber(number)
    687                                      .orderBy(COLUMN_ID, Query.ORDER_ASCENDING);
    688             long[] ids = null;
    689             Cursor cursor = null;
    690             try {
    691                 cursor = query(query);
    692                 if (cursor == null) {
    693                     return 0;
    694                 }
    695                 int count = cursor.getCount();
    696                 if (count == 0) {
    697                     return 0;
    698                 }
    699 
    700                 ids = new long[count];
    701                 int idx = 0;
    702                 for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
    703                     id = cursor.getLong(cursor.getColumnIndex(Contacts.Impl._ID));
    704                     ids[idx++] = id;
    705                     if (idx >= count) {
    706                         break;
    707                     }
    708                 }
    709             } finally {
    710                 if (cursor != null) {
    711                     cursor.close();
    712                 }
    713             }
    714 
    715             if ((ids == null) || (ids.length == 0)) {
    716                 return 0;
    717             }
    718 
    719             if (ids.length == 1) {
    720                 logger.debug("Update contact " + ids[0] + " with request: " + values);
    721                 return mResolver.update(ContentUris.withAppendedId(mBaseUri, ids[0]), values,
    722                         null, null);
    723             }
    724 
    725             logger.debug("Update contact " + number + " with request: " + values);
    726             return mResolver.update(mBaseUri, values, getWhereClauseForIds(ids),
    727                     getWhereArgsForIds(ids));
    728         }
    729     }
    730 
    731     /**
    732      * Get the EABProvider URI for the contact with the given ID.
    733      *
    734      * @hide
    735      */
    736     public Uri getContactUri(long id) {
    737         return ContentUris.withAppendedId(mBaseUri, id);
    738     }
    739 
    740     /**
    741      * Get a parameterized SQL WHERE clause to select a bunch of IDs.
    742      */
    743     static String getWhereClauseForIds(long[] ids) {
    744         StringBuilder whereClause = new StringBuilder();
    745         whereClause.append("(");
    746         for (int i = 0; i < ids.length; i++) {
    747             if (i > 0) {
    748                 whereClause.append("OR ");
    749             }
    750             whereClause.append(COLUMN_ID);
    751             whereClause.append(" = ? ");
    752         }
    753         whereClause.append(")");
    754         return whereClause.toString();
    755     }
    756 
    757     /**
    758      * Get the selection args for a clause returned by {@link #getWhereClauseForIds(long[])}.
    759      */
    760     static String[] getWhereArgsForIds(long[] ids) {
    761         String[] whereArgs = new String[ids.length];
    762         for (int i = 0; i < ids.length; i++) {
    763             whereArgs[i] = Long.toString(ids[i]);
    764         }
    765         return whereArgs;
    766     }
    767 
    768     static String getTimeString(long time) {
    769         if (time <= 0) {
    770             time = System.currentTimeMillis();
    771         }
    772 
    773         Time tobj = new Time();
    774         tobj.set(time);
    775         return String.format("%s.%s", tobj.format("%m-%d %H:%M:%S"), time % 1000);
    776     }
    777 
    778     /**
    779      * This class wraps a cursor returned by EABProvider -- the "underlying cursor" -- and
    780      * presents a different set of columns, those defined in the COLUMN_* constants.
    781      * Some columns correspond directly to underlying values while others are computed from
    782      * underlying data.
    783      */
    784     private static class CursorTranslator extends CursorWrapper {
    785         private Uri mBaseUri;
    786 
    787         public CursorTranslator(Cursor cursor, Uri baseUri) {
    788             super(cursor);
    789             mBaseUri = baseUri;
    790         }
    791 
    792         @Override
    793         public int getInt(int columnIndex) {
    794             return (int) getLong(columnIndex);
    795         }
    796 
    797         @Override
    798         public long getLong(int columnIndex) {
    799             return super.getLong(columnIndex);
    800         }
    801 
    802         @Override
    803         public String getString(int columnIndex) {
    804             return super.getString(columnIndex);
    805         }
    806     }
    807 
    808     public void updateAllCapabilityToUnknown() {
    809         if (mResolver == null) {
    810             logger.error("updateAllCapabilityToUnknown, mResolver=null");
    811             return;
    812         }
    813 
    814         ContentValues values = new ContentValues();
    815         values.put(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP, (String)null);
    816         values.put(Contacts.Impl.VOLTE_CALL_SERVICE_CONTACT_ADDRESS, (String)null);
    817         values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY, (String)null);
    818         values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP, (String)null);
    819         values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY, (String)null);
    820         values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY_TIMESTAMP, (String)null);
    821 
    822         values.put(Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS, (String)null);
    823         values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY, (String)null);
    824         values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP, (String)null);
    825         values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY, (String)null);
    826         values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP, (String)null);
    827 
    828         try {
    829             int count = ContactDbUtil.resetVtCapability(mResolver);
    830             logger.print("update Contact DB: updateAllCapabilityToUnknown count=" + count);
    831 
    832             count = mResolver.update(Contacts.Impl.CONTENT_URI,
    833                     values, null, null);
    834             logger.print("update EAB DB: updateAllCapabilityToUnknown count=" + count);
    835         } catch (Exception ex) {
    836             logger.error("updateAllCapabilityToUnknown exception: " + ex);
    837         }
    838     }
    839 
    840     public void updateAllVtCapabilityToUnknown() {
    841         if (mResolver == null) {
    842             logger.error("updateAllVtCapabilityToUnknown mResolver=null");
    843             return;
    844         }
    845 
    846         ContentValues values = new ContentValues();
    847         values.put(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP, (String)null);
    848         values.put(Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS, (String)null);
    849         values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY, (String)null);
    850         values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP, (String)null);
    851         values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY, (String)null);
    852         values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP, (String)null);
    853 
    854         try {
    855             int count = ContactDbUtil.resetVtCapability(mResolver);
    856             logger.print("update Contact DB: updateAllVtCapabilityToUnknown count=" + count);
    857 
    858             count = mResolver.update(Contacts.Impl.CONTENT_URI,
    859                     values, null, null);
    860             logger.print("update EAB DB: updateAllVtCapabilityToUnknown count=" + count);
    861         } catch (Exception ex) {
    862             logger.error("updateAllVtCapabilityToUnknown exception: " + ex);
    863         }
    864     }
    865 
    866     // if updateLastTimestamp is true, the rcsPresenceInfo is from network.
    867     // if the updateLastTimestamp is false, It is used to update the availabilty to unknown only.
    868     // And the availability will be updated only when it has expired, so we don't update the
    869     // timestamp to make sure the availablity still in expired status and will be subscribed from
    870     // network afterwards.
    871     public int update(RcsPresenceInfo rcsPresenceInfo, boolean updateLastTimestamp) {
    872         if (rcsPresenceInfo == null) {
    873             return 0;
    874         }
    875 
    876         String number = rcsPresenceInfo.getContactNumber();
    877         if (TextUtils.isEmpty(number)) {
    878             logger.error("Failed to update for the contact number is empty.");
    879             return 0;
    880         }
    881 
    882         ContentValues values = new ContentValues();
    883 
    884         int volteStatus = rcsPresenceInfo.getVolteStatus();
    885         if(volteStatus != RcsPresenceInfo.VolteStatus.VOLTE_UNKNOWN) {
    886             values.put(Contacts.Impl.VOLTE_STATUS, volteStatus);
    887         }
    888 
    889         if(updateLastTimestamp){
    890             values.put(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP,
    891                     (long)System.currentTimeMillis());
    892         }
    893 
    894         int lteCallCapability = rcsPresenceInfo.getServiceState(
    895                 RcsPresenceInfo.ServiceType.VOLTE_CALL);
    896         long lteCallTimestamp = rcsPresenceInfo.getTimeStamp(
    897                 RcsPresenceInfo.ServiceType.VOLTE_CALL);
    898         values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY, lteCallCapability);
    899         values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY_TIMESTAMP, (long)lteCallTimestamp);
    900         if(rcsPresenceInfo.getServiceState(RcsPresenceInfo.ServiceType.VOLTE_CALL)
    901                 != RcsPresenceInfo.ServiceState.UNKNOWN){
    902             String lteCallContactAddress =
    903                     rcsPresenceInfo.getServiceContact(RcsPresenceInfo.ServiceType.VOLTE_CALL);
    904             if (!TextUtils.isEmpty(lteCallContactAddress)) {
    905                 values.put(Contacts.Impl.VOLTE_CALL_SERVICE_CONTACT_ADDRESS, lteCallContactAddress);
    906             }
    907 
    908             values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY, lteCallCapability);
    909             values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP, lteCallTimestamp);
    910         }
    911 
    912         int videoCallCapability = rcsPresenceInfo.getServiceState(
    913                 RcsPresenceInfo.ServiceType.VT_CALL);
    914         long videoCallTimestamp = rcsPresenceInfo.getTimeStamp(
    915                 RcsPresenceInfo.ServiceType.VT_CALL);
    916         values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY, videoCallCapability);
    917         values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP, (long)videoCallTimestamp);
    918         if(rcsPresenceInfo.getServiceState(RcsPresenceInfo.ServiceType.VT_CALL)
    919                 != RcsPresenceInfo.ServiceState.UNKNOWN){
    920             String videoCallContactAddress =
    921                     rcsPresenceInfo.getServiceContact(RcsPresenceInfo.ServiceType.VT_CALL);
    922             if (!TextUtils.isEmpty(videoCallContactAddress)) {
    923                 values.put(Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS,
    924                         videoCallContactAddress);
    925             }
    926 
    927             values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY, videoCallCapability);
    928             values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP, videoCallTimestamp);
    929         }
    930 
    931         int count = 0;
    932         Cursor cursor = null;
    933         try{
    934             cursor = mResolver.query(Contacts.Impl.CONTENT_URI, DATA_QUERY_PROJECTION,
    935                     "PHONE_NUMBERS_EQUAL(" + Contacts.Impl.FORMATTED_NUMBER + ", ?, 1)",
    936                     new String[] {number}, null);
    937             if(cursor == null) {
    938                 logger.print("update rcsPresenceInfo to DB: update count=" + count);
    939                 return count;
    940             }
    941 
    942             ContactNumberUtils contactNumberUtils = ContactNumberUtils.getDefault();
    943             for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
    944                 String numberInDB = cursor.getString(DATA_QUERY_FORMATTED_NUMBER);
    945                 logger.debug("number=" + number + " numberInDB=" + numberInDB +
    946                         " formatedNumber in DB=" + contactNumberUtils.format(numberInDB));
    947                 if(number.equals(contactNumberUtils.format(numberInDB))) {
    948                     count = ContactDbUtil.updateVtCapability(mResolver,
    949                             cursor.getLong(DATA_QUERY_DATA_ID),
    950                             (videoCallCapability == RcsPresenceInfo.ServiceState.ONLINE));
    951                     logger.print("update rcsPresenceInfo to Contact DB, count=" + count);
    952 
    953                     int id = cursor.getInt(DATA_QUERY_ID);
    954                     count += mResolver.update(Contacts.Impl.CONTENT_URI, values,
    955                             Contacts.Impl._ID + "=" + id, null);
    956                     logger.debug("count=" + count);
    957                 }
    958             }
    959 
    960             logger.print("update rcsPresenceInfo to DB: update count=" + count +
    961                     " rcsPresenceInfo=" + rcsPresenceInfo);
    962         } catch(Exception e){
    963             logger.error("updateCapability exception");
    964         } finally {
    965             if(cursor != null) {
    966                 cursor.close();
    967                 cursor = null;
    968             }
    969         }
    970 
    971         return count;
    972     }
    973 }
    974 
    975