1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.providers.contacts; 18 19 import static com.android.providers.contacts.TestUtils.cv; 20 21 import android.accounts.Account; 22 import android.content.ContentProvider; 23 import android.content.ContentProviderOperation; 24 import android.content.ContentProviderResult; 25 import android.content.ContentResolver; 26 import android.content.ContentUris; 27 import android.content.ContentValues; 28 import android.content.Entity; 29 import android.content.EntityIterator; 30 import android.content.pm.UserInfo; 31 import android.content.res.AssetFileDescriptor; 32 import android.database.Cursor; 33 import android.database.MatrixCursor; 34 import android.database.sqlite.SQLiteDatabase; 35 import android.net.Uri; 36 import android.os.AsyncTask; 37 import android.os.UserManager; 38 import android.provider.CallLog.Calls; 39 import android.provider.CallLog; 40 import android.provider.ContactsContract; 41 import android.provider.ContactsContract.AggregationExceptions; 42 import android.provider.ContactsContract.CommonDataKinds.Callable; 43 import android.provider.ContactsContract.CommonDataKinds.Contactables; 44 import android.provider.ContactsContract.CommonDataKinds.Email; 45 import android.provider.ContactsContract.CommonDataKinds.GroupMembership; 46 import android.provider.ContactsContract.CommonDataKinds.Im; 47 import android.provider.ContactsContract.CommonDataKinds.Organization; 48 import android.provider.ContactsContract.CommonDataKinds.Phone; 49 import android.provider.ContactsContract.CommonDataKinds.Photo; 50 import android.provider.ContactsContract.CommonDataKinds.SipAddress; 51 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 52 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 53 import android.provider.ContactsContract.Contacts; 54 import android.provider.ContactsContract.Data; 55 import android.provider.ContactsContract.DataUsageFeedback; 56 import android.provider.ContactsContract.Directory; 57 import android.provider.ContactsContract.DisplayNameSources; 58 import android.provider.ContactsContract.DisplayPhoto; 59 import android.provider.ContactsContract.FullNameStyle; 60 import android.provider.ContactsContract.Groups; 61 import android.provider.ContactsContract.PhoneLookup; 62 import android.provider.ContactsContract.PhoneticNameStyle; 63 import android.provider.ContactsContract.PinnedPositions; 64 import android.provider.ContactsContract.Profile; 65 import android.provider.ContactsContract.ProviderStatus; 66 import android.provider.ContactsContract.RawContacts; 67 import android.provider.ContactsContract.RawContactsEntity; 68 import android.provider.ContactsContract.SearchSnippets; 69 import android.provider.ContactsContract.Settings; 70 import android.provider.ContactsContract.StatusUpdates; 71 import android.provider.ContactsContract.StreamItemPhotos; 72 import android.provider.ContactsContract.StreamItems; 73 import android.provider.OpenableColumns; 74 import android.test.MoreAsserts; 75 import android.test.suitebuilder.annotation.LargeTest; 76 import android.text.TextUtils; 77 78 import com.android.internal.util.ArrayUtils; 79 import com.android.providers.contacts.CallLogProviderTest.TestCallLogProvider; 80 import com.android.providers.contacts.ContactsActor.AlteringUserContext; 81 import com.android.providers.contacts.ContactsActor.MockUserManager; 82 import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns; 83 import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns; 84 import com.android.providers.contacts.ContactsDatabaseHelper.DataUsageStatColumns; 85 import com.android.providers.contacts.ContactsDatabaseHelper.DbProperties; 86 import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns; 87 import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; 88 import com.android.providers.contacts.ContactsDatabaseHelper.Tables; 89 import com.android.providers.contacts.testutil.CommonDatabaseUtils; 90 import com.android.providers.contacts.testutil.ContactUtil; 91 import com.android.providers.contacts.testutil.DataUtil; 92 import com.android.providers.contacts.testutil.DatabaseAsserts; 93 import com.android.providers.contacts.testutil.DeletedContactUtil; 94 import com.android.providers.contacts.testutil.RawContactUtil; 95 import com.android.providers.contacts.testutil.TestUtil; 96 import com.android.providers.contacts.tests.R; 97 98 import com.google.android.collect.Lists; 99 import com.google.android.collect.Sets; 100 101 import java.io.FileInputStream; 102 import java.io.IOException; 103 import java.io.OutputStream; 104 import java.text.Collator; 105 import java.util.ArrayList; 106 import java.util.Arrays; 107 import java.util.HashSet; 108 import java.util.List; 109 import java.util.Locale; 110 import java.util.Set; 111 112 /** 113 * Unit tests for {@link ContactsProvider2}. 114 * 115 * Run the test like this: 116 * <code> 117 adb shell am instrument -e class com.android.providers.contacts.ContactsProvider2Test -w \ 118 com.android.providers.contacts.tests/android.test.InstrumentationTestRunner 119 * </code> 120 */ 121 @LargeTest 122 public class ContactsProvider2Test extends BaseContactsProvider2Test { 123 124 private static final String TAG = ContactsProvider2Test.class.getSimpleName(); 125 126 public void testContactsProjection() { 127 assertProjection(Contacts.CONTENT_URI, new String[]{ 128 Contacts._ID, 129 Contacts.DISPLAY_NAME_PRIMARY, 130 Contacts.DISPLAY_NAME_ALTERNATIVE, 131 Contacts.DISPLAY_NAME_SOURCE, 132 Contacts.PHONETIC_NAME, 133 Contacts.PHONETIC_NAME_STYLE, 134 Contacts.SORT_KEY_PRIMARY, 135 Contacts.SORT_KEY_ALTERNATIVE, 136 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 137 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 138 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 139 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 140 Contacts.LAST_TIME_CONTACTED, 141 Contacts.TIMES_CONTACTED, 142 Contacts.STARRED, 143 Contacts.PINNED, 144 Contacts.IN_DEFAULT_DIRECTORY, 145 Contacts.IN_VISIBLE_GROUP, 146 Contacts.PHOTO_ID, 147 Contacts.PHOTO_FILE_ID, 148 Contacts.PHOTO_URI, 149 Contacts.PHOTO_THUMBNAIL_URI, 150 Contacts.CUSTOM_RINGTONE, 151 Contacts.HAS_PHONE_NUMBER, 152 Contacts.SEND_TO_VOICEMAIL, 153 Contacts.IS_USER_PROFILE, 154 Contacts.LOOKUP_KEY, 155 Contacts.NAME_RAW_CONTACT_ID, 156 Contacts.CONTACT_PRESENCE, 157 Contacts.CONTACT_CHAT_CAPABILITY, 158 Contacts.CONTACT_STATUS, 159 Contacts.CONTACT_STATUS_TIMESTAMP, 160 Contacts.CONTACT_STATUS_RES_PACKAGE, 161 Contacts.CONTACT_STATUS_LABEL, 162 Contacts.CONTACT_STATUS_ICON, 163 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP 164 }); 165 } 166 167 public void testContactsStrequentProjection() { 168 assertProjection(Contacts.CONTENT_STREQUENT_URI, new String[]{ 169 Contacts._ID, 170 Contacts.DISPLAY_NAME_PRIMARY, 171 Contacts.DISPLAY_NAME_ALTERNATIVE, 172 Contacts.DISPLAY_NAME_SOURCE, 173 Contacts.PHONETIC_NAME, 174 Contacts.PHONETIC_NAME_STYLE, 175 Contacts.SORT_KEY_PRIMARY, 176 Contacts.SORT_KEY_ALTERNATIVE, 177 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 178 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 179 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 180 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 181 Contacts.LAST_TIME_CONTACTED, 182 Contacts.TIMES_CONTACTED, 183 Contacts.STARRED, 184 Contacts.PINNED, 185 Contacts.IN_DEFAULT_DIRECTORY, 186 Contacts.IN_VISIBLE_GROUP, 187 Contacts.PHOTO_ID, 188 Contacts.PHOTO_FILE_ID, 189 Contacts.PHOTO_URI, 190 Contacts.PHOTO_THUMBNAIL_URI, 191 Contacts.CUSTOM_RINGTONE, 192 Contacts.HAS_PHONE_NUMBER, 193 Contacts.SEND_TO_VOICEMAIL, 194 Contacts.IS_USER_PROFILE, 195 Contacts.LOOKUP_KEY, 196 Contacts.NAME_RAW_CONTACT_ID, 197 Contacts.CONTACT_PRESENCE, 198 Contacts.CONTACT_CHAT_CAPABILITY, 199 Contacts.CONTACT_STATUS, 200 Contacts.CONTACT_STATUS_TIMESTAMP, 201 Contacts.CONTACT_STATUS_RES_PACKAGE, 202 Contacts.CONTACT_STATUS_LABEL, 203 Contacts.CONTACT_STATUS_ICON, 204 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 205 DataUsageStatColumns.TIMES_USED, 206 DataUsageStatColumns.LAST_TIME_USED, 207 }); 208 } 209 210 public void testContactsStrequentPhoneOnlyProjection() { 211 assertProjection(Contacts.CONTENT_STREQUENT_URI.buildUpon() 212 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build(), 213 new String[] { 214 Contacts._ID, 215 Contacts.DISPLAY_NAME_PRIMARY, 216 Contacts.DISPLAY_NAME_ALTERNATIVE, 217 Contacts.DISPLAY_NAME_SOURCE, 218 Contacts.PHONETIC_NAME, 219 Contacts.PHONETIC_NAME_STYLE, 220 Contacts.SORT_KEY_PRIMARY, 221 Contacts.SORT_KEY_ALTERNATIVE, 222 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 223 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 224 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 225 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 226 Contacts.LAST_TIME_CONTACTED, 227 Contacts.TIMES_CONTACTED, 228 Contacts.STARRED, 229 Contacts.PINNED, 230 Contacts.IN_DEFAULT_DIRECTORY, 231 Contacts.IN_VISIBLE_GROUP, 232 Contacts.PHOTO_ID, 233 Contacts.PHOTO_FILE_ID, 234 Contacts.PHOTO_URI, 235 Contacts.PHOTO_THUMBNAIL_URI, 236 Contacts.CUSTOM_RINGTONE, 237 Contacts.HAS_PHONE_NUMBER, 238 Contacts.SEND_TO_VOICEMAIL, 239 Contacts.IS_USER_PROFILE, 240 Contacts.LOOKUP_KEY, 241 Contacts.NAME_RAW_CONTACT_ID, 242 Contacts.CONTACT_PRESENCE, 243 Contacts.CONTACT_CHAT_CAPABILITY, 244 Contacts.CONTACT_STATUS, 245 Contacts.CONTACT_STATUS_TIMESTAMP, 246 Contacts.CONTACT_STATUS_RES_PACKAGE, 247 Contacts.CONTACT_STATUS_LABEL, 248 Contacts.CONTACT_STATUS_ICON, 249 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 250 DataUsageStatColumns.TIMES_USED, 251 DataUsageStatColumns.LAST_TIME_USED, 252 Phone.NUMBER, 253 Phone.TYPE, 254 Phone.LABEL, 255 Phone.IS_SUPER_PRIMARY, 256 Phone.CONTACT_ID 257 }); 258 } 259 260 public void testContactsWithSnippetProjection() { 261 assertProjection(Contacts.CONTENT_FILTER_URI.buildUpon().appendPath("nothing").build(), 262 new String[]{ 263 Contacts._ID, 264 Contacts.DISPLAY_NAME_PRIMARY, 265 Contacts.DISPLAY_NAME_ALTERNATIVE, 266 Contacts.DISPLAY_NAME_SOURCE, 267 Contacts.PHONETIC_NAME, 268 Contacts.PHONETIC_NAME_STYLE, 269 Contacts.SORT_KEY_PRIMARY, 270 Contacts.SORT_KEY_ALTERNATIVE, 271 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 272 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 273 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 274 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 275 Contacts.LAST_TIME_CONTACTED, 276 Contacts.TIMES_CONTACTED, 277 Contacts.STARRED, 278 Contacts.PINNED, 279 Contacts.IN_DEFAULT_DIRECTORY, 280 Contacts.IN_VISIBLE_GROUP, 281 Contacts.PHOTO_ID, 282 Contacts.PHOTO_FILE_ID, 283 Contacts.PHOTO_URI, 284 Contacts.PHOTO_THUMBNAIL_URI, 285 Contacts.CUSTOM_RINGTONE, 286 Contacts.HAS_PHONE_NUMBER, 287 Contacts.SEND_TO_VOICEMAIL, 288 Contacts.IS_USER_PROFILE, 289 Contacts.LOOKUP_KEY, 290 Contacts.NAME_RAW_CONTACT_ID, 291 Contacts.CONTACT_PRESENCE, 292 Contacts.CONTACT_CHAT_CAPABILITY, 293 Contacts.CONTACT_STATUS, 294 Contacts.CONTACT_STATUS_TIMESTAMP, 295 Contacts.CONTACT_STATUS_RES_PACKAGE, 296 Contacts.CONTACT_STATUS_LABEL, 297 Contacts.CONTACT_STATUS_ICON, 298 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 299 SearchSnippets.SNIPPET, 300 }); 301 } 302 303 public void testRawContactsProjection() { 304 assertProjection(RawContacts.CONTENT_URI, new String[]{ 305 RawContacts._ID, 306 RawContacts.CONTACT_ID, 307 RawContacts.ACCOUNT_NAME, 308 RawContacts.ACCOUNT_TYPE, 309 RawContacts.DATA_SET, 310 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 311 RawContacts.SOURCE_ID, 312 RawContacts.VERSION, 313 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 314 RawContacts.DIRTY, 315 RawContacts.DELETED, 316 RawContacts.DISPLAY_NAME_PRIMARY, 317 RawContacts.DISPLAY_NAME_ALTERNATIVE, 318 RawContacts.DISPLAY_NAME_SOURCE, 319 RawContacts.PHONETIC_NAME, 320 RawContacts.PHONETIC_NAME_STYLE, 321 RawContacts.NAME_VERIFIED, 322 RawContacts.SORT_KEY_PRIMARY, 323 RawContacts.SORT_KEY_ALTERNATIVE, 324 RawContactsColumns.PHONEBOOK_LABEL_PRIMARY, 325 RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 326 RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 327 RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 328 RawContacts.TIMES_CONTACTED, 329 RawContacts.LAST_TIME_CONTACTED, 330 RawContacts.CUSTOM_RINGTONE, 331 RawContacts.SEND_TO_VOICEMAIL, 332 RawContacts.STARRED, 333 RawContacts.PINNED, 334 RawContacts.AGGREGATION_MODE, 335 RawContacts.SYNC1, 336 RawContacts.SYNC2, 337 RawContacts.SYNC3, 338 RawContacts.SYNC4, 339 }); 340 } 341 342 public void testDataProjection() { 343 assertProjection(Data.CONTENT_URI, new String[]{ 344 Data._ID, 345 Data.RAW_CONTACT_ID, 346 Data.DATA_VERSION, 347 Data.IS_PRIMARY, 348 Data.IS_SUPER_PRIMARY, 349 Data.RES_PACKAGE, 350 Data.MIMETYPE, 351 Data.DATA1, 352 Data.DATA2, 353 Data.DATA3, 354 Data.DATA4, 355 Data.DATA5, 356 Data.DATA6, 357 Data.DATA7, 358 Data.DATA8, 359 Data.DATA9, 360 Data.DATA10, 361 Data.DATA11, 362 Data.DATA12, 363 Data.DATA13, 364 Data.DATA14, 365 Data.DATA15, 366 Data.SYNC1, 367 Data.SYNC2, 368 Data.SYNC3, 369 Data.SYNC4, 370 Data.CONTACT_ID, 371 Data.PRESENCE, 372 Data.CHAT_CAPABILITY, 373 Data.STATUS, 374 Data.STATUS_TIMESTAMP, 375 Data.STATUS_RES_PACKAGE, 376 Data.STATUS_LABEL, 377 Data.STATUS_ICON, 378 Data.TIMES_USED, 379 Data.LAST_TIME_USED, 380 RawContacts.ACCOUNT_NAME, 381 RawContacts.ACCOUNT_TYPE, 382 RawContacts.DATA_SET, 383 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 384 RawContacts.SOURCE_ID, 385 RawContacts.VERSION, 386 RawContacts.DIRTY, 387 RawContacts.NAME_VERIFIED, 388 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 389 Contacts._ID, 390 Contacts.DISPLAY_NAME_PRIMARY, 391 Contacts.DISPLAY_NAME_ALTERNATIVE, 392 Contacts.DISPLAY_NAME_SOURCE, 393 Contacts.PHONETIC_NAME, 394 Contacts.PHONETIC_NAME_STYLE, 395 Contacts.SORT_KEY_PRIMARY, 396 Contacts.SORT_KEY_ALTERNATIVE, 397 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 398 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 399 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 400 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 401 Contacts.LAST_TIME_CONTACTED, 402 Contacts.TIMES_CONTACTED, 403 Contacts.STARRED, 404 Contacts.PINNED, 405 Contacts.IN_DEFAULT_DIRECTORY, 406 Contacts.IN_VISIBLE_GROUP, 407 Contacts.PHOTO_ID, 408 Contacts.PHOTO_FILE_ID, 409 Contacts.PHOTO_URI, 410 Contacts.PHOTO_THUMBNAIL_URI, 411 Contacts.CUSTOM_RINGTONE, 412 Contacts.SEND_TO_VOICEMAIL, 413 Contacts.LOOKUP_KEY, 414 Contacts.NAME_RAW_CONTACT_ID, 415 Contacts.HAS_PHONE_NUMBER, 416 Contacts.CONTACT_PRESENCE, 417 Contacts.CONTACT_CHAT_CAPABILITY, 418 Contacts.CONTACT_STATUS, 419 Contacts.CONTACT_STATUS_TIMESTAMP, 420 Contacts.CONTACT_STATUS_RES_PACKAGE, 421 Contacts.CONTACT_STATUS_LABEL, 422 Contacts.CONTACT_STATUS_ICON, 423 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 424 GroupMembership.GROUP_SOURCE_ID, 425 }); 426 } 427 428 public void testDistinctDataProjection() { 429 assertProjection(Phone.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(), 430 new String[]{ 431 Data._ID, 432 Data.DATA_VERSION, 433 Data.IS_PRIMARY, 434 Data.IS_SUPER_PRIMARY, 435 Data.RES_PACKAGE, 436 Data.MIMETYPE, 437 Data.DATA1, 438 Data.DATA2, 439 Data.DATA3, 440 Data.DATA4, 441 Data.DATA5, 442 Data.DATA6, 443 Data.DATA7, 444 Data.DATA8, 445 Data.DATA9, 446 Data.DATA10, 447 Data.DATA11, 448 Data.DATA12, 449 Data.DATA13, 450 Data.DATA14, 451 Data.DATA15, 452 Data.SYNC1, 453 Data.SYNC2, 454 Data.SYNC3, 455 Data.SYNC4, 456 Data.CONTACT_ID, 457 Data.PRESENCE, 458 Data.CHAT_CAPABILITY, 459 Data.STATUS, 460 Data.STATUS_TIMESTAMP, 461 Data.STATUS_RES_PACKAGE, 462 Data.STATUS_LABEL, 463 Data.STATUS_ICON, 464 Data.TIMES_USED, 465 Data.LAST_TIME_USED, 466 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 467 Contacts._ID, 468 Contacts.DISPLAY_NAME_PRIMARY, 469 Contacts.DISPLAY_NAME_ALTERNATIVE, 470 Contacts.DISPLAY_NAME_SOURCE, 471 Contacts.PHONETIC_NAME, 472 Contacts.PHONETIC_NAME_STYLE, 473 Contacts.SORT_KEY_PRIMARY, 474 Contacts.SORT_KEY_ALTERNATIVE, 475 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 476 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 477 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 478 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 479 Contacts.LAST_TIME_CONTACTED, 480 Contacts.TIMES_CONTACTED, 481 Contacts.STARRED, 482 Contacts.PINNED, 483 Contacts.IN_DEFAULT_DIRECTORY, 484 Contacts.IN_VISIBLE_GROUP, 485 Contacts.PHOTO_ID, 486 Contacts.PHOTO_FILE_ID, 487 Contacts.PHOTO_URI, 488 Contacts.PHOTO_THUMBNAIL_URI, 489 Contacts.HAS_PHONE_NUMBER, 490 Contacts.CUSTOM_RINGTONE, 491 Contacts.SEND_TO_VOICEMAIL, 492 Contacts.LOOKUP_KEY, 493 Contacts.CONTACT_PRESENCE, 494 Contacts.CONTACT_CHAT_CAPABILITY, 495 Contacts.CONTACT_STATUS, 496 Contacts.CONTACT_STATUS_TIMESTAMP, 497 Contacts.CONTACT_STATUS_RES_PACKAGE, 498 Contacts.CONTACT_STATUS_LABEL, 499 Contacts.CONTACT_STATUS_ICON, 500 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 501 GroupMembership.GROUP_SOURCE_ID, 502 }); 503 } 504 505 public void testEntityProjection() { 506 assertProjection( 507 Uri.withAppendedPath(ContentUris.withAppendedId(Contacts.CONTENT_URI, 0), 508 Contacts.Entity.CONTENT_DIRECTORY), 509 new String[]{ 510 Contacts.Entity._ID, 511 Contacts.Entity.DATA_ID, 512 Contacts.Entity.RAW_CONTACT_ID, 513 Data.DATA_VERSION, 514 Data.IS_PRIMARY, 515 Data.IS_SUPER_PRIMARY, 516 Data.RES_PACKAGE, 517 Data.MIMETYPE, 518 Data.DATA1, 519 Data.DATA2, 520 Data.DATA3, 521 Data.DATA4, 522 Data.DATA5, 523 Data.DATA6, 524 Data.DATA7, 525 Data.DATA8, 526 Data.DATA9, 527 Data.DATA10, 528 Data.DATA11, 529 Data.DATA12, 530 Data.DATA13, 531 Data.DATA14, 532 Data.DATA15, 533 Data.SYNC1, 534 Data.SYNC2, 535 Data.SYNC3, 536 Data.SYNC4, 537 Data.CONTACT_ID, 538 Data.PRESENCE, 539 Data.CHAT_CAPABILITY, 540 Data.STATUS, 541 Data.STATUS_TIMESTAMP, 542 Data.STATUS_RES_PACKAGE, 543 Data.STATUS_LABEL, 544 Data.STATUS_ICON, 545 RawContacts.ACCOUNT_NAME, 546 RawContacts.ACCOUNT_TYPE, 547 RawContacts.DATA_SET, 548 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 549 RawContacts.SOURCE_ID, 550 RawContacts.VERSION, 551 RawContacts.DELETED, 552 RawContacts.DIRTY, 553 RawContacts.NAME_VERIFIED, 554 RawContacts.SYNC1, 555 RawContacts.SYNC2, 556 RawContacts.SYNC3, 557 RawContacts.SYNC4, 558 Contacts._ID, 559 Contacts.DISPLAY_NAME_PRIMARY, 560 Contacts.DISPLAY_NAME_ALTERNATIVE, 561 Contacts.DISPLAY_NAME_SOURCE, 562 Contacts.PHONETIC_NAME, 563 Contacts.PHONETIC_NAME_STYLE, 564 Contacts.SORT_KEY_PRIMARY, 565 Contacts.SORT_KEY_ALTERNATIVE, 566 ContactsColumns.PHONEBOOK_LABEL_PRIMARY, 567 ContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 568 ContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE, 569 ContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 570 Contacts.LAST_TIME_CONTACTED, 571 Contacts.TIMES_CONTACTED, 572 Contacts.STARRED, 573 Contacts.PINNED, 574 Contacts.IN_DEFAULT_DIRECTORY, 575 Contacts.IN_VISIBLE_GROUP, 576 Contacts.PHOTO_ID, 577 Contacts.PHOTO_FILE_ID, 578 Contacts.PHOTO_URI, 579 Contacts.PHOTO_THUMBNAIL_URI, 580 Contacts.CUSTOM_RINGTONE, 581 Contacts.SEND_TO_VOICEMAIL, 582 Contacts.IS_USER_PROFILE, 583 Contacts.LOOKUP_KEY, 584 Contacts.NAME_RAW_CONTACT_ID, 585 Contacts.HAS_PHONE_NUMBER, 586 Contacts.CONTACT_PRESENCE, 587 Contacts.CONTACT_CHAT_CAPABILITY, 588 Contacts.CONTACT_STATUS, 589 Contacts.CONTACT_STATUS_TIMESTAMP, 590 Contacts.CONTACT_STATUS_RES_PACKAGE, 591 Contacts.CONTACT_STATUS_LABEL, 592 Contacts.CONTACT_STATUS_ICON, 593 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP, 594 GroupMembership.GROUP_SOURCE_ID, 595 DataUsageStatColumns.TIMES_USED, 596 DataUsageStatColumns.LAST_TIME_USED, 597 }); 598 } 599 600 public void testRawEntityProjection() { 601 assertProjection(RawContactsEntity.CONTENT_URI, new String[]{ 602 RawContacts.Entity.DATA_ID, 603 RawContacts._ID, 604 RawContacts.CONTACT_ID, 605 RawContacts.ACCOUNT_NAME, 606 RawContacts.ACCOUNT_TYPE, 607 RawContacts.DATA_SET, 608 RawContacts.ACCOUNT_TYPE_AND_DATA_SET, 609 RawContacts.SOURCE_ID, 610 RawContacts.VERSION, 611 RawContacts.DIRTY, 612 RawContacts.NAME_VERIFIED, 613 RawContacts.DELETED, 614 RawContacts.SYNC1, 615 RawContacts.SYNC2, 616 RawContacts.SYNC3, 617 RawContacts.SYNC4, 618 RawContacts.STARRED, 619 RawContacts.RAW_CONTACT_IS_USER_PROFILE, 620 Data.DATA_VERSION, 621 Data.IS_PRIMARY, 622 Data.IS_SUPER_PRIMARY, 623 Data.RES_PACKAGE, 624 Data.MIMETYPE, 625 Data.DATA1, 626 Data.DATA2, 627 Data.DATA3, 628 Data.DATA4, 629 Data.DATA5, 630 Data.DATA6, 631 Data.DATA7, 632 Data.DATA8, 633 Data.DATA9, 634 Data.DATA10, 635 Data.DATA11, 636 Data.DATA12, 637 Data.DATA13, 638 Data.DATA14, 639 Data.DATA15, 640 Data.SYNC1, 641 Data.SYNC2, 642 Data.SYNC3, 643 Data.SYNC4, 644 GroupMembership.GROUP_SOURCE_ID, 645 }); 646 } 647 648 public void testPhoneLookupProjection() { 649 assertProjection(PhoneLookup.CONTENT_FILTER_URI.buildUpon().appendPath("123").build(), 650 new String[]{ 651 PhoneLookup._ID, 652 PhoneLookup.LOOKUP_KEY, 653 PhoneLookup.DISPLAY_NAME, 654 PhoneLookup.LAST_TIME_CONTACTED, 655 PhoneLookup.TIMES_CONTACTED, 656 PhoneLookup.STARRED, 657 PhoneLookup.IN_DEFAULT_DIRECTORY, 658 PhoneLookup.IN_VISIBLE_GROUP, 659 PhoneLookup.PHOTO_FILE_ID, 660 PhoneLookup.PHOTO_ID, 661 PhoneLookup.PHOTO_URI, 662 PhoneLookup.PHOTO_THUMBNAIL_URI, 663 PhoneLookup.CUSTOM_RINGTONE, 664 PhoneLookup.HAS_PHONE_NUMBER, 665 PhoneLookup.SEND_TO_VOICEMAIL, 666 PhoneLookup.NUMBER, 667 PhoneLookup.TYPE, 668 PhoneLookup.LABEL, 669 PhoneLookup.NORMALIZED_NUMBER, 670 }); 671 } 672 673 public void testPhoneLookupEnterpriseProjection() { 674 assertProjection(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI 675 .buildUpon().appendPath("123").build(), 676 new String[]{ 677 PhoneLookup._ID, 678 PhoneLookup.LOOKUP_KEY, 679 PhoneLookup.DISPLAY_NAME, 680 PhoneLookup.LAST_TIME_CONTACTED, 681 PhoneLookup.TIMES_CONTACTED, 682 PhoneLookup.STARRED, 683 PhoneLookup.IN_DEFAULT_DIRECTORY, 684 PhoneLookup.IN_VISIBLE_GROUP, 685 PhoneLookup.PHOTO_FILE_ID, 686 PhoneLookup.PHOTO_ID, 687 PhoneLookup.PHOTO_URI, 688 PhoneLookup.PHOTO_THUMBNAIL_URI, 689 PhoneLookup.CUSTOM_RINGTONE, 690 PhoneLookup.HAS_PHONE_NUMBER, 691 PhoneLookup.SEND_TO_VOICEMAIL, 692 PhoneLookup.NUMBER, 693 PhoneLookup.TYPE, 694 PhoneLookup.LABEL, 695 PhoneLookup.NORMALIZED_NUMBER, 696 }); 697 } 698 699 public void testGroupsProjection() { 700 assertProjection(Groups.CONTENT_URI, new String[]{ 701 Groups._ID, 702 Groups.ACCOUNT_NAME, 703 Groups.ACCOUNT_TYPE, 704 Groups.DATA_SET, 705 Groups.ACCOUNT_TYPE_AND_DATA_SET, 706 Groups.SOURCE_ID, 707 Groups.DIRTY, 708 Groups.VERSION, 709 Groups.RES_PACKAGE, 710 Groups.TITLE, 711 Groups.TITLE_RES, 712 Groups.GROUP_VISIBLE, 713 Groups.SYSTEM_ID, 714 Groups.DELETED, 715 Groups.NOTES, 716 Groups.SHOULD_SYNC, 717 Groups.FAVORITES, 718 Groups.AUTO_ADD, 719 Groups.GROUP_IS_READ_ONLY, 720 Groups.SYNC1, 721 Groups.SYNC2, 722 Groups.SYNC3, 723 Groups.SYNC4, 724 }); 725 } 726 727 public void testGroupsSummaryProjection() { 728 assertProjection(Groups.CONTENT_SUMMARY_URI, new String[]{ 729 Groups._ID, 730 Groups.ACCOUNT_NAME, 731 Groups.ACCOUNT_TYPE, 732 Groups.DATA_SET, 733 Groups.ACCOUNT_TYPE_AND_DATA_SET, 734 Groups.SOURCE_ID, 735 Groups.DIRTY, 736 Groups.VERSION, 737 Groups.RES_PACKAGE, 738 Groups.TITLE, 739 Groups.TITLE_RES, 740 Groups.GROUP_VISIBLE, 741 Groups.SYSTEM_ID, 742 Groups.DELETED, 743 Groups.NOTES, 744 Groups.SHOULD_SYNC, 745 Groups.FAVORITES, 746 Groups.AUTO_ADD, 747 Groups.GROUP_IS_READ_ONLY, 748 Groups.SYNC1, 749 Groups.SYNC2, 750 Groups.SYNC3, 751 Groups.SYNC4, 752 Groups.SUMMARY_COUNT, 753 Groups.SUMMARY_WITH_PHONES, 754 Groups.SUMMARY_GROUP_COUNT_PER_ACCOUNT, 755 }); 756 } 757 758 public void testAggregateExceptionProjection() { 759 assertProjection(AggregationExceptions.CONTENT_URI, new String[]{ 760 AggregationExceptionColumns._ID, 761 AggregationExceptions.TYPE, 762 AggregationExceptions.RAW_CONTACT_ID1, 763 AggregationExceptions.RAW_CONTACT_ID2, 764 }); 765 } 766 767 public void testSettingsProjection() { 768 assertProjection(Settings.CONTENT_URI, new String[]{ 769 Settings.ACCOUNT_NAME, 770 Settings.ACCOUNT_TYPE, 771 Settings.DATA_SET, 772 Settings.UNGROUPED_VISIBLE, 773 Settings.SHOULD_SYNC, 774 Settings.ANY_UNSYNCED, 775 Settings.UNGROUPED_COUNT, 776 Settings.UNGROUPED_WITH_PHONES, 777 }); 778 } 779 780 public void testStatusUpdatesProjection() { 781 assertProjection(StatusUpdates.CONTENT_URI, new String[]{ 782 PresenceColumns.RAW_CONTACT_ID, 783 StatusUpdates.DATA_ID, 784 StatusUpdates.IM_ACCOUNT, 785 StatusUpdates.IM_HANDLE, 786 StatusUpdates.PROTOCOL, 787 StatusUpdates.CUSTOM_PROTOCOL, 788 StatusUpdates.PRESENCE, 789 StatusUpdates.CHAT_CAPABILITY, 790 StatusUpdates.STATUS, 791 StatusUpdates.STATUS_TIMESTAMP, 792 StatusUpdates.STATUS_RES_PACKAGE, 793 StatusUpdates.STATUS_ICON, 794 StatusUpdates.STATUS_LABEL, 795 }); 796 } 797 798 public void testDirectoryProjection() { 799 assertProjection(Directory.CONTENT_URI, new String[]{ 800 Directory._ID, 801 Directory.PACKAGE_NAME, 802 Directory.TYPE_RESOURCE_ID, 803 Directory.DISPLAY_NAME, 804 Directory.DIRECTORY_AUTHORITY, 805 Directory.ACCOUNT_TYPE, 806 Directory.ACCOUNT_NAME, 807 Directory.EXPORT_SUPPORT, 808 Directory.SHORTCUT_SUPPORT, 809 Directory.PHOTO_SUPPORT, 810 }); 811 } 812 813 public void testRawContactsInsert() { 814 ContentValues values = new ContentValues(); 815 816 values.put(RawContacts.ACCOUNT_NAME, "a"); 817 values.put(RawContacts.ACCOUNT_TYPE, "b"); 818 values.put(RawContacts.DATA_SET, "ds"); 819 values.put(RawContacts.SOURCE_ID, "c"); 820 values.put(RawContacts.VERSION, 42); 821 values.put(RawContacts.DIRTY, 1); 822 values.put(RawContacts.DELETED, 1); 823 values.put(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED); 824 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 825 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 826 values.put(RawContacts.LAST_TIME_CONTACTED, 12345); 827 values.put(RawContacts.STARRED, 1); 828 values.put(RawContacts.SYNC1, "e"); 829 values.put(RawContacts.SYNC2, "f"); 830 values.put(RawContacts.SYNC3, "g"); 831 values.put(RawContacts.SYNC4, "h"); 832 833 Uri rowUri = mResolver.insert(RawContacts.CONTENT_URI, values); 834 long rawContactId = ContentUris.parseId(rowUri); 835 836 assertStoredValues(rowUri, values); 837 assertSelection(RawContacts.CONTENT_URI, values, RawContacts._ID, rawContactId); 838 assertNetworkNotified(true); 839 } 840 841 public void testDataDirectoryWithLookupUri() { 842 ContentValues values = new ContentValues(); 843 844 long rawContactId = RawContactUtil.createRawContactWithName(mResolver); 845 insertPhoneNumber(rawContactId, "555-GOOG-411"); 846 insertEmail(rawContactId, "google (at) android.com"); 847 848 long contactId = queryContactId(rawContactId); 849 String lookupKey = queryLookupKey(contactId); 850 851 // Complete and valid lookup URI 852 Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey); 853 Uri dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY); 854 855 assertDataRows(dataUri, values); 856 857 // Complete but stale lookup URI 858 lookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey); 859 dataUri = Uri.withAppendedPath(lookupUri, Contacts.Data.CONTENT_DIRECTORY); 860 assertDataRows(dataUri, values); 861 862 // Incomplete lookup URI (lookup key only, no contact ID) 863 dataUri = Uri.withAppendedPath(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, 864 lookupKey), Contacts.Data.CONTENT_DIRECTORY); 865 assertDataRows(dataUri, values); 866 } 867 868 private void assertDataRows(Uri dataUri, ContentValues values) { 869 Cursor cursor = mResolver.query(dataUri, new String[]{ Data.DATA1 }, null, null, Data._ID); 870 assertEquals(3, cursor.getCount()); 871 cursor.moveToFirst(); 872 values.put(Data.DATA1, "John Doe"); 873 assertCursorValues(cursor, values); 874 875 cursor.moveToNext(); 876 values.put(Data.DATA1, "555-GOOG-411"); 877 assertCursorValues(cursor, values); 878 879 cursor.moveToNext(); 880 values.put(Data.DATA1, "google (at) android.com"); 881 assertCursorValues(cursor, values); 882 883 cursor.close(); 884 } 885 886 public void testContactEntitiesWithIdBasedUri() { 887 ContentValues values = new ContentValues(); 888 Account account1 = new Account("act1", "actype1"); 889 Account account2 = new Account("act2", "actype2"); 890 891 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, account1); 892 insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk"); 893 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90, 894 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 895 896 long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2); 897 setAggregationException( 898 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 899 900 long contactId = queryContactId(rawContactId1); 901 902 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 903 Uri entityUri = Uri.withAppendedPath(contactUri, Contacts.Entity.CONTENT_DIRECTORY); 904 905 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 906 } 907 908 public void testContactEntitiesWithLookupUri() { 909 ContentValues values = new ContentValues(); 910 Account account1 = new Account("act1", "actype1"); 911 Account account2 = new Account("act2", "actype2"); 912 913 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, account1); 914 insertImHandle(rawContactId1, Im.PROTOCOL_GOOGLE_TALK, null, "gtalk"); 915 insertStatusUpdate(Im.PROTOCOL_GOOGLE_TALK, null, "gtalk", StatusUpdates.IDLE, "Busy", 90, 916 StatusUpdates.CAPABILITY_HAS_CAMERA, false); 917 918 long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2); 919 setAggregationException( 920 AggregationExceptions.TYPE_KEEP_TOGETHER, rawContactId1, rawContactId2); 921 922 long contactId = queryContactId(rawContactId1); 923 String lookupKey = queryLookupKey(contactId); 924 925 // First try with a matching contact ID 926 Uri contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey); 927 Uri entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY); 928 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 929 930 // Now try with a contact ID mismatch 931 contactLookupUri = ContactsContract.Contacts.getLookupUri(contactId + 1, lookupKey); 932 entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY); 933 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 934 935 // Now try without an ID altogether 936 contactLookupUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey); 937 entityUri = Uri.withAppendedPath(contactLookupUri, Contacts.Entity.CONTENT_DIRECTORY); 938 assertEntityRows(entityUri, contactId, rawContactId1, rawContactId2); 939 } 940 941 private void assertEntityRows(Uri entityUri, long contactId, long rawContactId1, 942 long rawContactId2) { 943 ContentValues values = new ContentValues(); 944 945 Cursor cursor = mResolver.query(entityUri, null, null, null, 946 Contacts.Entity.RAW_CONTACT_ID + "," + Contacts.Entity.DATA_ID); 947 assertEquals(3, cursor.getCount()); 948 949 // First row - name 950 cursor.moveToFirst(); 951 values.put(Contacts.Entity.CONTACT_ID, contactId); 952 values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1); 953 values.put(Contacts.Entity.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 954 values.put(Contacts.Entity.DATA1, "John Doe"); 955 values.put(Contacts.Entity.ACCOUNT_NAME, "act1"); 956 values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1"); 957 values.put(Contacts.Entity.DISPLAY_NAME, "John Doe"); 958 values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John"); 959 values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1); 960 values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 961 values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE); 962 values.put(Contacts.Entity.CONTACT_STATUS, "Busy"); 963 values.putNull(Contacts.Entity.PRESENCE); 964 assertCursorValues(cursor, values); 965 966 // Second row - IM 967 cursor.moveToNext(); 968 values.put(Contacts.Entity.CONTACT_ID, contactId); 969 values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId1); 970 values.put(Contacts.Entity.MIMETYPE, Im.CONTENT_ITEM_TYPE); 971 values.put(Contacts.Entity.DATA1, "gtalk"); 972 values.put(Contacts.Entity.ACCOUNT_NAME, "act1"); 973 values.put(Contacts.Entity.ACCOUNT_TYPE, "actype1"); 974 values.put(Contacts.Entity.DISPLAY_NAME, "John Doe"); 975 values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John"); 976 values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1); 977 values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 978 values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE); 979 values.put(Contacts.Entity.CONTACT_STATUS, "Busy"); 980 values.put(Contacts.Entity.PRESENCE, StatusUpdates.IDLE); 981 assertCursorValues(cursor, values); 982 983 // Third row - second raw contact, not data 984 cursor.moveToNext(); 985 values.put(Contacts.Entity.CONTACT_ID, contactId); 986 values.put(Contacts.Entity.RAW_CONTACT_ID, rawContactId2); 987 values.putNull(Contacts.Entity.MIMETYPE); 988 values.putNull(Contacts.Entity.DATA_ID); 989 values.putNull(Contacts.Entity.DATA1); 990 values.put(Contacts.Entity.ACCOUNT_NAME, "act2"); 991 values.put(Contacts.Entity.ACCOUNT_TYPE, "actype2"); 992 values.put(Contacts.Entity.DISPLAY_NAME, "John Doe"); 993 values.put(Contacts.Entity.DISPLAY_NAME_ALTERNATIVE, "Doe, John"); 994 values.put(Contacts.Entity.NAME_RAW_CONTACT_ID, rawContactId1); 995 values.put(Contacts.Entity.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 996 values.put(Contacts.Entity.CONTACT_PRESENCE, StatusUpdates.IDLE); 997 values.put(Contacts.Entity.CONTACT_STATUS, "Busy"); 998 values.putNull(Contacts.Entity.PRESENCE); 999 assertCursorValues(cursor, values); 1000 1001 cursor.close(); 1002 } 1003 1004 public void testDataInsert() { 1005 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 1006 1007 ContentValues values = new ContentValues(); 1008 putDataValues(values, rawContactId); 1009 Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 1010 long dataId = ContentUris.parseId(dataUri); 1011 1012 long contactId = queryContactId(rawContactId); 1013 values.put(RawContacts.CONTACT_ID, contactId); 1014 assertStoredValues(dataUri, values); 1015 1016 assertSelection(Data.CONTENT_URI, values, Data._ID, dataId); 1017 1018 // Access the same data through the directory under RawContacts 1019 Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId); 1020 Uri rawContactDataUri = 1021 Uri.withAppendedPath(rawContactUri, RawContacts.Data.CONTENT_DIRECTORY); 1022 assertSelection(rawContactDataUri, values, Data._ID, dataId); 1023 1024 // Access the same data through the directory under Contacts 1025 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 1026 Uri contactDataUri = Uri.withAppendedPath(contactUri, Contacts.Data.CONTENT_DIRECTORY); 1027 assertSelection(contactDataUri, values, Data._ID, dataId); 1028 assertNetworkNotified(true); 1029 } 1030 1031 public void testDataInsertPhoneNumberTooLongIsTrimmed() { 1032 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 1033 1034 ContentValues values = new ContentValues(); 1035 values.put(Data.RAW_CONTACT_ID, rawContactId); 1036 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1037 final StringBuilder sb = new StringBuilder(); 1038 for (int i = 0; i < 300; i++) { 1039 sb.append("12345"); 1040 } 1041 final String phoneNumber1500Chars = sb.toString(); 1042 values.put(Phone.NUMBER, phoneNumber1500Chars); 1043 1044 Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 1045 final long dataId = ContentUris.parseId(dataUri); 1046 1047 sb.setLength(0); 1048 for (int i = 0; i < 200; i++) { 1049 sb.append("12345"); 1050 } 1051 final String phoneNumber1000Chars = sb.toString(); 1052 final ContentValues expected = new ContentValues(); 1053 expected.put(Phone.NUMBER, phoneNumber1000Chars); 1054 assertSelection(dataUri, expected, Data._ID, dataId); 1055 } 1056 1057 public void testRawContactDataQuery() { 1058 Account account1 = new Account("a", "b"); 1059 Account account2 = new Account("c", "d"); 1060 long rawContactId1 = RawContactUtil.createRawContact(mResolver, account1); 1061 Uri dataUri1 = DataUtil.insertStructuredName(mResolver, rawContactId1, "John", "Doe"); 1062 long rawContactId2 = RawContactUtil.createRawContact(mResolver, account2); 1063 Uri dataUri2 = DataUtil.insertStructuredName(mResolver, rawContactId2, "Jane", "Doe"); 1064 1065 Uri uri1 = TestUtil.maybeAddAccountQueryParameters(dataUri1, account1); 1066 Uri uri2 = TestUtil.maybeAddAccountQueryParameters(dataUri2, account2); 1067 assertStoredValue(uri1, Data._ID, ContentUris.parseId(dataUri1)) ; 1068 assertStoredValue(uri2, Data._ID, ContentUris.parseId(dataUri2)) ; 1069 } 1070 1071 public void testPhonesQuery() { 1072 1073 ContentValues values = new ContentValues(); 1074 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1075 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1076 values.put(RawContacts.LAST_TIME_CONTACTED, 12345); 1077 values.put(RawContacts.TIMES_CONTACTED, 54321); 1078 values.put(RawContacts.STARRED, 1); 1079 1080 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1081 long rawContactId = ContentUris.parseId(rawContactUri); 1082 1083 DataUtil.insertStructuredName(mResolver, rawContactId, "Meghan", "Knox"); 1084 Uri uri = insertPhoneNumber(rawContactId, "18004664411"); 1085 long phoneId = ContentUris.parseId(uri); 1086 1087 1088 long contactId = queryContactId(rawContactId); 1089 values.clear(); 1090 values.put(Data._ID, phoneId); 1091 values.put(Data.RAW_CONTACT_ID, rawContactId); 1092 values.put(RawContacts.CONTACT_ID, contactId); 1093 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1094 values.put(Phone.NUMBER, "18004664411"); 1095 values.put(Phone.TYPE, Phone.TYPE_HOME); 1096 values.putNull(Phone.LABEL); 1097 values.put(Contacts.DISPLAY_NAME, "Meghan Knox"); 1098 values.put(Contacts.CUSTOM_RINGTONE, "d"); 1099 values.put(Contacts.SEND_TO_VOICEMAIL, 1); 1100 values.put(Contacts.LAST_TIME_CONTACTED, 12345); 1101 values.put(Contacts.TIMES_CONTACTED, 54321); 1102 values.put(Contacts.STARRED, 1); 1103 1104 assertStoredValues(ContentUris.withAppendedId(Phone.CONTENT_URI, phoneId), values); 1105 assertSelection(Phone.CONTENT_URI, values, Data._ID, phoneId); 1106 } 1107 1108 public void testPhonesWithMergedContacts() { 1109 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 1110 insertPhoneNumber(rawContactId1, "123456789", true); 1111 1112 long rawContactId2 = RawContactUtil.createRawContact(mResolver); 1113 insertPhoneNumber(rawContactId2, "123456789", true); 1114 1115 setAggregationException(AggregationExceptions.TYPE_KEEP_SEPARATE, 1116 rawContactId1, rawContactId2); 1117 assertNotAggregated(rawContactId1, rawContactId2); 1118 1119 ContentValues values1 = new ContentValues(); 1120 values1.put(Contacts.DISPLAY_NAME, "123456789"); 1121 values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1122 values1.put(Phone.NUMBER, "123456789"); 1123 1124 // There are two phone numbers, so we should get two rows. 1125 assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1}); 1126 1127 // Now set the dedupe flag. But still we should get two rows, because they're two 1128 // different contacts. We only dedupe within each contact. 1129 final Uri dedupeUri = Phone.CONTENT_URI.buildUpon() 1130 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true") 1131 .build(); 1132 assertStoredValues(dedupeUri, new ContentValues[] {values1, values1}); 1133 1134 // Now join them into a single contact. 1135 setAggregationException(AggregationExceptions.TYPE_KEEP_TOGETHER, 1136 rawContactId1, rawContactId2); 1137 1138 assertAggregated(rawContactId1, rawContactId2, "123456789"); 1139 1140 // Contact merge won't affect the default result of Phone Uri, where we don't dedupe. 1141 assertStoredValues(Phone.CONTENT_URI, new ContentValues[] {values1, values1}); 1142 1143 // Now we dedupe them. 1144 assertStoredValues(dedupeUri, values1); 1145 } 1146 1147 public void testPhonesNormalizedNumber() { 1148 final long rawContactId = RawContactUtil.createRawContact(mResolver); 1149 1150 // Write both a number and a normalized number. Those should be written as-is 1151 final ContentValues values = new ContentValues(); 1152 values.put(Data.RAW_CONTACT_ID, rawContactId); 1153 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1154 values.put(Phone.NUMBER, "1234"); 1155 values.put(Phone.NORMALIZED_NUMBER, "5678"); 1156 values.put(Phone.TYPE, Phone.TYPE_HOME); 1157 1158 final Uri dataUri = mResolver.insert(Data.CONTENT_URI, values); 1159 1160 // Check the lookup table. 1161 assertEquals(1, 1162 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1234"), null, null)); 1163 assertEquals(1, 1164 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "5678"), null, null)); 1165 1166 // Check the data table. 1167 assertStoredValues(dataUri, 1168 cv(Phone.NUMBER, "1234", Phone.NORMALIZED_NUMBER, "5678") 1169 ); 1170 1171 // Replace both in an UPDATE 1172 values.clear(); 1173 values.put(Phone.NUMBER, "4321"); 1174 values.put(Phone.NORMALIZED_NUMBER, "8765"); 1175 mResolver.update(dataUri, values, null, null); 1176 assertEquals(0, 1177 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "1234"), null, null)); 1178 assertEquals(1, 1179 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "4321"), null, null)); 1180 assertEquals(0, 1181 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "5678"), null, null)); 1182 assertEquals(1, 1183 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null)); 1184 1185 assertStoredValues(dataUri, 1186 cv(Phone.NUMBER, "4321", Phone.NORMALIZED_NUMBER, "8765") 1187 ); 1188 1189 // Replace only NUMBER ==> NORMALIZED_NUMBER will be inferred (we test that by making 1190 // sure the old manual value can not be found anymore) 1191 values.clear(); 1192 values.put(Phone.NUMBER, "+1-800-466-5432"); 1193 mResolver.update(dataUri, values, null, null); 1194 assertEquals( 1195 1, 1196 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null, 1197 null)); 1198 assertEquals(0, 1199 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null)); 1200 1201 assertStoredValues(dataUri, 1202 cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432") 1203 ); 1204 1205 // Replace only NORMALIZED_NUMBER ==> call is ignored, things will be unchanged 1206 values.clear(); 1207 values.put(Phone.NORMALIZED_NUMBER, "8765"); 1208 mResolver.update(dataUri, values, null, null); 1209 assertEquals( 1210 1, 1211 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "+1-800-466-5432"), null, 1212 null)); 1213 assertEquals(0, 1214 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "8765"), null, null)); 1215 1216 assertStoredValues(dataUri, 1217 cv(Phone.NUMBER, "+1-800-466-5432", Phone.NORMALIZED_NUMBER, "+18004665432") 1218 ); 1219 1220 // Replace NUMBER with an "invalid" number which can't be normalized. It should clear 1221 // NORMALIZED_NUMBER. 1222 1223 // 1. Set 999 to NORMALIZED_NUMBER explicitly. 1224 values.clear(); 1225 values.put(Phone.NUMBER, "888"); 1226 values.put(Phone.NORMALIZED_NUMBER, "999"); 1227 mResolver.update(dataUri, values, null, null); 1228 1229 assertEquals(1, 1230 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null)); 1231 1232 assertStoredValues(dataUri, 1233 cv(Phone.NUMBER, "888", Phone.NORMALIZED_NUMBER, "999") 1234 ); 1235 1236 // 2. Set an invalid number to NUMBER. 1237 values.clear(); 1238 values.put(Phone.NUMBER, "1"); 1239 mResolver.update(dataUri, values, null, null); 1240 1241 assertEquals(0, 1242 getCount(Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, "999"), null, null)); 1243 1244 assertStoredValues(dataUri, 1245 cv(Phone.NUMBER, "1", Phone.NORMALIZED_NUMBER, null) 1246 ); 1247 } 1248 1249 public void testPhonesFilterQuery() { 1250 testPhonesFilterQueryInter(Phone.CONTENT_FILTER_URI); 1251 } 1252 1253 /** 1254 * A convenient method for {@link #testPhonesFilterQuery()} and 1255 * {@link #testCallablesFilterQuery()}. 1256 * 1257 * This confirms if both URIs return identical results for phone-only contacts and 1258 * appropriately different results for contacts with sip addresses. 1259 * 1260 * @param baseFilterUri Either {@link Phone#CONTENT_FILTER_URI} or 1261 * {@link Callable#CONTENT_FILTER_URI}. 1262 */ 1263 private void testPhonesFilterQueryInter(Uri baseFilterUri) { 1264 assertTrue("Unsupported Uri (" + baseFilterUri + ")", 1265 Phone.CONTENT_FILTER_URI.equals(baseFilterUri) 1266 || Callable.CONTENT_FILTER_URI.equals(baseFilterUri)); 1267 1268 final long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Hot", 1269 "Tamale", TestUtil.ACCOUNT_1); 1270 insertPhoneNumber(rawContactId1, "1-800-466-4411"); 1271 1272 final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Chilled", 1273 "Guacamole", TestUtil.ACCOUNT_2); 1274 insertPhoneNumber(rawContactId2, "1-800-466-5432"); 1275 insertPhoneNumber(rawContactId2, "0 (at) example.com", false, Phone.TYPE_PAGER); 1276 insertPhoneNumber(rawContactId2, "1 (at) example.com", false, Phone.TYPE_PAGER); 1277 1278 final Uri filterUri1 = Uri.withAppendedPath(baseFilterUri, "tamale"); 1279 ContentValues values = new ContentValues(); 1280 values.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 1281 values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1282 values.put(Phone.NUMBER, "1-800-466-4411"); 1283 values.put(Phone.TYPE, Phone.TYPE_HOME); 1284 values.putNull(Phone.LABEL); 1285 assertStoredValuesWithProjection(filterUri1, values); 1286 1287 final Uri filterUri2 = Uri.withAppendedPath(baseFilterUri, "1-800-GOOG-411"); 1288 assertStoredValues(filterUri2, values); 1289 1290 final Uri filterUri3 = Uri.withAppendedPath(baseFilterUri, "18004664"); 1291 assertStoredValues(filterUri3, values); 1292 1293 final Uri filterUri4 = Uri.withAppendedPath(baseFilterUri, "encilada"); 1294 assertEquals(0, getCount(filterUri4, null, null)); 1295 1296 final Uri filterUri5 = Uri.withAppendedPath(baseFilterUri, "*"); 1297 assertEquals(0, getCount(filterUri5, null, null)); 1298 1299 ContentValues values1 = new ContentValues(); 1300 values1.put(Contacts.DISPLAY_NAME, "Chilled Guacamole"); 1301 values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1302 values1.put(Phone.NUMBER, "1-800-466-5432"); 1303 values1.put(Phone.TYPE, Phone.TYPE_HOME); 1304 values1.putNull(Phone.LABEL); 1305 1306 ContentValues values2 = new ContentValues(); 1307 values2.put(Contacts.DISPLAY_NAME, "Chilled Guacamole"); 1308 values2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1309 values2.put(Phone.NUMBER, "0 (at) example.com"); 1310 values2.put(Phone.TYPE, Phone.TYPE_PAGER); 1311 values2.putNull(Phone.LABEL); 1312 1313 ContentValues values3 = new ContentValues(); 1314 values3.put(Contacts.DISPLAY_NAME, "Chilled Guacamole"); 1315 values3.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 1316 values3.put(Phone.NUMBER, "1 (at) example.com"); 1317 values3.put(Phone.TYPE, Phone.TYPE_PAGER); 1318 values3.putNull(Phone.LABEL); 1319 1320 final Uri filterUri6 = Uri.withAppendedPath(baseFilterUri, "Chilled"); 1321 assertStoredValues(filterUri6, new ContentValues[]{values1, values2, values3}); 1322 1323 // Insert a SIP address. From here, Phone URI and Callable URI may return different results 1324 // than each other. 1325 insertSipAddress(rawContactId1, "sip_hot_tamale (at) example.com"); 1326 insertSipAddress(rawContactId1, "sip:sip_hot (at) example.com"); 1327 1328 final Uri filterUri7 = Uri.withAppendedPath(baseFilterUri, "sip_hot"); 1329 final Uri filterUri8 = Uri.withAppendedPath(baseFilterUri, "sip_hot_tamale"); 1330 if (Callable.CONTENT_FILTER_URI.equals(baseFilterUri)) { 1331 ContentValues values4 = new ContentValues(); 1332 values4.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 1333 values4.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE); 1334 values4.put(SipAddress.SIP_ADDRESS, "sip_hot_tamale (at) example.com"); 1335 1336 ContentValues values5 = new ContentValues(); 1337 values5.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 1338 values5.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE); 1339 values5.put(SipAddress.SIP_ADDRESS, "sip:sip_hot (at) example.com"); 1340 assertStoredValues(filterUri1, new ContentValues[] {values, values4, values5}); 1341 1342 assertStoredValues(filterUri7, new ContentValues[] {values4, values5}); 1343 assertStoredValues(filterUri8, values4); 1344 } else { 1345 // Sip address should not affect Phone URI. 1346 assertStoredValuesWithProjection(filterUri1, values); 1347 assertEquals(0, getCount(filterUri7, null, null)); 1348 } 1349 1350 // Sanity test. Run tests for "Chilled Guacamole" again and see nothing changes 1351 // after the Sip address being inserted. 1352 assertStoredValues(filterUri2, values); 1353 assertEquals(0, getCount(filterUri4, null, null)); 1354 assertEquals(0, getCount(filterUri5, null, null)); 1355 assertStoredValues(filterUri6, new ContentValues[] {values1, values2, values3} ); 1356 } 1357 1358 public void testPhonesFilterSearchParams() { 1359 final long rid1 = RawContactUtil.createRawContactWithName(mResolver, "Dad", null); 1360 insertPhoneNumber(rid1, "123-456-7890"); 1361 1362 final long rid2 = RawContactUtil.createRawContactWithName(mResolver, "Mam", null); 1363 insertPhoneNumber(rid2, "323-123-4567"); 1364 1365 // By default, "dad" will match both the display name and the phone number. 1366 // Because "dad" is "323" after the dialpad conversion, it'll match "Mam" too. 1367 assertStoredValues( 1368 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad").build(), 1369 cv(Phone.DISPLAY_NAME, "Dad", Phone.NUMBER, "123-456-7890"), 1370 cv(Phone.DISPLAY_NAME, "Mam", Phone.NUMBER, "323-123-4567") 1371 ); 1372 assertStoredValues( 1373 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad") 1374 .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0") 1375 .build(), 1376 cv(Phone.DISPLAY_NAME, "Dad", Phone.NUMBER, "123-456-7890") 1377 ); 1378 1379 assertStoredValues( 1380 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad") 1381 .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0") 1382 .build(), 1383 cv(Phone.DISPLAY_NAME, "Mam", Phone.NUMBER, "323-123-4567") 1384 ); 1385 assertStoredValues( 1386 Phone.CONTENT_FILTER_URI.buildUpon().appendPath("dad") 1387 .appendQueryParameter(Phone.SEARCH_DISPLAY_NAME_KEY, "0") 1388 .appendQueryParameter(Phone.SEARCH_PHONE_NUMBER_KEY, "0") 1389 .build() 1390 ); 1391 } 1392 1393 public void testPhoneLookup() { 1394 ContentValues values = new ContentValues(); 1395 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1396 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1397 1398 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1399 long rawContactId = ContentUris.parseId(rawContactUri); 1400 1401 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale"); 1402 insertPhoneNumber(rawContactId, "18004664411"); 1403 1404 // We'll create two lookup records, 18004664411 and +18004664411, and the below lookup 1405 // will match both. 1406 1407 Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411"); 1408 1409 values.clear(); 1410 values.put(PhoneLookup._ID, queryContactId(rawContactId)); 1411 values.put(PhoneLookup.DISPLAY_NAME, "Hot Tamale"); 1412 values.put(PhoneLookup.NUMBER, "18004664411"); 1413 values.put(PhoneLookup.TYPE, Phone.TYPE_HOME); 1414 values.putNull(PhoneLookup.LABEL); 1415 values.put(PhoneLookup.CUSTOM_RINGTONE, "d"); 1416 values.put(PhoneLookup.SEND_TO_VOICEMAIL, 1); 1417 assertStoredValues(lookupUri1, null, null, new ContentValues[] {values, values}); 1418 1419 // In the context that 8004664411 is a valid number, "4664411" as a 1420 // call id should match to both "8004664411" and "+18004664411". 1421 Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "4664411"); 1422 assertEquals(2, getCount(lookupUri2, null, null)); 1423 1424 // A wrong area code 799 vs 800 should not be matched 1425 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "7994664411"); 1426 assertEquals(0, getCount(lookupUri2, null, null)); 1427 } 1428 1429 public void testPhoneLookupStarUseCases() { 1430 // Create two raw contacts with numbers "*123" and "12 3". This is a real life example 1431 // from b/13195334. 1432 final ContentValues values = new ContentValues(); 1433 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1434 long rawContactId = ContentUris.parseId(rawContactUri); 1435 DataUtil.insertStructuredName(mResolver, rawContactId, "Emergency", /* familyName =*/ null); 1436 insertPhoneNumber(rawContactId, "*123"); 1437 1438 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1439 rawContactId = ContentUris.parseId(rawContactUri); 1440 DataUtil.insertStructuredName(mResolver, rawContactId, "Voicemail", /* familyName =*/ null); 1441 insertPhoneNumber(rawContactId, "12 3"); 1442 1443 // Verify: "123" returns the "Voicemail" raw contact id. It should not match 1444 // a phone number that starts with a "*". 1445 Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "123"); 1446 values.clear(); 1447 values.put(PhoneLookup.DISPLAY_NAME, "Voicemail"); 1448 assertStoredValues(lookupUri, null, null, new ContentValues[] {values}); 1449 1450 lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "(1) 23"); 1451 values.clear(); 1452 values.put(PhoneLookup.DISPLAY_NAME, "Voicemail"); 1453 assertStoredValues(lookupUri, null, null, new ContentValues[] {values}); 1454 1455 // Verify: "*123" returns the "Emergency" raw contact id. 1456 lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*1-23"); 1457 values.clear(); 1458 values.put(PhoneLookup.DISPLAY_NAME, "Emergency"); 1459 assertStoredValues(lookupUri, null, null, new ContentValues[] {values}); 1460 } 1461 1462 public void testPhoneLookupReturnsNothingRatherThanStar() { 1463 // Create Emergency raw contact with "*123456789" number. 1464 final ContentValues values = new ContentValues(); 1465 final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1466 final long rawContactId1 = ContentUris.parseId(rawContactUri); 1467 DataUtil.insertStructuredName(mResolver, rawContactId1, "Emergency", 1468 /* familyName =*/ null); 1469 insertPhoneNumber(rawContactId1, "*123456789"); 1470 1471 // Lookup should return no results. It does not ignore stars even when no other matches. 1472 final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "123456789"); 1473 assertEquals(0, getCount(lookupUri, null, null)); 1474 } 1475 1476 public void testPhoneLookupReturnsNothingRatherThanMissStar() { 1477 // Create Voice Mail raw contact with "123456789" number. 1478 final ContentValues values = new ContentValues(); 1479 final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1480 final long rawContactId1 = ContentUris.parseId(rawContactUri); 1481 DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail", 1482 /* familyName =*/ null); 1483 insertPhoneNumber(rawContactId1, "123456789"); 1484 1485 // Lookup should return no results. It does not ignore stars even when no other matches. 1486 final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*123456789"); 1487 assertEquals(0, getCount(lookupUri, null, null)); 1488 } 1489 1490 public void testPhoneLookupStarNoFallbackMatch() { 1491 final ContentValues values = new ContentValues(); 1492 final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1493 final long rawContactId1 = ContentUris.parseId(rawContactUri); 1494 DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail", 1495 /* familyName =*/ null); 1496 insertPhoneNumber(rawContactId1, "*011123456789"); 1497 1498 // The numbers "+123456789" and "*011123456789" are a "fallback" match. The + is equivalent 1499 // to "011". This lookup should return no results. Lookup does not ignore 1500 // stars, even when doing a fallback lookup. 1501 final Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+123456789"); 1502 assertEquals(0, getCount(lookupUri, null, null)); 1503 } 1504 1505 public void testPhoneLookupStarNotBreakFallbackMatching() { 1506 // Create a raw contact with a phone number starting with "011" 1507 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()); 1508 long rawContactId = ContentUris.parseId(rawContactUri); 1509 DataUtil.insertStructuredName(mResolver, rawContactId, "No star", 1510 /* familyName =*/ null); 1511 insertPhoneNumber(rawContactId, "011123456789"); 1512 1513 // Create a raw contact with a phone number starting with "*011" 1514 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, new ContentValues()); 1515 rawContactId = ContentUris.parseId(rawContactUri); 1516 DataUtil.insertStructuredName(mResolver, rawContactId, "Has star", 1517 /* familyName =*/ null); 1518 insertPhoneNumber(rawContactId, "*011123456789"); 1519 1520 // A phone number starting with "+" can (fallback) match the same phone number starting 1521 // with "001". Verify that this fallback matching still occurs in the presence of 1522 // numbers starting with "*"s. 1523 final Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, 1524 "+123456789"); 1525 final ContentValues values = new ContentValues(); 1526 values.put(PhoneLookup.DISPLAY_NAME, "No star"); 1527 assertStoredValues(lookupUri1, null, null, new ContentValues[]{values}); 1528 } 1529 1530 public void testPhoneLookupExplicitProjection() { 1531 final ContentValues values = new ContentValues(); 1532 final Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1533 final long rawContactId1 = ContentUris.parseId(rawContactUri); 1534 DataUtil.insertStructuredName(mResolver, rawContactId1, "Voice mail", 1535 /* familyName =*/ null); 1536 insertPhoneNumber(rawContactId1, "+1234567"); 1537 1538 // Performing a query with a non-null projection with or without PhoneLookup.Number inside 1539 // it should not cause a crash. 1540 Uri lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "1234567"); 1541 String[] projection = new String[] {PhoneLookup.DISPLAY_NAME}; 1542 mResolver.query(lookupUri, projection, null, null, null); 1543 projection = new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.NUMBER}; 1544 mResolver.query(lookupUri, projection, null, null, null); 1545 1546 // Shouldn't crash for a fallback query either 1547 lookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "*0111234567"); 1548 projection = new String[] {PhoneLookup.DISPLAY_NAME}; 1549 mResolver.query(lookupUri, projection, null, null, null); 1550 projection = new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.NUMBER}; 1551 mResolver.query(lookupUri, projection, null, null, null); 1552 } 1553 1554 public void testPhoneLookupUseCases() { 1555 ContentValues values = new ContentValues(); 1556 Uri rawContactUri; 1557 long rawContactId; 1558 Uri lookupUri2; 1559 1560 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1561 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1562 1563 // International format in contacts 1564 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1565 rawContactId = ContentUris.parseId(rawContactUri); 1566 1567 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale"); 1568 insertPhoneNumber(rawContactId, "+1-650-861-0000"); 1569 1570 values.clear(); 1571 1572 // match with international format 1573 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0000"); 1574 assertEquals(1, getCount(lookupUri2, null, null)); 1575 1576 // match with national format 1577 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0000"); 1578 assertEquals(1, getCount(lookupUri2, null, null)); 1579 1580 // does not match with wrong area code 1581 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "649 861 0000"); 1582 assertEquals(0, getCount(lookupUri2, null, null)); 1583 1584 // does not match with missing digits in mistyped area code 1585 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "5 861 0000"); 1586 assertEquals(0, getCount(lookupUri2, null, null)); 1587 1588 // does not match with missing digit in mistyped area code 1589 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "65 861 0000"); 1590 assertEquals(0, getCount(lookupUri2, null, null)); 1591 1592 // National format in contacts 1593 values.clear(); 1594 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1595 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1596 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1597 rawContactId = ContentUris.parseId(rawContactUri); 1598 1599 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot1", "Tamale"); 1600 insertPhoneNumber(rawContactId, "650-861-0001"); 1601 1602 values.clear(); 1603 1604 // match with international format 1605 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0001"); 1606 assertEquals(2, getCount(lookupUri2, null, null)); 1607 1608 // match with national format 1609 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0001"); 1610 assertEquals(2, getCount(lookupUri2, null, null)); 1611 1612 // Local format in contacts 1613 values.clear(); 1614 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1615 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1616 rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 1617 rawContactId = ContentUris.parseId(rawContactUri); 1618 1619 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot2", "Tamale"); 1620 insertPhoneNumber(rawContactId, "861-0002"); 1621 1622 values.clear(); 1623 1624 // match with international format 1625 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "+1 650 861 0002"); 1626 assertEquals(1, getCount(lookupUri2, null, null)); 1627 1628 // match with national format 1629 lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "650 861 0002"); 1630 assertEquals(1, getCount(lookupUri2, null, null)); 1631 } 1632 1633 public void testIntlPhoneLookupUseCases() { 1634 // Checks the logic that relies on phone_number_compare_loose(Gingerbread) as a fallback 1635 //for phone number lookups. 1636 String fullNumber = "01197297427289"; 1637 1638 ContentValues values = new ContentValues(); 1639 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1640 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1641 long rawContactId = ContentUris.parseId(mResolver.insert(RawContacts.CONTENT_URI, values)); 1642 DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang"); 1643 insertPhoneNumber(rawContactId, fullNumber); 1644 1645 // Full number should definitely match. 1646 assertEquals(2, getCount(Uri.withAppendedPath( 1647 PhoneLookup.CONTENT_FILTER_URI, fullNumber), null, null)); 1648 1649 // Shorter (local) number with 0 prefix should also match. 1650 assertEquals(2, getCount(Uri.withAppendedPath( 1651 PhoneLookup.CONTENT_FILTER_URI, "097427289"), null, null)); 1652 1653 // Number with international (+972) prefix should also match. 1654 assertEquals(1, getCount(Uri.withAppendedPath( 1655 PhoneLookup.CONTENT_FILTER_URI, "+97297427289"), null, null)); 1656 1657 // Same shorter number with dashes should match. 1658 assertEquals(2, getCount(Uri.withAppendedPath( 1659 PhoneLookup.CONTENT_FILTER_URI, "09-742-7289"), null, null)); 1660 1661 // Same shorter number with spaces should match. 1662 assertEquals(2, getCount(Uri.withAppendedPath( 1663 PhoneLookup.CONTENT_FILTER_URI, "09 742 7289"), null, null)); 1664 1665 // Some other number should not match. 1666 assertEquals(0, getCount(Uri.withAppendedPath( 1667 PhoneLookup.CONTENT_FILTER_URI, "049102395"), null, null)); 1668 } 1669 1670 public void testPhoneLookupB5252190() { 1671 // Test cases from b/5252190 1672 String storedNumber = "796010101"; 1673 1674 ContentValues values = new ContentValues(); 1675 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1676 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1677 long rawContactId = ContentUris.parseId(mResolver.insert(RawContacts.CONTENT_URI, values)); 1678 DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang"); 1679 insertPhoneNumber(rawContactId, storedNumber); 1680 1681 assertEquals(1, getCount(Uri.withAppendedPath( 1682 PhoneLookup.CONTENT_FILTER_URI, "0796010101"), null, null)); 1683 1684 assertEquals(1, getCount(Uri.withAppendedPath( 1685 PhoneLookup.CONTENT_FILTER_URI, "+48796010101"), null, null)); 1686 1687 assertEquals(1, getCount(Uri.withAppendedPath( 1688 PhoneLookup.CONTENT_FILTER_URI, "48796010101"), null, null)); 1689 1690 assertEquals(1, getCount(Uri.withAppendedPath( 1691 PhoneLookup.CONTENT_FILTER_URI, "4-879-601-0101"), null, null)); 1692 1693 assertEquals(1, getCount(Uri.withAppendedPath( 1694 PhoneLookup.CONTENT_FILTER_URI, "4 879 601 0101"), null, null)); 1695 } 1696 1697 public void testPhoneLookupUseStrictPhoneNumberCompare() { 1698 // Test lookup cases when mUseStrictPhoneNumberComparison is true 1699 final ContactsProvider2 cp = (ContactsProvider2) getProvider(); 1700 final ContactsDatabaseHelper dbHelper = cp.getThreadActiveDatabaseHelperForTest(); 1701 // Get and save the original value of mUseStrictPhoneNumberComparison so that we 1702 // can restore it when we are done with the test 1703 final boolean oldUseStrict = dbHelper.getUseStrictPhoneNumberComparisonForTest(); 1704 dbHelper.setUseStrictPhoneNumberComparisonForTest(true); 1705 1706 1707 try { 1708 String fullNumber = "01197297427289"; 1709 ContentValues values = new ContentValues(); 1710 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 1711 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 1712 long rawContactId = ContentUris.parseId( 1713 mResolver.insert(RawContacts.CONTENT_URI, values)); 1714 DataUtil.insertStructuredName(mResolver, rawContactId, "Senor", "Chang"); 1715 insertPhoneNumber(rawContactId, fullNumber); 1716 insertPhoneNumber(rawContactId, "5103337596"); 1717 insertPhoneNumber(rawContactId, "+19012345678"); 1718 // One match for full number 1719 assertEquals(1, getCount(Uri.withAppendedPath( 1720 PhoneLookup.CONTENT_FILTER_URI, fullNumber), null, null)); 1721 1722 // No matches for extra digit at the front 1723 assertEquals(0, getCount(Uri.withAppendedPath( 1724 PhoneLookup.CONTENT_FILTER_URI, "55103337596"), null, null)); 1725 // No matches for mispelled area code 1726 assertEquals(0, getCount(Uri.withAppendedPath( 1727 PhoneLookup.CONTENT_FILTER_URI, "5123337596"), null, null)); 1728 1729 // One match for matching number with dashes 1730 assertEquals(1, getCount(Uri.withAppendedPath( 1731 PhoneLookup.CONTENT_FILTER_URI, "510-333-7596"), null, null)); 1732 1733 // One match for matching number with international code 1734 assertEquals(1, getCount(Uri.withAppendedPath( 1735 PhoneLookup.CONTENT_FILTER_URI, "+1-510-333-7596"), null, null)); 1736 values.clear(); 1737 1738 // No matches for extra 0 in front 1739 assertEquals(0, getCount(Uri.withAppendedPath( 1740 PhoneLookup.CONTENT_FILTER_URI, "0-510-333-7596"), null, null)); 1741 values.clear(); 1742 1743 // No matches for different country code 1744 assertEquals(0, getCount(Uri.withAppendedPath( 1745 PhoneLookup.CONTENT_FILTER_URI, "+819012345678"), null, null)); 1746 values.clear(); 1747 } finally { 1748 // restore the original value of mUseStrictPhoneNumberComparison 1749 // upon test completion or failure 1750 dbHelper.setUseStrictPhoneNumberComparisonForTest(oldUseStrict); 1751 } 1752 } 1753 1754 /** 1755 * Test for enterprise caller-id, but with no corp profile. 1756 */ 1757 public void testPhoneLookupEnterprise_noCorpProfile() throws Exception { 1758 1759 Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111"); 1760 1761 // No contacts profile, no data. 1762 assertEquals(0, getCount(uri1)); 1763 1764 // Insert a contact into the primary CP2. 1765 long rawContactId = ContentUris.parseId( 1766 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues())); 1767 DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe"); 1768 insertPhoneNumber(rawContactId, "408-111-1111"); 1769 1770 // Do the query again and check the result. 1771 Cursor c = mResolver.query(uri1, null, null, null, null); 1772 try { 1773 assertEquals(1, c.getCount()); 1774 c.moveToPosition(0); 1775 long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID)); 1776 assertFalse(Contacts.isEnterpriseContactId(contactId)); // Make sure it's not rewritten. 1777 } finally { 1778 c.close(); 1779 } 1780 } 1781 1782 /** 1783 * Set up the corp user / CP2 and returns the corp CP2 instance. 1784 * 1785 * Create a second instance of CP2, and add it to the resolver, with the "user-id@" authority. 1786 */ 1787 private SynchronousContactsProvider2 setUpCorpProvider() throws Exception { 1788 mActor.mockUserManager.setUsers(MockUserManager.PRIMARY_USER, MockUserManager.CORP_USER); 1789 1790 // Note here we use a standalone CP2 so it'll have its own db helper. 1791 // Also use AlteringUserContext here to report the corp user id. 1792 return mActor.addProvider(StandaloneContactsProvider2.class, 1793 "" + MockUserManager.CORP_USER.id + "@com.android.contacts", 1794 new AlteringUserContext(mActor.getProviderContext(), MockUserManager.CORP_USER.id)); 1795 } 1796 1797 /** 1798 * Test for enterprise caller-id, with the corp profile. 1799 * 1800 * Note: in this test, we add one more provider instance for the authority 1801 * "10 (at) com.android.contacts" and use it as the corp cp2. 1802 */ 1803 public void testPhoneLookupEnterprise_withCorpProfile() throws Exception { 1804 final SynchronousContactsProvider2 corpCp2 = setUpCorpProvider(); 1805 1806 Uri uri1 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-111-1111"); 1807 Uri uri2 = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, "408-222-2222"); 1808 1809 // First, test with no contacts on either profile. 1810 assertEquals(0, getCount(uri1)); 1811 1812 // Insert a contact to the primary CP2. 1813 long rawContactId = ContentUris.parseId( 1814 mResolver.insert(RawContacts.CONTENT_URI, new ContentValues())); 1815 DataUtil.insertStructuredName(mResolver, rawContactId, "Contact1", "Doe"); 1816 insertPhoneNumber(rawContactId, "408-111-1111"); 1817 1818 // Insert a contact to the corp CP2, with the same phone number, but with a different name. 1819 rawContactId = ContentUris.parseId( 1820 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues())); 1821 // Insert a name 1822 ContentValues cv = cv( 1823 Data.RAW_CONTACT_ID, rawContactId, 1824 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE, 1825 StructuredName.DISPLAY_NAME, "Contact2 Corp", 1826 StructuredName.GIVEN_NAME, "Contact2", 1827 StructuredName.FAMILY_NAME, "Corp"); 1828 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 1829 1830 // Insert a number 1831 cv = cv( 1832 Data.RAW_CONTACT_ID, rawContactId, 1833 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE, 1834 Phone.NUMBER, "408-111-1111", 1835 Phone.TYPE, Phone.TYPE_HOME); 1836 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 1837 1838 // Insert one more contact to the corp CP2, with a different number. 1839 rawContactId = ContentUris.parseId( 1840 corpCp2.insert(RawContacts.CONTENT_URI, new ContentValues())); 1841 // Insert a name 1842 cv = cv( 1843 Data.RAW_CONTACT_ID, rawContactId, 1844 Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE, 1845 StructuredName.DISPLAY_NAME, "Contact3 Corp", 1846 StructuredName.GIVEN_NAME, "Contact3", 1847 StructuredName.FAMILY_NAME, "Corp"); 1848 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 1849 1850 // Insert a number 1851 cv = cv( 1852 Data.RAW_CONTACT_ID, rawContactId, 1853 Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE, 1854 Phone.NUMBER, "408-222-2222", 1855 Phone.TYPE, Phone.TYPE_HOME); 1856 corpCp2.insert(ContactsContract.Data.CONTENT_URI, cv); 1857 1858 // Okay, now execute queries and check the result. 1859 1860 // The first URL hits the contact in the primary CP2. 1861 // There's also a contact with this phone number in the corp CP2, but that will be ignored. 1862 Cursor c = mResolver.query(uri1, null, null, null, null); 1863 try { 1864 assertEquals(1, c.getCount()); 1865 c.moveToPosition(0); 1866 assertEquals("Contact1 Doe", c.getString(c.getColumnIndex(PhoneLookup.DISPLAY_NAME))); 1867 1868 // Make sure it has a personal contact ID. 1869 long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID)); 1870 assertFalse(Contacts.isEnterpriseContactId(contactId)); 1871 } finally { 1872 c.close(); 1873 } 1874 1875 // Test for the second phone number, which only exists in the corp cp2. 1876 c = mResolver.query(uri2, null, null, null, null); 1877 try { 1878 // This one actually returns 2 identical rows, probably because of the join 1879 // in phone_lookup. Callers only care the first row, so returning multiple identical 1880 // rows should be fine. 1881 assertTrue(c.getCount() > 0); 1882 c.moveToPosition(0); 1883 assertEquals("Contact3 Corp", c.getString(c.getColumnIndex(PhoneLookup.DISPLAY_NAME))); 1884 1885 // Make sure it has a corp contact ID. 1886 long contactId = c.getLong(c.getColumnIndex(PhoneLookup._ID)); 1887 assertTrue(Contacts.isEnterpriseContactId(contactId)); 1888 } finally { 1889 c.close(); 1890 } 1891 } 1892 1893 public void testUpgradeToVersion910_CallsDeletedForCorpProfileOnly() throws Exception { 1894 CallLogProvider provider = 1895 (CallLogProvider) addProvider(TestCallLogProvider.class, CallLog.AUTHORITY); 1896 final ContactsDatabaseHelper helper = provider.getDatabaseHelper(mContext); 1897 final SQLiteDatabase db = helper.getWritableDatabase(); 1898 1899 final ContentValues values = new ContentValues(); 1900 values.put(Calls.NUMBER, "123456789"); 1901 values.put(Calls.DATE, System.currentTimeMillis()); 1902 values.put(Calls.TYPE, Calls.OUTGOING_TYPE); 1903 values.put(Calls.DURATION, 10000); 1904 1905 mResolver.insert(Calls.CONTENT_URI, values); 1906 assertEquals(1, getCount(Calls.CONTENT_URI)); 1907 1908 helper.upgradeToVersion910(db); 1909 assertEquals(1, getCount(Calls.CONTENT_URI)); 1910 1911 mActor.mockUserManager.myUser = MockUserManager.CORP_USER.id; 1912 mActor.mockUserManager.setUsers(MockUserManager.CORP_USER); 1913 1914 helper.upgradeToVersion910(db); 1915 1916 // Switch back to the primary user to ensure that the calls table was really cleared, and 1917 // we are not getting an empty cursor just because of the call log read/write restriction 1918 // on managed profiles. 1919 mActor.mockUserManager.myUser = MockUserManager.PRIMARY_USER.id; 1920 mActor.mockUserManager.setUsers(MockUserManager.PRIMARY_USER); 1921 assertEquals(0, getCount(Calls.CONTENT_URI)); 1922 } 1923 1924 public void testRewriteCorpPhoneLookup() { 1925 // 19 columns 1926 final MatrixCursor c = new MatrixCursor(new String[] { 1927 PhoneLookup._ID, 1928 PhoneLookup.LOOKUP_KEY, 1929 PhoneLookup.DISPLAY_NAME, 1930 PhoneLookup.LAST_TIME_CONTACTED, 1931 PhoneLookup.TIMES_CONTACTED, 1932 PhoneLookup.STARRED, 1933 PhoneLookup.IN_DEFAULT_DIRECTORY, 1934 PhoneLookup.IN_VISIBLE_GROUP, 1935 PhoneLookup.PHOTO_FILE_ID, 1936 PhoneLookup.PHOTO_ID, 1937 PhoneLookup.PHOTO_URI, 1938 PhoneLookup.PHOTO_THUMBNAIL_URI, 1939 PhoneLookup.CUSTOM_RINGTONE, 1940 PhoneLookup.HAS_PHONE_NUMBER, 1941 PhoneLookup.SEND_TO_VOICEMAIL, 1942 PhoneLookup.NUMBER, 1943 PhoneLookup.TYPE, 1944 PhoneLookup.LABEL, 1945 PhoneLookup.NORMALIZED_NUMBER 1946 }); 1947 1948 // First, convert and make sure it returns an empty cursor. 1949 Cursor rewritten = ContactsProvider2.rewriteCorpPhoneLookup(c); 1950 assertEquals(0, rewritten.getCount()); 1951 assertEquals(19, rewritten.getColumnCount()); 1952 1953 c.addRow(new Object[] { 1954 1L, // PhoneLookup._ID, 1955 null, // PhoneLookup.LOOKUP_KEY, 1956 null, // PhoneLookup.DISPLAY_NAME, 1957 null, // PhoneLookup.LAST_TIME_CONTACTED, 1958 null, // PhoneLookup.TIMES_CONTACTED, 1959 null, // PhoneLookup.STARRED, 1960 null, // PhoneLookup.IN_DEFAULT_DIRECTORY, 1961 null, // PhoneLookup.IN_VISIBLE_GROUP, 1962 null, // PhoneLookup.PHOTO_FILE_ID, 1963 null, // PhoneLookup.PHOTO_ID, 1964 null, // PhoneLookup.PHOTO_URI, 1965 null, // PhoneLookup.PHOTO_THUMBNAIL_URI, 1966 null, // PhoneLookup.CUSTOM_RINGTONE, 1967 null, // PhoneLookup.HAS_PHONE_NUMBER, 1968 null, // PhoneLookup.SEND_TO_VOICEMAIL, 1969 null, // PhoneLookup.NUMBER, 1970 null, // PhoneLookup.TYPE, 1971 null, // PhoneLookup.LABEL, 1972 null, // PhoneLookup.NORMALIZED_NUMBER 1973 }); 1974 1975 c.addRow(new Object[] { 1976 10L, // PhoneLookup._ID, 1977 "key", // PhoneLookup.LOOKUP_KEY, 1978 "name", // PhoneLookup.DISPLAY_NAME, 1979 123, // PhoneLookup.LAST_TIME_CONTACTED, 1980 456, // PhoneLookup.TIMES_CONTACTED, 1981 1, // PhoneLookup.STARRED, 1982 1, // PhoneLookup.IN_DEFAULT_DIRECTORY, 1983 1, // PhoneLookup.IN_VISIBLE_GROUP, 1984 1001, // PhoneLookup.PHOTO_FILE_ID, 1985 1002, // PhoneLookup.PHOTO_ID, 1986 "content://a/a", // PhoneLookup.PHOTO_URI, 1987 "content://a/b", // PhoneLookup.PHOTO_THUMBNAIL_URI, 1988 "content://a/c", // PhoneLookup.CUSTOM_RINGTONE, 1989 1, // PhoneLookup.HAS_PHONE_NUMBER, 1990 1, // PhoneLookup.SEND_TO_VOICEMAIL, 1991 "1234", // PhoneLookup.NUMBER, 1992 1, // PhoneLookup.TYPE, 1993 "label", // PhoneLookup.LABEL, 1994 "+1234", // PhoneLookup.NORMALIZED_NUMBER 1995 }); 1996 rewritten = ContactsProvider2.rewriteCorpPhoneLookup(c); 1997 assertEquals(2, rewritten.getCount()); 1998 1999 rewritten.moveToPosition(0); 2000 int column = 0; 2001 assertEquals(1000000001L, rewritten.getLong(column++)); // We offset ID for corp contacts. 2002 assertEquals(null, rewritten.getString(column++)); 2003 assertEquals(null, rewritten.getString(column++)); 2004 assertEquals(null, rewritten.getString(column++)); 2005 assertEquals(null, rewritten.getString(column++)); 2006 assertEquals(null, rewritten.getString(column++)); 2007 assertEquals(null, rewritten.getString(column++)); 2008 assertEquals(null, rewritten.getString(column++)); 2009 assertEquals(null, rewritten.getString(column++)); 2010 assertEquals(null, rewritten.getString(column++)); 2011 assertEquals(null, rewritten.getString(column++)); 2012 assertEquals(null, rewritten.getString(column++)); 2013 assertEquals(null, rewritten.getString(column++)); 2014 assertEquals(null, rewritten.getString(column++)); 2015 assertEquals(null, rewritten.getString(column++)); 2016 assertEquals(null, rewritten.getString(column++)); 2017 assertEquals(null, rewritten.getString(column++)); 2018 assertEquals(null, rewritten.getString(column++)); 2019 assertEquals(null, rewritten.getString(column++)); 2020 2021 2022 rewritten.moveToNext(); 2023 column = 0; 2024 assertEquals(1000000010L, rewritten.getLong(column++)); // With offset. 2025 assertEquals("key", rewritten.getString(column++)); 2026 assertEquals("name", rewritten.getString(column++)); 2027 assertEquals(123, rewritten.getInt(column++)); 2028 assertEquals(456, rewritten.getInt(column++)); 2029 assertEquals(1, rewritten.getInt(column++)); 2030 assertEquals(1, rewritten.getInt(column++)); 2031 assertEquals(1, rewritten.getInt(column++)); 2032 assertEquals(null, rewritten.getString(column++)); // photo file id 2033 assertEquals(null, rewritten.getString(column++)); // photo id 2034 assertEquals("content://com.android.contacts/contacts_corp/10/display_photo", 2035 rewritten.getString(column++)); 2036 assertEquals("content://com.android.contacts/contacts_corp/10/photo", 2037 rewritten.getString(column++)); 2038 assertEquals(null, rewritten.getString(column++)); // ringtone 2039 assertEquals(1, rewritten.getInt(column++)); 2040 assertEquals(1, rewritten.getInt(column++)); 2041 assertEquals("1234", rewritten.getString(column++)); 2042 assertEquals(1, rewritten.getInt(column++)); 2043 assertEquals("label", rewritten.getString(column++)); 2044 assertEquals("+1234", rewritten.getString(column++)); 2045 } 2046 2047 public void testPhoneUpdate() { 2048 ContentValues values = new ContentValues(); 2049 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2050 long rawContactId = ContentUris.parseId(rawContactUri); 2051 2052 DataUtil.insertStructuredName(mResolver, rawContactId, "Hot", "Tamale"); 2053 Uri phoneUri = insertPhoneNumber(rawContactId, "18004664411"); 2054 2055 Uri lookupUri1 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664411"); 2056 Uri lookupUri2 = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, "8004664422"); 2057 assertEquals(2, getCount(lookupUri1, null, null)); 2058 assertEquals(0, getCount(lookupUri2, null, null)); 2059 2060 values.clear(); 2061 values.put(Phone.NUMBER, "18004664422"); 2062 mResolver.update(phoneUri, values, null, null); 2063 2064 assertEquals(0, getCount(lookupUri1, null, null)); 2065 assertEquals(2, getCount(lookupUri2, null, null)); 2066 2067 // Setting number to null will remove the phone lookup record 2068 values.clear(); 2069 values.putNull(Phone.NUMBER); 2070 mResolver.update(phoneUri, values, null, null); 2071 2072 assertEquals(0, getCount(lookupUri1, null, null)); 2073 assertEquals(0, getCount(lookupUri2, null, null)); 2074 2075 // Let's restore that phone lookup record 2076 values.clear(); 2077 values.put(Phone.NUMBER, "18004664422"); 2078 mResolver.update(phoneUri, values, null, null); 2079 assertEquals(0, getCount(lookupUri1, null, null)); 2080 assertEquals(2, getCount(lookupUri2, null, null)); 2081 assertNetworkNotified(true); 2082 } 2083 2084 /** Tests if {@link Callable#CONTENT_URI} returns both phones and sip addresses. */ 2085 public void testCallablesQuery() { 2086 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Meghan", "Knox"); 2087 long phoneId1 = ContentUris.parseId(insertPhoneNumber(rawContactId1, "18004664411")); 2088 long contactId1 = queryContactId(rawContactId1); 2089 2090 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 2091 long sipAddressId2 = ContentUris.parseId( 2092 insertSipAddress(rawContactId2, "sip (at) example.com")); 2093 long contactId2 = queryContactId(rawContactId2); 2094 2095 ContentValues values1 = new ContentValues(); 2096 values1.put(Data._ID, phoneId1); 2097 values1.put(Data.RAW_CONTACT_ID, rawContactId1); 2098 values1.put(RawContacts.CONTACT_ID, contactId1); 2099 values1.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 2100 values1.put(Phone.NUMBER, "18004664411"); 2101 values1.put(Phone.TYPE, Phone.TYPE_HOME); 2102 values1.putNull(Phone.LABEL); 2103 values1.put(Contacts.DISPLAY_NAME, "Meghan Knox"); 2104 2105 ContentValues values2 = new ContentValues(); 2106 values2.put(Data._ID, sipAddressId2); 2107 values2.put(Data.RAW_CONTACT_ID, rawContactId2); 2108 values2.put(RawContacts.CONTACT_ID, contactId2); 2109 values2.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE); 2110 values2.put(SipAddress.SIP_ADDRESS, "sip (at) example.com"); 2111 values2.put(Contacts.DISPLAY_NAME, "John Doe"); 2112 2113 assertEquals(2, getCount(Callable.CONTENT_URI, null, null)); 2114 assertStoredValues(Callable.CONTENT_URI, new ContentValues[] { values1, values2 }); 2115 } 2116 2117 public void testCallablesFilterQuery() { 2118 testPhonesFilterQueryInter(Callable.CONTENT_FILTER_URI); 2119 } 2120 2121 public void testEmailsQuery() { 2122 ContentValues values = new ContentValues(); 2123 values.put(RawContacts.CUSTOM_RINGTONE, "d"); 2124 values.put(RawContacts.SEND_TO_VOICEMAIL, 1); 2125 values.put(RawContacts.LAST_TIME_CONTACTED, 12345); 2126 values.put(RawContacts.TIMES_CONTACTED, 54321); 2127 values.put(RawContacts.STARRED, 1); 2128 2129 Uri rawContactUri = mResolver.insert(RawContacts.CONTENT_URI, values); 2130 final long rawContactId = ContentUris.parseId(rawContactUri); 2131 2132 DataUtil.insertStructuredName(mResolver, rawContactId, "Meghan", "Knox"); 2133 final Uri emailUri = insertEmail(rawContactId, "meghan (at) acme.com"); 2134 final long emailId = ContentUris.parseId(emailUri); 2135 2136 final long contactId = queryContactId(rawContactId); 2137 values.clear(); 2138 values.put(Data._ID, emailId); 2139 values.put(Data.RAW_CONTACT_ID, rawContactId); 2140 values.put(RawContacts.CONTACT_ID, contactId); 2141 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2142 values.put(Email.DATA, "meghan (at) acme.com"); 2143 values.put(Email.TYPE, Email.TYPE_HOME); 2144 values.putNull(Email.LABEL); 2145 values.put(Contacts.DISPLAY_NAME, "Meghan Knox"); 2146 values.put(Contacts.CUSTOM_RINGTONE, "d"); 2147 values.put(Contacts.SEND_TO_VOICEMAIL, 1); 2148 values.put(Contacts.LAST_TIME_CONTACTED, 12345); 2149 values.put(Contacts.TIMES_CONTACTED, 54321); 2150 values.put(Contacts.STARRED, 1); 2151 2152 assertStoredValues(Email.CONTENT_URI, values); 2153 assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values); 2154 assertSelection(Email.CONTENT_URI, values, Data._ID, emailId); 2155 2156 // Check if the provider detects duplicated email addresses. 2157 final Uri emailUri2 = insertEmail(rawContactId, "meghan (at) acme.com"); 2158 final long emailId2 = ContentUris.parseId(emailUri2); 2159 final ContentValues values2 = new ContentValues(values); 2160 values2.put(Data._ID, emailId2); 2161 2162 final Uri dedupeUri = Email.CONTENT_URI.buildUpon() 2163 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true") 2164 .build(); 2165 2166 // URI with ID should return a correct result. 2167 assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId), values); 2168 assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId), values); 2169 assertStoredValues(ContentUris.withAppendedId(Email.CONTENT_URI, emailId2), values2); 2170 assertStoredValues(ContentUris.withAppendedId(dedupeUri, emailId2), values2); 2171 2172 assertStoredValues(Email.CONTENT_URI, new ContentValues[] {values, values2}); 2173 2174 // If requested to remove duplicates, the query should return just one result, 2175 // whose _ID won't be deterministic. 2176 values.remove(Data._ID); 2177 assertStoredValues(dedupeUri, values); 2178 } 2179 2180 public void testEmailsLookupQuery() { 2181 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale"); 2182 insertEmail(rawContactId, "tamale (at) acme.com"); 2183 2184 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "tamale (at) acme.com"); 2185 ContentValues values = new ContentValues(); 2186 values.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 2187 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2188 values.put(Email.DATA, "tamale (at) acme.com"); 2189 values.put(Email.TYPE, Email.TYPE_HOME); 2190 values.putNull(Email.LABEL); 2191 assertStoredValues(filterUri1, values); 2192 2193 Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "Ta<TaMale (at) acme.com>"); 2194 assertStoredValues(filterUri2, values); 2195 2196 Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_LOOKUP_URI, "encilada (at) acme.com"); 2197 assertEquals(0, getCount(filterUri3, null, null)); 2198 } 2199 2200 public void testEmailsFilterQuery() { 2201 long rawContactId1 = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale", 2202 TestUtil.ACCOUNT_1); 2203 insertEmail(rawContactId1, "tamale (at) acme.com"); 2204 insertEmail(rawContactId1, "tamale (at) acme.com"); 2205 2206 long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Hot", "Tamale", 2207 TestUtil.ACCOUNT_2); 2208 insertEmail(rawContactId2, "tamale (at) acme.com"); 2209 2210 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tam"); 2211 ContentValues values = new ContentValues(); 2212 values.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 2213 values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2214 values.put(Email.DATA, "tamale (at) acme.com"); 2215 values.put(Email.TYPE, Email.TYPE_HOME); 2216 values.putNull(Email.LABEL); 2217 assertStoredValuesWithProjection(filterUri1, values); 2218 2219 Uri filterUri2 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot"); 2220 assertStoredValuesWithProjection(filterUri2, values); 2221 2222 Uri filterUri3 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "hot tamale"); 2223 assertStoredValuesWithProjection(filterUri3, values); 2224 2225 Uri filterUri4 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "tamale@acme"); 2226 assertStoredValuesWithProjection(filterUri4, values); 2227 2228 Uri filterUri5 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "encilada"); 2229 assertEquals(0, getCount(filterUri5, null, null)); 2230 } 2231 2232 /** 2233 * Tests if ContactsProvider2 returns addresses according to registration order. 2234 */ 2235 public void testEmailFilterDefaultSortOrder() { 2236 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 2237 insertEmail(rawContactId1, "address1 (at) email.com"); 2238 insertEmail(rawContactId1, "address2 (at) email.com"); 2239 insertEmail(rawContactId1, "address3 (at) email.com"); 2240 ContentValues v1 = new ContentValues(); 2241 v1.put(Email.ADDRESS, "address1 (at) email.com"); 2242 ContentValues v2 = new ContentValues(); 2243 v2.put(Email.ADDRESS, "address2 (at) email.com"); 2244 ContentValues v3 = new ContentValues(); 2245 v3.put(Email.ADDRESS, "address3 (at) email.com"); 2246 2247 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 2248 assertStoredValuesOrderly(filterUri, new ContentValues[]{v1, v2, v3}); 2249 } 2250 2251 /** 2252 * Tests if ContactsProvider2 returns primary addresses before the other addresses. 2253 */ 2254 public void testEmailFilterPrimaryAddress() { 2255 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 2256 insertEmail(rawContactId1, "address1 (at) email.com"); 2257 insertEmail(rawContactId1, "address2 (at) email.com", true); 2258 ContentValues v1 = new ContentValues(); 2259 v1.put(Email.ADDRESS, "address1 (at) email.com"); 2260 ContentValues v2 = new ContentValues(); 2261 v2.put(Email.ADDRESS, "address2 (at) email.com"); 2262 2263 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 2264 assertStoredValuesOrderly(filterUri, new ContentValues[] { v2, v1 }); 2265 } 2266 2267 /** 2268 * Tests if ContactsProvider2 has email address associated with a primary account before the 2269 * other address. 2270 */ 2271 public void testEmailFilterPrimaryAccount() { 2272 long rawContactId1 = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1); 2273 insertEmail(rawContactId1, "account1 (at) email.com"); 2274 long rawContactId2 = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_2); 2275 insertEmail(rawContactId2, "account2 (at) email.com"); 2276 ContentValues v1 = new ContentValues(); 2277 v1.put(Email.ADDRESS, "account1 (at) email.com"); 2278 ContentValues v2 = new ContentValues(); 2279 v2.put(Email.ADDRESS, "account2 (at) email.com"); 2280 2281 Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 2282 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_1.name) 2283 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, TestUtil.ACCOUNT_1.type) 2284 .build(); 2285 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2 }); 2286 2287 Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 2288 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_2.name) 2289 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, TestUtil.ACCOUNT_2.type) 2290 .build(); 2291 assertStoredValuesOrderly(filterUri2, new ContentValues[] { v2, v1 }); 2292 2293 // Just with PRIMARY_ACCOUNT_NAME 2294 Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 2295 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_1.name) 2296 .build(); 2297 assertStoredValuesOrderly(filterUri3, new ContentValues[]{v1, v2}); 2298 2299 Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 2300 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, TestUtil.ACCOUNT_2.name) 2301 .build(); 2302 assertStoredValuesOrderly(filterUri4, new ContentValues[] { v2, v1 }); 2303 } 2304 2305 /** 2306 * Test emails with the same domain as primary account are ordered first. 2307 */ 2308 public void testEmailFilterSameDomainAccountOrder() { 2309 final Account account = new Account("tester (at) email.com", "not_used"); 2310 final long rawContactId = RawContactUtil.createRawContact(mResolver, account); 2311 insertEmail(rawContactId, "account1 (at) testemail.com"); 2312 insertEmail(rawContactId, "account1 (at) email.com"); 2313 2314 final ContentValues v1 = cv(Email.ADDRESS, "account1 (at) testemail.com"); 2315 final ContentValues v2 = cv(Email.ADDRESS, "account1 (at) email.com"); 2316 2317 Uri filterUri1 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("acc") 2318 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_NAME, account.name) 2319 .appendQueryParameter(ContactsContract.PRIMARY_ACCOUNT_TYPE, account.type) 2320 .build(); 2321 assertStoredValuesOrderly(filterUri1, v2, v1); 2322 } 2323 2324 /** 2325 * Test "default" emails are sorted above emails used last. 2326 */ 2327 public void testEmailFilterSuperPrimaryOverUsageSort() { 2328 final long rawContactId = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1); 2329 final Uri emailUri1 = insertEmail(rawContactId, "account1 (at) testemail.com"); 2330 final Uri emailUri2 = insertEmail(rawContactId, "account2 (at) testemail.com"); 2331 insertEmail(rawContactId, "account3 (at) testemail.com", true, true); 2332 2333 // Update account1 and account 2 to have higher usage. 2334 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); 2335 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); 2336 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri2); 2337 2338 final ContentValues v1 = cv(Email.ADDRESS, "account1 (at) testemail.com"); 2339 final ContentValues v2 = cv(Email.ADDRESS, "account2 (at) testemail.com"); 2340 final ContentValues v3 = cv(Email.ADDRESS, "account3 (at) testemail.com"); 2341 2342 // Test that account 3 is first even though account 1 and 2 have higher usage. 2343 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "acc"); 2344 assertStoredValuesOrderly(filterUri, v3, v1, v2); 2345 } 2346 2347 /** 2348 * Test primary emails are sorted below emails used last. 2349 * 2350 * primary may be set without super primary. Only super primary indicates "default" in the 2351 * contact ui. 2352 */ 2353 public void testEmailFilterUsageOverPrimarySort() { 2354 final long rawContactId = RawContactUtil.createRawContact(mResolver, TestUtil.ACCOUNT_1); 2355 final Uri emailUri1 = insertEmail(rawContactId, "account1 (at) testemail.com"); 2356 final Uri emailUri2 = insertEmail(rawContactId, "account2 (at) testemail.com"); 2357 insertEmail(rawContactId, "account3 (at) testemail.com", true); 2358 2359 // Update account1 and account 2 to have higher usage. 2360 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); 2361 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri1); 2362 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, emailUri2); 2363 2364 final ContentValues v1 = cv(Email.ADDRESS, "account1 (at) testemail.com"); 2365 final ContentValues v2 = cv(Email.ADDRESS, "account2 (at) testemail.com"); 2366 final ContentValues v3 = cv(Email.ADDRESS, "account3 (at) testemail.com"); 2367 2368 // Test that account 3 is first even though account 1 and 2 have higher usage. 2369 Uri filterUri = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "acc"); 2370 assertStoredValuesOrderly(filterUri, v1, v2, v3); 2371 } 2372 2373 /** Tests {@link DataUsageFeedback} correctly promotes a data row instead of a raw contact. */ 2374 public void testEmailFilterSortOrderWithFeedback() { 2375 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 2376 String address1 = "address1 (at) email.com"; 2377 insertEmail(rawContactId1, address1); 2378 2379 long rawContactId2 = RawContactUtil.createRawContact(mResolver); 2380 String address2 = "address2 (at) email.com"; 2381 insertEmail(rawContactId2, address2); 2382 String address3 = "address3 (at) email.com"; 2383 ContentUris.parseId(insertEmail(rawContactId2, address3)); 2384 2385 ContentValues v1 = new ContentValues(); 2386 v1.put(Email.ADDRESS, "address1 (at) email.com"); 2387 ContentValues v2 = new ContentValues(); 2388 v2.put(Email.ADDRESS, "address2 (at) email.com"); 2389 ContentValues v3 = new ContentValues(); 2390 v3.put(Email.ADDRESS, "address3 (at) email.com"); 2391 2392 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 2393 Uri filterUri2 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address") 2394 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 2395 DataUsageFeedback.USAGE_TYPE_CALL) 2396 .build(); 2397 Uri filterUri3 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address") 2398 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 2399 DataUsageFeedback.USAGE_TYPE_LONG_TEXT) 2400 .build(); 2401 Uri filterUri4 = Email.CONTENT_FILTER_URI.buildUpon().appendPath("address") 2402 .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, 2403 DataUsageFeedback.USAGE_TYPE_SHORT_TEXT) 2404 .build(); 2405 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v1, v2, v3 }); 2406 assertStoredValuesOrderly(filterUri2, new ContentValues[] { v1, v2, v3 }); 2407 assertStoredValuesOrderly(filterUri3, new ContentValues[] { v1, v2, v3 }); 2408 assertStoredValuesOrderly(filterUri4, new ContentValues[] { v1, v2, v3 }); 2409 2410 sendFeedback(address3, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, v3); 2411 2412 assertStoredValuesWithProjection(RawContacts.CONTENT_URI, 2413 cv(RawContacts._ID, rawContactId1, 2414 RawContacts.TIMES_CONTACTED, 0 2415 ), 2416 cv(RawContacts._ID, rawContactId2, 2417 RawContacts.TIMES_CONTACTED, 1 2418 ) 2419 ); 2420 2421 // account3 (at) email.com should be the first. 2422 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v3, v1, v2 }); 2423 assertStoredValuesOrderly(filterUri3, new ContentValues[] { v3, v1, v2 }); 2424 } 2425 2426 /** 2427 * Tests {@link DataUsageFeedback} correctly bucketize contacts using each 2428 * {@link DataUsageStatColumns#LAST_TIME_USED} 2429 */ 2430 public void testEmailFilterSortOrderWithOldHistory() { 2431 long rawContactId1 = RawContactUtil.createRawContact(mResolver); 2432 long dataId1 = ContentUris.parseId(insertEmail(rawContactId1, "address1 (at) email.com")); 2433 long dataId2 = ContentUris.parseId(insertEmail(rawContactId1, "address2 (at) email.com")); 2434 long dataId3 = ContentUris.parseId(insertEmail(rawContactId1, "address3 (at) email.com")); 2435 long dataId4 = ContentUris.parseId(insertEmail(rawContactId1, "address4 (at) email.com")); 2436 2437 Uri filterUri1 = Uri.withAppendedPath(Email.CONTENT_FILTER_URI, "address"); 2438 2439 ContentValues v1 = new ContentValues(); 2440 v1.put(Email.ADDRESS, "address1 (at) email.com"); 2441 ContentValues v2 = new ContentValues(); 2442 v2.put(Email.ADDRESS, "address2 (at) email.com"); 2443 ContentValues v3 = new ContentValues(); 2444 v3.put(Email.ADDRESS, "address3 (at) email.com"); 2445 ContentValues v4 = new ContentValues(); 2446 v4.put(Email.ADDRESS, "address4 (at) email.com"); 2447 2448 final ContactsProvider2 provider = (ContactsProvider2) getProvider(); 2449 2450 long nowInMillis = System.currentTimeMillis(); 2451 long yesterdayInMillis = (nowInMillis - 24 * 60 * 60 * 1000); 2452 long sevenDaysAgoInMillis = (nowInMillis - 7 * 24 * 60 * 60 * 1000); 2453 long oneYearAgoInMillis = (nowInMillis - 365L * 24 * 60 * 60 * 1000); 2454 2455 // address4 is contacted just once yesterday. 2456 provider.updateDataUsageStat(Arrays.asList(dataId4), 2457 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, yesterdayInMillis); 2458 2459 // address3 is contacted twice 1 week ago. 2460 provider.updateDataUsageStat(Arrays.asList(dataId3), 2461 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis); 2462 provider.updateDataUsageStat(Arrays.asList(dataId3), 2463 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, sevenDaysAgoInMillis); 2464 2465 // address2 is contacted three times 1 year ago. 2466 provider.updateDataUsageStat(Arrays.asList(dataId2), 2467 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis); 2468 provider.updateDataUsageStat(Arrays.asList(dataId2), 2469 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis); 2470 provider.updateDataUsageStat(Arrays.asList(dataId2), 2471 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, oneYearAgoInMillis); 2472 2473 // auto-complete should prefer recently contacted methods 2474 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v4, v3, v2, v1 }); 2475 2476 // Pretend address2 is contacted right now 2477 provider.updateDataUsageStat(Arrays.asList(dataId2), 2478 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis); 2479 2480 // Now address2 is the most recently used address 2481 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v4, v3, v1 }); 2482 2483 // Pretend address1 is contacted right now 2484 provider.updateDataUsageStat(Arrays.asList(dataId1), 2485 DataUsageFeedback.USAGE_TYPE_LONG_TEXT, nowInMillis); 2486 2487 // address2 is preferred to address1 as address2 is used 4 times in total 2488 assertStoredValuesOrderly(filterUri1, new ContentValues[] { v2, v1, v4, v3 }); 2489 } 2490 2491 public void testPostalsQuery() { 2492 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Alice", "Nextore"); 2493 Uri dataUri = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View"); 2494 final long dataId = ContentUris.parseId(dataUri); 2495 2496 final long contactId = queryContactId(rawContactId); 2497 ContentValues values = new ContentValues(); 2498 values.put(Data._ID, dataId); 2499 values.put(Data.RAW_CONTACT_ID, rawContactId); 2500 values.put(RawContacts.CONTACT_ID, contactId); 2501 values.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE); 2502 values.put(StructuredPostal.FORMATTED_ADDRESS, "1600 Amphiteatre Ave, Mountain View"); 2503 values.put(Contacts.DISPLAY_NAME, "Alice Nextore"); 2504 2505 assertStoredValues(StructuredPostal.CONTENT_URI, values); 2506 assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId), 2507 values); 2508 assertSelection(StructuredPostal.CONTENT_URI, values, Data._ID, dataId); 2509 2510 // Check if the provider detects duplicated addresses. 2511 Uri dataUri2 = insertPostalAddress(rawContactId, "1600 Amphiteatre Ave, Mountain View"); 2512 final long dataId2 = ContentUris.parseId(dataUri2); 2513 final ContentValues values2 = new ContentValues(values); 2514 values2.put(Data._ID, dataId2); 2515 2516 final Uri dedupeUri = StructuredPostal.CONTENT_URI.buildUpon() 2517 .appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true") 2518 .build(); 2519 2520 // URI with ID should return a correct result. 2521 assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId), 2522 values); 2523 assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId), values); 2524 assertStoredValues(ContentUris.withAppendedId(StructuredPostal.CONTENT_URI, dataId2), 2525 values2); 2526 assertStoredValues(ContentUris.withAppendedId(dedupeUri, dataId2), values2); 2527 2528 assertStoredValues(StructuredPostal.CONTENT_URI, new ContentValues[] {values, values2}); 2529 2530 // If requested to remove duplicates, the query should return just one result, 2531 // whose _ID won't be deterministic. 2532 values.remove(Data._ID); 2533 assertStoredValues(dedupeUri, values); 2534 } 2535 2536 public void testDataContentUriInvisibleQuery() { 2537 final ContentValues values = new ContentValues(); 2538 final long contactId = createContact(values, "John", "Doe", 2539 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 2540 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 2541 2542 final Uri uri = Data.CONTENT_URI.buildUpon(). 2543 appendQueryParameter(Data.VISIBLE_CONTACTS_ONLY, "true").build(); 2544 assertEquals(4, getCount(uri, null, null)); 2545 2546 markInvisible(contactId); 2547 2548 assertEquals(0, getCount(uri, null, null)); 2549 } 2550 2551 public void testInDefaultDirectoryData() { 2552 final ContentValues values = new ContentValues(); 2553 final long contactId = createContact(values, "John", "Doe", 2554 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 2555 StatusUpdates.CAPABILITY_HAS_CAMERA); 2556 2557 final StringBuilder query = new StringBuilder() 2558 .append(Data.MIMETYPE).append("='").append(Email.CONTENT_ITEM_TYPE) 2559 .append("' AND ").append(Email.DATA).append("=? AND ") 2560 .append(Contacts.IN_DEFAULT_DIRECTORY).append("=1"); 2561 2562 assertEquals(1, 2563 getCount(Email.CONTENT_URI, query.toString(), new String[]{"goog411 (at) acme.com"})); 2564 2565 // Fire! 2566 markInvisible(contactId); 2567 2568 // Verify: making a contact visible changes the IN_DEFAULT_DIRECTORY data value. 2569 assertEquals(0, 2570 getCount(Email.CONTENT_URI, query.toString(), new String[]{"goog411 (at) acme.com"})); 2571 } 2572 2573 public void testContactablesQuery() { 2574 final long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot", 2575 "Tamale"); 2576 2577 insertPhoneNumber(rawContactId, "510-123-5769"); 2578 insertEmail(rawContactId, "tamale (at) acme.com"); 2579 2580 final ContentValues cv1 = new ContentValues(); 2581 cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 2582 cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2583 cv1.put(Email.DATA, "tamale (at) acme.com"); 2584 cv1.put(Email.TYPE, Email.TYPE_HOME); 2585 cv1.putNull(Email.LABEL); 2586 2587 final ContentValues cv2 = new ContentValues(); 2588 cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 2589 cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 2590 cv2.put(Phone.DATA, "510-123-5769"); 2591 cv2.put(Phone.TYPE, Phone.TYPE_HOME); 2592 cv2.putNull(Phone.LABEL); 2593 2594 final Uri filterUri0 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, ""); 2595 assertEquals(0, getCount(filterUri0, null, null)); 2596 2597 final Uri filterUri1 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale"); 2598 assertStoredValues(filterUri1, cv1, cv2); 2599 2600 final Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot"); 2601 assertStoredValues(filterUri2, cv1, cv2); 2602 2603 final Uri filterUri3 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale@ac"); 2604 assertStoredValues(filterUri3, cv1, cv2); 2605 2606 final Uri filterUri4 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "510"); 2607 assertStoredValues(filterUri4, cv1, cv2); 2608 2609 final Uri filterUri5 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "cold"); 2610 assertEquals(0, getCount(filterUri5, null, null)); 2611 2612 final Uri filterUri6 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, 2613 "tamale@google"); 2614 assertEquals(0, getCount(filterUri6, null, null)); 2615 2616 final Uri filterUri7 = Contactables.CONTENT_URI; 2617 assertStoredValues(filterUri7, cv1, cv2); 2618 } 2619 2620 public void testContactablesMultipleQuery() { 2621 2622 final long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Hot", 2623 "Tamale"); 2624 insertPhoneNumber(rawContactId, "510-123-5769"); 2625 insertEmail(rawContactId, "tamale (at) acme.com"); 2626 insertEmail(rawContactId, "hot (at) google.com"); 2627 2628 final long rawContactId2 = RawContactUtil.createRawContactWithName(mResolver, "Cold", 2629 "Tamago"); 2630 insertEmail(rawContactId2, "eggs (at) farmers.org"); 2631 2632 final long rawContactId3 = RawContactUtil.createRawContactWithName(mResolver, "John", "Doe"); 2633 insertPhoneNumber(rawContactId3, "518-354-1111"); 2634 insertEmail(rawContactId3, "doeadeer (at) afemaledeer.com"); 2635 2636 final ContentValues cv1 = new ContentValues(); 2637 cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 2638 cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2639 cv1.put(Email.DATA, "tamale (at) acme.com"); 2640 cv1.put(Email.TYPE, Email.TYPE_HOME); 2641 cv1.putNull(Email.LABEL); 2642 2643 final ContentValues cv2 = new ContentValues(); 2644 cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 2645 cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 2646 cv2.put(Phone.DATA, "510-123-5769"); 2647 cv2.put(Phone.TYPE, Phone.TYPE_HOME); 2648 cv2.putNull(Phone.LABEL); 2649 2650 final ContentValues cv3 = new ContentValues(); 2651 cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale"); 2652 cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2653 cv3.put(Email.DATA, "hot (at) google.com"); 2654 cv3.put(Email.TYPE, Email.TYPE_HOME); 2655 cv3.putNull(Email.LABEL); 2656 2657 final ContentValues cv4 = new ContentValues(); 2658 cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago"); 2659 cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2660 cv4.put(Email.DATA, "eggs (at) farmers.org"); 2661 cv4.put(Email.TYPE, Email.TYPE_HOME); 2662 cv4.putNull(Email.LABEL); 2663 2664 final ContentValues cv5 = new ContentValues(); 2665 cv5.put(Contacts.DISPLAY_NAME, "John Doe"); 2666 cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 2667 cv5.put(Email.DATA, "doeadeer (at) afemaledeer.com"); 2668 cv5.put(Email.TYPE, Email.TYPE_HOME); 2669 cv5.putNull(Email.LABEL); 2670 2671 final ContentValues cv6 = new ContentValues(); 2672 cv6.put(Contacts.DISPLAY_NAME, "John Doe"); 2673 cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 2674 cv6.put(Phone.DATA, "518-354-1111"); 2675 cv6.put(Phone.TYPE, Phone.TYPE_HOME); 2676 cv6.putNull(Phone.LABEL); 2677 2678 final Uri filterUri1 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tamale"); 2679 2680 assertStoredValues(filterUri1, cv1, cv2, cv3); 2681 2682 final Uri filterUri2 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "hot"); 2683 assertStoredValues(filterUri2, cv1, cv2, cv3); 2684 2685 final Uri filterUri3 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "tam"); 2686 assertStoredValues(filterUri3, cv1, cv2, cv3, cv4); 2687 2688 final Uri filterUri4 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "518"); 2689 assertStoredValues(filterUri4, cv5, cv6); 2690 2691 final Uri filterUri5 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "doe"); 2692 assertStoredValues(filterUri5, cv5, cv6); 2693 2694 final Uri filterUri6 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, "51"); 2695 assertStoredValues(filterUri6, cv1, cv2, cv3, cv5, cv6); 2696 2697 final Uri filterUri7 = Uri.withAppendedPath(Contactables.CONTENT_FILTER_URI, 2698 "tamale@google"); 2699 assertEquals(0, getCount(filterUri7, null, null)); 2700 2701 final Uri filterUri8 = Contactables.CONTENT_URI; 2702 assertStoredValues(filterUri8, cv1, cv2, cv3, cv4, cv5, cv6); 2703 2704 // test VISIBLE_CONTACTS_ONLY boolean parameter 2705 final Uri filterUri9 = filterUri6.buildUpon().appendQueryParameter( 2706 Contactables.VISIBLE_CONTACTS_ONLY, "true").build(); 2707 assertStoredValues(filterUri9, cv1, cv2, cv3, cv5, cv6); 2708 // mark Hot Tamale as invisible - cv1, cv2, and cv3 should no longer be in the cursor 2709 markInvisible(queryContactId(rawContactId)); 2710 assertStoredValues(filterUri9, cv5, cv6); 2711 } 2712 2713 2714 public void testQueryContactData() { 2715 ContentValues values = new ContentValues(); 2716 long contactId = createContact(values, "John", "Doe", 2717 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 2718 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 2719 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 2720 2721 assertStoredValues(contactUri, values); 2722 assertSelection(Contacts.CONTENT_URI, values, Contacts._ID, contactId); 2723 } 2724 2725 public void testQueryContactWithStatusUpdate() { 2726 ContentValues values = new ContentValues(); 2727 long contactId = createContact(values, "John", "Doe", 2728 "18004664411", "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 2729 StatusUpdates.CAPABILITY_HAS_CAMERA); 2730 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 2731 values.put(Contacts.CONTACT_CHAT_CAPABILITY, StatusUpdates.CAPABILITY_HAS_CAMERA); 2732 Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 2733 assertStoredValuesWithProjection(contactUri, values); 2734 assertSelectionWithProjection(Contacts.CONTENT_URI, values, Contacts._ID, contactId); 2735 } 2736 2737 public void testQueryContactFilterByName() { 2738 ContentValues values = new ContentValues(); 2739 long rawContactId = createRawContact(values, "18004664411", 2740 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 2741 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 2742 StatusUpdates.CAPABILITY_HAS_VOICE); 2743 2744 ContentValues nameValues = new ContentValues(); 2745 nameValues.put(StructuredName.GIVEN_NAME, "Stu"); 2746 nameValues.put(StructuredName.FAMILY_NAME, "Goulash"); 2747 nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "goo"); 2748 nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "LASH"); 2749 Uri nameUri = DataUtil.insertStructuredName(mResolver, rawContactId, nameValues); 2750 2751 long contactId = queryContactId(rawContactId); 2752 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 2753 2754 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goulash"); 2755 assertStoredValuesWithProjection(filterUri1, values); 2756 2757 assertContactFilter(contactId, "goolash"); 2758 assertContactFilter(contactId, "lash"); 2759 2760 assertContactFilterNoResult("goolish"); 2761 2762 // Phonetic name with given/family reversed should not match 2763 assertContactFilterNoResult("lashgoo"); 2764 2765 nameValues.clear(); 2766 nameValues.put(StructuredName.PHONETIC_FAMILY_NAME, "ga"); 2767 nameValues.put(StructuredName.PHONETIC_GIVEN_NAME, "losh"); 2768 2769 mResolver.update(nameUri, nameValues, null, null); 2770 2771 assertContactFilter(contactId, "galosh"); 2772 2773 assertContactFilterNoResult("goolish"); 2774 } 2775 2776 public void testQueryContactFilterByEmailAddress() { 2777 ContentValues values = new ContentValues(); 2778 long rawContactId = createRawContact(values, "18004664411", 2779 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 2780 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 2781 StatusUpdates.CAPABILITY_HAS_VOICE); 2782 2783 DataUtil.insertStructuredName(mResolver, rawContactId, "James", "Bond"); 2784 2785 long contactId = queryContactId(rawContactId); 2786 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 2787 2788 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "goog411 (at) acme.com"); 2789 assertStoredValuesWithProjection(filterUri1, values); 2790 2791 assertContactFilter(contactId, "goog"); 2792 assertContactFilter(contactId, "goog411"); 2793 assertContactFilter(contactId, "goog411@"); 2794 assertContactFilter(contactId, "goog411@acme"); 2795 assertContactFilter(contactId, "goog411 (at) acme.com"); 2796 2797 assertContactFilterNoResult("goog411 (at) acme.combo"); 2798 assertContactFilterNoResult("goog411 (at) le.com"); 2799 assertContactFilterNoResult("goolish"); 2800 } 2801 2802 public void testQueryContactFilterByPhoneNumber() { 2803 ContentValues values = new ContentValues(); 2804 long rawContactId = createRawContact(values, "18004664411", 2805 "goog411 (at) acme.com", StatusUpdates.INVISIBLE, 4, 1, 0, 2806 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO | 2807 StatusUpdates.CAPABILITY_HAS_VOICE); 2808 2809 DataUtil.insertStructuredName(mResolver, rawContactId, "James", "Bond"); 2810 2811 long contactId = queryContactId(rawContactId); 2812 values.put(Contacts.CONTACT_PRESENCE, StatusUpdates.INVISIBLE); 2813 2814 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, "18004664411"); 2815 assertStoredValuesWithProjection(filterUri1, values); 2816 2817 assertContactFilter(contactId, "18004664411"); 2818 assertContactFilter(contactId, "1800466"); 2819 assertContactFilter(contactId, "+18004664411"); 2820 assertContactFilter(contactId, "8004664411"); 2821 2822 assertContactFilterNoResult("78004664411"); 2823 assertContactFilterNoResult("18004664412"); 2824 assertContactFilterNoResult("8884664411"); 2825 } 2826 2827 /** 2828 * Checks ContactsProvider2 works well with strequent Uris. The provider should return starred 2829 * contacts and frequently used contacts. 2830 */ 2831 public void testQueryContactStrequent() { 2832 ContentValues values1 = new ContentValues(); 2833 final String email1 = "a (at) acme.com"; 2834 final String phoneNumber1 = "18004664411"; 2835 final int timesContacted1 = 0; 2836 createContact(values1, "Noah", "Tever", phoneNumber1, 2837 email1, StatusUpdates.OFFLINE, timesContacted1, 0, 0, 2838 StatusUpdates.CAPABILITY_HAS_CAMERA | StatusUpdates.CAPABILITY_HAS_VIDEO); 2839 final String phoneNumber2 = "18004664412"; 2840 ContentValues values2 = new ContentValues(); 2841 createContact(values2, "Sam", "Times", phoneNumber2, 2842 "b (at) acme.com", StatusUpdates.INVISIBLE, 3, 0, 0, 2843 StatusUpdates.CAPABILITY_HAS_CAMERA); 2844 ContentValues values3 = new ContentValues(); 2845 final String phoneNumber3 = "18004664413"; 2846 final int timesContacted3 = 5; 2847 createContact(values3, "Lotta", "Calling", phoneNumber3, 2848 "c (at) acme.com", StatusUpdates.AWAY, timesContacted3, 0, 0, 2849 StatusUpdates.CAPABILITY_HAS_VIDEO); 2850 ContentValues values4 = new ContentValues(); 2851 final long rawContactId4 = createRawContact(values4, "Fay", "Veritt", null, 2852 "d (at) acme.com", StatusUpdates.AVAILABLE, 0, 1, 0, 2853 StatusUpdates.CAPABILITY_HAS_VIDEO | StatusUpdates.CAPABILITY_HAS_VOICE); 2854 2855 // Starred contacts should be returned. TIMES_CONTACTED should be ignored and only data 2856 // usage feedback should be used for "frequently contacted" listing. 2857 assertStoredValues(Contacts.CONTENT_STREQUENT_URI, values4); 2858 2859 // Send feedback for the 3rd phone number, pretending we called that person via phone. 2860 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 2861 2862 // After the feedback, 3rd contact should be shown after starred one. 2863 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI, 2864 new ContentValues[] { values4, values3 }); 2865 2866 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 2867 // Twice. 2868 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 2869 2870 // After the feedback, 1st and 3rd contacts should be shown after starred one. 2871 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI, 2872 new ContentValues[] { values4, values1, values3 }); 2873 2874 // With phone-only parameter, 1st and 4th contacts shouldn't be returned because: 2875 // 1st: feedbacks are only about email, not about phone call. 2876 // 4th: it has no phone number though starred. 2877 Uri phoneOnlyStrequentUri = Contacts.CONTENT_STREQUENT_URI.buildUpon() 2878 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true") 2879 .build(); 2880 assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] { values3 }); 2881 2882 // Now the 4th contact has three phone numbers, one of which is called twice and 2883 // the other once 2884 final String phoneNumber4 = "18004664414"; 2885 final String phoneNumber5 = "18004664415"; 2886 final String phoneNumber6 = "18004664416"; 2887 insertPhoneNumber(rawContactId4, phoneNumber4); 2888 insertPhoneNumber(rawContactId4, phoneNumber5); 2889 insertPhoneNumber(rawContactId4, phoneNumber6); 2890 values3.put(Phone.NUMBER, phoneNumber3); 2891 values4.put(Phone.NUMBER, phoneNumber4); 2892 2893 sendFeedback(phoneNumber5, DataUsageFeedback.USAGE_TYPE_CALL, values4); 2894 sendFeedback(phoneNumber5, DataUsageFeedback.USAGE_TYPE_CALL, values4); 2895 sendFeedback(phoneNumber6, DataUsageFeedback.USAGE_TYPE_CALL, values4); 2896 2897 // Create a ContentValues object representing the second phone number of contact 4 2898 final ContentValues values5 = new ContentValues(values4); 2899 values5.put(Phone.NUMBER, phoneNumber5); 2900 2901 // Create a ContentValues object representing the third phone number of contact 4 2902 final ContentValues values6 = new ContentValues(values4); 2903 values6.put(Phone.NUMBER, phoneNumber6); 2904 2905 // Phone only strequent should return all phone numbers belonging to the 4th contact, 2906 // and then contact 3. 2907 assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] {values5, values6, 2908 values4, values3}); 2909 2910 // Send feedback for the 2rd phone number, pretending we send the person a SMS message. 2911 sendFeedback(phoneNumber2, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1); 2912 2913 // SMS feedback shouldn't affect phone-only results. 2914 assertStoredValuesOrderly(phoneOnlyStrequentUri, new ContentValues[] {values5, values6, 2915 values4, values3}); 2916 2917 values4.remove(Phone.NUMBER); 2918 Uri filterUri = Uri.withAppendedPath(Contacts.CONTENT_STREQUENT_FILTER_URI, "fay"); 2919 assertStoredValues(filterUri, values4); 2920 } 2921 2922 public void testQueryContactStrequentFrequentOrder() { 2923 // Prepare test data 2924 final long rid1 = RawContactUtil.createRawContact(mResolver); 2925 final long did1 = ContentUris.parseId(insertPhoneNumber(rid1, "1")); 2926 final long did1e = ContentUris.parseId(insertEmail(rid1, "1 (at) email.com")); 2927 2928 final long rid2 = RawContactUtil.createRawContact(mResolver); 2929 final long did2 = ContentUris.parseId(insertPhoneNumber(rid2, "2")); 2930 2931 final long rid3 = RawContactUtil.createRawContact(mResolver); 2932 final long did3 = ContentUris.parseId(insertPhoneNumber(rid3, "3")); 2933 2934 final long rid4 = RawContactUtil.createRawContact(mResolver); 2935 final long did4 = ContentUris.parseId(insertPhoneNumber(rid4, "4")); 2936 2937 final long rid5 = RawContactUtil.createRawContact(mResolver); 2938 final long did5 = ContentUris.parseId(insertPhoneNumber(rid5, "5")); 2939 2940 final long rid6 = RawContactUtil.createRawContact(mResolver); 2941 final long did6 = ContentUris.parseId(insertPhoneNumber(rid6, "6")); 2942 2943 final long rid7 = RawContactUtil.createRawContact(mResolver); 2944 final long did7 = ContentUris.parseId(insertPhoneNumber(rid7, "7")); 2945 2946 final long rid8 = RawContactUtil.createRawContact(mResolver); 2947 final long did8 = ContentUris.parseId(insertPhoneNumber(rid8, "8")); 2948 2949 final long cid1 = queryContactId(rid1); 2950 final long cid2 = queryContactId(rid2); 2951 final long cid3 = queryContactId(rid3); 2952 final long cid4 = queryContactId(rid4); 2953 final long cid5 = queryContactId(rid5); 2954 final long cid6 = queryContactId(rid6); 2955 final long cid7 = queryContactId(rid7); 2956 final long cid8 = queryContactId(rid8); 2957 2958 // Make sure they aren't aggregated. 2959 EvenMoreAsserts.assertUnique(cid1, cid2, cid3, cid4, cid5, cid6, cid7, cid8); 2960 2961 // Prepare the clock 2962 sMockClock.install(); 2963 2964 // We check the timestamp in SQL, which doesn't know about the MockClock. So we need to 2965 // use the actual (roughly) time. 2966 2967 final long nowInMillis = System.currentTimeMillis(); 2968 final long oneDayAgoInMillis = (nowInMillis - 24L * 60 * 60 * 1000); 2969 final long fourDaysAgoInMillis = (nowInMillis - 4L * 24 * 60 * 60 * 1000); 2970 final long eightDaysAgoInMillis = (nowInMillis - 8L * 24 * 60 * 60 * 1000); 2971 final long fifteenDaysAgoInMillis = (nowInMillis - 15L * 24 * 60 * 60 * 1000); 2972 // All contacts older than 30 days will not be included in frequents 2973 final long thirtyOneDaysAgoInMillis = (nowInMillis - 31L * 24 * 60 * 60 * 1000); 2974 2975 // Contacts in this bucket are considered more than 30 days old 2976 sMockClock.setCurrentTimeMillis(thirtyOneDaysAgoInMillis); 2977 2978 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did1, did2); 2979 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did1); 2980 2981 // Contacts in this bucket are considered more than 14 days old 2982 sMockClock.setCurrentTimeMillis(fifteenDaysAgoInMillis); 2983 2984 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did3, did4); 2985 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did3); 2986 2987 // Contacts in this bucket are considered more than 7 days old 2988 sMockClock.setCurrentTimeMillis(eightDaysAgoInMillis); 2989 2990 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did5, did6); 2991 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did5); 2992 2993 // Contact cid1 again, but it's an email, not a phone call. 2994 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_LONG_TEXT, did1e); 2995 2996 // Contacts in this bucket are considered more than 3 days old 2997 sMockClock.setCurrentTimeMillis(fourDaysAgoInMillis); 2998 2999 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did7); 3000 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did7); 3001 3002 3003 // Contacts in this bucket are considered less than 3 days old 3004 sMockClock.setCurrentTimeMillis(oneDayAgoInMillis); 3005 3006 updateDataUsageFeedback(DataUsageFeedback.USAGE_TYPE_CALL, did8); 3007 3008 sMockClock.setCurrentTimeMillis(nowInMillis); 3009 3010 // Check the order -- The regular frequent, which is contact based. 3011 // Note because we contacted cid1 8 days ago, it's been contacted 3 times, so it comes 3012 // before cid5 and cid6, which were contacted at the same time. 3013 // cid2 will not show up because it was contacted more than 30 days ago 3014 3015 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI, 3016 cv(Contacts._ID, cid8), 3017 cv(Contacts._ID, cid7), 3018 cv(Contacts._ID, cid1), 3019 cv(Contacts._ID, cid5), 3020 cv(Contacts._ID, cid6), 3021 cv(Contacts._ID, cid3), 3022 cv(Contacts._ID, cid4)); 3023 3024 // Check the order -- phone only frequent, which is data based. 3025 // Note this is based on data, and only looks at phone numbers, so the order is different 3026 // now. 3027 // did1, did2 will not show up because they were used to make calls more than 30 days ago. 3028 assertStoredValuesOrderly(Contacts.CONTENT_STREQUENT_URI.buildUpon() 3029 .appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "1").build(), 3030 cv(Data._ID, did8), 3031 cv(Data._ID, did7), 3032 cv(Data._ID, did5), 3033 cv(Data._ID, did6), 3034 cv(Data._ID, did3), 3035 cv(Data._ID, did4)); 3036 } 3037 3038 /** 3039 * Checks ContactsProvider2 works well with frequent Uri. The provider should return frequently 3040 * contacted person ordered by number of times contacted. 3041 */ 3042 public void testQueryContactFrequent() { 3043 ContentValues values1 = new ContentValues(); 3044 final String email1 = "a (at) acme.com"; 3045 createContact(values1, "Noah", "Tever", "18004664411", 3046 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0); 3047 ContentValues values2 = new ContentValues(); 3048 final String email2 = "b (at) acme.com"; 3049 createContact(values2, "Sam", "Times", "18004664412", 3050 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0); 3051 ContentValues values3 = new ContentValues(); 3052 final String phoneNumber3 = "18004664413"; 3053 final long contactId3 = createContact(values3, "Lotta", "Calling", phoneNumber3, 3054 "c (at) acme.com", StatusUpdates.AWAY, 0, 1, 0, 0); 3055 ContentValues values4 = new ContentValues(); 3056 createContact(values4, "Fay", "Veritt", "18004664414", 3057 "d (at) acme.com", StatusUpdates.AVAILABLE, 0, 1, 0, 0); 3058 3059 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3060 3061 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, values1); 3062 3063 // Pretend email was sent to the address twice. 3064 sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2); 3065 sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2); 3066 3067 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values2, values1}); 3068 3069 // Three times 3070 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 3071 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 3072 sendFeedback(phoneNumber3, DataUsageFeedback.USAGE_TYPE_CALL, values3); 3073 3074 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 3075 new ContentValues[] {values3, values2, values1}); 3076 3077 // Test it works with selection/selectionArgs 3078 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 3079 Contacts.STARRED + "=?", new String[] {"0"}, 3080 new ContentValues[] {values2, values1}); 3081 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 3082 Contacts.STARRED + "=?", new String[] {"1"}, 3083 new ContentValues[] {values3}); 3084 3085 values3.put(Contacts.STARRED, 0); 3086 assertEquals(1, 3087 mResolver.update(Uri.withAppendedPath(Contacts.CONTENT_URI, 3088 String.valueOf(contactId3)), 3089 values3, null, null)); 3090 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 3091 Contacts.STARRED + "=?", new String[] {"0"}, 3092 new ContentValues[] {values3, values2, values1}); 3093 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, 3094 Contacts.STARRED + "=?", new String[] {"1"}, 3095 new ContentValues[] {}); 3096 } 3097 3098 public void testQueryContactFrequentExcludingInvisible() { 3099 ContentValues values1 = new ContentValues(); 3100 final String email1 = "a (at) acme.com"; 3101 final long cid1 = createContact(values1, "Noah", "Tever", "18004664411", 3102 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0); 3103 ContentValues values2 = new ContentValues(); 3104 final String email2 = "b (at) acme.com"; 3105 final long cid2 = createContact(values2, "Sam", "Times", "18004664412", 3106 email2, StatusUpdates.INVISIBLE, 0, 0, 0, 0); 3107 3108 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3109 sendFeedback(email2, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values2); 3110 3111 // First, we have two contacts in frequent. 3112 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values2, values1}); 3113 3114 // Contact 2 goes invisible. 3115 markInvisible(cid2); 3116 3117 // Now we have only 1 frequent. 3118 assertStoredValues(Contacts.CONTENT_FREQUENT_URI, new ContentValues[] {values1}); 3119 3120 } 3121 3122 public void testQueryDataUsageStat() { 3123 ContentValues values1 = new ContentValues(); 3124 final String email1 = "a (at) acme.com"; 3125 final long cid1 = createContact(values1, "Noah", "Tever", "18004664411", 3126 email1, StatusUpdates.OFFLINE, 0, 0, 0, 0); 3127 3128 sMockClock.install(); 3129 sMockClock.setCurrentTimeMillis(100); 3130 3131 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3132 3133 assertDataUsageCursorContains(Data.CONTENT_URI, "a (at) acme.com", 1, 100); 3134 3135 sMockClock.setCurrentTimeMillis(111); 3136 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_LONG_TEXT, values1); 3137 3138 assertDataUsageCursorContains(Data.CONTENT_URI, "a (at) acme.com", 2, 111); 3139 3140 sMockClock.setCurrentTimeMillis(123); 3141 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_SHORT_TEXT, values1); 3142 3143 assertDataUsageCursorContains(Data.CONTENT_URI, "a (at) acme.com", 3, 123); 3144 3145 final Uri dataUriWithUsageTypeLongText = Data.CONTENT_URI.buildUpon().appendQueryParameter( 3146 DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_LONG_TEXT).build(); 3147 3148 assertDataUsageCursorContains(dataUriWithUsageTypeLongText, "a (at) acme.com", 2, 111); 3149 3150 sMockClock.setCurrentTimeMillis(200); 3151 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); 3152 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); 3153 sendFeedback(email1, DataUsageFeedback.USAGE_TYPE_CALL, values1); 3154 3155 assertDataUsageCursorContains(Data.CONTENT_URI, "a (at) acme.com", 6, 200); 3156 3157 final Uri dataUriWithUsageTypeCall = Data.CONTENT_URI.buildUpon().appendQueryParameter( 3158 DataUsageFeedback.USAGE_TYPE, DataUsageFeedback.USAGE_TYPE_CALL).build(); 3159 3160 assertDataUsageCursorContains(dataUriWithUsageTypeCall, "a (at) acme.com", 3, 200); 3161 } 3162 3163 public void testQueryContactGroup() { 3164 long groupId = createGroup(null, "testGroup", "Test Group"); 3165 3166 ContentValues values1 = new ContentValues(); 3167 createContact(values1, "Best", "West", "18004664411", 3168 "west (at) acme.com", StatusUpdates.OFFLINE, 0, 0, groupId, 3169 StatusUpdates.CAPABILITY_HAS_CAMERA); 3170 3171 ContentValues values2 = new ContentValues(); 3172 createContact(values2, "Rest", "East", "18004664422", 3173 "east (at) acme.com", StatusUpdates.AVAILABLE, 0, 0, 0, 3174 StatusUpdates.CAPABILITY_HAS_VOICE); 3175 3176 Uri filterUri1 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group"); 3177 Cursor c = mResolver.query(filterUri1, null, null, null, Contacts._ID); 3178 assertEquals(1, c.getCount()); 3179 c.moveToFirst(); 3180 assertCursorValues(c, values1); 3181 c.close(); 3182 3183 Uri filterUri2 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Test Group"); 3184 c = mResolver.query(filterUri2, null, Contacts.DISPLAY_NAME + "=?", 3185 new String[] { "Best West" }, Contacts._ID); 3186 assertEquals(1, c.getCount()); 3187 c.close(); 3188 3189 Uri filterUri3 = Uri.withAppendedPath(Contacts.CONTENT_GROUP_URI, "Next Group"); 3190 c = mResolver.query(filterUri3, null, null, null, Contacts._ID); 3191 assertEquals(0, c.getCount()); 3192 c.close(); 3193 } 3194 3195 private void expectSecurityException(String failureMessage, Uri uri, String[] projection, 3196 String selection, String[] selectionArgs, String sortOrder) { 3197 Cursor c = null; 3198 try { 3199 c = mResolver.query(uri, projection, selection, selectionArgs, sortOrder); 3200 fail(failureMessage); 3201 } catch (SecurityException expected) { 3202 // The security exception is expected to occur because we're missing a permission. 3203 } finally { 3204 if (c != null) { 3205 c.close(); 3206 } 3207 } 3208 } 3209 3210 public void testQueryProfileRequiresReadPermission() { 3211 mActor.removePermissions("android.permission.READ_PROFILE"); 3212 3213 createBasicProfileContact(new ContentValues()); 3214 3215 // Case 1: Retrieving profile contact. 3216 expectSecurityException( 3217 "Querying for the profile without READ_PROFILE access should fail.", 3218 Profile.CONTENT_URI, null, null, null, Contacts._ID); 3219 3220 // Case 2: Retrieving profile data. 3221 expectSecurityException( 3222 "Querying for the profile data without READ_PROFILE access should fail.", 3223 Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 3224 null, null, null, Contacts._ID); 3225 3226 // Case 3: Retrieving profile entities. 3227 expectSecurityException( 3228 "Querying for the profile entities without READ_PROFILE access should fail.", 3229 Profile.CONTENT_URI.buildUpon() 3230 .appendPath("entities").build(), null, null, null, Contacts._ID); 3231 } 3232 3233 public void testQueryProfileByContactIdRequiresReadPermission() { 3234 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3235 long profileContactId = queryContactId(profileRawContactId); 3236 3237 mActor.removePermissions("android.permission.READ_PROFILE"); 3238 3239 // A query for the profile contact by ID should fail. 3240 expectSecurityException( 3241 "Querying for the profile by contact ID without READ_PROFILE access should fail.", 3242 ContentUris.withAppendedId(Contacts.CONTENT_URI, profileContactId), 3243 null, null, null, Contacts._ID); 3244 } 3245 3246 public void testQueryProfileByRawContactIdRequiresReadPermission() { 3247 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3248 3249 // Remove profile read permission and attempt to retrieve the raw contact. 3250 mActor.removePermissions("android.permission.READ_PROFILE"); 3251 expectSecurityException( 3252 "Querying for the raw contact profile without READ_PROFILE access should fail.", 3253 ContentUris.withAppendedId(RawContacts.CONTENT_URI, 3254 profileRawContactId), null, null, null, RawContacts._ID); 3255 } 3256 3257 public void testQueryProfileRawContactRequiresReadPermission() { 3258 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3259 3260 // Remove profile read permission and attempt to retrieve the profile's raw contact data. 3261 mActor.removePermissions("android.permission.READ_PROFILE"); 3262 3263 // Case 1: Retrieve the overall raw contact set for the profile. 3264 expectSecurityException( 3265 "Querying for the raw contact profile without READ_PROFILE access should fail.", 3266 Profile.CONTENT_RAW_CONTACTS_URI, null, null, null, null); 3267 3268 // Case 2: Retrieve the raw contact profile data for the inserted raw contact ID. 3269 expectSecurityException( 3270 "Querying for the raw profile data without READ_PROFILE access should fail.", 3271 ContentUris.withAppendedId( 3272 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 3273 .appendPath("data").build(), null, null, null, null); 3274 3275 // Case 3: Retrieve the raw contact profile entity for the inserted raw contact ID. 3276 expectSecurityException( 3277 "Querying for the raw profile entities without READ_PROFILE access should fail.", 3278 ContentUris.withAppendedId( 3279 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 3280 .appendPath("entity").build(), null, null, null, null); 3281 } 3282 3283 public void testQueryProfileDataByDataIdRequiresReadPermission() { 3284 createBasicProfileContact(new ContentValues()); 3285 Cursor c = mResolver.query(Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 3286 new String[]{Data._ID, Data.MIMETYPE}, null, null, null); 3287 assertEquals(4, c.getCount()); // Photo, phone, email, name. 3288 c.moveToFirst(); 3289 long profileDataId = c.getLong(0); 3290 c.close(); 3291 3292 // Remove profile read permission and attempt to retrieve the data 3293 mActor.removePermissions("android.permission.READ_PROFILE"); 3294 expectSecurityException( 3295 "Querying for the data in the profile without READ_PROFILE access should fail.", 3296 ContentUris.withAppendedId(Data.CONTENT_URI, profileDataId), 3297 null, null, null, null); 3298 } 3299 3300 public void testQueryProfileDataRequiresReadPermission() { 3301 createBasicProfileContact(new ContentValues()); 3302 3303 // Remove profile read permission and attempt to retrieve all profile data. 3304 mActor.removePermissions("android.permission.READ_PROFILE"); 3305 expectSecurityException( 3306 "Querying for the data in the profile without READ_PROFILE access should fail.", 3307 Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 3308 null, null, null, null); 3309 } 3310 3311 public void testInsertProfileRequiresWritePermission() { 3312 mActor.removePermissions("android.permission.WRITE_PROFILE"); 3313 3314 // Creating a non-profile contact should be fine. 3315 createBasicNonProfileContact(new ContentValues()); 3316 3317 // Creating a profile contact should throw an exception. 3318 try { 3319 createBasicProfileContact(new ContentValues()); 3320 fail("Creating a profile contact should fail without WRITE_PROFILE access."); 3321 } catch (SecurityException expected) { 3322 } 3323 } 3324 3325 public void testInsertProfileDataRequiresWritePermission() { 3326 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3327 3328 mActor.removePermissions("android.permission.WRITE_PROFILE"); 3329 try { 3330 insertEmail(profileRawContactId, "foo (at) bar.net", false); 3331 fail("Inserting data into a profile contact should fail without WRITE_PROFILE access."); 3332 } catch (SecurityException expected) { 3333 } 3334 } 3335 3336 public void testUpdateDataDoesNotRequireProfilePermission() { 3337 mActor.removePermissions("android.permission.READ_PROFILE"); 3338 mActor.removePermissions("android.permission.WRITE_PROFILE"); 3339 3340 // Create a non-profile contact. 3341 long rawContactId = RawContactUtil.createRawContactWithName(mResolver, "Domo", "Arigato"); 3342 long dataId = getStoredLongValue(Data.CONTENT_URI, 3343 Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?", 3344 new String[]{String.valueOf(rawContactId), StructuredName.CONTENT_ITEM_TYPE}, 3345 Data._ID); 3346 3347 // Updates its name using a selection. 3348 ContentValues values = new ContentValues(); 3349 values.put(StructuredName.GIVEN_NAME, "Bob"); 3350 values.put(StructuredName.FAMILY_NAME, "Blob"); 3351 mResolver.update(Data.CONTENT_URI, values, Data._ID + "=?", 3352 new String[]{String.valueOf(dataId)}); 3353 3354 // Check that the update went through. 3355 assertStoredValues(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), values); 3356 } 3357 3358 public void testQueryContactThenProfile() { 3359 ContentValues profileValues = new ContentValues(); 3360 long profileRawContactId = createBasicProfileContact(profileValues); 3361 long profileContactId = queryContactId(profileRawContactId); 3362 3363 ContentValues nonProfileValues = new ContentValues(); 3364 long nonProfileRawContactId = createBasicNonProfileContact(nonProfileValues); 3365 long nonProfileContactId = queryContactId(nonProfileRawContactId); 3366 3367 assertStoredValues(Contacts.CONTENT_URI, nonProfileValues); 3368 assertSelection(Contacts.CONTENT_URI, nonProfileValues, Contacts._ID, nonProfileContactId); 3369 3370 assertStoredValues(Profile.CONTENT_URI, profileValues); 3371 } 3372 3373 public void testQueryContactExcludeProfile() { 3374 // Create a profile contact (it should not be returned by the general contact URI). 3375 createBasicProfileContact(new ContentValues()); 3376 3377 // Create a non-profile contact - this should be returned. 3378 ContentValues nonProfileValues = new ContentValues(); 3379 createBasicNonProfileContact(nonProfileValues); 3380 3381 assertStoredValues(Contacts.CONTENT_URI, new ContentValues[] {nonProfileValues}); 3382 } 3383 3384 public void testQueryProfile() { 3385 ContentValues profileValues = new ContentValues(); 3386 createBasicProfileContact(profileValues); 3387 3388 assertStoredValues(Profile.CONTENT_URI, profileValues); 3389 } 3390 3391 private ContentValues[] getExpectedProfileDataValues() { 3392 // Expected photo data values (only field is the photo BLOB, which we can't check). 3393 ContentValues photoRow = new ContentValues(); 3394 photoRow.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE); 3395 3396 // Expected phone data values. 3397 ContentValues phoneRow = new ContentValues(); 3398 phoneRow.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); 3399 phoneRow.put(Phone.NUMBER, "18005554411"); 3400 3401 // Expected email data values. 3402 ContentValues emailRow = new ContentValues(); 3403 emailRow.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE); 3404 emailRow.put(Email.ADDRESS, "mia.prophyl (at) acme.com"); 3405 3406 // Expected name data values. 3407 ContentValues nameRow = new ContentValues(); 3408 nameRow.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); 3409 nameRow.put(StructuredName.DISPLAY_NAME, "Mia Prophyl"); 3410 nameRow.put(StructuredName.GIVEN_NAME, "Mia"); 3411 nameRow.put(StructuredName.FAMILY_NAME, "Prophyl"); 3412 3413 return new ContentValues[]{photoRow, phoneRow, emailRow, nameRow}; 3414 } 3415 3416 public void testQueryProfileData() { 3417 createBasicProfileContact(new ContentValues()); 3418 3419 assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 3420 getExpectedProfileDataValues()); 3421 } 3422 3423 public void testQueryProfileEntities() { 3424 createBasicProfileContact(new ContentValues()); 3425 3426 assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("entities").build(), 3427 getExpectedProfileDataValues()); 3428 } 3429 3430 public void testQueryRawProfile() { 3431 ContentValues profileValues = new ContentValues(); 3432 createBasicProfileContact(profileValues); 3433 3434 // The raw contact view doesn't include the photo ID. 3435 profileValues.remove(Contacts.PHOTO_ID); 3436 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, profileValues); 3437 } 3438 3439 public void testQueryRawProfileById() { 3440 ContentValues profileValues = new ContentValues(); 3441 long profileRawContactId = createBasicProfileContact(profileValues); 3442 3443 // The raw contact view doesn't include the photo ID. 3444 profileValues.remove(Contacts.PHOTO_ID); 3445 assertStoredValues(ContentUris.withAppendedId( 3446 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId), profileValues); 3447 } 3448 3449 public void testQueryRawProfileData() { 3450 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3451 3452 assertStoredValues(ContentUris.withAppendedId( 3453 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 3454 .appendPath("data").build(), getExpectedProfileDataValues()); 3455 } 3456 3457 public void testQueryRawProfileEntity() { 3458 long profileRawContactId = createBasicProfileContact(new ContentValues()); 3459 3460 assertStoredValues(ContentUris.withAppendedId( 3461 Profile.CONTENT_RAW_CONTACTS_URI, profileRawContactId).buildUpon() 3462 .appendPath("entity").build(), getExpectedProfileDataValues()); 3463 } 3464 3465 public void testQueryDataForProfile() { 3466 createBasicProfileContact(new ContentValues()); 3467 3468 assertStoredValues(Profile.CONTENT_URI.buildUpon().appendPath("data").build(), 3469 getExpectedProfileDataValues()); 3470 } 3471 3472 public void testUpdateProfileRawContact() { 3473 createBasicProfileContact(new ContentValues()); 3474 ContentValues updatedValues = new ContentValues(); 3475 updatedValues.put(RawContacts.SEND_TO_VOICEMAIL, 0); 3476 updatedValues.put(RawContacts.CUSTOM_RINGTONE, "rachmaninoff3"); 3477 updatedValues.put(RawContacts.STARRED, 1); 3478 mResolver.update(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues, null, null); 3479 3480 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, updatedValues); 3481 } 3482 3483 public void testInsertProfileWithDataSetTriggersAccountCreation() { 3484 // Check that we have no profile raw contacts. 3485 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, new ContentValues[]{}); 3486 3487 // Insert a profile record with a new data set. 3488 Account account = new Account("a", "b"); 3489 String dataSet = "c"; 3490 Uri profileUri = TestUtil.maybeAddAccountQueryParameters(Profile.CONTENT_RAW_CONTACTS_URI, 3491 account) 3492 .buildUpon().appendQueryParameter(RawContacts.DATA_SET, dataSet).build(); 3493 ContentValues values = new ContentValues(); 3494 long rawContactId = ContentUris.parseId(mResolver.insert(profileUri, values)); 3495 values.put(RawContacts._ID, rawContactId); 3496 3497 // Check that querying for the profile gets the created raw contact. 3498 assertStoredValues(Profile.CONTENT_RAW_CONTACTS_URI, values); 3499 } 3500 3501 public void testLoadProfilePhoto() throws IOException { 3502 long rawContactId = createBasicProfileContact(new ContentValues()); 3503 insertPhoto(rawContactId, R.drawable.earth_normal); 3504 EvenMoreAsserts.assertImageRawData(getContext(), 3505 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.THUMBNAIL), 3506 Contacts.openContactPhotoInputStream(mResolver, Profile.CONTENT_URI, false)); 3507 } 3508 3509 public void testLoadProfileDisplayPhoto() throws IOException { 3510 long rawContactId = createBasicProfileContact(new ContentValues()); 3511 insertPhoto(rawContactId, R.drawable.earth_normal); 3512 EvenMoreAsserts.assertImageRawData(getContext(), 3513 loadPhotoFromResource(R.drawable.earth_normal, PhotoSize.DISPLAY_PHOTO), 3514 Contacts.openContactPhotoInputStream(mResolver,