    private Cursor queryLocal(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder, long directoryId,
            final boolean suppressProfileCheck) {
        if (VERBOSE_LOGGING) {
            Log.v(TAG, "query: " + uri);
        }
        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        String groupBy = null;
        String limit = getLimit(uri);
        String profileRestrictionColumnName = null;
        final int match = sUriMatcher.match(uri);
        switch (match) {
            case SYNCSTATE:
                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
                        sortOrder);
            case CONTACTS: {
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
                break;
            }
            case CONTACTS_ID: {
                long contactId = ContentUris.parseId(uri);
                enforceProfilePermissionForContact(db, contactId, false);
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(Contacts._ID + "=?");
                break;
            }
            case CONTACTS_LOOKUP:
            case CONTACTS_LOOKUP_ID: {
                List<String> pathSegments = uri.getPathSegments();
                int segmentCount = pathSegments.size();
                if (segmentCount < 3) {
                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
                            "Missing a lookup key", uri));
                }
                String lookupKey = pathSegments.get(2);
                if (segmentCount == 4) {
                    long contactId = Long.parseLong(pathSegments.get(3));
                    enforceProfilePermissionForContact(db, contactId, false);
                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
                    if (c != null) {
                        return c;
                    }
                }
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                selectionArgs = insertSelectionArg(selectionArgs,
                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
                qb.appendWhere(Contacts._ID + "=?");
                break;
            }
            case CONTACTS_LOOKUP_DATA:
            case CONTACTS_LOOKUP_ID_DATA: {
                List<String> pathSegments = uri.getPathSegments();
                int segmentCount = pathSegments.size();
                if (segmentCount < 4) {
                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
                            "Missing a lookup key", uri));
                }
                String lookupKey = pathSegments.get(2);
                if (segmentCount == 5) {
                    long contactId = Long.parseLong(pathSegments.get(3));
                    enforceProfilePermissionForContact(db, contactId, false);
                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
                    lookupQb.appendWhere(" AND ");
                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
                    if (c != null) {
                        return c;
                    }
                }
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                long contactId = lookupContactIdByLookupKey(db, lookupKey);
                enforceProfilePermissionForContact(db, contactId, false);
                selectionArgs = insertSelectionArg(selectionArgs,
                        String.valueOf(contactId));
                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_ID_STREAM_ITEMS: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                enforceProfilePermissionForContact(db, contactId, false);
                setTablesAndProjectionMapForStreamItems(qb);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(RawContactsColumns.CONCRETE_CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_LOOKUP_STREAM_ITEMS:
            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
                List<String> pathSegments = uri.getPathSegments();
                int segmentCount = pathSegments.size();
                if (segmentCount < 4) {
                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
                            "Missing a lookup key", uri));
                }
                String lookupKey = pathSegments.get(2);
                if (segmentCount == 5) {
                    long contactId = Long.parseLong(pathSegments.get(3));
                    enforceProfilePermissionForContact(db, contactId, false);
                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                    setTablesAndProjectionMapForStreamItems(lookupQb);
                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
                            RawContacts.CONTACT_ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
                    if (c != null) {
                        return c;
                    }
                }
                setTablesAndProjectionMapForStreamItems(qb);
                long contactId = lookupContactIdByLookupKey(db, lookupKey);
                enforceProfilePermissionForContact(db, contactId, false);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_AS_VCARD: {
                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
                long contactId = lookupContactIdByLookupKey(db, lookupKey);
                enforceProfilePermissionForContact(db, contactId, false);
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sContactsVCardProjectionMap);
                selectionArgs = insertSelectionArg(selectionArgs,
                        String.valueOf(contactId));
                qb.appendWhere(Contacts._ID + "=?");
                break;
            }
            case CONTACTS_AS_MULTI_VCARD: {
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
                String currentDateString = dateFormat.format(new Date()).toString();
                return db.rawQuery(
                    "SELECT" +
                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
                    " NULL AS " + OpenableColumns.SIZE,
                    new String[] { currentDateString });
            }
            case CONTACTS_FILTER: {
                String filterParam = "";
                if (uri.getPathSegments().size() > 2) {
                    filterParam = uri.getLastPathSegment();
                }
                setTablesAndProjectionMapForContactsWithSnippet(
                        qb, uri, projection, filterParam, directoryId);
                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
                break;
            }
            case CONTACTS_STREQUENT_FILTER:
            case CONTACTS_STREQUENT: {
                final boolean phoneOnly = readBooleanQueryParameter(
                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
                    String filterParam = uri.getLastPathSegment();
                    StringBuilder sb = new StringBuilder();
                    sb.append(Contacts._ID + " IN ");
                    appendContactFilterAsNestedQuery(sb, filterParam);
                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
                }
                String[] subProjection = null;
                if (projection != null) {
                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
                }
                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
                qb.setProjectionMap(phoneOnly ?
                        sStrequentPhoneOnlyStarredProjectionMap
                        : sStrequentStarredProjectionMap);
                qb.appendWhere(DbQueryUtils.concatenateClauses(
                        selection, Contacts.IS_USER_PROFILE + "=0"));
                if (phoneOnly) {
                    qb.appendWhere(" AND " + Contacts.HAS_PHONE_NUMBER + "=1");
                }
                qb.setStrict(true);
                final String starredQuery = qb.buildQuery(subProjection,
                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
                qb = new SQLiteQueryBuilder();
                qb.setStrict(true);
                final String frequentQuery;
                if (phoneOnly) {
                    final StringBuilder tableBuilder = new StringBuilder();
                    tableBuilder.append(Tables.DATA_USAGE_STAT
                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
                                + DataColumns.CONCRETE_ID + " AND "
                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
                    appendContactStatusUpdateJoin(tableBuilder, projection,
                            ContactsColumns.LAST_STATUS_UPDATE_ID);
                    qb.setTables(tableBuilder.toString());
                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
                    qb.appendWhere(DbQueryUtils.concatenateClauses(
                            selection,
                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
                            MimetypesColumns.MIMETYPE + " IN ("
                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
                } else {
                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
                    qb.appendWhere(DbQueryUtils.concatenateClauses(
                            selection,
                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)",
                            Contacts.IS_USER_PROFILE + "=0"));
                    frequentQuery = qb.buildQuery(subProjection,
                            null, Contacts._ID, null, null, null);
                }
                final String unionQuery =
                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
                String[] doubledSelectionArgs = null;
                if (selectionArgs != null) {
                    final int length = selectionArgs.length;
                    doubledSelectionArgs = new String[length * 2];
                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
                }
                Cursor cursor = db.rawQuery(unionQuery, doubledSelectionArgs);
                if (cursor != null) {
                    cursor.setNotificationUri(getContext().getContentResolver(),
                            ContactsContract.AUTHORITY_URI);
                }
                return cursor;
            }
            case CONTACTS_FREQUENT: {
                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
                qb.setProjectionMap(sStrequentFrequentProjectionMap);
                qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
                groupBy = Contacts._ID;
                if (!TextUtils.isEmpty(sortOrder)) {
                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
                } else {
                    sortOrder = FREQUENT_ORDER_BY;
                }
                break;
            }
            case CONTACTS_GROUP: {
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                if (uri.getPathSegments().size() > 2) {
                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                }
                break;
            }
            case PROFILE: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_ENTITIES: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForEntities(qb, uri, projection);
                qb.appendWhere(" AND " + Contacts.IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_DATA: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_DATA_ID: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data._ID + "=? AND "
                        + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_AS_VCARD: {
                enforceProfilePermission(false);
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sContactsVCardProjectionMap);
                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
                break;
            }
            case CONTACTS_ID_DATA: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_ID_PHOTO: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                enforceProfilePermissionForContact(db, contactId, false);
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
                break;
            }
            case CONTACTS_ID_ENTITIES: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                setTablesAndProjectionMapForEntities(qb, uri, projection);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_LOOKUP_ENTITIES:
            case CONTACTS_LOOKUP_ID_ENTITIES: {
                List<String> pathSegments = uri.getPathSegments();
                int segmentCount = pathSegments.size();
                if (segmentCount < 4) {
                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
                            "Missing a lookup key", uri));
                }
                String lookupKey = pathSegments.get(2);
                if (segmentCount == 5) {
                    long contactId = Long.parseLong(pathSegments.get(3));
                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
                    lookupQb.appendWhere(" AND ");
                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
                            Contacts.Entity.CONTACT_ID, contactId,
                            Contacts.Entity.LOOKUP_KEY, lookupKey);
                    if (c != null) {
                        return c;
                    }
                }
                setTablesAndProjectionMapForEntities(qb, uri, projection);
                selectionArgs = insertSelectionArg(selectionArgs,
                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
                break;
            }
            case STREAM_ITEMS: {
                setTablesAndProjectionMapForStreamItems(qb);
                break;
            }
            case STREAM_ITEMS_ID: {
                setTablesAndProjectionMapForStreamItems(qb);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(StreamItemsColumns.CONCRETE_ID + "=?");
                break;
            }
            case STREAM_ITEMS_LIMIT: {
                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
                return cursor;
            }
            case STREAM_ITEMS_PHOTOS: {
                setTablesAndProjectionMapForStreamItemPhotos(qb);
                break;
            }
            case STREAM_ITEMS_ID_PHOTOS: {
                setTablesAndProjectionMapForStreamItemPhotos(qb);
                String streamItemId = uri.getPathSegments().get(1);
                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
                break;
            }
            case STREAM_ITEMS_ID_PHOTOS_ID: {
                setTablesAndProjectionMapForStreamItemPhotos(qb);
                String streamItemId = uri.getPathSegments().get(1);
                String streamItemPhotoId = uri.getPathSegments().get(3);
                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
                break;
            }
            case PHOTO_DIMENSIONS: {
                MatrixCursor cursor = new MatrixCursor(
                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
                        1);
                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
                return cursor;
            }
            case PHONES: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
                break;
            }
            case PHONES_ID: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
                qb.appendWhere(" AND " + Data._ID + "=?");
                break;
            }
            case PHONES_FILTER: {
                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                Integer typeInt = sDataUsageTypeMap.get(typeParam);
                if (typeInt == null) {
                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
                }
                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
                if (uri.getPathSegments().size() > 2) {
                    String filterParam = uri.getLastPathSegment();
                    StringBuilder sb = new StringBuilder();
                    sb.append(" AND (");
                    boolean hasCondition = false;
                    boolean orNeeded = false;
                    String normalizedName = NameNormalizer.normalize(filterParam);
                    if (normalizedName.length() > 0) {
                        sb.append(Data.RAW_CONTACT_ID + " IN " +
                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
                                " FROM " + Tables.SEARCH_INDEX +
                                " JOIN " + Tables.RAW_CONTACTS +
                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
                        sb.append(")");
                        orNeeded = true;
                        hasCondition = true;
                    }
                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
                    if (!TextUtils.isEmpty(number)) {
                        if (orNeeded) {
                            sb.append(" OR ");
                        }
                        sb.append(Data._ID +
                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
                                + " FROM " + Tables.PHONE_LOOKUP
                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
                        sb.append(number);
                        sb.append("%')");
                        hasCondition = true;
                    }
                    if (!hasCondition) {
                        sb.append("0");
                    }
                    sb.append(")");
                    qb.appendWhere(sb);
                }
                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
                if (sortOrder == null) {
                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
                    } else {
                        sortOrder = PHONE_FILTER_SORT_ORDER;
                    }
                }
                break;
            }
            case EMAILS: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
                break;
            }
            case EMAILS_ID: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
                        + " AND " + Data._ID + "=?");
                break;
            }
            case EMAILS_LOOKUP: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
                if (uri.getPathSegments().size() > 2) {
                    String email = uri.getLastPathSegment();
                    String address = mDbHelper.extractAddressFromEmailAddress(email);
                    selectionArgs = insertSelectionArg(selectionArgs, address);
                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
                }
                break;
            }
            case EMAILS_FILTER: {
                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                Integer typeInt = sDataUsageTypeMap.get(typeParam);
                if (typeInt == null) {
                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
                }
                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
                String filterParam = null;
                if (uri.getPathSegments().size() > 3) {
                    filterParam = uri.getLastPathSegment();
                    if (TextUtils.isEmpty(filterParam)) {
                        filterParam = null;
                    }
                }
                if (filterParam == null) {
                    qb.appendWhere(" AND 0");
                } else {
                    StringBuilder sb = new StringBuilder();
                    sb.append(" AND " + Data._ID + " IN (");
                    sb.append(
                            "SELECT " + Data._ID +
                            " FROM " + Tables.DATA +
                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
                    sb.append(mDbHelper.getMimeTypeIdForEmail());
                    sb.append(" AND " + Data.DATA1 + " LIKE ");
                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
                    if (!filterParam.contains("@")) {
                        sb.append(
                                " UNION SELECT " + Data._ID +
                                " FROM " + Tables.DATA +
                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
                        sb.append(mDbHelper.getMimeTypeIdForEmail());
                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
                                " FROM " + Tables.SEARCH_INDEX +
                                " JOIN " + Tables.RAW_CONTACTS +
                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
                        sb.append(")");
                    }
                    sb.append(")");
                    qb.appendWhere(sb);
                }
                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
                if (sortOrder == null) {
                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
                    } else {
                        sortOrder = EMAIL_FILTER_SORT_ORDER;
                    }
                }
                break;
            }
            case POSTALS: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
                break;
            }
            case POSTALS_ID: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
                qb.appendWhere(" AND " + Data._ID + "=?");
                break;
            }
            case RAW_CONTACTS: {
                setTablesAndProjectionMapForRawContacts(qb, uri);
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                break;
            }
            case RAW_CONTACTS_ID: {
                long rawContactId = ContentUris.parseId(uri);
                enforceProfilePermissionForRawContact(db, rawContactId, false);
                setTablesAndProjectionMapForRawContacts(qb, uri);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                qb.appendWhere(" AND " + RawContacts._ID + "=?");
                break;
            }
            case RAW_CONTACTS_DATA: {
                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                break;
            }
            case RAW_CONTACTS_ID_STREAM_ITEMS: {
                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
                enforceProfilePermissionForRawContact(db, rawContactId, false);
                setTablesAndProjectionMapForStreamItems(qb);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
                break;
            }
            case PROFILE_RAW_CONTACTS: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForRawContacts(qb, uri);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_RAW_CONTACTS_ID: {
                enforceProfilePermission(false);
                long rawContactId = ContentUris.parseId(uri);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                setTablesAndProjectionMapForRawContacts(qb, uri);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
                        + RawContacts._ID + "=?");
                break;
            }
            case PROFILE_RAW_CONTACTS_ID_DATA: {
                enforceProfilePermission(false);
                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
                        + Data.RAW_CONTACT_ID + "=?");
                break;
            }
            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
                enforceProfilePermission(false);
                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                setTablesAndProjectionMapForRawEntities(qb, uri);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
                        + RawContacts._ID + "=?");
                break;
            }
            case DATA: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                break;
            }
            case DATA_ID: {
                long dataId = ContentUris.parseId(uri);
                enforceProfilePermissionForData(db, dataId, false);
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data._ID + "=?");
                break;
            }
            case PHONE_LOOKUP: {
                if (TextUtils.isEmpty(sortOrder)) {
                    sortOrder = " length(lookup.normalized_number) DESC";
                }
                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
                        mDbHelper.getCurrentCountryIso());
                String normalizedNumber =
                        PhoneNumberUtils.normalizeNumber(number);
                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
                qb.setProjectionMap(sPhoneLookupProjectionMap);
                selection = null;
                selectionArgs = null;
                break;
            }
            case GROUPS: {
                qb.setTables(Views.GROUPS);
                qb.setProjectionMap(sGroupsProjectionMap);
                appendAccountFromParameter(qb, uri, true);
                break;
            }
            case GROUPS_ID: {
                qb.setTables(Views.GROUPS);
                qb.setProjectionMap(sGroupsProjectionMap);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(Groups._ID + "=?");
                break;
            }
            case GROUPS_SUMMARY: {
                final boolean returnGroupCountPerAccount =
                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
                                false);
                qb.setTables(Views.GROUPS + " AS " + Tables.GROUPS);
                qb.setProjectionMap(returnGroupCountPerAccount ?
                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
                        : sGroupsSummaryProjectionMap);
                appendAccountFromParameter(qb, uri, true);
                groupBy = GroupsColumns.CONCRETE_ID;
                break;
            }
            case AGGREGATION_EXCEPTIONS: {
                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
                break;
            }
            case AGGREGATION_SUGGESTIONS: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                String filter = null;
                if (uri.getPathSegments().size() > 3) {
                    filter = uri.getPathSegments().get(3);
                }
                final int maxSuggestions;
                if (limit != null) {
                    maxSuggestions = Integer.parseInt(limit);
                } else {
                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
                }
                ArrayList<AggregationSuggestionParameter> parameters = null;
                List<String> query = uri.getQueryParameters("query");
                if (query != null && !query.isEmpty()) {
                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
                    for (String parameter : query) {
                        int offset = parameter.indexOf(':');
                        parameters.add(offset == -1
                                ? new AggregationSuggestionParameter(
                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
                                        parameter)
                                : new AggregationSuggestionParameter(
                                        parameter.substring(0, offset),
                                        parameter.substring(offset + 1)));
                    }
                }
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
                        maxSuggestions, filter, parameters);
            }
            case SETTINGS: {
                qb.setTables(Tables.SETTINGS);
                qb.setProjectionMap(sSettingsProjectionMap);
                appendAccountFromParameter(qb, uri, false);
                final String groupMembershipMimetypeId = Long.toString(mDbHelper
                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
                if (projection != null && projection.length != 0 &&
                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
                }
                if (projection != null && projection.length != 0 &&
                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
                }
                break;
            }
            case STATUS_UPDATES: {
                setTableAndProjectionMapForStatusUpdates(qb, projection);
                break;
            }
            case STATUS_UPDATES_ID: {
                setTableAndProjectionMapForStatusUpdates(qb, projection);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
                break;
            }
            case SEARCH_SUGGESTIONS: {
                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
                        db, uri, projection, limit);
            }
            case SEARCH_SHORTCUT: {
                String lookupKey = uri.getLastPathSegment();
                String filter = getQueryParameter(
                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
                return mGlobalSearchSupport.handleSearchShortcutRefresh(
                        db, projection, lookupKey, filter);
            }
            case LIVE_FOLDERS_CONTACTS:
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sLiveFoldersProjectionMap);
                break;
            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sLiveFoldersProjectionMap);
                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
                break;
            case LIVE_FOLDERS_CONTACTS_FAVORITES:
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sLiveFoldersProjectionMap);
                qb.appendWhere(Contacts.STARRED + "=1");
                break;
            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sLiveFoldersProjectionMap);
                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                break;
            case RAW_CONTACT_ENTITIES: {
                setTablesAndProjectionMapForRawEntities(qb, uri);
                break;
            }
            case RAW_CONTACT_ENTITY_ID: {
                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
                setTablesAndProjectionMapForRawEntities(qb, uri);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                qb.appendWhere(" AND " + RawContacts._ID + "=?");
                break;
            }
            case PROVIDER_STATUS: {
                return queryProviderStatus(uri, projection);
            }
            case DIRECTORIES : {
                qb.setTables(Tables.DIRECTORIES);
                qb.setProjectionMap(sDirectoryProjectionMap);
                break;
            }
            case DIRECTORIES_ID : {
                long id = ContentUris.parseId(uri);
                qb.setTables(Tables.DIRECTORIES);
                qb.setProjectionMap(sDirectoryProjectionMap);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
                qb.appendWhere(Directory._ID + "=?");
                break;
            }
            case COMPLETE_NAME: {
                return completeName(uri, projection);
            }
            default:
                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
                        sortOrder, limit);
        }
        qb.setStrict(true);
        if (profileRestrictionColumnName != null) {
            selection = appendProfileRestriction(uri, profileRestrictionColumnName,
                    suppressProfileCheck, selection);
        }
        Cursor cursor =
                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
        }
        return cursor;
    }

    private Cursor queryLocal(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder, long directoryId,
            final boolean suppressProfileCheck) {
        if (VERBOSE_LOGGING) {
            Log.v(TAG, "query: " + uri);
        }
        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        String groupBy = null;
        String limit = getLimit(uri);
        String profileRestrictionColumnName = null;
        final int match = sUriMatcher.match(uri);
        switch (match) {
            case SYNCSTATE:
                return mDbHelper.getSyncState().query(db, projection, selection,  selectionArgs,
                        sortOrder);
            case CONTACTS: {
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                appendLocalDirectorySelectionIfNeeded(qb, directoryId);
                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
                break;
            }
            case CONTACTS_ID: {
                long contactId = ContentUris.parseId(uri);
                enforceProfilePermissionForContact(db, contactId, false);
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(Contacts._ID + "=?");
                break;
            }
            case CONTACTS_LOOKUP:
            case CONTACTS_LOOKUP_ID: {
                List<String> pathSegments = uri.getPathSegments();
                int segmentCount = pathSegments.size();
                if (segmentCount < 3) {
                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
                            "Missing a lookup key", uri));
                }
                String lookupKey = pathSegments.get(2);
                if (segmentCount == 4) {
                    long contactId = Long.parseLong(pathSegments.get(3));
                    enforceProfilePermissionForContact(db, contactId, false);
                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                    setTablesAndProjectionMapForContacts(lookupQb, uri, projection);
                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
                            Contacts._ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
                    if (c != null) {
                        return c;
                    }
                }
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                selectionArgs = insertSelectionArg(selectionArgs,
                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
                qb.appendWhere(Contacts._ID + "=?");
                break;
            }
            case CONTACTS_LOOKUP_DATA:
            case CONTACTS_LOOKUP_ID_DATA: {
                List<String> pathSegments = uri.getPathSegments();
                int segmentCount = pathSegments.size();
                if (segmentCount < 4) {
                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
                            "Missing a lookup key", uri));
                }
                String lookupKey = pathSegments.get(2);
                if (segmentCount == 5) {
                    long contactId = Long.parseLong(pathSegments.get(3));
                    enforceProfilePermissionForContact(db, contactId, false);
                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                    setTablesAndProjectionMapForData(lookupQb, uri, projection, false);
                    lookupQb.appendWhere(" AND ");
                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
                            Data.CONTACT_ID, contactId, Data.LOOKUP_KEY, lookupKey);
                    if (c != null) {
                        return c;
                    }
                }
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                long contactId = lookupContactIdByLookupKey(db, lookupKey);
                enforceProfilePermissionForContact(db, contactId, false);
                selectionArgs = insertSelectionArg(selectionArgs,
                        String.valueOf(contactId));
                qb.appendWhere(" AND " + Data.CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_ID_STREAM_ITEMS: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                enforceProfilePermissionForContact(db, contactId, false);
                setTablesAndProjectionMapForStreamItems(qb);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(RawContactsColumns.CONCRETE_CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_LOOKUP_STREAM_ITEMS:
            case CONTACTS_LOOKUP_ID_STREAM_ITEMS: {
                List<String> pathSegments = uri.getPathSegments();
                int segmentCount = pathSegments.size();
                if (segmentCount < 4) {
                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
                            "Missing a lookup key", uri));
                }
                String lookupKey = pathSegments.get(2);
                if (segmentCount == 5) {
                    long contactId = Long.parseLong(pathSegments.get(3));
                    enforceProfilePermissionForContact(db, contactId, false);
                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                    setTablesAndProjectionMapForStreamItems(lookupQb);
                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
                            RawContacts.CONTACT_ID, contactId, Contacts.LOOKUP_KEY, lookupKey);
                    if (c != null) {
                        return c;
                    }
                }
                setTablesAndProjectionMapForStreamItems(qb);
                long contactId = lookupContactIdByLookupKey(db, lookupKey);
                enforceProfilePermissionForContact(db, contactId, false);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(RawContacts.CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_AS_VCARD: {
                final String lookupKey = Uri.encode(uri.getPathSegments().get(2));
                long contactId = lookupContactIdByLookupKey(db, lookupKey);
                enforceProfilePermissionForContact(db, contactId, false);
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sContactsVCardProjectionMap);
                selectionArgs = insertSelectionArg(selectionArgs,
                        String.valueOf(contactId));
                qb.appendWhere(Contacts._ID + "=?");
                break;
            }
            case CONTACTS_AS_MULTI_VCARD: {
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
                String currentDateString = dateFormat.format(new Date()).toString();
                return db.rawQuery(
                    "SELECT" +
                    " 'vcards_' || ? || '.vcf' AS " + OpenableColumns.DISPLAY_NAME + "," +
                    " NULL AS " + OpenableColumns.SIZE,
                    new String[] { currentDateString });
            }
            case CONTACTS_FILTER: {
                String filterParam = "";
                if (uri.getPathSegments().size() > 2) {
                    filterParam = uri.getLastPathSegment();
                }
                setTablesAndProjectionMapForContactsWithSnippet(
                        qb, uri, projection, filterParam, directoryId);
                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
                sortOrder = prependProfileSortIfNeeded(uri, sortOrder, suppressProfileCheck);
                break;
            }
            case CONTACTS_STREQUENT_FILTER:
            case CONTACTS_STREQUENT: {
                final boolean phoneOnly = readBooleanQueryParameter(
                        uri, ContactsContract.STREQUENT_PHONE_ONLY, false);
                if (match == CONTACTS_STREQUENT_FILTER && uri.getPathSegments().size() > 3) {
                    String filterParam = uri.getLastPathSegment();
                    StringBuilder sb = new StringBuilder();
                    sb.append(Contacts._ID + " IN ");
                    appendContactFilterAsNestedQuery(sb, filterParam);
                    selection = DbQueryUtils.concatenateClauses(selection, sb.toString());
                }
                String[] subProjection = null;
                if (projection != null) {
                    subProjection = appendProjectionArg(projection, TIMES_USED_SORT_COLUMN);
                }
                setTablesAndProjectionMapForContacts(qb, uri, projection, false);
                qb.setProjectionMap(phoneOnly ?
                        sStrequentPhoneOnlyStarredProjectionMap
                        : sStrequentStarredProjectionMap);
                qb.appendWhere(DbQueryUtils.concatenateClauses(
                        selection, Contacts.IS_USER_PROFILE + "=0"));
                if (phoneOnly) {
                    qb.appendWhere(" AND " + Contacts.HAS_PHONE_NUMBER + "=1");
                }
                qb.setStrict(true);
                final String starredQuery = qb.buildQuery(subProjection,
                        Contacts.STARRED + "=1", Contacts._ID, null, null, null);
                qb = new SQLiteQueryBuilder();
                qb.setStrict(true);
                final String frequentQuery;
                if (phoneOnly) {
                    final StringBuilder tableBuilder = new StringBuilder();
                    tableBuilder.append(Tables.DATA_USAGE_STAT
                            + " INNER JOIN " + Views.DATA + " " + Tables.DATA
                            + " ON (" + DataUsageStatColumns.CONCRETE_DATA_ID + "="
                                + DataColumns.CONCRETE_ID + " AND "
                            + DataUsageStatColumns.CONCRETE_USAGE_TYPE + "="
                                + DataUsageStatColumns.USAGE_TYPE_INT_CALL + ")");
                    appendContactPresenceJoin(tableBuilder, projection, RawContacts.CONTACT_ID);
                    appendContactStatusUpdateJoin(tableBuilder, projection,
                            ContactsColumns.LAST_STATUS_UPDATE_ID);
                    qb.setTables(tableBuilder.toString());
                    qb.setProjectionMap(sStrequentPhoneOnlyFrequentProjectionMap);
                    qb.appendWhere(DbQueryUtils.concatenateClauses(
                            selection,
                            Contacts.STARRED + "=0 OR " + Contacts.STARRED + " IS NULL",
                            MimetypesColumns.MIMETYPE + " IN ("
                            + "'" + Phone.CONTENT_ITEM_TYPE + "', "
                            + "'" + SipAddress.CONTENT_ITEM_TYPE + "')"));
                    frequentQuery = qb.buildQuery(subProjection, null, null, null, null, null);
                } else {
                    setTablesAndProjectionMapForContacts(qb, uri, projection, true);
                    qb.setProjectionMap(sStrequentFrequentProjectionMap);
                    qb.appendWhere(DbQueryUtils.concatenateClauses(
                            selection,
                            "(" + Contacts.STARRED + " =0 OR " + Contacts.STARRED + " IS NULL)",
                            Contacts.IS_USER_PROFILE + "=0"));
                    frequentQuery = qb.buildQuery(subProjection,
                            null, Contacts._ID, null, null, null);
                }
                final String unionQuery =
                        qb.buildUnionQuery(new String[] {starredQuery, frequentQuery},
                                STREQUENT_ORDER_BY, STREQUENT_LIMIT);
                String[] doubledSelectionArgs = null;
                if (selectionArgs != null) {
                    final int length = selectionArgs.length;
                    doubledSelectionArgs = new String[length * 2];
                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, 0, length);
                    System.arraycopy(selectionArgs, 0, doubledSelectionArgs, length, length);
                }
                Cursor cursor = db.rawQuery(unionQuery, doubledSelectionArgs);
                if (cursor != null) {
                    cursor.setNotificationUri(getContext().getContentResolver(),
                            ContactsContract.AUTHORITY_URI);
                }
                return cursor;
            }
            case CONTACTS_FREQUENT: {
                setTablesAndProjectionMapForContacts(qb, uri, projection, true);
                qb.setProjectionMap(sStrequentFrequentProjectionMap);
                qb.appendWhere(Contacts.IS_USER_PROFILE + "=0");
                groupBy = Contacts._ID;
                if (!TextUtils.isEmpty(sortOrder)) {
                    sortOrder = FREQUENT_ORDER_BY + ", " + sortOrder;
                } else {
                    sortOrder = FREQUENT_ORDER_BY;
                }
                break;
            }
            case CONTACTS_GROUP: {
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                if (uri.getPathSegments().size() > 2) {
                    qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
                    selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                }
                break;
            }
            case PROFILE: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_ENTITIES: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForEntities(qb, uri, projection);
                qb.appendWhere(" AND " + Contacts.IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_DATA: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_DATA_ID: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data._ID + "=? AND "
                        + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_AS_VCARD: {
                enforceProfilePermission(false);
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sContactsVCardProjectionMap);
                qb.appendWhere(Contacts.IS_USER_PROFILE + "=1");
                break;
            }
            case CONTACTS_ID_DATA: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_ID_PHOTO: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                enforceProfilePermissionForContact(db, contactId, false);
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
                qb.appendWhere(" AND " + Data._ID + "=" + Contacts.PHOTO_ID);
                break;
            }
            case CONTACTS_ID_ENTITIES: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                setTablesAndProjectionMapForEntities(qb, uri, projection);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(contactId));
                qb.appendWhere(" AND " + RawContacts.CONTACT_ID + "=?");
                break;
            }
            case CONTACTS_LOOKUP_ENTITIES:
            case CONTACTS_LOOKUP_ID_ENTITIES: {
                List<String> pathSegments = uri.getPathSegments();
                int segmentCount = pathSegments.size();
                if (segmentCount < 4) {
                    throw new IllegalArgumentException(mDbHelper.exceptionMessage(
                            "Missing a lookup key", uri));
                }
                String lookupKey = pathSegments.get(2);
                if (segmentCount == 5) {
                    long contactId = Long.parseLong(pathSegments.get(3));
                    SQLiteQueryBuilder lookupQb = new SQLiteQueryBuilder();
                    setTablesAndProjectionMapForEntities(lookupQb, uri, projection);
                    lookupQb.appendWhere(" AND ");
                    Cursor c = queryWithContactIdAndLookupKey(lookupQb, db, uri,
                            projection, selection, selectionArgs, sortOrder, groupBy, limit,
                            Contacts.Entity.CONTACT_ID, contactId,
                            Contacts.Entity.LOOKUP_KEY, lookupKey);
                    if (c != null) {
                        return c;
                    }
                }
                setTablesAndProjectionMapForEntities(qb, uri, projection);
                selectionArgs = insertSelectionArg(selectionArgs,
                        String.valueOf(lookupContactIdByLookupKey(db, lookupKey)));
                qb.appendWhere(" AND " + Contacts.Entity.CONTACT_ID + "=?");
                break;
            }
            case STREAM_ITEMS: {
                setTablesAndProjectionMapForStreamItems(qb);
                break;
            }
            case STREAM_ITEMS_ID: {
                setTablesAndProjectionMapForStreamItems(qb);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(StreamItemsColumns.CONCRETE_ID + "=?");
                break;
            }
            case STREAM_ITEMS_LIMIT: {
                MatrixCursor cursor = new MatrixCursor(new String[]{StreamItems.MAX_ITEMS}, 1);
                cursor.addRow(new Object[]{MAX_STREAM_ITEMS_PER_RAW_CONTACT});
                return cursor;
            }
            case STREAM_ITEMS_PHOTOS: {
                setTablesAndProjectionMapForStreamItemPhotos(qb);
                break;
            }
            case STREAM_ITEMS_ID_PHOTOS: {
                setTablesAndProjectionMapForStreamItemPhotos(qb);
                String streamItemId = uri.getPathSegments().get(1);
                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=?");
                break;
            }
            case STREAM_ITEMS_ID_PHOTOS_ID: {
                setTablesAndProjectionMapForStreamItemPhotos(qb);
                String streamItemId = uri.getPathSegments().get(1);
                String streamItemPhotoId = uri.getPathSegments().get(3);
                selectionArgs = insertSelectionArg(selectionArgs, streamItemPhotoId);
                selectionArgs = insertSelectionArg(selectionArgs, streamItemId);
                qb.appendWhere(StreamItemPhotosColumns.CONCRETE_STREAM_ITEM_ID + "=? AND " +
                        StreamItemPhotosColumns.CONCRETE_ID + "=?");
                break;
            }
            case PHOTO_DIMENSIONS: {
                MatrixCursor cursor = new MatrixCursor(
                        new String[]{DisplayPhoto.DISPLAY_MAX_DIM, DisplayPhoto.THUMBNAIL_MAX_DIM},
                        1);
                cursor.addRow(new Object[]{mMaxDisplayPhotoDim, mMaxThumbnailPhotoDim});
                return cursor;
            }
            case PHONES: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
                break;
            }
            case PHONES_ID: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
                qb.appendWhere(" AND " + Data._ID + "=?");
                break;
            }
            case PHONES_FILTER: {
                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                Integer typeInt = sDataUsageTypeMap.get(typeParam);
                if (typeInt == null) {
                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_CALL;
                }
                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Phone.CONTENT_ITEM_TYPE + "'");
                if (uri.getPathSegments().size() > 2) {
                    String filterParam = uri.getLastPathSegment();
                    StringBuilder sb = new StringBuilder();
                    sb.append(" AND (");
                    boolean hasCondition = false;
                    boolean orNeeded = false;
                    String normalizedName = NameNormalizer.normalize(filterParam);
                    if (normalizedName.length() > 0) {
                        sb.append(Data.RAW_CONTACT_ID + " IN " +
                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
                                " FROM " + Tables.SEARCH_INDEX +
                                " JOIN " + Tables.RAW_CONTACTS +
                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
                        sb.append(")");
                        orNeeded = true;
                        hasCondition = true;
                    }
                    String number = PhoneNumberUtils.normalizeNumber(filterParam);
                    if (!TextUtils.isEmpty(number)) {
                        if (orNeeded) {
                            sb.append(" OR ");
                        }
                        sb.append(Data._ID +
                                " IN (SELECT DISTINCT " + PhoneLookupColumns.DATA_ID
                                + " FROM " + Tables.PHONE_LOOKUP
                                + " WHERE " + PhoneLookupColumns.NORMALIZED_NUMBER + " LIKE '");
                        sb.append(number);
                        sb.append("%')");
                        hasCondition = true;
                    }
                    if (!hasCondition) {
                        sb.append("0");
                    }
                    sb.append(")");
                    qb.appendWhere(sb);
                }
                groupBy = PhoneColumns.NORMALIZED_NUMBER + "," + RawContacts.CONTACT_ID;
                if (sortOrder == null) {
                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
                        sortOrder = accountPromotionSortOrder + ", " + PHONE_FILTER_SORT_ORDER;
                    } else {
                        sortOrder = PHONE_FILTER_SORT_ORDER;
                    }
                }
                break;
            }
            case EMAILS: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
                break;
            }
            case EMAILS_ID: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'"
                        + " AND " + Data._ID + "=?");
                break;
            }
            case EMAILS_LOOKUP: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'");
                if (uri.getPathSegments().size() > 2) {
                    String email = uri.getLastPathSegment();
                    String address = mDbHelper.extractAddressFromEmailAddress(email);
                    selectionArgs = insertSelectionArg(selectionArgs, address);
                    qb.appendWhere(" AND UPPER(" + Email.DATA + ")=UPPER(?)");
                }
                break;
            }
            case EMAILS_FILTER: {
                String typeParam = uri.getQueryParameter(DataUsageFeedback.USAGE_TYPE);
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                Integer typeInt = sDataUsageTypeMap.get(typeParam);
                if (typeInt == null) {
                    typeInt = DataUsageStatColumns.USAGE_TYPE_INT_LONG_TEXT;
                }
                setTablesAndProjectionMapForData(qb, uri, projection, true, typeInt);
                String filterParam = null;
                if (uri.getPathSegments().size() > 3) {
                    filterParam = uri.getLastPathSegment();
                    if (TextUtils.isEmpty(filterParam)) {
                        filterParam = null;
                    }
                }
                if (filterParam == null) {
                    qb.appendWhere(" AND 0");
                } else {
                    StringBuilder sb = new StringBuilder();
                    sb.append(" AND " + Data._ID + " IN (");
                    sb.append(
                            "SELECT " + Data._ID +
                            " FROM " + Tables.DATA +
                            " WHERE " + DataColumns.MIMETYPE_ID + "=");
                    sb.append(mDbHelper.getMimeTypeIdForEmail());
                    sb.append(" AND " + Data.DATA1 + " LIKE ");
                    DatabaseUtils.appendEscapedSQLString(sb, filterParam + '%');
                    if (!filterParam.contains("@")) {
                        sb.append(
                                " UNION SELECT " + Data._ID +
                                " FROM " + Tables.DATA +
                                " WHERE +" + DataColumns.MIMETYPE_ID + "=");
                        sb.append(mDbHelper.getMimeTypeIdForEmail());
                        sb.append(" AND " + Data.RAW_CONTACT_ID + " IN " +
                                "(SELECT " + RawContactsColumns.CONCRETE_ID +
                                " FROM " + Tables.SEARCH_INDEX +
                                " JOIN " + Tables.RAW_CONTACTS +
                                " ON (" + Tables.SEARCH_INDEX + "." + SearchIndexColumns.CONTACT_ID
                                        + "=" + RawContactsColumns.CONCRETE_CONTACT_ID + ")" +
                                " WHERE " + SearchIndexColumns.NAME + " MATCH ");
                        DatabaseUtils.appendEscapedSQLString(sb, sanitizeMatch(filterParam) + "*");
                        sb.append(")");
                    }
                    sb.append(")");
                    qb.appendWhere(sb);
                }
                groupBy = Email.DATA + "," + RawContacts.CONTACT_ID;
                if (sortOrder == null) {
                    final String accountPromotionSortOrder = getAccountPromotionSortOrder(uri);
                    if (!TextUtils.isEmpty(accountPromotionSortOrder)) {
                        sortOrder = accountPromotionSortOrder + ", " + EMAIL_FILTER_SORT_ORDER;
                    } else {
                        sortOrder = EMAIL_FILTER_SORT_ORDER;
                    }
                }
                break;
            }
            case POSTALS: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
                break;
            }
            case POSTALS_ID: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data.MIMETYPE + " = '"
                        + StructuredPostal.CONTENT_ITEM_TYPE + "'");
                qb.appendWhere(" AND " + Data._ID + "=?");
                break;
            }
            case RAW_CONTACTS: {
                setTablesAndProjectionMapForRawContacts(qb, uri);
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                break;
            }
            case RAW_CONTACTS_ID: {
                long rawContactId = ContentUris.parseId(uri);
                enforceProfilePermissionForRawContact(db, rawContactId, false);
                setTablesAndProjectionMapForRawContacts(qb, uri);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                qb.appendWhere(" AND " + RawContacts._ID + "=?");
                break;
            }
            case RAW_CONTACTS_DATA: {
                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                qb.appendWhere(" AND " + Data.RAW_CONTACT_ID + "=?");
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                break;
            }
            case RAW_CONTACTS_ID_STREAM_ITEMS: {
                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
                enforceProfilePermissionForRawContact(db, rawContactId, false);
                setTablesAndProjectionMapForStreamItems(qb);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                qb.appendWhere(StreamItems.RAW_CONTACT_ID + "=?");
                break;
            }
            case PROFILE_RAW_CONTACTS: {
                enforceProfilePermission(false);
                setTablesAndProjectionMapForRawContacts(qb, uri);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1");
                break;
            }
            case PROFILE_RAW_CONTACTS_ID: {
                enforceProfilePermission(false);
                long rawContactId = ContentUris.parseId(uri);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                setTablesAndProjectionMapForRawContacts(qb, uri);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
                        + RawContacts._ID + "=?");
                break;
            }
            case PROFILE_RAW_CONTACTS_ID_DATA: {
                enforceProfilePermission(false);
                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
                        + Data.RAW_CONTACT_ID + "=?");
                break;
            }
            case PROFILE_RAW_CONTACTS_ID_ENTITIES: {
                enforceProfilePermission(false);
                long rawContactId = Long.parseLong(uri.getPathSegments().get(2));
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                setTablesAndProjectionMapForRawEntities(qb, uri);
                qb.appendWhere(" AND " + RawContacts.RAW_CONTACT_IS_USER_PROFILE + "=1 AND "
                        + RawContacts._ID + "=?");
                break;
            }
            case DATA: {
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                profileRestrictionColumnName = RawContacts.RAW_CONTACT_IS_USER_PROFILE;
                break;
            }
            case DATA_ID: {
                long dataId = ContentUris.parseId(uri);
                enforceProfilePermissionForData(db, dataId, false);
                setTablesAndProjectionMapForData(qb, uri, projection, false);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(" AND " + Data._ID + "=?");
                break;
            }
            case PHONE_LOOKUP: {
                if (TextUtils.isEmpty(sortOrder)) {
                    sortOrder = " length(lookup.normalized_number) DESC";
                }
                String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : "";
                String numberE164 = PhoneNumberUtils.formatNumberToE164(number,
                        mDbHelper.getCurrentCountryIso());
                String normalizedNumber =
                        PhoneNumberUtils.normalizeNumber(number);
                profileRestrictionColumnName = Contacts.IS_USER_PROFILE;
                mDbHelper.buildPhoneLookupAndContactQuery(qb, normalizedNumber, numberE164);
                qb.setProjectionMap(sPhoneLookupProjectionMap);
                selection = null;
                selectionArgs = null;
                break;
            }
            case GROUPS: {
                qb.setTables(Views.GROUPS);
                qb.setProjectionMap(sGroupsProjectionMap);
                appendAccountFromParameter(qb, uri, true);
                break;
            }
            case GROUPS_ID: {
                qb.setTables(Views.GROUPS);
                qb.setProjectionMap(sGroupsProjectionMap);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(Groups._ID + "=?");
                break;
            }
            case GROUPS_SUMMARY: {
                final boolean returnGroupCountPerAccount =
                        readBooleanQueryParameter(uri, Groups.PARAM_RETURN_GROUP_COUNT_PER_ACCOUNT,
                                false);
                qb.setTables(Views.GROUPS + " AS " + Tables.GROUPS);
                qb.setProjectionMap(returnGroupCountPerAccount ?
                        sGroupsSummaryProjectionMapWithGroupCountPerAccount
                        : sGroupsSummaryProjectionMap);
                appendAccountFromParameter(qb, uri, true);
                groupBy = GroupsColumns.CONCRETE_ID;
                break;
            }
            case AGGREGATION_EXCEPTIONS: {
                qb.setTables(Tables.AGGREGATION_EXCEPTIONS);
                qb.setProjectionMap(sAggregationExceptionsProjectionMap);
                break;
            }
            case AGGREGATION_SUGGESTIONS: {
                long contactId = Long.parseLong(uri.getPathSegments().get(1));
                String filter = null;
                if (uri.getPathSegments().size() > 3) {
                    filter = uri.getPathSegments().get(3);
                }
                final int maxSuggestions;
                if (limit != null) {
                    maxSuggestions = Integer.parseInt(limit);
                } else {
                    maxSuggestions = DEFAULT_MAX_SUGGESTIONS;
                }
                ArrayList<AggregationSuggestionParameter> parameters = null;
                List<String> query = uri.getQueryParameters("query");
                if (query != null && !query.isEmpty()) {
                    parameters = new ArrayList<AggregationSuggestionParameter>(query.size());
                    for (String parameter : query) {
                        int offset = parameter.indexOf(':');
                        parameters.add(offset == -1
                                ? new AggregationSuggestionParameter(
                                        AggregationSuggestions.PARAMETER_MATCH_NAME,
                                        parameter)
                                : new AggregationSuggestionParameter(
                                        parameter.substring(0, offset),
                                        parameter.substring(offset + 1)));
                    }
                }
                setTablesAndProjectionMapForContacts(qb, uri, projection);
                return mContactAggregator.queryAggregationSuggestions(qb, projection, contactId,
                        maxSuggestions, filter, parameters);
            }
            case SETTINGS: {
                qb.setTables(Tables.SETTINGS);
                qb.setProjectionMap(sSettingsProjectionMap);
                appendAccountFromParameter(qb, uri, false);
                final String groupMembershipMimetypeId = Long.toString(mDbHelper
                        .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE));
                if (projection != null && projection.length != 0 &&
                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) {
                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
                }
                if (projection != null && projection.length != 0 &&
                        mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) {
                    selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId);
                }
                break;
            }
            case STATUS_UPDATES: {
                setTableAndProjectionMapForStatusUpdates(qb, projection);
                break;
            }
            case STATUS_UPDATES_ID: {
                setTableAndProjectionMapForStatusUpdates(qb, projection);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                qb.appendWhere(DataColumns.CONCRETE_ID + "=?");
                break;
            }
            case SEARCH_SUGGESTIONS: {
                return mGlobalSearchSupport.handleSearchSuggestionsQuery(
                        db, uri, projection, limit);
            }
            case SEARCH_SHORTCUT: {
                String lookupKey = uri.getLastPathSegment();
                String filter = getQueryParameter(
                        uri, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA);
                return mGlobalSearchSupport.handleSearchShortcutRefresh(
                        db, projection, lookupKey, filter);
            }
            case LIVE_FOLDERS_CONTACTS:
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sLiveFoldersProjectionMap);
                break;
            case LIVE_FOLDERS_CONTACTS_WITH_PHONES:
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sLiveFoldersProjectionMap);
                qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1");
                break;
            case LIVE_FOLDERS_CONTACTS_FAVORITES:
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sLiveFoldersProjectionMap);
                qb.appendWhere(Contacts.STARRED + "=1");
                break;
            case LIVE_FOLDERS_CONTACTS_GROUP_NAME:
                qb.setTables(Views.CONTACTS);
                qb.setProjectionMap(sLiveFoldersProjectionMap);
                qb.appendWhere(CONTACTS_IN_GROUP_SELECT);
                selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment());
                break;
            case RAW_CONTACT_ENTITIES: {
                setTablesAndProjectionMapForRawEntities(qb, uri);
                break;
            }
            case RAW_CONTACT_ENTITY_ID: {
                long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
                setTablesAndProjectionMapForRawEntities(qb, uri);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(rawContactId));
                qb.appendWhere(" AND " + RawContacts._ID + "=?");
                break;
            }
            case PROVIDER_STATUS: {
                return queryProviderStatus(uri, projection);
            }
            case DIRECTORIES : {
                qb.setTables(Tables.DIRECTORIES);
                qb.setProjectionMap(sDirectoryProjectionMap);
                break;
            }
            case DIRECTORIES_ID : {
                long id = ContentUris.parseId(uri);
                qb.setTables(Tables.DIRECTORIES);
                qb.setProjectionMap(sDirectoryProjectionMap);
                selectionArgs = insertSelectionArg(selectionArgs, String.valueOf(id));
                qb.appendWhere(Directory._ID + "=?");
                break;
            }
            case COMPLETE_NAME: {
                return completeName(uri, projection);
            }
            default:
                return mLegacyApiSupport.query(uri, projection, selection, selectionArgs,
                        sortOrder, limit);
        }
        qb.setStrict(true);
        if (profileRestrictionColumnName != null) {
            selection = appendProfileRestriction(uri, profileRestrictionColumnName,
                    suppressProfileCheck, selection);
        }
        Cursor cursor =
                query(db, qb, projection, selection, selectionArgs, sortOrder, groupBy, limit);
        if (readBooleanQueryParameter(uri, ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, false)) {
            cursor = bundleLetterCountExtras(cursor, db, qb, selection, selectionArgs, sortOrder);
        }
        return cursor;
    }
