blob: b957622d0b7522b7a1917b6e807622af460c575f [file] [log] [blame]
package org.robolectric.shadows;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.telephony.PhoneStateListener.LISTEN_CALL_STATE;
import static android.telephony.PhoneStateListener.LISTEN_CELL_INFO;
import static android.telephony.PhoneStateListener.LISTEN_CELL_LOCATION;
import static android.telephony.PhoneStateListener.LISTEN_NONE;
import static android.telephony.TelephonyManager.CALL_STATE_IDLE;
import static android.telephony.TelephonyManager.CALL_STATE_RINGING;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.PersistableBundle;
import android.os.SystemProperties;
import android.telecom.PhoneAccountHandle;
import android.telephony.CellInfo;
import android.telephony.CellLocation;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.CellInfoCallback;
import android.text.TextUtils;
import android.util.SparseArray;
import android.util.SparseIntArray;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.Executor;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
@Implements(value = TelephonyManager.class, looseSignatures = true)
public class ShadowTelephonyManager {
@RealObject
protected TelephonyManager realTelephonyManager;
private final Map<PhoneStateListener, Integer> phoneStateRegistrations = new HashMap<>();
private final Map<Integer, String> slotIndexToDeviceId = new HashMap<>();
private final Map<Integer, String> slotIndexToImei = new HashMap<>();
private final Map<Integer, String> slotIndexToMeid = new HashMap<>();
private final Map<PhoneAccountHandle, Boolean> voicemailVibrationEnabledMap = new HashMap<>();
private final Map<PhoneAccountHandle, Uri> voicemailRingtoneUriMap = new HashMap<>();
private final Map<PhoneAccountHandle, TelephonyManager> phoneAccountToTelephonyManagers =
new HashMap<>();
private PhoneStateListener lastListener;
private int lastEventFlags;
private String deviceId;
private String imei;
private String meid;
private String groupIdLevel1;
private String networkOperatorName = "";
private String networkCountryIso;
private String networkOperator = "";
private String simOperator;
private String simOperatorName;
private String simSerialNumber;
private boolean readPhoneStatePermission = true;
private int phoneType = TelephonyManager.PHONE_TYPE_GSM;
private String line1Number;
private int networkType;
private int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
private List<CellInfo> allCellInfo = Collections.emptyList();
private List<CellInfo> callbackCellInfos = null;
private CellLocation cellLocation = null;
private int callState = CALL_STATE_IDLE;
private int dataState = TelephonyManager.DATA_DISCONNECTED;
private String incomingPhoneNumber = null;
private boolean isSmsCapable = true;
private String voiceMailNumber;
private String voiceMailAlphaTag;
private int phoneCount = 1;
private Map<Integer, TelephonyManager> subscriptionIdsToTelephonyManagers = new HashMap<>();
private PersistableBundle carrierConfig;
private ServiceState serviceState;
private boolean isNetworkRoaming;
private final SparseIntArray simStates = new SparseIntArray();
private final SparseIntArray currentPhoneTypes = new SparseIntArray();
private final SparseArray<List<String>> carrierPackageNames = new SparseArray<>();
private final Map<Integer, String> simCountryIsoMap = new HashMap<>();
private int simCarrierId;
private String subscriberId;
private /*UiccSlotInfo[]*/ Object uiccSlotInfos;
private String visualVoicemailPackageName = null;
private SignalStrength signalStrength;
private boolean dataEnabled = false;
private boolean isRttSupported;
private final List<String> sentDialerSpecialCodes = new ArrayList<>();
private boolean hearingAidCompatibilitySupported = false;
private int requestCellInfoUpdateErrorCode = 0;
private Throwable requestCellInfoUpdateDetail = null;
{
resetSimStates();
resetSimCountryIsos();
}
@Implementation
protected void listen(PhoneStateListener listener, int flags) {
lastListener = listener;
lastEventFlags = flags;
if (flags == LISTEN_NONE) {
phoneStateRegistrations.remove(listener);
} else {
initListener(listener, flags);
phoneStateRegistrations.put(listener, flags);
}
}
/**
* Returns the most recent listener passed to #listen().
*
* @return Phone state listener.
* @deprecated Avoid using.
*/
@Deprecated
public PhoneStateListener getListener() {
return lastListener;
}
/**
* Returns the most recent flags passed to #listen().
*
* @return Event flags.
* @deprecated Avoid using.
*/
@Deprecated
public int getEventFlags() {
return lastEventFlags;
}
/** Call state may be specified via {@link #setCallState(int)}. */
@Implementation
protected int getCallState() {
return callState;
}
/** Sets the current call state to the desired state and updates any listeners. */
public void setCallState(int callState) {
setCallState(callState, null);
}
/**
* Sets the current call state with the option to specify an incoming phone number for the
* CALL_STATE_RINGING state. The incoming phone number will be ignored for all other cases.
*/
public void setCallState(int callState, String incomingPhoneNumber) {
if (callState != CALL_STATE_RINGING) {
incomingPhoneNumber = null;
}
this.callState = callState;
this.incomingPhoneNumber = incomingPhoneNumber;
for (PhoneStateListener listener : getListenersForFlags(LISTEN_CALL_STATE)) {
listener.onCallStateChanged(callState, incomingPhoneNumber);
}
}
/**
* Data state may be specified via {@link #setDataState(int)}. If no override is set, this
* defaults to {@link TelephonyManager#DATA_DISCONNECTED}.
*/
@Implementation
protected int getDataState() {
return dataState;
}
/** Sets the data state returned by {@link #getDataState()}. */
public void setDataState(int dataState) {
this.dataState = dataState;
}
@Implementation
protected String getDeviceId() {
checkReadPhoneStatePermission();
return deviceId;
}
public void setDeviceId(String newDeviceId) {
deviceId = newDeviceId;
}
public void setNetworkOperatorName(String networkOperatorName) {
this.networkOperatorName = networkOperatorName;
}
@Implementation(minSdk = LOLLIPOP)
protected String getImei() {
checkReadPhoneStatePermission();
return imei;
}
@Implementation(minSdk = O)
protected String getImei(int slotIndex) {
checkReadPhoneStatePermission();
return slotIndexToImei.get(slotIndex);
}
/** Set the IMEI returned by getImei(). */
public void setImei(String imei) {
this.imei = imei;
}
/** Set the IMEI returned by {@link #getImei(int)}. */
public void setImei(int slotIndex, String imei) {
slotIndexToImei.put(slotIndex, imei);
}
@Implementation(minSdk = O)
protected String getMeid() {
checkReadPhoneStatePermission();
return meid;
}
@Implementation(minSdk = O)
protected String getMeid(int slotIndex) {
checkReadPhoneStatePermission();
return slotIndexToMeid.get(slotIndex);
}
/** Set the MEID returned by getMeid(). */
public void setMeid(String meid) {
this.meid = meid;
}
/** Set the MEID returned by {@link #getMeid(int)}. */
public void setMeid(int slotIndex, String meid) {
slotIndexToMeid.put(slotIndex, meid);
}
@Implementation
protected String getNetworkOperatorName() {
return networkOperatorName;
}
public void setNetworkCountryIso(String networkCountryIso) {
this.networkCountryIso = networkCountryIso;
}
@Implementation
protected String getNetworkCountryIso() {
return networkCountryIso;
}
public void setNetworkOperator(String networkOperator) {
this.networkOperator = networkOperator;
}
@Implementation
protected String getNetworkOperator() {
return networkOperator;
}
@Implementation
protected String getSimOperator() {
return simOperator;
}
public void setSimOperator(String simOperator) {
this.simOperator = simOperator;
}
@Implementation
protected String getSimOperatorName() {
return simOperatorName;
}
public void setSimOperatorName(String simOperatorName) {
this.simOperatorName = simOperatorName;
}
@Implementation
protected String getSimSerialNumber() {
checkReadPhoneStatePermission();
return this.simSerialNumber;
}
/** sets the serial number that will be returned by {@link #getSimSerialNumber}. */
public void setSimSerialNumber(String simSerialNumber) {
this.simSerialNumber = simSerialNumber;
}
@Implementation
protected String getSimCountryIso() {
return simCountryIsoMap.get(/* subId= */ 0);
}
@Implementation(minSdk = N)
@HiddenApi
protected String getSimCountryIso(int subId) {
return simCountryIsoMap.get(subId);
}
public void setSimCountryIso(String simCountryIso) {
setSimCountryIso(/* subId= */ 0, simCountryIso);
}
/** Sets the {@code simCountryIso} for the given {@code subId}. */
public void setSimCountryIso(int subId, String simCountryIso) {
simCountryIsoMap.put(subId, simCountryIso);
}
/** Clears {@code subId} to simCountryIso mapping and resets to default state. */
public void resetSimCountryIsos() {
simCountryIsoMap.clear();
simCountryIsoMap.put(0, "");
}
@Implementation
protected int getSimState() {
return getSimState(/* slotIndex= */ 0);
}
/** Sets the sim state of slot 0. */
public void setSimState(int simState) {
setSimState(/* slotIndex= */ 0, simState);
}
/** Set the sim state for the given {@code slotIndex}. */
public void setSimState(int slotIndex, int state) {
simStates.put(slotIndex, state);
}
@Implementation(minSdk = O)
protected int getSimState(int slotIndex) {
return simStates.get(slotIndex, TelephonyManager.SIM_STATE_UNKNOWN);
}
/** Sets the UICC slots information returned by {@link #getUiccSlotsInfo()}. */
public void setUiccSlotsInfo(/*UiccSlotInfo[]*/ Object uiccSlotsInfos) {
this.uiccSlotInfos = uiccSlotsInfos;
}
/** Returns the UICC slots information set by {@link #setUiccSlotsInfo}. */
@Implementation(minSdk = P)
@HiddenApi
protected /*UiccSlotInfo[]*/ Object getUiccSlotsInfo() {
return uiccSlotInfos;
}
/** Clears {@code slotIndex} to state mapping and resets to default state. */
public void resetSimStates() {
simStates.clear();
simStates.put(0, TelephonyManager.SIM_STATE_READY);
}
public void setReadPhoneStatePermission(boolean readPhoneStatePermission) {
this.readPhoneStatePermission = readPhoneStatePermission;
}
private void checkReadPhoneStatePermission() {
if (!readPhoneStatePermission) {
throw new SecurityException();
}
}
@Implementation
protected int getPhoneType() {
return phoneType;
}
public void setPhoneType(int phoneType) {
this.phoneType = phoneType;
}
@Implementation
protected String getLine1Number() {
return line1Number;
}
public void setLine1Number(String line1Number) {
this.line1Number = line1Number;
}
@Implementation
protected int getNetworkType() {
return networkType;
}
public void setNetworkType(int networkType) {
this.networkType = networkType;
}
/**
* Returns whatever value was set by the last call to {@link #setVoiceNetworkType}, defaulting to
* {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if it was never called.
*/
@Implementation(minSdk = N)
protected int getVoiceNetworkType() {
return voiceNetworkType;
}
/**
* Sets the value to be returned by calls to {@link getVoiceNetworkType}. This <b>should</b>
* correspond to one of the {@code NETWORK_TYPE_*} constants defined on {@link TelephonyManager},
* but this is not enforced.
*/
public void setVoiceNetworkType(int voiceNetworkType) {
this.voiceNetworkType = voiceNetworkType;
}
@Implementation(minSdk = JELLY_BEAN_MR1)
protected List<CellInfo> getAllCellInfo() {
return allCellInfo;
}
public void setAllCellInfo(List<CellInfo> allCellInfo) {
this.allCellInfo = allCellInfo;
if (VERSION.SDK_INT >= JELLY_BEAN_MR1) {
for (PhoneStateListener listener : getListenersForFlags(LISTEN_CELL_INFO)) {
listener.onCellInfoChanged(allCellInfo);
}
}
}
/**
* Returns the value set by {@link #setCallbackCellInfos}, defaulting to calling the real
* {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if it was never called.
*/
@Implementation(minSdk = Q)
protected void requestCellInfoUpdate(Executor executor, CellInfoCallback callback) {
if (callbackCellInfos == null) {
Shadow.directlyOn(realTelephonyManager, TelephonyManager.class).requestCellInfoUpdate(
executor, callback);
} else if (requestCellInfoUpdateErrorCode != 0 || requestCellInfoUpdateDetail != null) {
// perform the "failure" callback operation via the specified executor
executor.execute(
() -> {
callback.onError(requestCellInfoUpdateErrorCode, requestCellInfoUpdateDetail);
});
} else {
// perform the "success" callback operation via the specified executor
executor.execute(
() -> {
callback.onCellInfo(callbackCellInfos);
});
}
}
/**
* Sets the value to be returned by calls to {@link requestCellInfoUpdate}. Note that it does not
* set the value to be returned by calls to {@link getAllCellInfo}; for that, see
* {@link setAllCellInfo}.
*/
public void setCallbackCellInfos(List<CellInfo> callbackCellInfos) {
this.callbackCellInfos = callbackCellInfos;
}
/**
* Sets the values to be returned by a presumed error condition in {@link requestCellInfoUpdate}.
* These values will persist until cleared: to clear, set (0, null) using this method.
*/
public void setRequestCellInfoUpdateErrorValues(int errorCode, Throwable detail) {
requestCellInfoUpdateErrorCode = errorCode;
requestCellInfoUpdateDetail = detail;
}
@Implementation
protected CellLocation getCellLocation() {
return this.cellLocation;
}
public void setCellLocation(CellLocation cellLocation) {
this.cellLocation = cellLocation;
for (PhoneStateListener listener : getListenersForFlags(LISTEN_CELL_LOCATION)) {
listener.onCellLocationChanged(cellLocation);
}
}
@Implementation(minSdk = JELLY_BEAN_MR2)
protected String getGroupIdLevel1() {
return this.groupIdLevel1;
}
public void setGroupIdLevel1(String groupIdLevel1) {
this.groupIdLevel1 = groupIdLevel1;
}
private void initListener(PhoneStateListener listener, int flags) {
if ((flags & LISTEN_CALL_STATE) != 0) {
listener.onCallStateChanged(callState, incomingPhoneNumber);
}
if ((flags & LISTEN_CELL_INFO) != 0) {
if (VERSION.SDK_INT >= JELLY_BEAN_MR1) {
listener.onCellInfoChanged(allCellInfo);
}
}
if ((flags & LISTEN_CELL_LOCATION) != 0) {
listener.onCellLocationChanged(cellLocation);
}
}
private Iterable<PhoneStateListener> getListenersForFlags(int flags) {
return Iterables.filter(
phoneStateRegistrations.keySet(),
new Predicate<PhoneStateListener>() {
@Override
public boolean apply(PhoneStateListener input) {
// only select PhoneStateListeners with matching flags
return (phoneStateRegistrations.get(input) & flags) != 0;
}
});
}
/** @return `true` by default, or the value specified via {@link #setIsSmsCapable(boolean)} */
@Implementation
protected boolean isSmsCapable() {
return isSmsCapable;
}
/** Sets the value returned by {@link TelephonyManager#isSmsCapable()}. */
public void setIsSmsCapable(boolean isSmsCapable) {
this.isSmsCapable = isSmsCapable;
}
/**
* Returns a new empty {@link PersistableBundle} by default, or the value specified via {@link
* #setCarrierConfig(PersistableBundle)}.
*/
@Implementation(minSdk = O)
protected PersistableBundle getCarrierConfig() {
return carrierConfig != null ? carrierConfig : new PersistableBundle();
}
/**
* Sets the value returned by {@link TelephonyManager#getCarrierConfig()}.
*
* @param carrierConfig
*/
public void setCarrierConfig(PersistableBundle carrierConfig) {
this.carrierConfig = carrierConfig;
}
/**
* Returns {@code null} by default, or the value specified via {@link
* #setVoiceMailNumber(String)}.
*/
@Implementation
protected String getVoiceMailNumber() {
return voiceMailNumber;
}
/** Sets the value returned by {@link TelephonyManager#getVoiceMailNumber()}. */
public void setVoiceMailNumber(String voiceMailNumber) {
this.voiceMailNumber = voiceMailNumber;
}
/**
* Returns {@code null} by default or the value specified via {@link
* #setVoiceMailAlphaTag(String)}.
*/
@Implementation
protected String getVoiceMailAlphaTag() {
return voiceMailAlphaTag;
}
/** Sets the value returned by {@link TelephonyManager#getVoiceMailAlphaTag()}. */
public void setVoiceMailAlphaTag(String voiceMailAlphaTag) {
this.voiceMailAlphaTag = voiceMailAlphaTag;
}
/** Returns 1 by default or the value specified via {@link #setPhoneCount(int)}. */
@Implementation(minSdk = M)
protected int getPhoneCount() {
return phoneCount;
}
/** Sets the value returned by {@link TelephonyManager#getPhoneCount()}. */
public void setPhoneCount(int phoneCount) {
this.phoneCount = phoneCount;
}
/**
* Returns {@code null} by default or the value specified via {@link #setDeviceId(int, String)}.
*/
@Implementation(minSdk = M)
protected String getDeviceId(int slot) {
return slotIndexToDeviceId.get(slot);
}
/** Sets the value returned by {@link TelephonyManager#getDeviceId(int)}. */
public void setDeviceId(int slot, String deviceId) {
slotIndexToDeviceId.put(slot, deviceId);
}
/**
* Returns {@code null} by default or the value specified via {@link
* #setVoicemailVibrationEnabled(PhoneAccountHandle, boolean)}.
*/
@Implementation(minSdk = N)
protected boolean isVoicemailVibrationEnabled(PhoneAccountHandle handle) {
Boolean result = voicemailVibrationEnabledMap.get(handle);
return result != null ? result : false;
}
/**
* Sets the value returned by {@link
* TelephonyManager#isVoicemailVibrationEnabled(PhoneAccountHandle)}.
*/
@Implementation(minSdk = O)
protected void setVoicemailVibrationEnabled(PhoneAccountHandle handle, boolean isEnabled) {
voicemailVibrationEnabledMap.put(handle, isEnabled);
}
/**
* Returns {@code null} by default or the value specified via {@link
* #setVoicemailRingtoneUri(PhoneAccountHandle, Uri)}.
*/
@Implementation(minSdk = N)
protected Uri getVoicemailRingtoneUri(PhoneAccountHandle handle) {
return voicemailRingtoneUriMap.get(handle);
}
/**
* Sets the value returned by {@link
* TelephonyManager#getVoicemailRingtoneUri(PhoneAccountHandle)}.
*/
@Implementation(minSdk = O)
protected void setVoicemailRingtoneUri(PhoneAccountHandle handle, Uri uri) {
voicemailRingtoneUriMap.put(handle, uri);
}
/**
* Returns {@code null} by default or the value specified via {@link
* #setTelephonyManagerForHandle(PhoneAccountHandle, TelephonyManager)}.
*/
@Implementation(minSdk = O)
protected TelephonyManager createForPhoneAccountHandle(PhoneAccountHandle handle) {
return phoneAccountToTelephonyManagers.get(handle);
}
/**
* Sets the value returned by {@link
* TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle)}.
*/
public void setTelephonyManagerForHandle(
PhoneAccountHandle handle, TelephonyManager telephonyManager) {
phoneAccountToTelephonyManagers.put(handle, telephonyManager);
}
/**
* Returns {@code null} by default or the value specified via {@link
* #setTelephonyManagerForSubscriptionId(int, TelephonyManager)}
*/
@Implementation(minSdk = N)
protected TelephonyManager createForSubscriptionId(int subId) {
return subscriptionIdsToTelephonyManagers.get(subId);
}
/** Sets the value returned by {@link TelephonyManager#createForSubscriptionId(int)}. */
public void setTelephonyManagerForSubscriptionId(
int subscriptionId, TelephonyManager telephonyManager) {
subscriptionIdsToTelephonyManagers.put(subscriptionId, telephonyManager);
}
/**
* Returns {@code null} by default or the value specified via {@link
* #setServiceState(ServiceState)}
*/
@Implementation(minSdk = O)
protected ServiceState getServiceState() {
return serviceState;
}
/** Sets the value returned by {@link TelephonyManager#getServiceState()}. */
public void setServiceState(ServiceState serviceState) {
this.serviceState = serviceState;
}
/**
* Returns {@code false} by default or the value specified via {@link
* #setIsNetworkRoaming(boolean)}
*/
@Implementation
protected boolean isNetworkRoaming() {
return isNetworkRoaming;
}
/** Sets the value returned by {@link TelephonyManager#isNetworkRoaming()}. */
public void setIsNetworkRoaming(boolean isNetworkRoaming) {
this.isNetworkRoaming = isNetworkRoaming;
}
@Implementation(minSdk = M)
@HiddenApi
protected int getCurrentPhoneType(int subId) {
return currentPhoneTypes.get(subId, TelephonyManager.PHONE_TYPE_NONE);
}
/** Sets the phone type for the given {@code subId}. */
public void setCurrentPhoneType(int subId, int phoneType) {
currentPhoneTypes.put(subId, phoneType);
}
/** Removes all {@code subId} to {@code phoneType} mappings. */
public void clearPhoneTypes() {
currentPhoneTypes.clear();
}
@Implementation(minSdk = M)
@HiddenApi
protected List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) {
return carrierPackageNames.get(phoneId);
}
@Implementation(minSdk = LOLLIPOP)
@HiddenApi
protected List<String> getCarrierPackageNamesForIntent(Intent intent) {
return carrierPackageNames.get(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
/** Sets the {@code packages} for the given {@code phoneId}. */
public void setCarrierPackageNamesForPhone(int phoneId, List<String> packages) {
carrierPackageNames.put(phoneId, packages);
}
@Implementation(minSdk = P)
protected int getSimCarrierId() {
return simCarrierId;
}
/** Sets the value to be returned by {@link #getSimCarrierId()}. */
public void setSimCarrierId(int simCarrierId) {
this.simCarrierId = simCarrierId;
}
@Implementation
protected String getSubscriberId() {
return subscriberId;
}
/** Sets the value to be returned by {@link #getSubscriberId()}. */
public void setSubscriberId(String subscriberId) {
this.subscriberId = subscriberId;
}
/** Returns the value set by {@link #setVisualVoicemailPackageName(String)}. */
@Implementation(minSdk = O)
protected String getVisualVoicemailPackageName() {
return visualVoicemailPackageName;
}
/** Sets the value to be returned by {@link #getVisualVoicemailPackageName()}. */
public void setVisualVoicemailPackageName(String visualVoicemailPackageName) {
this.visualVoicemailPackageName = visualVoicemailPackageName;
}
@Implementation(minSdk = P)
protected SignalStrength getSignalStrength() {
return signalStrength;
}
/** Sets the value to be returned by {@link #getSignalStrength()} */
public void setSignalStrength(SignalStrength signalStrength) {
this.signalStrength = signalStrength;
for (PhoneStateListener listener :
getListenersForFlags(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS)) {
listener.onSignalStrengthsChanged(signalStrength);
}
}
/**
* Cribbed from {@link android.telephony.PhoneNumberUtils#isEmergencyNumberInternal}.
*
* TODO(b/122324733) need better implementation
*/
@Implementation(minSdk = Build.VERSION_CODES.Q)
protected boolean isEmergencyNumber(String number) {
if (number == null) {
return false;
}
Context context = ReflectionHelpers.getField(realTelephonyManager, "mContext");
Locale locale = context == null ? null : context.getResources().getConfiguration().locale;
String defaultCountryIso = locale == null ? null : locale.getCountry();
int slotId = -1;
boolean useExactMatch = true;
// retrieve the list of emergency numbers
// check read-write ecclist property first
String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
String emergencyNumbers = SystemProperties.get(ecclist, "");
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;
}
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;
}
}
}
return false;
}
@Implementation(minSdk = Build.VERSION_CODES.Q)
protected boolean isPotentialEmergencyNumber(String number) {
return isEmergencyNumber(number);
}
/**
* Implementation for {@link TelephonyManager#isDataEnabled}.
* @return False by default, unless set with {@link TelephonyManager#setDataEnabled}.
*/
@Implementation(minSdk = Build.VERSION_CODES.O)
protected boolean isDataEnabled() {
return dataEnabled;
}
/**
* Implementation for {@link TelephonyManager#setDataEnabled}. Marked as public in order to allow
* it to be used as a test API.
*/
@Implementation(minSdk = Build.VERSION_CODES.O)
public void setDataEnabled(boolean enabled) {
dataEnabled = enabled;
}
/**
* Implementation for {@link TelephonyManager#isRttSupported}.
*
* @return False by default, unless set with {@link #setRttSupported(boolean)}.
*/
@Implementation(minSdk = Build.VERSION_CODES.Q)
protected boolean isRttSupported() {
return isRttSupported;
}
/** Sets the value to be returned by {@link #isRttSupported()} */
public void setRttSupported(boolean isRttSupported) {
this.isRttSupported = isRttSupported;
}
/**
* Implementation for {@link TelephonyManager#sendDialerSpecialCode(String)}.
*
* @param inputCode special code to be sent.
*/
@Implementation(minSdk = O)
public void sendDialerSpecialCode(String inputCode) {
sentDialerSpecialCodes.add(inputCode);
}
/**
* Returns immutable list of special codes sent using {@link
* TelephonyManager#sendDialerSpecialCode(String)}. Special codes contained in the list are in the
* order they were sent.
*/
public List<String> getSentDialerSpecialCodes() {
return ImmutableList.copyOf(sentDialerSpecialCodes);
}
/** Sets the value to be returned by {@link #isHearingAidCompatibilitySupported()}. */
public void setHearingAidCompatibilitySupported(boolean isSupported) {
hearingAidCompatibilitySupported = isSupported;
}
/**
* Implementation for {@link TelephonyManager#isHearingAidCompatibilitySupported()}.
*
* @return False by default, unless set with {@link
* #setHearingAidCompatibilitySupported(boolean)}.
*/
@Implementation(minSdk = M)
protected boolean isHearingAidCompatibilitySupported() {
return hearingAidCompatibilitySupported;
}
}