• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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 android.content.ContentValues;
20 import android.database.Cursor;
21 import android.database.sqlite.SQLiteDatabase;
22 import android.provider.ContactsContract;
23 import android.provider.ContactsContract.RawContacts;
24 import android.test.MoreAsserts;
25 import android.test.suitebuilder.annotation.LargeTest;
26 import android.test.suitebuilder.annotation.SmallTest;
27 
28 import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
29 import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
30 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
31 import com.google.android.collect.Sets;
32 
33 import java.util.HashSet;
34 import java.util.Set;
35 
36 @SmallTest
37 public class ContactsDatabaseHelperTest extends BaseContactsProvider2Test {
38     private ContactsDatabaseHelper mDbHelper;
39     private SQLiteDatabase mDb;
40 
41     @Override
setUp()42     protected void setUp() throws Exception {
43         super.setUp();
44         mDbHelper = getContactsProvider().getDatabaseHelper(getContext());
45         mDb = mDbHelper.getWritableDatabase();
46     }
47 
testGetOrCreateAccountId()48     public void testGetOrCreateAccountId() {
49         final AccountWithDataSet a1 = null;
50         final AccountWithDataSet a2 = new AccountWithDataSet("a", null, null);
51         final AccountWithDataSet a3 = new AccountWithDataSet(null, "b", null);
52         final AccountWithDataSet a4 = new AccountWithDataSet(null, null, "c");
53         final AccountWithDataSet a5 = new AccountWithDataSet("a", "b", "c");
54 
55         // First, there's no accounts.  getAccountIdOrNull() always returns null.
56         assertNull(mDbHelper.getAccountIdOrNull(a1));
57         assertNull(mDbHelper.getAccountIdOrNull(a2));
58         assertNull(mDbHelper.getAccountIdOrNull(a3));
59         assertNull(mDbHelper.getAccountIdOrNull(a4));
60         assertNull(mDbHelper.getAccountIdOrNull(a5));
61 
62         // getOrCreateAccountId should create accounts.
63         final long a1id = mDbHelper.getOrCreateAccountIdInTransaction(a1);
64         final long a2id = mDbHelper.getOrCreateAccountIdInTransaction(a2);
65         final long a3id = mDbHelper.getOrCreateAccountIdInTransaction(a3);
66         final long a4id = mDbHelper.getOrCreateAccountIdInTransaction(a4);
67         final long a5id = mDbHelper.getOrCreateAccountIdInTransaction(a5);
68 
69         // The IDs should be all positive and unique.
70         assertTrue(a1id > 0);
71         assertTrue(a2id > 0);
72         assertTrue(a3id > 0);
73         assertTrue(a4id > 0);
74         assertTrue(a5id > 0);
75 
76         final Set<Long> ids = Sets.newHashSet();
77         ids.add(a1id);
78         ids.add(a2id);
79         ids.add(a3id);
80         ids.add(a4id);
81         ids.add(a5id);
82         assertEquals(5, ids.size());
83 
84         // Second call: This time getOrCreateAccountId will return the existing IDs.
85         assertEquals(a1id, mDbHelper.getOrCreateAccountIdInTransaction(a1));
86         assertEquals(a2id, mDbHelper.getOrCreateAccountIdInTransaction(a2));
87         assertEquals(a3id, mDbHelper.getOrCreateAccountIdInTransaction(a3));
88         assertEquals(a4id, mDbHelper.getOrCreateAccountIdInTransaction(a4));
89         assertEquals(a5id, mDbHelper.getOrCreateAccountIdInTransaction(a5));
90 
91         // Now getAccountIdOrNull() returns IDs too.
92         assertEquals((Long) a1id, mDbHelper.getAccountIdOrNull(a1));
93         assertEquals((Long) a2id, mDbHelper.getAccountIdOrNull(a2));
94         assertEquals((Long) a3id, mDbHelper.getAccountIdOrNull(a3));
95         assertEquals((Long) a4id, mDbHelper.getAccountIdOrNull(a4));
96         assertEquals((Long) a5id, mDbHelper.getAccountIdOrNull(a5));
97 
98         // null and AccountWithDataSet.NULL should be treated as the same thing.
99         assertEquals(a1id, mDbHelper.getOrCreateAccountIdInTransaction(AccountWithDataSet.LOCAL));
100         assertEquals((Long) a1id, mDbHelper.getAccountIdOrNull(AccountWithDataSet.LOCAL));
101 
102         // Remove all accounts.
103         mDbHelper.getWritableDatabase().execSQL("delete from " + Tables.ACCOUNTS);
104 
105         assertNull(mDbHelper.getAccountIdOrNull(AccountWithDataSet.LOCAL));
106         assertNull(mDbHelper.getAccountIdOrNull(a1));
107         assertNull(mDbHelper.getAccountIdOrNull(a2));
108         assertNull(mDbHelper.getAccountIdOrNull(a3));
109         assertNull(mDbHelper.getAccountIdOrNull(a4));
110         assertNull(mDbHelper.getAccountIdOrNull(a5));
111 
112         // Logically same as a5, but physically different object.
113         final AccountWithDataSet a5b = new AccountWithDataSet("a", "b", "c");
114         // a5 and a5b should have the same ID.
115         assertEquals(
116                 mDbHelper.getOrCreateAccountIdInTransaction(a5),
117                 mDbHelper.getOrCreateAccountIdInTransaction(a5b));
118     }
119 
120     /**
121      * Test for {@link ContactsDatabaseHelper#queryIdWithOneArg} and
122      * {@link ContactsDatabaseHelper#insertWithOneArgAndReturnId}.
123      */
testQueryIdWithOneArg_insertWithOneArgAndReturnId()124     public void testQueryIdWithOneArg_insertWithOneArgAndReturnId() {
125         final String query =
126                 "SELECT " + MimetypesColumns._ID +
127                         " FROM " + Tables.MIMETYPES +
128                         " WHERE " + MimetypesColumns.MIMETYPE + "=?";
129 
130         final String insert =
131                 "INSERT INTO " + Tables.MIMETYPES + "("
132                         + MimetypesColumns.MIMETYPE +
133                         ") VALUES (?)";
134 
135         // First, the table is empty.
136         assertEquals(-1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value1"));
137         assertEquals(-1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value2"));
138 
139         // Insert one value.
140         final long id1 = ContactsDatabaseHelper.insertWithOneArgAndReturnId(mDb, insert, "value1");
141         MoreAsserts.assertNotEqual(-1, id1);
142 
143         assertEquals(id1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value1"));
144         assertEquals(-1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value2"));
145 
146 
147         // Insert one value.
148         final long id2 = ContactsDatabaseHelper.insertWithOneArgAndReturnId(mDb, insert, "value2");
149         MoreAsserts.assertNotEqual(-1, id2);
150 
151         assertEquals(id1, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value1"));
152         assertEquals(id2, ContactsDatabaseHelper.queryIdWithOneArg(mDb, query, "value2"));
153 
154         // Insert the same value and cause a conflict.
155         assertEquals(-1, ContactsDatabaseHelper.insertWithOneArgAndReturnId(mDb, insert, "value2"));
156     }
157 
158     /**
159      * Test for {@link ContactsDatabaseHelper#getPackageId(String)} and
160      * {@link ContactsDatabaseHelper#getMimeTypeId(String)}.
161      *
162      * We test them at the same time here, to make sure they're not mixing up the caches.
163      */
testGetPackageId_getMimeTypeId()164     public void testGetPackageId_getMimeTypeId() {
165 
166         // Test for getPackageId.
167         final long packageId1 = mDbHelper.getPackageId("value1");
168         final long packageId2 = mDbHelper.getPackageId("value2");
169         final long packageId3 = mDbHelper.getPackageId("value3");
170 
171         // Make sure they're all different.
172         final HashSet<Long> set = new HashSet<>();
173         set.add(packageId1);
174         set.add(packageId2);
175         set.add(packageId3);
176 
177         assertEquals(3, set.size());
178 
179         // Test for getMimeTypeId.
180         final long mimetypeId1 = mDbHelper.getMimeTypeId("value1");
181         final long mimetypeId2 = mDbHelper.getMimeTypeId("value2");
182         final long mimetypeId3 = mDbHelper.getMimeTypeId("value3");
183 
184         // Make sure they're all different.
185         set.clear();
186         set.add(mimetypeId1);
187         set.add(mimetypeId2);
188         set.add(mimetypeId3);
189 
190         assertEquals(3, set.size());
191 
192         // Call with the same values and make sure they return the cached value.
193         final long packageId1b = mDbHelper.getPackageId("value1");
194         final long mimetypeId1b = mDbHelper.getMimeTypeId("value1");
195 
196         assertEquals(packageId1, packageId1b);
197         assertEquals(mimetypeId1, mimetypeId1b);
198 
199         // Make sure the caches are also updated.
200         assertEquals(packageId2, (long) mDbHelper.mPackageCache.get("value2"));
201         assertEquals(mimetypeId2, (long) mDbHelper.mMimetypeCache.get("value2"));
202 
203         // Clear the cache, but they should still return the values, selecting from the database.
204         mDbHelper.mPackageCache.clear();
205         mDbHelper.mMimetypeCache.clear();
206         assertEquals(packageId1, mDbHelper.getPackageId("value1"));
207         assertEquals(mimetypeId1, mDbHelper.getMimeTypeId("value1"));
208 
209         // Empty the table
210         mDb.execSQL("DELETE FROM " + Tables.MIMETYPES);
211 
212         // We should still have the cached value.
213         assertEquals(mimetypeId1, mDbHelper.getMimeTypeId("value1"));
214     }
215 
216     /**
217      * Try to cause conflicts in getMimeTypeId() by calling it from multiple threads with
218      * the current time as the argument and make sure it won't crash.
219      *
220      * We don't know from the test if there have actually been conflits, but if you look at
221      * logcat you'll see a lot of conflict warnings.
222      */
223     @LargeTest
testGetMimeTypeId_conflict()224     public void testGetMimeTypeId_conflict() {
225 
226         final int NUM_THREADS = 4;
227         final int DURATION_SECONDS = 5;
228 
229         final long finishTime = System.currentTimeMillis() + DURATION_SECONDS * 1000;
230 
231         final Runnable r = new Runnable() {
232             @Override
233             public void run() {
234                 for (;;) {
235                     final long now = System.currentTimeMillis();
236                     if (now >= finishTime) {
237                         return;
238                     }
239                     assertTrue(mDbHelper.getMimeTypeId(String.valueOf(now)) > 0);
240                 }
241             }
242         };
243         final Thread[] threads = new Thread[NUM_THREADS];
244         for (int i = 0; i < threads.length; i++) {
245             threads[i] = new Thread(r);
246             threads[i].setDaemon(true);
247         }
248         for (int i = 0; i < threads.length; i++) {
249             threads[i].start();
250         }
251         for (int i = 0; i < threads.length; i++) {
252             try {
253                 threads[i].join();
254             } catch (InterruptedException ignore) {
255             }
256         }
257     }
258 
testUpgradeHashId()259     public void testUpgradeHashId() {
260         // Create an account.
261         final long accountId = mDbHelper.getOrCreateAccountIdInTransaction(
262                 AccountWithDataSet.LOCAL);
263         // Create a raw contact.
264         ContentValues rawContactValues = new ContentValues();
265         rawContactValues.put(ContactsDatabaseHelper.RawContactsColumns.ACCOUNT_ID, accountId);
266         final long rawContactId = mDb.insert(Tables.RAW_CONTACTS,null, rawContactValues);
267         assertTrue(rawContactId > 0);
268         // Create data for the raw contact Id.
269         final StringBuilder data1 = new StringBuilder();
270         for (int i = 0; i < 2048; i++) {
271             data1.append("L");
272         }
273         final String dataString = data1.toString();
274         final String hashId = mDbHelper.generateHashId(dataString, null);
275         final int mimeType = 1;
276         final ContentValues values = new ContentValues();
277         values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
278         values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, mimeType);
279         values.put(ContactsContract.Data.DATA1, dataString);
280         for (int i = 0; i < 2048; i++) {
281             assertTrue(mDb.insert(Tables.DATA, null, values) > 0);
282         }
283         mDbHelper.upgradeToVersion1101(mDb);
284         final Cursor c = mDb.query(Tables.DATA, new String[]{ContactsContract.Data.HASH_ID},
285                 null, null, null, null, null);
286         try {
287             assertEquals(2048, c.getCount());
288             while (c.moveToNext()) {
289                 final String expectedHashId = c.getString(0);
290                 assertEquals(expectedHashId, hashId);
291             }
292         } finally {
293             c.close();
294         }
295     }
296 
testUpgradeHashIdForPhoto()297     public void testUpgradeHashIdForPhoto() {
298         // Create an account.
299         final long accountId = mDbHelper.getOrCreateAccountIdInTransaction(
300                 AccountWithDataSet.LOCAL);
301         // Create a raw contact.
302         ContentValues rawContactValues = new ContentValues();
303         rawContactValues.put(ContactsDatabaseHelper.RawContactsColumns.ACCOUNT_ID, accountId);
304         final long rawContactId = mDb.insert(Tables.RAW_CONTACTS,null, rawContactValues);
305         assertTrue(rawContactId > 0);
306 
307         // Create data for the raw contact Id.
308         final long mimeType = mDbHelper.getMimeTypeId(
309                 ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
310         final String photoHashId = mDbHelper.getPhotoHashId();
311         final ContentValues values = new ContentValues();
312         values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
313         values.put(ContactsDatabaseHelper.DataColumns.MIMETYPE_ID, mimeType);
314         for (int i = 0; i < 2048; i++) {
315             assertTrue(mDb.insert(Tables.DATA, null, values) > 0);
316         }
317         mDbHelper.upgradeToVersion1110(mDb);
318         final Cursor c = mDb.query(Tables.DATA, new String[]{ContactsContract.Data.HASH_ID},
319                 null, null, null, null, null);
320         try {
321             assertEquals(2048, c.getCount());
322             while (c.moveToNext()) {
323                 final String actualHashId = c.getString(0);
324                 assertEquals(photoHashId, actualHashId);
325             }
326         } finally {
327             c.close();
328         }
329     }
330 
testUpgradeToVersion111_SetPrimaryPhonebookBucketToNumberBucket()331     public void testUpgradeToVersion111_SetPrimaryPhonebookBucketToNumberBucket() {
332         // Zero primary phone book bucket and null primary sort key
333         final ContentValues contentValues = new ContentValues();
334         contentValues.put(RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY, 0);
335         mDb.insert(Tables.RAW_CONTACTS, null, contentValues);
336 
337         mDbHelper.upgradeToVersion1111(mDb);
338 
339         // Assert that the primary phone book bucket/label has been set to the number bucket/label
340         final ContactLocaleUtils localeUtils = ContactLocaleUtils.getInstance();
341         final int numberBucket = localeUtils.getNumberBucketIndex();
342         final String numberLabel = localeUtils.getBucketLabel(numberBucket);
343         assertUpgradeToVersion1111(numberBucket, numberLabel,
344                 RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
345                 RawContactsColumns.PHONEBOOK_LABEL_PRIMARY);
346     }
347 
testUpgradeToVersion111_SetAltPhonebookBucketToNumberBucket()348     public void testUpgradeToVersion111_SetAltPhonebookBucketToNumberBucket() {
349         // Zero alt phone book bucket and null alt sort key
350         final ContentValues contentValues = new ContentValues();
351         contentValues.put(RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, 0);
352         mDb.insert(Tables.RAW_CONTACTS, null, contentValues);
353 
354         mDbHelper.upgradeToVersion1111(mDb);
355 
356         // Assert that the alt phone book bucket/label has been set to the number bucket/label
357         final ContactLocaleUtils localeUtils = ContactLocaleUtils.getInstance();
358         final int numberBucket = localeUtils.getNumberBucketIndex();
359         final String numberLabel = localeUtils.getBucketLabel(numberBucket);
360         assertUpgradeToVersion1111(numberBucket, numberLabel,
361                 RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
362                 RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE);
363     }
364 
testUpgradeToVersion111_NonZeroPrimaryPhonebookBucket()365     public void testUpgradeToVersion111_NonZeroPrimaryPhonebookBucket() {
366         // Non-zero primary phone book bucket
367         final int primaryBucket = 1;
368         final ContentValues contentValues = new ContentValues();
369         contentValues.put(RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY, primaryBucket);
370         mDb.insert(Tables.RAW_CONTACTS, null, contentValues);
371 
372         mDbHelper.upgradeToVersion1111(mDb);
373 
374         // Assert that the primary phone book bucket/label is unchanged
375         assertUpgradeToVersion1111(primaryBucket, null, RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
376                 RawContactsColumns.PHONEBOOK_LABEL_PRIMARY);
377     }
378 
testUpgradeToVersion111_NonNullPrimarySortKey()379     public void testUpgradeToVersion111_NonNullPrimarySortKey() {
380         // Non-null primary sort key
381         final ContentValues contentValues = new ContentValues();
382         contentValues.put(RawContacts.SORT_KEY_PRIMARY, "sort_key_primary");
383         mDb.insert(Tables.RAW_CONTACTS, null, contentValues);
384 
385         mDbHelper.upgradeToVersion1111(mDb);
386 
387         // Assert that the primary phone book bucket/label is unchanged
388         assertUpgradeToVersion1111(0, null, RawContactsColumns.PHONEBOOK_BUCKET_PRIMARY,
389                 RawContactsColumns.PHONEBOOK_LABEL_PRIMARY);
390     }
391 
testUpgradeToVersion111_NonZeroAltPhonebookBucket()392     public void testUpgradeToVersion111_NonZeroAltPhonebookBucket() {
393         // Non-zero alt phone book bucket
394         final int altBucket = 1;
395         final ContentValues contentValues = new ContentValues();
396         contentValues.put(RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE, altBucket);
397         mDb.insert(Tables.RAW_CONTACTS, null, contentValues);
398 
399         mDbHelper.upgradeToVersion1111(mDb);
400 
401         // Assert that the alt phone book bucket/label is unchanged
402         assertUpgradeToVersion1111(altBucket, null, RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
403                 RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE);
404     }
405 
testUpgradeToVersion111_NonNullAltSortKeyToNumber()406     public void testUpgradeToVersion111_NonNullAltSortKeyToNumber() {
407         // Non-null alt sort key
408         final ContentValues contentValues = new ContentValues();
409         contentValues.put(RawContacts.SORT_KEY_ALTERNATIVE, "sort_key_alt");
410         mDb.insert(Tables.RAW_CONTACTS, null, contentValues);
411 
412         mDbHelper.upgradeToVersion1111(mDb);
413 
414         // Assert that the alt phone book bucket/label is unchanged
415         assertUpgradeToVersion1111(0, null, RawContactsColumns.PHONEBOOK_BUCKET_ALTERNATIVE,
416                 RawContactsColumns.PHONEBOOK_LABEL_ALTERNATIVE);
417     }
418 
assertUpgradeToVersion1111(int expectedBucket, String expectedLabel, String bucketColumn, String labelColumn)419     private void assertUpgradeToVersion1111(int expectedBucket, String expectedLabel,
420             String bucketColumn, String labelColumn) {
421         final Cursor cursor = mDb.query(Tables.RAW_CONTACTS,
422                 new String[]{bucketColumn, labelColumn}, null, null, null, null, null);
423         try {
424             assertEquals(1, cursor.getCount());
425             assertTrue(cursor.moveToNext());
426             assertEquals(expectedBucket, cursor.getInt(0));
427             assertEquals(expectedLabel, cursor.getString(1));
428         } finally {
429             cursor.close();
430         }
431     }
432 }
433