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