| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2006 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 |  | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 17 | package android.telecom; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 18 |  | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 19 | import android.annotation.Nullable; | 
| Artur Satayev | 2ebb31c | 2020-01-08 12:24:36 +0000 | [diff] [blame] | 20 | import android.compat.annotation.UnsupportedAppUsage; | 
| Hall Liu | 7d02a83 | 2018-11-21 14:40:19 -0800 | [diff] [blame] | 21 | import android.content.ComponentName; | 
| Roshan Pius | 93018a4 | 2015-07-13 12:57:40 -0700 | [diff] [blame] | 22 | import android.content.ContentResolver; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 23 | import android.content.Context; | 
|  | 24 | import android.database.Cursor; | 
| Daisuke Miyakawa | 2659748 | 2012-04-16 14:18:09 -0700 | [diff] [blame] | 25 | import android.graphics.Bitmap; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 26 | import android.graphics.drawable.Drawable; | 
| Jake Hamby | 7f5bee0 | 2013-10-08 16:31:16 -0700 | [diff] [blame] | 27 | import android.location.Country; | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 28 | import android.location.CountryDetector; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 29 | import android.net.Uri; | 
| Mathew Inwood | 8e742f9 | 2020-10-27 11:47:29 +0000 | [diff] [blame] | 30 | import android.os.Build; | 
| Dmitri Plotnikov | 3c513ed | 2009-08-19 15:56:30 -0700 | [diff] [blame] | 31 | import android.provider.ContactsContract.CommonDataKinds.Phone; | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 32 | import android.provider.ContactsContract.Contacts; | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 33 | import android.provider.ContactsContract.Data; | 
|  | 34 | import android.provider.ContactsContract.PhoneLookup; | 
|  | 35 | import android.provider.ContactsContract.RawContacts; | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 36 | import android.telephony.PhoneNumberUtils; | 
|  | 37 | import android.telephony.SubscriptionManager; | 
|  | 38 | import android.telephony.TelephonyManager; | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 39 | import android.text.TextUtils; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 40 |  | 
| Shaopeng Jia | e713576 | 2011-08-12 13:25:41 +0200 | [diff] [blame] | 41 | import com.android.i18n.phonenumbers.NumberParseException; | 
|  | 42 | import com.android.i18n.phonenumbers.PhoneNumberUtil; | 
|  | 43 | import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber; | 
| Hall Liu | 7d02a83 | 2018-11-21 14:40:19 -0800 | [diff] [blame] | 44 | import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder; | 
| Austin Wang | a63a2c0 | 2019-12-19 06:38:19 +0000 | [diff] [blame] | 45 | import com.android.internal.annotations.VisibleForTesting; | 
| Artur Satayev | 2ebb31c | 2020-01-08 12:24:36 +0000 | [diff] [blame] | 46 |  | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 47 | import java.util.Locale; | 
|  | 48 |  | 
| Wink Saville | 2e27a0b | 2010-10-07 08:28:34 -0700 | [diff] [blame] | 49 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 50 | /** | 
|  | 51 | * Looks up caller information for the given phone number. | 
|  | 52 | * | 
|  | 53 | * {@hide} | 
|  | 54 | */ | 
|  | 55 | public class CallerInfo { | 
|  | 56 | private static final String TAG = "CallerInfo"; | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 57 | private static final boolean VDBG = Log.VERBOSE; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 58 |  | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 59 | /** @hide */ | 
| Victor Chang | 9359ee1 | 2016-01-04 15:48:09 +0000 | [diff] [blame] | 60 | public static final long USER_TYPE_CURRENT = 0; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 61 | /** @hide */ | 
| Victor Chang | 9359ee1 | 2016-01-04 15:48:09 +0000 | [diff] [blame] | 62 | public static final long USER_TYPE_WORK = 1; | 
|  | 63 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 64 | /** | 
|  | 65 | * Please note that, any one of these member variables can be null, | 
|  | 66 | * and any accesses to them should be prepared to handle such a case. | 
|  | 67 | * | 
|  | 68 | * Also, it is implied that phoneNumber is more often populated than | 
|  | 69 | * name is, (think of calls being dialed/received using numbers where | 
|  | 70 | * names are not known to the device), so phoneNumber should serve as | 
|  | 71 | * a dependable fallback when name is unavailable. | 
|  | 72 | * | 
|  | 73 | * One other detail here is that this CallerInfo object reflects | 
|  | 74 | * information found on a connection, it is an OUTPUT that serves | 
|  | 75 | * mainly to display information to the user.  In no way is this object | 
|  | 76 | * used as input to make a connection, so we can choose to display | 
|  | 77 | * whatever human-readable text makes sense to the user for a | 
|  | 78 | * connection.  This is especially relevant for the phone number field, | 
|  | 79 | * since it is the one field that is most likely exposed to the user. | 
|  | 80 | * | 
|  | 81 | * As an example: | 
|  | 82 | *   1. User dials "911" | 
|  | 83 | *   2. Device recognizes that this is an emergency number | 
|  | 84 | *   3. We use the "Emergency Number" string instead of "911" in the | 
|  | 85 | *     phoneNumber field. | 
|  | 86 | * | 
|  | 87 | * What we're really doing here is treating phoneNumber as an essential | 
|  | 88 | * field here, NOT name.  We're NOT always guaranteed to have a name | 
|  | 89 | * for a connection, but the number should be displayable. | 
|  | 90 | */ | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 91 | private String name; | 
|  | 92 | private String phoneNumber; | 
|  | 93 | /** @hide */ | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 94 | public String normalizedNumber; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 95 | /** @hide */ | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 96 | public String geoDescription; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 97 | /** @hide */ | 
| Wink Saville | dda5391 | 2009-05-28 17:32:34 -0700 | [diff] [blame] | 98 | public String cnapName; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 99 | /** @hide */ | 
| Wink Saville | dda5391 | 2009-05-28 17:32:34 -0700 | [diff] [blame] | 100 | public int numberPresentation; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 101 | /** @hide */ | 
| Wink Saville | dda5391 | 2009-05-28 17:32:34 -0700 | [diff] [blame] | 102 | public int namePresentation; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 103 | /** @hide */ | 
| Wink Saville | dda5391 | 2009-05-28 17:32:34 -0700 | [diff] [blame] | 104 | public boolean contactExists; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 105 | /** @hide */ | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 106 | public String phoneLabel; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 107 | /** | 
|  | 108 | * Split up the phoneLabel into number type and label name. | 
|  | 109 | * @hide | 
|  | 110 | */ | 
| Mathew Inwood | b4936a3 | 2018-08-21 16:40:35 +0100 | [diff] [blame] | 111 | @UnsupportedAppUsage | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 112 | public int    numberType; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 113 | /** @hide */ | 
| Mathew Inwood | b4936a3 | 2018-08-21 16:40:35 +0100 | [diff] [blame] | 114 | @UnsupportedAppUsage | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 115 | public String numberLabel; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 116 | /** @hide */ | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 117 | public int photoResource; | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 118 |  | 
|  | 119 | // Contact ID, which will be 0 if a contact comes from the corp CP2. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 120 | private long contactIdOrZero; | 
|  | 121 | /** @hide */ | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 122 | public boolean needUpdate; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 123 | /** @hide */ | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 124 | public Uri contactRefUri; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 125 | /** @hide */ | 
| Yorke Lee | 282f368 | 2014-08-29 18:39:16 -0700 | [diff] [blame] | 126 | public String lookupKey; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 127 | /** @hide */ | 
| Hall Liu | 7d02a83 | 2018-11-21 14:40:19 -0800 | [diff] [blame] | 128 | public ComponentName preferredPhoneAccountComponent; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 129 | /** @hide */ | 
| Hall Liu | 7d02a83 | 2018-11-21 14:40:19 -0800 | [diff] [blame] | 130 | public String preferredPhoneAccountId; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 131 | /** @hide */ | 
| Victor Chang | 9359ee1 | 2016-01-04 15:48:09 +0000 | [diff] [blame] | 132 | public long userType; | 
|  | 133 |  | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 134 | /** | 
|  | 135 | * Contact display photo URI.  If a contact has no display photo but a thumbnail, it'll be | 
|  | 136 | * the thumbnail URI instead. | 
|  | 137 | */ | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 138 | private Uri contactDisplayPhotoUri; | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 139 |  | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 140 | // fields to hold individual contact preference data, | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 141 | // including the send to voicemail flag and the ringtone | 
|  | 142 | // uri reference. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 143 | /** @hide */ | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 144 | public Uri contactRingtoneUri; | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 145 | /** @hide */ | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 146 | public boolean shouldSendToVoicemail; | 
|  | 147 |  | 
|  | 148 | /** | 
|  | 149 | * Drawable representing the caller image.  This is essentially | 
|  | 150 | * a cache for the image data tied into the connection / | 
| Daisuke Miyakawa | 2659748 | 2012-04-16 14:18:09 -0700 | [diff] [blame] | 151 | * callerinfo object. | 
|  | 152 | * | 
|  | 153 | * This might be a high resolution picture which is more suitable | 
|  | 154 | * for full-screen image view than for smaller icons used in some | 
|  | 155 | * kinds of notifications. | 
|  | 156 | * | 
|  | 157 | * The {@link #isCachedPhotoCurrent} flag indicates if the image | 
|  | 158 | * data needs to be reloaded. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 159 | * | 
|  | 160 | * @hide | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 161 | */ | 
|  | 162 | public Drawable cachedPhoto; | 
| Daisuke Miyakawa | 2659748 | 2012-04-16 14:18:09 -0700 | [diff] [blame] | 163 | /** | 
|  | 164 | * Bitmap representing the caller image which has possibly lower | 
|  | 165 | * resolution than {@link #cachedPhoto} and thus more suitable for | 
|  | 166 | * icons (like notification icons). | 
|  | 167 | * | 
|  | 168 | * In usual cases this is just down-scaled image of {@link #cachedPhoto}. | 
|  | 169 | * If the down-scaling fails, this will just become null. | 
|  | 170 | * | 
|  | 171 | * The {@link #isCachedPhotoCurrent} flag indicates if the image | 
|  | 172 | * data needs to be reloaded. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 173 | * | 
|  | 174 | * @hide | 
| Daisuke Miyakawa | 2659748 | 2012-04-16 14:18:09 -0700 | [diff] [blame] | 175 | */ | 
|  | 176 | public Bitmap cachedPhotoIcon; | 
|  | 177 | /** | 
|  | 178 | * Boolean which indicates if {@link #cachedPhoto} and | 
|  | 179 | * {@link #cachedPhotoIcon} is fresh enough. If it is false, | 
|  | 180 | * those images aren't pointing to valid objects. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 181 | * | 
|  | 182 | * @hide | 
| Daisuke Miyakawa | 2659748 | 2012-04-16 14:18:09 -0700 | [diff] [blame] | 183 | */ | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 184 | public boolean isCachedPhotoCurrent; | 
|  | 185 |  | 
| Nicolas Catania | e224158 | 2009-09-14 19:01:43 -0700 | [diff] [blame] | 186 | private boolean mIsEmergency; | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 187 | private boolean mIsVoiceMail; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 188 |  | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 189 | /** @hide */ | 
| Mathew Inwood | 8e742f9 | 2020-10-27 11:47:29 +0000 | [diff] [blame] | 190 | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 191 | public CallerInfo() { | 
| Nicolas Catania | e224158 | 2009-09-14 19:01:43 -0700 | [diff] [blame] | 192 | // TODO: Move all the basic initialization here? | 
|  | 193 | mIsEmergency = false; | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 194 | mIsVoiceMail = false; | 
| Victor Chang | 9359ee1 | 2016-01-04 15:48:09 +0000 | [diff] [blame] | 195 | userType = USER_TYPE_CURRENT; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 196 | } | 
|  | 197 |  | 
|  | 198 | /** | 
|  | 199 | * getCallerInfo given a Cursor. | 
|  | 200 | * @param context the context used to retrieve string constants | 
|  | 201 | * @param contactRef the URI to attach to this CallerInfo object | 
|  | 202 | * @param cursor the first object in the cursor is used to build the CallerInfo object. | 
|  | 203 | * @return the CallerInfo which contains the caller id for the given | 
|  | 204 | * number. The returned CallerInfo is null if no number is supplied. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 205 | * | 
|  | 206 | * @hide | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 207 | */ | 
|  | 208 | public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) { | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 209 | CallerInfo info = new CallerInfo(); | 
|  | 210 | info.photoResource = 0; | 
|  | 211 | info.phoneLabel = null; | 
|  | 212 | info.numberType = 0; | 
|  | 213 | info.numberLabel = null; | 
|  | 214 | info.cachedPhoto = null; | 
|  | 215 | info.isCachedPhotoCurrent = false; | 
| Wink Saville | dda5391 | 2009-05-28 17:32:34 -0700 | [diff] [blame] | 216 | info.contactExists = false; | 
| Victor Chang | 9359ee1 | 2016-01-04 15:48:09 +0000 | [diff] [blame] | 217 | info.userType = USER_TYPE_CURRENT; | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 218 |  | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 219 | if (VDBG) Log.v(TAG, "getCallerInfo() based on cursor..."); | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 220 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 221 | if (cursor != null) { | 
|  | 222 | if (cursor.moveToFirst()) { | 
| Nicolas Catania | c72509b | 2010-01-05 16:03:08 -0800 | [diff] [blame] | 223 | // TODO: photo_id is always available but not taken | 
|  | 224 | // care of here. Maybe we should store it in the | 
|  | 225 | // CallerInfo object as well. | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 226 |  | 
|  | 227 | int columnIndex; | 
|  | 228 |  | 
|  | 229 | // Look for the name | 
| Dmitri Plotnikov | 3c513ed | 2009-08-19 15:56:30 -0700 | [diff] [blame] | 230 | columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME); | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 231 | if (columnIndex != -1) { | 
|  | 232 | info.name = cursor.getString(columnIndex); | 
|  | 233 | } | 
|  | 234 |  | 
|  | 235 | // Look for the number | 
| Dmitri Plotnikov | 3c513ed | 2009-08-19 15:56:30 -0700 | [diff] [blame] | 236 | columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER); | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 237 | if (columnIndex != -1) { | 
|  | 238 | info.phoneNumber = cursor.getString(columnIndex); | 
|  | 239 | } | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 240 |  | 
| Bai Tao | 6a3d188 | 2010-08-31 17:46:33 +0800 | [diff] [blame] | 241 | // Look for the normalized number | 
|  | 242 | columnIndex = cursor.getColumnIndex(PhoneLookup.NORMALIZED_NUMBER); | 
|  | 243 | if (columnIndex != -1) { | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 244 | info.normalizedNumber = cursor.getString(columnIndex); | 
| Bai Tao | 6a3d188 | 2010-08-31 17:46:33 +0800 | [diff] [blame] | 245 | } | 
|  | 246 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 247 | // Look for the label/type combo | 
| Dmitri Plotnikov | 3c513ed | 2009-08-19 15:56:30 -0700 | [diff] [blame] | 248 | columnIndex = cursor.getColumnIndex(PhoneLookup.LABEL); | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 249 | if (columnIndex != -1) { | 
| Dmitri Plotnikov | 3c513ed | 2009-08-19 15:56:30 -0700 | [diff] [blame] | 250 | int typeColumnIndex = cursor.getColumnIndex(PhoneLookup.TYPE); | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 251 | if (typeColumnIndex != -1) { | 
|  | 252 | info.numberType = cursor.getInt(typeColumnIndex); | 
|  | 253 | info.numberLabel = cursor.getString(columnIndex); | 
| Dmitri Plotnikov | 3c513ed | 2009-08-19 15:56:30 -0700 | [diff] [blame] | 254 | info.phoneLabel = Phone.getDisplayLabel(context, | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 255 | info.numberType, info.numberLabel) | 
|  | 256 | .toString(); | 
|  | 257 | } | 
|  | 258 | } | 
|  | 259 |  | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 260 | // Look for the person_id. | 
|  | 261 | columnIndex = getColumnIndexForPersonId(contactRef, cursor); | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 262 | if (columnIndex != -1) { | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 263 | final long contactId = cursor.getLong(columnIndex); | 
| Makoto Onuki | 0e91733 | 2014-08-26 14:06:30 -0700 | [diff] [blame] | 264 | if (contactId != 0 && !Contacts.isEnterpriseContactId(contactId)) { | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 265 | info.contactIdOrZero = contactId; | 
|  | 266 | if (VDBG) { | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 267 | Log.v(TAG, "==> got info.contactIdOrZero: " + info.contactIdOrZero); | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 268 | } | 
|  | 269 | } | 
| Victor Chang | 9359ee1 | 2016-01-04 15:48:09 +0000 | [diff] [blame] | 270 | if (Contacts.isEnterpriseContactId(contactId)) { | 
|  | 271 | info.userType = USER_TYPE_WORK; | 
|  | 272 | } | 
| Nicolas Catania | c72509b | 2010-01-05 16:03:08 -0800 | [diff] [blame] | 273 | } else { | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 274 | // No valid columnIndex, so we can't look up person_id. | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 275 | Log.w(TAG, "Couldn't find contact_id column for " + contactRef); | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 276 | // Watch out: this means that anything that depends on | 
|  | 277 | // person_id will be broken (like contact photo lookups in | 
|  | 278 | // the in-call UI, for example.) | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 279 | } | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 280 |  | 
| Yorke Lee | 282f368 | 2014-08-29 18:39:16 -0700 | [diff] [blame] | 281 | // Contact lookupKey | 
|  | 282 | columnIndex = cursor.getColumnIndex(PhoneLookup.LOOKUP_KEY); | 
|  | 283 | if (columnIndex != -1) { | 
|  | 284 | info.lookupKey = cursor.getString(columnIndex); | 
|  | 285 | } | 
|  | 286 |  | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 287 | // Display photo URI. | 
|  | 288 | columnIndex = cursor.getColumnIndex(PhoneLookup.PHOTO_URI); | 
|  | 289 | if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) { | 
|  | 290 | info.contactDisplayPhotoUri = Uri.parse(cursor.getString(columnIndex)); | 
|  | 291 | } else { | 
|  | 292 | info.contactDisplayPhotoUri = null; | 
|  | 293 | } | 
|  | 294 |  | 
| Hall Liu | 7d02a83 | 2018-11-21 14:40:19 -0800 | [diff] [blame] | 295 | columnIndex = cursor.getColumnIndex(Data.PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME); | 
|  | 296 | if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) { | 
|  | 297 | info.preferredPhoneAccountComponent = | 
|  | 298 | ComponentName.unflattenFromString(cursor.getString(columnIndex)); | 
|  | 299 | } | 
|  | 300 |  | 
|  | 301 | columnIndex = cursor.getColumnIndex(Data.PREFERRED_PHONE_ACCOUNT_ID); | 
|  | 302 | if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) { | 
|  | 303 | info.preferredPhoneAccountId = cursor.getString(columnIndex); | 
|  | 304 | } | 
|  | 305 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 306 | // look for the custom ringtone, create from the string stored | 
|  | 307 | // in the database. | 
| Wenyi Wang | 750bb85 | 2015-09-15 16:46:29 -0700 | [diff] [blame] | 308 | // An empty string ("") in the database indicates a silent ringtone, | 
|  | 309 | // and we set contactRingtoneUri = Uri.EMPTY, so that no ringtone will be played. | 
|  | 310 | // {null} in the database indicates the default ringtone, | 
|  | 311 | // and we set contactRingtoneUri = null, so that default ringtone will be played. | 
| Dmitri Plotnikov | 3c513ed | 2009-08-19 15:56:30 -0700 | [diff] [blame] | 312 | columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE); | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 313 | if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) { | 
| Wenyi Wang | 750bb85 | 2015-09-15 16:46:29 -0700 | [diff] [blame] | 314 | if (TextUtils.isEmpty(cursor.getString(columnIndex))) { | 
|  | 315 | info.contactRingtoneUri = Uri.EMPTY; | 
|  | 316 | } else { | 
|  | 317 | info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex)); | 
|  | 318 | } | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 319 | } else { | 
|  | 320 | info.contactRingtoneUri = null; | 
|  | 321 | } | 
|  | 322 |  | 
|  | 323 | // look for the send to voicemail flag, set it to true only | 
|  | 324 | // under certain circumstances. | 
| Dmitri Plotnikov | 3c513ed | 2009-08-19 15:56:30 -0700 | [diff] [blame] | 325 | columnIndex = cursor.getColumnIndex(PhoneLookup.SEND_TO_VOICEMAIL); | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 326 | info.shouldSendToVoicemail = (columnIndex != -1) && | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 327 | ((cursor.getInt(columnIndex)) == 1); | 
| Wink Saville | dda5391 | 2009-05-28 17:32:34 -0700 | [diff] [blame] | 328 | info.contactExists = true; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 329 | } | 
|  | 330 | cursor.close(); | 
| Wink Saville | fb40dd4 | 2014-06-12 17:02:31 -0700 | [diff] [blame] | 331 | cursor = null; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 332 | } | 
|  | 333 |  | 
|  | 334 | info.needUpdate = false; | 
|  | 335 | info.name = normalize(info.name); | 
|  | 336 | info.contactRefUri = contactRef; | 
|  | 337 |  | 
|  | 338 | return info; | 
|  | 339 | } | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 340 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 341 | /** | 
|  | 342 | * getCallerInfo given a URI, look up in the call-log database | 
|  | 343 | * for the uri unique key. | 
|  | 344 | * @param context the context used to get the ContentResolver | 
|  | 345 | * @param contactRef the URI used to lookup caller id | 
|  | 346 | * @return the CallerInfo which contains the caller id for the given | 
|  | 347 | * number. The returned CallerInfo is null if no number is supplied. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 348 | * | 
|  | 349 | * @hide | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 350 | */ | 
| Mathew Inwood | 8e742f9 | 2020-10-27 11:47:29 +0000 | [diff] [blame] | 351 | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 352 | public static CallerInfo getCallerInfo(Context context, Uri contactRef) { | 
| Roshan Pius | 93018a4 | 2015-07-13 12:57:40 -0700 | [diff] [blame] | 353 | CallerInfo info = null; | 
|  | 354 | ContentResolver cr = CallerInfoAsyncQuery.getCurrentProfileContentResolver(context); | 
|  | 355 | if (cr != null) { | 
|  | 356 | try { | 
|  | 357 | info = getCallerInfo(context, contactRef, | 
|  | 358 | cr.query(contactRef, null, null, null, null)); | 
|  | 359 | } catch (RuntimeException re) { | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 360 | Log.e(TAG, re, "Error getting caller info."); | 
| Roshan Pius | 93018a4 | 2015-07-13 12:57:40 -0700 | [diff] [blame] | 361 | } | 
|  | 362 | } | 
|  | 363 | return info; | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 364 | } | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 365 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 366 | /** | 
|  | 367 | * getCallerInfo given a phone number, look up in the call-log database | 
|  | 368 | * for the matching caller id info. | 
|  | 369 | * @param context the context used to get the ContentResolver | 
|  | 370 | * @param number the phone number used to lookup caller id | 
|  | 371 | * @return the CallerInfo which contains the caller id for the given | 
|  | 372 | * number. The returned CallerInfo is null if no number is supplied. If | 
|  | 373 | * a matching number is not found, then a generic caller info is returned, | 
|  | 374 | * with all relevant fields empty or null. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 375 | * | 
|  | 376 | * @hide | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 377 | */ | 
| Mathew Inwood | 8e742f9 | 2020-10-27 11:47:29 +0000 | [diff] [blame] | 378 | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 379 | public static CallerInfo getCallerInfo(Context context, String number) { | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 380 | if (VDBG) Log.v(TAG, "getCallerInfo() based on number..."); | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 381 |  | 
| Shishir Agrawal | 7ea3e8b | 2016-01-25 13:03:07 -0800 | [diff] [blame] | 382 | int subId = SubscriptionManager.getDefaultSubscriptionId(); | 
| Wink Saville | fb40dd4 | 2014-06-12 17:02:31 -0700 | [diff] [blame] | 383 | return getCallerInfo(context, number, subId); | 
|  | 384 | } | 
|  | 385 |  | 
|  | 386 | /** | 
|  | 387 | * getCallerInfo given a phone number and subscription, look up in the call-log database | 
|  | 388 | * for the matching caller id info. | 
|  | 389 | * @param context the context used to get the ContentResolver | 
|  | 390 | * @param number the phone number used to lookup caller id | 
|  | 391 | * @param subId the subscription for checking for if voice mail number or not | 
|  | 392 | * @return the CallerInfo which contains the caller id for the given | 
|  | 393 | * number. The returned CallerInfo is null if no number is supplied. If | 
|  | 394 | * a matching number is not found, then a generic caller info is returned, | 
|  | 395 | * with all relevant fields empty or null. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 396 | * | 
|  | 397 | * @hide | 
| Wink Saville | fb40dd4 | 2014-06-12 17:02:31 -0700 | [diff] [blame] | 398 | */ | 
| Mathew Inwood | 8e742f9 | 2020-10-27 11:47:29 +0000 | [diff] [blame] | 399 | @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) | 
| Wink Saville | 63f03dd | 2014-10-23 10:44:45 -0700 | [diff] [blame] | 400 | public static CallerInfo getCallerInfo(Context context, String number, int subId) { | 
| Wink Saville | fb40dd4 | 2014-06-12 17:02:31 -0700 | [diff] [blame] | 401 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 402 | if (TextUtils.isEmpty(number)) { | 
|  | 403 | return null; | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 404 | } | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 405 |  | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 406 | // Change the callerInfo number ONLY if it is an emergency number | 
|  | 407 | // or if it is the voicemail number.  If it is either, take a | 
|  | 408 | // shortcut and skip the query. | 
| Taesu Lee | 902b89d | 2020-10-07 14:55:25 +0900 | [diff] [blame] | 409 | TelephonyManager tm = context.getSystemService(TelephonyManager.class); | 
|  | 410 | if (tm.isEmergencyNumber(number)) { | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 411 | return new CallerInfo().markAsEmergency(context); | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 412 | } else if (PhoneNumberUtils.isVoiceMailNumber(null, subId, number)) { | 
|  | 413 | return new CallerInfo().markAsVoiceMail(context, subId); | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 414 | } | 
|  | 415 |  | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 416 | Uri contactUri = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, | 
|  | 417 | Uri.encode(number)); | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 418 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 419 | CallerInfo info = getCallerInfo(context, contactUri); | 
| Hung-ying Tyan | 6fe795e | 2010-10-20 11:12:02 +0800 | [diff] [blame] | 420 | info = doSecondaryLookupIfNecessary(context, number, info); | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 421 |  | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 422 | // if no query results were returned with a viable number, | 
|  | 423 | // fill in the original number value we used to query with. | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 424 | if (TextUtils.isEmpty(info.phoneNumber)) { | 
|  | 425 | info.phoneNumber = number; | 
|  | 426 | } | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 427 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 428 | return info; | 
|  | 429 | } | 
|  | 430 |  | 
|  | 431 | /** | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 432 | * @return Name assocaited with this caller. | 
|  | 433 | */ | 
|  | 434 | @Nullable | 
|  | 435 | public String getName() { | 
|  | 436 | return name; | 
|  | 437 | } | 
|  | 438 |  | 
|  | 439 | /** | 
|  | 440 | * Set caller Info Name. | 
|  | 441 | * @param name caller Info Name | 
|  | 442 | * | 
|  | 443 | * @hide | 
|  | 444 | */ | 
|  | 445 | public void setName(@Nullable String name) { | 
|  | 446 | this.name = name; | 
|  | 447 | } | 
|  | 448 |  | 
|  | 449 | /** | 
|  | 450 | * @return Phone number assocaited with this caller. | 
|  | 451 | */ | 
|  | 452 | @Nullable | 
|  | 453 | public String getPhoneNumber() { | 
|  | 454 | return phoneNumber; | 
|  | 455 | } | 
|  | 456 |  | 
|  | 457 | /** @hide */ | 
|  | 458 | public void setPhoneNumber(String number) { | 
|  | 459 | phoneNumber = number; | 
|  | 460 | } | 
|  | 461 |  | 
|  | 462 | /** | 
|  | 463 | * @return Contact ID, which will be 0 if a contact comes from the corp Contacts Provider. | 
|  | 464 | */ | 
|  | 465 | public long getContactId() { | 
|  | 466 | return contactIdOrZero; | 
|  | 467 | } | 
|  | 468 |  | 
|  | 469 | /** | 
|  | 470 | * @return Contact display photo URI. If a contact has no display photo but a thumbnail, | 
|  | 471 | * it'll the thumbnail URI instead. | 
|  | 472 | */ | 
|  | 473 | @Nullable | 
|  | 474 | public Uri getContactDisplayPhotoUri() { | 
|  | 475 | return contactDisplayPhotoUri; | 
|  | 476 | } | 
|  | 477 |  | 
|  | 478 | /** @hide */ | 
|  | 479 | @VisibleForTesting | 
|  | 480 | public void SetContactDisplayPhotoUri(Uri photoUri) { | 
|  | 481 | contactDisplayPhotoUri = photoUri; | 
|  | 482 | } | 
|  | 483 |  | 
|  | 484 | /** | 
| Hung-ying Tyan | 6fe795e | 2010-10-20 11:12:02 +0800 | [diff] [blame] | 485 | * Performs another lookup if previous lookup fails and it's a SIP call | 
|  | 486 | * and the peer's username is all numeric. Look up the username as it | 
|  | 487 | * could be a PSTN number in the contact database. | 
|  | 488 | * | 
|  | 489 | * @param context the query context | 
|  | 490 | * @param number the original phone number, could be a SIP URI | 
|  | 491 | * @param previousResult the result of previous lookup | 
|  | 492 | * @return previousResult if it's not the case | 
|  | 493 | */ | 
|  | 494 | static CallerInfo doSecondaryLookupIfNecessary(Context context, | 
|  | 495 | String number, CallerInfo previousResult) { | 
|  | 496 | if (!previousResult.contactExists | 
|  | 497 | && PhoneNumberUtils.isUriNumber(number)) { | 
| David Brown | 158f116 | 2011-11-16 22:10:56 -0800 | [diff] [blame] | 498 | String username = PhoneNumberUtils.getUsernameFromUriNumber(number); | 
| Hung-ying Tyan | 6fe795e | 2010-10-20 11:12:02 +0800 | [diff] [blame] | 499 | if (PhoneNumberUtils.isGlobalPhoneNumber(username)) { | 
|  | 500 | previousResult = getCallerInfo(context, | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 501 | Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, | 
| Hung-ying Tyan | 6fe795e | 2010-10-20 11:12:02 +0800 | [diff] [blame] | 502 | Uri.encode(username))); | 
|  | 503 | } | 
|  | 504 | } | 
|  | 505 | return previousResult; | 
|  | 506 | } | 
|  | 507 |  | 
| Nicolas Catania | e224158 | 2009-09-14 19:01:43 -0700 | [diff] [blame] | 508 | // Accessors | 
|  | 509 |  | 
|  | 510 | /** | 
|  | 511 | * @return true if the caller info is an emergency number. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 512 | * @hide | 
| Nicolas Catania | e224158 | 2009-09-14 19:01:43 -0700 | [diff] [blame] | 513 | */ | 
|  | 514 | public boolean isEmergencyNumber() { | 
|  | 515 | return mIsEmergency; | 
|  | 516 | } | 
|  | 517 |  | 
|  | 518 | /** | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 519 | * @return true if the caller info is a voicemail number. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 520 | * @hide | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 521 | */ | 
|  | 522 | public boolean isVoiceMailNumber() { | 
|  | 523 | return mIsVoiceMail; | 
|  | 524 | } | 
|  | 525 |  | 
|  | 526 | /** | 
| Nicolas Catania | e224158 | 2009-09-14 19:01:43 -0700 | [diff] [blame] | 527 | * Mark this CallerInfo as an emergency call. | 
|  | 528 | * @param context To lookup the localized 'Emergency Number' string. | 
|  | 529 | * @return this instance. | 
|  | 530 | */ | 
|  | 531 | // TODO: Note we're setting the phone number here (refer to | 
|  | 532 | // javadoc comments at the top of CallerInfo class) to a localized | 
|  | 533 | // string 'Emergency Number'. This is pretty bad because we are | 
|  | 534 | // making UI work here instead of just packaging the data. We | 
|  | 535 | // should set the phone number to the dialed number and name to | 
|  | 536 | // 'Emergency Number' and let the UI make the decision about what | 
|  | 537 | // should be displayed. | 
|  | 538 | /* package */ CallerInfo markAsEmergency(Context context) { | 
|  | 539 | phoneNumber = context.getString( | 
|  | 540 | com.android.internal.R.string.emergency_call_dialog_number_for_display); | 
|  | 541 | photoResource = com.android.internal.R.drawable.picture_emergency; | 
|  | 542 | mIsEmergency = true; | 
|  | 543 | return this; | 
|  | 544 | } | 
|  | 545 |  | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 546 |  | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 547 | /* package */ CallerInfo markAsVoiceMail(Context context, int subId) { | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 548 | mIsVoiceMail = true; | 
|  | 549 |  | 
|  | 550 | try { | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 551 | phoneNumber = context.getSystemService(TelephonyManager.class) | 
|  | 552 | .createForSubscriptionId(subId) | 
|  | 553 | .getVoiceMailAlphaTag(); | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 554 | } catch (SecurityException se) { | 
|  | 555 | // Should never happen: if this process does not have | 
|  | 556 | // permission to retrieve VM tag, it should not have | 
|  | 557 | // permission to retrieve VM number and would not call | 
|  | 558 | // this method. | 
|  | 559 | // Leave phoneNumber untouched. | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 560 | Log.e(TAG, se, "Cannot access VoiceMail."); | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 561 | } | 
|  | 562 | // TODO: There is no voicemail picture? | 
|  | 563 | // FIXME: FIND ANOTHER ICON | 
|  | 564 | // photoResource = android.R.drawable.badge_voicemail; | 
|  | 565 | return this; | 
|  | 566 | } | 
|  | 567 |  | 
| The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 568 | private static String normalize(String s) { | 
|  | 569 | if (s == null || s.length() > 0) { | 
|  | 570 | return s; | 
|  | 571 | } else { | 
|  | 572 | return null; | 
|  | 573 | } | 
|  | 574 | } | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 575 |  | 
|  | 576 | /** | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 577 | * Returns the column index to use to find the "person_id" field in | 
|  | 578 | * the specified cursor, based on the contact URI that was originally | 
|  | 579 | * queried. | 
|  | 580 | * | 
|  | 581 | * This is a helper function for the getCallerInfo() method that takes | 
|  | 582 | * a Cursor.  Looking up the person_id is nontrivial (compared to all | 
|  | 583 | * the other CallerInfo fields) since the column we need to use | 
|  | 584 | * depends on what query we originally ran. | 
|  | 585 | * | 
|  | 586 | * Watch out: be sure to not do any database access in this method, since | 
|  | 587 | * it's run from the UI thread (see comments below for more info.) | 
|  | 588 | * | 
|  | 589 | * @return the columnIndex to use (with cursor.getLong()) to get the | 
|  | 590 | * person_id, or -1 if we couldn't figure out what colum to use. | 
|  | 591 | * | 
|  | 592 | * TODO: Add a unittest for this method.  (This is a little tricky to | 
|  | 593 | * test, since we'll need a live contacts database to test against, | 
|  | 594 | * preloaded with at least some phone numbers and SIP addresses.  And | 
|  | 595 | * we'll probably have to hardcode the column indexes we expect, so | 
|  | 596 | * the test might break whenever the contacts schema changes.  But we | 
|  | 597 | * can at least make sure we handle all the URI patterns we claim to, | 
|  | 598 | * and that the mime types match what we expect...) | 
|  | 599 | */ | 
|  | 600 | private static int getColumnIndexForPersonId(Uri contactRef, Cursor cursor) { | 
|  | 601 | // TODO: This is pretty ugly now, see bug 2269240 for | 
|  | 602 | // more details. The column to use depends upon the type of URL: | 
|  | 603 | // - content://com.android.contacts/data/phones ==> use the "contact_id" column | 
|  | 604 | // - content://com.android.contacts/phone_lookup ==> use the "_ID" column | 
|  | 605 | // - content://com.android.contacts/data ==> use the "contact_id" column | 
|  | 606 | // If it's none of the above, we leave columnIndex=-1 which means | 
|  | 607 | // that the person_id field will be left unset. | 
|  | 608 | // | 
|  | 609 | // The logic here *used* to be based on the mime type of contactRef | 
|  | 610 | // (for example Phone.CONTENT_ITEM_TYPE would tell us to use the | 
|  | 611 | // RawContacts.CONTACT_ID column).  But looking up the mime type requires | 
|  | 612 | // a call to context.getContentResolver().getType(contactRef), which | 
|  | 613 | // isn't safe to do from the UI thread since it can cause an ANR if | 
|  | 614 | // the contacts provider is slow or blocked (like during a sync.) | 
|  | 615 | // | 
|  | 616 | // So instead, figure out the column to use for person_id by just | 
|  | 617 | // looking at the URI itself. | 
|  | 618 |  | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 619 | if (VDBG) Log.v(TAG, "- getColumnIndexForPersonId: contactRef URI = '" | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 620 | + contactRef + "'..."); | 
|  | 621 | // Warning: Do not enable the following logging (due to ANR risk.) | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 622 | // if (VDBG) Log.v(TAG, "- MIME type: " | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 623 | //                 + context.getContentResolver().getType(contactRef)); | 
|  | 624 |  | 
|  | 625 | String url = contactRef.toString(); | 
|  | 626 | String columnName = null; | 
|  | 627 | if (url.startsWith("content://com.android.contacts/data/phones")) { | 
|  | 628 | // Direct lookup in the Phone table. | 
|  | 629 | // MIME type: Phone.CONTENT_ITEM_TYPE (= "vnd.android.cursor.item/phone_v2") | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 630 | if (VDBG) Log.v(TAG, "'data/phones' URI; using RawContacts.CONTACT_ID"); | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 631 | columnName = RawContacts.CONTACT_ID; | 
|  | 632 | } else if (url.startsWith("content://com.android.contacts/data")) { | 
|  | 633 | // Direct lookup in the Data table. | 
|  | 634 | // MIME type: Data.CONTENT_TYPE (= "vnd.android.cursor.dir/data") | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 635 | if (VDBG) Log.v(TAG, "'data' URI; using Data.CONTACT_ID"); | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 636 | // (Note Data.CONTACT_ID and RawContacts.CONTACT_ID are equivalent.) | 
|  | 637 | columnName = Data.CONTACT_ID; | 
|  | 638 | } else if (url.startsWith("content://com.android.contacts/phone_lookup")) { | 
|  | 639 | // Lookup in the PhoneLookup table, which provides "fuzzy matching" | 
|  | 640 | // for phone numbers. | 
|  | 641 | // MIME type: PhoneLookup.CONTENT_TYPE (= "vnd.android.cursor.dir/phone_lookup") | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 642 | if (VDBG) Log.v(TAG, "'phone_lookup' URI; using PhoneLookup._ID"); | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 643 | columnName = PhoneLookup._ID; | 
|  | 644 | } else { | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 645 | Log.w(TAG, "Unexpected prefix for contactRef '" + url + "'"); | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 646 | } | 
|  | 647 | int columnIndex = (columnName != null) ? cursor.getColumnIndex(columnName) : -1; | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 648 | if (VDBG) Log.v(TAG, "==> Using column '" + columnName | 
| David Brown | 85e0ff8 | 2010-10-22 12:54:42 -0700 | [diff] [blame] | 649 | + "' (columnIndex = " + columnIndex + ") for person_id lookup..."); | 
|  | 650 | return columnIndex; | 
|  | 651 | } | 
|  | 652 |  | 
|  | 653 | /** | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 654 | * Updates this CallerInfo's geoDescription field, based on the raw | 
|  | 655 | * phone number in the phoneNumber field. | 
|  | 656 | * | 
|  | 657 | * (Note that the various getCallerInfo() methods do *not* set the | 
|  | 658 | * geoDescription automatically; you need to call this method | 
|  | 659 | * explicitly to get it.) | 
|  | 660 | * | 
|  | 661 | * @param context the context used to look up the current locale / country | 
|  | 662 | * @param fallbackNumber if this CallerInfo's phoneNumber field is empty, | 
|  | 663 | *        this specifies a fallback number to use instead. | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 664 | * @hide | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 665 | */ | 
|  | 666 | public void updateGeoDescription(Context context, String fallbackNumber) { | 
|  | 667 | String number = TextUtils.isEmpty(phoneNumber) ? fallbackNumber : phoneNumber; | 
|  | 668 | geoDescription = getGeoDescription(context, number); | 
|  | 669 | } | 
|  | 670 |  | 
|  | 671 | /** | 
|  | 672 | * @return a geographical description string for the specified number. | 
| Shaopeng Jia | e713576 | 2011-08-12 13:25:41 +0200 | [diff] [blame] | 673 | * @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 674 | * | 
|  | 675 | * @hide | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 676 | */ | 
| Ceci Wu | 3b90d48 | 2016-08-25 14:48:19 +0800 | [diff] [blame] | 677 | public static String getGeoDescription(Context context, String number) { | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 678 | if (VDBG) Log.v(TAG, "getGeoDescription('" + number + "')..."); | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 679 |  | 
|  | 680 | if (TextUtils.isEmpty(number)) { | 
|  | 681 | return null; | 
|  | 682 | } | 
|  | 683 |  | 
|  | 684 | PhoneNumberUtil util = PhoneNumberUtil.getInstance(); | 
|  | 685 | PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance(); | 
|  | 686 |  | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 687 | Locale locale = context.getResources().getConfiguration().locale; | 
| Shaopeng Jia | 9683f99 | 2011-09-07 14:07:15 +0200 | [diff] [blame] | 688 | String countryIso = getCurrentCountryIso(context, locale); | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 689 | PhoneNumber pn = null; | 
|  | 690 | try { | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 691 | if (VDBG) Log.v(TAG, "parsing '" + number | 
| David Brown | cec25c4 | 2011-06-23 14:17:27 -0700 | [diff] [blame] | 692 | + "' for countryIso '" + countryIso + "'..."); | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 693 | pn = util.parse(number, countryIso); | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 694 | if (VDBG) Log.v(TAG, "- parsed number: " + pn); | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 695 | } catch (NumberParseException e) { | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 696 | Log.w(TAG, "getGeoDescription: NumberParseException for incoming number '" | 
|  | 697 | + Log.pii(number) + "'"); | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 698 | } | 
|  | 699 |  | 
|  | 700 | if (pn != null) { | 
|  | 701 | String description = geocoder.getDescriptionForNumber(pn, locale); | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 702 | if (VDBG) Log.v(TAG, "- got description: '" + description + "'"); | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 703 | return description; | 
|  | 704 | } else { | 
|  | 705 | return null; | 
|  | 706 | } | 
|  | 707 | } | 
|  | 708 |  | 
|  | 709 | /** | 
| Shaopeng Jia | 9683f99 | 2011-09-07 14:07:15 +0200 | [diff] [blame] | 710 | * @return The ISO 3166-1 two letters country code of the country the user | 
|  | 711 | *         is in. | 
|  | 712 | */ | 
|  | 713 | private static String getCurrentCountryIso(Context context, Locale locale) { | 
| Jake Hamby | 7f5bee0 | 2013-10-08 16:31:16 -0700 | [diff] [blame] | 714 | String countryIso = null; | 
|  | 715 | CountryDetector detector = (CountryDetector) context.getSystemService( | 
|  | 716 | Context.COUNTRY_DETECTOR); | 
|  | 717 | if (detector != null) { | 
|  | 718 | Country country = detector.detectCountry(); | 
|  | 719 | if (country != null) { | 
|  | 720 | countryIso = country.getCountryIso(); | 
|  | 721 | } else { | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 722 | Log.e(TAG, new Exception(), "CountryDetector.detectCountry() returned null."); | 
| Jake Hamby | 7f5bee0 | 2013-10-08 16:31:16 -0700 | [diff] [blame] | 723 | } | 
|  | 724 | } | 
|  | 725 | if (countryIso == null) { | 
|  | 726 | countryIso = locale.getCountry(); | 
| Hall Liu | d2f962a | 2019-10-31 15:17:58 -0700 | [diff] [blame] | 727 | Log.w(TAG, "No CountryDetector; falling back to countryIso based on locale: " | 
| Jake Hamby | 7f5bee0 | 2013-10-08 16:31:16 -0700 | [diff] [blame] | 728 | + countryIso); | 
|  | 729 | } | 
|  | 730 | return countryIso; | 
| Shaopeng Jia | 9683f99 | 2011-09-07 14:07:15 +0200 | [diff] [blame] | 731 | } | 
|  | 732 |  | 
| Chen Xu | fba9ca4 | 2019-09-07 18:56:17 -0700 | [diff] [blame] | 733 | /** @hide */ | 
| Jay Shrauner | a2c9348 | 2013-10-21 11:54:19 -0700 | [diff] [blame] | 734 | protected static String getCurrentCountryIso(Context context) { | 
|  | 735 | return getCurrentCountryIso(context, Locale.getDefault()); | 
|  | 736 | } | 
|  | 737 |  | 
| Shaopeng Jia | 9683f99 | 2011-09-07 14:07:15 +0200 | [diff] [blame] | 738 | /** | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 739 | * @return a string debug representation of this instance. | 
|  | 740 | */ | 
| Victor Chang | 9359ee1 | 2016-01-04 15:48:09 +0000 | [diff] [blame] | 741 | @Override | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 742 | public String toString() { | 
| David Brown | 04639ba | 2010-11-30 15:31:15 -0800 | [diff] [blame] | 743 | // Warning: never check in this file with VERBOSE_DEBUG = true | 
|  | 744 | // because that will result in PII in the system log. | 
|  | 745 | final boolean VERBOSE_DEBUG = false; | 
|  | 746 |  | 
|  | 747 | if (VERBOSE_DEBUG) { | 
|  | 748 | return new StringBuilder(384) | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 749 | .append(super.toString() + " { ") | 
| David Brown | 04639ba | 2010-11-30 15:31:15 -0800 | [diff] [blame] | 750 | .append("\nname: " + name) | 
|  | 751 | .append("\nphoneNumber: " + phoneNumber) | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 752 | .append("\nnormalizedNumber: " + normalizedNumber) | 
|  | 753 | .append("\ngeoDescription: " + geoDescription) | 
| David Brown | 04639ba | 2010-11-30 15:31:15 -0800 | [diff] [blame] | 754 | .append("\ncnapName: " + cnapName) | 
|  | 755 | .append("\nnumberPresentation: " + numberPresentation) | 
|  | 756 | .append("\nnamePresentation: " + namePresentation) | 
|  | 757 | .append("\ncontactExits: " + contactExists) | 
|  | 758 | .append("\nphoneLabel: " + phoneLabel) | 
|  | 759 | .append("\nnumberType: " + numberType) | 
|  | 760 | .append("\nnumberLabel: " + numberLabel) | 
|  | 761 | .append("\nphotoResource: " + photoResource) | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 762 | .append("\ncontactIdOrZero: " + contactIdOrZero) | 
| David Brown | 04639ba | 2010-11-30 15:31:15 -0800 | [diff] [blame] | 763 | .append("\nneedUpdate: " + needUpdate) | 
| Makoto Onuki | a2295e6 | 2014-07-10 15:32:16 -0700 | [diff] [blame] | 764 | .append("\ncontactRingtoneUri: " + contactRingtoneUri) | 
|  | 765 | .append("\ncontactDisplayPhotoUri: " + contactDisplayPhotoUri) | 
| David Brown | 04639ba | 2010-11-30 15:31:15 -0800 | [diff] [blame] | 766 | .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail) | 
|  | 767 | .append("\ncachedPhoto: " + cachedPhoto) | 
|  | 768 | .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent) | 
|  | 769 | .append("\nemergency: " + mIsEmergency) | 
|  | 770 | .append("\nvoicemail " + mIsVoiceMail) | 
|  | 771 | .append("\ncontactExists " + contactExists) | 
| Victor Chang | 9359ee1 | 2016-01-04 15:48:09 +0000 | [diff] [blame] | 772 | .append("\nuserType " + userType) | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 773 | .append(" }") | 
| David Brown | 04639ba | 2010-11-30 15:31:15 -0800 | [diff] [blame] | 774 | .toString(); | 
|  | 775 | } else { | 
|  | 776 | return new StringBuilder(128) | 
| David Brown | 94202fe | 2011-06-10 16:24:05 -0700 | [diff] [blame] | 777 | .append(super.toString() + " { ") | 
| David Brown | 04639ba | 2010-11-30 15:31:15 -0800 | [diff] [blame] | 778 | .append("name " + ((name == null) ? "null" : "non-null")) | 
|  | 779 | .append(", phoneNumber " + ((phoneNumber == null) ? "null" : "non-null")) | 
|  | 780 | .append(" }") | 
|  | 781 | .toString(); | 
|  | 782 | } | 
| Nicolas Catania | 60d45f0 | 2009-09-15 18:32:02 -0700 | [diff] [blame] | 783 | } | 
| Wink Saville | 2563a3a | 2009-06-09 10:30:03 -0700 | [diff] [blame] | 784 | } |