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.voice.EmergencyServiceCategory;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.telephony.CarrierConfigManager;
26 import android.telephony.PhoneNumberUtils;
27 import android.util.SparseArray;
28 import android.util.SparseIntArray;
29 
30 import com.android.telephony.Rlog;
31 
32 import java.lang.annotation.Retention;
33 import java.lang.annotation.RetentionPolicy;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Objects;
39 import java.util.Set;
40 import java.util.stream.Collectors;
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             android.hardware.radio.voice.EmergencyNumber.SOURCE_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 =
182             android.hardware.radio.voice.EmergencyNumber.SOURCE_SIM;
183     /**
184      * Bit-field which indicates the number is from the platform-maintained database.
185      */
186     public static final int EMERGENCY_NUMBER_SOURCE_DATABASE =  1 << 4;
187     /**
188      * Bit-field which indicates the number is from test mode.
189      *
190      * @hide
191      */
192     @TestApi
193     public static final int EMERGENCY_NUMBER_SOURCE_TEST =  1 << 5;
194     /** Bit-field which indicates the number is from the modem config. */
195     public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
196             android.hardware.radio.voice.EmergencyNumber.SOURCE_MODEM_CONFIG;
197     /**
198      * Bit-field which indicates the number is available as default.
199      *
200      * 112, 911 must always be available; additionally, 000, 08, 110, 999, 118 and 119 must be
201      * available when sim is not present.
202      *
203      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
204      */
205     public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT =
206             android.hardware.radio.voice.EmergencyNumber.SOURCE_DEFAULT;
207 
208     private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
209     static {
210         EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
211         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
212         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
213         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DATABASE);
214         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
215         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
216     }
217 
218     /**
219      * Indicated the framework does not know whether an emergency call should be placed using
220      * emergency or normal call routing. This means the underlying radio or IMS implementation is
221      * free to determine for itself how to route the call.
222      */
223     public static final int EMERGENCY_CALL_ROUTING_UNKNOWN = 0;
224     /**
225      * Indicates the radio or IMS implementation must handle the call through emergency routing.
226      */
227     public static final int EMERGENCY_CALL_ROUTING_EMERGENCY = 1;
228     /**
229      * Indicates the radio or IMS implementation must handle the call through normal call routing.
230      */
231     public static final int EMERGENCY_CALL_ROUTING_NORMAL = 2;
232 
233     /**
234      * The routing to tell how to handle the call for the corresponding emergency number.
235      *
236      * @hide
237      */
238     @IntDef(flag = false, prefix = { "EMERGENCY_CALL_ROUTING_" }, value = {
239             EMERGENCY_CALL_ROUTING_UNKNOWN,
240             EMERGENCY_CALL_ROUTING_EMERGENCY,
241             EMERGENCY_CALL_ROUTING_NORMAL
242     })
243     @Retention(RetentionPolicy.SOURCE)
244     public @interface EmergencyCallRouting {}
245 
246 
247     private final String mNumber;
248     private final String mCountryIso;
249     private final String mMnc;
250     private final int mEmergencyServiceCategoryBitmask;
251     private final List<String> mEmergencyUrns;
252     private final int mEmergencyNumberSourceBitmask;
253     private final int mEmergencyCallRouting;
254     /**
255      * The source of the EmergencyNumber in the order of precedence.
256      */
257     private static final int[] EMERGENCY_NUMBER_SOURCE_PRECEDENCE;
258     static {
259         EMERGENCY_NUMBER_SOURCE_PRECEDENCE = new int[4];
260         EMERGENCY_NUMBER_SOURCE_PRECEDENCE[0] = EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING;
261         EMERGENCY_NUMBER_SOURCE_PRECEDENCE[1] = EMERGENCY_NUMBER_SOURCE_SIM;
262         EMERGENCY_NUMBER_SOURCE_PRECEDENCE[2] = EMERGENCY_NUMBER_SOURCE_DATABASE;
263         EMERGENCY_NUMBER_SOURCE_PRECEDENCE[3] = EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG;
264     }
265 
266     /** @hide */
EmergencyNumber(@onNull String number, @NonNull String countryIso, @NonNull String mnc, @EmergencyServiceCategories int emergencyServiceCategories, @NonNull List<String> emergencyUrns, @EmergencyNumberSources int emergencyNumberSources, @EmergencyCallRouting int emergencyCallRouting)267     public EmergencyNumber(@NonNull String number, @NonNull String countryIso, @NonNull String mnc,
268                            @EmergencyServiceCategories int emergencyServiceCategories,
269                            @NonNull List<String> emergencyUrns,
270                            @EmergencyNumberSources int emergencyNumberSources,
271                            @EmergencyCallRouting int emergencyCallRouting) {
272         this.mNumber = number;
273         this.mCountryIso = countryIso;
274         this.mMnc = mnc;
275         this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
276         this.mEmergencyUrns = emergencyUrns;
277         this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
278         this.mEmergencyCallRouting = emergencyCallRouting;
279     }
280 
281     /** @hide */
EmergencyNumber(Parcel source)282     public EmergencyNumber(Parcel source) {
283         mNumber = source.readString();
284         mCountryIso = source.readString();
285         mMnc = source.readString();
286         mEmergencyServiceCategoryBitmask = source.readInt();
287         mEmergencyUrns = source.createStringArrayList();
288         mEmergencyNumberSourceBitmask = source.readInt();
289         mEmergencyCallRouting = source.readInt();
290     }
291 
292     @Override
293     /** @hide */
writeToParcel(Parcel dest, int flags)294     public void writeToParcel(Parcel dest, int flags) {
295         dest.writeString(mNumber);
296         dest.writeString(mCountryIso);
297         dest.writeString(mMnc);
298         dest.writeInt(mEmergencyServiceCategoryBitmask);
299         dest.writeStringList(mEmergencyUrns);
300         dest.writeInt(mEmergencyNumberSourceBitmask);
301         dest.writeInt(mEmergencyCallRouting);
302     }
303 
304     public static final @NonNull Creator<EmergencyNumber> CREATOR =
305             new Creator<EmergencyNumber>() {
306                 @Override
307                 public EmergencyNumber createFromParcel(Parcel in) {
308                     return new EmergencyNumber(in);
309                 }
310 
311                 @Override
312                 public EmergencyNumber[] newArray(int size) {
313                     return new EmergencyNumber[size];
314                 }
315             };
316 
317     /**
318      * Get the dialing number of the emergency number.
319      *
320      * The character in the number string is only the dial pad
321      * character('0'-'9', '*', '+', or '#'). For example: 911.
322      *
323      * If the number starts with carrier prefix, the carrier prefix is configured in
324      * {@link CarrierConfigManager#KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY}.
325      *
326      * @return the dialing number.
327      */
getNumber()328     public @NonNull String getNumber() {
329         return mNumber;
330     }
331 
332     /**
333      * Get the country code string (lowercase character) in ISO 3166 format of the emergency number.
334      *
335      * @return the country code string (lowercase character) in ISO 3166 format.
336      */
getCountryIso()337     public @NonNull String getCountryIso() {
338         return mCountryIso;
339     }
340 
341     /**
342      * Get the Mobile Network Code of the emergency number.
343      *
344      * @return the Mobile Network Code of the emergency number.
345      */
getMnc()346     public @NonNull String getMnc() {
347         return mMnc;
348     }
349 
350     /**
351      * Returns the bitmask of emergency service categories of the emergency number.
352      *
353      * @return bitmask of the emergency service categories
354      *
355      * @hide
356      */
getEmergencyServiceCategoryBitmask()357     public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() {
358         return mEmergencyServiceCategoryBitmask;
359     }
360 
361     /**
362      * Returns the bitmask of emergency service categories of the emergency number for
363      * internal dialing.
364      *
365      * @return bitmask of the emergency service categories
366      *
367      * @hide
368      */
getEmergencyServiceCategoryBitmaskInternalDial()369     public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmaskInternalDial() {
370         if (mEmergencyNumberSourceBitmask == EMERGENCY_NUMBER_SOURCE_DATABASE) {
371             return EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
372         }
373         return mEmergencyServiceCategoryBitmask;
374     }
375 
376     /**
377      * Returns the emergency service categories of the emergency number.
378      *
379      * Note: if the emergency number is in {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}, only
380      * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED} is returned and it means the number is in
381      * all categories.
382      *
383      * @return a list of the emergency service categories
384      */
getEmergencyServiceCategories()385     public @NonNull List<Integer> getEmergencyServiceCategories() {
386         List<Integer> categories = new ArrayList<>();
387         if (serviceUnspecified()) {
388             categories.add(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
389             return categories;
390         }
391         for (Integer category : EMERGENCY_SERVICE_CATEGORY_SET) {
392             if (isInEmergencyServiceCategories(category)) {
393                 categories.add(category);
394             }
395         }
396         return categories;
397     }
398 
399     /**
400      * Returns the list of emergency Uniform Resources Names (URN) of the emergency number.
401      *
402      * For example, {@code urn:service:sos} is the generic URN for contacting emergency services
403      * of all type.
404      *
405      * Reference: 3gpp 24.503, Section 5.1.6.8.1 - General;
406      *            RFC 5031
407      *
408      * @return list of emergency Uniform Resources Names (URN) or an empty list if the emergency
409      *         number does not have a specified emergency Uniform Resource Name.
410      */
getEmergencyUrns()411     public @NonNull List<String> getEmergencyUrns() {
412         return Collections.unmodifiableList(mEmergencyUrns);
413     }
414 
415     /**
416      * Checks if the emergency service category is unspecified for the emergency number
417      * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}.
418      *
419      * @return {@code true} if the emergency service category is unspecified for the emergency
420      * number {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
421      */
serviceUnspecified()422     private boolean serviceUnspecified() {
423         return mEmergencyServiceCategoryBitmask == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
424     }
425 
426     /**
427      * Checks if the emergency number is in the supplied emergency service category(s).
428      *
429      * @param categories - the supplied emergency service categories
430      *
431      * @return {@code true} if the emergency number is in the specified emergency service
432      * category(s) or if its emergency service category is
433      * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
434      */
isInEmergencyServiceCategories(@mergencyServiceCategories int categories)435     public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) {
436         if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
437             return serviceUnspecified();
438         }
439         if (serviceUnspecified()) {
440             return true;
441         }
442         return (mEmergencyServiceCategoryBitmask & categories) == categories;
443     }
444 
445     /**
446      * Returns the bitmask of the sources of the emergency number.
447      *
448      * @return bitmask of the emergency number sources
449      *
450      * @hide
451      */
getEmergencyNumberSourceBitmask()452     public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() {
453         return mEmergencyNumberSourceBitmask;
454     }
455 
456     /**
457      * Returns a list of sources of the emergency number.
458      *
459      * @return a list of emergency number sources
460      */
getEmergencyNumberSources()461     public @NonNull List<Integer> getEmergencyNumberSources() {
462         List<Integer> sources = new ArrayList<>();
463         for (Integer source : EMERGENCY_NUMBER_SOURCE_SET) {
464             if ((mEmergencyNumberSourceBitmask & source) == source) {
465                 sources.add(source);
466             }
467         }
468         return sources;
469     }
470 
471     /**
472      * Checks if the emergency number is from the specified emergency number source(s).
473      *
474      * @return {@code true} if the emergency number is from the specified emergency number
475      * source(s); {@code false} otherwise.
476      *
477      * @param sources - the supplied emergency number sources
478      */
isFromSources(@mergencyNumberSources int sources)479     public boolean isFromSources(@EmergencyNumberSources int sources) {
480         return (mEmergencyNumberSourceBitmask & sources) == sources;
481     }
482 
483     /**
484      * Returns the emergency call routing information.
485      *
486      * <p>Some regions require some emergency numbers which are not routed using typical emergency
487      * call processing, but are instead placed as regular phone calls. The emergency call routing
488      * field provides information about how an emergency call will be routed when it is placed.
489      *
490      * @return the emergency call routing requirement
491      */
getEmergencyCallRouting()492     public @EmergencyCallRouting int getEmergencyCallRouting() {
493         return mEmergencyCallRouting;
494     }
495 
496     @Override
497     /** @hide */
describeContents()498     public int describeContents() {
499         return 0;
500     }
501 
502     @Override
toString()503     public String toString() {
504         return String.format("[EmergencyNumber: %s, countryIso=%s, mnc=%s, src=%s, routing=%s, "
505                         + "categories=%s, urns=%s]",
506                 mNumber,
507                 mCountryIso,
508                 mMnc,
509                 sourceBitmaskToString(mEmergencyNumberSourceBitmask),
510                 routingToString(mEmergencyCallRouting),
511                 categoriesToString(mEmergencyServiceCategoryBitmask),
512                 (mEmergencyUrns == null ? "" :
513                         mEmergencyUrns.stream().collect(Collectors.joining(","))));
514     }
515 
516     /**
517      * @param categories emergency service category bitmask
518      * @return loggable string describing the category bitmask
519      */
categoriesToString(@mergencyServiceCategories int categories)520     private String categoriesToString(@EmergencyServiceCategories int categories) {
521         StringBuilder sb = new StringBuilder();
522         if ((categories & EMERGENCY_SERVICE_CATEGORY_AIEC) == EMERGENCY_SERVICE_CATEGORY_AIEC) {
523             sb.append("auto ");
524         }
525         if ((categories & EMERGENCY_SERVICE_CATEGORY_AMBULANCE)
526                 == EMERGENCY_SERVICE_CATEGORY_AMBULANCE) {
527             sb.append("ambulance ");
528         }
529         if ((categories & EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE)
530                 == EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE) {
531             sb.append("fire ");
532         }
533         if ((categories & EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD)
534                 == EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD) {
535             sb.append("marine ");
536         }
537         if ((categories & EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE)
538                 == EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE) {
539             sb.append("mountain ");
540         }
541         if ((categories & EMERGENCY_SERVICE_CATEGORY_POLICE) == EMERGENCY_SERVICE_CATEGORY_POLICE) {
542             sb.append("police ");
543         }
544         if ((categories & EMERGENCY_SERVICE_CATEGORY_MIEC) == EMERGENCY_SERVICE_CATEGORY_MIEC) {
545             sb.append("manual ");
546         }
547         return sb.toString();
548     }
549 
550     /**
551      * @param routing emergency call routing type
552      * @return loggable string describing the routing type.
553      */
routingToString(@mergencyCallRouting int routing)554     private String routingToString(@EmergencyCallRouting int routing) {
555         return switch(routing) {
556             case EMERGENCY_CALL_ROUTING_EMERGENCY -> "emergency";
557             case EMERGENCY_CALL_ROUTING_NORMAL -> "normal";
558             case EMERGENCY_CALL_ROUTING_UNKNOWN -> "unknown";
559             default -> "��";
560         };
561     }
562 
563     /**
564      * Builds a string describing the sources for an emergency number.
565      * @param sourceBitmask the source bitmask
566      * @return loggable string describing the sources.
567      */
sourceBitmaskToString(@mergencyNumberSources int sourceBitmask)568     private String sourceBitmaskToString(@EmergencyNumberSources int sourceBitmask) {
569         StringBuilder sb = new StringBuilder();
570         if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)
571                 == EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING) {
572             sb.append("net ");
573         }
574         if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_SIM) == EMERGENCY_NUMBER_SOURCE_SIM) {
575             sb.append("sim ");
576         }
577         if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_DATABASE)
578                 == EMERGENCY_NUMBER_SOURCE_DATABASE) {
579             sb.append("db ");
580         }
581         if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)
582                 == EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG) {
583             sb.append("mdm ");
584         }
585         if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_DEFAULT) == EMERGENCY_NUMBER_SOURCE_DEFAULT) {
586             sb.append("def ");
587         }
588         if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_TEST) == EMERGENCY_NUMBER_SOURCE_TEST) {
589             sb.append("tst ");
590         }
591         return sb.toString();
592     }
593 
594     @Override
equals(Object o)595     public boolean equals(Object o) {
596         if (!EmergencyNumber.class.isInstance(o)) {
597             return false;
598         }
599         EmergencyNumber other = (EmergencyNumber) o;
600         return mNumber.equals(other.mNumber)
601                 && mCountryIso.equals(other.mCountryIso)
602                 && mMnc.equals(other.mMnc)
603                 && mEmergencyServiceCategoryBitmask == other.mEmergencyServiceCategoryBitmask
604                 && mEmergencyUrns.equals(other.mEmergencyUrns)
605                 && mEmergencyNumberSourceBitmask == other.mEmergencyNumberSourceBitmask
606                 && mEmergencyCallRouting == other.mEmergencyCallRouting;
607     }
608 
609     @Override
hashCode()610     public int hashCode() {
611         return Objects.hash(mNumber, mCountryIso, mMnc, mEmergencyServiceCategoryBitmask,
612                 mEmergencyUrns, mEmergencyNumberSourceBitmask, mEmergencyCallRouting);
613     }
614 
615     /**
616      * Calculate the score for display priority.
617      *
618      * A higher display priority score means the emergency number has a higher display priority.
619      * The score is higher if the source is defined for a higher display priority.
620      *
621      * The priority of sources are defined as follows:
622      *     EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
623      *     EMERGENCY_NUMBER_SOURCE_SIM >
624      *     EMERGENCY_NUMBER_SOURCE_DATABASE >
625      *     EMERGENCY_NUMBER_SOURCE_DEFAULT >
626      *     EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
627      *
628      */
getDisplayPriorityScore()629     private int getDisplayPriorityScore() {
630         int score = 0;
631         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)) {
632             score += 1 << 4;
633         }
634         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
635             score += 1 << 3;
636         }
637         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
638             score += 1 << 2;
639         }
640         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
641             score += 1 << 1;
642         }
643         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)) {
644             score += 1 << 0;
645         }
646         return score;
647     }
648 
649     /**
650      * Compare the display priority for this emergency number and the supplied emergency number.
651      *
652      * @param emergencyNumber the supplied emergency number
653      * @return a negative value if the supplied emergency number has a lower display priority;
654      *         a positive value if the supplied emergency number has a higher display priority;
655      *         0 if both have equal display priority.
656      */
657     @Override
compareTo(@onNull EmergencyNumber emergencyNumber)658     public int compareTo(@NonNull EmergencyNumber emergencyNumber) {
659         if (this.getDisplayPriorityScore()
660                 > emergencyNumber.getDisplayPriorityScore()) {
661             return -1;
662         } else if (this.getDisplayPriorityScore()
663                 < emergencyNumber.getDisplayPriorityScore()) {
664             return 1;
665         } else if (this.getNumber().compareTo(emergencyNumber.getNumber()) != 0) {
666             return this.getNumber().compareTo(emergencyNumber.getNumber());
667         } else if (this.getCountryIso().compareTo(emergencyNumber.getCountryIso()) != 0) {
668             return this.getCountryIso().compareTo(emergencyNumber.getCountryIso());
669         } else if (this.getMnc().compareTo(emergencyNumber.getMnc()) != 0) {
670             return this.getMnc().compareTo(emergencyNumber.getMnc());
671         } else if (this.getEmergencyServiceCategoryBitmask()
672                 != emergencyNumber.getEmergencyServiceCategoryBitmask()) {
673             return this.getEmergencyServiceCategoryBitmask()
674                     > emergencyNumber.getEmergencyServiceCategoryBitmask() ? -1 : 1;
675         } else if (this.getEmergencyUrns().toString().compareTo(
676                 emergencyNumber.getEmergencyUrns().toString()) != 0) {
677             return this.getEmergencyUrns().toString().compareTo(
678                     emergencyNumber.getEmergencyUrns().toString());
679         } else if (this.getEmergencyCallRouting()
680                 != emergencyNumber.getEmergencyCallRouting()) {
681             return this.getEmergencyCallRouting()
682                     > emergencyNumber.getEmergencyCallRouting() ? -1 : 1;
683         } else {
684             return 0;
685         }
686     }
687 
688     /**
689      * In-place merge same emergency numbers in the emergency number list.
690      *
691      * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
692      * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
693      * for the same EmergencyNumber.
694      *
695      * @param emergencyNumberList the emergency number list to process
696      *
697      * @hide
698      */
mergeSameNumbersInEmergencyNumberList( List<EmergencyNumber> emergencyNumberList)699     public static void mergeSameNumbersInEmergencyNumberList(
700             List<EmergencyNumber> emergencyNumberList) {
701         mergeSameNumbersInEmergencyNumberList(emergencyNumberList, false);
702     }
703 
704     /**
705      * In-place merge same emergency numbers in the emergency number list.
706      *
707      * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’ and 'mnc' fields.
708      * If mergeServiceCategoriesAndUrns is true ignore comparing of 'urns' and
709      * 'categories' fields and determine these fields from most precedent number. Else compare
710      * to get unique combination of EmergencyNumber.
711      * Multiple Emergency Number Sources should be merged into one bitfield for the
712      * same EmergencyNumber.
713      *
714      * @param emergencyNumberList the emergency number list to process
715      * @param mergeServiceCategoriesAndUrns {@code true} determine service category and urns
716      * from most precedent number. {@code false} compare those fields for determing duplicate.
717      *
718      * @hide
719      */
mergeSameNumbersInEmergencyNumberList( @onNull List<EmergencyNumber> emergencyNumberList, boolean mergeServiceCategoriesAndUrns)720     public static void mergeSameNumbersInEmergencyNumberList(
721             @NonNull List<EmergencyNumber> emergencyNumberList,
722             boolean mergeServiceCategoriesAndUrns) {
723         if (emergencyNumberList == null) {
724             return;
725         }
726 
727         Set<Integer> duplicatedEmergencyNumberPosition = new HashSet<>();
728         for (int i = 0; i < emergencyNumberList.size(); i++) {
729             for (int j = 0; j < i; j++) {
730                 if (areSameEmergencyNumbers(emergencyNumberList.get(i),
731                         emergencyNumberList.get(j), mergeServiceCategoriesAndUrns)) {
732                     Rlog.e(LOG_TAG, "Found unexpected duplicate numbers "
733                             + emergencyNumberList.get(i)
734                             + " vs " + emergencyNumberList.get(j));
735                     // Set the merged emergency number in the current position
736                     emergencyNumberList.set(i,
737                             mergeSameEmergencyNumbers(emergencyNumberList.get(i),
738                             emergencyNumberList.get(j), mergeServiceCategoriesAndUrns));
739                     // Mark the emergency number has been merged
740                     duplicatedEmergencyNumberPosition.add(j);
741                 }
742             }
743         }
744 
745         // Remove the marked emergency number in the original list
746         for (int i = emergencyNumberList.size() - 1; i >= 0; i--) {
747             if (duplicatedEmergencyNumberPosition.contains(i)) {
748                 emergencyNumberList.remove(i);
749             }
750         }
751         Collections.sort(emergencyNumberList);
752     }
753 
754     /**
755      * Check if two emergency numbers are the same.
756      *
757      * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' fields.
758      * If mergeServiceCategoriesAndUrns is true ignore comparing of 'urns' and
759      * 'categories' fields and determine these fields from most precedent number. Else compare
760      * to get unique combination of EmergencyNumber.
761      * Multiple Emergency Number Sources should be
762      * merged into one bitfield for the same EmergencyNumber.
763      *
764      * @param first first EmergencyNumber to compare
765      * @param second second EmergencyNumber to compare
766      * @param ignoreServiceCategoryAndUrns {@code true} Ignore comparing of service category
767      * and Urns so that they can be determined from most precedent number. {@code false} compare
768      * those fields for determing duplicate.
769      * @return true if they are the same EmergencyNumbers; false otherwise.
770      *
771      * @hide
772      */
areSameEmergencyNumbers(@onNull EmergencyNumber first, @NonNull EmergencyNumber second, boolean ignoreServiceCategoryAndUrns)773     public static boolean areSameEmergencyNumbers(@NonNull EmergencyNumber first,
774             @NonNull EmergencyNumber second, boolean ignoreServiceCategoryAndUrns) {
775         if (!first.getNumber().equals(second.getNumber())) {
776             return false;
777         }
778         if (!first.getCountryIso().equals(second.getCountryIso())) {
779             return false;
780         }
781         if (!first.getMnc().equals(second.getMnc())) {
782             return false;
783         }
784         if (!ignoreServiceCategoryAndUrns) {
785             if (first.getEmergencyServiceCategoryBitmask()
786                     != second.getEmergencyServiceCategoryBitmask()) {
787                 return false;
788             }
789             if (!first.getEmergencyUrns().equals(second.getEmergencyUrns())) {
790                 return false;
791             }
792         }
793         // Never merge two numbers if one of them is from test mode but the other one is not;
794         // This supports to remove a number from the test mode.
795         if (first.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)
796                 ^ second.isFromSources(EMERGENCY_NUMBER_SOURCE_TEST)) {
797             return false;
798         }
799         return true;
800     }
801 
802     /**
803      * Get a merged EmergencyNumber from two same emergency numbers. Two emergency numbers are
804      * the same if {@link #areSameEmergencyNumbers} returns {@code true}.
805      *
806      * @param first first EmergencyNumber to compare
807      * @param second second EmergencyNumber to compare
808      * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
809      *
810      * @hide
811      */
mergeSameEmergencyNumbers(@onNull EmergencyNumber first, @NonNull EmergencyNumber second)812     public static EmergencyNumber mergeSameEmergencyNumbers(@NonNull EmergencyNumber first,
813                                                             @NonNull EmergencyNumber second) {
814         if (areSameEmergencyNumbers(first, second, false)) {
815             int routing = first.getEmergencyCallRouting();
816 
817             if (second.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
818                 routing = second.getEmergencyCallRouting();
819             }
820 
821             return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
822                     first.getEmergencyServiceCategoryBitmask(),
823                     first.getEmergencyUrns(),
824                     first.getEmergencyNumberSourceBitmask()
825                             | second.getEmergencyNumberSourceBitmask(),
826                     routing);
827         }
828         return null;
829     }
830 
831     /**
832      * Get merged EmergencyUrns list from two same emergency numbers.
833      * By giving priority to the urns from first number.
834      *
835      * @param firstEmergencyUrns first number's Urns
836      * @param secondEmergencyUrns second number's Urns
837      * @return a merged Urns
838      *
839      * @hide
840      */
mergeEmergencyUrns(@onNull List<String> firstEmergencyUrns, @NonNull List<String> secondEmergencyUrns)841     private static List<String> mergeEmergencyUrns(@NonNull List<String> firstEmergencyUrns,
842             @NonNull List<String> secondEmergencyUrns) {
843         List<String> mergedUrns = new ArrayList<String>();
844         mergedUrns.addAll(firstEmergencyUrns);
845         for (String urn : secondEmergencyUrns) {
846             if (!firstEmergencyUrns.contains(urn)) {
847                 mergedUrns.add(urn);
848             }
849         }
850         return mergedUrns;
851     }
852 
853     /**
854      * Get the highest precedence source of the given Emergency number. Then get service catergory
855      * and urns list fill in the respective map with key as source.
856      *
857      * @param num EmergencyNumber to get the source, service category & urns
858      * @param serviceCategoryArray Array to store the category of the given EmergencyNumber
859      * with key as highest precedence source
860      * @param urnsArray Array to store the list of Urns of the given EmergencyNumber
861      * with key as highest precedence source
862      *
863      * @hide
864      */
fillServiceCategoryAndUrns(@onNull EmergencyNumber num, @NonNull SparseIntArray serviceCategoryArray, @NonNull SparseArray<List<String>> urnsArray)865     private static void fillServiceCategoryAndUrns(@NonNull EmergencyNumber num,
866             @NonNull SparseIntArray serviceCategoryArray,
867             @NonNull SparseArray<List<String>> urnsArray) {
868         int numberSrc = num.getEmergencyNumberSourceBitmask();
869         for (Integer source : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) {
870             if ((numberSrc & source) == source) {
871                 if (!num.isInEmergencyServiceCategories(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED)) {
872                     serviceCategoryArray.put(source, num.getEmergencyServiceCategoryBitmask());
873                 }
874                 urnsArray.put(source, num.getEmergencyUrns());
875                 break;
876             }
877         }
878     }
879 
880     /**
881      * Get a merged EmergencyNumber from two same emergency numbers from
882      * Emergency number list. Two emergency numbers are the same if
883      * {@link #areSameEmergencyNumbers} returns {@code true}.
884      *
885      * @param first first EmergencyNumber to compare
886      * @param second second EmergencyNumber to compare
887      * @param mergeServiceCategoriesAndUrns {@code true} then determine service category and urns
888      * Service catetory : set from most precedence source number(N/W, SIM, DB, modem_cfg)
889      * Urns : merge from both with first priority from most precedence source number
890      * {@code false} then call {@link #mergeSameEmergencyNumbers} to merge.
891      * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
892      *
893      * @hide
894      */
mergeSameEmergencyNumbers( @onNull EmergencyNumber first, @NonNull EmergencyNumber second, boolean mergeServiceCategoriesAndUrns)895     public static @NonNull EmergencyNumber mergeSameEmergencyNumbers(
896             @NonNull EmergencyNumber first, @NonNull EmergencyNumber second,
897             boolean mergeServiceCategoriesAndUrns) {
898         if (!mergeServiceCategoriesAndUrns) {
899             return mergeSameEmergencyNumbers(first, second);
900         }
901 
902         int routing = first.getEmergencyCallRouting();
903         int serviceCategory = first.getEmergencyServiceCategoryBitmask();
904         List<String> mergedEmergencyUrns = new ArrayList<String>();
905         //Maps to store the service category and urns of both the first and second emergency number
906         // with key as most precedent source
907         SparseIntArray serviceCategoryArray = new SparseIntArray(2);
908         SparseArray<List<String>> urnsArray = new SparseArray(2);
909 
910         fillServiceCategoryAndUrns(first, serviceCategoryArray, urnsArray);
911         fillServiceCategoryAndUrns(second, serviceCategoryArray, urnsArray);
912 
913         if (second.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
914             routing = second.getEmergencyCallRouting();
915         }
916 
917         // Determine serviceCategory of most precedence number
918         for (int sourceOfCategory : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) {
919             if (serviceCategoryArray.indexOfKey(sourceOfCategory) >= 0) {
920                 serviceCategory = serviceCategoryArray.get(sourceOfCategory);
921                 break;
922             }
923         }
924 
925         // Merge Urns in precedence number
926         for (int sourceOfUrn : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) {
927             if (urnsArray.contains(sourceOfUrn)) {
928                 mergedEmergencyUrns = mergeEmergencyUrns(mergedEmergencyUrns,
929                         urnsArray.get(sourceOfUrn));
930             }
931         }
932 
933         return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
934                 serviceCategory, mergedEmergencyUrns,
935                 first.getEmergencyNumberSourceBitmask()
936                         | second.getEmergencyNumberSourceBitmask(),
937                 routing);
938     }
939 
940     /**
941      * Validate Emergency Number address that only contains the dialable character
942      * {@link PhoneNumberUtils#isDialable(char)}
943      *
944      * @hide
945      */
validateEmergencyNumberAddress(String address)946     public static boolean validateEmergencyNumberAddress(String address) {
947         if (address == null) {
948             return false;
949         }
950         for (char c : address.toCharArray()) {
951             if (!PhoneNumberUtils.isDialable(c)) {
952                 return false;
953             }
954         }
955         return true;
956     }
957 }
958