1 /*
2  * Copyright (C) 2018 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 
17 package android.telephony.emergency;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.TestApi;
22 import android.hardware.radio.V1_4.EmergencyNumberSource;
23 import android.hardware.radio.V1_4.EmergencyServiceCategory;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.telephony.CarrierConfigManager;
27 import android.telephony.PhoneNumberUtils;
28 import android.util.SparseArray;
29 import android.util.SparseIntArray;
30 
31 import com.android.telephony.Rlog;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Objects;
40 import java.util.Set;
41 
42 /**
43  * A parcelable class that wraps and retrieves the information of number, service category(s) and
44  * country code for a specific emergency number.
45  */
46 public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNumber> {
47 
48     private static final String LOG_TAG = "EmergencyNumber";
49 
50     /**
51      * Defining Emergency Service Category as follows:
52      *  - General emergency call, all categories;
53      *  - Police;
54      *  - Ambulance;
55      *  - Fire Brigade;
56      *  - Marine Guard;
57      *  - Mountain Rescue;
58      *  - Manually Initiated eCall (MIeC);
59      *  - Automatically Initiated eCall (AIeC);
60      *
61      * Category UNSPECIFIED (General emergency call, all categories) indicates that no specific
62      * services are associated with this emergency number; if the emergency number is specified,
63      * it has one or more defined emergency service categories.
64      *
65      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
66      *
67      * @hide
68      */
69     @IntDef(flag = true, prefix = { "EMERGENCY_SERVICE_CATEGORY_" }, value = {
70             EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
71             EMERGENCY_SERVICE_CATEGORY_POLICE,
72             EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
73             EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
74             EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
75             EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE,
76             EMERGENCY_SERVICE_CATEGORY_MIEC,
77             EMERGENCY_SERVICE_CATEGORY_AIEC
78     })
79     @Retention(RetentionPolicy.SOURCE)
80     public @interface EmergencyServiceCategories {}
81 
82     /**
83      * Emergency Service Category UNSPECIFIED (General emergency call, all categories) bit-field
84      * indicates that no specific services are associated with this emergency number; if the
85      * emergency number is specified, it has one or more defined emergency service categories.
86      *
87      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
88      */
89     public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED =
90             EmergencyServiceCategory.UNSPECIFIED;
91     /**
92      * Bit-field that indicates Emergency Service Category for Police.
93      *
94      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
95      */
96     public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = EmergencyServiceCategory.POLICE;
97     /**
98      * Bit-field that indicates Emergency Service Category for Ambulance.
99      *
100      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
101      */
102     public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE =
103             EmergencyServiceCategory.AMBULANCE;
104     /**
105      * Bit-field that indicates Emergency Service Category for Fire Brigade.
106      *
107      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
108      */
109     public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE =
110             EmergencyServiceCategory.FIRE_BRIGADE;
111     /**
112      * Bit-field that indicates Emergency Service Category for Marine Guard.
113      *
114      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
115      */
116     public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD =
117             EmergencyServiceCategory.MARINE_GUARD;
118     /**
119      * Bit-field that indicates Emergency Service Category for Mountain Rescue.
120      *
121      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
122      */
123     public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE =
124             EmergencyServiceCategory.MOUNTAIN_RESCUE;
125     /**
126      * Bit-field that indicates Emergency Service Category for Manually Initiated eCall (MIeC)
127      *
128      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
129      */
130     public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = EmergencyServiceCategory.MIEC;
131     /**
132      * Bit-field that indicates Emergency Service Category for Automatically Initiated eCall (AIeC)
133      *
134      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
135      */
136     public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = EmergencyServiceCategory.AIEC;
137 
138     private static final Set<Integer> EMERGENCY_SERVICE_CATEGORY_SET;
139     static {
140         EMERGENCY_SERVICE_CATEGORY_SET = new HashSet<Integer>();
141         EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_POLICE);
142         EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
143         EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
144         EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
145         EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
146         EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MIEC);
147         EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AIEC);
148     }
149 
150     /**
151      * The source to tell where the corresponding @1.4::EmergencyNumber comes from.
152      *
153      * The emergency number has one or more defined emergency number sources.
154      *
155      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
156      *
157      * @hide
158      */
159     @IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = {
160             EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
161             EMERGENCY_NUMBER_SOURCE_SIM,
162             EMERGENCY_NUMBER_SOURCE_DATABASE,
163             EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
164             EMERGENCY_NUMBER_SOURCE_DEFAULT
165     })
166     @Retention(RetentionPolicy.SOURCE)
167     public @interface EmergencyNumberSources {}
168 
169     /**
170      * Bit-field which indicates the number is from the network signaling.
171      *
172      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
173      */
174     public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING =
175             EmergencyNumberSource.NETWORK_SIGNALING;
176     /**
177      * Bit-field which indicates the number is from the sim.
178      *
179      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
180      */
181     public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM;
182     /**
183      * Bit-field which indicates the number is from the platform-maintained database.
184      */
185     public static final int EMERGENCY_NUMBER_SOURCE_DATABASE =  1 << 4;
186     /**
187      * Bit-field which indicates the number is from test mode.
188      *
189      * @hide
190      */
191     @TestApi
192     public static final int EMERGENCY_NUMBER_SOURCE_TEST =  1 << 5;
193     /** Bit-field which indicates the number is from the modem config. */
194     public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
195             EmergencyNumberSource.MODEM_CONFIG;
196     /**
197      * Bit-field which indicates the number is available as default.
198      *
199      * 112, 911 must always be available; additionally, 000, 08, 110, 999, 118 and 119 must be
200      * available when sim is not present.
201      *
202      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
203      */
204     public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = EmergencyNumberSource.DEFAULT;
205 
206     private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
207     static {
208         EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
209         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
210         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
211         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DATABASE);
212         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
213         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
214     }
215 
216     /**
217      * Indicated the framework does not know whether an emergency call should be placed using
218      * emergency or normal call routing. This means the underlying radio or IMS implementation is
219      * free to determine for itself how to route the call.
220      */
221     public static final int EMERGENCY_CALL_ROUTING_UNKNOWN = 0;
222     /**
223      * Indicates the radio or IMS implementation must handle the call through emergency routing.
224      */
225     public static final int EMERGENCY_CALL_ROUTING_EMERGENCY = 1;
226     /**
227      * Indicates the radio or IMS implementation must handle the call through normal call routing.
228      */
229     public static final int EMERGENCY_CALL_ROUTING_NORMAL = 2;
230 
231     /**
232      * The routing to tell how to handle the call for the corresponding emergency number.
233      *
234      * @hide
235      */
236     @IntDef(flag = false, prefix = { "EMERGENCY_CALL_ROUTING_" }, value = {
237             EMERGENCY_CALL_ROUTING_UNKNOWN,
238             EMERGENCY_CALL_ROUTING_EMERGENCY,
239             EMERGENCY_CALL_ROUTING_NORMAL
240     })
241     @Retention(RetentionPolicy.SOURCE)
242     public @interface EmergencyCallRouting {}
243 
244 
245     private final String mNumber;
246     private final String mCountryIso;
247     private final String mMnc;
248     private final int mEmergencyServiceCategoryBitmask;
249     private final List<String> mEmergencyUrns;
250     private final int mEmergencyNumberSourceBitmask;
251     private final int mEmergencyCallRouting;
252     /**
253      * The source of the EmergencyNumber in the order of precedence.
254      */
255     private static final int[] EMERGENCY_NUMBER_SOURCE_PRECEDENCE;
256     static {
257         EMERGENCY_NUMBER_SOURCE_PRECEDENCE = new int[4];
258         EMERGENCY_NUMBER_SOURCE_PRECEDENCE[0] = EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING;
259         EMERGENCY_NUMBER_SOURCE_PRECEDENCE[1] = EMERGENCY_NUMBER_SOURCE_SIM;
260         EMERGENCY_NUMBER_SOURCE_PRECEDENCE[2] = EMERGENCY_NUMBER_SOURCE_DATABASE;
261         EMERGENCY_NUMBER_SOURCE_PRECEDENCE[3] = EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG;
262     }
263 
264     /** @hide */
EmergencyNumber(@onNull String number, @NonNull String countryIso, @NonNull String mnc, @EmergencyServiceCategories int emergencyServiceCategories, @NonNull List<String> emergencyUrns, @EmergencyNumberSources int emergencyNumberSources, @EmergencyCallRouting int emergencyCallRouting)265     public EmergencyNumber(@NonNull String number, @NonNull String countryIso, @NonNull String mnc,
266                            @EmergencyServiceCategories int emergencyServiceCategories,
267                            @NonNull List<String> emergencyUrns,
268                            @EmergencyNumberSources int emergencyNumberSources,
269                            @EmergencyCallRouting int emergencyCallRouting) {
270         this.mNumber = number;
271         this.mCountryIso = countryIso;
272         this.mMnc = mnc;
273         this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
274         this.mEmergencyUrns = emergencyUrns;
275         this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
276         this.mEmergencyCallRouting = emergencyCallRouting;
277     }
278 
279     /** @hide */
EmergencyNumber(Parcel source)280     public EmergencyNumber(Parcel source) {
281         mNumber = source.readString();
282         mCountryIso = source.readString();
283         mMnc = source.readString();
284         mEmergencyServiceCategoryBitmask = source.readInt();
285         mEmergencyUrns = source.createStringArrayList();
286         mEmergencyNumberSourceBitmask = source.readInt();
287         mEmergencyCallRouting = source.readInt();
288     }
289 
290     @Override
291     /** @hide */
writeToParcel(Parcel dest, int flags)292     public void writeToParcel(Parcel dest, int flags) {
293         dest.writeString(mNumber);
294         dest.writeString(mCountryIso);
295         dest.writeString(mMnc);
296         dest.writeInt(mEmergencyServiceCategoryBitmask);
297         dest.writeStringList(mEmergencyUrns);
298         dest.writeInt(mEmergencyNumberSourceBitmask);
299         dest.writeInt(mEmergencyCallRouting);
300     }
301 
302     public static final @android.annotation.NonNull Parcelable.Creator<EmergencyNumber> CREATOR =
303             new Parcelable.Creator<EmergencyNumber>() {
304                 @Override
305                 public EmergencyNumber createFromParcel(Parcel in) {
306                     return new EmergencyNumber(in);
307                 }
308 
309                 @Override
310                 public EmergencyNumber[] newArray(int size) {
311                     return new EmergencyNumber[size];
312                 }
313             };
314 
315     /**
316      * Get the dialing number of the emergency number.
317      *
318      * The character in the number string is only the dial pad
319      * character('0'-'9', '*', '+', or '#'). For example: 911.
320      *
321      * If the number starts with carrier prefix, the carrier prefix is configured in
322      * {@link CarrierConfigManager#KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY}.
323      *
324      * @return the dialing number.
325      */
getNumber()326     public @NonNull String getNumber() {
327         return mNumber;
328     }
329 
330     /**
331      * Get the country code string (lowercase character) in ISO 3166 format of the emergency number.
332      *
333      * @return the country code string (lowercase character) in ISO 3166 format.
334      */
getCountryIso()335     public @NonNull String getCountryIso() {
336         return mCountryIso;
337     }
338 
339     /**
340      * Get the Mobile Network Code of the emergency number.
341      *
342      * @return the Mobile Network Code of the emergency number.
343      */
getMnc()344     public @NonNull String getMnc() {
345         return mMnc;
346     }
347 
348     /**
349      * Returns the bitmask of emergency service categories of the emergency number.
350      *
351      * @return bitmask of the emergency service categories
352      *
353      * @hide
354      */
getEmergencyServiceCategoryBitmask()355     public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() {
356         return mEmergencyServiceCategoryBitmask;
357     }
358 
359     /**
360      * Returns the bitmask of emergency service categories of the emergency number for
361      * internal dialing.
362      *
363      * @return bitmask of the emergency service categories
364      *
365      * @hide
366      */
getEmergencyServiceCategoryBitmaskInternalDial()367     public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmaskInternalDial() {
368         if (mEmergencyNumberSourceBitmask == EMERGENCY_NUMBER_SOURCE_DATABASE) {
369             return EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
370         }
371         return mEmergencyServiceCategoryBitmask;
372     }
373 
374     /**
375      * Returns the emergency service categories of the emergency number.
376      *
377      * Note: if the emergency number is in {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}, only
378      * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} is returned and it means the number is in
379      * all categories.
380      *
381      * @return a list of the emergency service categories
382      */
getEmergencyServiceCategories()383     public @NonNull List<Integer> getEmergencyServiceCategories() {
384         List<Integer> categories = new ArrayList<>();
385         if (serviceUnspecified()) {
386             categories.add(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
387             return categories;
388         }
389         for (Integer category : EMERGENCY_SERVICE_CATEGORY_SET) {
390             if (isInEmergencyServiceCategories(category)) {
391                 categories.add(category);
392             }
393         }
394         return categories;
395     }
396 
397     /**
398      * Returns the list of emergency Uniform Resources Names (URN) of the emergency number.
399      *
400      * For example, {@code urn:service:sos} is the generic URN for contacting emergency services
401      * of all type.
402      *
403      * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
404      *            RFC 5031
405      *
406      * @return list of emergency Uniform Resources Names (URN) or an empty list if the emergency
407      *         number does not have a specified emergency Uniform Resource Name.
408      */
getEmergencyUrns()409     public @NonNull List<String> getEmergencyUrns() {
410         return Collections.unmodifiableList(mEmergencyUrns);
411     }
412 
413     /**
414      * Checks if the emergency service category is unspecified for the emergency number
415      * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}.
416      *
417      * @return {@code true} if the emergency service category is unspecified for the emergency
418      * number {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
419      */
serviceUnspecified()420     private boolean serviceUnspecified() {
421         return mEmergencyServiceCategoryBitmask == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
422     }
423 
424     /**
425      * Checks if the emergency number is in the supplied emergency service category(s).
426      *
427      * @param categories - the supplied emergency service categories
428      *
429      * @return {@code true} if the emergency number is in the specified emergency service
430      * category(s) or if its emergency service category is
431      * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
432      */
isInEmergencyServiceCategories(@mergencyServiceCategories int categories)433     public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) {
434         if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
435             return serviceUnspecified();
436         }
437         if (serviceUnspecified()) {
438             return true;
439         }
440         return (mEmergencyServiceCategoryBitmask & categories) == categories;
441     }
442 
443     /**
444      * Returns the bitmask of the sources of the emergency number.
445      *
446      * @return bitmask of the emergency number sources
447      *
448      * @hide
449      */
getEmergencyNumberSourceBitmask()450     public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() {
451         return mEmergencyNumberSourceBitmask;
452     }
453 
454     /**
455      * Returns a list of sources of the emergency number.
456      *
457      * @return a list of emergency number sources
458      */
getEmergencyNumberSources()459     public @NonNull List<Integer> getEmergencyNumberSources() {
460         List<Integer> sources = new ArrayList<>();
461         for (Integer source : EMERGENCY_NUMBER_SOURCE_SET) {
462             if ((mEmergencyNumberSourceBitmask & source) == source) {
463                 sources.add(source);
464             }
465         }
466         return sources;
467     }
468 
469     /**
470      * Checks if the emergency number is from the specified emergency number source(s).
471      *
472      * @return {@code true} if the emergency number is from the specified emergency number
473      * source(s); {@code false} otherwise.
474      *
475      * @param sources - the supplied emergency number sources
476      */
isFromSources(@mergencyNumberSources int sources)477     public boolean isFromSources(@EmergencyNumberSources int sources) {
478         return (mEmergencyNumberSourceBitmask & sources) == sources;
479     }
480 
481     /**
482      * Returns the emergency call routing information.
483      *
484      * <p>Some regions require some emergency numbers which are not routed using typical emergency
485      * call processing, but are instead placed as regular phone calls. The emergency call routing
486      * field provides information about how an emergency call will be routed when it is placed.
487      *
488      * @return the emergency call routing requirement
489      */
getEmergencyCallRouting()490     public @EmergencyCallRouting int getEmergencyCallRouting() {
491         return mEmergencyCallRouting;
492     }
493 
494     @Override
495     /** @hide */
describeContents()496     public int describeContents() {
497         return 0;
498     }
499 
500     @Override
toString()501     public String toString() {
502         return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
503                 + "|Mnc-" + mMnc
504                 + "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
505                 + "|Urns-" + mEmergencyUrns
506                 + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask)
507                 + "|Routing-" + Integer.toBinaryString(mEmergencyCallRouting);
508     }
509 
510     @Override
equals(Object o)511     public boolean equals(Object o) {
512         if (!EmergencyNumber.class.isInstance(o)) {
513             return false;
514         }
515         EmergencyNumber other = (EmergencyNumber) o;
516         return mNumber.equals(other.mNumber)
517                 && mCountryIso.equals(other.mCountryIso)
518                 && mMnc.equals(other.mMnc)
519                 && mEmergencyServiceCategoryBitmask == other.mEmergencyServiceCategoryBitmask
520                 && mEmergencyUrns.equals(other.mEmergencyUrns)
521                 && mEmergencyNumberSourceBitmask == other.mEmergencyNumberSourceBitmask
522                 && mEmergencyCallRouting == other.mEmergencyCallRouting;
523     }
524 
525     @Override
hashCode()526     public int hashCode() {
527         return Objects.hash(mNumber, mCountryIso, mMnc, mEmergencyServiceCategoryBitmask,
528                 mEmergencyUrns, mEmergencyNumberSourceBitmask, mEmergencyCallRouting);
529     }
530 
531     /**
532      * Calculate the score for display priority.
533      *
534      * A higher display priority score means the emergency number has a higher display priority.
535      * The score is higher if the source is defined for a higher display priority.
536      *
537      * The priority of sources are defined as follows:
538      *     EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
539      *     EMERGENCY_NUMBER_SOURCE_SIM >
540      *     EMERGENCY_NUMBER_SOURCE_DATABASE >
541      *     EMERGENCY_NUMBER_SOURCE_DEFAULT >
542      *     EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
543      *
544      */
getDisplayPriorityScore()545     private int getDisplayPriorityScore() {
546         int score = 0;
547         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)) {
548             score += 1 << 4;
549         }
550         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
551             score += 1 << 3;
552         }
553         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
554             score += 1 << 2;
555         }
556         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
557             score += 1 << 1;
558         }
559         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)) {
560             score += 1 << 0;
561         }
562         return score;
563     }
564 
565     /**
566      * Compare the display priority for this emergency number and the supplied emergency number.
567      *
568      * @param emergencyNumber the supplied emergency number
569      * @return a negative value if the supplied emergency number has a lower display priority;
570      *         a positive value if the supplied emergency number has a higher display priority;
571      *         0 if both have equal display priority.
572      */
573     @Override
compareTo(@onNull EmergencyNumber emergencyNumber)574     public int compareTo(@NonNull EmergencyNumber emergencyNumber) {
575         if (this.getDisplayPriorityScore()
576                 > emergencyNumber.getDisplayPriorityScore()) {
577             return -1;
578         } else if (this.getDisplayPriorityScore()
579                 < emergencyNumber.getDisplayPriorityScore()) {
580             return 1;
581         } else if (this.getNumber().compareTo(emergencyNumber.getNumber()) != 0) {
582             return this.getNumber().compareTo(emergencyNumber.getNumber());
583         } else if (this.getCountryIso().compareTo(emergencyNumber.getCountryIso()) != 0) {
584             return this.getCountryIso().compareTo(emergencyNumber.getCountryIso());
585         } else if (this.getMnc().compareTo(emergencyNumber.getMnc()) != 0) {
586             return this.getMnc().compareTo(emergencyNumber.getMnc());
587         } else if (this.getEmergencyServiceCategoryBitmask()
588                 != emergencyNumber.getEmergencyServiceCategoryBitmask()) {
589             return this.getEmergencyServiceCategoryBitmask()
590                     > emergencyNumber.getEmergencyServiceCategoryBitmask() ? -1 : 1;
591         } else if (this.getEmergencyUrns().toString().compareTo(
592                 emergencyNumber.getEmergencyUrns().toString()) != 0) {
593             return this.getEmergencyUrns().toString().compareTo(
594                     emergencyNumber.getEmergencyUrns().toString());
595         } else if (this.getEmergencyCallRouting()
596                 != emergencyNumber.getEmergencyCallRouting()) {
597             return this.getEmergencyCallRouting()
598                     > emergencyNumber.getEmergencyCallRouting() ? -1 : 1;
599         } else {
600             return 0;
601         }
602     }
603 
604     /**
605      * In-place merge same emergency numbers in the emergency number list.
606      *
607      * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
608      * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
609      * for the same EmergencyNumber.
610      *
611      * @param emergencyNumberList the emergency number list to process
612      *
613      * @hide
614      */
mergeSameNumbersInEmergencyNumberList( List<EmergencyNumber> emergencyNumberList)615     public static void mergeSameNumbersInEmergencyNumberList(
616             List<EmergencyNumber> emergencyNumberList) {
617         mergeSameNumbersInEmergencyNumberList(emergencyNumberList, false);
618     }
619 
620     /**
621      * In-place merge same emergency numbers in the emergency number list.
622      *
623      * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’ and 'mnc' fields.
624      * If mergeServiceCategoriesAndUrns is true ignore comparing of 'urns' and
625      * 'categories' fields and determine these fields from most precedent number. Else compare
626      * to get unique combination of EmergencyNumber.
627      * Multiple Emergency Number Sources should be merged into one bitfield for the
628      * same EmergencyNumber.
629      *
630      * @param emergencyNumberList the emergency number list to process
631      * @param mergeServiceCategoriesAndUrns {@code true} determine service category and urns
632      * from most precedent number. {@code false} compare those fields for determing duplicate.
633      *
634      * @hide
635      */
mergeSameNumbersInEmergencyNumberList( @onNull List<EmergencyNumber> emergencyNumberList, boolean mergeServiceCategoriesAndUrns)636     public static void mergeSameNumbersInEmergencyNumberList(
637             @NonNull List<EmergencyNumber> emergencyNumberList,
638             boolean mergeServiceCategoriesAndUrns) {
639         if (emergencyNumberList == null) {
640             return;
641         }
642 
643         Set<Integer> duplicatedEmergencyNumberPosition = new HashSet<>();
644         for (int i = 0; i < emergencyNumberList.size(); i++) {
645             for (int j = 0; j < i; j++) {
646                 if (areSameEmergencyNumbers(emergencyNumberList.get(i),
647                         emergencyNumberList.get(j), mergeServiceCategoriesAndUrns)) {
648                     Rlog.e(LOG_TAG, "Found unexpected duplicate numbers "
649                             + emergencyNumberList.get(i)
650                             + " vs " + emergencyNumberList.get(j));
651                     // Set the merged emergency number in the current position
652                     emergencyNumberList.set(i,
653                             mergeSameEmergencyNumbers(emergencyNumberList.get(i),
654                             emergencyNumberList.get(j), mergeServiceCategoriesAndUrns));
655                     // Mark the emergency number has been merged
656                     duplicatedEmergencyNumberPosition.add(j);
657                 }
658             }
659         }
660 
661         // Remove the marked emergency number in the original list
662         for (int i = emergencyNumberList.size() - 1; i >= 0; i--) {
663             if (duplicatedEmergencyNumberPosition.contains(i)) {
664                 emergencyNumberList.remove(i);
665             }
666         }
667         Collections.sort(emergencyNumberList);
668     }
669 
670     /**
671      * Check if two emergency numbers are the same.
672      *
673      * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' fields.
674      * If mergeServiceCategoriesAndUrns is true ignore comparing of 'urns' and
675      * 'categories' fields and determine these fields from most precedent number. Else compare
676      * to get unique combination of EmergencyNumber.
677      * Multiple Emergency Number Sources should be
678      * merged into one bitfield for the same EmergencyNumber.
679      *
680      * @param first first EmergencyNumber to compare
681      * @param second second EmergencyNumber to compare
682      * @param ignoreServiceCategoryAndUrns {@code true} Ignore comparing of service category
683      * and Urns so that they can be determined from most precedent number. {@code false} compare
684      * those fields for determing duplicate.
685      * @return true if they are the same EmergencyNumbers; false otherwise.
686      *
687      * @hide
688      */
areSameEmergencyNumbers(@onNull EmergencyNumber first, @NonNull EmergencyNumber second, boolean ignoreServiceCategoryAndUrns)689     public static boolean areSameEmergencyNumbers(@NonNull EmergencyNumber first,
690             @NonNull EmergencyNumber second, boolean ignoreServiceCategoryAndUrns) {
691         if (!first.getNumber().equals(second.getNumber())) {
692             return false;
693         }
694         if (!first.getCountryIso().equals(second.getCountryIso())) {
695             return false;
696         }
697         if (!first.getMnc().equals(second.getMnc())) {
698             return false;
699         }
700         if (!ignoreServiceCategoryAndUrns) {
701             if (first.getEmergencyServiceCategoryBitmask()
702                     != second.getEmergencyServiceCategoryBitmask()) {
703                 return false;
704             }
705             if (!first.getEmergencyUrns().equals(second.getEmergencyUrns())) {
706                 return false;
707             }
708         }
709         // Never merge two numbers if one of them is from test mode but the other one is not;
710         // This supports to remove a number from the test mode.
711         if (first.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)
712                 ^ second.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)) {
713             return false;
714         }
715         return true;
716     }
717 
718     /**
719      * Get a merged EmergencyNumber from two same emergency numbers. Two emergency numbers are
720      * the same if {@link #areSameEmergencyNumbers} returns {@code true}.
721      *
722      * @param first first EmergencyNumber to compare
723      * @param second second EmergencyNumber to compare
724      * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
725      *
726      * @hide
727      */
mergeSameEmergencyNumbers(@onNull EmergencyNumber first, @NonNull EmergencyNumber second)728     public static EmergencyNumber mergeSameEmergencyNumbers(@NonNull EmergencyNumber first,
729                                                             @NonNull EmergencyNumber second) {
730         if (areSameEmergencyNumbers(first, second, false)) {
731             int routing = first.getEmergencyCallRouting();
732 
733             if (second.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
734                 routing = second.getEmergencyCallRouting();
735             }
736 
737             return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
738                     first.getEmergencyServiceCategoryBitmask(),
739                     first.getEmergencyUrns(),
740                     first.getEmergencyNumberSourceBitmask()
741                             | second.getEmergencyNumberSourceBitmask(),
742                     routing);
743         }
744         return null;
745     }
746 
747     /**
748      * Get merged EmergencyUrns list from two same emergency numbers.
749      * By giving priority to the urns from first number.
750      *
751      * @param firstEmergencyUrns first number's Urns
752      * @param secondEmergencyUrns second number's Urns
753      * @return a merged Urns
754      *
755      * @hide
756      */
mergeEmergencyUrns(@onNull List<String> firstEmergencyUrns, @NonNull List<String> secondEmergencyUrns)757     private static List<String> mergeEmergencyUrns(@NonNull List<String> firstEmergencyUrns,
758             @NonNull List<String> secondEmergencyUrns) {
759         List<String> mergedUrns = new ArrayList<String>();
760         mergedUrns.addAll(firstEmergencyUrns);
761         for (String urn : secondEmergencyUrns) {
762             if (!firstEmergencyUrns.contains(urn)) {
763                 mergedUrns.add(urn);
764             }
765         }
766         return mergedUrns;
767     }
768 
769     /**
770      * Get the highest precedence source of the given Emergency number. Then get service catergory
771      * and urns list fill in the respective map with key as source.
772      *
773      * @param num EmergencyNumber to get the source, service category & urns
774      * @param serviceCategoryArray Array to store the category of the given EmergencyNumber
775      * with key as highest precedence source
776      * @param urnsArray Array to store the list of Urns of the given EmergencyNumber
777      * with key as highest precedence source
778      *
779      * @hide
780      */
fillServiceCategoryAndUrns(@onNull EmergencyNumber num, @NonNull SparseIntArray serviceCategoryArray, @NonNull SparseArray<List<String>> urnsArray)781     private static void fillServiceCategoryAndUrns(@NonNull EmergencyNumber num,
782             @NonNull SparseIntArray serviceCategoryArray,
783             @NonNull SparseArray<List<String>> urnsArray) {
784         int numberSrc = num.getEmergencyNumberSourceBitmask();
785         for (Integer source : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) {
786             if ((numberSrc & source) == source) {
787                 if (!num.isInEmergencyServiceCategories(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED)) {
788                     serviceCategoryArray.put(source, num.getEmergencyServiceCategoryBitmask());
789                 }
790                 urnsArray.put(source, num.getEmergencyUrns());
791                 break;
792             }
793         }
794     }
795 
796     /**
797      * Get a merged EmergencyNumber from two same emergency numbers from
798      * Emergency number list. Two emergency numbers are the same if
799      * {@link #areSameEmergencyNumbers} returns {@code true}.
800      *
801      * @param first first EmergencyNumber to compare
802      * @param second second EmergencyNumber to compare
803      * @param mergeServiceCategoriesAndUrns {@code true} then determine service category and urns
804      * Service catetory : set from most precedence source number(N/W, SIM, DB, modem_cfg)
805      * Urns : merge from both with first priority from most precedence source number
806      * {@code false} then call {@link #mergeSameEmergencyNumbers} to merge.
807      * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
808      *
809      * @hide
810      */
mergeSameEmergencyNumbers( @onNull EmergencyNumber first, @NonNull EmergencyNumber second, boolean mergeServiceCategoriesAndUrns)811     public static @NonNull EmergencyNumber mergeSameEmergencyNumbers(
812             @NonNull EmergencyNumber first, @NonNull EmergencyNumber second,
813             boolean mergeServiceCategoriesAndUrns) {
814         if (!mergeServiceCategoriesAndUrns) {
815             return mergeSameEmergencyNumbers(first, second);
816         }
817 
818         int routing = first.getEmergencyCallRouting();
819         int serviceCategory = first.getEmergencyServiceCategoryBitmask();
820         List<String> mergedEmergencyUrns = new ArrayList<String>();
821         //Maps to store the service category and urns of both the first and second emergency number
822         // with key as most precedent source
823         SparseIntArray serviceCategoryArray = new SparseIntArray(2);
824         SparseArray<List<String>> urnsArray = new SparseArray(2);
825 
826         fillServiceCategoryAndUrns(first, serviceCategoryArray, urnsArray);
827         fillServiceCategoryAndUrns(second, serviceCategoryArray, urnsArray);
828 
829         if (second.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
830             routing = second.getEmergencyCallRouting();
831         }
832 
833         // Determine serviceCategory of most precedence number
834         for (int sourceOfCategory : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) {
835             if (serviceCategoryArray.indexOfKey(sourceOfCategory) >= 0) {
836                 serviceCategory = serviceCategoryArray.get(sourceOfCategory);
837                 break;
838             }
839         }
840 
841         // Merge Urns in precedence number
842         for (int sourceOfUrn : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) {
843             if (urnsArray.contains(sourceOfUrn)) {
844                 mergedEmergencyUrns = mergeEmergencyUrns(mergedEmergencyUrns,
845                         urnsArray.get(sourceOfUrn));
846             }
847         }
848 
849         return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
850                 serviceCategory, mergedEmergencyUrns,
851                 first.getEmergencyNumberSourceBitmask()
852                         | second.getEmergencyNumberSourceBitmask(),
853                 routing);
854     }
855 
856     /**
857      * Validate Emergency Number address that only contains the dialable character
858      * {@link PhoneNumberUtils#isDialable(char)}
859      *
860      * @hide
861      */
validateEmergencyNumberAddress(String address)862     public static boolean validateEmergencyNumberAddress(String address) {
863         if (address == null) {
864             return false;
865         }
866         for (char c : address.toCharArray()) {
867             if (!PhoneNumberUtils.isDialable(c)) {
868                 return false;
869             }
870         }
871         return true;
872     }
873 }
874