Home | History | Annotate | Download | only in contacts
      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      */
     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
    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      */
    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 
    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