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