1 /* 2 * Copyright (c) 2015, Motorola Mobility LLC 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * - Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * - Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * - Neither the name of Motorola Mobility nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 18 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MOTOROLA MOBILITY LLC BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 * DAMAGE. 27 */ 28 29 package com.android.ims.internal; 30 31 import android.content.Context; 32 import android.os.Build; 33 import android.telephony.PhoneNumberUtils; 34 import android.telephony.TelephonyManager; 35 import android.text.TextUtils; 36 import android.util.Log; 37 38 import java.util.List; 39 import java.util.ArrayList; 40 41 /** 42 * @hide 43 */ 44 public class ContactNumberUtils { 45 /** 46 * Sample code: 47 * 48 * ContactNumberUtils mNumberUtils = ContactNumberUtils.getDefault(); 49 * mNumberUtils.setContext(this); 50 * 51 * number = mNumberUtils.format(number); 52 * int result = mNumberUtils.validate(number); 53 * if (ContactNumberUtils.NUMBER_VALID == result) { 54 * } 55 */ getDefault()56 public static ContactNumberUtils getDefault() { 57 if(sInstance == null) { 58 sInstance = new ContactNumberUtils(); 59 } 60 61 return sInstance; 62 } 63 setContext(Context context)64 public void setContext(Context context) { 65 mContext = context; 66 } 67 68 /** 69 * Format contact number to the common format 70 * 71 * @param phoneNumber read from contact db. 72 * @return formatted contact number. 73 */ format(final String phoneNumber)74 public String format(final String phoneNumber) { 75 String number = phoneNumber; 76 if (TextUtils.isEmpty(number)) { 77 return null; 78 } 79 80 if(number.startsWith("*67") || number.startsWith("*82")) { 81 number = number.substring(3); 82 if (TextUtils.isEmpty(number)) { 83 return null; 84 } 85 } 86 87 number = PhoneNumberUtils.stripSeparators(number); 88 89 int len = number.length(); 90 if (len == NUMBER_LENGTH_NO_AREA_CODE) { 91 number = addAreaCode(number); 92 } 93 94 number = PhoneNumberUtils.normalizeNumber(number); 95 96 len = number.length(); 97 if (len == NUMBER_LENGTH_NORMAL) { 98 if (!number.startsWith("+1")) { 99 number = "+1" + number; 100 } 101 } else if (len == NUMBER_LENGTH_NORMAL + 1) { 102 if (number.startsWith("1")) { 103 number = "+" + number; 104 } 105 } else if(len >= NUMBER_LENGTH_NORMAL + 2) { 106 if ((len >= NUMBER_LENGTH_NORMAL + 4) && (number.startsWith("011"))) { 107 number = "+" + number.substring(3); 108 } 109 110 if (!number.startsWith("+")) { 111 if (number.startsWith("1")) { 112 number = "+" + number;; 113 } else { 114 number = "+1" + number; 115 } 116 } 117 } 118 119 if(number.length() > NUMBER_LENGTH_MAX) { 120 return null; 121 } 122 123 return number; 124 } 125 126 /** 127 * Contact nubmer error code. 128 */ 129 public static int NUMBER_VALID = 0; 130 public static int NUMBER_EMERGENCY = 1; 131 public static int NUMBER_SHORT_CODE = 2; 132 public static int NUMBER_PRELOADED_ENTRY = 3; 133 public static int NUMBER_FREE_PHONE = 4; 134 public static int NUMBER_INVALID = 5; 135 136 /** 137 * Check if it is a valid contact number for presence. 138 * 139 * Note: mContext must be set via setContext() before calling this method. 140 * 141 * @param phoneNumber read from contact db. 142 * @return contact number error code. 143 */ validate(final String phoneNumber)144 public int validate(final String phoneNumber) { 145 String number = phoneNumber; 146 if (TextUtils.isEmpty(number)) { 147 return NUMBER_INVALID; 148 } 149 150 if(number.startsWith("*67") || number.startsWith("*82")) { 151 number = number.substring(3); 152 if (TextUtils.isEmpty(number)) { 153 return NUMBER_INVALID; 154 } 155 } 156 157 if(number.contains("*")) { 158 return NUMBER_PRELOADED_ENTRY; 159 } 160 161 number = PhoneNumberUtils.stripSeparators(number); 162 if (!number.equals(PhoneNumberUtils.convertKeypadLettersToDigits(number))) { 163 return NUMBER_INVALID; 164 } 165 166 boolean isEmergencyNumber; 167 if (mContext == null) { 168 Log.e(TAG, "context is unexpectedly null to provide emergency identification service"); 169 isEmergencyNumber = false; 170 } else { 171 TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); 172 isEmergencyNumber = tm.isEmergencyNumber(number); 173 } 174 175 if (isEmergencyNumber) { 176 return NUMBER_EMERGENCY; 177 // TODO: To handle short code 178 //} else if ((mContext != null) && PhoneNumberUtils.isN11Number(mContext, number)) { 179 // return NUMBER_SHORT_CODE; 180 } else if (number.startsWith("#")) { 181 return NUMBER_PRELOADED_ENTRY; 182 } else if (isInExcludedList(number)) { 183 return NUMBER_FREE_PHONE; 184 } 185 186 int len = number.length(); 187 if (len < NUMBER_LENGTH_NORMAL) { 188 return NUMBER_INVALID; 189 } 190 191 number = format(number); 192 if (number.startsWith("+")) { 193 len = number.length(); 194 // make sure the number after stripped the national number still be 10 digits 195 if (len >= NUMBER_LENGTH_NORMAL + 2) { 196 return NUMBER_VALID; 197 } 198 } 199 200 return NUMBER_INVALID; 201 } 202 203 204 /** 205 * Some utility functions for presence service only. 206 * @hide 207 */ format(List<String> numbers)208 public String[] format(List<String> numbers) { 209 if ((numbers == null) || (numbers.size() == 0)) { 210 return null; 211 } 212 213 int size = numbers.size(); 214 String[] outContactsArray = new String[size]; 215 for (int i = 0; i < size; i++) { 216 String number = numbers.get(i); 217 outContactsArray[i] = format(number); 218 if (DEBUG) { 219 Log.d(TAG, "outContactsArray[" + i + "] = " + outContactsArray[i]); 220 } 221 } 222 223 return outContactsArray; 224 } 225 format(String[] numbers)226 public String[] format(String[] numbers) { 227 if ((numbers == null) || (numbers.length == 0)) { 228 return null; 229 } 230 231 int length = numbers.length; 232 String[] outContactsArray = new String[length]; 233 for (int i = 0; i < length; i++) { 234 String number = numbers[i]; 235 outContactsArray[i] = format(number); 236 if (DEBUG) { 237 Log.d(TAG, "outContactsArray[" + i + "] = " + outContactsArray[i]); 238 } 239 } 240 241 return outContactsArray; 242 } validate(List<String> numbers)243 public int validate(List<String> numbers) { 244 if ((numbers == null) || (numbers.size() == 0)) { 245 return NUMBER_INVALID; 246 } 247 248 int size = numbers.size(); 249 for (int i = 0; i < size; i++) { 250 String number = numbers.get(i); 251 int result = validate(number); 252 if (result != NUMBER_VALID) { 253 return result; 254 } 255 } 256 257 return NUMBER_VALID; 258 } 259 validate(String[] numbers)260 public int validate(String[] numbers) { 261 if ((numbers == null) || (numbers.length == 0)) { 262 return NUMBER_INVALID; 263 } 264 265 int length = numbers.length; 266 for (int i = 0; i < length; i++) { 267 String number = numbers[i]; 268 int result = validate(number); 269 if (result != NUMBER_VALID) { 270 return result; 271 } 272 } 273 274 return NUMBER_VALID; 275 } 276 277 /** 278 * The logger related. 279 */ 280 private static final boolean DEBUG = Build.IS_DEBUGGABLE; 281 private static final String TAG = "ContactNumberUtils"; 282 283 /** 284 * Contact number length. 285 */ 286 // As per E164 the maximum number length should be 15. 287 // But as per implemention of libphonenumber it found longer length in Germany. 288 // So we use the same length as libphonenumber. 289 private int NUMBER_LENGTH_MAX = 17; 290 private int NUMBER_LENGTH_NORMAL = 10; 291 private int NUMBER_LENGTH_NO_AREA_CODE = 7; 292 293 /** 294 * Save the singleton instance. 295 */ 296 private static ContactNumberUtils sInstance = null; 297 private Context mContext = null; 298 299 /** 300 * Constructor 301 */ ContactNumberUtils()302 private ContactNumberUtils() { 303 if (DEBUG) { 304 Log.d(TAG, "ContactNumberUtils constructor"); 305 } 306 } 307 308 /** 309 * Add device's own area code to the number which length is 7. 310 */ addAreaCode(String number)311 private String addAreaCode(String number) { 312 if (mContext == null) { 313 if (DEBUG) { 314 Log.e(TAG, "mContext is null, please update context."); 315 } 316 return number; 317 } 318 319 String mdn = null; 320 TelephonyManager tm = (TelephonyManager) 321 mContext.getSystemService(Context.TELEPHONY_SERVICE); 322 mdn = tm.getLine1Number(); 323 324 if ((mdn == null) || (mdn.length() == 0) || mdn.startsWith("00000")) { 325 return number; 326 } 327 328 mdn = PhoneNumberUtils.stripSeparators(mdn); 329 if (mdn.length() >= NUMBER_LENGTH_NORMAL) { 330 mdn = mdn.substring(mdn.length() - NUMBER_LENGTH_NORMAL); 331 } 332 mdn = mdn.substring(0, 3); 333 334 number = mdn + number; 335 return number; 336 } 337 338 /** 339 * The excluded number list. 340 */ 341 private static ArrayList<String> sExcludes = null; 342 isInExcludedList(String number)343 private boolean isInExcludedList(String number){ 344 if (sExcludes == null) { 345 sExcludes = new ArrayList<String>(); 346 sExcludes.add("800"); 347 sExcludes.add("822"); 348 sExcludes.add("833"); 349 sExcludes.add("844"); 350 sExcludes.add("855"); 351 sExcludes.add("866"); 352 sExcludes.add("877"); 353 sExcludes.add("880882"); 354 sExcludes.add("888"); 355 sExcludes.add("900"); 356 sExcludes.add("911"); 357 } 358 359 String tempNumber = format(number); 360 if(TextUtils.isEmpty(tempNumber)) { 361 return true; //exclude empty/null string. 362 } 363 364 if(tempNumber.startsWith("1")) { 365 tempNumber = tempNumber.substring(1); 366 } else if(tempNumber.startsWith("+1")) { 367 tempNumber = tempNumber.substring(2); 368 } 369 370 if(TextUtils.isEmpty(tempNumber)) { 371 return true; //exclude empty/null string. 372 } 373 374 for (String num : sExcludes) { 375 if(tempNumber.startsWith(num)) { 376 return true; 377 } 378 } 379 380 return false; 381 } 382 } 383 384