diff options
11 files changed, 409 insertions, 115 deletions
diff --git a/api/current.txt b/api/current.txt index 58ddaca94139..97a127d1dc7e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -43786,10 +43786,10 @@ package android.telephony { method public static java.lang.String getStrippedReversed(java.lang.String); method public static final boolean is12Key(char); method public static final boolean isDialable(char); - method public static boolean isEmergencyNumber(java.lang.String); + method public static deprecated boolean isEmergencyNumber(java.lang.String); method public static boolean isGlobalPhoneNumber(java.lang.String); method public static boolean isISODigit(char); - method public static boolean isLocalEmergencyNumber(android.content.Context, java.lang.String); + method public static deprecated boolean isLocalEmergencyNumber(android.content.Context, java.lang.String); method public static final boolean isNonSeparator(char); method public static final boolean isReallyDialable(char); method public static final boolean isStartsPostDial(char); @@ -44497,11 +44497,13 @@ package android.telephony.emergency { method public java.util.List<java.lang.Integer> getEmergencyNumberSources(); method public java.util.List<java.lang.Integer> getEmergencyServiceCategories(); method public int getEmergencyServiceCategoryBitmask(); + method public java.lang.String getMnc(); method public java.lang.String getNumber(); method public boolean isFromSources(int); method public boolean isInEmergencyServiceCategories(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR; + field public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 16; // 0x10 field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8 field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4 field public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 1; // 0x1 diff --git a/api/system-current.txt b/api/system-current.txt index 86e262529252..5bfa6359ee17 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5967,6 +5967,7 @@ package android.telephony { method public int getVoiceActivationState(); method public boolean handlePinMmi(java.lang.String); method public boolean handlePinMmiForSubscriber(int, java.lang.String); + method public boolean isCurrentPotentialEmergencyNumber(java.lang.String); method public boolean isDataConnectivityPossible(); method public deprecated boolean isIdle(); method public deprecated boolean isOffhook(); diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index a2cbfaa02bfb..b04ae1746d18 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -67,7 +67,9 @@ import com.android.server.am.BatteryStatsService; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; /** @@ -196,6 +198,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private ArrayList<List<PhysicalChannelConfig>> mPhysicalChannelConfigs; + private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList; + private int[] mSrvccState; private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -229,8 +233,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { | PhoneStateListener.LISTEN_CELL_INFO; static final int ENFORCE_PHONE_STATE_PERMISSION_MASK = - PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR | - PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR; + PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR + | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR + | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST; static final int PRECISE_PHONE_STATE_PERMISSION_MASK = PhoneStateListener.LISTEN_PRECISE_CALL_STATE | @@ -357,6 +362,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mCellInfo = new ArrayList<List<CellInfo>>(); mSrvccState = new int[numPhones]; mPhysicalChannelConfigs = new ArrayList<List<PhysicalChannelConfig>>(); + mEmergencyNumberList = new HashMap<>(); for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; @@ -752,6 +758,13 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) { + try { + r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); + } catch (RemoteException ex) { + remove(r.binder); + } + } if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) { try { r.callback.onPhoneCapabilityChanged(mPhoneCapability); @@ -1665,10 +1678,30 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void notifyEmergencyNumberList(List<EmergencyNumber> emergencyNumberList) { - // TODO checkPermission, modify Listener constent documentation - // TODO implement multisim emergency number list update in listener - // TODO implement PhoneStateListenerTest + public void notifyEmergencyNumberList() { + if (!checkNotifyPermission("notifyEmergencyNumberList()")) { + return; + } + + synchronized (mRecords) { + mEmergencyNumberList = TelephonyManager.getDefault().getCurrentEmergencyNumberList(); + + for (Record r : mRecords) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST)) { + try { + r.callback.onEmergencyNumberListChanged(mEmergencyNumberList); + if (VDBG) { + log("notifyEmergencyNumberList: emergencyNumberList= " + + mEmergencyNumberList); + } + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } } @@ -1710,6 +1743,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mPhoneCapability=" + mPhoneCapability); pw.println("mPreferredDataSubId=" + mPreferredDataSubId); pw.println("mRadioPowerState=" + mRadioPowerState); + pw.println("mEmergencyNumberList=" + mEmergencyNumberList); pw.decreaseIndent(); diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index f6e8d3422eca..c95837e1e1de 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -1738,7 +1738,10 @@ public class PhoneNumberUtils { * @param number the number to look up. * @return true if the number is in the list of emergency numbers * listed in the RIL / SIM, otherwise return false. + * + * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} instead. */ + @Deprecated public static boolean isEmergencyNumber(String number) { return isEmergencyNumber(getDefaultVoiceSubId(), number); } @@ -1751,8 +1754,13 @@ public class PhoneNumberUtils { * @param number the number to look up. * @return true if the number is in the list of emergency numbers * listed in the RIL / SIM, otherwise return false. + * + * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} + * instead. + * * @hide */ + @Deprecated @UnsupportedAppUsage public static boolean isEmergencyNumber(int subId, String number) { // Return true only if the specified number *exactly* matches @@ -1778,8 +1786,12 @@ public class PhoneNumberUtils { * listed in the RIL / SIM, *or* if the number starts with the * same digits as any of those emergency numbers. * + * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)} + * instead. + * * @hide */ + @Deprecated public static boolean isPotentialEmergencyNumber(String number) { return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number); } @@ -1802,9 +1814,14 @@ public class PhoneNumberUtils { * @return true if the number is in the list of emergency numbers * listed in the RIL / SIM, *or* if the number starts with the * same digits as any of those emergency numbers. + * + * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)} + * instead. + * * @hide */ @UnsupportedAppUsage + @Deprecated public static boolean isPotentialEmergencyNumber(int subId, String number) { // Check against the emergency numbers listed by the RIL / SIM, // and *don't* require an exact match. @@ -1867,8 +1884,12 @@ public class PhoneNumberUtils { * @return if the number is an emergency number for the specific country, then return true, * otherwise false * + * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} + * instead. + * * @hide */ + @Deprecated @UnsupportedAppUsage public static boolean isEmergencyNumber(String number, String defaultCountryIso) { return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso); @@ -1882,8 +1903,13 @@ public class PhoneNumberUtils { * @param defaultCountryIso the specific country which the number should be checked against * @return if the number is an emergency number for the specific country, then return true, * otherwise false + * + * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} + * instead. + * * @hide */ + @Deprecated public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) { return isEmergencyNumberInternal(subId, number, defaultCountryIso, @@ -1909,8 +1935,12 @@ public class PhoneNumberUtils { * country, *or* if the number starts with the same digits as * any of those emergency numbers. * + * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)} + * instead. + * * @hide */ + @Deprecated public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) { return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso); } @@ -1934,8 +1964,13 @@ public class PhoneNumberUtils { * @return true if the number is an emergency number for the specific * country, *or* if the number starts with the same digits as * any of those emergency numbers. + * + * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)} + * instead. + * * @hide */ + @Deprecated public static boolean isPotentialEmergencyNumber(int subId, String number, String defaultCountryIso) { return isEmergencyNumberInternal(subId, number, @@ -1983,92 +2018,7 @@ public class PhoneNumberUtils { private static boolean isEmergencyNumberInternal(int subId, String number, String defaultCountryIso, boolean useExactMatch) { - // If the number passed in is null, just return false: - if (number == null) return false; - - // If the number passed in is a SIP address, return false, since the - // concept of "emergency numbers" is only meaningful for calls placed - // over the cell network. - // (Be sure to do this check *before* calling extractNetworkPortionAlt(), - // since the whole point of extractNetworkPortionAlt() is to filter out - // any non-dialable characters (which would turn 'abc911def@example.com' - // into '911', for example.)) - if (isUriNumber(number)) { - return false; - } - - // Strip the separators from the number before comparing it - // to the list. - number = extractNetworkPortionAlt(number); - - String emergencyNumbers = ""; - int slotId = SubscriptionManager.getSlotIndex(subId); - - // retrieve the list of emergency numbers - // check read-write ecclist property first - String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId); - - emergencyNumbers = SystemProperties.get(ecclist, ""); - - Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:" - + defaultCountryIso + " emergencyNumbers: " + emergencyNumbers); - - if (TextUtils.isEmpty(emergencyNumbers)) { - // then read-only ecclist property since old RIL only uses this - emergencyNumbers = SystemProperties.get("ro.ril.ecclist"); - } - - if (!TextUtils.isEmpty(emergencyNumbers)) { - // searches through the comma-separated list for a match, - // return true if one is found. - for (String emergencyNum : emergencyNumbers.split(",")) { - // It is not possible to append additional digits to an emergency number to dial - // the number in Brazil - it won't connect. - if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) { - if (number.equals(emergencyNum)) { - return true; - } - } else { - if (number.startsWith(emergencyNum)) { - return true; - } - } - } - // no matches found against the list! - return false; - } - - Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers." - + " Use embedded logic for determining ones."); - - // If slot id is invalid, means that there is no sim card. - // According spec 3GPP TS22.101, the following numbers should be - // ECC numbers when SIM/USIM is not present. - emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911"); - - for (String emergencyNum : emergencyNumbers.split(",")) { - if (useExactMatch) { - if (number.equals(emergencyNum)) { - return true; - } - } else { - if (number.startsWith(emergencyNum)) { - return true; - } - } - } - - // No ecclist system property, so use our own list. - if (defaultCountryIso != null) { - ShortNumberInfo info = ShortNumberInfo.getInstance(); - if (useExactMatch) { - return info.isEmergencyNumber(number, defaultCountryIso); - } else { - return info.connectsToEmergencyNumber(number, defaultCountryIso); - } - } - - return false; + return TelephonyManager.getDefault().isCurrentEmergencyNumber(number); } /** @@ -2078,7 +2028,11 @@ public class PhoneNumberUtils { * @param context the specific context which the number should be checked against * @return true if the specified number is an emergency number for the country the user * is currently in. + * + * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} + * instead. */ + @Deprecated public static boolean isLocalEmergencyNumber(Context context, String number) { return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number); } @@ -2091,8 +2045,13 @@ public class PhoneNumberUtils { * @param context the specific context which the number should be checked against * @return true if the specified number is an emergency number for the country the user * is currently in. + * + * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} + * instead. + * * @hide */ + @Deprecated @UnsupportedAppUsage public static boolean isLocalEmergencyNumber(Context context, int subId, String number) { return isLocalEmergencyNumberInternal(subId, number, @@ -2120,8 +2079,13 @@ public class PhoneNumberUtils { * CountryDetector. * * @see android.location.CountryDetector + * + * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)} + * instead. + * * @hide */ + @Deprecated @UnsupportedAppUsage public static boolean isPotentialLocalEmergencyNumber(Context context, String number) { return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number); @@ -2147,9 +2111,13 @@ public class PhoneNumberUtils { * @return true if the specified number is an emergency number for a local country, based on the * CountryDetector. * + * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)} + * instead. + * * @hide */ @UnsupportedAppUsage + @Deprecated public static boolean isPotentialLocalEmergencyNumber(Context context, int subId, String number) { return isLocalEmergencyNumberInternal(subId, number, @@ -2217,6 +2185,101 @@ public class PhoneNumberUtils { } /** + * Back-up old logics for {@link #isEmergencyNumberInternal} for legacy and deprecate purpose. + * + * @hide + */ + public static boolean isEmergencyNumberInternal(String number, boolean useExactMatch, + String defaultCountryIso) { + // If the number passed in is null, just return false: + if (number == null) return false; + + // If the number passed in is a SIP address, return false, since the + // concept of "emergency numbers" is only meaningful for calls placed + // over the cell network. + // (Be sure to do this check *before* calling extractNetworkPortionAlt(), + // since the whole point of extractNetworkPortionAlt() is to filter out + // any non-dialable characters (which would turn 'abc911def@example.com' + // into '911', for example.)) + if (PhoneNumberUtils.isUriNumber(number)) { + return false; + } + + // Strip the separators from the number before comparing it + // to the list. + number = PhoneNumberUtils.extractNetworkPortionAlt(number); + + String emergencyNumbers = ""; + int slotId = SubscriptionManager.getSlotIndex(getDefaultVoiceSubId()); + + // retrieve the list of emergency numbers + // check read-write ecclist property first + String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId); + + emergencyNumbers = SystemProperties.get(ecclist, ""); + + Rlog.d(LOG_TAG, "slotId:" + slotId + " country:" + + defaultCountryIso + " emergencyNumbers: " + emergencyNumbers); + + if (TextUtils.isEmpty(emergencyNumbers)) { + // then read-only ecclist property since old RIL only uses this + emergencyNumbers = SystemProperties.get("ro.ril.ecclist"); + } + + if (!TextUtils.isEmpty(emergencyNumbers)) { + // searches through the comma-separated list for a match, + // return true if one is found. + for (String emergencyNum : emergencyNumbers.split(",")) { + // It is not possible to append additional digits to an emergency number to dial + // the number in Brazil - it won't connect. + if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) { + if (number.equals(emergencyNum)) { + return true; + } + } else { + if (number.startsWith(emergencyNum)) { + return true; + } + } + } + // no matches found against the list! + return false; + } + + Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers." + + " Use embedded logic for determining ones."); + + // If slot id is invalid, means that there is no sim card. + // According spec 3GPP TS22.101, the following numbers should be + // ECC numbers when SIM/USIM is not present. + emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911"); + + for (String emergencyNum : emergencyNumbers.split(",")) { + if (useExactMatch) { + if (number.equals(emergencyNum)) { + return true; + } + } else { + if (number.startsWith(emergencyNum)) { + return true; + } + } + } + + // No ecclist system property, so use our own list. + if (defaultCountryIso != null) { + ShortNumberInfo info = ShortNumberInfo.getInstance(); + if (useExactMatch) { + return info.isEmergencyNumber(number, defaultCountryIso); + } else { + return info.connectsToEmergencyNumber(number, defaultCountryIso); + } + } + + return false; + } + + /** * isVoiceMailNumber: checks a given number against the voicemail * number provided by the RIL and SIM card. The caller must have * the READ_PHONE_STATE credential. diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index e8a28cac3140..0df0dafbe1dd 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -27,12 +27,14 @@ import android.os.Bundle; import android.os.Handler; import android.os.HandlerExecutor; import android.os.Looper; +import android.telephony.emergency.EmergencyNumber; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.IPhoneStateListener; import java.lang.ref.WeakReference; import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; /** @@ -313,6 +315,8 @@ public class PhoneStateListener { * * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). + * + * @see #onEmergencyNumberListChanged */ public static final int LISTEN_EMERGENCY_NUMBER_LIST = 0x01000000; @@ -603,6 +607,21 @@ public class PhoneStateListener { } /** + * Callback invoked when the current emergency number list has changed + * + * @param emergencyNumberList Map including the key as the active subscription ID + * (Note: if there is no active subscription, the key is + * {@link SubscriptionManager#getDefaultSubscriptionId}) + * and the value as the list of {@link EmergencyNumber}; + * null if this information is not available. + * @hide + */ + public void onEmergencyNumberListChanged( + @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) { + // default implementation empty + } + + /** * Callback invoked when OEM hook raw event is received. Requires * the READ_PRIVILEGED_PHONE_STATE permission. * @param rawData is the byte array of the OEM hook raw data. @@ -859,6 +878,16 @@ public class PhoneStateListener { () -> psl.onPhysicalChannelConfigurationChanged(configs))); } + @Override + public void onEmergencyNumberListChanged(Map emergencyNumberList) { + PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); + if (psl == null) return; + + Binder.withCleanCallingIdentity( + () -> mExecutor.execute( + () -> psl.onEmergencyNumberListChanged(emergencyNumberList))); + } + public void onPhoneCapabilityChanged(PhoneCapability capability) { PhoneStateListener psl = mPhoneStateListenerWeakRef.get(); if (psl == null) return; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index f36f36001376..422e66de7e0b 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9454,8 +9454,13 @@ public class TelephonyManager { /** * Get the emergency number list based on current locale, sim, default, modem and network. * - * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at - * the smaller index in the returned list. + * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher + * priority sources will be located at the smaller index; the priority order of sources are: + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG} * * <p>The subscriptions which the returned list would be based on, are all the active * subscriptions, no matter which subscription could be used to create TelephonyManager. @@ -9464,8 +9469,9 @@ public class TelephonyManager { * app has carrier privileges (see {@link #hasCarrierPrivileges}). * * @return Map including the key as the active subscription ID (Note: if there is no active - * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value - * as the list of {@link EmergencyNumber}; null if this information is not available. + * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value + * as the list of {@link EmergencyNumber}; null if this information is not available; or throw + * a SecurityException if the caller does not have the permission. */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable @@ -9486,8 +9492,13 @@ public class TelephonyManager { * Get the per-category emergency number list based on current locale, sim, default, modem * and network. * - * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at - * the smaller index in the returned list. + * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher + * priority sources will be located at the smaller index; the priority order of sources are: + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} > + * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG} * * <p>The subscriptions which the returned list would be based on, are all the active * subscriptions, no matter which subscription could be used to create TelephonyManager. @@ -9508,8 +9519,9 @@ public class TelephonyManager { * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li> * </ol> * @return Map including the key as the active subscription ID (Note: if there is no active - * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value - * as the list of {@link EmergencyNumber}; null if this information is not available. + * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value + * as the list of {@link EmergencyNumber}; null if this information is not available; or throw + * a SecurityException if the caller does not have the permission. */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable @@ -9556,7 +9568,44 @@ public class TelephonyManager { if (telephony == null) { return false; } - return telephony.isCurrentEmergencyNumber(number); + return telephony.isCurrentEmergencyNumber(number, true); + } catch (RemoteException ex) { + Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex); + } + return false; + } + + /** + * Checks if the supplied number is an emergency number based on current locale, sim, default, + * modem and network. + * + * <p> Specifically, this method will return {@code true} if the specified number is an + * emergency number, *or* if the number simply starts with the same digits as any current + * emergency number. + * + * <p>The subscriptions which the identification would be based on, are all the active + * subscriptions, no matter which subscription could be used to create TelephonyManager. + * + * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or + * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @param number - the number to look up + * @return {@code true} if the given number is an emergency number or it simply starts with + * the same digits of any current emergency number based on current locale, sim, modem and + * network; {@code false} if it is not; or throw an SecurityException if the caller does not + * have the required permission/privileges + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isCurrentPotentialEmergencyNumber(@NonNull String number) { + try { + ITelephony telephony = getITelephony(); + if (telephony == null) { + return false; + } + return telephony.isCurrentEmergencyNumber(number, false); } catch (RemoteException ex) { Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex); } diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index 41f7bd7ade63..fe062d5d974a 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -22,6 +22,7 @@ import android.hardware.radio.V1_4.EmergencyNumberSource; import android.hardware.radio.V1_4.EmergencyServiceCategory; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.Rlog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -150,6 +151,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu @IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = { EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING, EMERGENCY_NUMBER_SOURCE_SIM, + EMERGENCY_NUMBER_SOURCE_DATABASE, EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG, EMERGENCY_NUMBER_SOURCE_DEFAULT }) @@ -169,6 +171,10 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu * Reference: 3gpp 22.101, Section 10 - Emergency Calls */ public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM; + /** + * Bit-field which indicates the number is from the platform-maintained database. + */ + public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 1 << 4; /** Bit-field which indicates the number is from the modem config. */ public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = EmergencyNumberSource.MODEM_CONFIG; @@ -187,21 +193,24 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>(); EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING); EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM); + EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DATABASE); EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG); EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT); } private final String mNumber; private final String mCountryIso; + private final String mMnc; private final int mEmergencyServiceCategoryBitmask; private final int mEmergencyNumberSourceBitmask; /** @hide */ public EmergencyNumber(@NonNull String number, @NonNull String countryIso, - int emergencyServiceCategories, + @NonNull String mnc, int emergencyServiceCategories, int emergencyNumberSources) { this.mNumber = number; this.mCountryIso = countryIso; + this.mMnc = mnc; this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories; this.mEmergencyNumberSourceBitmask = emergencyNumberSources; } @@ -210,6 +219,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu public EmergencyNumber(Parcel source) { mNumber = source.readString(); mCountryIso = source.readString(); + mMnc = source.readString(); mEmergencyServiceCategoryBitmask = source.readInt(); mEmergencyNumberSourceBitmask = source.readInt(); } @@ -236,6 +246,15 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu } /** + * Get the Mobile Network Code of the emergency number. + * + * @return the Mobile Network Code of the emergency number. + */ + public String getMnc() { + return mMnc; + } + + /** * Returns the bitmask of emergency service categories of the emergency number. * * @return bitmask of the emergency service categories @@ -338,6 +357,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu public void writeToParcel(Parcel dest, int flags) { dest.writeString(mNumber); dest.writeString(mCountryIso); + dest.writeString(mMnc); dest.writeInt(mEmergencyServiceCategoryBitmask); dest.writeInt(mEmergencyNumberSourceBitmask); } @@ -350,10 +370,10 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu @Override public String toString() { - return "EmergencyNumber = " + "[Number]" + mNumber + " / [CountryIso]" + mCountryIso - + " / [ServiceCategories]" - + Integer.toBinaryString(mEmergencyServiceCategoryBitmask) - + " / [Sources]" + Integer.toBinaryString(mEmergencyNumberSourceBitmask); + return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso + + "|Mnc-" + mMnc + + "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask) + + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask); } @Override @@ -373,6 +393,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu * The priority of sources are defined as follows: * EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING > * EMERGENCY_NUMBER_SOURCE_SIM > + * EMERGENCY_NUMBER_SOURCE_DATABASE > * EMERGENCY_NUMBER_SOURCE_DEFAULT > * EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG * @@ -385,7 +406,9 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) { score += 1 << 3; } - // TODO add a score if the number comes from Google's emergency number database + if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) { + score += 1 << 2; + } if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) { score += 1 << 1; } @@ -412,14 +435,104 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu < emergencyNumber.getDisplayPriorityScore()) { return 1; } else { - /** - * TODO if both numbers have the same display priority score, the number matches the - * Google's emergency number database has a higher display priority. - */ return 0; } } + /** + * In-place merge same emergency numbers in the emergency number list. + * + * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and + * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield + * for the same EmergencyNumber. + * + * @param emergencyNumberList the emergency number list to process + * + * @hide + */ + public static void mergeSameNumbersInEmergencyNumberList( + List<EmergencyNumber> emergencyNumberList) { + if (emergencyNumberList == null) { + return; + } + Set<EmergencyNumber> mergedEmergencyNumber = new HashSet<>(); + for (int i = 0; i < emergencyNumberList.size(); i++) { + // Skip the check because it was merged. + if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) { + continue; + } + for (int j = i + 1; j < emergencyNumberList.size(); j++) { + if (isSameEmergencyNumber( + emergencyNumberList.get(i), emergencyNumberList.get(j))) { + 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, mergeNumbers( + emergencyNumberList.get(i), emergencyNumberList.get(j))); + // Mark the emergency number has been merged + mergedEmergencyNumber.add(emergencyNumberList.get(j)); + } + } + } + // Remove the marked emergency number in the orignal list + for (int i = 0; i < emergencyNumberList.size(); i++) { + if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) { + emergencyNumberList.remove(i--); + } + } + } + + /** + * Check if two emergency numbers are the same. + * + * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and + * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield + * for the same EmergencyNumber. + * + * @param first first EmergencyNumber to compare + * @param second second EmergencyNumber to compare + * @return true if they are the same EmergencyNumbers; false otherwise. + * + * @hide + */ + public static boolean isSameEmergencyNumber(@NonNull EmergencyNumber first, + @NonNull EmergencyNumber second) { + if (!first.getNumber().equals(second.getNumber())) { + return false; + } + if (!first.getCountryIso().equals(second.getCountryIso())) { + return false; + } + if (!first.getMnc().equals(second.getMnc())) { + return false; + } + if (first.getEmergencyServiceCategoryBitmask() + != second.getEmergencyServiceCategoryBitmask()) { + return false; + } + return true; + } + + /** + * Get a merged EmergencyNumber for two numbers if they are the same. + * + * @param first first EmergencyNumber to compare + * @param second second EmergencyNumber to compare + * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber + * + * @hide + */ + public static EmergencyNumber mergeNumbers(@NonNull EmergencyNumber first, + @NonNull EmergencyNumber second) { + if (isSameEmergencyNumber(first, second)) { + return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(), + first.getEmergencyServiceCategoryBitmask(), + first.getEmergencyNumberSourceBitmask() + | second.getEmergencyNumberSourceBitmask()); + } + return null; + } + public static final Parcelable.Creator<EmergencyNumber> CREATOR = new Parcelable.Creator<EmergencyNumber>() { @Override diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl index 79f0635c67f7..78fc0bc487bf 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -25,6 +25,7 @@ import android.telephony.PhoneCapability; import android.telephony.PhysicalChannelConfig; import android.telephony.PreciseCallState; import android.telephony.PreciseDataConnectionState; +import android.telephony.emergency.EmergencyNumber; oneway interface IPhoneStateListener { void onServiceStateChanged(in ServiceState serviceState); @@ -53,5 +54,6 @@ oneway interface IPhoneStateListener { void onPhoneCapabilityChanged(in PhoneCapability capability); void onPreferredDataSubIdChanged(in int subId); void onRadioPowerStateChanged(in int state); + void onEmergencyNumberListChanged(in Map emergencyNumberList); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 399dc5255176..88b9302afacb 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1716,7 +1716,7 @@ interface ITelephony { /** * Identify if the number is emergency number, based on all the active subscriptions. */ - boolean isCurrentEmergencyNumber(String number); + boolean isCurrentEmergencyNumber(String number, boolean exactMatch); /** * Return a list of certs in hex string from loaded carrier privileges access rules. diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 76e7509c1094..d9f5c3f6d0fa 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -81,5 +81,5 @@ interface ITelephonyRegistry { void notifyPhoneCapabilityChanged(in PhoneCapability capability); void notifyPreferredDataSubIdChanged(int preferredSubId); void notifyRadioPowerStateChanged(in int state); - void notifyEmergencyNumberList(in List<EmergencyNumber> emergencyNumberList); + void notifyEmergencyNumberList(); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index cb8269efe443..c9343171e03e 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -483,4 +483,5 @@ public interface RILConstants { int RIL_UNSOL_HAL_NON_RIL_BASE = 1100; int RIL_UNSOL_ICC_SLOT_STATUS = 1100; int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101; + int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102; } |