1bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell/*
2bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * Copyright (C) 2014 The Android Open Source Project
3bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell *
4bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * Licensed under the Apache License, Version 2.0 (the "License");
5bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * you may not use this file except in compliance with the License.
6bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * You may obtain a copy of the License at
7bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell *
8bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell *      http://www.apache.org/licenses/LICENSE-2.0
9bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell *
10bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * Unless required by applicable law or agreed to in writing, software
11bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * distributed under the License is distributed on an "AS IS" BASIS,
12bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * See the License for the specific language governing permissions and
14bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * limitations under the License
15bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell */
16bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwellpackage com.android.providers.contacts;
17bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
18bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwellimport com.android.internal.annotations.VisibleForTesting;
19bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
20bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwellimport android.database.Cursor;
21bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwellimport android.database.MatrixCursor;
22bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwellimport android.provider.ContactsContract.PhoneLookup;
23bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwellimport android.telephony.PhoneNumberUtils;
24bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwellimport android.text.TextUtils;
25bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwellimport android.util.Log;
26bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
27bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell/**
28bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell * Helper class for PHONE_LOOKUP's that involve numbers with "*" prefixes.
29bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell */
30bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell/* package-protected */ final class PhoneLookupWithStarPrefix {
31bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    private static final String TAG = "PhoneLookupWSP";
32bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
33bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    /**
34bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * Returns a cursor with a subset of the rows passed into this function. If {@param number}
35bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * starts with a "*" then only rows from {@param cursor} that have a number equal to
36bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * {@param number} will be returned. If {@param number} doesn't start with a "*", then
37bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * only rows from {@param cursor} that have numbers without starting "*" characters
38bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * will be returned.
39bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     *
40bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * This function is used to resolve b/13195334.
41bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     *
42bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * @param number unnormalized phone number.
43bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * @param cursor this function takes ownership of the cursor. The calling scope MUST NOT
44bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * use or close() the cursor passed into this function. The cursor must contain
45bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * PhoneLookup.NUMBER.
46bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     *
47bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * @return a cursor that the calling context owns
48bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     */
49bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    public static Cursor removeNonStarMatchesFromCursor(String number, Cursor cursor) {
50bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
51bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        // Close cursors that we don't return.
52bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        Cursor unreturnedCursor = cursor;
53bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
54bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        try {
55bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            if (TextUtils.isEmpty(number)) {
56bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                unreturnedCursor = null;
57bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return cursor;
58bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            }
59bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
60bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            final String queryPhoneNumberNormalized = normalizeNumberWithStar(number);
61bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            if (!queryPhoneNumberNormalized.startsWith("*")
62bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                    && !matchingNumberStartsWithStar(cursor)) {
63bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                cursor.moveToPosition(-1);
64bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                unreturnedCursor = null;
65bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return cursor;
66bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            }
67bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
68bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            final MatrixCursor matrixCursor = new MatrixCursor(cursor.getColumnNames());
69bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
70bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            // Close cursors that we don't return.
71bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            Cursor unreturnedMatrixCursor = matrixCursor;
72bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
73bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            try {
74bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                cursor.moveToPosition(-1);
75bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                while (cursor.moveToNext()) {
76bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                    final int numberIndex = cursor.getColumnIndex(PhoneLookup.NUMBER);
77bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                    final String matchingNumberNormalized
78bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                            = normalizeNumberWithStar(cursor.getString(numberIndex));
79bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                    if (!matchingNumberNormalized.startsWith("*")
80bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                            && !queryPhoneNumberNormalized.startsWith("*")
81bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                            || matchingNumberNormalized.equals(queryPhoneNumberNormalized)) {
82bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                        // Copy row from cursor into matrixCursor
83bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                        final MatrixCursor.RowBuilder b = matrixCursor.newRow();
84bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                        for (int column = 0; column < cursor.getColumnCount(); column++) {
85bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                            b.add(cursor.getColumnName(column), cursorValue(cursor, column));
86bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                        }
87bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                    }
88bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                }
89bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                unreturnedMatrixCursor = null;
90bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return matrixCursor;
91bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            } finally {
92bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                if (unreturnedMatrixCursor != null) {
93bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                    unreturnedMatrixCursor.close();
94bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                }
95bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            }
96bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        } finally {
97bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            if (unreturnedCursor != null) {
98bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                unreturnedCursor.close();
99bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            }
100bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        }
101bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    }
102bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
103bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    @VisibleForTesting
104bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    static String normalizeNumberWithStar(String phoneNumber) {
105bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        if (TextUtils.isEmpty(phoneNumber)) {
106bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            return phoneNumber;
107bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        }
108bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        if (phoneNumber.startsWith("*")) {
109bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            // Use PhoneNumberUtils.normalizeNumber() to normalize the rest of the number after
110bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            // the leading "*". Strip out the "+" since "+"s are only allowed as leading
111bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            // characters. NOTE: This statement has poor performance. Fortunately, it won't be
112bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            // called very often.
113bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            return "*" + PhoneNumberUtils.normalizeNumber(
114bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                    phoneNumber.substring(1).replace("+", ""));
115bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        }
116bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        return PhoneNumberUtils.normalizeNumber(phoneNumber);
117bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    }
118bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
119bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    /**
120bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     * @return whether {@param cursor} contain any numbers that start with "*"
121bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell     */
122bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    private static boolean matchingNumberStartsWithStar(Cursor cursor) {
123bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        cursor.moveToPosition(-1);
124bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        while (cursor.moveToNext()) {
125bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            final int numberIndex = cursor.getColumnIndex(PhoneLookup.NUMBER);
126bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            final String phoneNumber = normalizeNumberWithStar(cursor.getString(numberIndex));
127bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            if (phoneNumber.startsWith("*")) {
128bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return true;
129bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            }
130bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        }
131bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        return false;
132bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    }
133bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell
134bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    private static Object cursorValue(Cursor cursor, int column) {
135bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        switch(cursor.getType(column)) {
136bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            case Cursor.FIELD_TYPE_BLOB:
137bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return cursor.getBlob(column);
138bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            case Cursor.FIELD_TYPE_INTEGER:
139bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return cursor.getInt(column);
140bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            case Cursor.FIELD_TYPE_FLOAT:
141bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return cursor.getFloat(column);
142bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            case Cursor.FIELD_TYPE_STRING:
143bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return cursor.getString(column);
144bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            case Cursor.FIELD_TYPE_NULL:
145bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return null;
146bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell            default:
147bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                Log.d(TAG, "Invalid value in cursor: " + cursor.getType(column));
148bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell                return null;
149bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell        }
150bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell    }
151bf98e55afd5f39f72dc05c704409655b89a7fa25Brian Attwell}
152