• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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          */
Request(long id)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 
Request(String number)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 
getContactId()160         public long getContactId() {
161             return mId;
162         }
163 
getContactNumber()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          */
setVolteCallServiceContactAddress(String address)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          */
setVolteCallCapability(boolean b)184         public Request setVolteCallCapability(boolean b) {
185             mVolteCallCapability = b ? 1 : 0;
186             mFieldUpdatedFlags |= sVolteCallCapabilityFlag;
187             return this;
188         }
189 
setVolteCallCapability(int val)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          */
setVolteCallAvailability(boolean b)201         public Request setVolteCallAvailability(boolean b) {
202             mVolteCallAvailability = b ? 1 : 0;
203             mFieldUpdatedFlags |= sVolteCallAvailabilityFlag;
204             return this;
205         }
206 
setVolteCallAvailability(int val)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          */
setVideoCallServiceContactAddress(String address)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          */
setVideoCallCapability(boolean b)229         public Request setVideoCallCapability(boolean b) {
230             mVideoCallCapability = b ? 1 : 0;
231             mFieldUpdatedFlags |= sVideoCallCapabilityFlag;
232             return this;
233         }
234 
setVideoCallCapability(int val)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          */
setVideoCallAvailability(boolean b)246         public Request setVideoCallAvailability(boolean b) {
247             mVideoCallAvailability = b ? 1 : 0;
248             mFieldUpdatedFlags |= sVideoCallAvailabilityFlag;
249             return this;
250         }
251 
setVideoCallAvailability(int val)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          */
setLastUpdatedTimeStamp(long timestamp)263         public Request setLastUpdatedTimeStamp(long timestamp) {
264             mContactLastUpdatedTimeStamp = timestamp;
265             mFieldUpdatedFlags |= sContactLastUpdatedTimeStampFlag;
266             return this;
267         }
268 
setVolteCallCapabilityTimeStamp(long timestamp)269         public Request setVolteCallCapabilityTimeStamp(long timestamp) {
270             mVolteCallCapabilityTimeStamp = timestamp;
271             mFieldUpdatedFlags |= sVolteCallCapabilityTimeStampFlag;
272             return this;
273         }
274 
setVolteCallAvailabilityTimeStamp(long timestamp)275         public Request setVolteCallAvailabilityTimeStamp(long timestamp) {
276             mVolteCallAvailabilityTimeStamp = timestamp;
277             mFieldUpdatedFlags |= sVolteCallAvailabilityTimeStampFlag;
278             return this;
279         }
280 
setVideoCallCapabilityTimeStamp(long timestamp)281         public Request setVideoCallCapabilityTimeStamp(long timestamp) {
282             mVideoCallCapabilityTimeStamp = timestamp;
283             mFieldUpdatedFlags |= sVideoCallCapabilityTimeStampFlag;
284             return this;
285         }
286 
setVideoCallAvailabilityTimeStamp(long timestamp)287         public Request setVideoCallAvailabilityTimeStamp(long timestamp) {
288             mVideoCallAvailabilityTimeStamp = timestamp;
289             mFieldUpdatedFlags |= sVideoCallAvailabilityTimeStampFlag;
290             return this;
291         }
292 
reset()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          */
toContentValues()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
toString()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          */
setFilterById(long... ids)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          */
setFilterByNumber(String number)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          */
setFilterByTime(String selection)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          */
setFilterByTime(String column, long last)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          */
setFilterByEarliestTime(String column, long earliest)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          */
orderBy(String column, int direction)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          */
runQuery(ContentResolver resolver, String[] projection, Uri baseUri)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 
joinStrings(String joiner, Iterable<String> parts)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
toString()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      */
EABContactManager(ContentResolver resolver, String packageName)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      */
query(Query query)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      */
update(Request request)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      */
getContactUri(long id)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      */
getWhereClauseForIds(long[] ids)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      */
getWhereArgsForIds(long[] ids)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 
getTimeString(long time)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 
CursorTranslator(Cursor cursor, Uri baseUri)787         public CursorTranslator(Cursor cursor, Uri baseUri) {
788             super(cursor);
789             mBaseUri = baseUri;
790         }
791 
792         @Override
getInt(int columnIndex)793         public int getInt(int columnIndex) {
794             return (int) getLong(columnIndex);
795         }
796 
797         @Override
getLong(int columnIndex)798         public long getLong(int columnIndex) {
799             return super.getLong(columnIndex);
800         }
801 
802         @Override
getString(int columnIndex)803         public String getString(int columnIndex) {
804             return super.getString(columnIndex);
805         }
806     }
807 
updateAllCapabilityToUnknown()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 
updateAllVtCapabilityToUnknown()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.
update(RcsPresenceInfo rcsPresenceInfo, boolean updateLastTimestamp)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