ContactsProvider2.java revision 35ed95769096bb5dd406ad7d1fcaa49a0e35a307
14f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/*
24f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Copyright (C) 2009 The Android Open Source Project
34f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
44f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License");
54f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * you may not use this file except in compliance with the License.
64f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * You may obtain a copy of the License at
74f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
84f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *      http://www.apache.org/licenses/LICENSE-2.0
94f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton *
104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Unless required by applicable law or agreed to in writing, software
114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS,
124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * See the License for the specific language governing permissions and
144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * limitations under the License
154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarpackage com.android.providers.contacts;
1828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar
1928f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.AggregatesColumns;
2028f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.AggregationExceptionColumns;
2128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.Clauses;
2228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.ContactsColumns;
2328f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.ContactOptionsColumns;
2428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.DataColumns;
2528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.GroupsColumns;
2628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.MimetypesColumns;
2728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.PhoneLookupColumns;
2828f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.Tables;
2935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport com.android.internal.content.SyncStateContentProviderHelper;
304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
31b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
3235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.pm.PackageManager;
334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.content.ContentProvider;
3435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.UriMatcher;
35b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.content.Context;
3635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentValues;
3735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport android.content.EntityIterator;
3935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.Entity;
4035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentProviderResult;
417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport android.content.OperationApplicationException;
4235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentProviderOperation;
434f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
44ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
45b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
48c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
50619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.os.Binder;
51b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
52508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
53de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract;
546bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikovimport android.provider.Contacts.ContactMethods;
55de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Aggregates;
56b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions;
57de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds;
58de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Contacts;
59de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.Data;
60ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.Groups;
611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkeyimport android.provider.ContactsContract.Presence;
62619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.provider.ContactsContract.RestrictionExceptions;
636bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikovimport android.provider.ContactsContract.Aggregates.AggregationSuggestions;
64ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
65ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
66de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Phone;
67ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millarimport android.provider.ContactsContract.CommonDataKinds.Postal;
684097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
69a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.telephony.PhoneNumberUtils;
70a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamiltonimport android.text.TextUtils;
7100d71133c63c882fb41729ddc3a52f66fb155374Evan Millarimport android.util.Log;
724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport java.util.ArrayList;
74b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport java.util.HashMap;
754f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton/**
774f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * Contacts content provider. The contract between this provider and applications
784f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton * is defined in {@link ContactsContract}.
794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton */
80035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintanapublic class ContactsProvider2 extends ContentProvider {
81b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    // TODO: clean up debug tag and rename this class
82b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey    private static final String TAG = "ContactsProvider ~~~~";
834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
84619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: define broadcastreceiver to catch app uninstalls that should clear exceptions
85619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: carefully prevent all incoming nested queries; they can be gaping security holes
86619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    // TODO: check for restricted flag during insert(), update(), and delete() calls
87619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
88a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
90d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_ORDER_BY = Aggregates.STARRED + " DESC, "
91d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            + Aggregates.TIMES_CONTACTED + " DESC, "
92d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            + Aggregates.DISPLAY_NAME + " ASC";
93d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final String STREQUENT_LIMIT =
94d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            "(SELECT COUNT(1) FROM " + Tables.AGGREGATES + " WHERE "
95d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            + Aggregates.STARRED + "=1) + 25";
96d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int AGGREGATES = 1000;
986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int AGGREGATES_ID = 1001;
996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int AGGREGATES_DATA = 1002;
1001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int AGGREGATES_SUMMARY = 1003;
1011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int AGGREGATES_SUMMARY_ID = 1004;
102ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int AGGREGATES_SUMMARY_FILTER = 1005;
103d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final int AGGREGATES_SUMMARY_STREQUENT = 1006;
104d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar    private static final int AGGREGATES_SUMMARY_STREQUENT_FILTER = 1007;
1054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1066bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS = 2002;
1076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS_ID = 2003;
1086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS_DATA = 2004;
10928ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey    private static final int CONTACTS_FILTER_EMAIL = 2005;
1104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1116bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
113ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
114ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES_FILTER = 3003;
115ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int POSTALS = 3004;
116a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1176bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
119b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
120b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
121b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE = 7000;
1231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE_ID = 7001;
1241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
12531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
12631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
127619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private static final int RESTRICTION_EXCEPTIONS = 9000;
128619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
129ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
130ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
132ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
13335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
13435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
135ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private interface Projections {
136ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String[] PROJ_CONTACTS = new String[] {
137ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            ContactsColumns.CONCRETE_ID,
138ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
139ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
140ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String[] PROJ_DATA_CONTACTS = new String[] {
141ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                ContactsColumns.CONCRETE_ID,
142ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                DataColumns.CONCRETE_ID,
143ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Contacts.AGGREGATE_ID,
144ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                ContactsColumns.PACKAGE_ID,
145ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Contacts.IS_RESTRICTED,
146ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Data.MIMETYPE,
147ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
148ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
149ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_CONTACT_ID = 0;
150ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_DATA_ID = 1;
151ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_AGGREGATE_ID = 2;
152ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_PACKAGE_ID = 3;
153ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_IS_RESTRICTED = 4;
154ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_MIMETYPE = 5;
155ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
156ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String[] PROJ_DATA_AGGREGATES = new String[] {
157ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            ContactsColumns.CONCRETE_ID,
158ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                DataColumns.CONCRETE_ID,
159ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.CONCRETE_ID,
160ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                MimetypesColumns.CONCRETE_ID,
161ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Phone.NUMBER,
162ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Email.DATA,
163ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID,
164ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
165ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID,
166ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
167ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
168ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
169ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_MIMETYPE_ID = 3;
170ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_PHONE_NUMBER = 4;
171ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_EMAIL_DATA = 5;
172ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_OPTIMAL_PHONE_ID = 6;
173ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_FALLBACK_PHONE_ID = 7;
174ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_OPTIMAL_EMAIL_ID = 8;
175ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_FALLBACK_EMAIL_ID = 9;
17680c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
177ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
17931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
18031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
18131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1826bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /** Contains just the contacts columns */
1836bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final HashMap<String, String> sAggregatesProjectionMap;
18400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar    /** Contains the aggregate columns along with primary phone */
1851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final HashMap<String, String> sAggregatesSummaryProjectionMap;
186de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    /** Contains the data, contacts, and aggregate columns, for joined tables. */
187de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    private static final HashMap<String, String> sDataContactsAggregateProjectionMap;
188a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the contacts columns */
1894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
190a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the data columns */
1914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sDataProjectionMap;
192a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains the data and contacts columns, for joined tables */
193a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final HashMap<String, String> sDataContactsProjectionMap;
194ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
195ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
196ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
197ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
198b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    /** Contains the just the agg_exceptions columns */
199b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
200619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /** Contains the just the {@link RestrictionExceptions} columns */
201619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private static final HashMap<String, String> sRestrictionExceptionsProjectionMap;
2027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
203c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the contact id associated with a data record. */
204c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdSelect;
205c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the mimetype id associated with a data record. */
206c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedMimetypeSelect;
207c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the aggregate id associated with a contact record. */
208c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedAggregateIdSelect;
209c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns a list of contact ids associated with an aggregate record. */
210c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdListSelect;
211c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
212c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "primary" is selected.*/
213c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetPrimaryWhere;
214c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
215c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "super primary" is selected.*/
216c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetSuperPrimaryWhere;
217c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
218c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
219c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precomipled sql statement for setting a data record to the super primary. */
220c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
221a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final String GTALK_PROTOCOL_STRING = ContactMethods
2231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            .encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
2241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
2264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
227a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
2286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates", AGGREGATES);
2296bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#", AGGREGATES_ID);
2306bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/data", AGGREGATES_DATA);
2311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary", AGGREGATES_SUMMARY);
2321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/#", AGGREGATES_SUMMARY_ID);
233ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/filter/*",
234ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                AGGREGATES_SUMMARY_FILTER);
235d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/",
236d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT);
237d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/filter/*",
238d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT_FILTER);
23931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/suggestions",
24031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
2414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
2424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
243a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
244b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter_email/*",
245b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                CONTACTS_FILTER_EMAIL);
246b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
2484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
249ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
250ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
251ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
2521f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
253ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
254ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
255ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
256ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
25735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
25835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
259a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
260b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
261b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
262b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
263b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
2644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
265bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence", PRESENCE);
266bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence/#", PRESENCE_ID);
2671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
268619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "restriction_exceptions", RESTRICTION_EXCEPTIONS);
269619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
270fec4e13316f2731d84394e5fa2f93af3febdc20cEvan Millar        HashMap<String, String> columns;
2714f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2726bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        // Aggregates projection map
2736bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns = new HashMap<String, String>();
27400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(Aggregates._ID, "aggregates._id AS _id");
2756bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Aggregates.DISPLAY_NAME, Aggregates.DISPLAY_NAME);
2766bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Aggregates.LAST_TIME_CONTACTED, Aggregates.LAST_TIME_CONTACTED);
277d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        columns.put(Aggregates.TIMES_CONTACTED, Aggregates.TIMES_CONTACTED);
2786bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Aggregates.STARRED, Aggregates.STARRED);
279ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Aggregates.IN_VISIBLE_GROUP, Aggregates.IN_VISIBLE_GROUP);
280de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.put(Aggregates.PRIMARY_PHONE_ID, Aggregates.PRIMARY_PHONE_ID);
281c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Aggregates.PRIMARY_EMAIL_ID, Aggregates.PRIMARY_EMAIL_ID);
282d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        columns.put(Aggregates.CUSTOM_RINGTONE, Aggregates.CUSTOM_RINGTONE);
283d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        columns.put(Aggregates.SEND_TO_VOICEMAIL, Aggregates.SEND_TO_VOICEMAIL);
284619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
285619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID);
286619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
287619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID);
2886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        sAggregatesProjectionMap = columns;
2896bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
2901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Aggregates primaries projection map. The overall presence status is
2911f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // the most-present value, as indicated by the largest value.
2921f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns = new HashMap<String, String>();
2931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns.putAll(sAggregatesProjectionMap);
29400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.TYPE, CommonDataKinds.Phone.TYPE);
29500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.LABEL, CommonDataKinds.Phone.LABEL);
29600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.NUMBER, CommonDataKinds.Phone.NUMBER);
297bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        columns.put(Presence.PRESENCE_STATUS, "MAX(" + Presence.PRESENCE_STATUS + ")");
2981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        sAggregatesSummaryProjectionMap = columns;
29900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts projection map
3014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
3024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Contacts._ID, "contacts._id AS _id");
303619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(Contacts.PACKAGE, Contacts.PACKAGE);
3046bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Contacts.AGGREGATE_ID, Contacts.AGGREGATE_ID);
305035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Contacts.ACCOUNT_NAME, Contacts.ACCOUNT_NAME);
306035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Contacts.ACCOUNT_TYPE, Contacts.ACCOUNT_TYPE);
3077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Contacts.SOURCE_ID, Contacts.SOURCE_ID);
3087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Contacts.VERSION, Contacts.VERSION);
3097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Contacts.DIRTY, Contacts.DIRTY);
3104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        sContactsProjectionMap = columns;
3114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
3124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Data projection map
3134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
3144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data._ID, "data._id AS _id");
3154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data.CONTACT_ID, Data.CONTACT_ID);
316508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        columns.put(Data.MIMETYPE, Data.MIMETYPE);
317c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
318c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
319f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
3207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA1, "data.data1 as data1");
3217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA2, "data.data2 as data2");
3227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA3, "data.data3 as data3");
3237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA4, "data.data4 as data4");
3247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA5, "data.data5 as data5");
3257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA6, "data.data6 as data6");
3267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA7, "data.data7 as data7");
3277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA8, "data.data8 as data8");
3287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA9, "data.data9 as data9");
3297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA10, "data.data10 as data10");
330d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        // Mappings used for backwards compatibility.
331d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        columns.put("number", Phone.NUMBER);
3324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        sDataProjectionMap = columns;
333a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
334a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        // Data and contacts projection map for joins. _id comes from the data table
335a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns = new HashMap<String, String>();
336a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns.putAll(sContactsProjectionMap);
337a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns.putAll(sDataProjectionMap); // _id will be replaced with the one from data
338ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
339a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        sDataContactsProjectionMap = columns;
3407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
341de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        // Data and contacts projection map for joins. _id comes from the data table
342de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns = new HashMap<String, String>();
343de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.putAll(sAggregatesProjectionMap);
3447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.putAll(sContactsProjectionMap); //
345de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.putAll(sDataProjectionMap); // _id will be replaced with the one from data
346ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
347de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        sDataContactsAggregateProjectionMap = columns;
348c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
349ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
350ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
351ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups._ID, "groups._id AS _id");
352035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
353035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
354ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.PACKAGE, Groups.PACKAGE);
355ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.PACKAGE_ID, GroupsColumns.CONCRETE_PACKAGE_ID);
356ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
357ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE_RESOURCE, Groups.TITLE_RESOURCE);
358ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
359ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
360ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
361ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Contacts and groups projection map
362ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
363ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
364ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
365ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + AggregatesColumns.CONCRETE_ID
366ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
367ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
368ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
369ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
370ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
371ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + AggregatesColumns.CONCRETE_ID + ") FROM "
372ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
373ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
374ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + " AND " + Clauses.HAS_PRIMARY_PHONE + ") AS " + Groups.SUMMARY_WITH_PHONES);
375ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
376ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
377ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
378b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
379b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
380b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
381b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
382127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.AGGREGATE_ID,
383127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                "contacts1." + Contacts.AGGREGATE_ID + " AS " + AggregationExceptions.AGGREGATE_ID);
384127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.CONTACT_ID, AggregationExceptionColumns.CONTACT_ID2);
385b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
386b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
387619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Restriction exception projection map
388619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns = new HashMap<String, String>();
389619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(RestrictionExceptions.PACKAGE_PROVIDER, RestrictionExceptions.PACKAGE_PROVIDER);
390619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(RestrictionExceptions.PACKAGE_CLIENT, RestrictionExceptions.PACKAGE_CLIENT);
391619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(RestrictionExceptions.ALLOW_ACCESS, "1"); // Access granted if row returned
392619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        sRestrictionExceptionsProjectionMap = columns;
393619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
394c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdSelect = "SELECT " + Data.CONTACT_ID + " FROM " + Tables.DATA + " WHERE "
395c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + Data._ID + "=?";
396c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedMimetypeSelect = "SELECT " + DataColumns.MIMETYPE_ID + " FROM " + Tables.DATA
397c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Data._ID + "=?";
398c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedAggregateIdSelect = "SELECT " + Contacts.AGGREGATE_ID + " FROM " + Tables.CONTACTS
399c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts._ID + "=(" + sNestedContactIdSelect + ")";
400c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdListSelect = "SELECT " + Contacts._ID + " FROM " + Tables.CONTACTS
401c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts.AGGREGATE_ID + "=(" + sNestedAggregateIdSelect + ")";
402c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sSetPrimaryWhere = Data.CONTACT_ID + "=(" + sNestedContactIdSelect + ") AND "
403c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
404c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sSetSuperPrimaryWhere  = Data.CONTACT_ID + " IN (" + sNestedContactIdListSelect + ") AND "
405c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
4064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
4074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
40853056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
4094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private OpenHelper mOpenHelper;
41031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
411a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    private ContactAggregator mContactAggregator;
4124097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
413a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
414a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
41553056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
416a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
417a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
418a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
419a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
420a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
42153056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
42253056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
423a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
4244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
4254f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
4264f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
427b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        final Context context = getContext();
42835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
42931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        mOpenHelper = getOpenHelper(context);
4301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
4314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
43253056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mContactAggregator = new ContactAggregator(context, mOpenHelper, mAggregationScheduler);
433a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
434c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
435c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_PRIMARY
436c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetPrimaryWhere);
437c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
438c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_SUPER_PRIMARY
439c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetSuperPrimaryWhere);
440a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
44128f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
44228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
44328f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
44428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
44528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_conjunctions));
4464097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
4471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
4484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
4494f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
45031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
45131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    protected OpenHelper getOpenHelper(final Context context) {
45231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        return OpenHelper.getInstance(context);
45331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
45431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
455a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
456a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
457a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
458a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
459a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
460a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
461a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
462a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
463a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
464a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
465a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
466a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
467a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
468a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mOpenHelper.wipeData();
469a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
470a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
471a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
472a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Called when a change has been made.
473a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
474a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param uri the uri that the change was made to
475a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
476a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private void onChange(Uri uri) {
477a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null);
478a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
479a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
4804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
4814f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean isTemporary() {
4824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return false;
4834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
4844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
4854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
4864f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Uri insert(Uri uri, ContentValues values) {
487a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
488a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
48935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
490a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
49135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
49235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                id = mOpenHelper.getSyncState().insert(mOpenHelper.getWritableDatabase(), values);
49335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
49435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
4956bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
4966bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                id = insertAggregate(values);
4976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
4986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
4996bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
500a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS: {
501f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
502f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                id = insertContact(values, account);
503a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
504a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
505a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
506a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
507a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                values.put(Data.CONTACT_ID, uri.getPathSegments().get(1));
508035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
509a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
510a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
511a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
512a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
513035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
514a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
515a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
516a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
517ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
518ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
519ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                id = insertGroup(values, account);
520ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
521ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
522ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
5241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                id = insertPresence(values);
5251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
5261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
5271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
528a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
529508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                throw new UnsupportedOperationException("Unknown uri: " + uri);
530a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
531a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
5337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
5347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
5357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
5367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final Uri result = ContentUris.withAppendedId(uri, id);
537a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        onChange(result);
538a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return result;
539a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
540a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
541a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
542035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
543035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
544035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
545035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
546035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
5477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
548035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
549035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
550035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountName = values.getAsString(Contacts.ACCOUNT_NAME);
551035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountType = values.getAsString(Contacts.ACCOUNT_TYPE);
552035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
553035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
554035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
555035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
556035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
557035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
558035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
559035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
560035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_NAME, account.mName);
561035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_TYPE, account.mType);
562035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
563035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
5647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
5657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
5667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
5676bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * Inserts an item in the aggregates table
5686bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
5696bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
5706bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
5716bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
5726bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private long insertAggregate(ContentValues values) {
573a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregates are created automatically");
5746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
5756bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
5766bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
577a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
578a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
579a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
580f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
581a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
582a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
583f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private long insertContact(ContentValues values, Account account) {
5846bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        /*
5856bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * The contact record is inserted in the contacts table, but it needs to
5866bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * be processed by the aggregator before it will be returned by the
5876bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * "aggregates" queries.
5886bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         */
589a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
5906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
591a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
592a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        overriddenValues.putNull(Contacts.AGGREGATE_ID);
593f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
5947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
5957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
5967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
597619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Replace package with internal mapping
598619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String packageName = overriddenValues.getAsString(Contacts.PACKAGE);
599619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        overriddenValues.put(ContactsColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
600619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        overriddenValues.remove(Contacts.PACKAGE);
601619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
602a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        long rowId = db.insert(Tables.CONTACTS, Contacts.AGGREGATE_ID, overriddenValues);
603a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
604a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mContactAggregator.schedule();
605a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
606a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        return rowId;
607a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
608a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
609a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
610a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
611a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
612a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
613a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
614a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
615035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private long insertData(ContentValues values) {
616a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        boolean success = false;
617a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
618a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
619a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
620a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        db.beginTransaction();
621a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        try {
622a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            long contactId = values.getAsLong(Data.CONTACT_ID);
623a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
624619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            // Replace mimetype with internal mapping
625508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            final String mimeType = values.getAsString(Data.MIMETYPE);
626b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            values.put(DataColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType));
627508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            values.remove(Data.MIMETYPE);
628508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
6294097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
6304097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                parseStructuredName(values);
6314097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            }
6324097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
633508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            // Insert the data row itself
634b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            id = db.insert(Tables.DATA, Data.DATA1, values);
635508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
636a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            // If it's a phone number add the normalized version to the lookup table
6374097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
638508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                final ContentValues phoneValues = new ContentValues();
639508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                final String number = values.getAsString(Phone.NUMBER);
640508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER,
641508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                        PhoneNumberUtils.getStrippedReversed(number));
642508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                phoneValues.put(PhoneLookupColumns.DATA_ID, id);
643a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.CONTACT_ID, contactId);
644b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
645a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
646a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
6476bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikov            mContactAggregator.markContactForAggregation(contactId);
648a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
649a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.setTransactionSuccessful();
650a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            success = true;
651a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        } finally {
652a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.endTransaction();
653a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
654a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
655a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (success) {
656a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.schedule();
657a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
658a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
659a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
6604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
6614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
662a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
663ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Delete the given {@link Data} row, fixing up any {@link Aggregates}
664ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * primaries that reference it.
665ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
666ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private int deleteData(long dataId) {
667ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
668ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
669ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimePhone = mOpenHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
670ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimeEmail = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
671ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
672ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Check to see if the data about to be deleted was a super-primary on
673ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // the parent aggregate, and set flags to fix-up once deleted.
674ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long aggId = -1;
675ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long mimeId = -1;
676ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        String dataRaw = null;
677ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixOptimal = false;
678ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixFallback = false;
679ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
680ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor cursor = null;
681ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        try {
682ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES,
683ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Projections.PROJ_DATA_AGGREGATES, DataColumns.CONCRETE_ID + "=" + dataId, null,
684ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    null, null, null);
685ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor.moveToFirst()) {
686ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                aggId = cursor.getLong(Projections.COL_AGGREGATE_ID);
687ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mimeId = cursor.getLong(Projections.COL_MIMETYPE_ID);
688ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
689ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    dataRaw = cursor.getString(Projections.COL_PHONE_NUMBER);
690ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixOptimal = (cursor.getLong(Projections.COL_OPTIMAL_PHONE_ID) == dataId);
691ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixFallback = (cursor.getLong(Projections.COL_FALLBACK_PHONE_ID) == dataId);
692ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
693ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    dataRaw = cursor.getString(Projections.COL_EMAIL_DATA);
694ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixOptimal = (cursor.getLong(Projections.COL_OPTIMAL_EMAIL_ID) == dataId);
695ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixFallback = (cursor.getLong(Projections.COL_FALLBACK_EMAIL_ID) == dataId);
696ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
697ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
698ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        } finally {
699ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor != null) {
700ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.close();
701ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor = null;
702ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
703ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
704ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
705ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Delete the requested data item.
706ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        int dataDeleted = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
707ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
708ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Fix-up any super-primary values that are now invalid.
709ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (fixOptimal || fixFallback) {
710ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final ContentValues values = new ContentValues();
711ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final StringBuilder scoreClause = new StringBuilder();
712ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
713ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String SCORE = "score";
714ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
715ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Build scoring clause that will first pick data items under the
716ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // same aggregate that have identical values, otherwise fall back to
717ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // normal primary scoring from the member contacts.
718ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("(CASE WHEN ");
719ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (mimeId == mimePhone) {
720ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Phone.NUMBER);
721ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            } else if (mimeId == mimeEmail) {
722ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Email.DATA);
723ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
724ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("=");
725ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            DatabaseUtils.appendEscapedSQLString(scoreClause, dataRaw);
726ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append(" THEN 2 ELSE " + Data.IS_PRIMARY + " END) AS " + SCORE);
727ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
728ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String[] PROJ_PRIMARY = new String[] {
729ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    DataColumns.CONCRETE_ID,
730ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Contacts.IS_RESTRICTED,
731ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    ContactsColumns.PACKAGE_ID,
732ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    scoreClause.toString(),
733ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            };
734ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
735ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_DATA_ID = 0;
736ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_IS_RESTRICTED = 1;
737ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_PACKAGE_ID = 2;
738ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_SCORE = 3;
739ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
740ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES, PROJ_PRIMARY,
741ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND " + DataColumns.MIMETYPE_ID
742ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                            + "=" + mimeId, null, null, null, SCORE);
743ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
744ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixOptimal) {
745ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
746ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colPackageId = null;
747ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
748ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID;
749ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colPackageId = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID;
750ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
751ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID;
752ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colPackageId = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID;
753ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
754ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
755ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixOptimal told us that
756ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
757ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
758ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colPackageId);
759ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
760ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // When finding a new optimal primary, we only care about the
761ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // highest scoring value, regardless of source.
762ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (cursor.moveToFirst()) {
763ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final long newOptimal = cursor.getLong(COL_DATA_ID);
764ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final long newOptimalPackage = cursor.getLong(COL_PACKAGE_ID);
765ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
766ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (newOptimal != 0) {
767ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, newOptimal);
768ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
769ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (newOptimalPackage != 0) {
770ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colPackageId, newOptimalPackage);
771ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
772ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
773ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
774ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
775ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixFallback) {
776ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
777ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
778ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID;
779ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
780ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID;
781ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
782ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
783ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixFallback told us that
784ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
785ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
786ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
787ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // The best fallback value is the highest scoring data item that
788ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // hasn't been restricted.
789ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.moveToPosition(-1);
790ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                while (cursor.moveToNext()) {
791ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final boolean isRestricted = (cursor.getInt(COL_IS_RESTRICTED) == 1);
792ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (!isRestricted) {
793ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, cursor.getLong(COL_DATA_ID));
794ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        break;
795ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
796ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
797ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
798ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
799ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Push through any aggregate updates we have
800ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (values.size() > 0) {
801ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                db.update(Tables.AGGREGATES, values, AggregatesColumns.CONCRETE_ID + "=" + aggId,
802ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        null);
803ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
804ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
805ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
806ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return dataDeleted;
807ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
808ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
809ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
8104097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov     * Parse the supplied display name, but only if the incoming values do not already contain
8114097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov     * structured name parts.
8124097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov     */
8134097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private void parseStructuredName(ContentValues values) {
8144097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        final String fullName = values.getAsString(StructuredName.DISPLAY_NAME);
8154097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        if (TextUtils.isEmpty(fullName)
8164097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.PREFIX))
8174097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.GIVEN_NAME))
8184097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.MIDDLE_NAME))
8194097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.FAMILY_NAME))
8204097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.SUFFIX))) {
8214097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            return;
8224097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        }
8234097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
8244097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        NameSplitter.Name name = new NameSplitter.Name();
8254097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        mNameSplitter.split(name, fullName);
8264097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
8274097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.PREFIX, name.getPrefix());
8284097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.GIVEN_NAME, name.getGivenNames());
8294097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.MIDDLE_NAME, name.getMiddleName());
8304097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.FAMILY_NAME, name.getFamilyName());
8314097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.SUFFIX, name.getSuffix());
8324097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    }
8334097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
8344097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    /**
835ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
836ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
837ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private long insertGroup(ContentValues values, Account account) {
838ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
839ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
840ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
841ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
842ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
843ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
844ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
845ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
846ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.PACKAGE);
847ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        overriddenValues.put(Groups.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
848ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        overriddenValues.remove(Groups.PACKAGE);
849ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
850ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return db.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
851ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
852ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
853ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
8541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * Inserts a presence update.
8551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
8561f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private long insertPresence(ContentValues values) {
8571f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
8581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String handle = values.getAsString(Presence.IM_HANDLE);
8591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String protocol = values.getAsString(Presence.IM_PROTOCOL);
8601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (TextUtils.isEmpty(handle) || TextUtils.isEmpty(protocol)) {
8611f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            throw new IllegalArgumentException("IM_PROTOCOL and IM_HANDLE are required");
8621f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
8631f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
8641f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // TODO: generalize to allow other providers to match against email
8651f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        boolean matchEmail = GTALK_PROTOCOL_STRING.equals(protocol);
8661f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
8671f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String selection;
8681f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String[] selectionArgs;
8691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (matchEmail) {
8701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selection = "(" + Clauses.WHERE_IM_MATCHES + ") OR (" + Clauses.WHERE_EMAIL_MATCHES + ")";
8711f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle, handle };
8721f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
8731f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selection = Clauses.WHERE_IM_MATCHES;
8741f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle };
8751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
8761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
8771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long dataId = -1;
8781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long aggId = -1;
8791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
8801f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
881ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES,
882ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Projections.PROJ_DATA_CONTACTS, selection, selectionArgs, null, null, null);
8831f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
884ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                dataId = cursor.getLong(Projections.COL_DATA_ID);
885ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                aggId = cursor.getLong(Projections.COL_AGGREGATE_ID);
8861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
8871f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
8881f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
8891f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
8901f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
89131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
89231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
89331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
8941f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
8951f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
8961f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.DATA_ID, dataId);
8971f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.AGGREGATE_ID, aggId);
8981f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
8991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Insert the presence update
9001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long presenceId = db.replace(Tables.PRESENCE, null, values);
9011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return presenceId;
9021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
9031f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
9044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
9054f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int delete(Uri uri, String selection, String[] selectionArgs) {
906508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
907508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
908508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
90935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
91035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().delete(db, selection, selectionArgs);
91135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
9126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: {
9136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                long aggregateId = ContentUris.parseId(uri);
9146bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
9156bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                // Remove references to the aggregate first
9166bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                ContentValues values = new ContentValues();
9176bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                values.putNull(Contacts.AGGREGATE_ID);
918b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                db.update(Tables.CONTACTS, values, Contacts.AGGREGATE_ID + "=" + aggregateId, null);
9196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
920b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return db.delete(Tables.AGGREGATES, BaseColumns._ID + "=" + aggregateId, null);
9216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
9226bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
923508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case CONTACTS_ID: {
924508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long contactId = ContentUris.parseId(uri);
925b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int contactsDeleted = db.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
926b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, Data.CONTACT_ID + "=" + contactId, null);
927508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                return contactsDeleted + dataDeleted;
928508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
929508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
930508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID: {
931508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
932ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return deleteData(dataId);
933ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
934ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
935ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
936ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
937ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final long groupMembershipMimetypeId = mOpenHelper
938ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
939ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int groupsDeleted = db.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
940ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
941ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
942ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupId, null);
943ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
944ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return groupsDeleted + dataDeleted;
945508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
946508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
9471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
9481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return db.delete(Tables.PRESENCE, null, null);
9491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
9501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
951508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            default:
952508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                throw new UnsupportedOperationException("Unknown uri: " + uri);
953508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
9544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
9554f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
956f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private static Account readAccountFromQueryParams(Uri uri) {
957035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String name = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
958035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String type = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
959f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
960f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            return null;
961f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        }
962f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        return new Account(name, type);
963f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    }
964f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
965ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
9664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
9674f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
96800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
96935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
97000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
97100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
97200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
97335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
97435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().update(db, values, selection, selectionArgs);
97535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
976c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            // TODO(emillar): We will want to disallow editing the aggregates table at some point.
97700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES: {
97800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                count = db.update(Tables.AGGREGATES, values, selection, selectionArgs);
97900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
98000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
98100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
98200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES_ID: {
983d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                count = updateAggregateData(db, ContentUris.parseId(uri), values);
984c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
985c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
986c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
987c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            case DATA_ID: {
988c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                boolean containsIsSuperPrimary = values.containsKey(Data.IS_SUPER_PRIMARY);
989c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                boolean containsIsPrimary = values.containsKey(Data.IS_PRIMARY);
990c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                final long id = ContentUris.parseId(uri);
991c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
992c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                // Remove primary or super primary values being set to 0. This is disallowed by the
993c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                // content provider.
9948b341f8ea85257c5f7103863405e0273921e16bcEvan Millar                if (containsIsSuperPrimary && values.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
995c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    containsIsSuperPrimary = false;
996c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_SUPER_PRIMARY);
997c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
9988b341f8ea85257c5f7103863405e0273921e16bcEvan Millar                if (containsIsPrimary && values.getAsInteger(Data.IS_PRIMARY) == 0) {
999c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    containsIsPrimary = false;
1000c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_PRIMARY);
1001c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
1002c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1003c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                if (containsIsSuperPrimary) {
1004c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsSuperPrimary(id);
1005c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsPrimary(id);
1006c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1007c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    // Now that we've taken care of setting these, remove them from "values".
1008c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_SUPER_PRIMARY);
1009c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    if (containsIsPrimary) {
1010c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                        values.remove(Data.IS_PRIMARY);
1011c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    }
1012c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                } else if (containsIsPrimary) {
1013c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsPrimary(id);
1014c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1015c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    // Now that we've taken care of setting this, remove it from "values".
1016c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_PRIMARY);
1017c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
1018c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1019c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                if (values.size() > 0) {
1020c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    String selectionWithId = (Data._ID + " = " + ContentUris.parseId(uri) + " ")
1021f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                            + (selection == null ? "" : " AND " + selection);
1022c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    count = db.update(Tables.DATA, values, selectionWithId, selectionArgs);
1023c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
102400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
102500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
10267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS: {
10287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selection, selectionArgs);
10297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
10307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
10317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID: {
10337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selectionWithId = (Contacts._ID + " = " + ContentUris.parseId(uri) + " ")
10347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        + (selection == null ? "" : " AND " + selection);
10357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selectionWithId, selectionArgs);
10367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                Log.i(TAG, "Selection is: " + selectionWithId);
10377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
10387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
10397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
10407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case DATA: {
10417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.DATA, values, selection, selectionArgs);
10427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
10437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
10447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1045ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1046ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selection, selectionArgs);
1047ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1048ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1049ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1050ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1051ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1052ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1053ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String selectionWithId = (Groups._ID + "=" + groupId + " ")
1054ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + (selection == null ? "" : " AND " + selection);
1055ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selectionWithId, selectionArgs);
1056ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1057ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // If changing visibility, then update aggregates
1058ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (values.containsKey(Groups.GROUP_VISIBLE)) {
1059ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    mOpenHelper.updateAllVisible();
1060ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1061ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1062ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1063ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1064ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1065127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
1066127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                count = updateAggregationException(db, values);
1067b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
1068b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1069b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1070619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case RESTRICTION_EXCEPTIONS: {
1071619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Enforce required fields
1072619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                boolean hasFields = values.containsKey(RestrictionExceptions.PACKAGE_PROVIDER)
1073619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        && values.containsKey(RestrictionExceptions.PACKAGE_CLIENT)
1074619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        && values.containsKey(RestrictionExceptions.ALLOW_ACCESS);
1075619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (!hasFields) {
1076619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    throw new IllegalArgumentException("PACKAGE_PROVIDER, PACKAGE_CLIENT, and"
1077619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                            + "ALLOW_ACCESS are all required fields");
1078619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1079619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1080619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final String packageProvider = values
1081619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        .getAsString(RestrictionExceptions.PACKAGE_PROVIDER);
1082619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final boolean allowAccess = (values
1083619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        .getAsInteger(RestrictionExceptions.ALLOW_ACCESS) == 1);
1084619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1085619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final Context context = getContext();
1086619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final PackageManager pm = context.getPackageManager();
1087619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1088619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Enforce that caller has authority over the requested package
1089619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: move back to Binder.getCallingUid() when we can stub-out test suite
1090619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final int callingUid = OpenHelper
1091619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        .getUidForPackageName(pm, context.getPackageName());
1092619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final String[] ownedPackages = pm.getPackagesForUid(callingUid);
1093619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (!isContained(ownedPackages, packageProvider)) {
1094619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    throw new RuntimeException(
1095619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                            "Requested PACKAGE_PROVIDER doesn't belong to calling UID.");
1096619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1097619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1098619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Add or remove exception using exception helper
1099619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (allowAccess) {
1100619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    mOpenHelper.addRestrictionException(context, values);
1101619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                } else {
1102619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    mOpenHelper.removeRestrictionException(context, values);
1103619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1104619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1105619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1106619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1107619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
11087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
11097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
111000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
111100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
111200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        if (count > 0) {
111300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            getContext().getContentResolver().notifyChange(uri, null);
111400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
111500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
11164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
11174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1118d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    private int updateAggregateData(SQLiteDatabase db, long aggregateId, ContentValues values) {
1119d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1120d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // First update all constituent contacts
1121d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        ContentValues optionValues = new ContentValues(3);
1122d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (values.containsKey(Aggregates.CUSTOM_RINGTONE)) {
1123d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            optionValues.put(ContactOptionsColumns.CUSTOM_RINGTONE,
1124d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                    values.getAsString(Aggregates.CUSTOM_RINGTONE));
1125d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1126d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (values.containsKey(Aggregates.SEND_TO_VOICEMAIL)) {
1127d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            optionValues.put(ContactOptionsColumns.SEND_TO_VOICEMAIL,
1128d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                    values.getAsBoolean(Aggregates.SEND_TO_VOICEMAIL));
1129d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1130d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1131d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
1132d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (optionValues.size() == 0) {
1133d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
1134d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1135d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1136ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor c = db.query(Tables.CONTACTS, Projections.PROJ_CONTACTS, Contacts.AGGREGATE_ID + "="
1137d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                + aggregateId, null, null, null, null);
1138d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        try {
1139d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            while (c.moveToNext()) {
1140ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long contactId = c.getLong(Projections.COL_CONTACT_ID);
1141d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1142d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                optionValues.put(ContactOptionsColumns._ID, contactId);
1143d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                db.replace(Tables.CONTACT_OPTIONS, null, optionValues);
1144d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            }
1145d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        } finally {
1146d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            c.close();
1147d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1148d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1149d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Now update the aggregate itself.  Ignore all supplied fields except rington and
1150d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // send_to_voicemail
1151d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        optionValues.clear();
1152d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (values.containsKey(Aggregates.CUSTOM_RINGTONE)) {
1153d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            optionValues.put(Aggregates.CUSTOM_RINGTONE,
1154d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                    values.getAsString(Aggregates.CUSTOM_RINGTONE));
1155d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1156d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (values.containsKey(Aggregates.SEND_TO_VOICEMAIL)) {
1157d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            optionValues.put(Aggregates.SEND_TO_VOICEMAIL,
1158d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                    values.getAsBoolean(Aggregates.SEND_TO_VOICEMAIL));
1159d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1160d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1161d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        return db.update(Tables.AGGREGATES, optionValues, Aggregates._ID + "=" + aggregateId, null);
1162d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
1163d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1164127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private static class ContactPair {
1165127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId1;
1166127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId2;
1167127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1168127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        /**
1169127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         * Constructor that ensures that this.contactId1 &lt; this.contactId2
1170127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         */
1171127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        public ContactPair(long contactId1, long contactId2) {
1172127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (contactId1 < contactId2) {
1173127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId1;
1174127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId2;
1175127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1176127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId1;
1177127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId2;
1178127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1179127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1180127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    }
118180c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1182127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
1183127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
1184127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long aggregateId = values.getAsInteger(AggregationExceptions.AGGREGATE_ID);
1185127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long contactId = values.getAsInteger(AggregationExceptions.CONTACT_ID);
118680c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1187127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // First, we build a list of contactID-contactID pairs for the given aggregate and contact.
1188127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ArrayList<ContactPair> pairs = new ArrayList<ContactPair>();
1189ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor c = db.query(Tables.CONTACTS, Projections.PROJ_CONTACTS,
1190127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                Contacts.AGGREGATE_ID + "=" + aggregateId,
1191127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                null, null, null, null);
1192127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        try {
1193127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            while (c.moveToNext()) {
1194ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long aggregatedContactId = c.getLong(Projections.COL_CONTACT_ID);
1195e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                if (aggregatedContactId != contactId) {
1196e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                    pairs.add(new ContactPair(aggregatedContactId, contactId));
1197e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                }
1198b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1199b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        } finally {
1200b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            c.close();
1201b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
1202127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1203127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // Now we iterate through all contact pairs to see if we need to insert/delete/update
1204127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // the corresponding exception
1205127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ContentValues exceptionValues = new ContentValues(3);
1206127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
1207127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        for (ContactPair pair : pairs) {
1208127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            final String whereClause =
1209127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    AggregationExceptionColumns.CONTACT_ID1 + "=" + pair.contactId1 + " AND "
1210127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    + AggregationExceptionColumns.CONTACT_ID2 + "=" + pair.contactId2;
1211127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
1212127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.delete(Tables.AGGREGATION_EXCEPTIONS, whereClause, null);
1213127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1214127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID1, pair.contactId1);
1215127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID2, pair.contactId2);
1216127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
1217127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                        exceptionValues);
1218127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1219127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1220127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1221127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        mContactAggregator.markContactForAggregation(contactId);
1222127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        mContactAggregator.aggregateContact(contactId);
12237a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov        if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC
12247a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov                || exceptionType == AggregationExceptions.TYPE_KEEP_OUT) {
12257a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov            mContactAggregator.updateAggregateData(aggregateId);
12267a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov        }
1227127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1228127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
1229127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
1230127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
1231b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
1232b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1233619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1234619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list.
1235619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1236619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private boolean isContained(String[] array, String value) {
1237bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array != null) {
1238bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            for (String test : array) {
1239bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                if (value.equals(test)) {
1240bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                    return true;
1241bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                }
1242619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1243619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1244619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return false;
1245619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1246619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1247619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1248619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list, and add to the
1249619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * array if the value doesn't already appear.
1250619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1251619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private String[] assertContained(String[] array, String value) {
1252bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array == null) {
1253bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            array = new String[] {value};
1254bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        } else if (!isContained(array, value)) {
1255619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            String[] newArray = new String[array.length + 1];
1256619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            System.arraycopy(array, 0, newArray, 0, array.length);
1257619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            newArray[array.length] = value;
1258619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            array = newArray;
1259619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1260619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return array;
1261619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1262619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
12634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
12644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
12654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
12664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
126735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1268d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
12691f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
12701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String limit = null;
1271bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        String aggregateIdColName = Tables.AGGREGATES + "." + Aggregates._ID;
12724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1273619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
1274619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
1275a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
12764f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
127735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
127835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().query(db, projection, selection,  selectionArgs,
127935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
128035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
12816bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
1282b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1283619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1284619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
1285619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setProjectionMap(sAggregatesProjectionMap);
1286619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1287619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1288619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1289619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case AGGREGATES_ID: {
1290619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = ContentUris.parseId(uri);
1291619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1292ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1293619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1294619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
12956bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                qb.setProjectionMap(sAggregatesProjectionMap);
12966bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
12976bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
12986bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
12991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY: {
1300619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
13011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1302619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1303619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1304619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
13051f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1306bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
13071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
13081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
13091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
13101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY_ID: {
1311619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
13121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                long aggId = ContentUris.parseId(uri);
13131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1314ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1315619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1316619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1317619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
13181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1319bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
13201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
13211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
13221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1323ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case AGGREGATES_SUMMARY_FILTER: {
1324619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1325ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1326ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1327ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
1328ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1329ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
1330bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
1331ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1332ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1333ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1334d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT_FILTER:
1335d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT: {
1336d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the first query for starred
1337d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1338d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1339d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
1340d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
1341d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1342d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1343d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String starredQuery = qb.buildQuery(projection, Aggregates.STARRED + "=1",
1344bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null,
1345d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        null /* limit */);
1346d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1347d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
1348d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
1349d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1350d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1351d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
1352d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
1353d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1354d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1355d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String frequentQuery = qb.buildQuery(projection,
1356d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Aggregates.TIMES_CONTACTED + " > 0 AND (" + Aggregates.STARRED
1357d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        + " = 0 OR " + Aggregates.STARRED + " IS NULL)",
1358bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null, null);
1359d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1360d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
1361d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
1362d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
1363d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                Cursor c = db.rawQueryWithFactory(null, query, null,
1364d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1365d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1366d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if ((c != null) && !isTemporary()) {
1367d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
1368d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
1369d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1370d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
1371d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
1372d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
13736bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_DATA: {
1374619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = Long.parseLong(uri.getPathSegments().get(1));
1375ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1376de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1377619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Contacts.AGGREGATE_ID + "=" + aggId + " AND ");
1378619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
13796bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
13806bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
138100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
1382ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
1383ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1384ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1385ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
1386ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
1387ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(" AND " + buildAggregateLookupWhereClause(
1388ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                            uri.getLastPathSegment()));
1389ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
1390ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1391ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1392ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1393ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES: {
1394ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1395ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1396ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = \"" + Phone.CONTENT_ITEM_TYPE + "\"");
1397ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1398ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1399ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1400ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
1401ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1402ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1403ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = \"" + Postal.CONTENT_ITEM_TYPE + "\"");
1404ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1405ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1406ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
14074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: {
1408035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.setTables(Tables.CONTACTS_JOIN_PACKAGES);
14094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
1410619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
14114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
14124f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
14134f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
14144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: {
1415619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = ContentUris.parseId(uri);
1416035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.setTables(Tables.CONTACTS_JOIN_PACKAGES);
14174f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
1418ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(ContactsColumns.CONCRETE_ID + "=" + contactId + " AND ");
1419619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
14204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
14214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
14224f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1423a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
1424619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = Long.parseLong(uri.getPathSegments().get(1));
1425ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES);
14267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                qb.setProjectionMap(sDataContactsProjectionMap);
1427619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Data.CONTACT_ID + "=" + contactId + " AND ");
1428619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
1429a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1430a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1431a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
143228ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey            case CONTACTS_FILTER_EMAIL: {
1433619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1434ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1435e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.setProjectionMap(sDataContactsProjectionMap);
14365d0f923eb4c5351ebf323cc6f19c82acff98693eJeff Sharkey                qb.appendWhere(Data.MIMETYPE + "='" + CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'");
1437e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhere(" AND " + CommonDataKinds.Email.DATA + "=");
1438e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhereEscapeString(uri.getPathSegments().get(2));
1439e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
1440e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
1441e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
1442e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
1443035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
1444035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
1445343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                if (!TextUtils.isEmpty(accountName)) {
1446035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                    qb.appendWhere(Contacts.ACCOUNT_NAME + "="
1447035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountName) + " AND "
1448035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + Contacts.ACCOUNT_TYPE + "="
1449035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountType) + " AND ");
1450343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                }
1451ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES);
1452e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.setProjectionMap(sDataProjectionMap);
1453619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
1454e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
1455e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
1456e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
14574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
1458ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES);
14594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sDataProjectionMap);
1460ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(DataColumns.CONCRETE_ID + "=" + ContentUris.parseId(uri) + " AND ");
1461619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
14624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
14634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
14644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1465a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
1466619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1467a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
1468a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
1469a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
1470e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                    sortOrder = Data.CONTACT_ID;
1471a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
1472a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1473a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                final String number = uri.getLastPathSegment();
1474bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov                OpenHelper.buildPhoneLookupQuery(qb, number);
1475a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                qb.setProjectionMap(sDataContactsProjectionMap);
1476a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1477a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1478a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1479ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1480ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
1481ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
1482ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1483ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1484ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1485ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1486ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1487ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
1488ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
1489ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(GroupsColumns.CONCRETE_ID + "=" + groupId);
1490ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1491ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1492ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1493ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
1494ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES_DATA_CONTACTS_AGGREGATES);
1495ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
1496ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                groupBy = GroupsColumns.CONCRETE_ID;
1497ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1498ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1499ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1500b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
1501127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS_JOIN_CONTACTS);
1502b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
1503b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
1504b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1505b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
150631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
150731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                long aggregateId = Long.parseLong(uri.getPathSegments().get(1));
150831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final String maxSuggestionsParam =
150931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        uri.getQueryParameter(AggregationSuggestions.MAX_SUGGESTIONS);
151031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
151131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
151231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                if (maxSuggestionsParam != null) {
151331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(maxSuggestionsParam);
151431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
151531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
151631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
151731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
151831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(aggregateId, projection,
151931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        sAggregatesProjectionMap, maxSuggestions);
152031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
152131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1522619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case RESTRICTION_EXCEPTIONS: {
1523619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setTables(Tables.RESTRICTION_EXCEPTIONS);
1524619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setProjectionMap(sRestrictionExceptionsProjectionMap);
1525619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1526619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1527619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
15284f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
15294f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                throw new UnsupportedOperationException("Unknown uri: " + uri);
15304f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
15314f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
15324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Perform the query and set the notification uri
15331f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final Cursor c = qb.query(db, projection, selection, selectionArgs,
1534bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy, null, sortOrder, limit);
15354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
15364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
15374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
15384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
15394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
15404f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
15417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1542619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Restrict selection of {@link Aggregates} to only public ones, or those
1543619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * the caller has been granted a {@link RestrictionExceptions} to.
1544619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1545619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregateRestrictionExceptions(SQLiteQueryBuilder qb) {
1546619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = OpenHelper.getUidForPackageName(getContext().getPackageManager(),
1547619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                getContext().getPackageName());
1548619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1549619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere("(" + AggregatesColumns.SINGLE_RESTRICTED_PACKAGE_ID + " IS NULL");
1550619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String exceptionClause = mOpenHelper.getRestrictionExceptionClause(clientUid,
1551619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.SINGLE_RESTRICTED_PACKAGE_ID);
1552619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (exceptionClause != null) {
1553619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            qb.appendWhere(" OR (" + exceptionClause + ")");
1554619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1555619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere(")");
1556619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1557619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1558619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1559619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the calling process, and
1560619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * add projections to correctly select {@link Aggregates#PRIMARY_PHONE_ID}
1561619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * and {@link Aggregates#PRIMARY_EMAIL_ID}.
1562619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1563619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregatePrimaryRestrictionExceptions(HashMap<String, String> projection) {
1564619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: move back to Binder.getCallingUid() when we can stub-out test suite
1565619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = OpenHelper.getUidForPackageName(getContext().getPackageManager(),
1566619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                getContext().getPackageName());
1567619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1568619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String projectionPhone = "(CASE WHEN "
1569619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + mOpenHelper.getRestrictionExceptionClause(clientUid,
1570619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID) + " THEN "
1571619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID + " ELSE "
1572619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID + " END) AS "
1573619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + Aggregates.PRIMARY_PHONE_ID;
1574619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_PHONE_ID);
1575619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_PHONE_ID, projectionPhone);
1576619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1577619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String projectionEmail = "(CASE WHEN "
1578619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + mOpenHelper.getRestrictionExceptionClause(clientUid,
1579619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID) + " THEN "
1580619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID + " ELSE "
1581619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID + " END) AS "
1582619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + Aggregates.PRIMARY_EMAIL_ID;
1583619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_EMAIL_ID);
1584619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_EMAIL_ID, projectionEmail);
1585619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1586619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1587619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1588619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
1589619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
1590619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
1591619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1592619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyContactsRestrictionExceptions(SQLiteQueryBuilder qb) {
1593619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: move back to Binder.getCallingUid() when we can stub-out test suite
1594619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = OpenHelper.getUidForPackageName(getContext().getPackageManager(),
1595619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                getContext().getPackageName());
1596619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1597619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere("(" + Contacts.IS_RESTRICTED + "=0");
1598619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String exceptionClause = mOpenHelper.getRestrictionExceptionClause(clientUid,
1599619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                ContactsColumns.PACKAGE_ID);
1600619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (exceptionClause != null) {
1601619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            qb.appendWhere(" OR (" + exceptionClause + ")");
1602619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1603619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere(")");
1604619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1605619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1606619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1607619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
1608619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
1609619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
1610619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1611619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyDataRestrictionExceptions(SQLiteQueryBuilder qb) {
1612619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        applyContactsRestrictionExceptions(qb);
1613619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1614619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1615619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
16167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
16177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
16187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
16197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    private static class ContactsEntityIterator implements EntityIterator {
16207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
16217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
16227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
16247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data1",
16257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data2",
16267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data3",
16277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data4",
16287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data5",
16297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data6",
16307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data7",
16317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data8",
16327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data9",
16337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data10"};
16347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] PROJECTION = new String[]{
1636035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_NAME,
1637035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_TYPE,
1638035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.SOURCE_ID,
1639035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.VERSION,
1640035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.DIRTY,
1641035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data._ID,
1642035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.MIMETYPE,
1643035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA1,
1644035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA2,
1645035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA3,
1646035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA4,
1647035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA5,
1648035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA6,
1649035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA7,
1650035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA8,
1651035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA9,
1652035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA10,
1653035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.CONTACT_ID,
1654035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.IS_PRIMARY,
1655035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA_VERSION};
1656035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
1657035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
1658035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
1659035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
1660035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
1661035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
1662035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
1663035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_MIMETYPE = 6;
1664035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA1 = 7;
1665035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_CONTACT_ID = 17;
1666035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_IS_PRIMARY = 18;
1667035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_VERSION = 19;
16687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public ContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, Uri uri,
16707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
16717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
16727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final String updatedSortOrder = (sortOrder == null)
16747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    ? Contacts.Data.CONTACT_ID
16757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    : (Contacts.Data.CONTACT_ID + "," + sortOrder);
16767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
16787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
1679ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES);
1680035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            qb.setProjectionMap(sDataContactsProjectionMap);
16817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
16827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                qb.appendWhere(Data.CONTACT_ID + "=" + contactsIdString);
16837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
1684035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
1685035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
1686035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (!TextUtils.isEmpty(accountName)) {
1687035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.appendWhere(Contacts.ACCOUNT_NAME + "="
1688035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
1689035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + Contacts.ACCOUNT_TYPE + "="
1690035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
1691035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
16927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
16937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    null, null, updatedSortOrder);
16947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
16957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
16967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
16977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
16987e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
16997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
17007e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17017e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
17027e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
17037e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
17047e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
17067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
17077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
17087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
17117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
17127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
17147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
17157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
17167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
17187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
17197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
17207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
17227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final long contactId = c.getLong(COLUMN_CONTACT_ID);
17247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
17267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
1727035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
1728035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
17297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts._ID, contactId);
17307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.DIRTY, c.getLong(COLUMN_DIRTY));
1731f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            contactValues.put(Contacts.VERSION, c.getLong(COLUMN_VERSION));
17327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
17337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
17347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
17367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
17377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (contactId != c.getLong(COLUMN_CONTACT_ID)) {
17387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
17397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
17407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
17417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
17427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                dataValues.put(Contacts.Data._ID, c.getString(COLUMN_DATA_ID));
1743f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
1744f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.IS_PRIMARY, c.getString(COLUMN_IS_PRIMARY));
1745f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
17467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                for (int i = 0; i < 10; i++) {
17477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
17487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
17497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
17507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
17517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
17527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
17537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
17547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
17557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
17567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
17577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
17587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
17597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
17607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
17617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
17627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
17637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
17657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
17667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
17677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1768a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
17697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
17707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
17717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
17727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
17737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS:
17747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID:
17757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
17767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (match == CONTACTS_ID) {
17777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
17787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
17797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                return new ContactsEntityIterator(this, contactsIdString,
17817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        uri, selection, selectionArgs, sortOrder);
17827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
17837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
17847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
17857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
17867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17874f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
17884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
1789a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
17904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
17916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: return Aggregates.CONTENT_TYPE;
17926bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: return Aggregates.CONTENT_ITEM_TYPE;
17934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: return Contacts.CONTENT_TYPE;
17944f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: return Contacts.CONTENT_ITEM_TYPE;
1795508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
17966bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1797508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
1798b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return mOpenHelper.getDataMimeType(dataId);
179931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: return AggregationExceptions.CONTENT_TYPE;
180031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTION_ID: return AggregationExceptions.CONTENT_ITEM_TYPE;
180131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: return Aggregates.CONTENT_TYPE;
18024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
1803a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        throw new UnsupportedOperationException("Unknown uri: " + uri);
18044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
18057e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1806b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    @Override
18077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
18087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            throws OperationApplicationException {
18097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
18117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        db.beginTransaction();
18127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        try {
18137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentProviderResult[] results = super.applyBatch(operations);
18147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.setTransactionSuccessful();
18157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return results;
18167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        } finally {
18177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.endTransaction();
18187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
18197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
1820c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1821c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
1822c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
1823c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
1824c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
1825c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
1826c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
1827c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsPrimary(long dataId) {
1828c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
1829c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(2, dataId);
1830c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(3, dataId);
1831c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
1832c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
1833c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1834c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
1835c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
1836c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
1837c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
1838c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
1839c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
1840c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsSuperPrimary(long dataId) {
1841c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
1842c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(2, dataId);
1843c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(3, dataId);
1844c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
1845619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1846619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Find the parent aggregate and package for this new primary
1847619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1848619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1849619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        long aggId = -1;
1850619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        long packageId = -1;
1851619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isRestricted = false;
1852619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        String mimeType = null;
1853619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1854619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        Cursor cursor = null;
1855619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        try {
1856ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES,
1857ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Projections.PROJ_DATA_CONTACTS, DataColumns.CONCRETE_ID + "=" + dataId, null,
1858ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    null, null, null);
1859619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor.moveToFirst()) {
1860ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                aggId = cursor.getLong(Projections.COL_AGGREGATE_ID);
1861ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                packageId = cursor.getLong(Projections.COL_PACKAGE_ID);
1862ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                isRestricted = (cursor.getInt(Projections.COL_IS_RESTRICTED) == 1);
1863ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mimeType = cursor.getString(Projections.COL_MIMETYPE);
1864619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1865619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } finally {
1866619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor != null) {
1867619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                cursor.close();
1868619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1869619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1870619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1871619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Bypass aggregate update if no parent found, or if we don't keep track
1872619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // of super-primary for this mimetype.
1873d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (aggId == -1) {
1874d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return;
1875d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1876619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1877619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isPhone = CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimeType);
1878619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isEmail = CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(mimeType);
1879619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1880619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Record this value as the new primary for the parent aggregate
1881619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final ContentValues values = new ContentValues();
1882619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (isPhone) {
1883619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID, dataId);
1884619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID, packageId);
1885619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (isEmail) {
1886619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID, dataId);
1887619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID, packageId);
1888619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1889619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1890619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // If this data is unrestricted, then also set as fallback
1891619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (!isRestricted && isPhone) {
1892619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID, dataId);
1893619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (!isRestricted && isEmail) {
1894619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID, dataId);
1895619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1896619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1897619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Push update into aggregates table, if needed
1898619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (values.size() > 0) {
1899619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            db.update(Tables.AGGREGATES, values, Aggregates._ID + "=" + aggId, null);
1900619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1901619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1902c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
1903ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1904ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private String buildAggregateLookupWhereClause(String filterParam) {
1905ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        StringBuilder filter = new StringBuilder();
1906ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.AGGREGATES);
1907ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(".");
1908ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Aggregates._ID);
1909ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" IN (SELECT ");
1910ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts.AGGREGATE_ID);
1911ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" FROM ");
1912ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.CONTACTS);
1913ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" WHERE ");
1914ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts._ID);
1915d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(" IN (SELECT  contact_id FROM name_lookup WHERE normalized_name GLOB '");
1916ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // NOTE: Query parameters won't work here since the SQL compiler
1917ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // needs to parse the actual string to know that it can use the
1918ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // index to do a prefix scan.
1919d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(NameNormalizer.normalize(filterParam) + "*");
1920d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append("'))");
1921ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        return filter.toString();
1922ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
1923ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
19244f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
1925