diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index e78a1e107afea..64bcf7171adea 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -25,6 +25,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.telephony.CarrierConfigManager; import android.telephony.PhoneNumberUtils; +import android.util.SparseArray; +import android.util.SparseIntArray; import com.android.telephony.Rlog; @@ -247,6 +249,17 @@ public final class EmergencyNumber implements Parcelable, Comparable mEmergencyUrns; private final int mEmergencyNumberSourceBitmask; private final int mEmergencyCallRouting; + /** + * The source of the EmergencyNumber in the order of precedence. + */ + private static final int[] EMERGENCY_NUMBER_SOURCE_PRECEDENCE; + static { + EMERGENCY_NUMBER_SOURCE_PRECEDENCE = new int[4]; + EMERGENCY_NUMBER_SOURCE_PRECEDENCE[0] = EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING; + EMERGENCY_NUMBER_SOURCE_PRECEDENCE[1] = EMERGENCY_NUMBER_SOURCE_SIM; + EMERGENCY_NUMBER_SOURCE_PRECEDENCE[2] = EMERGENCY_NUMBER_SOURCE_DATABASE; + EMERGENCY_NUMBER_SOURCE_PRECEDENCE[3] = EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG; + } /** @hide */ public EmergencyNumber(@NonNull String number, @NonNull String countryIso, @NonNull String mnc, @@ -601,19 +614,44 @@ public final class EmergencyNumber implements Parcelable, Comparable emergencyNumberList) { + mergeSameNumbersInEmergencyNumberList(emergencyNumberList, false); + } + + /** + * In-place merge same emergency numbers in the emergency number list. + * + * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’ and 'mnc' fields. + * If mergeServiceCategoriesAndUrns is true ignore comparing of 'urns' and + * 'categories' fields and determine these fields from most precedent number. Else compare + * to get unique combination of EmergencyNumber. + * Multiple Emergency Number Sources should be merged into one bitfield for the + * same EmergencyNumber. + * + * @param emergencyNumberList the emergency number list to process + * @param mergeServiceCategoriesAndUrns {@code true} determine service category and urns + * from most precedent number. {@code false} compare those fields for determing duplicate. + * + * @hide + */ + public static void mergeSameNumbersInEmergencyNumberList( + @NonNull List emergencyNumberList, + boolean mergeServiceCategoriesAndUrns) { if (emergencyNumberList == null) { return; } + Set duplicatedEmergencyNumberPosition = new HashSet<>(); for (int i = 0; i < emergencyNumberList.size(); i++) { for (int j = 0; j < i; j++) { - if (areSameEmergencyNumbers( - emergencyNumberList.get(i), emergencyNumberList.get(j))) { - Rlog.e(LOG_TAG, "Found unexpected duplicate numbers: " - + emergencyNumberList.get(i) + " vs " + emergencyNumberList.get(j)); + if (areSameEmergencyNumbers(emergencyNumberList.get(i), + emergencyNumberList.get(j), mergeServiceCategoriesAndUrns)) { + Rlog.e(LOG_TAG, "Found unexpected duplicate numbers " + + emergencyNumberList.get(i) + + " vs " + emergencyNumberList.get(j)); // Set the merged emergency number in the current position - emergencyNumberList.set(i, mergeSameEmergencyNumbers( - emergencyNumberList.get(i), emergencyNumberList.get(j))); + emergencyNumberList.set(i, + mergeSameEmergencyNumbers(emergencyNumberList.get(i), + emergencyNumberList.get(j), mergeServiceCategoriesAndUrns)); // Mark the emergency number has been merged duplicatedEmergencyNumberPosition.add(j); } @@ -632,18 +670,24 @@ public final class EmergencyNumber implements Parcelable, Comparable mergeEmergencyUrns(@NonNull List firstEmergencyUrns, + @NonNull List secondEmergencyUrns) { + List mergedUrns = new ArrayList(); + mergedUrns.addAll(firstEmergencyUrns); + for (String urn : secondEmergencyUrns) { + if (!firstEmergencyUrns.contains(urn)) { + mergedUrns.add(urn); + } + } + return mergedUrns; + } + + /** + * Get the highest precedence source of the given Emergency number. Then get service catergory + * and urns list fill in the respective map with key as source. + * + * @param num EmergencyNumber to get the source, service category & urns + * @param serviceCategoryArray Array to store the category of the given EmergencyNumber + * with key as highest precedence source + * @param urnsArray Array to store the list of Urns of the given EmergencyNumber + * with key as highest precedence source + * + * @hide + */ + private static void fillServiceCategoryAndUrns(@NonNull EmergencyNumber num, + @NonNull SparseIntArray serviceCategoryArray, + @NonNull SparseArray> urnsArray) { + int numberSrc = num.getEmergencyNumberSourceBitmask(); + for (Integer source : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) { + if ((numberSrc & source) == source) { + if (!num.isInEmergencyServiceCategories(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED)) { + serviceCategoryArray.put(source, num.getEmergencyServiceCategoryBitmask()); + } + urnsArray.put(source, num.getEmergencyUrns()); + break; + } + } + } + + /** + * Get a merged EmergencyNumber from two same emergency numbers from + * Emergency number list. Two emergency numbers are the same if + * {@link #areSameEmergencyNumbers} returns {@code true}. + * + * @param first first EmergencyNumber to compare + * @param second second EmergencyNumber to compare + * @param mergeServiceCategoriesAndUrns {@code true} then determine service category and urns + * Service catetory : set from most precedence source number(N/W, SIM, DB, modem_cfg) + * Urns : merge from both with first priority from most precedence source number + * {@code false} then call {@link #mergeSameEmergencyNumbers} to merge. + * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber + * + * @hide + */ + public static @NonNull EmergencyNumber mergeSameEmergencyNumbers( + @NonNull EmergencyNumber first, @NonNull EmergencyNumber second, + boolean mergeServiceCategoriesAndUrns) { + if (!mergeServiceCategoriesAndUrns) { + return mergeSameEmergencyNumbers(first, second); + } + + int routing = first.getEmergencyCallRouting(); + int serviceCategory = first.getEmergencyServiceCategoryBitmask(); + List mergedEmergencyUrns = new ArrayList(); + //Maps to store the service category and urns of both the first and second emergency number + // with key as most precedent source + SparseIntArray serviceCategoryArray = new SparseIntArray(2); + SparseArray> urnsArray = new SparseArray(2); + + fillServiceCategoryAndUrns(first, serviceCategoryArray, urnsArray); + fillServiceCategoryAndUrns(second, serviceCategoryArray, urnsArray); + + if (second.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) { + routing = second.getEmergencyCallRouting(); + } + + // Determine serviceCategory of most precedence number + for (int sourceOfCategory : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) { + if (serviceCategoryArray.indexOfKey(sourceOfCategory) >= 0) { + serviceCategory = serviceCategoryArray.get(sourceOfCategory); + break; + } + } + + // Merge Urns in precedence number + for (int sourceOfUrn : EMERGENCY_NUMBER_SOURCE_PRECEDENCE) { + if (urnsArray.contains(sourceOfUrn)) { + mergedEmergencyUrns = mergeEmergencyUrns(mergedEmergencyUrns, + urnsArray.get(sourceOfUrn)); + } + } + + return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(), + serviceCategory, mergedEmergencyUrns, + first.getEmergencyNumberSourceBitmask() + | second.getEmergencyNumberSourceBitmask(), + routing); + } + /** * Validate Emergency Number address that only contains the dialable character * {@link PhoneNumberUtils#isDialable(char)}