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