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