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