ContactsDatabaseHelper.java revision 35ed95769096bb5dd406ad7d1fcaa49a0e35a307
1b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey/*
2b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * Copyright (C) 2009 The Android Open Source Project
3b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey *
4b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * you may not use this file except in compliance with the License.
6b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * You may obtain a copy of the License at
7b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey *
8b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey *
10b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * Unless required by applicable law or agreed to in writing, software
11b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * See the License for the specific language governing permissions and
14b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * limitations under the License
15b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey */
16b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
1728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarpackage com.android.providers.contacts;
18b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
19619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.content.ContentValues;
20b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.content.Context;
2135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport com.android.internal.content.SyncStateContentProviderHelper;
22619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.content.pm.ApplicationInfo;
23619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.content.pm.PackageManager;
24619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.content.pm.PackageManager.NameNotFoundException;
25619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.database.Cursor;
26b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.database.DatabaseUtils;
27b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.database.sqlite.SQLiteDatabase;
28b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.database.sqlite.SQLiteDoneException;
29b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikovimport android.database.sqlite.SQLiteException;
30b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.database.sqlite.SQLiteOpenHelper;
31bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikovimport android.database.sqlite.SQLiteQueryBuilder;
32b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.database.sqlite.SQLiteStatement;
33619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.os.Binder;
34b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.provider.BaseColumns;
35619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.provider.SocialContract.Activities;
36de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Aggregates;
37b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
38de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Contacts;
39de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Data;
40ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.Groups;
411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkeyimport android.provider.ContactsContract.Presence;
42619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.provider.ContactsContract.RestrictionExceptions;
431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
44ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Im;
46bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
47619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
48bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikovimport android.telephony.PhoneNumberUtils;
49b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.util.Log;
50b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
51b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport java.util.HashMap;
52619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport java.util.LinkedList;
53b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
54b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey/**
55b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * Database open helper for contacts and social activity data. Designed as a
567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana * singleton to make sure that all {@link android.content.ContentProvider} users get the same
57b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * reference. Provides handy methods for maintaining package and mime-type
58b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey * lookup tables.
59b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey */
60b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey/* package */ class OpenHelper extends SQLiteOpenHelper {
61b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private static final String TAG = "OpenHelper";
62b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
63035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private static final int DATABASE_VERSION = 39;
64b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private static final String DATABASE_NAME = "contacts2.db";
651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final String DATABASE_PRESENCE = "presence_db";
66b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
67b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public interface Tables {
687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public static final String ACCOUNTS = "accounts";
69b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String AGGREGATES = "aggregates";
70b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String CONTACTS = "contacts";
71ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String PACKAGES = "packages";
72ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String MIMETYPES = "mimetypes";
73b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String PHONE_LOOKUP = "phone_lookup";
74a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final String NAME_LOOKUP = "name_lookup";
75b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        public static final String AGGREGATION_EXCEPTIONS = "agg_exceptions";
76619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String RESTRICTION_EXCEPTIONS = "rest_exceptions";
77d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        public static final String CONTACT_OPTIONS = "contact_options";
78b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String DATA = "data";
79ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String GROUPS = "groups";
801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        public static final String PRESENCE = "presence";
81b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final String NICKNAME_LOOKUP = "nickname_lookup";
82b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        public static final String AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE = "aggregates "
84fb241add950ad1314dff339805cb9eb2d6136f96Jeff Sharkey                + "LEFT OUTER JOIN presence ON (aggregates._id = presence.aggregate_id) "
85619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + "LEFT OUTER JOIN data ON (primary_phone_id = data._id)";
8600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
87ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String DATA_JOIN_MIMETYPES = "data "
88ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id = mimetypes._id)";
89b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
90bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        public static final String DATA_JOIN_MIMETYPE_CONTACTS = "data "
9128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id = mimetypes._id) "
92bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov                + "LEFT OUTER JOIN contacts ON (data.contact_id = contacts._id)";
93bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov
94ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String DATA_JOIN_CONTACTS_GROUPS = "data "
95ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN contacts ON (data.contact_id = contacts._id)"
96ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN groups ON (groups._id = data." + GroupMembership.GROUP_ROW_ID
97ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ")";
98ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
99ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES = "data "
100ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id = mimetypes._id) "
101619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + "LEFT OUTER JOIN contacts ON (data.contact_id = contacts._id) "
102ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN packages ON (contacts.package_id = packages._id)";
1037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
104ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES = "data "
105ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id = mimetypes._id) "
106ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN contacts ON (data.contact_id = contacts._id) "
107ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN packages ON (contacts.package_id = packages._id) "
108ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN aggregates ON (contacts.aggregate_id = aggregates._id)";
109ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
110ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_GROUPS = "data "
111ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id = mimetypes._id) "
112ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN contacts ON (data.contact_id = contacts._id) "
113ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN packages ON (contacts.package_id = packages._id) "
114ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN groups ON (groups._id = data." + GroupMembership.GROUP_ROW_ID
115ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ")";
116ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
117ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String GROUPS_JOIN_PACKAGES = "groups "
118ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN packages ON (groups.package_id = packages._id)";
119ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
120ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES = "data "
121ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN mimetypes ON (data.mimetype_id = mimetypes._id) "
122619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + "LEFT OUTER JOIN contacts ON (data.contact_id = contacts._id) "
123ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN aggregates ON (contacts.aggregate_id = aggregates._id)";
124ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
125ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String GROUPS_JOIN_PACKAGES_DATA_CONTACTS_AGGREGATES = "groups "
126ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN packages ON (groups.package_id = packages._id) "
127ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN data ON (groups._id = data." + GroupMembership.GROUP_ROW_ID
128ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") " + "LEFT OUTER JOIN contacts ON (data.contact_id = contacts._id) "
129619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + "LEFT OUTER JOIN aggregates ON (contacts.aggregate_id = aggregates._id)";
130b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
131b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String ACTIVITIES = "activities";
132b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
133ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String ACTIVITIES_JOIN_MIMETYPES = "activities "
134ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN mimetypes ON (activities.mimetype_id = mimetypes._id)";
135b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
136ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String ACTIVITIES_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES = "activities "
137ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN mimetypes ON (activities.mimetype_id = mimetypes._id) "
138619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + "LEFT OUTER JOIN contacts ON (activities.author_contact_id = contacts._id) "
139ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "LEFT OUTER JOIN packages ON (contacts.package_id = packages._id) "
140619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + "LEFT OUTER JOIN aggregates ON (contacts.aggregate_id = aggregates._id)";
1417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
142035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        public static final String CONTACTS_JOIN_PACKAGES = "contacts "
143035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                + "LEFT OUTER JOIN packages ON (contacts.package_id = packages._id) ";
144b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
145a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final String NAME_LOOKUP_JOIN_CONTACTS = "name_lookup "
146a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov                + "INNER JOIN contacts ON (name_lookup.contact_id = contacts._id)";
147b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
148127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        public static final String AGGREGATION_EXCEPTIONS_JOIN_CONTACTS = "agg_exceptions "
149b86b7796abc1a980d7e87afd6c1f05fe0fabe4fdDmitri Plotnikov                + "INNER JOIN contacts contacts1 "
150127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                + "ON (agg_exceptions.contact_id1 = contacts1._id) ";
151127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
152b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        public static final String AGGREGATION_EXCEPTIONS_JOIN_CONTACTS_TWICE = "agg_exceptions "
153d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                + "INNER JOIN contacts contacts1 "
154d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                + "ON (agg_exceptions.contact_id1 = contacts1._id) "
155d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                + "INNER JOIN contacts contacts2 "
156d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                + "ON (agg_exceptions.contact_id2 = contacts2._id) ";
157d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
158d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        public static final String CONTACTS_JOIN_CONTACT_OPTIONS = "contacts "
159d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                + "LEFT OUTER JOIN contact_options ON (contacts._id = contact_options._id)";
160b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
161b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
1621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    public interface Clauses {
163ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String WHERE_IM_MATCHES = MimetypesColumns.MIMETYPE + "=" + Im.MIMETYPE
1641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                + " AND " + Im.PROTOCOL + "=? AND " + Im.DATA + "=?";
1651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
166ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String WHERE_EMAIL_MATCHES = MimetypesColumns.MIMETYPE + "="
1671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                + Email.MIMETYPE + " AND " + Email.DATA + "=?";
168ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
169ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String MIMETYPE_IS_GROUP_MEMBERSHIP = MimetypesColumns.CONCRETE_MIMETYPE
170ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "='" + GroupMembership.CONTENT_ITEM_TYPE + "'";
171ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
172ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String BELONGS_TO_GROUP = DataColumns.CONCRETE_GROUP_ID + "="
173ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + GroupsColumns.CONCRETE_ID;
174ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
175ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String HAS_PRIMARY_PHONE = "("
176ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID + " IS NOT NULL OR "
177ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID + " IS NOT NULL)";
178ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
179ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // TODO: add in check against package_visible
180ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String IN_VISIBLE_GROUP = "SELECT MIN(COUNT(" + DataColumns.CONCRETE_ID
181ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + "),1) FROM " + Tables.DATA_JOIN_CONTACTS_GROUPS + " WHERE "
182ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + DataColumns.MIMETYPE_ID + "=? AND " + Contacts.AGGREGATE_ID + "="
183ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + AggregatesColumns.CONCRETE_ID + " AND " + Groups.GROUP_VISIBLE + "=1";
1841f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
1851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
186619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    public interface AggregatesColumns {
187619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String OPTIMAL_PRIMARY_PHONE_ID = "optimal_phone_id";
188619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String OPTIMAL_PRIMARY_PHONE_PACKAGE_ID = "optimal_phone_package_id";
189619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String FALLBACK_PRIMARY_PHONE_ID = "fallback_phone_id";
190619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
191619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String OPTIMAL_PRIMARY_EMAIL_ID = "optimal_email_id";
192619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID = "optimal_email_package_id";
193619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String FALLBACK_PRIMARY_EMAIL_ID = "fallback_email_id";
194619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
195619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String SINGLE_RESTRICTED_PACKAGE_ID = "single_restricted_package_id";
196ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
197ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String CONCRETE_ID = Tables.AGGREGATES + "." + BaseColumns._ID;
198619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
199619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
200619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    public interface ContactsColumns {
201b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String PACKAGE_ID = "package_id";
202ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
203ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String CONCRETE_ID = Tables.CONTACTS + "." + BaseColumns._ID;
204619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
205619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
206619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    public interface DataColumns {
207b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String MIMETYPE_ID = "mimetype_id";
208ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
209ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String CONCRETE_ID = Tables.DATA + "." + BaseColumns._ID;
210ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String CONCRETE_CONTACT_ID = Tables.DATA + "." + Data.CONTACT_ID;
211ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String CONCRETE_GROUP_ID = Tables.DATA + "."
212ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + GroupMembership.GROUP_ROW_ID;
213ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
214ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
215ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    public interface GroupsColumns {
216ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String CONCRETE_ID = Tables.GROUPS + "." + BaseColumns._ID;
217ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String CONCRETE_PACKAGE_ID = Tables.GROUPS + "." + Groups.PACKAGE_ID;
218b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
219b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
220b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public interface ActivitiesColumns {
221b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String PACKAGE_ID = "package_id";
222b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String MIMETYPE_ID = "mimetype_id";
223b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
224b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
225b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public interface PhoneLookupColumns {
226b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String _ID = BaseColumns._ID;
227b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String DATA_ID = "data_id";
228b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String CONTACT_ID = "contact_id";
229b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String NORMALIZED_NUMBER = "normalized_number";
230b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
231b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
232a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public interface NameLookupColumns {
233a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final String _ID = BaseColumns._ID;
234a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final String CONTACT_ID = "contact_id";
235a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final String NORMALIZED_NAME = "normalized_name";
236a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final String NAME_TYPE = "name_type";
237a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
238a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
239a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov    public final static class NameLookupType {
240a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final int FULL_NAME = 0;
241a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final int FULL_NAME_CONCATENATED = 1;
242a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final int FULL_NAME_REVERSE = 2;
243a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        public static final int FULL_NAME_REVERSE_CONCATENATED = 3;
244b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final int FULL_NAME_WITH_NICKNAME = 4;
245b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final int FULL_NAME_WITH_NICKNAME_REVERSE = 5;
246b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final int GIVEN_NAME_ONLY = 6;
247b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final int GIVEN_NAME_ONLY_AS_NICKNAME = 7;
248b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final int FAMILY_NAME_ONLY = 8;
249b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final int FAMILY_NAME_ONLY_AS_NICKNAME = 9;
250b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final int NICKNAME = 10;
251a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov        public static final int EMAIL_BASED_NICKNAME = 11;
252a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
253a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov        // This is the highest name lookup type code plus one
254a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov        public static final int TYPE_COUNT = 12;
255a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov
256a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov        public static boolean isBasedOnStructuredName(int nameLookupType) {
257a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov            return nameLookupType != NameLookupType.EMAIL_BASED_NICKNAME
258a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov                    && nameLookupType != NameLookupType.NICKNAME;
259a5ad551e1753086825499f1aeb6415bb986f3588Dmitri Plotnikov        }
260a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
261a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
262ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    public interface PackagesColumns {
263b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String _ID = BaseColumns._ID;
264b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String PACKAGE = "package";
265b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
266b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
267ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    public interface MimetypesColumns {
268b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String _ID = BaseColumns._ID;
269b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        public static final String MIMETYPE = "mimetype";
270ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
271ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String CONCRETE_ID = Tables.MIMETYPES + "." + BaseColumns._ID;
272ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String CONCRETE_MIMETYPE = Tables.MIMETYPES + "." + MIMETYPE;
273b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
274b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
275b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    public interface AggregationExceptionColumns {
276b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        public static final String _ID = BaseColumns._ID;
277127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        public static final String CONTACT_ID1 = "contact_id1";
278127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        public static final String CONTACT_ID2 = "contact_id2";
279b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
280b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
281619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    public interface RestrictionExceptionsColumns {
282619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String PACKAGE_PROVIDER_ID = "package_provider_id";
283619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public static final String PACKAGE_CLIENT_ID = "package_client_id";
284619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
285619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
286d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    public interface ContactOptionsColumns {
287d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        public static final String _ID = BaseColumns._ID;
288d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        public static final String CUSTOM_RINGTONE = "custom_ringtone";
289d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        public static final String SEND_TO_VOICEMAIL = "send_to_voicemail";
290d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
291d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
292b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    public interface NicknameLookupColumns {
293b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final String NAME = "name";
294b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        public static final String CLUSTER = "cluster";
295b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    }
296b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
297b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    private static final String[] NICKNAME_LOOKUP_COLUMNS = new String[] {
298b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        NicknameLookupColumns.CLUSTER
299b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    };
300b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
301b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    private static final int COL_NICKNAME_LOOKUP_CLUSTER = 0;
302b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
303b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    /** In-memory cache of previously found mimetype mappings */
304bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov    private final HashMap<String, Long> mMimetypeCache = new HashMap<String, Long>();
305b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    /** In-memory cache of previously found package name mappings */
306bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov    private final HashMap<String, Long> mPackageCache = new HashMap<String, Long>();
307b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
308b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
309b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    /** Compiled statements for querying and inserting mappings */
310b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private SQLiteStatement mMimetypeQuery;
311b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private SQLiteStatement mPackageQuery;
3126bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov    private SQLiteStatement mAggregateIdQuery;
31361bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov    private SQLiteStatement mAggregateIdUpdate;
314b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private SQLiteStatement mMimetypeInsert;
315b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private SQLiteStatement mPackageInsert;
31661bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov    private SQLiteStatement mNameLookupInsert;
317b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
318b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private SQLiteStatement mDataMimetypeQuery;
319b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private SQLiteStatement mActivitiesMimetypeQuery;
320b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
321b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    private final Context mContext;
32235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private final SyncStateContentProviderHelper mSyncState;
323619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private final RestrictionExceptionsCache mCache;
324b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    private HashMap<String, String[]> mNicknameClusterCache;
325b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
326ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Compiled statements for updating {@link Aggregates#IN_VISIBLE_GROUP}. */
327ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private SQLiteStatement mVisibleAllUpdate;
328ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private SQLiteStatement mVisibleSpecificUpdate;
329ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
330619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
331b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private static OpenHelper sSingleton = null;
332b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
333b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public static synchronized OpenHelper getInstance(Context context) {
334b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        if (sSingleton == null) {
335b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            sSingleton = new OpenHelper(context);
336b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        }
337b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        return sSingleton;
338b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
339b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
3401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    /**
34131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov     * Private constructor, callers except unit tests should obtain an instance through
34235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana     * {@link #getInstance(android.content.Context)} instead.
3431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
34431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* package */ OpenHelper(Context context) {
345b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        super(context, DATABASE_NAME, null, DATABASE_VERSION);
346b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        Log.i(TAG, "Creating OpenHelper");
347619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
348b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        mContext = context;
349619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        mCache = new RestrictionExceptionsCache();
350619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        mCache.loadFromDatabase(context, getReadableDatabase());
35135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        mSyncState = new SyncStateContentProviderHelper();
35235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
353b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
354b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
355b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    @Override
356b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public void onOpen(SQLiteDatabase db) {
35735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        mSyncState.onDatabaseOpened(db);
35835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
359b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Create compiled statements for package and mimetype lookups
360ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns._ID + " FROM "
361ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Tables.MIMETYPES + " WHERE " + MimetypesColumns.MIMETYPE + "=?");
362ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mPackageQuery = db.compileStatement("SELECT " + PackagesColumns._ID + " FROM "
363ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Tables.PACKAGES + " WHERE " + PackagesColumns.PACKAGE + "=?");
3646bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov        mAggregateIdQuery = db.compileStatement("SELECT " + Contacts.AGGREGATE_ID + " FROM "
3656bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov                + Tables.CONTACTS + " WHERE " + Contacts._ID + "=?");
36661bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        mAggregateIdUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
36761bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov                + Contacts.AGGREGATE_ID + "=?" + " WHERE " + Contacts._ID + "=?");
368ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mMimetypeInsert = db.compileStatement("INSERT INTO " + Tables.MIMETYPES + "("
369ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + MimetypesColumns.MIMETYPE + ") VALUES (?)");
370ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mPackageInsert = db.compileStatement("INSERT INTO " + Tables.PACKAGES + "("
371ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + PackagesColumns.PACKAGE + ") VALUES (?)");
372ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
373ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mDataMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns.MIMETYPE + " FROM "
374ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Tables.DATA_JOIN_MIMETYPES + " WHERE " + Tables.DATA + "." + Data._ID + "=?");
375ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mActivitiesMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns.MIMETYPE
376ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + " FROM " + Tables.ACTIVITIES_JOIN_MIMETYPES + " WHERE " + Tables.ACTIVITIES + "."
377b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                + Activities._ID + "=?");
37861bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        mNameLookupInsert = db.compileStatement("INSERT INTO " + Tables.NAME_LOOKUP + "("
37961bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov                + NameLookupColumns.CONTACT_ID + "," + NameLookupColumns.NAME_TYPE + ","
38061bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov                + NameLookupColumns.NORMALIZED_NAME + ") VALUES (?,?,?)");
3811f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
382ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final String visibleUpdate = "UPDATE " + Tables.AGGREGATES + " SET "
383ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Aggregates.IN_VISIBLE_GROUP + "= (" + Clauses.IN_VISIBLE_GROUP + ")";
384ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
385ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mVisibleAllUpdate = db.compileStatement(visibleUpdate);
386ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mVisibleSpecificUpdate = db.compileStatement(visibleUpdate + " WHERE "
387ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + AggregatesColumns.CONCRETE_ID + "=?");
388ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
3891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Make sure we have an in-memory presence table
3901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String tableName = DATABASE_PRESENCE + "." + Tables.PRESENCE;
3911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String indexName = DATABASE_PRESENCE + ".presenceIndex";
3921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
3931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        db.execSQL("ATTACH DATABASE ':memory:' AS " + DATABASE_PRESENCE + ";");
3941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        db.execSQL("CREATE TABLE IF NOT EXISTS " + tableName + " ("+
3951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                BaseColumns._ID + " INTEGER PRIMARY KEY," +
3961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                Presence.AGGREGATE_ID + " INTEGER REFERENCES aggregates(_id)," +
3971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                Presence.DATA_ID + " INTEGER REFERENCES data(_id)," +
3981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                Presence.IM_PROTOCOL + " TEXT," +
3991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                Presence.IM_HANDLE + " TEXT," +
4001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                Presence.IM_ACCOUNT + " TEXT," +
4011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                Presence.PRESENCE_STATUS + " INTEGER," +
4021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                Presence.PRESENCE_CUSTOM_STATUS + " TEXT," +
4031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                "UNIQUE(" + Presence.IM_PROTOCOL + ", " + Presence.IM_HANDLE + ", " + Presence.IM_ACCOUNT + ")" +
4041f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        ");");
4051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
406fb241add950ad1314dff339805cb9eb2d6136f96Jeff Sharkey        db.execSQL("CREATE INDEX IF NOT EXISTS " + indexName + " ON " + Tables.PRESENCE + " ("
4071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                + Presence.AGGREGATE_ID + ");");
408b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
409b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
410b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    @Override
411b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public void onCreate(SQLiteDatabase db) {
412b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        Log.i(TAG, "Bootstrapping database");
413b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
41435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        mSyncState.createDatabase(db);
41535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
416b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // One row per group of contacts corresponding to the same person
417b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("CREATE TABLE " + Tables.AGGREGATES + " (" +
418b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
419b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Aggregates.DISPLAY_NAME + " TEXT," +
420b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Aggregates.TIMES_CONTACTED + " INTEGER," +
421b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Aggregates.LAST_TIME_CONTACTED + " INTEGER," +
42200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                Aggregates.STARRED + " INTEGER," +
423619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID + " INTEGER REFERENCES data(_id)," +
424619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID + " INTEGER REFERENCES package(_id)," +
425619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID + " INTEGER REFERENCES data(_id)," +
426619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID + " INTEGER REFERENCES data(_id)," +
427619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID + " INTEGER REFERENCES package(_id)," +
428619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID + " INTEGER REFERENCES data(_id)," +
429619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.SINGLE_RESTRICTED_PACKAGE_ID + " INTEGER REFERENCES package(_id)," +
430de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar                Aggregates.PHOTO_ID + " INTEGER REFERENCES data(_id)," +
431d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                Aggregates.CUSTOM_RINGTONE + " TEXT," +
43228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                Aggregates.SEND_TO_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0," +
43328f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                Aggregates.IN_VISIBLE_GROUP + " INTEGER NOT NULL DEFAULT 1" +
434b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        ");");
435b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
436b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Contacts table
437b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("CREATE TABLE " + Tables.CONTACTS + " (" +
438b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Contacts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
439619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                ContactsColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id) NOT NULL," +
440619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                Contacts.IS_RESTRICTED + " INTEGER NOT NULL DEFAULT 0," +
441035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_NAME + " STRING DEFAULT NULL, " +
442035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_TYPE + " STRING DEFAULT NULL, " +
4437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                Contacts.SOURCE_ID + " TEXT," +
4447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                Contacts.VERSION + " INTEGER NOT NULL DEFAULT 1," +
4457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                Contacts.DIRTY + " INTEGER NOT NULL DEFAULT 1," +
446a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                Contacts.AGGREGATE_ID + " INTEGER " +
447b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        ");");
448b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
449d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Contact options table. It has the same primary key as the corresponding contact.
450d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        db.execSQL("CREATE TABLE " + Tables.CONTACT_OPTIONS + " (" +
451d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                ContactOptionsColumns._ID + " INTEGER PRIMARY KEY," +
452d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                ContactOptionsColumns.CUSTOM_RINGTONE + " TEXT," +
453d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                ContactOptionsColumns.SEND_TO_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0" +
454d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov       ");");
455d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
456b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Package name mapping table
457ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        db.execSQL("CREATE TABLE " + Tables.PACKAGES + " (" +
458ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                PackagesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
459ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                PackagesColumns.PACKAGE + " TEXT NOT NULL" +
460b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        ");");
461b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
462ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Mimetype mapping table
463ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        db.execSQL("CREATE TABLE " + Tables.MIMETYPES + " (" +
464ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                MimetypesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
465ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                MimetypesColumns.MIMETYPE + " TEXT NOT NULL" +
466b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        ");");
467b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
468b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Public generic data table
469b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("CREATE TABLE " + Tables.DATA + " (" +
470b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Data._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
471b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                DataColumns.MIMETYPE_ID + " INTEGER REFERENCES mimetype(_id) NOT NULL," +
472b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Data.CONTACT_ID + " INTEGER NOT NULL," +
473f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.IS_PRIMARY + " INTEGER NOT NULL DEFAULT 0," +
474f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.IS_SUPER_PRIMARY + " INTEGER NOT NULL DEFAULT 0," +
475f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA_VERSION + " INTEGER NOT NULL DEFAULT 0," +
476f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA1 + " TEXT," +
477f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA2 + " TEXT," +
478f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA3 + " TEXT," +
479f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA4 + " TEXT," +
480f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA5 + " TEXT," +
481f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA6 + " TEXT," +
482f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA7 + " TEXT," +
483f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA8 + " TEXT," +
484f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA9 + " TEXT," +
485f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                Data.DATA10 + " TEXT" +
486b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        ");");
487b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
488f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        /**
489f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana         * set contact.dirty whenever the contact is updated and the new version does not explicity
490f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana         * clear the dirty flag
491f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana         *
492f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana         * Want to have a data row that has the server version of the contact. Then when I save
493f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana         * an entry from the server into the provider I will set the server version of the data
494f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana         * while also clearing the dirty flag of the contact.
495f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana         *
496f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana         * increment the contact.version whenever the contact is updated
497f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana         */
498f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        db.execSQL("CREATE TRIGGER " + Tables.CONTACTS + "_updated1 "
499f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "   BEFORE UPDATE ON " + Tables.CONTACTS
500f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + " BEGIN "
501f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "   UPDATE " + Tables.CONTACTS
502f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     SET "
503f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                +         Contacts.VERSION + "=OLD." + Contacts.VERSION + "+1, "
504f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                +         Contacts.DIRTY + "=1"
505f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     WHERE " + Contacts._ID + "=OLD." + Contacts._ID + ";"
506f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + " END");
507f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
508f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        db.execSQL("CREATE TRIGGER " + Tables.CONTACTS + "_deleted "
509f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "   BEFORE DELETE ON " + Tables.CONTACTS
510f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + " BEGIN "
511f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "   DELETE FROM " + Tables.DATA
512f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     WHERE " + Data.CONTACT_ID + "=OLD." + Contacts._ID + ";"
513f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "   DELETE FROM " + Tables.PHONE_LOOKUP
514f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     WHERE " + PhoneLookupColumns.CONTACT_ID + "=OLD." + Contacts._ID + ";"
515f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + " END");
516f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
517f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        db.execSQL("CREATE TRIGGER " + Tables.DATA + "_updated AFTER UPDATE ON " + Tables.DATA
518f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + " BEGIN "
519f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "   UPDATE " + Tables.DATA
520f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     SET " + Data.DATA_VERSION + "=OLD." + Data.DATA_VERSION + "+1 "
521f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     WHERE " + Data._ID + "=OLD." + Data._ID + ";"
522f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "   UPDATE " + Tables.CONTACTS
523f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     SET " + Contacts.DIRTY + "=1"
524f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     WHERE " + Contacts._ID + "=OLD." + Contacts._ID + ";"
525f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + " END");
526f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
527f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        db.execSQL("CREATE TRIGGER " + Tables.DATA + "_deleted BEFORE DELETE ON " + Tables.DATA
528f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + " BEGIN "
529f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "   UPDATE " + Tables.CONTACTS
530f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     SET " + Contacts.DIRTY + "=1"
531f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     WHERE " + Contacts._ID + "=OLD." + Contacts._ID + ";"
532f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "   DELETE FROM " + Tables.PHONE_LOOKUP
533f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + "     WHERE " + PhoneLookupColumns.DATA_ID + "=OLD." + Data._ID + ";"
534f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                + " END");
535f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
536b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Private phone numbers table used for lookup
537b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("CREATE TABLE " + Tables.PHONE_LOOKUP + " (" +
538b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                PhoneLookupColumns._ID + " INTEGER PRIMARY KEY," +
539b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                PhoneLookupColumns.DATA_ID + " INTEGER REFERENCES data(_id) NOT NULL," +
540b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                PhoneLookupColumns.CONTACT_ID + " INTEGER REFERENCES contacts(_id) NOT NULL," +
541b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                PhoneLookupColumns.NORMALIZED_NUMBER + " TEXT NOT NULL" +
542b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        ");");
543b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
544b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("CREATE INDEX phone_lookup_index ON " + Tables.PHONE_LOOKUP + " (" +
545b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                PhoneLookupColumns.NORMALIZED_NUMBER + " ASC, " +
546b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                PhoneLookupColumns.CONTACT_ID + ", " +
547b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                PhoneLookupColumns.DATA_ID +
548b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        ");");
549b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
550a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        // Private name/nickname table used for lookup
551a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("CREATE TABLE " + Tables.NAME_LOOKUP + " (" +
552a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                NameLookupColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
553a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                NameLookupColumns.CONTACT_ID + " INTEGER REFERENCES contacts(_id) NOT NULL," +
554619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                NameLookupColumns.NORMALIZED_NAME + " TEXT," +
555a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                NameLookupColumns.NAME_TYPE + " INTEGER" +
556a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ");");
557a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
558a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("CREATE INDEX name_lookup_index ON " + Tables.NAME_LOOKUP + " (" +
559a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                NameLookupColumns.NORMALIZED_NAME + " ASC, " +
560a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                NameLookupColumns.NAME_TYPE + " ASC, " +
56161bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov                NameLookupColumns.CONTACT_ID +
562a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ");");
563a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
564b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        db.execSQL("CREATE TABLE " + Tables.NICKNAME_LOOKUP + " (" +
565b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                NicknameLookupColumns.NAME + " TEXT," +
566b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                NicknameLookupColumns.CLUSTER + " TEXT" +
567b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        ");");
568b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
569b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        db.execSQL("CREATE UNIQUE INDEX nickname_lookup_index ON " + Tables.NICKNAME_LOOKUP + " (" +
570b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                NicknameLookupColumns.NAME + ", " +
571b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                NicknameLookupColumns.CLUSTER +
572b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        ");");
573b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
574ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups table
575ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        db.execSQL("CREATE TABLE " + Tables.GROUPS + " (" +
576ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Groups._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
577ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Groups.PACKAGE_ID + " INTEGER REFERENCES package(_id) NOT NULL," +
578035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Groups.ACCOUNT_NAME + " STRING DEFAULT NULL, " +
579035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Groups.ACCOUNT_TYPE + " STRING DEFAULT NULL, " +
580ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Groups.SOURCE_ID + " TEXT," +
581ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Groups.TITLE + " TEXT," +
582ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Groups.TITLE_RESOURCE + " INTEGER," +
583ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Groups.GROUP_VISIBLE + " INTEGER" +
584ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ");");
585ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
586b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.AGGREGATION_EXCEPTIONS + " (" +
587b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AggregationExceptionColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
588b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AggregationExceptions.TYPE + " INTEGER NOT NULL, " +
589127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                AggregationExceptionColumns.CONTACT_ID1 + " INTEGER REFERENCES contacts(_id), " +
590127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                AggregationExceptionColumns.CONTACT_ID2 + " INTEGER REFERENCES contacts(_id)" +
591b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov		");");
592b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
593b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS aggregation_exception_index1 ON " +
594b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                Tables.AGGREGATION_EXCEPTIONS + " (" +
595127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                AggregationExceptionColumns.CONTACT_ID1 + ", " +
596127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                AggregationExceptionColumns.CONTACT_ID2 +
597b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        ");");
598b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
599b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS aggregation_exception_index2 ON " +
600b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                Tables.AGGREGATION_EXCEPTIONS + " (" +
601127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                AggregationExceptionColumns.CONTACT_ID2 + ", " +
602127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                AggregationExceptionColumns.CONTACT_ID1 +
603b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        ");");
604b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
605619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Restriction exceptions table
606619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        db.execSQL("CREATE TABLE " + Tables.RESTRICTION_EXCEPTIONS + " (" +
607619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
608619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                RestrictionExceptions.PACKAGE_PROVIDER + " TEXT NOT NULL, " +
609619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                RestrictionExceptions.PACKAGE_CLIENT + " TEXT NOT NULL, " +
610619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                RestrictionExceptionsColumns.PACKAGE_PROVIDER_ID + " INTEGER NOT NULL, " +
611619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                RestrictionExceptionsColumns.PACKAGE_CLIENT_ID + " INTEGER NOT NULL" +
612619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        ");");
613619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
614b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Activities table
615b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("CREATE TABLE " + Tables.ACTIVITIES + " (" +
616b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Activities._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
617b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                ActivitiesColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id) NOT NULL," +
618b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                ActivitiesColumns.MIMETYPE_ID + " INTEGER REFERENCES mimetype(_id) NOT NULL," +
619b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Activities.RAW_ID + " TEXT," +
620499791a4f9ab29fa94ff48dd7acff55b2ac089a7Dmitri Plotnikov                Activities.IN_REPLY_TO + " TEXT," +
621b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Activities.AUTHOR_CONTACT_ID +  " INTEGER REFERENCES contacts(_id)," +
622b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Activities.TARGET_CONTACT_ID + " INTEGER REFERENCES contacts(_id)," +
623b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Activities.PUBLISHED + " INTEGER NOT NULL," +
624499791a4f9ab29fa94ff48dd7acff55b2ac089a7Dmitri Plotnikov                Activities.THREAD_PUBLISHED + " INTEGER NOT NULL," +
625b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Activities.TITLE + " TEXT NOT NULL," +
626b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Activities.SUMMARY + " TEXT," +
627adb55c2d8295d300961d86a3605c8ddc469cd4a2Dmitri Plotnikov                Activities.LINK + " TEXT, " +
628b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                Activities.THUMBNAIL + " BLOB" +
629b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        ");");
630b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
631b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        loadNicknameLookupTable(db);
632b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
633b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
634b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    @Override
635b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
6367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        Log.i(TAG, "Upgrading from version " + oldVersion + " to " + newVersion
637b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                + ", data will be lost!");
638b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
6397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        db.execSQL("DROP TABLE IF EXISTS " + Tables.ACCOUNTS + ";");
640b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("DROP TABLE IF EXISTS " + Tables.AGGREGATES + ";");
641b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("DROP TABLE IF EXISTS " + Tables.CONTACTS + ";");
642ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        db.execSQL("DROP TABLE IF EXISTS " + Tables.PACKAGES + ";");
643ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        db.execSQL("DROP TABLE IF EXISTS " + Tables.MIMETYPES + ";");
644b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("DROP TABLE IF EXISTS " + Tables.DATA + ";");
645b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("DROP TABLE IF EXISTS " + Tables.PHONE_LOOKUP + ";");
646a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("DROP TABLE IF EXISTS " + Tables.NAME_LOOKUP + ";");
647b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        db.execSQL("DROP TABLE IF EXISTS " + Tables.NICKNAME_LOOKUP + ";");
648ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        db.execSQL("DROP TABLE IF EXISTS " + Tables.GROUPS + ";");
649619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        db.execSQL("DROP TABLE IF EXISTS " + Tables.RESTRICTION_EXCEPTIONS + ";");
650b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        db.execSQL("DROP TABLE IF EXISTS " + Tables.ACTIVITIES + ";");
651b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
652d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // TODO: we should not be dropping agg_exceptions and contact_options. In case that table's
653d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // schema changes, we should try to preserve the data, because it was entered by the user
654d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // and has never been synched to the server.
655d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        db.execSQL("DROP TABLE IF EXISTS " + Tables.AGGREGATION_EXCEPTIONS + ";");
656d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        db.execSQL("DROP TABLE IF EXISTS " + Tables.CONTACT_OPTIONS + ";");
657b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
658b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        onCreate(db);
65935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
66035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        // TODO: eventually when this supports upgrades we should do something like the following:
66135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana//        if (!upgradeDatabase(db, oldVersion, newVersion)) {
66235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana//            mSyncState.discardSyncData(db, null /* all accounts */);
66335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana//            ContentResolver.requestSync(null /* all accounts */,
66435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana//                    mContentUri.getAuthority(), new Bundle());
66535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana//        }
666b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
667b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
668a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
669a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data except mime type and package lookup tables.
670a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
671a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public void wipeData() {
672a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        SQLiteDatabase db = getWritableDatabase();
673a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("DELETE FROM " + Tables.AGGREGATES + ";");
674a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("DELETE FROM " + Tables.CONTACTS + ";");
675d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        db.execSQL("DELETE FROM " + Tables.CONTACT_OPTIONS + ";");
676a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("DELETE FROM " + Tables.DATA + ";");
677a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("DELETE FROM " + Tables.PHONE_LOOKUP + ";");
678a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("DELETE FROM " + Tables.NAME_LOOKUP + ";");
679ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        db.execSQL("DELETE FROM " + Tables.GROUPS + ";");
680b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        db.execSQL("DELETE FROM " + Tables.AGGREGATION_EXCEPTIONS + ";");
681619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        db.execSQL("DELETE FROM " + Tables.RESTRICTION_EXCEPTIONS + ";");
682a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("DELETE FROM " + Tables.ACTIVITIES + ";");
683b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
684b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        // Note: we are not removing reference data from Tables.NICKNAME_LOOKUP
685b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
686a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        db.execSQL("VACUUM;");
687a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
688b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
689b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    /**
690619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Return the {@link ApplicationInfo#uid} for the given package name.
691619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
692619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    public static int getUidForPackageName(PackageManager pm, String packageName) {
693619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        try {
694619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            ApplicationInfo clientInfo = pm.getApplicationInfo(packageName, 0 /* no flags */);
695619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            return clientInfo.uid;
696619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } catch (NameNotFoundException e) {
697619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            throw new RuntimeException(e);
698619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
699619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
700619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
701619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
702b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * Perform an internal string-to-integer lookup using the compiled
703b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * {@link SQLiteStatement} provided, using the in-memory cache to speed up
704b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * lookups. If a mapping isn't found in cache or database, it will be
705b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * created. All new, uncached answers are added to the cache automatically.
706b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     *
707b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * @param query Compiled statement used to query for the mapping.
708b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * @param insert Compiled statement used to insert a new mapping when no
709b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     *            existing one is found in cache or from query.
710b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * @param value Value to find mapping for.
711b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * @param cache In-memory cache of previous answers.
712b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * @return An unique integer mapping for the given value.
713b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     */
714b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private synchronized long getCachedId(SQLiteStatement query, SQLiteStatement insert,
715b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            String value, HashMap<String, Long> cache) {
716b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Try an in-memory cache lookup
717b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        if (cache.containsKey(value)) {
718b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            return cache.get(value);
719b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        }
720b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
721b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        long id = -1;
722b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        try {
723b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            // Try searching database for mapping
724b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            DatabaseUtils.bindObjectToProgram(query, 1, value);
725b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            id = query.simpleQueryForLong();
726b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        } catch (SQLiteDoneException e) {
727b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            // Nothing found, so try inserting new mapping
728b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            DatabaseUtils.bindObjectToProgram(insert, 1, value);
729b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            id = insert.executeInsert();
730b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        }
731b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
732b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        if (id != -1) {
733b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            // Cache and return the new answer
734b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            cache.put(value, id);
735b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            return id;
736b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        } else {
737b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            // Otherwise throw if no mapping found or created
738b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            throw new IllegalStateException("Couldn't find or create internal "
739b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                    + "lookup table entry for value " + value);
740b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        }
741b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
742b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
743b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    /**
744ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Convert a package name into an integer, using {@link Tables#PACKAGES} for
745b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * lookups and possible allocation of new IDs as needed.
746b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     */
747b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public long getPackageId(String packageName) {
748b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Make sure compiled statements are ready by opening database
749b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        getReadableDatabase();
750b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        return getCachedId(mPackageQuery, mPackageInsert, packageName, mPackageCache);
751b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
752b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
753b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    /**
754ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Convert a mimetype into an integer, using {@link Tables#MIMETYPES} for
755b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * lookups and possible allocation of new IDs as needed.
756b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     */
757b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public long getMimeTypeId(String mimetype) {
758b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Make sure compiled statements are ready by opening database
759b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        getReadableDatabase();
760b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        return getCachedId(mMimetypeQuery, mMimetypeInsert, mimetype, mMimetypeCache);
761b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
762b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
763b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    /**
764ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Find the mimetype for the given {@link Data#_ID}.
765b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     */
766b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public String getDataMimeType(long dataId) {
767b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Make sure compiled statements are ready by opening database
768b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        getReadableDatabase();
769b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        try {
770b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            // Try database query to find mimetype
771b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            DatabaseUtils.bindObjectToProgram(mDataMimetypeQuery, 1, dataId);
772b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            String mimetype = mDataMimetypeQuery.simpleQueryForString();
773b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            return mimetype;
774b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        } catch (SQLiteDoneException e) {
775b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            // No valid mapping found, so return null
776b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            return null;
777b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        }
778b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
779b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey
780b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    /**
781b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     * Find the mime-type for the given {@link Activities#_ID}.
782b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey     */
783b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    public String getActivityMimeType(long activityId) {
784b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        // Make sure compiled statements are ready by opening database
785b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        getReadableDatabase();
786b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        try {
787b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            // Try database query to find mimetype
788b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            DatabaseUtils.bindObjectToProgram(mActivitiesMimetypeQuery, 1, activityId);
789b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            String mimetype = mActivitiesMimetypeQuery.simpleQueryForString();
790b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            return mimetype;
791b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        } catch (SQLiteDoneException e) {
792b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            // No valid mapping found, so return null
793b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            return null;
794b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        }
795b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    }
7966bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov
7976bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov    /**
798ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Update {@link Aggregates#IN_VISIBLE_GROUP} for all aggregates.
799ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
800ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    public void updateAllVisible() {
801ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long groupMembershipMimetypeId = getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
802ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mVisibleAllUpdate.bindLong(1, groupMembershipMimetypeId);
803ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mVisibleAllUpdate.execute();
804ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
805ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
806ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
807ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Update {@link Aggregates#IN_VISIBLE_GROUP} for a specific aggregate.
808ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
809ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    public void updateAggregateVisible(long aggId) {
810ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long groupMembershipMimetypeId = getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
811ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mVisibleSpecificUpdate.bindLong(1, groupMembershipMimetypeId);
812ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mVisibleSpecificUpdate.bindLong(2, aggId);
813ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        mVisibleSpecificUpdate.execute();
814ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
815ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
816ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
81761bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov     * Updates the aggregate ID for the specified contact.
81861bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov     */
81961bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov    public void setAggregateId(long contactId, long aggregateId) {
82061bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        getWritableDatabase();
82161bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mAggregateIdUpdate, 1, aggregateId);
82261bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mAggregateIdUpdate, 2, contactId);
82361bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        mAggregateIdUpdate.execute();
82461bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov    }
82561bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov
82661bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov    /**
8276bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov     * Returns aggregate ID for the given contact or zero if it is NULL.
8286bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov     */
8296bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov    public long getAggregateId(long contactId) {
8306bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov        getReadableDatabase();
8316bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov        try {
8326bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov            DatabaseUtils.bindObjectToProgram(mAggregateIdQuery, 1, contactId);
8336bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov            return mAggregateIdQuery.simpleQueryForLong();
8346bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov        } catch (SQLiteDoneException e) {
8356bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov            // No valid mapping found, so return -1
8366bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov            return 0;
8376bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov        }
8386bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov    }
83961bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov
84061bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov    /**
84161bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov     * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
84261bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov     */
84361bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov    public void insertNameLookup(long contactId, int lookupType, String name) {
84461bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        getWritableDatabase();
84561bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, contactId);
84661bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, lookupType);
84761bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, name);
84861bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov        mNameLookupInsert.executeInsert();
84961bbb2287e8102b7e03922c03809c34b7b317d1cDmitri Plotnikov    }
850619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
851bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov    public static void buildPhoneLookupQuery(SQLiteQueryBuilder qb, final String number) {
852bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        final String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number);
853bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        final StringBuilder tables = new StringBuilder();
854bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        tables.append("contacts, (SELECT data_id FROM phone_lookup "
855bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov                + "WHERE (phone_lookup.normalized_number GLOB '");
856bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        tables.append(normalizedNumber);
857ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        tables.append("*')) AS lookup, " + Tables.DATA_JOIN_MIMETYPES);
858bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        qb.setTables(tables.toString());
859bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        qb.appendWhere("lookup.data_id=data._id AND data.contact_id=contacts._id AND ");
860bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        qb.appendWhere("PHONE_NUMBERS_EQUAL(data." + Phone.NUMBER + ", ");
861bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        qb.appendWhereEscapeString(number);
862bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        qb.appendWhere(")");
863bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov    }
864bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov
865bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov
866619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
867b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     * Loads common nickname mappings into the database.
868b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     */
869b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    private void loadNicknameLookupTable(SQLiteDatabase db) {
87028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        String[] strings = mContext.getResources().getStringArray(
87128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                com.android.internal.R.array.common_nicknames);
872b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        if (strings == null || strings.length == 0) {
873b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            return;
874b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        }
875b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
876b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        SQLiteStatement nicknameLookupInsert = db.compileStatement("INSERT INTO "
877b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                + Tables.NICKNAME_LOOKUP + "(" + NicknameLookupColumns.NAME + ","
878b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                + NicknameLookupColumns.CLUSTER + ") VALUES (?,?)");
879b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
880b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        for (int clusterId = 0; clusterId < strings.length; clusterId++) {
881b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            String[] names = strings[clusterId].split(",");
882b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            for (int j = 0; j < names.length; j++) {
883b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                String name = NameNormalizer.normalize(names[j]);
884b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                try {
885b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(nicknameLookupInsert, 1, name);
886b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                    DatabaseUtils.bindObjectToProgram(nicknameLookupInsert, 2,
887b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                            String.valueOf(clusterId));
888b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                    nicknameLookupInsert.executeInsert();
889b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                } catch (SQLiteException e) {
890b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
891b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                    // Print the exception and keep going - this is not a fatal error
892b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                    Log.e(TAG, "Cannot insert nickname: " + names[j], e);
893b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                }
894b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            }
895b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        }
896b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    }
897b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
898b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    /**
899b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     * Returns common nickname cluster IDs for a given name. For example, it
900b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     * will return the same value for "Robert", "Bob" and "Rob". Some names belong to multiple
901b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     * clusters, e.g. Leo could be Leonard or Leopold.
902b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     *
903b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     * May return null.
904b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     *
905b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     * @param normalizedName A normalized first name, see {@link NameNormalizer#normalize}.
906b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov     */
907b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    public String[] getCommonNicknameClusters(String normalizedName) {
908b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        if (mNicknameClusterCache == null) {
909b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            mNicknameClusterCache = new HashMap<String, String[]>();
910b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        }
911b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
912b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        synchronized (mNicknameClusterCache) {
913b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            if (mNicknameClusterCache.containsKey(normalizedName)) {
914b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                return mNicknameClusterCache.get(normalizedName);
915b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            }
916b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        }
917b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
918b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        String[] clusters = null;
919b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        SQLiteDatabase db = getReadableDatabase();
920b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
921b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        Cursor cursor = db.query(Tables.NICKNAME_LOOKUP, NICKNAME_LOOKUP_COLUMNS,
922b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
923b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                null, null, null);
924b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        try {
925b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            int count = cursor.getCount();
926b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            if (count > 0) {
927b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                clusters = new String[count];
928b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                for (int i = 0; i < count; i++) {
929b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                    cursor.moveToNext();
930b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                    clusters[i] = cursor.getString(COL_NICKNAME_LOOKUP_CLUSTER);
931b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov                }
932b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            }
933b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        } finally {
934b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            cursor.close();
935b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        }
936b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
937b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        synchronized (mNicknameClusterCache) {
938b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov            mNicknameClusterCache.put(normalizedName, clusters);
939b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        }
940b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
941b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov        return clusters;
942b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    }
943b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov
944b597adb79f1f57a24be2be09e3f45fa0f04f6f8fDmitri Plotnikov    /**
945619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Add a {@link RestrictionExceptions} record. This will update the
946619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * in-memory lookup table, and write to the database when needed. Any
947619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * callers should enforce that the {@link Binder#getCallingUid()} has the
948619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * authority to grant exceptions.
949619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
950619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    public void addRestrictionException(Context context, ContentValues values) {
951619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final PackageManager pm = context.getPackageManager();
952619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final SQLiteDatabase db = this.getWritableDatabase();
953619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
954619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Read incoming package values and find lookup values
955619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String packageProvider = values.getAsString(RestrictionExceptions.PACKAGE_PROVIDER);
956619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String packageClient = values.getAsString(RestrictionExceptions.PACKAGE_CLIENT);
957619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final long packageProviderId = getPackageId(packageProvider);
958619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final long packageClientId = getPackageId(packageClient);
959619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
960619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Find the client UID to update our internal lookup table and write the
961619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // exception to our database if we changed the in-memory cache.
962619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = getUidForPackageName(pm, packageClient);
963619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean cacheChanged = mCache.addException(packageProviderId, clientUid);
964619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (cacheChanged) {
965619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(RestrictionExceptionsColumns.PACKAGE_PROVIDER_ID, packageProviderId);
966619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(RestrictionExceptionsColumns.PACKAGE_CLIENT_ID, packageClientId);
967619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.remove(RestrictionExceptions.ALLOW_ACCESS);
968619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            db.insert(Tables.RESTRICTION_EXCEPTIONS, null, values);
969619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
970619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
971619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
972619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
973619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
974619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Remove a {@link RestrictionExceptions} record. This will update the
975619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * in-memory lookup table, and write to the database when needed. Any
976619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * callers should enforce that the {@link Binder#getCallingUid()} has the
977619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * authority to revoke exceptions.
978619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
979619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    public void removeRestrictionException(Context context, ContentValues values) {
980619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final PackageManager pm = context.getPackageManager();
981619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final SQLiteDatabase db = this.getWritableDatabase();
982619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
983619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Read incoming package values and find lookup values
984619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String packageProvider = values.getAsString(RestrictionExceptions.PACKAGE_PROVIDER);
985619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String packageClient = values.getAsString(RestrictionExceptions.PACKAGE_CLIENT);
986619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final long packageProviderId = getPackageId(packageProvider);
987619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final long packageClientId = getPackageId(packageClient);
988619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
989619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Find the client UID to update our internal lookup table and remove
990619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // the exception from our database if we changed the in-memory cache.
991619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = getUidForPackageName(pm, packageClient);
992619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final boolean cacheChanged = mCache.removeException(packageProviderId, clientUid);
993619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (cacheChanged) {
994619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            db.delete(Tables.RESTRICTION_EXCEPTIONS,
995619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    RestrictionExceptionsColumns.PACKAGE_PROVIDER_ID + "=" + packageProviderId
996619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                            + " AND " + RestrictionExceptionsColumns.PACKAGE_CLIENT_ID + "="
997619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                            + packageClientId, null);
998619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
999619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1000619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1001619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1002619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1003619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Return the exception clause that should be used when running {@link Data}
1004619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * queries that may be impacted by {@link Contacts#IS_RESTRICTED}. Will
1005619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * return a clause of all of the provider packages that have granted
1006619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * exceptions to the requested client UID.
1007619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1008619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    public String getRestrictionExceptionClause(int clientUid, String column) {
1009619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return mCache.getExceptionQueryClause(clientUid, column);
1010619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1011619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1012619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1013619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Utility class to build a selection query clause that matches a specific
1014619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * column against any one of the contained values. You must provide any
1015619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * escaping of the field values yourself.
1016619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1017619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private static class MatchesClause<T> extends LinkedList<T> {
1018bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        private final HashMap<String, String> mCache = new HashMap<String, String>();
1019619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1020619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        private static final String JOIN_OR = " OR ";
1021619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1022619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public synchronized boolean addMatch(T object) {
1023619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            mCache.clear();
1024619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            return super.add(object);
1025619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1026619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1027619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public synchronized void removeMatch(T object) {
1028619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            mCache.clear();
1029619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            super.remove(object);
1030619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1031619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1032619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        /**
1033619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * Return the query clause that would match the given column string to
1034619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * any values added through {@link #addMatch(Object)}.
1035619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         */
1036619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public synchronized String getQueryClause(String column, StringBuilder recycle) {
1037619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            // We maintain an internal cache for each requested column, and only
1038619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            // build the actual value when needed.
1039619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            String queryClause = mCache.get(column);
1040619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            final int size = this.size();
1041619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (queryClause == null && size > 0) {
1042619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                recycle.setLength(0);
1043619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                for (int i = 0; i < size; i++) {
1044619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    recycle.append(column);
1045619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    recycle.append("=");
1046619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    recycle.append(this.get(i));
1047619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    recycle.append(JOIN_OR);
1048619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1049619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1050619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Trim off the last "OR" clause and store cached value.
1051619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final int length = recycle.length();
1052619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                recycle.delete(length - JOIN_OR.length(), length);
1053619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                queryClause = recycle.toString();
1054619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                mCache.put(column, queryClause);
1055619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1056619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            return queryClause;
1057619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1058619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1059619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1060619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1061619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Optimized in-memory cache for storing {@link RestrictionExceptions} that
1062619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * have been read up from database. Helper methods indicate when an
1063619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * exception change require writing to disk, and build query clauses for a
1064619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * specific {@link RestrictionExceptions#PACKAGE_CLIENT}.
1065619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1066619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private static class RestrictionExceptionsCache extends HashMap<Integer, MatchesClause<Long>> {
1067bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov        private final StringBuilder mBuilder = new StringBuilder();
1068619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1069619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        private static final String[] PROJ_RESTRICTION_EXCEPTIONS = new String[] {
1070619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                RestrictionExceptionsColumns.PACKAGE_PROVIDER_ID,
1071619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                RestrictionExceptions.PACKAGE_CLIENT,
1072619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        };
1073619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1074619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        private static final int COL_PACKAGE_PROVIDER_ID = 0;
1075619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        private static final int COL_PACKAGE_CLIENT = 1;
1076619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1077619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public void loadFromDatabase(Context context, SQLiteDatabase db) {
1078619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            final PackageManager pm = context.getPackageManager();
1079619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1080619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            // Load all existing exceptions from our database.
1081619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            Cursor cursor = null;
1082619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            try {
1083619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                cursor = db.query(Tables.RESTRICTION_EXCEPTIONS, PROJ_RESTRICTION_EXCEPTIONS, null,
1084619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        null, null, null, null);
1085619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                while (cursor.moveToNext()) {
1086619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    // Read provider and client package details from database
1087619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    final long packageProviderId = cursor.getLong(COL_PACKAGE_PROVIDER_ID);
1088619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    final String clientPackage = cursor.getString(COL_PACKAGE_CLIENT);
1089619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1090619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    try {
1091619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        // Create exception entry for this client
1092619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        final int clientUid = getUidForPackageName(pm, clientPackage);
1093619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        addException(packageProviderId, clientUid);
1094619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    } catch (RuntimeException e) {
1095619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        Log.w(TAG, "Failed to grant restriction exception to " + clientPackage);
1096619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        continue;
1097619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    }
1098619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1099619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1100619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            } finally {
1101619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (cursor != null) {
1102619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    cursor.close();
1103619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1104619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1105619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1106619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1107619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        /**
1108619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * Lazily fetch a {@link MatchesClause} instance, creating a new one if
1109619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * both needed and requested.
1110619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         */
1111619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        private MatchesClause<Long> getLazy(int clientUid, boolean create) {
1112619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            MatchesClause<Long> matchesClause = get(clientUid);
1113619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (matchesClause == null && create) {
1114619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                matchesClause = new MatchesClause<Long>();
1115619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                put(clientUid, matchesClause);
1116619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1117619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            return matchesClause;
1118619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1119619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1120619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        /**
1121619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * Build a query clause that will allow the restriction exceptions
1122619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * granted to a specific {@link Binder#getCallingUid()}.
1123619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         */
1124619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public String getExceptionQueryClause(int clientUid, String column) {
1125619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            MatchesClause<Long> matchesClause = getLazy(clientUid, false);
1126619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (matchesClause != null) {
1127619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                return matchesClause.getQueryClause(column, mBuilder);
1128619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            } else {
1129ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // When no matching clause found, return 0 to provide a false
1130ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // value for the query string.
1131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return "0";
1132619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1133619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1134619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1135619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        /**
1136619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * Add a {@link RestrictionExceptions} into the cache. Returns true if
1137619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * this action resulted in the cache being changed.
1138619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         */
1139619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public boolean addException(long packageProviderId, int clientUid) {
1140619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            MatchesClause<Long> matchesClause = getLazy(clientUid, true);
1141619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (matchesClause.contains(packageProviderId)) {
1142619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                return false;
1143619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            } else {
1144619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                matchesClause.addMatch(packageProviderId);
1145619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                return true;
1146619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1147619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1148619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1149619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        /**
1150619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * Remove a {@link RestrictionExceptions} from the cache. Returns true if
1151619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         * this action resulted in the cache being changed.
1152619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey         */
1153619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        public boolean removeException(long packageProviderId, int clientUid) {
1154619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            MatchesClause<Long> matchesClause = getLazy(clientUid, false);
1155619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (matchesClause == null || !matchesClause.contains(packageProviderId)) {
1156619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                return false;
1157619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            } else {
1158619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                matchesClause.removeMatch(packageProviderId);
1159619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                return true;
1160619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1161619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1162619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1163619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
116435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    public SyncStateContentProviderHelper getSyncState() {
116535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        return mSyncState;
116635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    }
1167b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey}
1168