• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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