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