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