| /* |
| * Copyright (c) 2015, Motorola Mobility LLC |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * - Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * - Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * - Neither the name of Motorola Mobility nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
| * DAMAGE. |
| */ |
| |
| package com.android.service.ims.presence; |
| |
| import android.content.Context; |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.database.Cursor; |
| import android.database.CursorWrapper; |
| import android.database.Cursor; |
| import android.database.DatabaseUtils; |
| import android.net.Uri; |
| import android.text.format.TimeMigrationUtils; |
| import android.text.TextUtils; |
| |
| import com.android.ims.internal.EABContract; |
| import com.android.ims.internal.ContactNumberUtils; |
| import com.android.ims.RcsPresenceInfo; |
| import com.android.ims.internal.Logger; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| public class EABContactManager { |
| private Logger logger = Logger.getLogger(this.getClass().getName()); |
| |
| /** |
| * An identifier for a particular EAB contact number, unique across the system. |
| * Clients use this ID to make subsequent calls related to the contact. |
| */ |
| public final static String COLUMN_ID = Contacts.Impl._ID; |
| |
| /** |
| * Timestamp when the presence was last updated, in {@link System#currentTimeMillis |
| * System.currentTimeMillis()} (wall clock time in UTC). |
| */ |
| public final static String COLUMN_LAST_UPDATED_TIMESTAMP = |
| Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP; |
| |
| /** |
| * columns to request from EABProvider. |
| * @hide |
| */ |
| public static final String[] CONTACT_COLUMNS = new String[] { |
| Contacts.Impl._ID, |
| Contacts.Impl.CONTACT_NUMBER, |
| Contacts.Impl.CONTACT_NAME, |
| Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP, |
| Contacts.Impl.VOLTE_CALL_SERVICE_CONTACT_ADDRESS, |
| Contacts.Impl.VOLTE_CALL_CAPABILITY, |
| Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP, |
| Contacts.Impl.VOLTE_CALL_AVAILABILITY, |
| Contacts.Impl.VOLTE_CALL_AVAILABILITY_TIMESTAMP, |
| Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS, |
| Contacts.Impl.VIDEO_CALL_CAPABILITY, |
| Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP, |
| Contacts.Impl.VIDEO_CALL_AVAILABILITY, |
| Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP, |
| Contacts.Impl.VOLTE_STATUS |
| }; |
| |
| /** |
| * Look up the formatted number and Data ID |
| */ |
| private static final String[] DATA_QUERY_PROJECTION = new String[] { |
| Contacts.Impl._ID, |
| Contacts.Impl.FORMATTED_NUMBER, |
| EABContract.EABColumns.DATA_ID |
| }; |
| // Data Query Columns, which match the DATA_QUERY_PROJECTION |
| private static final int DATA_QUERY_ID = 0; |
| private static final int DATA_QUERY_FORMATTED_NUMBER = 1; |
| private static final int DATA_QUERY_DATA_ID = 2; |
| |
| |
| /** |
| * This class contains all the information necessary to request a new contact. |
| */ |
| public static class Request { |
| private long mId = -1; |
| private String mContactNumber = null; |
| private String mContactName = null; |
| |
| private int mVolteCallCapability = -1; |
| private long mVolteCallCapabilityTimeStamp = -1; |
| private int mVolteCallAvailability = -1; |
| private long mVolteCallAvailabilityTimeStamp = -1; |
| private String mVolteCallServiceContactAddress = null; |
| |
| private int mVideoCallCapability = -1; |
| private long mVideoCallCapabilityTimeStamp = -1; |
| private int mVideoCallAvailability = -1; |
| private long mVideoCallAvailabilityTimeStamp = -1; |
| private String mVideoCallServiceContactAddress = null; |
| |
| private long mContactLastUpdatedTimeStamp = -1; |
| private int mFieldUpdatedFlags = 0; |
| |
| private static int sVolteCallCapabilityFlag = 0x0001; |
| private static int sVolteCallCapabilityTimeStampFlag = 0x0002; |
| private static int sVolteCallAvailabilityFlag = 0x0004; |
| private static int sVolteCallAvailabilityTimeStampFlag = 0x0008; |
| private static int sVolteCallServiceContactAddressFlag = 0x0010; |
| private static int sVideoCallCapabilityFlag = 0x0020; |
| private static int sVideoCallCapabilityTimeStampFlag = 0x0040; |
| private static int sVideoCallAvailabilityFlag = 0x0080; |
| private static int sVideoCallAvailabilityTimeStampFlag = 0x0100; |
| private static int sVideoCallServiceContactAddressFlag = 0x0200; |
| private static int sContactLastUpdatedTimeStampFlag = 0x0400; |
| |
| /** |
| * @param id the contact id. |
| */ |
| public Request(long id) { |
| if (id < 0) { |
| throw new IllegalArgumentException( |
| "Can't update EAB presence item with id: " + id); |
| } |
| |
| mId = id; |
| } |
| |
| public Request(String number) { |
| if (TextUtils.isEmpty(number)) { |
| throw new IllegalArgumentException( |
| "Can't update EAB presence item with number: " + number); |
| } |
| |
| mContactNumber = number; |
| } |
| |
| public long getContactId() { |
| return mId; |
| } |
| |
| public String getContactNumber() { |
| return mContactNumber; |
| } |
| |
| /** |
| * Set Volte call service contact address. |
| * @param address contact from NOTIFY |
| * @return this object |
| */ |
| public Request setVolteCallServiceContactAddress(String address) { |
| mVolteCallServiceContactAddress = address; |
| mFieldUpdatedFlags |= sVolteCallServiceContactAddressFlag; |
| return this; |
| } |
| |
| /** |
| * Set Volte call capability. |
| * @param b wheter volte call is supported or not |
| * @return this object |
| */ |
| public Request setVolteCallCapability(boolean b) { |
| mVolteCallCapability = b ? 1 : 0; |
| mFieldUpdatedFlags |= sVolteCallCapabilityFlag; |
| return this; |
| } |
| |
| public Request setVolteCallCapability(int val) { |
| mVolteCallCapability = val; |
| mFieldUpdatedFlags |= sVolteCallCapabilityFlag; |
| return this; |
| } |
| |
| /** |
| * Set Volte call availability. |
| * @param b wheter volte call is available or not |
| * @return this object |
| */ |
| public Request setVolteCallAvailability(boolean b) { |
| mVolteCallAvailability = b ? 1 : 0; |
| mFieldUpdatedFlags |= sVolteCallAvailabilityFlag; |
| return this; |
| } |
| |
| public Request setVolteCallAvailability(int val) { |
| mVolteCallAvailability = val; |
| mFieldUpdatedFlags |= sVolteCallAvailabilityFlag; |
| return this; |
| } |
| |
| /** |
| * Set Video call service contact address. |
| * @param address contact from NOTIFY. |
| * @return this object |
| */ |
| public Request setVideoCallServiceContactAddress(String address) { |
| mVideoCallServiceContactAddress = address; |
| mFieldUpdatedFlags |= sVideoCallServiceContactAddressFlag; |
| return this; |
| } |
| |
| /** |
| * Set Video call capability. |
| * @param b wheter volte call is supported or not |
| * @return this object |
| */ |
| public Request setVideoCallCapability(boolean b) { |
| mVideoCallCapability = b ? 1 : 0; |
| mFieldUpdatedFlags |= sVideoCallCapabilityFlag; |
| return this; |
| } |
| |
| public Request setVideoCallCapability(int val) { |
| mVideoCallCapability = val; |
| mFieldUpdatedFlags |= sVideoCallCapabilityFlag; |
| return this; |
| } |
| |
| /** |
| * Set Video call availability. |
| * @param b wheter volte call is available or not |
| * @return this object |
| */ |
| public Request setVideoCallAvailability(boolean b) { |
| mVideoCallAvailability = b ? 1 : 0; |
| mFieldUpdatedFlags |= sVideoCallAvailabilityFlag; |
| return this; |
| } |
| |
| public Request setVideoCallAvailability(int val) { |
| mVideoCallAvailability = val; |
| mFieldUpdatedFlags |= sVideoCallAvailabilityFlag; |
| return this; |
| } |
| |
| /** |
| * Set the update timestamp. |
| * @param long timestamp the last update timestamp |
| * @return this object |
| */ |
| public Request setLastUpdatedTimeStamp(long timestamp) { |
| mContactLastUpdatedTimeStamp = timestamp; |
| mFieldUpdatedFlags |= sContactLastUpdatedTimeStampFlag; |
| return this; |
| } |
| |
| public Request setVolteCallCapabilityTimeStamp(long timestamp) { |
| mVolteCallCapabilityTimeStamp = timestamp; |
| mFieldUpdatedFlags |= sVolteCallCapabilityTimeStampFlag; |
| return this; |
| } |
| |
| public Request setVolteCallAvailabilityTimeStamp(long timestamp) { |
| mVolteCallAvailabilityTimeStamp = timestamp; |
| mFieldUpdatedFlags |= sVolteCallAvailabilityTimeStampFlag; |
| return this; |
| } |
| |
| public Request setVideoCallCapabilityTimeStamp(long timestamp) { |
| mVideoCallCapabilityTimeStamp = timestamp; |
| mFieldUpdatedFlags |= sVideoCallCapabilityTimeStampFlag; |
| return this; |
| } |
| |
| public Request setVideoCallAvailabilityTimeStamp(long timestamp) { |
| mVideoCallAvailabilityTimeStamp = timestamp; |
| mFieldUpdatedFlags |= sVideoCallAvailabilityTimeStampFlag; |
| return this; |
| } |
| |
| public Request reset() { |
| mVolteCallCapability = -1; |
| mVolteCallCapabilityTimeStamp = -1; |
| mVolteCallAvailability = -1; |
| mVolteCallAvailabilityTimeStamp = -1; |
| mVolteCallServiceContactAddress = null; |
| |
| mVideoCallCapability = -1; |
| mVideoCallCapabilityTimeStamp = -1; |
| mVideoCallAvailability = -1; |
| mVideoCallAvailabilityTimeStamp = -1; |
| mVideoCallServiceContactAddress = null; |
| |
| mContactLastUpdatedTimeStamp = -1; |
| mFieldUpdatedFlags = 0; |
| return this; |
| } |
| |
| /** |
| * @return ContentValues to be passed to EABProvider.update() |
| */ |
| ContentValues toContentValues() { |
| ContentValues values = new ContentValues(); |
| |
| if ((mFieldUpdatedFlags & sVolteCallCapabilityFlag) > 0) { |
| values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY, |
| mVolteCallCapability); |
| } |
| if ((mFieldUpdatedFlags & sVolteCallCapabilityTimeStampFlag) > 0) { |
| values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP, |
| mVolteCallCapabilityTimeStamp); |
| } |
| if ((mFieldUpdatedFlags & sVolteCallAvailabilityFlag) > 0) { |
| values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY, |
| mVolteCallAvailability); |
| } |
| if ((mFieldUpdatedFlags & sVolteCallAvailabilityTimeStampFlag) > 0) { |
| values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY_TIMESTAMP, |
| mVolteCallAvailabilityTimeStamp); |
| } |
| if ((mFieldUpdatedFlags & sVolteCallServiceContactAddressFlag) > 0) { |
| values.put(Contacts.Impl.VOLTE_CALL_SERVICE_CONTACT_ADDRESS, |
| mVolteCallServiceContactAddress); |
| } |
| |
| if ((mFieldUpdatedFlags & sVideoCallCapabilityFlag) > 0) { |
| values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY, |
| mVideoCallCapability); |
| } |
| if ((mFieldUpdatedFlags & sVideoCallCapabilityTimeStampFlag) > 0) { |
| values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP, |
| mVideoCallCapabilityTimeStamp); |
| } |
| if ((mFieldUpdatedFlags & sVideoCallAvailabilityFlag) > 0) { |
| values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY, |
| mVideoCallAvailability); |
| } |
| if ((mFieldUpdatedFlags & sVideoCallAvailabilityTimeStampFlag) > 0) { |
| values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP, |
| mVideoCallAvailabilityTimeStamp); |
| } |
| if ((mFieldUpdatedFlags & sVideoCallServiceContactAddressFlag) > 0) { |
| values.put(Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS, |
| mVideoCallServiceContactAddress); |
| } |
| |
| if ((mFieldUpdatedFlags & sContactLastUpdatedTimeStampFlag) > 0 ) { |
| values.put(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP, |
| mContactLastUpdatedTimeStamp); |
| } |
| |
| return values; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(512); |
| sb.append("EABContactManager.Request { "); |
| if (mId != -1) { |
| sb.append("\nId: " + mId); |
| } |
| if (!TextUtils.isEmpty(mContactNumber)) { |
| sb.append("\nContact Number: " + mContactNumber); |
| } |
| if (!TextUtils.isEmpty(mContactName)) { |
| sb.append("\nContact Name: " + mContactName); |
| } |
| |
| if ((mFieldUpdatedFlags & sVolteCallCapabilityFlag) > 0) { |
| sb.append("\nVolte call capability: " + mVolteCallCapability); |
| } |
| if ((mFieldUpdatedFlags & sVolteCallCapabilityTimeStampFlag) > 0) { |
| sb.append("\nVolte call capability timestamp: " + mVolteCallCapabilityTimeStamp |
| + "(" + getTimeString(mVolteCallCapabilityTimeStamp) + ")"); |
| } |
| if ((mFieldUpdatedFlags & sVolteCallAvailabilityFlag) > 0) { |
| sb.append("\nVolte call availability: " + mVolteCallAvailability); |
| } |
| if ((mFieldUpdatedFlags & sVolteCallAvailabilityTimeStampFlag) > 0) { |
| sb.append("\nVolte call availablity timestamp: " + mVolteCallAvailabilityTimeStamp |
| + "(" + getTimeString(mVolteCallAvailabilityTimeStamp) + ")"); |
| } |
| if ((mFieldUpdatedFlags & sVolteCallServiceContactAddressFlag) > 0) { |
| sb.append("\nVolte Call Service address: " + mVolteCallServiceContactAddress); |
| } |
| |
| if ((mFieldUpdatedFlags & sVideoCallCapabilityFlag) > 0) { |
| sb.append("\nVideo call capability: " + mVideoCallCapability); |
| } |
| if ((mFieldUpdatedFlags & sVideoCallCapabilityTimeStampFlag) > 0) { |
| sb.append("\nVideo call capability timestamp: " + mVideoCallCapabilityTimeStamp |
| + "(" + getTimeString(mVideoCallCapabilityTimeStamp) + ")"); |
| } |
| if ((mFieldUpdatedFlags & sVideoCallAvailabilityFlag) > 0) { |
| sb.append("\nVideo call availability: " + mVideoCallAvailability); |
| } |
| if ((mFieldUpdatedFlags & sVideoCallAvailabilityTimeStampFlag) > 0) { |
| sb.append("\nVideo call availablity timestamp: " + mVideoCallAvailabilityTimeStamp |
| + "(" + getTimeString(mVideoCallAvailabilityTimeStamp) + ")"); |
| } |
| if ((mFieldUpdatedFlags & sVideoCallServiceContactAddressFlag) > 0) { |
| sb.append("\nVideo Call Service address: " + mVideoCallServiceContactAddress); |
| } |
| |
| if ((mFieldUpdatedFlags & sContactLastUpdatedTimeStampFlag) > 0 ) { |
| sb.append("\nContact last update time: " + mContactLastUpdatedTimeStamp |
| + "(" + getTimeString(mContactLastUpdatedTimeStamp) + ")"); |
| } |
| |
| sb.append(" }"); |
| return sb.toString(); |
| } |
| } |
| |
| /** |
| * This class may be used to filter EABProvider queries. |
| */ |
| public static class Query { |
| /** |
| * Constant for use with {@link #orderBy} |
| * @hide |
| */ |
| public static final int ORDER_ASCENDING = 1; |
| |
| /** |
| * Constant for use with {@link #orderBy} |
| * @hide |
| */ |
| public static final int ORDER_DESCENDING = 2; |
| |
| private long[] mIds = null; |
| private String mContactNumber = null; |
| private List<String> mTimeFilters = null; |
| private String mOrderByColumn = COLUMN_LAST_UPDATED_TIMESTAMP; |
| private int mOrderDirection = ORDER_ASCENDING; |
| |
| /** |
| * Include only the contacts with the given IDs. |
| * @return this object |
| */ |
| public Query setFilterById(long... ids) { |
| mIds = ids; |
| return this; |
| } |
| |
| /** |
| * Include only the contacts with the given number. |
| * @return this object |
| */ |
| public Query setFilterByNumber(String number) { |
| mContactNumber = number; |
| return this; |
| } |
| |
| /** |
| * Include the contacts that meet the specified time condition. |
| * @return this object |
| */ |
| public Query setFilterByTime(String selection) { |
| if (mTimeFilters == null) { |
| mTimeFilters = new ArrayList<String>(); |
| } |
| |
| mTimeFilters.add(selection); |
| return this; |
| } |
| |
| /** |
| * Include only the contacts that has not been updated before the last time. |
| * @return this object |
| */ |
| public Query setFilterByTime(String column, long last) { |
| if (mTimeFilters == null) { |
| mTimeFilters = new ArrayList<String>(); |
| } |
| |
| mTimeFilters.add(column + "<='" + last + "'"); |
| return this; |
| } |
| |
| /** |
| * Include only the contacts that has not been updated after the eariest time. |
| * @return this object |
| */ |
| public Query setFilterByEarliestTime(String column, long earliest) { |
| if (mTimeFilters == null) { |
| mTimeFilters = new ArrayList<String>(); |
| } |
| |
| mTimeFilters.add(column + ">='" + earliest + "'"); |
| return this; |
| } |
| |
| /** |
| * Change the sort order of the returned Cursor. |
| * |
| * @param column one of the COLUMN_* constants; currently, only |
| * {@link #COLUMN_LAST_MODIFIED_TIMESTAMP} and {@link #COLUMN_TOTAL_SIZE_BYTES} are |
| * supported. |
| * @param direction either {@link #ORDER_ASCENDING} or {@link #ORDER_DESCENDING} |
| * @return this object |
| * @hide |
| */ |
| public Query orderBy(String column, int direction) { |
| if (direction != ORDER_ASCENDING && direction != ORDER_DESCENDING) { |
| throw new IllegalArgumentException("Invalid direction: " + direction); |
| } |
| |
| if (column.equals(COLUMN_ID)) { |
| mOrderByColumn = Contacts.Impl._ID; |
| } else if (column.equals(COLUMN_LAST_UPDATED_TIMESTAMP)) { |
| mOrderByColumn = Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP; |
| } else { |
| throw new IllegalArgumentException("Cannot order by " + column); |
| } |
| mOrderDirection = direction; |
| return this; |
| } |
| |
| /** |
| * Run this query using the given ContentResolver. |
| * @param projection the projection to pass to ContentResolver.query() |
| * @return the Cursor returned by ContentResolver.query() |
| */ |
| Cursor runQuery(ContentResolver resolver, String[] projection, Uri baseUri) { |
| Uri uri = baseUri; |
| List<String> selectionParts = new ArrayList<String>(); |
| String[] selectionArgs = null; |
| |
| if (mIds != null) { |
| selectionParts.add(getWhereClauseForIds(mIds)); |
| selectionArgs = getWhereArgsForIds(mIds); |
| } |
| |
| if (!TextUtils.isEmpty(mContactNumber)) { |
| String number = mContactNumber; |
| if (number.startsWith("tel:")) { |
| number = number.substring(4); |
| } |
| String escapedPhoneNumber = DatabaseUtils.sqlEscapeString(number); |
| String cselection = "(" + Contacts.Impl.CONTACT_NUMBER + "=" + escapedPhoneNumber; |
| cselection += " OR PHONE_NUMBERS_EQUAL(" + Contacts.Impl.CONTACT_NUMBER + ", "; |
| cselection += escapedPhoneNumber + ", 0))"; |
| |
| selectionParts.add(cselection); |
| } |
| |
| if (mTimeFilters != null) { |
| String cselection = joinStrings(" OR ", mTimeFilters); |
| int size = mTimeFilters.size(); |
| if (size > 1) { |
| cselection = "(" + cselection + ")"; |
| } |
| selectionParts.add(cselection); |
| } |
| |
| String selection = joinStrings(" AND ", selectionParts); |
| String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC" : "DESC"); |
| String orderBy = mOrderByColumn + " " + orderDirection; |
| |
| return resolver.query(uri, projection, selection, selectionArgs, orderBy); |
| } |
| |
| private String joinStrings(String joiner, Iterable<String> parts) { |
| StringBuilder builder = new StringBuilder(); |
| boolean first = true; |
| for (String part : parts) { |
| if (!first) { |
| builder.append(joiner); |
| } |
| builder.append(part); |
| first = false; |
| } |
| return builder.toString(); |
| } |
| |
| @Override |
| public String toString() { |
| List<String> selectionParts = new ArrayList<String>(); |
| String[] selectionArgs = null; |
| |
| if (mIds != null) { |
| selectionParts.add(getWhereClauseForIds(mIds)); |
| selectionArgs = getWhereArgsForIds(mIds); |
| } |
| |
| if (!TextUtils.isEmpty(mContactNumber)) { |
| String number = mContactNumber; |
| if (number.startsWith("tel:")) { |
| number = number.substring(4); |
| } |
| String escapedPhoneNumber = DatabaseUtils.sqlEscapeString(number); |
| String cselection = "(" + Contacts.Impl.CONTACT_NUMBER + "=" + escapedPhoneNumber; |
| cselection += " OR PHONE_NUMBERS_EQUAL(" + Contacts.Impl.CONTACT_NUMBER + ", "; |
| cselection += escapedPhoneNumber + ", 0))"; |
| |
| selectionParts.add(cselection); |
| } |
| |
| if (mTimeFilters != null) { |
| String cselection = joinStrings(" OR ", mTimeFilters); |
| int size = mTimeFilters.size(); |
| if (size > 1) { |
| cselection = "(" + cselection + ")"; |
| } |
| selectionParts.add(cselection); |
| } |
| |
| String selection = joinStrings(" AND ", selectionParts); |
| String orderDirection = (mOrderDirection == ORDER_ASCENDING ? "ASC" : "DESC"); |
| String orderBy = mOrderByColumn + " " + orderDirection; |
| |
| StringBuilder sb = new StringBuilder(512); |
| sb.append("EABContactManager.Query { "); |
| sb.append("\nSelection: " + selection); |
| sb.append("\nSelectionArgs: " + selectionArgs); |
| sb.append("\nOrderBy: " + orderBy); |
| sb.append(" }"); |
| return sb.toString(); |
| } |
| } |
| |
| private ContentResolver mResolver; |
| private String mPackageName; |
| private Uri mBaseUri = Contacts.Impl.CONTENT_URI; |
| |
| /** |
| * @hide |
| */ |
| public EABContactManager(ContentResolver resolver, String packageName) { |
| mResolver = resolver; |
| mPackageName = packageName; |
| } |
| |
| /** |
| * Query the presence manager about contacts that have been requested. |
| * @param query parameters specifying filters for this query |
| * @return a Cursor over the result set of contacts, with columns consisting of all the |
| * COLUMN_* constants. |
| */ |
| public Cursor query(Query query) { |
| Cursor underlyingCursor = query.runQuery(mResolver, CONTACT_COLUMNS, mBaseUri); |
| if (underlyingCursor == null) { |
| return null; |
| } |
| |
| return new CursorTranslator(underlyingCursor, mBaseUri); |
| } |
| |
| /** |
| * Update a contact presence status. |
| * |
| * @param request the parameters specifying this contact presence |
| * @return an ID for the contact, unique across the system. This ID is used to make future |
| * calls related to this contact. |
| */ |
| public int update(Request request) { |
| if (request == null) { |
| return 0; |
| } |
| |
| long id = request.getContactId(); |
| String number = request.getContactNumber(); |
| if ((id == -1) && TextUtils.isEmpty(number)) { |
| throw new IllegalArgumentException("invalid request for contact update."); |
| } |
| |
| ContentValues values = request.toContentValues(); |
| if (id != -1) { |
| logger.debug("Update contact " + id + " with request: " + values); |
| return mResolver.update(ContentUris.withAppendedId(mBaseUri, id), values, |
| null, null); |
| } else { |
| Query query = new Query().setFilterByNumber(number) |
| .orderBy(COLUMN_ID, Query.ORDER_ASCENDING); |
| long[] ids = null; |
| Cursor cursor = null; |
| try { |
| cursor = query(query); |
| if (cursor == null) { |
| return 0; |
| } |
| int count = cursor.getCount(); |
| if (count == 0) { |
| return 0; |
| } |
| |
| ids = new long[count]; |
| int idx = 0; |
| for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { |
| id = cursor.getLong(cursor.getColumnIndex(Contacts.Impl._ID)); |
| ids[idx++] = id; |
| if (idx >= count) { |
| break; |
| } |
| } |
| } finally { |
| if (cursor != null) { |
| cursor.close(); |
| } |
| } |
| |
| if ((ids == null) || (ids.length == 0)) { |
| return 0; |
| } |
| |
| if (ids.length == 1) { |
| logger.debug("Update contact " + ids[0] + " with request: " + values); |
| return mResolver.update(ContentUris.withAppendedId(mBaseUri, ids[0]), values, |
| null, null); |
| } |
| |
| logger.debug("Update contact " + number + " with request: " + values); |
| return mResolver.update(mBaseUri, values, getWhereClauseForIds(ids), |
| getWhereArgsForIds(ids)); |
| } |
| } |
| |
| /** |
| * Get the EABProvider URI for the contact with the given ID. |
| * |
| * @hide |
| */ |
| public Uri getContactUri(long id) { |
| return ContentUris.withAppendedId(mBaseUri, id); |
| } |
| |
| /** |
| * Get a parameterized SQL WHERE clause to select a bunch of IDs. |
| */ |
| static String getWhereClauseForIds(long[] ids) { |
| StringBuilder whereClause = new StringBuilder(); |
| whereClause.append("("); |
| for (int i = 0; i < ids.length; i++) { |
| if (i > 0) { |
| whereClause.append("OR "); |
| } |
| whereClause.append(COLUMN_ID); |
| whereClause.append(" = ? "); |
| } |
| whereClause.append(")"); |
| return whereClause.toString(); |
| } |
| |
| /** |
| * Get the selection args for a clause returned by {@link #getWhereClauseForIds(long[])}. |
| */ |
| static String[] getWhereArgsForIds(long[] ids) { |
| String[] whereArgs = new String[ids.length]; |
| for (int i = 0; i < ids.length; i++) { |
| whereArgs[i] = Long.toString(ids[i]); |
| } |
| return whereArgs; |
| } |
| |
| static String getTimeString(long time) { |
| if (time <= 0) { |
| time = System.currentTimeMillis(); |
| } |
| |
| String timeString = TimeMigrationUtils.formatMillisWithFixedFormat(time); |
| return String.format("%s.%s", timeString, time % 1000); |
| } |
| |
| /** |
| * This class wraps a cursor returned by EABProvider -- the "underlying cursor" -- and |
| * presents a different set of columns, those defined in the COLUMN_* constants. |
| * Some columns correspond directly to underlying values while others are computed from |
| * underlying data. |
| */ |
| private static class CursorTranslator extends CursorWrapper { |
| private Uri mBaseUri; |
| |
| public CursorTranslator(Cursor cursor, Uri baseUri) { |
| super(cursor); |
| mBaseUri = baseUri; |
| } |
| |
| @Override |
| public int getInt(int columnIndex) { |
| return (int) getLong(columnIndex); |
| } |
| |
| @Override |
| public long getLong(int columnIndex) { |
| return super.getLong(columnIndex); |
| } |
| |
| @Override |
| public String getString(int columnIndex) { |
| return super.getString(columnIndex); |
| } |
| } |
| |
| public void updateAllCapabilityToUnknown() { |
| if (mResolver == null) { |
| logger.error("updateAllCapabilityToUnknown, mResolver=null"); |
| return; |
| } |
| |
| ContentValues values = new ContentValues(); |
| values.put(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP, (String)null); |
| values.put(Contacts.Impl.VOLTE_CALL_SERVICE_CONTACT_ADDRESS, (String)null); |
| values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY, (String)null); |
| values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP, (String)null); |
| values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY, (String)null); |
| values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY_TIMESTAMP, (String)null); |
| |
| values.put(Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS, (String)null); |
| values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY, (String)null); |
| values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP, (String)null); |
| values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY, (String)null); |
| values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP, (String)null); |
| |
| try { |
| int count = ContactDbUtil.resetVtCapability(mResolver); |
| logger.print("update Contact DB: updateAllCapabilityToUnknown count=" + count); |
| |
| count = mResolver.update(Contacts.Impl.CONTENT_URI, |
| values, null, null); |
| logger.print("update EAB DB: updateAllCapabilityToUnknown count=" + count); |
| } catch (Exception ex) { |
| logger.error("updateAllCapabilityToUnknown exception: " + ex); |
| } |
| } |
| |
| public void updateAllVtCapabilityToUnknown() { |
| if (mResolver == null) { |
| logger.error("updateAllVtCapabilityToUnknown mResolver=null"); |
| return; |
| } |
| |
| ContentValues values = new ContentValues(); |
| values.put(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP, (String)null); |
| values.put(Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS, (String)null); |
| values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY, (String)null); |
| values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP, (String)null); |
| values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY, (String)null); |
| values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP, (String)null); |
| |
| try { |
| int count = ContactDbUtil.resetVtCapability(mResolver); |
| logger.print("update Contact DB: updateAllVtCapabilityToUnknown count=" + count); |
| |
| count = mResolver.update(Contacts.Impl.CONTENT_URI, |
| values, null, null); |
| logger.print("update EAB DB: updateAllVtCapabilityToUnknown count=" + count); |
| } catch (Exception ex) { |
| logger.error("updateAllVtCapabilityToUnknown exception: " + ex); |
| } |
| } |
| |
| // if updateLastTimestamp is true, the rcsPresenceInfo is from network. |
| // if the updateLastTimestamp is false, It is used to update the availabilty to unknown only. |
| // And the availability will be updated only when it has expired, so we don't update the |
| // timestamp to make sure the availablity still in expired status and will be subscribed from |
| // network afterwards. |
| public int update(RcsPresenceInfo rcsPresenceInfo, boolean updateLastTimestamp) { |
| if (rcsPresenceInfo == null) { |
| return 0; |
| } |
| |
| String number = rcsPresenceInfo.getContactNumber(); |
| if (TextUtils.isEmpty(number)) { |
| logger.error("Failed to update for the contact number is empty."); |
| return 0; |
| } |
| |
| ContentValues values = new ContentValues(); |
| |
| int volteStatus = rcsPresenceInfo.getVolteStatus(); |
| if(volteStatus != RcsPresenceInfo.VolteStatus.VOLTE_UNKNOWN) { |
| values.put(Contacts.Impl.VOLTE_STATUS, volteStatus); |
| } |
| |
| if(updateLastTimestamp){ |
| values.put(Contacts.Impl.CONTACT_LAST_UPDATED_TIMESTAMP, |
| (long)System.currentTimeMillis()); |
| } |
| |
| int lteCallCapability = rcsPresenceInfo.getServiceState( |
| RcsPresenceInfo.ServiceType.VOLTE_CALL); |
| long lteCallTimestamp = rcsPresenceInfo.getTimeStamp( |
| RcsPresenceInfo.ServiceType.VOLTE_CALL); |
| values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY, lteCallCapability); |
| values.put(Contacts.Impl.VOLTE_CALL_AVAILABILITY_TIMESTAMP, (long)lteCallTimestamp); |
| if(rcsPresenceInfo.getServiceState(RcsPresenceInfo.ServiceType.VOLTE_CALL) |
| != RcsPresenceInfo.ServiceState.UNKNOWN){ |
| String lteCallContactAddress = |
| rcsPresenceInfo.getServiceContact(RcsPresenceInfo.ServiceType.VOLTE_CALL); |
| if (!TextUtils.isEmpty(lteCallContactAddress)) { |
| values.put(Contacts.Impl.VOLTE_CALL_SERVICE_CONTACT_ADDRESS, lteCallContactAddress); |
| } |
| |
| values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY, lteCallCapability); |
| values.put(Contacts.Impl.VOLTE_CALL_CAPABILITY_TIMESTAMP, lteCallTimestamp); |
| } |
| |
| int videoCallCapability = rcsPresenceInfo.getServiceState( |
| RcsPresenceInfo.ServiceType.VT_CALL); |
| long videoCallTimestamp = rcsPresenceInfo.getTimeStamp( |
| RcsPresenceInfo.ServiceType.VT_CALL); |
| values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY, videoCallCapability); |
| values.put(Contacts.Impl.VIDEO_CALL_AVAILABILITY_TIMESTAMP, (long)videoCallTimestamp); |
| if(rcsPresenceInfo.getServiceState(RcsPresenceInfo.ServiceType.VT_CALL) |
| != RcsPresenceInfo.ServiceState.UNKNOWN){ |
| String videoCallContactAddress = |
| rcsPresenceInfo.getServiceContact(RcsPresenceInfo.ServiceType.VT_CALL); |
| if (!TextUtils.isEmpty(videoCallContactAddress)) { |
| values.put(Contacts.Impl.VIDEO_CALL_SERVICE_CONTACT_ADDRESS, |
| videoCallContactAddress); |
| } |
| |
| values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY, videoCallCapability); |
| values.put(Contacts.Impl.VIDEO_CALL_CAPABILITY_TIMESTAMP, videoCallTimestamp); |
| } |
| |
| int count = 0; |
| Cursor cursor = null; |
| try{ |
| cursor = mResolver.query(Contacts.Impl.CONTENT_URI, DATA_QUERY_PROJECTION, |
| "PHONE_NUMBERS_EQUAL(" + Contacts.Impl.FORMATTED_NUMBER + ", ?, 1)", |
| new String[] {number}, null); |
| if(cursor == null) { |
| logger.print("update rcsPresenceInfo to DB: update count=" + count); |
| return count; |
| } |
| |
| ContactNumberUtils contactNumberUtils = ContactNumberUtils.getDefault(); |
| for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) { |
| String numberInDB = cursor.getString(DATA_QUERY_FORMATTED_NUMBER); |
| logger.debug("number=" + number + " numberInDB=" + numberInDB + |
| " formatedNumber in DB=" + contactNumberUtils.format(numberInDB)); |
| if(number.equals(contactNumberUtils.format(numberInDB))) { |
| count = ContactDbUtil.updateVtCapability(mResolver, |
| cursor.getLong(DATA_QUERY_DATA_ID), |
| (videoCallCapability == RcsPresenceInfo.ServiceState.ONLINE)); |
| logger.print("update rcsPresenceInfo to Contact DB, count=" + count); |
| |
| int id = cursor.getInt(DATA_QUERY_ID); |
| count += mResolver.update(Contacts.Impl.CONTENT_URI, values, |
| Contacts.Impl._ID + "=" + id, null); |
| logger.debug("count=" + count); |
| } |
| } |
| |
| logger.print("update rcsPresenceInfo to DB: update count=" + count + |
| " rcsPresenceInfo=" + rcsPresenceInfo); |
| } catch(Exception e){ |
| logger.error("updateCapability exception"); |
| } finally { |
| if(cursor != null) { |
| cursor.close(); |
| cursor = null; |
| } |
| } |
| |
| return count; |
| } |
| } |
| |