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 * @param phoneNumber read from contact db. 140 * @return contact number error code. 141 */ validate(final String phoneNumber)142 public int validate(final String phoneNumber) { 143 String number = phoneNumber; 144 if (TextUtils.isEmpty(number)) { 145 return NUMBER_INVALID; 146 } 147 148 if(number.startsWith("*67") || number.startsWith("*82")) { 149 number = number.substring(3); 150 if (TextUtils.isEmpty(number)) { 151 return NUMBER_INVALID; 152 } 153 } 154 155 if(number.contains("*")) { 156 return NUMBER_PRELOADED_ENTRY; 157 } 158 159 number = PhoneNumberUtils.stripSeparators(number); 160 if (!number.equals(PhoneNumberUtils.convertKeypadLettersToDigits(number))) { 161 return NUMBER_INVALID; 162 } 163 164 if (PhoneNumberUtils.isEmergencyNumber(number)) { 165 return NUMBER_EMERGENCY; 166 // TODO: To handle short code 167 //} else if ((mContext != null) && PhoneNumberUtils.isN11Number(mContext, number)) { 168 // return NUMBER_SHORT_CODE; 169 } else if (number.startsWith("#")) { 170 return NUMBER_PRELOADED_ENTRY; 171 } else if (isInExcludedList(number)) { 172 return NUMBER_FREE_PHONE; 173 } 174 175 int len = number.length(); 176 if (len < NUMBER_LENGTH_NORMAL) { 177 return NUMBER_INVALID; 178 } 179 180 number = format(number); 181 if (number.startsWith("+")) { 182 len = number.length(); 183 // make sure the number after stripped the national number still be 10 digits 184 if (len >= NUMBER_LENGTH_NORMAL + 2) { 185 return NUMBER_VALID; 186 } 187 } 188 189 return NUMBER_INVALID; 190 } 191 192 193 /** 194 * Some utility functions for presence service only. 195 * @hide 196 */ format(List<String> numbers)197 public String[] format(List<String> numbers) { 198 if ((numbers == null) || (numbers.size() == 0)) { 199 return null; 200 } 201 202 int size = numbers.size(); 203 String[] outContactsArray = new String[size]; 204 for (int i = 0; i < size; i++) { 205 String number = numbers.get(i); 206 outContactsArray[i] = format(number); 207 if (DEBUG) { 208 Log.d(TAG, "outContactsArray[" + i + "] = " + outContactsArray[i]); 209 } 210 } 211 212 return outContactsArray; 213 } 214 format(String[] numbers)215 public String[] format(String[] numbers) { 216 if ((numbers == null) || (numbers.length == 0)) { 217 return null; 218 } 219 220 int length = numbers.length; 221 String[] outContactsArray = new String[length]; 222 for (int i = 0; i < length; i++) { 223 String number = numbers[i]; 224 outContactsArray[i] = format(number); 225 if (DEBUG) { 226 Log.d(TAG, "outContactsArray[" + i + "] = " + outContactsArray[i]); 227 } 228 } 229 230 return outContactsArray; 231 } validate(List<String> numbers)232 public int validate(List<String> numbers) { 233 if ((numbers == null) || (numbers.size() == 0)) { 234 return NUMBER_INVALID; 235 } 236 237 int size = numbers.size(); 238 for (int i = 0; i < size; i++) { 239 String number = numbers.get(i); 240 int result = validate(number); 241 if (result != NUMBER_VALID) { 242 return result; 243 } 244 } 245 246 return NUMBER_VALID; 247 } 248 validate(String[] numbers)249 public int validate(String[] numbers) { 250 if ((numbers == null) || (numbers.length == 0)) { 251 return NUMBER_INVALID; 252 } 253 254 int length = numbers.length; 255 for (int i = 0; i < length; i++) { 256 String number = numbers[i]; 257 int result = validate(number); 258 if (result != NUMBER_VALID) { 259 return result; 260 } 261 } 262 263 return NUMBER_VALID; 264 } 265 266 /** 267 * The logger related. 268 */ 269 private static final boolean DEBUG = Build.IS_DEBUGGABLE; 270 private static final String TAG = "ContactNumberUtils"; 271 272 /** 273 * Contact number length. 274 */ 275 // As per E164 the maximum number length should be 15. 276 // But as per implemention of libphonenumber it found longer length in Germany. 277 // So we use the same length as libphonenumber. 278 private int NUMBER_LENGTH_MAX = 17; 279 private int NUMBER_LENGTH_NORMAL = 10; 280 private int NUMBER_LENGTH_NO_AREA_CODE = 7; 281 282 /** 283 * Save the singleton instance. 284 */ 285 private static ContactNumberUtils sInstance = null; 286 private Context mContext = null; 287 288 /** 289 * Constructor 290 */ ContactNumberUtils()291 private ContactNumberUtils() { 292 if (DEBUG) { 293 Log.d(TAG, "ContactNumberUtils constructor"); 294 } 295 } 296 297 /** 298 * Add device's own area code to the number which length is 7. 299 */ addAreaCode(String number)300 private String addAreaCode(String number) { 301 if (mContext == null) { 302 if (DEBUG) { 303 Log.e(TAG, "mContext is null, please update context."); 304 } 305 return number; 306 } 307 308 String mdn = null; 309 TelephonyManager tm = (TelephonyManager) 310 mContext.getSystemService(Context.TELEPHONY_SERVICE); 311 mdn = tm.getLine1Number(); 312 313 if ((mdn == null) || (mdn.length() == 0) || mdn.startsWith("00000")) { 314 return number; 315 } 316 317 mdn = PhoneNumberUtils.stripSeparators(mdn); 318 if (mdn.length() >= NUMBER_LENGTH_NORMAL) { 319 mdn = mdn.substring(mdn.length() - NUMBER_LENGTH_NORMAL); 320 } 321 mdn = mdn.substring(0, 3); 322 323 number = mdn + number; 324 return number; 325 } 326 327 /** 328 * The excluded number list. 329 */ 330 private static ArrayList<String> sExcludes = null; 331 isInExcludedList(String number)332 private boolean isInExcludedList(String number){ 333 if (sExcludes == null) { 334 sExcludes = new ArrayList<String>(); 335 sExcludes.add("800"); 336 sExcludes.add("822"); 337 sExcludes.add("833"); 338 sExcludes.add("844"); 339 sExcludes.add("855"); 340 sExcludes.add("866"); 341 sExcludes.add("877"); 342 sExcludes.add("880882"); 343 sExcludes.add("888"); 344 sExcludes.add("900"); 345 sExcludes.add("911"); 346 } 347 348 String tempNumber = format(number); 349 if(TextUtils.isEmpty(tempNumber)) { 350 return true; //exclude empty/null string. 351 } 352 353 if(tempNumber.startsWith("1")) { 354 tempNumber = tempNumber.substring(1); 355 } else if(tempNumber.startsWith("+1")) { 356 tempNumber = tempNumber.substring(2); 357 } 358 359 if(TextUtils.isEmpty(tempNumber)) { 360 return true; //exclude empty/null string. 361 } 362 363 for (String num : sExcludes) { 364 if(tempNumber.startsWith(num)) { 365 return true; 366 } 367 } 368 369 return false; 370 } 371 } 372 373