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