ContactsProvider2.java revision 9261b2141aa90a4fed632fd6da03026d4c216280
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.DataColumns;
2428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.GroupsColumns;
2528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.MimetypesColumns;
2628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.PhoneLookupColumns;
2728f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millarimport com.android.providers.contacts.OpenHelper.Tables;
2835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport com.android.internal.content.SyncStateContentProviderHelper;
29b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.accounts.Account;
3035ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.pm.PackageManager;
31f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikovimport android.app.SearchManager;
324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.content.ContentProvider;
3335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.UriMatcher;
34b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkeyimport android.content.Context;
3535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentValues;
3635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentUris;
377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport android.content.EntityIterator;
3835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.Entity;
3935ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentProviderResult;
407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintanaimport android.content.OperationApplicationException;
4135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintanaimport android.content.ContentProviderOperation;
424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.Cursor;
43ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkeyimport android.database.DatabaseUtils;
44b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.database.sqlite.SQLiteCursor;
454f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteDatabase;
464f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.database.sqlite.SQLiteQueryBuilder;
47c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millarimport android.database.sqlite.SQLiteStatement;
484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamiltonimport android.net.Uri;
49619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkeyimport android.os.Binder;
50b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikovimport android.os.RemoteException;
51508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkeyimport android.provider.BaseColumns;
52de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millarimport android.provider.ContactsContract;
536bccc079d8fea5c51f9fa6fd06044bd8f5109c6fDmitri Plotnikovimport android.provider.Contacts.ContactMethods;
54f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikovimport android.provider.Contacts.People;
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;
105b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private static final int AGGREGATES_SUMMARY_GROUP = 1008;
1064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS = 2002;
1086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS_ID = 2003;
1096bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int CONTACTS_DATA = 2004;
11028ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey    private static final int CONTACTS_FILTER_EMAIL = 2005;
1114f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1126bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA = 3000;
1136bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int DATA_ID = 3001;
114ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES = 3002;
115ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int PHONES_FILTER = 3003;
116ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private static final int POSTALS = 3004;
117a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1186bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final int PHONE_LOOKUP = 4000;
1196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
120b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTIONS = 6000;
121b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final int AGGREGATION_EXCEPTION_ID = 6001;
122b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE = 7000;
1241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final int PRESENCE_ID = 7001;
1251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
12631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int AGGREGATION_SUGGESTIONS = 8000;
12731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
128619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private static final int RESTRICTION_EXCEPTIONS = 9000;
129619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
130ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS = 10000;
131ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_ID = 10001;
132ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final int GROUPS_SUMMARY = 10003;
133ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
13435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana    private static final int SYNCSTATE = 11000;
13535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
136ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private interface Projections {
1379261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        public static final String[] CONTACTS_ACCOUNT_AND_PACKAGE =
1389261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                new String[]{Contacts.ACCOUNT_NAME, Contacts.ACCOUNT_TYPE,
1399261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                        ContactsColumns.PACKAGE_ID};
1409261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        public static final int CONTACTS_ACCOUNT_AND_PACKAGE_COL_ACCOUNT_NAME = 0;
1419261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        public static final int CONTACTS_ACCOUNT_AND_PACKAGE_COL_ACCOUNT_TYPE = 1;
1429261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        public static final int CONTACTS_ACCOUNT_AND_PACKAGE_COL_PACKAGE_ID = 2;
1439261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
144ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String[] PROJ_CONTACTS = new String[] {
145ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            ContactsColumns.CONCRETE_ID,
146ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
147ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
148ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String[] PROJ_DATA_CONTACTS = new String[] {
149ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                ContactsColumns.CONCRETE_ID,
150ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                DataColumns.CONCRETE_ID,
151ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Contacts.AGGREGATE_ID,
152ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                ContactsColumns.PACKAGE_ID,
153ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Contacts.IS_RESTRICTED,
154ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Data.MIMETYPE,
155ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
156ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
157ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_CONTACT_ID = 0;
158ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_DATA_ID = 1;
159ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_AGGREGATE_ID = 2;
160ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_PACKAGE_ID = 3;
161ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_IS_RESTRICTED = 4;
162ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_MIMETYPE = 5;
163ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
164ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final String[] PROJ_DATA_AGGREGATES = new String[] {
165ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            ContactsColumns.CONCRETE_ID,
166ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                DataColumns.CONCRETE_ID,
167ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.CONCRETE_ID,
168ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                MimetypesColumns.CONCRETE_ID,
169ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Phone.NUMBER,
170ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                Email.DATA,
171ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID,
172ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
173ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID,
174ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
175ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        };
176ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
177ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_MIMETYPE_ID = 3;
178ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_PHONE_NUMBER = 4;
179ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_EMAIL_DATA = 5;
180ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_OPTIMAL_PHONE_ID = 6;
181ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_FALLBACK_PHONE_ID = 7;
182ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_OPTIMAL_EMAIL_ID = 8;
183ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        public static final int COL_FALLBACK_EMAIL_ID = 9;
18480c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
185ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
18731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /** Default for the maximum number of returned aggregation suggestions. */
18831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    private static final int DEFAULT_MAX_SUGGESTIONS = 5;
18931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /** Contains just the contacts columns */
1916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private static final HashMap<String, String> sAggregatesProjectionMap;
19200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar    /** Contains the aggregate columns along with primary phone */
1931f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final HashMap<String, String> sAggregatesSummaryProjectionMap;
194de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    /** Contains the data, contacts, and aggregate columns, for joined tables. */
195de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar    private static final HashMap<String, String> sDataContactsAggregateProjectionMap;
1969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data, contacts, group sourceid and aggregate columns, for joined tables. */
1979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataContactsGroupsAggregateProjectionMap;
198a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the contacts columns */
1994f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private static final HashMap<String, String> sContactsProjectionMap;
200a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains just the data columns */
2019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataGroupsProjectionMap;
2029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /** Contains the data and contacts columns, for joined tables */
2039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private static final HashMap<String, String> sDataContactsGroupsProjectionMap;
204a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /** Contains the data and contacts columns, for joined tables */
205a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private static final HashMap<String, String> sDataContactsProjectionMap;
206ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains the just the {@link Groups} columns */
207ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsProjectionMap;
208ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /** Contains {@link Groups} columns along with summary details */
209ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private static final HashMap<String, String> sGroupsSummaryProjectionMap;
210b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    /** Contains the just the agg_exceptions columns */
211b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    private static final HashMap<String, String> sAggregationExceptionsProjectionMap;
212619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /** Contains the just the {@link RestrictionExceptions} columns */
213619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private static final HashMap<String, String> sRestrictionExceptionsProjectionMap;
2147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
215c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the contact id associated with a data record. */
216c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdSelect;
217c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the mimetype id associated with a data record. */
218c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedMimetypeSelect;
219c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns the aggregate id associated with a contact record. */
220c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedAggregateIdSelect;
221c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql select statement that returns a list of contact ids associated with an aggregate record. */
222c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sNestedContactIdListSelect;
223c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
224c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "primary" is selected.*/
225c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetPrimaryWhere;
226c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Sql where statement used to match all the data records that need to be updated when a new
227c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * "super primary" is selected.*/
228c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private static final String sSetSuperPrimaryWhere;
229b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    /** Sql where statement for filtering on groups. */
230b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private static final String sAggregatesInGroupSelect;
231c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precompiled sql statement for setting a data record to the primary. */
232c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetPrimaryStatement;
233c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /** Precomipled sql statement for setting a data record to the super primary. */
234c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private SQLiteStatement mSetSuperPrimaryStatement;
235f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    /** Precomipled sql statement for incrementing times contacted for an aggregate */
236f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private SQLiteStatement mLastTimeContactedUpdate;
237a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
2381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private static final String GTALK_PROTOCOL_STRING = ContactMethods
2391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            .encodePredefinedImProtocol(ContactMethods.PROTOCOL_GOOGLE_TALK);
2401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
2414f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    static {
2424f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts URI matching table
243a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final UriMatcher matcher = sUriMatcher;
2446bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates", AGGREGATES);
2456bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#", AGGREGATES_ID);
2466bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/data", AGGREGATES_DATA);
2471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary", AGGREGATES_SUMMARY);
2481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/#", AGGREGATES_SUMMARY_ID);
249ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/filter/*",
250ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                AGGREGATES_SUMMARY_FILTER);
251d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/",
252d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT);
253d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/strequent/filter/*",
254d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                AGGREGATES_SUMMARY_STREQUENT_FILTER);
255b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "aggregates_summary/group/*",
256b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                AGGREGATES_SUMMARY_GROUP);
25731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregates/#/suggestions",
25831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                AGGREGATION_SUGGESTIONS);
2594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts", CONTACTS);
2604f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#", CONTACTS_ID);
261a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "contacts/#/data", CONTACTS_DATA);
262b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "contacts/filter_email/*",
263b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                CONTACTS_FILTER_EMAIL);
264b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
2654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data", DATA);
2664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "data/#", DATA_ID);
267ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones", PHONES);
268ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/phones/filter/*", PHONES_FILTER);
269ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "data/postals", POSTALS);
2701f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
271ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups", GROUPS);
272ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups/#", GROUPS_ID);
273ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "groups_summary", GROUPS_SUMMARY);
274ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
27535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        matcher.addURI(ContactsContract.AUTHORITY, SyncStateContentProviderHelper.PATH, SYNCSTATE);
27635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
277a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        matcher.addURI(ContactsContract.AUTHORITY, "phone_lookup/*", PHONE_LOOKUP);
278b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions",
279b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTIONS);
280b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        matcher.addURI(ContactsContract.AUTHORITY, "aggregation_exceptions/*",
281b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                AGGREGATION_EXCEPTION_ID);
2824f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
283bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence", PRESENCE);
284bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        matcher.addURI(ContactsContract.AUTHORITY, "presence/#", PRESENCE_ID);
2851f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
286619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        matcher.addURI(ContactsContract.AUTHORITY, "restriction_exceptions", RESTRICTION_EXCEPTIONS);
287619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
288fec4e13316f2731d84394e5fa2f93af3febdc20cEvan Millar        HashMap<String, String> columns;
2894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
2906bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        // Aggregates projection map
2916bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns = new HashMap<String, String>();
29200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(Aggregates._ID, "aggregates._id AS _id");
2936bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Aggregates.DISPLAY_NAME, Aggregates.DISPLAY_NAME);
2946bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Aggregates.LAST_TIME_CONTACTED, Aggregates.LAST_TIME_CONTACTED);
295d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        columns.put(Aggregates.TIMES_CONTACTED, Aggregates.TIMES_CONTACTED);
2966bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Aggregates.STARRED, Aggregates.STARRED);
297ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Aggregates.IN_VISIBLE_GROUP, Aggregates.IN_VISIBLE_GROUP);
298ae6ca1f34cf5458d79ec803411d4308879a91e92Evan Millar        columns.put(Aggregates.PHOTO_ID, Aggregates.PHOTO_ID);
299de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.put(Aggregates.PRIMARY_PHONE_ID, Aggregates.PRIMARY_PHONE_ID);
300c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Aggregates.PRIMARY_EMAIL_ID, Aggregates.PRIMARY_EMAIL_ID);
301d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        columns.put(Aggregates.CUSTOM_RINGTONE, Aggregates.CUSTOM_RINGTONE);
302d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        columns.put(Aggregates.SEND_TO_VOICEMAIL, Aggregates.SEND_TO_VOICEMAIL);
303619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID,
304619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID);
305619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID,
306619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID);
3076bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        sAggregatesProjectionMap = columns;
3086bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
3091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Aggregates primaries projection map. The overall presence status is
3101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // the most-present value, as indicated by the largest value.
3111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns = new HashMap<String, String>();
3121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        columns.putAll(sAggregatesProjectionMap);
31300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.TYPE, CommonDataKinds.Phone.TYPE);
31400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.LABEL, CommonDataKinds.Phone.LABEL);
31500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        columns.put(CommonDataKinds.Phone.NUMBER, CommonDataKinds.Phone.NUMBER);
316bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        columns.put(Presence.PRESENCE_STATUS, "MAX(" + Presence.PRESENCE_STATUS + ")");
3171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        sAggregatesSummaryProjectionMap = columns;
31800d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
3194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Contacts projection map
3204f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
3214f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Contacts._ID, "contacts._id AS _id");
322619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(Contacts.PACKAGE, Contacts.PACKAGE);
3236bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        columns.put(Contacts.AGGREGATE_ID, Contacts.AGGREGATE_ID);
3249261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.ACCOUNT_NAME,
3259261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_ACCOUNT_NAME + " as " + Contacts.ACCOUNT_NAME);
3269261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.ACCOUNT_TYPE,
3279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_ACCOUNT_TYPE + " as " + Contacts.ACCOUNT_TYPE);
3289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.SOURCE_ID,
3299261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_SOURCE_ID + " as " + Contacts.SOURCE_ID);
3309261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.VERSION,
3319261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_VERSION + " as " + Contacts.VERSION);
3329261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Contacts.DIRTY,
3339261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_DIRTY + " as " + Contacts.DIRTY);
3344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        sContactsProjectionMap = columns;
3354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
3364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Data projection map
3374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns = new HashMap<String, String>();
3384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data._ID, "data._id AS _id");
3394f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        columns.put(Data.CONTACT_ID, Data.CONTACT_ID);
340508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        columns.put(Data.MIMETYPE, Data.MIMETYPE);
341c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_PRIMARY, Data.IS_PRIMARY);
342c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        columns.put(Data.IS_SUPER_PRIMARY, Data.IS_SUPER_PRIMARY);
343f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        columns.put(Data.DATA_VERSION, Data.DATA_VERSION);
3447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA1, "data.data1 as data1");
3457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA2, "data.data2 as data2");
3467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA3, "data.data3 as data3");
3477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA4, "data.data4 as data4");
3487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA5, "data.data5 as data5");
3497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA6, "data.data6 as data6");
3507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA7, "data.data7 as data7");
3517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA8, "data.data8 as data8");
3527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA9, "data.data9 as data9");
3537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.put(Data.DATA10, "data.data10 as data10");
3549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(GroupMembership.GROUP_SOURCE_ID, "groups.sourceid as " + GroupMembership.GROUP_SOURCE_ID);
355d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        // Mappings used for backwards compatibility.
356d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        columns.put("number", Phone.NUMBER);
3579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataGroupsProjectionMap = columns;
358a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
3599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data, groups and contacts projection map for joins. _id comes from the data table
360a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns = new HashMap<String, String>();
361a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        columns.putAll(sContactsProjectionMap);
3629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataGroupsProjectionMap); // _id will be replaced with the one from data
363ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
3649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataContactsGroupsProjectionMap = columns;
3659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
3669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data and contacts projection map for joins. _id comes from the data table
3679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns = new HashMap<String, String>();
3689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataContactsGroupsProjectionMap);
3699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.remove(GroupMembership.GROUP_SOURCE_ID);
370a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        sDataContactsProjectionMap = columns;
3717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
372de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        // Data and contacts projection map for joins. _id comes from the data table
373de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns = new HashMap<String, String>();
374de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        columns.putAll(sAggregatesProjectionMap);
3757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        columns.putAll(sContactsProjectionMap); //
3769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataGroupsProjectionMap); // _id will be replaced with the one from data
377ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Data.CONTACT_ID, DataColumns.CONCRETE_CONTACT_ID);
3789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        sDataContactsGroupsAggregateProjectionMap = columns;
3799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
3809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // Data and contacts projection map for joins. _id comes from the data table
3819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns = new HashMap<String, String>();
3829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.putAll(sDataContactsGroupsAggregateProjectionMap);
3839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.remove(GroupMembership.GROUP_SOURCE_ID);
384de4c4d84028c6c6999c6d9277b54b661f207b992Evan Millar        sDataContactsAggregateProjectionMap = columns;
385c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
386ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Groups projection map
387ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
388ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups._ID, "groups._id AS _id");
389035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_NAME, Groups.ACCOUNT_NAME);
390035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        columns.put(Groups.ACCOUNT_TYPE, Groups.ACCOUNT_TYPE);
3919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.SOURCE_ID, Groups.SOURCE_ID);
3929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.DIRTY, Groups.DIRTY);
3939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        columns.put(Groups.VERSION, Groups.VERSION);
394ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.PACKAGE, Groups.PACKAGE);
395ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.PACKAGE_ID, GroupsColumns.CONCRETE_PACKAGE_ID);
396ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE, Groups.TITLE);
397ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.TITLE_RESOURCE, Groups.TITLE_RESOURCE);
398ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.GROUP_VISIBLE, Groups.GROUP_VISIBLE);
399ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsProjectionMap = columns;
400ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
401ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Contacts and groups projection map
402ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns = new HashMap<String, String>();
403ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.putAll(sGroupsProjectionMap);
404ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
405ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_COUNT, "(SELECT COUNT(DISTINCT " + AggregatesColumns.CONCRETE_ID
406ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") FROM " + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
407ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
408ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + ") AS " + Groups.SUMMARY_COUNT);
409ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
410ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        columns.put(Groups.SUMMARY_WITH_PHONES, "(SELECT COUNT(DISTINCT "
411ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + AggregatesColumns.CONCRETE_ID + ") FROM "
412ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES + " WHERE "
413ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + Clauses.MIMETYPE_IS_GROUP_MEMBERSHIP + " AND " + Clauses.BELONGS_TO_GROUP
414ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                + " AND " + Clauses.HAS_PRIMARY_PHONE + ") AS " + Groups.SUMMARY_WITH_PHONES);
415ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
416ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        sGroupsSummaryProjectionMap = columns;
417ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
418b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        // Aggregate exception projection map
419b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns = new HashMap<String, String>();
420b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptionColumns._ID, Tables.AGGREGATION_EXCEPTIONS + "._id AS _id");
421b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        columns.put(AggregationExceptions.TYPE, AggregationExceptions.TYPE);
422127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.AGGREGATE_ID,
423127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                "contacts1." + Contacts.AGGREGATE_ID + " AS " + AggregationExceptions.AGGREGATE_ID);
424127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        columns.put(AggregationExceptions.CONTACT_ID, AggregationExceptionColumns.CONTACT_ID2);
425b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        sAggregationExceptionsProjectionMap = columns;
426b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
427619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Restriction exception projection map
428619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns = new HashMap<String, String>();
429619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(RestrictionExceptions.PACKAGE_PROVIDER, RestrictionExceptions.PACKAGE_PROVIDER);
430619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(RestrictionExceptions.PACKAGE_CLIENT, RestrictionExceptions.PACKAGE_CLIENT);
431619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        columns.put(RestrictionExceptions.ALLOW_ACCESS, "1"); // Access granted if row returned
432619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        sRestrictionExceptionsProjectionMap = columns;
433619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
434c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdSelect = "SELECT " + Data.CONTACT_ID + " FROM " + Tables.DATA + " WHERE "
435c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + Data._ID + "=?";
436c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedMimetypeSelect = "SELECT " + DataColumns.MIMETYPE_ID + " FROM " + Tables.DATA
437c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Data._ID + "=?";
438c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedAggregateIdSelect = "SELECT " + Contacts.AGGREGATE_ID + " FROM " + Tables.CONTACTS
439c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts._ID + "=(" + sNestedContactIdSelect + ")";
440c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sNestedContactIdListSelect = "SELECT " + Contacts._ID + " FROM " + Tables.CONTACTS
441c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + " WHERE " + Contacts.AGGREGATE_ID + "=(" + sNestedAggregateIdSelect + ")";
442c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        sSetPrimaryWhere = Data.CONTACT_ID + "=(" + sNestedContactIdSelect + ") AND "
443c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
444b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        sSetSuperPrimaryWhere = Data.CONTACT_ID + " IN (" + sNestedContactIdListSelect + ") AND "
445c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + DataColumns.MIMETYPE_ID + "=(" + sNestedMimetypeSelect + ")";
446b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        sAggregatesInGroupSelect = AggregatesColumns.CONCRETE_ID + " IN (SELECT "
447b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Contacts.AGGREGATE_ID + " FROM " + Tables.CONTACTS + " WHERE ("
448b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + ContactsColumns.CONCRETE_ID + " IN (SELECT " + Tables.DATA + "."
449b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Data.CONTACT_ID + " FROM " + Tables.DATA_JOIN_MIMETYPES + " WHERE ("
450b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE + "' AND "
451b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + GroupMembership.GROUP_ROW_ID + "=(SELECT " + Tables.GROUPS + "."
452b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                + Groups._ID + " FROM " + Tables.GROUPS + " WHERE " + Groups.TITLE + "=?)))))";
4534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
4544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
45553056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    private final ContactAggregationScheduler mAggregationScheduler;
4564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    private OpenHelper mOpenHelper;
45731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
458a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    private ContactAggregator mContactAggregator;
4594097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private NameSplitter mNameSplitter;
460f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private LegacyApiSupport mLegacyApiSupport;
461a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
462a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    public ContactsProvider2() {
46353056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        this(new ContactAggregationScheduler());
464a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
465a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
466a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
467a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Constructor for testing.
468a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
46953056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov    /* package */ ContactsProvider2(ContactAggregationScheduler scheduler) {
47053056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mAggregationScheduler = scheduler;
471a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
4724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
4734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
4744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean onCreate() {
475b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey        final Context context = getContext();
47635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
47731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        mOpenHelper = getOpenHelper(context);
4781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
4794f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
48053056d49d8adf5d1fe2d18cb60c3c26f281e7a6cDmitri Plotnikov        mContactAggregator = new ContactAggregator(context, mOpenHelper, mAggregationScheduler);
481a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
482c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement = db.compileStatement(
483c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_PRIMARY
484c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetPrimaryWhere);
485c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement = db.compileStatement(
486c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                "UPDATE " + Tables.DATA + " SET " + Data.IS_SUPER_PRIMARY
487c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                + "=(_id=?) WHERE " + sSetSuperPrimaryWhere);
488f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
489f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                + Contacts.TIMES_CONTACTED + "=" + Contacts.TIMES_CONTACTED + "+1,"
490f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                + Contacts.LAST_TIME_CONTACTED + "=? WHERE " + Contacts.AGGREGATE_ID + "=?");
491a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
49228f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar        mNameSplitter = new NameSplitter(
49328f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_prefixes),
49428f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_last_name_prefixes),
49528f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_suffixes),
49628f8857b1b46bde18b85c6d3c2a63ac44c3c2e1cEvan Millar                context.getString(com.android.internal.R.string.common_name_conjunctions));
4974097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
498f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLegacyApiSupport = new LegacyApiSupport(context, mOpenHelper, this);
4991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return (db != null);
5004f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
5014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
50231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    /* Visible for testing */
50331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    protected OpenHelper getOpenHelper(final Context context) {
50431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov        return OpenHelper.getInstance(context);
50531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov    }
50631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
507a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
508a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    protected void finalize() throws Throwable {
509a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        if (mContactAggregator != null) {
510a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            mContactAggregator.quit();
511a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        }
512a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
513a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        super.finalize();
514a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
515a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
516a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
517a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     * Wipes all data from the contacts database.
518a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov     */
519a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /* package */ void wipeData() {
520a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        mOpenHelper.wipeData();
521a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    }
522a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
523a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
524a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Called when a change has been made.
525a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
526a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param uri the uri that the change was made to
527a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
528a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    private void onChange(Uri uri) {
529a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        getContext().getContentResolver().notifyChange(ContactsContract.AUTHORITY_URI, null);
530a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
531a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5324f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
5334f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public boolean isTemporary() {
5344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return false;
5354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
5364f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
5374f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
5384f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Uri insert(Uri uri, ContentValues values) {
539a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
540a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
54135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
542a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        switch (match) {
54335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
54435ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                id = mOpenHelper.getSyncState().insert(mOpenHelper.getWritableDatabase(), values);
54535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                break;
54635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
5476bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
548f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                insertAggregate(values);
5496bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
5506bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
5516bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
552a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS: {
553f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                final Account account = readAccountFromQueryParams(uri);
554f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                id = insertContact(values, account);
555a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
556a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
557a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
558a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
559a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                values.put(Data.CONTACT_ID, uri.getPathSegments().get(1));
560035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
561a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
562a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
563a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
564a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case DATA: {
565035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                id = insertData(values);
566a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
567a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
568a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
569ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
570ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final Account account = readAccountFromQueryParams(uri);
571ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                id = insertGroup(values, account);
572ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
573ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
574ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
5751f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
5761f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                id = insertPresence(values);
5771f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
5781f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
5791f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
580a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            default:
581f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.insert(uri, values);
582a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
583a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
5847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        if (id < 0) {
5857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return null;
5867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
5877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
5887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final Uri result = ContentUris.withAppendedId(uri, id);
589a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        onChange(result);
590a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return result;
591a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
592a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
593a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
594035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * If account is non-null then store it in the values. If the account is already
595035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * specified in the values then it must be consistent with the account, if it is non-null.
596035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param values the ContentValues to read from and update
597035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @param account the explicitly provided Account
598035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana     * @return false if the accounts are inconsistent
5997e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
600035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private boolean resolveAccount(ContentValues values, Account account) {
601035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        // If either is specified then both must be specified.
602035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountName = values.getAsString(Contacts.ACCOUNT_NAME);
603035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String accountType = values.getAsString(Contacts.ACCOUNT_TYPE);
604035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (!TextUtils.isEmpty(accountName) || !TextUtils.isEmpty(accountType)) {
605035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final Account valuesAccount = new Account(accountName, accountType);
606035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (account != null && !valuesAccount.equals(account)) {
607035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                return false;
608035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
609035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            account = valuesAccount;
610035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
611035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        if (account != null) {
612035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_NAME, account.mName);
613035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            values.put(Contacts.ACCOUNT_TYPE, account.mType);
614035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        }
615035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        return true;
6167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
6177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
6187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
6196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * Inserts an item in the aggregates table
6206bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     *
6216bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @param values the values for the new row
6226bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     * @return the row ID of the newly created row
6236bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov     */
6246bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    private long insertAggregate(ContentValues values) {
625a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        throw new UnsupportedOperationException("Aggregates are created automatically");
6266bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    }
6276bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
6286bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov    /**
629a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the contacts table
630a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
631a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
632f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana     * @param account the account this contact should be associated with. may be null.
633a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
634a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
635f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private long insertContact(ContentValues values, Account account) {
6366bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov        /*
6376bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * The contact record is inserted in the contacts table, but it needs to
6386bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * be processed by the aggregator before it will be returned by the
6396bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         * "aggregates" queries.
6406bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov         */
641a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
6426bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
643a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        ContentValues overriddenValues = new ContentValues(values);
644a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov        overriddenValues.putNull(Contacts.AGGREGATE_ID);
645f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (!resolveAccount(overriddenValues, account)) {
6467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return -1;
6477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
6487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
649619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Replace package with internal mapping
650619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String packageName = overriddenValues.getAsString(Contacts.PACKAGE);
651619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        overriddenValues.put(ContactsColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
652619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        overriddenValues.remove(Contacts.PACKAGE);
653619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
654f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        long contactId = db.insert(Tables.CONTACTS, Contacts.AGGREGATE_ID, overriddenValues);
655f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
656f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        int aggregationMode = Contacts.AGGREGATION_MODE_DEFAULT;
657f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        if (values.containsKey(Contacts.AGGREGATION_MODE)) {
658f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            aggregationMode = values.getAsInteger(Contacts.AGGREGATION_MODE);
659f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
660a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
661f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        triggerAggregation(contactId, aggregationMode);
662a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
663f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        return contactId;
664a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    }
665a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
666a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton    /**
667a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * Inserts an item in the data table
668a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     *
669a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @param values the values for the new row
670a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     * @return the row ID of the newly created row
671a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton     */
672035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana    private long insertData(ContentValues values) {
673f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        int aggregationMode = Contacts.AGGREGATION_MODE_DISABLED;
674a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
675a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
676a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        long id = 0;
677a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        db.beginTransaction();
678a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        try {
679a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov            long contactId = values.getAsLong(Data.CONTACT_ID);
680a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
681619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            // Replace mimetype with internal mapping
682508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            final String mimeType = values.getAsString(Data.MIMETYPE);
683b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            values.put(DataColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType));
684508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            values.remove(Data.MIMETYPE);
685508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
6864097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
6874097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                parseStructuredName(values);
6889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else if (GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
6899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
6909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
6919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (containsGroupSourceId && containsGroupId) {
6929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalArgumentException(
6939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            "you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
6949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                                    + "and GroupMembership.GROUP_ROW_ID");
6959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
6969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
6979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!containsGroupSourceId && !containsGroupId) {
6989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalArgumentException(
6999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            "you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
7009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                                    + "and GroupMembership.GROUP_ROW_ID");
7019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
7029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
7039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (containsGroupSourceId) {
7049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
7059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    final long groupId = getOrMakeGroup(db, contactId, sourceId);
7069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    values.remove(GroupMembership.GROUP_SOURCE_ID);
7079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    values.put(GroupMembership.GROUP_ROW_ID, groupId);
7089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
7094097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            }
7104097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
711508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            // Insert the data row itself
712b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey            id = db.insert(Tables.DATA, Data.DATA1, values);
713508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
714a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            // If it's a phone number add the normalized version to the lookup table
7154097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
716508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                final ContentValues phoneValues = new ContentValues();
717508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                final String number = values.getAsString(Phone.NUMBER);
718508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                phoneValues.put(PhoneLookupColumns.NORMALIZED_NUMBER,
719508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                        PhoneNumberUtils.getStrippedReversed(number));
720508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                phoneValues.put(PhoneLookupColumns.DATA_ID, id);
721a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov                phoneValues.put(PhoneLookupColumns.CONTACT_ID, contactId);
722b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                db.insert(Tables.PHONE_LOOKUP, null, phoneValues);
723a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
724a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
725f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            aggregationMode = mContactAggregator.markContactForAggregation(contactId);
726a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
727a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.setTransactionSuccessful();
728a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        } finally {
729a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            db.endTransaction();
730a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        }
731a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov
732f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        triggerAggregation(id, aggregationMode);
733a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        return id;
7344f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
7354f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
736f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    private void triggerAggregation(long contactId, int aggregationMode) {
737f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        switch (aggregationMode) {
738f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            case Contacts.AGGREGATION_MODE_DEFAULT:
739f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.schedule();
740f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
741f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
742f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            case Contacts.AGGREGATION_MODE_IMMEDITATE:
743f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.aggregateContact(contactId);
744f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
745f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
746f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            case Contacts.AGGREGATION_MODE_DISABLED:
747f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                // Do nothing
748f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                break;
749f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        }
750f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
751f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov
752a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    /**
7539261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * Returns the group id of the group with sourceId and the same account as contactId.
7549261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * If the group doesn't already exist then it is first created,
7559261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param db SQLiteDatabase to use for this operation
7569261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param contactId the contact this group is associated with
7579261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @param sourceId the sourceIf of the group to query or create
7589261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @return the group id of the existing or created group
7599261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalArgumentException if the contact is not associated with an account
7609261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     * @throws IllegalStateException if a group needs to be created but the creation failed
7619261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana     */
7629261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    private long getOrMakeGroup(SQLiteDatabase db, long contactId, String sourceId) {
7639261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Account account = null;
7649261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        long contactPackage = -1;
7659261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        Cursor c = db.query(Tables.CONTACTS,
7669261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Projections.CONTACTS_ACCOUNT_AND_PACKAGE,
7679261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Contacts._ID + "=" + contactId, null, null, null, null);
7689261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
7699261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
7709261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                final String accountName =
7719261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                        c.getString(Projections.CONTACTS_ACCOUNT_AND_PACKAGE_COL_ACCOUNT_NAME);
7729261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                final String accountType =
7739261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                        c.getString(Projections.CONTACTS_ACCOUNT_AND_PACKAGE_COL_ACCOUNT_TYPE);
7749261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
7759261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    account = new Account(accountName, accountType);
7769261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
7779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                contactPackage = c.getLong(Projections.CONTACTS_ACCOUNT_AND_PACKAGE_COL_PACKAGE_ID);
7789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
7799261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
7809261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
7819261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
7829261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        if (account == null) {
7839261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            throw new IllegalArgumentException("if the groupmembership only "
7849261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "has a sourceid the the contact must be associate with "
7859261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    + "an account");
7869261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
7879261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
7889261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // look up the group that contains this sourceId and has the same account name and type
7899261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        // as the contact refered to by contactId
7909261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        c = db.query(Tables.GROUPS, new String[]{Contacts._ID},
7919261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
7929261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                new String[]{sourceId, account.mName, account.mType}, null, null, null);
7939261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        try {
7949261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            if (c.moveToNext()) {
7959261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return c.getLong(0);
7969261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            } else {
7979261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                ContentValues groupValues = new ContentValues();
7989261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.PACKAGE_ID, contactPackage);
7999261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.ACCOUNT_NAME, account.mName);
8009261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.ACCOUNT_TYPE, account.mType);
8019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                groupValues.put(Groups.SOURCE_ID, sourceId);
8029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
8039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (groupId < 0) {
8049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    throw new IllegalStateException("unable to create a new group with "
8059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            + "this sourceid: " + groupValues);
8069261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
8079261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                return groupId;
8089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            }
8099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        } finally {
8109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            c.close();
8119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        }
8129261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    }
8139261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana
8149261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana    /**
815ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Delete the given {@link Data} row, fixing up any {@link Aggregates}
816ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * primaries that reference it.
817ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
818ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private int deleteData(long dataId) {
819ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
820ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
821ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimePhone = mOpenHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
822ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final long mimeEmail = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
823ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
824ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Check to see if the data about to be deleted was a super-primary on
825ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // the parent aggregate, and set flags to fix-up once deleted.
826ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long aggId = -1;
827ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        long mimeId = -1;
828ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        String dataRaw = null;
829ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixOptimal = false;
830ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        boolean fixFallback = false;
831ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
832ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor cursor = null;
833ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        try {
834ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES,
835ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Projections.PROJ_DATA_AGGREGATES, DataColumns.CONCRETE_ID + "=" + dataId, null,
836ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    null, null, null);
837ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor.moveToFirst()) {
838ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                aggId = cursor.getLong(Projections.COL_AGGREGATE_ID);
839ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mimeId = cursor.getLong(Projections.COL_MIMETYPE_ID);
840ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
841ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    dataRaw = cursor.getString(Projections.COL_PHONE_NUMBER);
842ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixOptimal = (cursor.getLong(Projections.COL_OPTIMAL_PHONE_ID) == dataId);
843ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixFallback = (cursor.getLong(Projections.COL_FALLBACK_PHONE_ID) == dataId);
844ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
845ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    dataRaw = cursor.getString(Projections.COL_EMAIL_DATA);
846ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixOptimal = (cursor.getLong(Projections.COL_OPTIMAL_EMAIL_ID) == dataId);
847ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    fixFallback = (cursor.getLong(Projections.COL_FALLBACK_EMAIL_ID) == dataId);
848ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
849ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
850ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        } finally {
851ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (cursor != null) {
852ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.close();
853ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor = null;
854ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
855ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
856ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
857ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Delete the requested data item.
858ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        int dataDeleted = db.delete(Tables.DATA, Data._ID + "=" + dataId, null);
859ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
860ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Fix-up any super-primary values that are now invalid.
861ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (fixOptimal || fixFallback) {
862ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final ContentValues values = new ContentValues();
863ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final StringBuilder scoreClause = new StringBuilder();
864ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
865ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String SCORE = "score";
866ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
867ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Build scoring clause that will first pick data items under the
868ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // same aggregate that have identical values, otherwise fall back to
869ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // normal primary scoring from the member contacts.
870ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("(CASE WHEN ");
871ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (mimeId == mimePhone) {
872ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Phone.NUMBER);
873ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            } else if (mimeId == mimeEmail) {
874ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                scoreClause.append(Email.DATA);
875ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
876ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append("=");
877ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            DatabaseUtils.appendEscapedSQLString(scoreClause, dataRaw);
878ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            scoreClause.append(" THEN 2 ELSE " + Data.IS_PRIMARY + " END) AS " + SCORE);
879ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
880ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final String[] PROJ_PRIMARY = new String[] {
881ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    DataColumns.CONCRETE_ID,
882ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Contacts.IS_RESTRICTED,
883ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    ContactsColumns.PACKAGE_ID,
884ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    scoreClause.toString(),
885ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            };
886ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
887ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_DATA_ID = 0;
888ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_IS_RESTRICTED = 1;
889ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_PACKAGE_ID = 2;
890ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            final int COL_SCORE = 3;
891ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
892ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_AGGREGATES, PROJ_PRIMARY,
893ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND " + DataColumns.MIMETYPE_ID
894ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                            + "=" + mimeId, null, null, null, SCORE);
895ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
896ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixOptimal) {
897ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
898ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colPackageId = null;
899ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
900ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID;
901ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colPackageId = AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID;
902ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
903ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID;
904ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colPackageId = AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID;
905ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
906ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
907ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixOptimal told us that
908ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
909ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
910ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colPackageId);
911ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
912ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // When finding a new optimal primary, we only care about the
913ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // highest scoring value, regardless of source.
914ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (cursor.moveToFirst()) {
915ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final long newOptimal = cursor.getLong(COL_DATA_ID);
916ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final long newOptimalPackage = cursor.getLong(COL_PACKAGE_ID);
917ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
918ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (newOptimal != 0) {
919ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, newOptimal);
920ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
921ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (newOptimalPackage != 0) {
922ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colPackageId, newOptimalPackage);
923ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
924ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
925ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
926ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
927ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (fixFallback) {
928ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String colId = null;
929ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (mimeId == mimePhone) {
930ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID;
931ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                } else if (mimeId == mimeEmail) {
932ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    colId = AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID;
933ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
934ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
935ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // Start by replacing with null, since fixFallback told us that
936ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // the previous aggregate values are bad.
937ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                values.putNull(colId);
938ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
939ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // The best fallback value is the highest scoring data item that
940ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // hasn't been restricted.
941ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                cursor.moveToPosition(-1);
942ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                while (cursor.moveToNext()) {
943ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    final boolean isRestricted = (cursor.getInt(COL_IS_RESTRICTED) == 1);
944ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    if (!isRestricted) {
945ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        values.put(colId, cursor.getLong(COL_DATA_ID));
946ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        break;
947ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    }
948ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
949ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
950ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
951ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            // Push through any aggregate updates we have
952ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            if (values.size() > 0) {
953ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                db.update(Tables.AGGREGATES, values, AggregatesColumns.CONCRETE_ID + "=" + aggId,
954ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        null);
955ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
956ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
957ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
958ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return dataDeleted;
959ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
960ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
961ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
9624097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov     * Parse the supplied display name, but only if the incoming values do not already contain
9634097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov     * structured name parts.
9644097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov     */
9654097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    private void parseStructuredName(ContentValues values) {
9664097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        final String fullName = values.getAsString(StructuredName.DISPLAY_NAME);
9674097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        if (TextUtils.isEmpty(fullName)
9684097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.PREFIX))
9694097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.GIVEN_NAME))
9704097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.MIDDLE_NAME))
9714097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.FAMILY_NAME))
9724097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov                || !TextUtils.isEmpty(values.getAsString(StructuredName.SUFFIX))) {
9734097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov            return;
9744097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        }
9754097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
9764097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        NameSplitter.Name name = new NameSplitter.Name();
9774097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        mNameSplitter.split(name, fullName);
9784097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
9794097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.PREFIX, name.getPrefix());
9804097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.GIVEN_NAME, name.getGivenNames());
9814097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.MIDDLE_NAME, name.getMiddleName());
9824097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.FAMILY_NAME, name.getFamilyName());
9834097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov        values.put(StructuredName.SUFFIX, name.getSuffix());
9844097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    }
9854097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov
9864097855e2d672b3f8e1c5c8a169efb80203bf53eDmitri Plotnikov    /**
987ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     * Inserts an item in the groups table
988ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey     */
989ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    private long insertGroup(ContentValues values, Account account) {
990ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
991ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
992ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        ContentValues overriddenValues = new ContentValues(values);
993ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        if (!resolveAccount(overriddenValues, account)) {
994ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            return -1;
995ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        }
996ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
997ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        // Replace package with internal mapping
998ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        final String packageName = overriddenValues.getAsString(Groups.PACKAGE);
999ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        overriddenValues.put(Groups.PACKAGE_ID, mOpenHelper.getPackageId(packageName));
1000ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        overriddenValues.remove(Groups.PACKAGE);
1001ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1002ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        return db.insert(Tables.GROUPS, Groups.TITLE, overriddenValues);
1003ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    }
1004ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1005ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey    /**
10061f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     * Inserts a presence update.
10071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey     */
10081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    private long insertPresence(ContentValues values) {
10091f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
10101f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String handle = values.getAsString(Presence.IM_HANDLE);
10111f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final String protocol = values.getAsString(Presence.IM_PROTOCOL);
10121f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (TextUtils.isEmpty(handle) || TextUtils.isEmpty(protocol)) {
10131f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            throw new IllegalArgumentException("IM_PROTOCOL and IM_HANDLE are required");
10141f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
10151f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
10161f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // TODO: generalize to allow other providers to match against email
10171f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        boolean matchEmail = GTALK_PROTOCOL_STRING.equals(protocol);
10181f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
10191f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String selection;
10201f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String[] selectionArgs;
10211f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        if (matchEmail) {
10221f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selection = "(" + Clauses.WHERE_IM_MATCHES + ") OR (" + Clauses.WHERE_EMAIL_MATCHES + ")";
10231f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle, handle };
10241f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } else {
10251f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selection = Clauses.WHERE_IM_MATCHES;
10261f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            selectionArgs = new String[] { protocol, handle };
10271f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
10281f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
10291f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long dataId = -1;
10301f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long aggId = -1;
10311f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        Cursor cursor = null;
10321f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        try {
1033ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES,
1034ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Projections.PROJ_DATA_CONTACTS, selection, selectionArgs, null, null, null);
10351f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            if (cursor.moveToFirst()) {
1036ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                dataId = cursor.getLong(Projections.COL_DATA_ID);
1037ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                aggId = cursor.getLong(Projections.COL_AGGREGATE_ID);
10381f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            } else {
10391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                // No contact found, return a null URI
10401f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return -1;
10411f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
10421f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        } finally {
104331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            if (cursor != null) {
104431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                cursor.close();
104531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
10461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        }
10471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
10481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.DATA_ID, dataId);
10491f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        values.put(Presence.AGGREGATE_ID, aggId);
10501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
10511f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        // Insert the presence update
10521f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        long presenceId = db.replace(Tables.PRESENCE, null, values);
10531f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        return presenceId;
10541f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey    }
10551f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
10564f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
10574f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int delete(Uri uri, String selection, String[] selectionArgs) {
1058508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1059508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        final int match = sUriMatcher.match(uri);
1060508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        switch (match) {
106135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
106235ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().delete(db, selection, selectionArgs);
106335ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
10646bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: {
10656bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                long aggregateId = ContentUris.parseId(uri);
10666bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
10676bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                // Remove references to the aggregate first
10686bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                ContentValues values = new ContentValues();
10696bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                values.putNull(Contacts.AGGREGATE_ID);
1070b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                db.update(Tables.CONTACTS, values, Contacts.AGGREGATE_ID + "=" + aggregateId, null);
10716bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1072b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return db.delete(Tables.AGGREGATES, BaseColumns._ID + "=" + aggregateId, null);
10736bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
10746bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
1075508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case CONTACTS_ID: {
1076508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long contactId = ContentUris.parseId(uri);
1077b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int contactsDeleted = db.delete(Tables.CONTACTS, Contacts._ID + "=" + contactId, null);
1078b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, Data.CONTACT_ID + "=" + contactId, null);
1079508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                return contactsDeleted + dataDeleted;
1080508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
1081508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
1082508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID: {
1083508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
1084ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return deleteData(dataId);
1085ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1086ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1087ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1088ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1089ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                final long groupMembershipMimetypeId = mOpenHelper
1090ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
1091ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int groupsDeleted = db.delete(Tables.GROUPS, Groups._ID + "=" + groupId, null);
1092ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                int dataDeleted = db.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "="
1093ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "="
1094ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + groupId, null);
1095ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1096ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                return groupsDeleted + dataDeleted;
1097508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            }
1098508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey
10991f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case PRESENCE: {
11001f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                return db.delete(Tables.PRESENCE, null, null);
11011f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
11021f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1103508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            default:
1104508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                throw new UnsupportedOperationException("Unknown uri: " + uri);
1105508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey        }
11064f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
11074f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1108f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    private static Account readAccountFromQueryParams(Uri uri) {
1109035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String name = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
1110035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        final String type = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
1111f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(type)) {
1112f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            return null;
1113f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        }
1114f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana        return new Account(name, type);
1115f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana    }
1116f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana
1117ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
11184f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
11194f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
112000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
112135ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana        int count = 0;
112200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
112300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        final int match = sUriMatcher.match(uri);
112400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        switch(match) {
112535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
112635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().update(db, values, selection, selectionArgs);
112735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1128c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            // TODO(emillar): We will want to disallow editing the aggregates table at some point.
112900d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES: {
113000d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                count = db.update(Tables.AGGREGATES, values, selection, selectionArgs);
113100d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
113200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
113300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
113400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            case AGGREGATES_ID: {
1135d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov                count = updateAggregateData(db, ContentUris.parseId(uri), values);
1136c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                break;
1137c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            }
1138c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1139c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar            case DATA_ID: {
1140c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                boolean containsIsSuperPrimary = values.containsKey(Data.IS_SUPER_PRIMARY);
1141c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                boolean containsIsPrimary = values.containsKey(Data.IS_PRIMARY);
1142c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                final long id = ContentUris.parseId(uri);
1143c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1144c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                // Remove primary or super primary values being set to 0. This is disallowed by the
1145c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                // content provider.
11468b341f8ea85257c5f7103863405e0273921e16bcEvan Millar                if (containsIsSuperPrimary && values.getAsInteger(Data.IS_SUPER_PRIMARY) == 0) {
1147c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    containsIsSuperPrimary = false;
1148c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_SUPER_PRIMARY);
1149c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
11508b341f8ea85257c5f7103863405e0273921e16bcEvan Millar                if (containsIsPrimary && values.getAsInteger(Data.IS_PRIMARY) == 0) {
1151c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    containsIsPrimary = false;
1152c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_PRIMARY);
1153c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
1154c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1155c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                if (containsIsSuperPrimary) {
1156c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsSuperPrimary(id);
1157c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsPrimary(id);
1158c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1159c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    // Now that we've taken care of setting these, remove them from "values".
1160c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_SUPER_PRIMARY);
1161c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    if (containsIsPrimary) {
1162c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                        values.remove(Data.IS_PRIMARY);
1163c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    }
1164c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                } else if (containsIsPrimary) {
1165c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    setIsPrimary(id);
1166c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1167c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    // Now that we've taken care of setting this, remove it from "values".
1168c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    values.remove(Data.IS_PRIMARY);
1169c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
1170c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1171c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                if (values.size() > 0) {
1172c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    String selectionWithId = (Data._ID + " = " + ContentUris.parseId(uri) + " ")
1173f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                            + (selection == null ? "" : " AND " + selection);
1174c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                    count = db.update(Tables.DATA, values, selectionWithId, selectionArgs);
1175c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar                }
117600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar                break;
117700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            }
11787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
11797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS: {
11807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selection, selectionArgs);
11817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
11827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
11837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
11847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID: {
11857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selectionWithId = (Contacts._ID + " = " + ContentUris.parseId(uri) + " ")
11867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        + (selection == null ? "" : " AND " + selection);
11877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.CONTACTS, values, selectionWithId, selectionArgs);
11887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                Log.i(TAG, "Selection is: " + selectionWithId);
11897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
11907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
11917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
11927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case DATA: {
11937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                count = db.update(Tables.DATA, values, selection, selectionArgs);
11947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                break;
11957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
11967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1197ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1198ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selection, selectionArgs);
1199ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mOpenHelper.updateAllVisible();
1200ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1201ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1202ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1203ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1204ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1205ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                String selectionWithId = (Groups._ID + "=" + groupId + " ")
1206ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                        + (selection == null ? "" : " AND " + selection);
1207ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                count = db.update(Tables.GROUPS, values, selectionWithId, selectionArgs);
1208ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1209ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                // If changing visibility, then update aggregates
1210ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                if (values.containsKey(Groups.GROUP_VISIBLE)) {
1211ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    mOpenHelper.updateAllVisible();
1212ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                }
1213ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1214ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1215ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1216ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1217127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
1218127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                count = updateAggregationException(db, values);
1219b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
1220b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1221b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1222619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case RESTRICTION_EXCEPTIONS: {
1223619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Enforce required fields
1224619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                boolean hasFields = values.containsKey(RestrictionExceptions.PACKAGE_PROVIDER)
1225619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        && values.containsKey(RestrictionExceptions.PACKAGE_CLIENT)
1226619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        && values.containsKey(RestrictionExceptions.ALLOW_ACCESS);
1227619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (!hasFields) {
1228619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    throw new IllegalArgumentException("PACKAGE_PROVIDER, PACKAGE_CLIENT, and"
1229619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                            + "ALLOW_ACCESS are all required fields");
1230619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1231619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1232619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final String packageProvider = values
1233619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        .getAsString(RestrictionExceptions.PACKAGE_PROVIDER);
1234619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final boolean allowAccess = (values
1235619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        .getAsInteger(RestrictionExceptions.ALLOW_ACCESS) == 1);
1236619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1237619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final Context context = getContext();
1238619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final PackageManager pm = context.getPackageManager();
1239619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1240619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Enforce that caller has authority over the requested package
1241619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: move back to Binder.getCallingUid() when we can stub-out test suite
1242619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final int callingUid = OpenHelper
1243619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        .getUidForPackageName(pm, context.getPackageName());
1244619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                final String[] ownedPackages = pm.getPackagesForUid(callingUid);
1245619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (!isContained(ownedPackages, packageProvider)) {
1246619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    throw new RuntimeException(
1247619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                            "Requested PACKAGE_PROVIDER doesn't belong to calling UID.");
1248619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1249619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1250619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // Add or remove exception using exception helper
1251619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                if (allowAccess) {
1252619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    mOpenHelper.addRestrictionException(context, values);
1253619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                } else {
1254619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    mOpenHelper.removeRestrictionException(context, values);
1255619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                }
1256619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1257619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1258619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1259619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
12607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
1261f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.update(uri, values, selection, selectionArgs);
126200d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
126300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
126400d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        if (count > 0) {
126500d71133c63c882fb41729ddc3a52f66fb155374Evan Millar            getContext().getContentResolver().notifyChange(uri, null);
126600d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        }
126700d71133c63c882fb41729ddc3a52f66fb155374Evan Millar        return count;
12684f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
12694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1270d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    private int updateAggregateData(SQLiteDatabase db, long aggregateId, ContentValues values) {
1271d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1272d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // First update all constituent contacts
1273f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        ContentValues optionValues = new ContentValues(5);
1274f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyStringValue(optionValues, Contacts.CUSTOM_RINGTONE,
1275f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.CUSTOM_RINGTONE);
1276f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.SEND_TO_VOICEMAIL,
1277f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.SEND_TO_VOICEMAIL);
1278f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.LAST_TIME_CONTACTED,
1279f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.LAST_TIME_CONTACTED);
1280f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.TIMES_CONTACTED,
1281f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.TIMES_CONTACTED);
1282f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        OpenHelper.copyLongValue(optionValues, Contacts.STARRED,
1283f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                values, Aggregates.STARRED);
1284d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1285d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        // Nothing to update - just return
1286d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (optionValues.size() == 0) {
1287d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return 0;
1288d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
1289d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1290f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        db.update(Tables.CONTACTS, optionValues, Contacts.AGGREGATE_ID + "=" + aggregateId, null);
1291f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        return db.update(Tables.AGGREGATES, values, Aggregates._ID + "=" + aggregateId, null);
1292f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    }
1293d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1294f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov    public void updateContactTime(long aggregateId, long lastTimeContacted) {
1295f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(1, lastTimeContacted);
1296f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.bindLong(2, aggregateId);
1297f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        mLastTimeContactedUpdate.execute();
1298d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov    }
1299d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov
1300127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private static class ContactPair {
1301127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId1;
1302127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        final long contactId2;
1303127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1304127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        /**
1305127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         * Constructor that ensures that this.contactId1 &lt; this.contactId2
1306127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov         */
1307127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        public ContactPair(long contactId1, long contactId2) {
1308127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (contactId1 < contactId2) {
1309127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId1;
1310127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId2;
1311127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1312127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId2 = contactId1;
1313127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                this.contactId1 = contactId2;
1314127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1315127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1316127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    }
131780c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1318127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov    private int updateAggregationException(SQLiteDatabase db, ContentValues values) {
1319127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        int exceptionType = values.getAsInteger(AggregationExceptions.TYPE);
1320127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long aggregateId = values.getAsInteger(AggregationExceptions.AGGREGATE_ID);
1321127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        long contactId = values.getAsInteger(AggregationExceptions.CONTACT_ID);
132280c457131bd22afe34828d1a5d15e90bb5f43375Dmitri Plotnikov
1323127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // First, we build a list of contactID-contactID pairs for the given aggregate and contact.
1324127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ArrayList<ContactPair> pairs = new ArrayList<ContactPair>();
1325ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey        Cursor c = db.query(Tables.CONTACTS, Projections.PROJ_CONTACTS,
1326127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                Contacts.AGGREGATE_ID + "=" + aggregateId,
1327127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                null, null, null, null);
1328127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        try {
1329127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            while (c.moveToNext()) {
1330ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long aggregatedContactId = c.getLong(Projections.COL_CONTACT_ID);
1331e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                if (aggregatedContactId != contactId) {
1332e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                    pairs.add(new ContactPair(aggregatedContactId, contactId));
1333e2e0ba75ce239f0f5481cdef9082daebf8fc2d35Dmitri Plotnikov                }
1334b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1335b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        } finally {
1336b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            c.close();
1337b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov        }
1338127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1339127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // Now we iterate through all contact pairs to see if we need to insert/delete/update
1340127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // the corresponding exception
1341127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        ContentValues exceptionValues = new ContentValues(3);
1342127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        exceptionValues.put(AggregationExceptions.TYPE, exceptionType);
1343127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        for (ContactPair pair : pairs) {
1344127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            final String whereClause =
1345127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    AggregationExceptionColumns.CONTACT_ID1 + "=" + pair.contactId1 + " AND "
1346127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                    + AggregationExceptionColumns.CONTACT_ID2 + "=" + pair.contactId2;
1347127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC) {
1348127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.delete(Tables.AGGREGATION_EXCEPTIONS, whereClause, null);
1349127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            } else {
1350127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID1, pair.contactId1);
1351127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                exceptionValues.put(AggregationExceptionColumns.CONTACT_ID2, pair.contactId2);
1352127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                db.replace(Tables.AGGREGATION_EXCEPTIONS, AggregationExceptions._ID,
1353127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                        exceptionValues);
1354127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov            }
1355127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        }
1356127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1357f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        int aggregationMode = mContactAggregator.markContactForAggregation(contactId);
1358f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov        if (aggregationMode != Contacts.AGGREGATION_MODE_DISABLED) {
1359f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            mContactAggregator.aggregateContact(db, contactId);
1360f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            if (exceptionType == AggregationExceptions.TYPE_AUTOMATIC
1361f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                    || exceptionType == AggregationExceptions.TYPE_KEEP_OUT) {
1362f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                mContactAggregator.updateAggregateData(aggregateId);
1363f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov            }
13647a39bf269294a8130ddd463460b9b36cf4ff74a8Dmitri Plotnikov        }
1365127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov
1366127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // The return value is fake - we just confirm that we made a change, not count actual
1367127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        // rows changed.
1368127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov        return 1;
1369b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    }
1370b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
1371619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1372619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list.
1373619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1374619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private boolean isContained(String[] array, String value) {
1375bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array != null) {
1376bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            for (String test : array) {
1377bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                if (value.equals(test)) {
1378bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                    return true;
1379bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                }
1380619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1381619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1382619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return false;
1383619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1384619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1385619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1386619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Test if a {@link String} value appears in the given list, and add to the
1387619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * array if the value doesn't already appear.
1388619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1389619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private String[] assertContained(String[] array, String value) {
1390bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        if (array == null) {
1391bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar            array = new String[] {value};
1392bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        } else if (!isContained(array, value)) {
1393619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            String[] newArray = new String[array.length + 1];
1394619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            System.arraycopy(array, 0, newArray, 0, array.length);
1395619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            newArray[array.length] = value;
1396619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            array = newArray;
1397619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1398619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        return array;
1399619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1400619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
14014f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
14024f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
14034f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            String sortOrder) {
14044f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
140535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
1406d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
14071f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String groupBy = null;
14081f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        String limit = null;
1409bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar        String aggregateIdColName = Tables.AGGREGATES + "." + Aggregates._ID;
14104f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1411619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: Consider writing a test case for RestrictionExceptions when you
1412619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // write a new query() block to make sure it protects restricted data.
1413a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
14144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
141535ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana            case SYNCSTATE:
141635ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                return mOpenHelper.getSyncState().query(db, projection, selection,  selectionArgs,
141735ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana                        sortOrder);
141835ed95769096bb5dd406ad7d1fcaa49a0e35a307Fred Quintana
14196bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: {
1420b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1421619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1422619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
1423619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setProjectionMap(sAggregatesProjectionMap);
1424619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1425619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1426619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1427619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case AGGREGATES_ID: {
1428619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = ContentUris.parseId(uri);
1429619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setTables(Tables.AGGREGATES);
1430ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1431619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1432619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesProjectionMap);
14336bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                qb.setProjectionMap(sAggregatesProjectionMap);
14346bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
14356bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
14366bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov
14371f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY: {
1438619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
14391f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1440619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1441619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1442619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
14431f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1444bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
14451f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
14461f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
14471f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
14481f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            case AGGREGATES_SUMMARY_ID: {
1449619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: join into social status tables
14501f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                long aggId = ContentUris.parseId(uri);
14511f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1452ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(AggregatesColumns.CONCRETE_ID + "=" + aggId + " AND ");
1453619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregateRestrictionExceptions(qb);
1454619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1455619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
14561f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1457bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
14581f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey                break;
14591f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey            }
14601f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey
1461ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case AGGREGATES_SUMMARY_FILTER: {
1462619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1463ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1464ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1465ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
1466ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1467ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
1468bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy = aggregateIdColName;
1469ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1470ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1471ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1472d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT_FILTER:
1473d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            case AGGREGATES_SUMMARY_STREQUENT: {
1474d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the first query for starred
1475d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1476d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1477d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
1478d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
1479d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1480d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1481d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String starredQuery = qb.buildQuery(projection, Aggregates.STARRED + "=1",
1482bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null,
1483d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        null /* limit */);
1484d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1485d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Build the second query for frequent
1486d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb = new SQLiteQueryBuilder();
1487d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1488d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1489d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if (match == AGGREGATES_SUMMARY_STREQUENT_FILTER
1490d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        && uri.getPathSegments().size() > 3) {
1491d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    qb.appendWhere(buildAggregateLookupWhereClause(uri.getLastPathSegment()));
1492d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1493d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String frequentQuery = qb.buildQuery(projection,
1494d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Aggregates.TIMES_CONTACTED + " > 0 AND (" + Aggregates.STARRED
1495d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        + " = 0 OR " + Aggregates.STARRED + " IS NULL)",
1496bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                        null, aggregateIdColName, null, null, null);
1497d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1498d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                // Put them together
1499d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                final String query = qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
1500d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        STREQUENT_ORDER_BY, STREQUENT_LIMIT);
1501d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                Cursor c = db.rawQueryWithFactory(null, query, null,
1502d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                        Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1503d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1504d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                if ((c != null) && !isTemporary()) {
1505d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                    c.setNotificationUri(getContext().getContentResolver(),
1506d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                            ContactsContract.AUTHORITY_URI);
1507d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                }
1508d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar                return c;
1509d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar            }
1510d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar
1511b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            case AGGREGATES_SUMMARY_GROUP: {
1512b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                qb.setTables(Tables.AGGREGATES_JOIN_PRESENCE_PRIMARY_PHONE);
1513b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                applyAggregateRestrictionExceptions(qb);
1514b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                applyAggregatePrimaryRestrictionExceptions(sAggregatesSummaryProjectionMap);
1515b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                projection = assertContained(projection, Aggregates.PRIMARY_PHONE_ID);
1516b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                qb.setProjectionMap(sAggregatesSummaryProjectionMap);
1517b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                if (uri.getPathSegments().size() > 2) {
1518b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                    qb.appendWhere(" AND " + sAggregatesInGroupSelect);
1519b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                    selectionArgs = appendGroupArg(selectionArgs, uri.getLastPathSegment());
1520b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                }
1521b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                groupBy = aggregateIdColName;
1522b67163a1088f09c59f324350662eb18772fac6b6Evan Millar                break;
1523b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            }
1524b67163a1088f09c59f324350662eb18772fac6b6Evan Millar
15256bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_DATA: {
1526619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long aggId = Long.parseLong(uri.getPathSegments().get(1));
15279261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES_GROUPS);
15289261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataContactsGroupsAggregateProjectionMap);
1529619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Contacts.AGGREGATE_ID + "=" + aggId + " AND ");
1530619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
15316bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                break;
15326bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            }
153300d71133c63c882fb41729ddc3a52f66fb155374Evan Millar
1534ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES_FILTER: {
1535ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1536ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1537ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
1538ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                if (uri.getPathSegments().size() > 2) {
1539ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                    qb.appendWhere(" AND " + buildAggregateLookupWhereClause(
1540ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                            uri.getLastPathSegment()));
1541ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                }
1542ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1543ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1544ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1545ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case PHONES: {
1546ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1547ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1548ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = \"" + Phone.CONTENT_ITEM_TYPE + "\"");
1549ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1550ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1551ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
1552ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            case POSTALS: {
1553ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1554ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.setProjectionMap(sDataContactsAggregateProjectionMap);
1555ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                qb.appendWhere(Data.MIMETYPE + " = \"" + Postal.CONTENT_ITEM_TYPE + "\"");
1556ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar                break;
1557ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar            }
1558ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
15594f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: {
1560035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.setTables(Tables.CONTACTS_JOIN_PACKAGES);
15614f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
1562619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
15634f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
15644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
15654f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
15664f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: {
1567619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = ContentUris.parseId(uri);
1568035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.setTables(Tables.CONTACTS_JOIN_PACKAGES);
15694f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                qb.setProjectionMap(sContactsProjectionMap);
1570ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(ContactsColumns.CONCRETE_ID + "=" + contactId + " AND ");
1571619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyContactsRestrictionExceptions(qb);
15724f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
15734f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
15744f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1575a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case CONTACTS_DATA: {
1576619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                long contactId = Long.parseLong(uri.getPathSegments().get(1));
15779261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_GROUPS);
15789261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataContactsGroupsProjectionMap);
1579619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.appendWhere(Data.CONTACT_ID + "=" + contactId + " AND ");
1580619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
1581a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1582a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1583a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
158428ab0f857caa92402878244d9c5ea2a59e070935Jeff Sharkey            case CONTACTS_FILTER_EMAIL: {
1585619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1586ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_AGGREGATES);
1587e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.setProjectionMap(sDataContactsProjectionMap);
15885d0f923eb4c5351ebf323cc6f19c82acff98693eJeff Sharkey                qb.appendWhere(Data.MIMETYPE + "='" + CommonDataKinds.Email.CONTENT_ITEM_TYPE + "'");
1589e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhere(" AND " + CommonDataKinds.Email.DATA + "=");
1590e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                qb.appendWhereEscapeString(uri.getPathSegments().get(2));
1591e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
1592e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
1593e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
1594e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            case DATA: {
1595035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
1596035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
1597343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                if (!TextUtils.isEmpty(accountName)) {
1598035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                    qb.appendWhere(Contacts.ACCOUNT_NAME + "="
1599035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountName) + " AND "
1600035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + Contacts.ACCOUNT_TYPE + "="
1601035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                            + DatabaseUtils.sqlEscapeString(accountType) + " AND ");
1602343c56b5679c58bf1835a0e219fff57beae6ecefFred Quintana                }
16039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_GROUPS);
16049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataGroupsProjectionMap);
1605619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
1606e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                break;
1607e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey            }
1608e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey
16094f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case DATA_ID: {
16109261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_GROUPS);
16119261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                qb.setProjectionMap(sDataGroupsProjectionMap);
1612ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(DataColumns.CONCRETE_ID + "=" + ContentUris.parseId(uri) + " AND ");
1613619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                applyDataRestrictionExceptions(qb);
16144f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton                break;
16154f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            }
16164f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
1617a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            case PHONE_LOOKUP: {
1618619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                // TODO: filter query based on callingUid
1619a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                if (TextUtils.isEmpty(sortOrder)) {
1620a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // Default the sort order to something reasonable so we get consistent
1621a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                    // results when callers don't request an ordering
1622e17c303827c5be8107dcd4a8b64333f349b688f5Jeff Sharkey                    sortOrder = Data.CONTACT_ID;
1623a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                }
1624a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1625a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                final String number = uri.getLastPathSegment();
1626bf659107617a6291ba8bfeebc3f2e50138075ab5Dmitri Plotnikov                OpenHelper.buildPhoneLookupQuery(qb, number);
1627a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                qb.setProjectionMap(sDataContactsProjectionMap);
1628a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton                break;
1629a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton            }
1630a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton
1631ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS: {
1632ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
1633ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
1634ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1635ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1636ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1637ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_ID: {
1638ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                long groupId = ContentUris.parseId(uri);
1639ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES);
1640ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsProjectionMap);
1641ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.appendWhere(GroupsColumns.CONCRETE_ID + "=" + groupId);
1642ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1643ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1644ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1645ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            case GROUPS_SUMMARY: {
1646ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setTables(Tables.GROUPS_JOIN_PACKAGES_DATA_CONTACTS_AGGREGATES);
1647ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                qb.setProjectionMap(sGroupsSummaryProjectionMap);
1648ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                groupBy = GroupsColumns.CONCRETE_ID;
1649ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                break;
1650ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            }
1651ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey
1652b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: {
1653127071b6305023a79b7d8f473ef6887843389f6eDmitri Plotnikov                qb.setTables(Tables.AGGREGATION_EXCEPTIONS_JOIN_CONTACTS);
1654b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
1655b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov                break;
1656b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov            }
1657b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov
165831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: {
165931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                long aggregateId = Long.parseLong(uri.getPathSegments().get(1));
166031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final String maxSuggestionsParam =
166131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        uri.getQueryParameter(AggregationSuggestions.MAX_SUGGESTIONS);
166231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
166331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                final int maxSuggestions;
166431b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                if (maxSuggestionsParam != null) {
166531b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = Integer.parseInt(maxSuggestionsParam);
166631b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                } else {
166731b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
166831b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                }
166931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
167031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                return mContactAggregator.queryAggregationSuggestions(aggregateId, projection,
167131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov                        sAggregatesProjectionMap, maxSuggestions);
167231b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            }
167331b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov
1674619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            case RESTRICTION_EXCEPTIONS: {
1675619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setTables(Tables.RESTRICTION_EXCEPTIONS);
1676619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                qb.setProjectionMap(sRestrictionExceptionsProjectionMap);
1677619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                break;
1678619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
1679619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
16804f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            default:
1681f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
1682f4e1358f1c8f5fe5e9e7689e36e04c57c2385169Dmitri Plotnikov                        sortOrder);
16834f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
16844f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
16854f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        // Perform the query and set the notification uri
16861f42f1958113b2dadc6cf26b51192b42f883f3b0Jeff Sharkey        final Cursor c = qb.query(db, projection, selection, selectionArgs,
1687bc5c799a52b5bde2f273efd118ebe2228c3d8f15Evan Millar                groupBy, null, sortOrder, limit);
16884f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        if (c != null) {
16894f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            c.setNotificationUri(getContext().getContentResolver(), ContactsContract.AUTHORITY_URI);
16904f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
16914f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        return c;
16924f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
16934f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton
16947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    /**
1695619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Restrict selection of {@link Aggregates} to only public ones, or those
1696619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * the caller has been granted a {@link RestrictionExceptions} to.
1697619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1698619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregateRestrictionExceptions(SQLiteQueryBuilder qb) {
1699619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = OpenHelper.getUidForPackageName(getContext().getPackageManager(),
1700619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                getContext().getPackageName());
1701619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1702619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere("(" + AggregatesColumns.SINGLE_RESTRICTED_PACKAGE_ID + " IS NULL");
1703619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String exceptionClause = mOpenHelper.getRestrictionExceptionClause(clientUid,
1704619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                AggregatesColumns.SINGLE_RESTRICTED_PACKAGE_ID);
1705619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (exceptionClause != null) {
1706619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            qb.appendWhere(" OR (" + exceptionClause + ")");
1707619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1708619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere(")");
1709619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1710619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1711619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1712619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the calling process, and
1713619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * add projections to correctly select {@link Aggregates#PRIMARY_PHONE_ID}
1714619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * and {@link Aggregates#PRIMARY_EMAIL_ID}.
1715619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1716619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyAggregatePrimaryRestrictionExceptions(HashMap<String, String> projection) {
1717619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: move back to Binder.getCallingUid() when we can stub-out test suite
1718619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = OpenHelper.getUidForPackageName(getContext().getPackageManager(),
1719619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                getContext().getPackageName());
1720619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1721619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String projectionPhone = "(CASE WHEN "
1722619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + mOpenHelper.getRestrictionExceptionClause(clientUid,
1723619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                        AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID) + " THEN "
1724619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID + " ELSE "
1725619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID + " END) AS "
1726619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                + Aggregates.PRIMARY_PHONE_ID;
1727619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_PHONE_ID);
1728619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_PHONE_ID, projectionPhone);
1729619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1730619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String projectionEmail = "(CASE WHEN "
1731619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + mOpenHelper.getRestrictionExceptionClause(clientUid,
1732619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                    AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID) + " THEN "
1733619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID + " ELSE "
1734619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID + " END) AS "
1735619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            + Aggregates.PRIMARY_EMAIL_ID;
1736619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.remove(Aggregates.PRIMARY_EMAIL_ID);
1737619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        projection.put(Aggregates.PRIMARY_EMAIL_ID, projectionEmail);
1738619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1739619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1740619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1741619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
1742619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
1743619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
1744619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1745619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyContactsRestrictionExceptions(SQLiteQueryBuilder qb) {
1746619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // TODO: move back to Binder.getCallingUid() when we can stub-out test suite
1747619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final int clientUid = OpenHelper.getUidForPackageName(getContext().getPackageManager(),
1748619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                getContext().getPackageName());
1749619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1750619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere("(" + Contacts.IS_RESTRICTED + "=0");
1751619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final String exceptionClause = mOpenHelper.getRestrictionExceptionClause(clientUid,
17529261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                OpenHelper.ContactsColumns.CONCRETE_PACKAGE_ID);
1753619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (exceptionClause != null) {
1754619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            qb.appendWhere(" OR (" + exceptionClause + ")");
1755619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
1756619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        qb.appendWhere(")");
1757619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1758619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1759619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
1760619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * Find any exceptions that have been granted to the
1761619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link Binder#getCallingUid()}, and add a limiting clause to the given
1762619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     * {@link SQLiteQueryBuilder} to hide restricted data.
1763619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey     */
1764619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    private void applyDataRestrictionExceptions(SQLiteQueryBuilder qb) {
1765619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        applyContactsRestrictionExceptions(qb);
1766619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    }
1767619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
1768619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey    /**
17697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * An implementation of EntityIterator that joins the contacts and data tables
17707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     * and consumes all the data rows for a contact in order to build the Entity for a contact.
17717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana     */
17727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    private static class ContactsEntityIterator implements EntityIterator {
17737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private final Cursor mEntityCursor;
17747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private volatile boolean mIsClosed;
17757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] DATA_KEYS = new String[]{
17777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data1",
17787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data2",
17797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data3",
17807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data4",
17817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data5",
17827e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data6",
17837e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data7",
17847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data8",
17857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data9",
17867e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                "data10"};
17877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
17887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        private static final String[] PROJECTION = new String[]{
1789035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_NAME,
1790035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.ACCOUNT_TYPE,
1791035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.SOURCE_ID,
1792035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.VERSION,
1793035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.DIRTY,
1794035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data._ID,
1795035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.MIMETYPE,
1796035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA1,
1797035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA2,
1798035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA3,
1799035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA4,
1800035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA5,
1801035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA6,
1802035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA7,
1803035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA8,
1804035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA9,
1805035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.DATA10,
1806035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.CONTACT_ID,
1807035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                Contacts.Data.IS_PRIMARY,
18089261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                Contacts.Data.DATA_VERSION,
18099261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                GroupMembership.GROUP_SOURCE_ID};
1810035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana
1811035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_NAME = 0;
1812035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_ACCOUNT_TYPE = 1;
1813035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_SOURCE_ID = 2;
1814035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_VERSION = 3;
1815035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DIRTY = 4;
1816035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_ID = 5;
1817035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_MIMETYPE = 6;
1818035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA1 = 7;
1819035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_CONTACT_ID = 17;
1820035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_IS_PRIMARY = 18;
1821035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana        private static final int COLUMN_DATA_VERSION = 19;
18229261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana        private static final int COLUMN_GROUP_SOURCE_ID = 20;
18237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public ContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, Uri uri,
18257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String selection, String[] selectionArgs, String sortOrder) {
18267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = false;
18277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18287e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final String updatedSortOrder = (sortOrder == null)
18297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    ? Contacts.Data.CONTACT_ID
18307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    : (Contacts.Data.CONTACT_ID + "," + sortOrder);
18317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase();
18337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
18349261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            qb.setTables(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES_GROUPS);
18359261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana            qb.setProjectionMap(sDataContactsGroupsProjectionMap);
18367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (contactsIdString != null) {
18377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                qb.appendWhere(Data.CONTACT_ID + "=" + contactsIdString);
18387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
1839035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountName = uri.getQueryParameter(Contacts.ACCOUNT_NAME);
1840035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            final String accountType = uri.getQueryParameter(Contacts.ACCOUNT_TYPE);
1841035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            if (!TextUtils.isEmpty(accountName)) {
1842035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                qb.appendWhere(Contacts.ACCOUNT_NAME + "="
1843035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountName) + " AND "
1844035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + Contacts.ACCOUNT_TYPE + "="
1845035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana                        + DatabaseUtils.sqlEscapeString(accountType));
1846035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            }
18477e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor = qb.query(db, PROJECTION, selection, selectionArgs,
18487e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    null, null, updatedSortOrder);
18497e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.moveToFirst();
18507e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
18517e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18527e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public void close() {
18537e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
18547e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("closing when already closed");
18557e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
18567e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mIsClosed = true;
18577e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            mEntityCursor.close();
18587e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
18597e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18607e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public boolean hasNext() throws RemoteException {
18617e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
18627e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling hasNext() when the iterator is closed");
18637e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
18647e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return !mEntityCursor.isAfterLast();
18667e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
18677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        public Entity next() throws RemoteException {
18697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (mIsClosed) {
18707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("calling next() when the iterator is closed");
18717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
18727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            if (!hasNext()) {
18737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new IllegalStateException("you may only call next() if hasNext() is true");
18747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            }
18757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final SQLiteCursor c = (SQLiteCursor) mEntityCursor;
18777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            final long contactId = c.getLong(COLUMN_CONTACT_ID);
18797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18807e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // we expect the cursor is already at the row we need to read from
18817e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentValues contactValues = new ContentValues();
1882035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_NAME, c.getString(COLUMN_ACCOUNT_NAME));
1883035b4cc204be2641079a0b04e9ee9791a8f8248bFred Quintana            contactValues.put(Contacts.ACCOUNT_TYPE, c.getString(COLUMN_ACCOUNT_TYPE));
18847e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts._ID, contactId);
18857e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.DIRTY, c.getLong(COLUMN_DIRTY));
1886f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana            contactValues.put(Contacts.VERSION, c.getLong(COLUMN_VERSION));
18877e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            contactValues.put(Contacts.SOURCE_ID, c.getString(COLUMN_SOURCE_ID));
18887e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            Entity contact = new Entity(contactValues);
18897e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
18907e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            // read data rows until the contact id changes
18917e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            do {
18927e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (contactId != c.getLong(COLUMN_CONTACT_ID)) {
18937e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    break;
18947e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
18957e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                // add the data to to the contact
18967e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                ContentValues dataValues = new ContentValues();
18977e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                dataValues.put(Contacts.Data._ID, c.getString(COLUMN_DATA_ID));
1898f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.MIMETYPE, c.getString(COLUMN_MIMETYPE));
1899f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.IS_PRIMARY, c.getString(COLUMN_IS_PRIMARY));
1900f5b20724819e51b339ff6ba5b8eb6b444cc31452Fred Quintana                dataValues.put(Contacts.Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
19019261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                if (!c.isNull(COLUMN_GROUP_SOURCE_ID)) {
19029261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                    dataValues.put(GroupMembership.GROUP_SOURCE_ID,
19039261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                            c.getString(COLUMN_GROUP_SOURCE_ID));
19049261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                }
19059261b2141aa90a4fed632fd6da03026d4c216280Fred Quintana                dataValues.put(Contacts.Data.DATA_VERSION, c.getLong(COLUMN_DATA_VERSION));
19067e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                for (int i = 0; i < 10; i++) {
19077e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    final int columnIndex = i + COLUMN_DATA1;
19087e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    String key = DATA_KEYS[i];
19097e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    if (c.isNull(columnIndex)) {
19107e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        // don't put anything
19117e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isLong(columnIndex)) {
19127e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getLong(columnIndex));
19137e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isFloat(columnIndex)) {
19147e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getFloat(columnIndex));
19157e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isString(columnIndex)) {
19167e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getString(columnIndex));
19177e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    } else if (c.isBlob(columnIndex)) {
19187e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        dataValues.put(key, c.getBlob(columnIndex));
19197e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    }
19207e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
19217e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                contact.addSubValue(Data.CONTENT_URI, dataValues);
19227e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            } while (mEntityCursor.moveToNext());
19237e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19247e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return contact;
19257e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
19267e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
19277e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1928a8dc456684a104c7e5547ba17d44f952022cd8c5Dmitri Plotnikov    @Override
19297e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public EntityIterator queryEntities(Uri uri, String selection, String[] selectionArgs,
19307e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            String sortOrder) {
19317e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final int match = sUriMatcher.match(uri);
19327e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        switch (match) {
19337e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS:
19347e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            case CONTACTS_ID:
19357e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                String contactsIdString = null;
19367e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                if (match == CONTACTS_ID) {
19377e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                    contactsIdString = uri.getPathSegments().get(1);
19387e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                }
19397e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19407e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                return new ContactsEntityIterator(this, contactsIdString,
19417e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                        uri, selection, selectionArgs, sortOrder);
19427e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            default:
19437e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana                throw new UnsupportedOperationException("Unknown uri: " + uri);
19447e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
19457e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
19467e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19474f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    @Override
19484f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    public String getType(Uri uri) {
1949a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        final int match = sUriMatcher.match(uri);
19504f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        switch (match) {
19516bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES: return Aggregates.CONTENT_TYPE;
19526bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov            case AGGREGATES_ID: return Aggregates.CONTENT_ITEM_TYPE;
19534f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS: return Contacts.CONTENT_TYPE;
19544f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton            case CONTACTS_ID: return Contacts.CONTENT_ITEM_TYPE;
1955508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey            case DATA_ID:
19566bc8c0d15f4eacd2e92e9064c88cdf0659524a0eDmitri Plotnikov                final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
1957508f57ee336c20583400b48b614bd3d57ca849ecJeff Sharkey                long dataId = ContentUris.parseId(uri);
1958b650982af7aeb2800efdcea587b8ce153259cf1cJeff Sharkey                return mOpenHelper.getDataMimeType(dataId);
195931b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTIONS: return AggregationExceptions.CONTENT_TYPE;
196031b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_EXCEPTION_ID: return AggregationExceptions.CONTENT_ITEM_TYPE;
196131b86315536573a72dc7fff1baac3b314e5a04c3Dmitri Plotnikov            case AGGREGATION_SUGGESTIONS: return Aggregates.CONTENT_TYPE;
19624f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton        }
1963a36c566037b4bb05f035d0c2cfd8b51386d7a8a6Jeff Hamilton        throw new UnsupportedOperationException("Unknown uri: " + uri);
19644f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton    }
19657e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
1966b174c7ccd337a7bea6269139c9b09acc69ae40c1Dmitri Plotnikov    @Override
19677e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
19687e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            throws OperationApplicationException {
19697e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana
19707e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
19717e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        db.beginTransaction();
19727e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        try {
19737e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            ContentProviderResult[] results = super.applyBatch(operations);
19747e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.setTransactionSuccessful();
19757e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            return results;
19767e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        } finally {
19777e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana            db.endTransaction();
19787e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana        }
19797e4676dfcaa8853b81c2133e0e318ed3436fe787Fred Quintana    }
1980c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1981c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
1982c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to primary, and resets all data records of
1983c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * the same mimetype and under the same contact to not be primary.
1984c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
1985c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
1986c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
1987c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsPrimary(long dataId) {
1988c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(1, dataId);
1989c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(2, dataId);
1990c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.bindLong(3, dataId);
1991c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetPrimaryStatement.execute();
1992c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
1993c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar
1994c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    /*
1995c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * Sets the given dataId record in the "data" table to "super primary", and resets all data
1996c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * records of the same mimetype and under the same aggregate to not be "super primary".
1997c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     *
1998c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     * @param dataId the id of the data record to be set to primary.
1999c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar     */
2000c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    private void setIsSuperPrimary(long dataId) {
2001c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(1, dataId);
2002c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(2, dataId);
2003c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.bindLong(3, dataId);
2004c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar        mSetSuperPrimaryStatement.execute();
2005619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2006619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Find the parent aggregate and package for this new primary
2007619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
2008619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2009619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        long aggId = -1;
2010619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        long packageId = -1;
2011619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isRestricted = false;
2012619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        String mimeType = null;
2013619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2014619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        Cursor cursor = null;
2015619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        try {
2016ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey            cursor = db.query(Tables.DATA_JOIN_MIMETYPES_CONTACTS_PACKAGES,
2017ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    Projections.PROJ_DATA_CONTACTS, DataColumns.CONCRETE_ID + "=" + dataId, null,
2018ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                    null, null, null);
2019619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor.moveToFirst()) {
2020ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                aggId = cursor.getLong(Projections.COL_AGGREGATE_ID);
2021ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                packageId = cursor.getLong(Projections.COL_PACKAGE_ID);
2022ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                isRestricted = (cursor.getInt(Projections.COL_IS_RESTRICTED) == 1);
2023ca8172420c0913dff96ea607d477d8b8abfe5ddbJeff Sharkey                mimeType = cursor.getString(Projections.COL_MIMETYPE);
2024619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2025619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } finally {
2026619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            if (cursor != null) {
2027619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey                cursor.close();
2028619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            }
2029619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2030619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2031619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Bypass aggregate update if no parent found, or if we don't keep track
2032619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // of super-primary for this mimetype.
2033d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        if (aggId == -1) {
2034d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov            return;
2035d35d9c748af4c3182679c4c546137acfc11eb7a8Dmitri Plotnikov        }
2036619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2037619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isPhone = CommonDataKinds.Phone.CONTENT_ITEM_TYPE.equals(mimeType);
2038619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        boolean isEmail = CommonDataKinds.Email.CONTENT_ITEM_TYPE.equals(mimeType);
2039619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2040619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Record this value as the new primary for the parent aggregate
2041619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        final ContentValues values = new ContentValues();
2042619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (isPhone) {
2043619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_ID, dataId);
2044619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_PHONE_PACKAGE_ID, packageId);
2045619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (isEmail) {
2046619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_ID, dataId);
2047619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.OPTIMAL_PRIMARY_EMAIL_PACKAGE_ID, packageId);
2048619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2049619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2050619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // If this data is unrestricted, then also set as fallback
2051619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (!isRestricted && isPhone) {
2052619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_PHONE_ID, dataId);
2053619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        } else if (!isRestricted && isEmail) {
2054619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            values.put(AggregatesColumns.FALLBACK_PRIMARY_EMAIL_ID, dataId);
2055619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2056619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2057619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        // Push update into aggregates table, if needed
2058619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        if (values.size() > 0) {
2059619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey            db.update(Tables.AGGREGATES, values, Aggregates._ID + "=" + aggId, null);
2060619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey        }
2061619871b0fb0175d75ff9336bfe5aec0b27b9bdadJeff Sharkey
2062c0834a81ef469e6ee7e72ce34a8a02855a162858Evan Millar    }
2063ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2064ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    private String buildAggregateLookupWhereClause(String filterParam) {
2065ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        StringBuilder filter = new StringBuilder();
2066ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.AGGREGATES);
2067ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(".");
2068ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Aggregates._ID);
2069ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" IN (SELECT ");
2070ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts.AGGREGATE_ID);
2071ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" FROM ");
2072ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Tables.CONTACTS);
2073ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(" WHERE ");
2074ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        filter.append(Contacts._ID);
2075d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(" IN (SELECT  contact_id FROM name_lookup WHERE normalized_name GLOB '");
2076ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // NOTE: Query parameters won't work here since the SQL compiler
2077ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // needs to parse the actual string to know that it can use the
2078ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        // index to do a prefix scan.
2079d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append(NameNormalizer.normalize(filterParam) + "*");
2080d3b76e00699679c519adc1c1770cd28ee6ae7aa3Evan Millar        filter.append("'))");
2081ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar        return filter.toString();
2082ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar    }
2083ea0ec7120315589eaafb45d88ff872abbde35e38Evan Millar
2084b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    private String[] appendGroupArg(String[] selectionArgs, String arg) {
2085b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        if (selectionArgs == null) {
2086b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return new String[] {arg};
2087b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        } else {
2088b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            int newLength = selectionArgs.length + 1;
2089b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            String[] newSelectionArgs = new String[newLength];
2090b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            System.arraycopy(selectionArgs, 0, newSelectionArgs, 0, selectionArgs.length);
2091b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            newSelectionArgs[newLength - 1] = arg;
2092b67163a1088f09c59f324350662eb18772fac6b6Evan Millar            return newSelectionArgs;
2093b67163a1088f09c59f324350662eb18772fac6b6Evan Millar        }
2094b67163a1088f09c59f324350662eb18772fac6b6Evan Millar    }
20954f864360c24bd26c111bf38a035e8e2d2609e84aJeff Hamilton}
2096