aboutsummaryrefslogtreecommitdiff
path: root/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java
diff options
context:
space:
mode:
Diffstat (limited to 'shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java')
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java212
1 files changed, 139 insertions, 73 deletions
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java
index 7b2a756b2..871e9f3a8 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java
@@ -1,8 +1,6 @@
package org.robolectric.shadows;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-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.LOLLIPOP_MR1;
import static android.os.Build.VERSION_CODES.M;
@@ -54,19 +52,17 @@ import android.telephony.TelephonyManager.CellInfoCallback;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.emergency.EmergencyNumber;
import android.text.TextUtils;
-import android.util.SparseArray;
-import android.util.SparseBooleanArray;
-import android.util.SparseIntArray;
import com.google.common.base.Ascii;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -88,17 +84,24 @@ public class ShadowTelephonyManager {
@RealObject protected TelephonyManager realTelephonyManager;
- private final Map<PhoneStateListener, Integer> phoneStateRegistrations = new HashMap<>();
+ private final Map<PhoneStateListener, Integer> phoneStateRegistrations =
+ Collections.synchronizedMap(new LinkedHashMap<>());
private final /*List<TelephonyCallback>*/ List<Object> telephonyCallbackRegistrations =
new ArrayList<>();
- 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 final Map<PhoneAccountHandle, Integer> phoneAccountHandleSubscriptionId = new HashMap<>();
+ private static final Map<Integer, String> slotIndexToDeviceId =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final Map<Integer, String> slotIndexToImei =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final Map<Integer, String> slotIndexToMeid =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final Map<PhoneAccountHandle, Boolean> voicemailVibrationEnabledMap =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final Map<PhoneAccountHandle, Uri> voicemailRingtoneUriMap =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final Map<PhoneAccountHandle, TelephonyManager> phoneAccountToTelephonyManagers =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final Map<PhoneAccountHandle, Integer> phoneAccountHandleSubscriptionId =
+ Collections.synchronizedMap(new LinkedHashMap<>());
private PhoneStateListener lastListener;
private /*TelephonyCallback*/ Object lastTelephonyCallback;
@@ -117,47 +120,53 @@ public class ShadowTelephonyManager {
private String simOperator = "";
private String simOperatorName;
private String simSerialNumber;
- private boolean readPhoneStatePermission = true;
+ private static volatile boolean readPhoneStatePermission = true;
private int phoneType = TelephonyManager.PHONE_TYPE_GSM;
private String line1Number;
private int networkType;
private int dataNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
private int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
- private List<CellInfo> allCellInfo = Collections.emptyList();
- private List<CellInfo> callbackCellInfos = null;
- private CellLocation cellLocation = null;
+ private static volatile List<CellInfo> allCellInfo = Collections.emptyList();
+ private static volatile List<CellInfo> callbackCellInfos = null;
+ private static volatile CellLocation cellLocation = null;
private int callState = CALL_STATE_IDLE;
private int dataState = TelephonyManager.DATA_DISCONNECTED;
private int dataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
private String incomingPhoneNumber = null;
- private boolean isSmsCapable = true;
- private boolean voiceCapable = true;
+ private static volatile boolean isSmsCapable = true;
+ private static volatile boolean voiceCapable = true;
private String voiceMailNumber;
private String voiceMailAlphaTag;
- private int phoneCount = 1;
- private int activeModemCount = 1;
- private Map<Integer, TelephonyManager> subscriptionIdsToTelephonyManagers = new HashMap<>();
+ private static volatile int phoneCount = 1;
+ private static volatile int activeModemCount = 1;
+ private static volatile Map<Integer, TelephonyManager> subscriptionIdsToTelephonyManagers =
+ Collections.synchronizedMap(new LinkedHashMap<>());
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 static final Map<Integer, Integer> simStates =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final Map<Integer, Integer> currentPhoneTypes =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final Map<Integer, List<String>> carrierPackageNames =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final Map<Integer, String> simCountryIsoMap =
+ Collections.synchronizedMap(new LinkedHashMap<>());
private int simCarrierId;
private int carrierIdFromSimMccMnc;
private String subscriberId;
- private /*UiccSlotInfo[]*/ Object uiccSlotInfos;
- private /*UiccCardInfo[]*/ Object uiccCardsInfo = new ArrayList<>();
+ private static volatile /*UiccSlotInfo[]*/ Object uiccSlotInfos;
+ private static volatile /*List<UiccCardInfo>*/ Object uiccCardsInfo = new ArrayList<>();
private String visualVoicemailPackageName = null;
private SignalStrength signalStrength;
private boolean dataEnabled = false;
private final Set<Integer> dataDisabledReasons = new HashSet<>();
private boolean isRttSupported;
- private boolean isTtyModeSupported;
- private final SparseBooleanArray subIdToHasCarrierPrivileges = new SparseBooleanArray();
- private final List<String> sentDialerSpecialCodes = new ArrayList<>();
- private boolean hearingAidCompatibilitySupported = false;
+ private static volatile boolean isTtyModeSupported;
+ private static final Map<Integer, Boolean> subIdToHasCarrierPrivileges =
+ Collections.synchronizedMap(new LinkedHashMap<>());
+ private static final List<String> sentDialerSpecialCodes = new ArrayList<>();
+ private static volatile boolean hearingAidCompatibilitySupported = false;
private int requestCellInfoUpdateErrorCode = 0;
private Throwable requestCellInfoUpdateDetail = null;
private Object telephonyDisplayInfo;
@@ -165,7 +174,7 @@ public class ShadowTelephonyManager {
private static int callComposerStatus = 0;
private VisualVoicemailSmsParams lastVisualVoicemailSmsParams;
private VisualVoicemailSmsFilterSettings visualVoicemailSmsFilterSettings;
- private boolean emergencyCallbackMode;
+ private static volatile boolean emergencyCallbackMode;
private static Map<Integer, List<EmergencyNumber>> emergencyNumbersList;
/**
@@ -176,17 +185,45 @@ public class ShadowTelephonyManager {
*/
private Object callback;
- private /*PhoneCapability*/ Object phoneCapability;
+ private static volatile /*PhoneCapability*/ Object phoneCapability;
- {
- resetSimStates();
- resetSimCountryIsos();
+ static {
+ resetAllSimStates();
+ resetAllSimCountryIsos();
}
@Resetter
public static void reset() {
+ subscriptionIdsToTelephonyManagers.clear();
+ resetAllSimStates();
+ currentPhoneTypes.clear();
+ carrierPackageNames.clear();
+ resetAllSimCountryIsos();
+ slotIndexToDeviceId.clear();
+ slotIndexToImei.clear();
+ slotIndexToMeid.clear();
+ voicemailVibrationEnabledMap.clear();
+ voicemailRingtoneUriMap.clear();
+ phoneAccountToTelephonyManagers.clear();
+ phoneAccountHandleSubscriptionId.clear();
+ subIdToHasCarrierPrivileges.clear();
+ allCellInfo = Collections.emptyList();
+ cellLocation = null;
+ callbackCellInfos = null;
+ uiccSlotInfos = null;
+ uiccCardsInfo = new ArrayList<>();
callComposerStatus = 0;
+ emergencyCallbackMode = false;
emergencyNumbersList = null;
+ phoneCapability = null;
+ isTtyModeSupported = false;
+ readPhoneStatePermission = true;
+ isSmsCapable = true;
+ voiceCapable = true;
+ phoneCount = 1;
+ activeModemCount = 1;
+ sentDialerSpecialCodes.clear();
+ hearingAidCompatibilitySupported = false;
}
@Implementation(minSdk = S)
@@ -217,7 +254,7 @@ public class ShadowTelephonyManager {
}
public void setPhoneCapability(/*PhoneCapability*/ Object phoneCapability) {
- this.phoneCapability = phoneCapability;
+ ShadowTelephonyManager.phoneCapability = phoneCapability;
}
@Implementation(minSdk = S)
@@ -548,11 +585,21 @@ public class ShadowTelephonyManager {
}
/** Clears {@code subId} to simCountryIso mapping and resets to default state. */
- public void resetSimCountryIsos() {
+ public static void resetAllSimCountryIsos() {
simCountryIsoMap.clear();
simCountryIsoMap.put(0, "");
}
+ /**
+ * Clears {@code subId} to simCountryIso mapping and resets to default state.
+ *
+ * @deprecated for resetAllSimCountryIsos
+ */
+ @Deprecated
+ public void resetSimCountryIsos() {
+ resetAllSimCountryIsos();
+ }
+
@Implementation
protected int getSimState() {
return getSimState(/* slotIndex= */ 0);
@@ -570,12 +617,12 @@ public class ShadowTelephonyManager {
@Implementation(minSdk = O)
protected int getSimState(int slotIndex) {
- return simStates.get(slotIndex, TelephonyManager.SIM_STATE_UNKNOWN);
+ return simStates.getOrDefault(slotIndex, TelephonyManager.SIM_STATE_UNKNOWN);
}
/** Sets the UICC slots information returned by {@link #getUiccSlotsInfo()}. */
public void setUiccSlotsInfo(/*UiccSlotInfo[]*/ Object uiccSlotsInfos) {
- this.uiccSlotInfos = uiccSlotsInfos;
+ ShadowTelephonyManager.uiccSlotInfos = uiccSlotsInfos;
}
/** Returns the UICC slots information set by {@link #setUiccSlotsInfo}. */
@@ -586,25 +633,35 @@ public class ShadowTelephonyManager {
}
/** Sets the UICC cards information returned by {@link #getUiccCardsInfo()}. */
- public void setUiccCardsInfo(/*UiccCardsInfo[]*/ Object uiccCardsInfo) {
- this.uiccCardsInfo = uiccCardsInfo;
+ public void setUiccCardsInfo(/*List<UiccCardInfo>*/ Object uiccCardsInfo) {
+ ShadowTelephonyManager.uiccCardsInfo = uiccCardsInfo;
}
/** Returns the UICC cards information set by {@link #setUiccCardsInfo}. */
@Implementation(minSdk = Q)
@HiddenApi
- protected /*UiccSlotInfo[]*/ Object getUiccCardsInfo() {
+ protected /*List<UiccCardInfo>*/ Object getUiccCardsInfo() {
return uiccCardsInfo;
}
/** Clears {@code slotIndex} to state mapping and resets to default state. */
- public void resetSimStates() {
+ public static void resetAllSimStates() {
simStates.clear();
simStates.put(0, TelephonyManager.SIM_STATE_READY);
}
+ /**
+ * Clears {@code slotIndex} to state mapping and resets to default state.
+ *
+ * @deprecated use resetAllSimStates()
+ */
+ @Deprecated
+ public void resetSimStates() {
+ resetAllSimStates();
+ }
+
public void setReadPhoneStatePermission(boolean readPhoneStatePermission) {
- this.readPhoneStatePermission = readPhoneStatePermission;
+ ShadowTelephonyManager.readPhoneStatePermission = readPhoneStatePermission;
}
private void checkReadPhoneStatePermission() {
@@ -712,18 +769,16 @@ public class ShadowTelephonyManager {
this.voiceNetworkType = voiceNetworkType;
}
- @Implementation(minSdk = JELLY_BEAN_MR1)
+ @Implementation
protected List<CellInfo> getAllCellInfo() {
return allCellInfo;
}
public void setAllCellInfo(List<CellInfo> allCellInfo) {
- this.allCellInfo = allCellInfo;
+ ShadowTelephonyManager.allCellInfo = allCellInfo;
- if (VERSION.SDK_INT >= JELLY_BEAN_MR1) {
- for (PhoneStateListener listener : getListenersForFlags(LISTEN_CELL_INFO)) {
- listener.onCellInfoChanged(allCellInfo);
- }
+ for (PhoneStateListener listener : getListenersForFlags(LISTEN_CELL_INFO)) {
+ listener.onCellInfoChanged(allCellInfo);
}
if (VERSION.SDK_INT >= S) {
for (CellInfoListener listener : getCallbackForListener(CellInfoListener.class)) {
@@ -739,6 +794,7 @@ public class ShadowTelephonyManager {
@Implementation(minSdk = Q)
protected void requestCellInfoUpdate(Object cellInfoExecutor, Object cellInfoCallback) {
Executor executor = (Executor) cellInfoExecutor;
+ List<CellInfo> callbackCellInfos = ShadowTelephonyManager.callbackCellInfos;
if (callbackCellInfos == null) {
// ignore
} else if (requestCellInfoUpdateErrorCode != 0 || requestCellInfoUpdateDetail != null) {
@@ -768,7 +824,7 @@ public class ShadowTelephonyManager {
* setAllCellInfo}.
*/
public void setCallbackCellInfos(List<CellInfo> callbackCellInfos) {
- this.callbackCellInfos = callbackCellInfos;
+ ShadowTelephonyManager.callbackCellInfos = callbackCellInfos;
}
/**
@@ -782,12 +838,11 @@ public class ShadowTelephonyManager {
@Implementation
protected CellLocation getCellLocation() {
- return this.cellLocation;
+ return ShadowTelephonyManager.cellLocation;
}
public void setCellLocation(CellLocation cellLocation) {
- this.cellLocation = cellLocation;
-
+ ShadowTelephonyManager.cellLocation = cellLocation;
for (PhoneStateListener listener : getListenersForFlags(LISTEN_CELL_LOCATION)) {
listener.onCellLocationChanged(cellLocation);
}
@@ -798,7 +853,7 @@ public class ShadowTelephonyManager {
}
}
- @Implementation(minSdk = JELLY_BEAN_MR2)
+ @Implementation
protected String getGroupIdLevel1() {
checkReadPhoneStatePermission();
return this.groupIdLevel1;
@@ -810,13 +865,18 @@ public class ShadowTelephonyManager {
@CallSuper
protected void initListener(PhoneStateListener listener, int flags) {
+ // grab the state "atomically" before doing callbacks, in case they modify the state
+ String incomingPhoneNumber = this.incomingPhoneNumber;
+ List<CellInfo> allCellInfo = ShadowTelephonyManager.allCellInfo;
+ CellLocation cellLocation = ShadowTelephonyManager.cellLocation;
+ Object telephonyDisplayInfo = this.telephonyDisplayInfo;
+ ServiceState serviceState = this.serviceState;
+
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);
- }
+ listener.onCellInfoChanged(allCellInfo);
}
if ((flags & LISTEN_CELL_LOCATION) != 0) {
listener.onCellLocationChanged(cellLocation);
@@ -837,6 +897,12 @@ public class ShadowTelephonyManager {
if (VERSION.SDK_INT < S) {
return;
}
+ // grab the state "atomically" before doing callbacks, in case they modify the state
+ int callState = this.callState;
+ List<CellInfo> allCellInfo = ShadowTelephonyManager.allCellInfo;
+ CellLocation cellLocation = ShadowTelephonyManager.cellLocation;
+ Object telephonyDisplayInfo = this.telephonyDisplayInfo;
+ ServiceState serviceState = this.serviceState;
if (callback instanceof CallStateListener) {
((CallStateListener) callback).onCallStateChanged(callState);
@@ -858,7 +924,7 @@ public class ShadowTelephonyManager {
protected Iterable<PhoneStateListener> getListenersForFlags(int flags) {
return Iterables.filter(
- phoneStateRegistrations.keySet(),
+ ImmutableSet.copyOf(phoneStateRegistrations.keySet()),
new Predicate<PhoneStateListener>() {
@Override
public boolean apply(PhoneStateListener input) {
@@ -874,7 +940,7 @@ public class ShadowTelephonyManager {
*/
protected <T> Iterable<T> getCallbackForListener(Class<T> clazz) {
// Only selects TelephonyCallback with matching class.
- return Iterables.filter(telephonyCallbackRegistrations, clazz);
+ return Iterables.filter(ImmutableList.copyOf(telephonyCallbackRegistrations), clazz);
}
/**
@@ -887,7 +953,7 @@ public class ShadowTelephonyManager {
/** Sets the value returned by {@link TelephonyManager#isSmsCapable()}. */
public void setIsSmsCapable(boolean isSmsCapable) {
- this.isSmsCapable = isSmsCapable;
+ ShadowTelephonyManager.isSmsCapable = isSmsCapable;
}
/**
@@ -947,7 +1013,7 @@ public class ShadowTelephonyManager {
/** Sets the value returned by {@link TelephonyManager#getPhoneCount()}. */
public void setPhoneCount(int phoneCount) {
- this.phoneCount = phoneCount;
+ ShadowTelephonyManager.phoneCount = phoneCount;
}
/** Returns 1 by default or the value specified via {@link #setActiveModemCount(int)}. */
@@ -958,7 +1024,7 @@ public class ShadowTelephonyManager {
/** Sets the value returned by {@link TelephonyManager#getActiveModemCount()}. */
public void setActiveModemCount(int activeModemCount) {
- this.activeModemCount = activeModemCount;
+ ShadowTelephonyManager.activeModemCount = activeModemCount;
}
/**
@@ -985,7 +1051,7 @@ public class ShadowTelephonyManager {
/** Sets the value returned by {@link #isVoiceCapable()}. */
public void setVoiceCapable(boolean voiceCapable) {
- this.voiceCapable = voiceCapable;
+ ShadowTelephonyManager.voiceCapable = voiceCapable;
}
/**
@@ -1109,7 +1175,7 @@ public class ShadowTelephonyManager {
@Implementation(minSdk = M)
@HiddenApi
protected int getCurrentPhoneType(int subId) {
- return currentPhoneTypes.get(subId, TelephonyManager.PHONE_TYPE_NONE);
+ return currentPhoneTypes.getOrDefault(subId, TelephonyManager.PHONE_TYPE_NONE);
}
/** Sets the phone type for the given {@code subId}. */
@@ -1118,7 +1184,7 @@ public class ShadowTelephonyManager {
}
/** Removes all {@code subId} to {@code phoneType} mappings. */
- public void clearPhoneTypes() {
+ public static void clearPhoneTypes() {
currentPhoneTypes.clear();
}
@@ -1288,7 +1354,7 @@ public class ShadowTelephonyManager {
* @param emergencyCallbackMode whether the device is in ECBM or not.
*/
public void setEmergencyCallbackMode(boolean emergencyCallbackMode) {
- this.emergencyCallbackMode = emergencyCallbackMode;
+ ShadowTelephonyManager.emergencyCallbackMode = emergencyCallbackMode;
}
@Implementation(minSdk = Build.VERSION_CODES.O)
@@ -1377,7 +1443,7 @@ public class ShadowTelephonyManager {
/** Sets the value to be returned by {@link #isTtyModeSupported()} */
public void setTtyModeSupported(boolean isTtyModeSupported) {
- this.isTtyModeSupported = isTtyModeSupported;
+ ShadowTelephonyManager.isTtyModeSupported = isTtyModeSupported;
}
/**
@@ -1386,7 +1452,7 @@ public class ShadowTelephonyManager {
@Implementation(minSdk = Build.VERSION_CODES.N)
@HiddenApi
protected boolean hasCarrierPrivileges(int subId) {
- return subIdToHasCarrierPrivileges.get(subId);
+ return subIdToHasCarrierPrivileges.getOrDefault(subId, false);
}
public void setHasCarrierPrivileges(boolean hasCarrierPrivileges) {