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