1 package org.robolectric.shadows; 2 3 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; 4 import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2; 5 import static android.telephony.PhoneStateListener.LISTEN_CALL_STATE; 6 import static android.telephony.PhoneStateListener.LISTEN_CELL_INFO; 7 import static android.telephony.PhoneStateListener.LISTEN_CELL_LOCATION; 8 import static android.telephony.PhoneStateListener.LISTEN_NONE; 9 import static android.telephony.TelephonyManager.CALL_STATE_IDLE; 10 import static android.telephony.TelephonyManager.CALL_STATE_RINGING; 11 12 import android.os.Build.VERSION; 13 import android.telephony.CellInfo; 14 import android.telephony.CellLocation; 15 import android.telephony.PhoneStateListener; 16 import android.telephony.TelephonyManager; 17 import com.google.common.base.Predicate; 18 import com.google.common.collect.Iterables; 19 import java.util.Collections; 20 import java.util.HashMap; 21 import java.util.List; 22 import java.util.Map; 23 import org.robolectric.annotation.Implementation; 24 import org.robolectric.annotation.Implements; 25 26 @Implements(TelephonyManager.class) 27 public class ShadowTelephonyManager { 28 29 private final Map<PhoneStateListener, Integer> phoneStateRegistrations = new HashMap<>(); 30 private PhoneStateListener lastListener; 31 private int lastEventFlags; 32 33 private String deviceId; 34 private String groupIdLevel1; 35 private String networkOperatorName; 36 private String networkCountryIso; 37 private String networkOperator; 38 private String simOperator; 39 private String simOperatorName; 40 private boolean readPhoneStatePermission = true; 41 private int phoneType = TelephonyManager.PHONE_TYPE_GSM; 42 private String simCountryIso = ""; 43 private int simState = TelephonyManager.SIM_STATE_READY; 44 private String line1Number; 45 private int networkType; 46 private List<CellInfo> allCellInfo = Collections.emptyList(); 47 private CellLocation cellLocation = null; 48 private int callState = CALL_STATE_IDLE; 49 private String incomingPhoneNumber = null; 50 51 @Implementation 52 public void listen(PhoneStateListener listener, int flags) { 53 lastListener = listener; 54 lastEventFlags = flags; 55 56 if (flags == LISTEN_NONE) { 57 phoneStateRegistrations.remove(listener); 58 } else { 59 initListener(listener, flags); 60 phoneStateRegistrations.put(listener, flags); 61 } 62 } 63 64 /** 65 * Returns the most recent listener passed to #listen(). 66 * 67 * @return Phone state listener. 68 * @deprecated Avoid using. 69 */ 70 @Deprecated 71 public PhoneStateListener getListener() { 72 return lastListener; 73 } 74 75 /** 76 * Returns the most recent flags passed to #listen(). 77 * 78 * @return Event flags. 79 * @deprecated Avoid using. 80 */ 81 @Deprecated 82 public int getEventFlags() { 83 return lastEventFlags; 84 } 85 86 /** Call state may be specified via {@link #setCallState(int)}. */ 87 @Implementation 88 public int getCallState() { 89 return callState; 90 } 91 92 /** Sets the current call state to the desired state and updates any listeners. */ 93 public void setCallState(int callState) { 94 setCallState(callState, null); 95 } 96 97 /** 98 * Sets the current call state with the option to specify an incoming phone number for the 99 * CALL_STATE_RINGING state. The incoming phone number will be ignored for all other cases. 100 */ 101 public void setCallState(int callState, String incomingPhoneNumber) { 102 if (callState != CALL_STATE_RINGING) { 103 incomingPhoneNumber = null; 104 } 105 106 this.callState = callState; 107 this.incomingPhoneNumber = incomingPhoneNumber; 108 109 for (PhoneStateListener listener : getListenersForFlags(LISTEN_CALL_STATE)) { 110 listener.onCallStateChanged(callState, incomingPhoneNumber); 111 } 112 } 113 114 @Implementation 115 public String getDeviceId() { 116 checkReadPhoneStatePermission(); 117 return deviceId; 118 } 119 120 public void setDeviceId(String newDeviceId) { 121 deviceId = newDeviceId; 122 } 123 124 public void setNetworkOperatorName(String networkOperatorName) { 125 this.networkOperatorName = networkOperatorName; 126 } 127 128 @Implementation 129 public String getNetworkOperatorName() { 130 return networkOperatorName; 131 } 132 133 public void setNetworkCountryIso(String networkCountryIso) { 134 this.networkCountryIso = networkCountryIso; 135 } 136 137 @Implementation 138 public String getNetworkCountryIso() { 139 return networkCountryIso; 140 } 141 142 public void setNetworkOperator(String networkOperator) { 143 this.networkOperator = networkOperator; 144 } 145 146 @Implementation 147 public String getNetworkOperator() { 148 return networkOperator; 149 } 150 151 @Implementation 152 public String getSimOperator() { 153 return simOperator; 154 } 155 156 public void setSimOperator(String simOperator) { 157 this.simOperator = simOperator; 158 } 159 160 @Implementation 161 public String getSimOperatorName() { 162 return simOperatorName; 163 } 164 165 public void setSimOperatorName(String simOperatorName) { 166 this.simOperatorName = simOperatorName; 167 } 168 169 @Implementation 170 public String getSimCountryIso() { 171 return simCountryIso; 172 } 173 174 public void setSimCountryIso(String simCountryIso) { 175 this.simCountryIso = simCountryIso; 176 } 177 178 @Implementation 179 public int getSimState() { 180 return simState; 181 } 182 183 public void setSimState(int simState) { 184 this.simState = simState; 185 } 186 187 public void setReadPhoneStatePermission(boolean readPhoneStatePermission) { 188 this.readPhoneStatePermission = readPhoneStatePermission; 189 } 190 191 private void checkReadPhoneStatePermission() { 192 if (!readPhoneStatePermission) { 193 throw new SecurityException(); 194 } 195 } 196 197 @Implementation 198 public int getPhoneType() { 199 return phoneType; 200 } 201 202 public void setPhoneType(int phoneType) { 203 this.phoneType = phoneType; 204 } 205 206 @Implementation 207 public String getLine1Number() { 208 return line1Number; 209 } 210 211 public void setLine1Number(String line1Number) { 212 this.line1Number = line1Number; 213 } 214 215 @Implementation 216 public int getNetworkType() { 217 return networkType; 218 } 219 220 public void setNetworkType(int networkType) { 221 this.networkType = networkType; 222 } 223 224 @Implementation(minSdk = JELLY_BEAN_MR1) 225 public List<CellInfo> getAllCellInfo() { 226 return allCellInfo; 227 } 228 229 public void setAllCellInfo(List<CellInfo> allCellInfo) { 230 this.allCellInfo = allCellInfo; 231 232 if (VERSION.SDK_INT >= JELLY_BEAN_MR1) { 233 for (PhoneStateListener listener : getListenersForFlags(LISTEN_CELL_INFO)) { 234 listener.onCellInfoChanged(allCellInfo); 235 } 236 } 237 } 238 239 @Implementation 240 public CellLocation getCellLocation() { 241 return this.cellLocation; 242 } 243 244 public void setCellLocation(CellLocation cellLocation) { 245 this.cellLocation = cellLocation; 246 247 for (PhoneStateListener listener : getListenersForFlags(LISTEN_CELL_LOCATION)) { 248 listener.onCellLocationChanged(cellLocation); 249 } 250 } 251 252 @Implementation(minSdk = JELLY_BEAN_MR2) 253 public String getGroupIdLevel1() { 254 return this.groupIdLevel1; 255 } 256 257 public void setGroupIdLevel1(String groupIdLevel1) { 258 this.groupIdLevel1 = groupIdLevel1; 259 } 260 261 private void initListener(PhoneStateListener listener, int flags) { 262 if ((flags & LISTEN_CALL_STATE) != 0) { 263 listener.onCallStateChanged(callState, incomingPhoneNumber); 264 } 265 if ((flags & LISTEN_CELL_INFO) != 0) { 266 if (VERSION.SDK_INT >= JELLY_BEAN_MR1) { 267 listener.onCellInfoChanged(allCellInfo); 268 } 269 } 270 if ((flags & LISTEN_CELL_LOCATION) != 0) { 271 listener.onCellLocationChanged(cellLocation); 272 } 273 } 274 275 private Iterable<PhoneStateListener> getListenersForFlags(int flags) { 276 return Iterables.filter( 277 phoneStateRegistrations.keySet(), 278 new Predicate<PhoneStateListener>() { 279 @Override 280 public boolean apply(PhoneStateListener input) { 281 // only select PhoneStateListeners with matching flags 282 return (phoneStateRegistrations.get(input) & flags) != 0; 283 } 284 }); 285 } 286 } 287