1 /* 2 * Copyright (C) 2010 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 android.provider.cts.contacts; 18 19 import android.content.ContentProviderClient; 20 import android.content.ContentResolver; 21 import android.content.ContentUris; 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.content.pm.ResolveInfo; 27 import android.net.Uri; 28 import android.os.SystemClock; 29 import android.provider.ContactsContract; 30 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 31 import android.provider.ContactsContract.Contacts; 32 import android.provider.ContactsContract.Directory; 33 import android.provider.ContactsContract.RawContacts; 34 import android.provider.cts.contacts.ContactsContract_TestDataBuilder.TestContact; 35 import android.provider.cts.contacts.ContactsContract_TestDataBuilder.TestRawContact; 36 import android.provider.cts.contacts.account.StaticAccountAuthenticator; 37 import android.test.AndroidTestCase; 38 39 import java.util.List; 40 41 public class ContactsContract_ContactsTest extends AndroidTestCase { 42 43 private StaticAccountAuthenticator mAuthenticator; 44 private ContentResolver mResolver; 45 private ContactsContract_TestDataBuilder mBuilder; 46 47 @Override setUp()48 protected void setUp() throws Exception { 49 super.setUp(); 50 mResolver = getContext().getContentResolver(); 51 ContentProviderClient provider = 52 mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY); 53 mBuilder = new ContactsContract_TestDataBuilder(provider); 54 55 mAuthenticator = new StaticAccountAuthenticator(getContext()); 56 } 57 58 @Override tearDown()59 protected void tearDown() throws Exception { 60 super.tearDown(); 61 mBuilder.cleanup(); 62 } 63 testMarkAsContacted()64 public void testMarkAsContacted() throws Exception { 65 TestRawContact rawContact = mBuilder.newRawContact().insert().load(); 66 TestContact contact = rawContact.getContact().load(); 67 68 assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED)); 69 assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED)); 70 71 assertEquals(0, rawContact.getLong(Contacts.LAST_TIME_CONTACTED)); 72 assertEquals(0, rawContact.getLong(Contacts.TIMES_CONTACTED)); 73 74 // Note we no longer support contact affinity as of Q, so times_contacted and 75 // last_time_contacted are always 0. 76 77 for (int i = 1; i < 10; i++) { 78 Contacts.markAsContacted(mResolver, contact.getId()); 79 contact.load(); 80 rawContact.load(); 81 82 assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED)); 83 assertEquals("#" + i, 0, contact.getLong(Contacts.TIMES_CONTACTED)); 84 85 assertEquals(0, rawContact.getLong(Contacts.LAST_TIME_CONTACTED)); 86 assertEquals("#" + i, 0, rawContact.getLong(Contacts.TIMES_CONTACTED)); 87 } 88 } 89 testContentUri()90 public void testContentUri() { 91 Context context = getContext(); 92 PackageManager packageManager = context.getPackageManager(); 93 Intent intent = new Intent(Intent.ACTION_VIEW, ContactsContract.Contacts.CONTENT_URI); 94 List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, 0); 95 assertFalse("Device does not support the activity intent: " + intent, 96 resolveInfos.isEmpty()); 97 } 98 testLookupUri()99 public void testLookupUri() throws Exception { 100 TestRawContact rawContact = mBuilder.newRawContact().insert().load(); 101 TestContact contact = rawContact.getContact().load(); 102 103 Uri contactUri = contact.getUri(); 104 long contactId = contact.getId(); 105 String lookupKey = contact.getString(Contacts.LOOKUP_KEY); 106 107 Uri lookupUri = Contacts.getLookupUri(contactId, lookupKey); 108 assertEquals(ContentUris.withAppendedId(Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, 109 lookupKey), contactId), lookupUri); 110 111 Uri nullLookupUri = Contacts.getLookupUri(contactId, null); 112 assertNull(nullLookupUri); 113 114 Uri emptyLookupUri = Contacts.getLookupUri(contactId, ""); 115 assertNull(emptyLookupUri); 116 117 Uri lookupUri2 = Contacts.getLookupUri(mResolver, contactUri); 118 assertEquals(lookupUri, lookupUri2); 119 120 Uri contactUri2 = Contacts.lookupContact(mResolver, lookupUri); 121 assertEquals(contactUri, contactUri2); 122 } 123 testInsert_isUnsupported()124 public void testInsert_isUnsupported() { 125 DatabaseAsserts.assertInsertIsUnsupported(mResolver, Contacts.CONTENT_URI); 126 } 127 testContactDelete_removesContactRecord()128 public void testContactDelete_removesContactRecord() { 129 assertContactCreateDelete(); 130 } 131 testContactDelete_hasDeleteLog()132 public void testContactDelete_hasDeleteLog() { 133 long start = System.currentTimeMillis(); 134 DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(); 135 DatabaseAsserts.assertHasDeleteLogGreaterThan(mResolver, ids.mContactId, start); 136 137 // Clean up. Must also remove raw contact. 138 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 139 } 140 testContactDelete_marksRawContactsForDeletion()141 public void testContactDelete_marksRawContactsForDeletion() { 142 DatabaseAsserts.ContactIdPair ids = assertContactCreateDelete(); 143 144 String[] projection = new String[] { 145 RawContacts.DIRTY, 146 RawContacts.DELETED 147 }; 148 List<String[]> records = RawContactUtil.queryByContactId(mResolver, ids.mContactId, 149 projection); 150 for (String[] arr : records) { 151 assertEquals("1", arr[0]); 152 assertEquals("1", arr[1]); 153 } 154 155 // Clean up 156 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 157 } 158 testContactDelete_localContactDeletedImmediately()159 public void testContactDelete_localContactDeletedImmediately() { 160 // Create a raw contact in the local (null) account 161 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact( 162 mResolver, null); 163 164 ContactUtil.delete(mResolver, ids.mContactId); 165 166 // Assert that the local raw contact is removed from the database and 167 // not merely marked DELETED=1. 168 assertNull(RawContactUtil.queryByRawContactId(mResolver, ids.mRawContactId, null)); 169 170 // Nothing to clean up 171 } 172 testContactDelete_allLocalContactsDeletedImmediately()173 public void testContactDelete_allLocalContactsDeletedImmediately() { 174 // Create two raw contacts in the local (null) account 175 DatabaseAsserts.ContactIdPair ids1 = DatabaseAsserts.assertAndCreateContactWithName( 176 mResolver, null, "John Smith"); 177 DatabaseAsserts.ContactIdPair ids2 = DatabaseAsserts.assertAndCreateContactWithName( 178 mResolver, null, "John Smith"); 179 180 // Aggregate the two raw contacts together 181 ContactUtil.setAggregationException(mResolver, 182 ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER, ids1.mRawContactId, 183 ids2.mRawContactId); 184 185 // Assert that the contacts were aggregated together 186 long contactId1 = RawContactUtil.queryContactIdByRawContactId(mResolver, 187 ids1.mRawContactId); 188 long contactId2 = RawContactUtil.queryContactIdByRawContactId(mResolver, 189 ids2.mRawContactId); 190 assertEquals(contactId1, contactId2); 191 192 // Delete the contact 193 ContactUtil.delete(mResolver, contactId1); 194 195 // Assert that both of the local raw contacts were removed from the database and 196 // not merely marked DELETED=1. 197 assertNull(RawContactUtil.queryByRawContactId(mResolver, ids1.mRawContactId, null)); 198 assertNull(RawContactUtil.queryByRawContactId(mResolver, ids2.mRawContactId, null)); 199 200 // Nothing to clean up 201 } 202 testContactDelete_localContactDeletedImmediatelyWhenAggregatedWithNonLocal()203 public void testContactDelete_localContactDeletedImmediatelyWhenAggregatedWithNonLocal() { 204 // Create a raw contact in the local (null) account 205 DatabaseAsserts.ContactIdPair ids1 = DatabaseAsserts.assertAndCreateContactWithName( 206 mResolver, null, "John Smith"); 207 208 // Create a raw contact in a non-local account with the same name 209 DatabaseAsserts.ContactIdPair ids2 = DatabaseAsserts.assertAndCreateContactWithName( 210 mResolver, StaticAccountAuthenticator.ACCOUNT_1, "John Smith"); 211 212 // Aggregate the two raw contacts together 213 ContactUtil.setAggregationException(mResolver, 214 ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER, ids1.mRawContactId, 215 ids2.mRawContactId); 216 217 // Assert that the contacts were aggregated together 218 long contactId1 = RawContactUtil.queryContactIdByRawContactId(mResolver, 219 ids1.mRawContactId); 220 long contactId2 = RawContactUtil.queryContactIdByRawContactId(mResolver, 221 ids2.mRawContactId); 222 assertEquals(contactId1, contactId2); 223 224 // Delete the contact 225 ContactUtil.delete(mResolver, contactId1); 226 227 // Assert that the local raw contact was removed from the database 228 assertNull(RawContactUtil.queryByRawContactId(mResolver, ids1.mRawContactId, null)); 229 230 // Assert that the non-local raw contact was marked DELETED=1 231 String[] projection = new String[]{ 232 RawContacts.DIRTY, 233 RawContacts.DELETED 234 }; 235 List<String[]> records = RawContactUtil.queryByContactId(mResolver, ids2.mContactId, 236 projection); 237 for (String[] arr : records) { 238 assertEquals("1", arr[0]); 239 assertEquals("1", arr[1]); 240 } 241 242 // Clean up 243 RawContactUtil.delete(mResolver, ids2.mRawContactId, true); 244 } 245 testContactUpdate_updatesContactUpdatedTimestamp()246 public void testContactUpdate_updatesContactUpdatedTimestamp() { 247 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 248 249 long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 250 251 ContentValues values = new ContentValues(); 252 values.put(ContactsContract.Contacts.STARRED, 1); 253 254 SystemClock.sleep(1); 255 ContactUtil.update(mResolver, ids.mContactId, values); 256 257 long newTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId); 258 assertTrue(newTime > baseTime); 259 260 // Clean up 261 RawContactUtil.delete(mResolver, ids.mRawContactId, true); 262 } 263 264 /** 265 * Note we no longer support contact affinity as of Q, so times_contacted and 266 * last_time_contacted are always 0. 267 */ testContactUpdate_usageStats()268 public void testContactUpdate_usageStats() throws Exception { 269 final TestRawContact rawContact = mBuilder.newRawContact().insert().load(); 270 final TestContact contact = rawContact.getContact().load(); 271 272 contact.load(); 273 assertEquals(0L, contact.getLong(Contacts.TIMES_CONTACTED)); 274 assertEquals(0L, contact.getLong(Contacts.LAST_TIME_CONTACTED)); 275 276 final long now = System.currentTimeMillis(); 277 278 ContentValues values = new ContentValues(); 279 values.clear(); 280 values.put(Contacts.TIMES_CONTACTED, 3); 281 values.put(Contacts.LAST_TIME_CONTACTED, now); 282 ContactUtil.update(mResolver, contact.getId(), values); 283 284 contact.load(); 285 assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED)); 286 assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED)); 287 288 // This is also the same as markAsContacted(). 289 values.clear(); 290 values.put(Contacts.LAST_TIME_CONTACTED, now); 291 ContactUtil.update(mResolver, contact.getId(), values); 292 293 contact.load(); 294 assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED)); 295 assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED)); 296 297 values.clear(); 298 values.put(Contacts.TIMES_CONTACTED, 10); 299 300 ContactUtil.update(mResolver, contact.getId(), values); 301 302 contact.load(); 303 assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED)); 304 assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED)); 305 } 306 307 /** 308 * Make sure the rounded usage stats values are also what the callers would see in where 309 * clauses. 310 * 311 * This tests both contacts and raw_contacts. 312 */ testContactUpdateDelete_usageStats_visibilityInWhere()313 public void testContactUpdateDelete_usageStats_visibilityInWhere() throws Exception { 314 final TestRawContact rawContact = mBuilder.newRawContact().insert().load(); 315 final TestContact contact = rawContact.getContact().load(); 316 317 // To make things more predictable, inline markAsContacted here with a known timestamp. 318 final long now = (System.currentTimeMillis() / 86400 * 86400) + 86400 * 5 + 123; 319 320 ContentValues values = new ContentValues(); 321 values.put(Contacts.LAST_TIME_CONTACTED, now); 322 323 // This makes the internal TIMES_CONTACTED 35. But the visible value is still 30. 324 for (int i = 0; i < 35; i++) { 325 ContactUtil.update(mResolver, contact.getId(), values); 326 } 327 328 contact.load(); 329 rawContact.load(); 330 331 assertEquals(0, contact.getLong(Contacts.LAST_TIME_CONTACTED)); 332 assertEquals(0, contact.getLong(Contacts.TIMES_CONTACTED)); 333 334 assertEquals(0, rawContact.getLong(Contacts.LAST_TIME_CONTACTED)); 335 assertEquals(0, rawContact.getLong(Contacts.TIMES_CONTACTED)); 336 } 337 338 /** Make sure local contacts are visible by default. */ testContactQuery_localContactVisibleByDefault()339 public void testContactQuery_localContactVisibleByDefault() throws Exception { 340 // Raw contacts without an account specified are created in the local account 341 final TestRawContact localRawContact = mBuilder.newRawContact().insert().load(); 342 final TestContact contact = localRawContact.getContact().load(); 343 344 assertEquals(RawContacts.getLocalAccountName(mContext), 345 localRawContact.getString(RawContacts.ACCOUNT_NAME)); 346 assertEquals(RawContacts.getLocalAccountType(mContext), 347 localRawContact.getString(RawContacts.ACCOUNT_TYPE)); 348 assertNull(localRawContact.getString(RawContacts.DATA_SET)); 349 assertEquals(1, contact.getLong(Contacts.IN_VISIBLE_GROUP)); 350 } 351 testProjection()352 public void testProjection() throws Exception { 353 final TestRawContact rawContact = mBuilder.newRawContact().insert().load(); 354 rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE) 355 .with(StructuredName.GIVEN_NAME, "xxx") 356 .insert(); 357 358 final TestContact contact = rawContact.getContact().load(); 359 final long contactId = contact.getId(); 360 final String lookupKey = contact.getString(Contacts.LOOKUP_KEY); 361 362 final String[] PROJECTION = new String[]{ 363 Contacts._ID, 364 Contacts.DISPLAY_NAME, 365 Contacts.DISPLAY_NAME_PRIMARY, 366 Contacts.DISPLAY_NAME_ALTERNATIVE, 367 Contacts.DISPLAY_NAME_SOURCE, 368 Contacts.PHONETIC_NAME, 369 Contacts.PHONETIC_NAME_STYLE, 370 Contacts.SORT_KEY_PRIMARY, 371 Contacts.SORT_KEY_ALTERNATIVE, 372 Contacts.LAST_TIME_CONTACTED, 373 Contacts.TIMES_CONTACTED, 374 Contacts.STARRED, 375 Contacts.PINNED, 376 Contacts.IN_DEFAULT_DIRECTORY, 377 Contacts.IN_VISIBLE_GROUP, 378 Contacts.PHOTO_ID, 379 Contacts.PHOTO_FILE_ID, 380 Contacts.PHOTO_URI, 381 Contacts.PHOTO_THUMBNAIL_URI, 382 Contacts.CUSTOM_RINGTONE, 383 Contacts.HAS_PHONE_NUMBER, 384 Contacts.SEND_TO_VOICEMAIL, 385 Contacts.IS_USER_PROFILE, 386 Contacts.LOOKUP_KEY, 387 Contacts.NAME_RAW_CONTACT_ID, 388 Contacts.CONTACT_PRESENCE, 389 Contacts.CONTACT_CHAT_CAPABILITY, 390 Contacts.CONTACT_STATUS, 391 Contacts.CONTACT_STATUS_TIMESTAMP, 392 Contacts.CONTACT_STATUS_RES_PACKAGE, 393 Contacts.CONTACT_STATUS_LABEL, 394 Contacts.CONTACT_STATUS_ICON, 395 Contacts.CONTACT_LAST_UPDATED_TIMESTAMP 396 }; 397 398 // Contacts.CONTENT_URI 399 DatabaseAsserts.checkProjection(mResolver, 400 Contacts.CONTENT_URI, 401 PROJECTION, 402 new long[]{contact.getId()} 403 ); 404 405 // Contacts.CONTENT_FILTER_URI 406 DatabaseAsserts.checkProjection(mResolver, 407 Contacts.CONTENT_FILTER_URI.buildUpon().appendEncodedPath("xxx").build(), 408 PROJECTION, 409 new long[]{contact.getId()} 410 ); 411 412 // Contacts.CONTENT_FILTER_URI 413 DatabaseAsserts.checkProjection(mResolver, 414 Contacts.ENTERPRISE_CONTENT_FILTER_URI.buildUpon().appendEncodedPath("xxx") 415 .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, 416 String.valueOf(Directory.DEFAULT)).build(), 417 PROJECTION, 418 new long[]{contact.getId()} 419 ); 420 421 // Contacts.CONTENT_LOOKUP_URI 422 DatabaseAsserts.checkProjection(mResolver, 423 Contacts.getLookupUri(contactId, lookupKey), 424 PROJECTION, 425 new long[]{contact.getId()} 426 ); 427 } 428 429 /** 430 * Create a contact. Delete it. And assert that the contact record is no longer present. 431 * 432 * @return The contact id and raw contact id that was created. 433 */ assertContactCreateDelete()434 private DatabaseAsserts.ContactIdPair assertContactCreateDelete() { 435 DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver); 436 437 SystemClock.sleep(1); 438 ContactUtil.delete(mResolver, ids.mContactId); 439 440 assertFalse(ContactUtil.recordExistsForContactId(mResolver, ids.mContactId)); 441 442 return ids; 443 } 444 } 445