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