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