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.os.Build.VERSION_CODES.LOLLIPOP; 6 import static android.os.Build.VERSION_CODES.M; 7 import static android.os.Build.VERSION_CODES.N; 8 import static android.os.Build.VERSION_CODES.O; 9 import static android.os.Build.VERSION_CODES.P; 10 import static android.telephony.PhoneStateListener.LISTEN_CALL_STATE; 11 import static android.telephony.PhoneStateListener.LISTEN_CELL_INFO; 12 import static android.telephony.PhoneStateListener.LISTEN_CELL_LOCATION; 13 import static android.telephony.PhoneStateListener.LISTEN_NONE; 14 import static android.telephony.TelephonyManager.CALL_STATE_IDLE; 15 import static android.telephony.TelephonyManager.CALL_STATE_RINGING; 16 17 import android.content.Intent; 18 import android.net.Uri; 19 import android.os.Build; 20 import android.os.Build.VERSION; 21 import android.os.PersistableBundle; 22 import android.telecom.PhoneAccountHandle; 23 import android.telephony.CellInfo; 24 import android.telephony.CellLocation; 25 import android.telephony.PhoneStateListener; 26 import android.telephony.ServiceState; 27 import android.telephony.SubscriptionManager; 28 import android.telephony.TelephonyManager; 29 import android.util.SparseArray; 30 import android.util.SparseIntArray; 31 import com.google.common.base.Predicate; 32 import com.google.common.collect.Iterables; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import org.robolectric.annotation.HiddenApi; 38 import org.robolectric.annotation.Implementation; 39 import org.robolectric.annotation.Implements; 40 41 @Implements(TelephonyManager.class) 42 public class ShadowTelephonyManager { 43 44 private final Map<PhoneStateListener, Integer> phoneStateRegistrations = new HashMap<>(); 45 private final Map<Integer, String> slotIndexToDeviceId = new HashMap<>(); 46 private final Map<PhoneAccountHandle, Boolean> voicemailVibrationEnabledMap = new HashMap<>(); 47 private final Map<PhoneAccountHandle, Uri> voicemailRingtoneUriMap = new HashMap<>(); 48 private final Map<PhoneAccountHandle, TelephonyManager> phoneAccountToTelephonyManagers = 49 new HashMap<>(); 50 51 private PhoneStateListener lastListener; 52 private int lastEventFlags; 53 54 private String deviceId; 55 private String imei; 56 private String meid; 57 private String groupIdLevel1; 58 private String networkOperatorName = ""; 59 private String networkCountryIso; 60 private String networkOperator = ""; 61 private String simOperator; 62 private String simOperatorName; 63 private String simSerialNumber; 64 private boolean readPhoneStatePermission = true; 65 private int phoneType = TelephonyManager.PHONE_TYPE_GSM; 66 private String line1Number; 67 private int networkType; 68 private int voiceNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; 69 private List<CellInfo> allCellInfo = Collections.emptyList(); 70 private CellLocation cellLocation = null; 71 private int callState = CALL_STATE_IDLE; 72 private String incomingPhoneNumber = null; 73 private boolean isAnasEnabled; 74 private boolean isSmsCapable = true; 75 private String voiceMailNumber; 76 private String voiceMailAlphaTag; 77 private int phoneCount = 1; 78 private Map<Integer, TelephonyManager> subscriptionIdsToTelephonyManagers = new HashMap<>(); 79 private PersistableBundle carrierConfig; 80 private ServiceState serviceState; 81 private boolean isNetworkRoaming; 82 private final SparseIntArray simStates = new SparseIntArray(); 83 private final SparseIntArray currentPhoneTypes = new SparseIntArray(); 84 private final SparseArray<List<String>> carrierPackageNames = new SparseArray<>(); 85 private final Map<Integer, String> simCountryIsoMap = new HashMap<>(); 86 private int simCarrierId; 87 private String subscriberId; 88 89 { resetSimStates()90 resetSimStates(); resetSimCountryIsos()91 resetSimCountryIsos(); 92 } 93 94 @Implementation listen(PhoneStateListener listener, int flags)95 protected void listen(PhoneStateListener listener, int flags) { 96 lastListener = listener; 97 lastEventFlags = flags; 98 99 if (flags == LISTEN_NONE) { 100 phoneStateRegistrations.remove(listener); 101 } else { 102 initListener(listener, flags); 103 phoneStateRegistrations.put(listener, flags); 104 } 105 } 106 107 /** 108 * Returns the most recent listener passed to #listen(). 109 * 110 * @return Phone state listener. 111 * @deprecated Avoid using. 112 */ 113 @Deprecated getListener()114 public PhoneStateListener getListener() { 115 return lastListener; 116 } 117 118 /** 119 * Returns the most recent flags passed to #listen(). 120 * 121 * @return Event flags. 122 * @deprecated Avoid using. 123 */ 124 @Deprecated getEventFlags()125 public int getEventFlags() { 126 return lastEventFlags; 127 } 128 129 /** Call state may be specified via {@link #setCallState(int)}. */ 130 @Implementation getCallState()131 protected int getCallState() { 132 return callState; 133 } 134 135 /** Sets the current call state to the desired state and updates any listeners. */ setCallState(int callState)136 public void setCallState(int callState) { 137 setCallState(callState, null); 138 } 139 140 /** 141 * Sets the current call state with the option to specify an incoming phone number for the 142 * CALL_STATE_RINGING state. The incoming phone number will be ignored for all other cases. 143 */ setCallState(int callState, String incomingPhoneNumber)144 public void setCallState(int callState, String incomingPhoneNumber) { 145 if (callState != CALL_STATE_RINGING) { 146 incomingPhoneNumber = null; 147 } 148 149 this.callState = callState; 150 this.incomingPhoneNumber = incomingPhoneNumber; 151 152 for (PhoneStateListener listener : getListenersForFlags(LISTEN_CALL_STATE)) { 153 listener.onCallStateChanged(callState, incomingPhoneNumber); 154 } 155 } 156 157 @Implementation getDeviceId()158 protected String getDeviceId() { 159 checkReadPhoneStatePermission(); 160 return deviceId; 161 } 162 setDeviceId(String newDeviceId)163 public void setDeviceId(String newDeviceId) { 164 deviceId = newDeviceId; 165 } 166 setNetworkOperatorName(String networkOperatorName)167 public void setNetworkOperatorName(String networkOperatorName) { 168 this.networkOperatorName = networkOperatorName; 169 } 170 171 @Implementation(minSdk = LOLLIPOP) getImei()172 protected String getImei() { 173 checkReadPhoneStatePermission(); 174 return imei; 175 } 176 177 /** Set the IMEI returned by getImei(). */ setImei(String imei)178 public void setImei(String imei) { 179 this.imei = imei; 180 } 181 182 @Implementation(minSdk = O) getMeid()183 protected String getMeid() { 184 checkReadPhoneStatePermission(); 185 return meid; 186 } 187 188 /** Set the MEID returned by getMeid(). */ setMeid(String meid)189 public void setMeid(String meid) { 190 this.meid = meid; 191 } 192 193 @Implementation getNetworkOperatorName()194 protected String getNetworkOperatorName() { 195 return networkOperatorName; 196 } 197 setNetworkCountryIso(String networkCountryIso)198 public void setNetworkCountryIso(String networkCountryIso) { 199 this.networkCountryIso = networkCountryIso; 200 } 201 202 @Implementation getNetworkCountryIso()203 protected String getNetworkCountryIso() { 204 return networkCountryIso; 205 } 206 setNetworkOperator(String networkOperator)207 public void setNetworkOperator(String networkOperator) { 208 this.networkOperator = networkOperator; 209 } 210 211 @Implementation getNetworkOperator()212 protected String getNetworkOperator() { 213 return networkOperator; 214 } 215 216 @Implementation getSimOperator()217 protected String getSimOperator() { 218 return simOperator; 219 } 220 setSimOperator(String simOperator)221 public void setSimOperator(String simOperator) { 222 this.simOperator = simOperator; 223 } 224 225 @Implementation getSimOperatorName()226 protected String getSimOperatorName() { 227 return simOperatorName; 228 } 229 setSimOperatorName(String simOperatorName)230 public void setSimOperatorName(String simOperatorName) { 231 this.simOperatorName = simOperatorName; 232 } 233 234 @Implementation getSimSerialNumber()235 protected String getSimSerialNumber() { 236 checkReadPhoneStatePermission(); 237 return this.simSerialNumber; 238 } 239 240 /** sets the serial number that will be returned by {@link #getSimSerialNumber}. */ setSimSerialNumber(String simSerialNumber)241 public void setSimSerialNumber(String simSerialNumber) { 242 this.simSerialNumber = simSerialNumber; 243 } 244 245 @Implementation getSimCountryIso()246 protected String getSimCountryIso() { 247 return simCountryIsoMap.get(/* subId= */ 0); 248 } 249 250 @Implementation(minSdk = N) 251 @HiddenApi getSimCountryIso(int subId)252 protected String getSimCountryIso(int subId) { 253 return simCountryIsoMap.get(subId); 254 } 255 setSimCountryIso(String simCountryIso)256 public void setSimCountryIso(String simCountryIso) { 257 setSimCountryIso(/* subId= */ 0, simCountryIso); 258 } 259 260 /** Sets the {@code simCountryIso} for the given {@code subId}. */ setSimCountryIso(int subId, String simCountryIso)261 public void setSimCountryIso(int subId, String simCountryIso) { 262 simCountryIsoMap.put(subId, simCountryIso); 263 } 264 265 /** Clears {@code subId} to simCountryIso mapping and resets to default state. */ resetSimCountryIsos()266 public void resetSimCountryIsos() { 267 simCountryIsoMap.clear(); 268 simCountryIsoMap.put(0, ""); 269 } 270 271 @Implementation getSimState()272 protected int getSimState() { 273 return getSimState(/* slotIndex= */ 0); 274 } 275 276 /** Sets the sim state of slot 0. */ setSimState(int simState)277 public void setSimState(int simState) { 278 setSimState(/* slotIndex= */ 0, simState); 279 } 280 281 /** Set the sim state for the given {@code slotIndex}. */ setSimState(int slotIndex, int state)282 public void setSimState(int slotIndex, int state) { 283 simStates.put(slotIndex, state); 284 } 285 286 @Implementation(minSdk = O) getSimState(int slotIndex)287 protected int getSimState(int slotIndex) { 288 return simStates.get(slotIndex, TelephonyManager.SIM_STATE_UNKNOWN); 289 } 290 291 /** Clears {@code slotIndex} to state mapping and resets to default state. */ resetSimStates()292 public void resetSimStates() { 293 simStates.clear(); 294 simStates.put(0, TelephonyManager.SIM_STATE_READY); 295 } 296 setReadPhoneStatePermission(boolean readPhoneStatePermission)297 public void setReadPhoneStatePermission(boolean readPhoneStatePermission) { 298 this.readPhoneStatePermission = readPhoneStatePermission; 299 } 300 checkReadPhoneStatePermission()301 private void checkReadPhoneStatePermission() { 302 if (!readPhoneStatePermission) { 303 throw new SecurityException(); 304 } 305 } 306 307 @Implementation getPhoneType()308 protected int getPhoneType() { 309 return phoneType; 310 } 311 setPhoneType(int phoneType)312 public void setPhoneType(int phoneType) { 313 this.phoneType = phoneType; 314 } 315 316 @Implementation getLine1Number()317 protected String getLine1Number() { 318 return line1Number; 319 } 320 setLine1Number(String line1Number)321 public void setLine1Number(String line1Number) { 322 this.line1Number = line1Number; 323 } 324 325 @Implementation getNetworkType()326 protected int getNetworkType() { 327 return networkType; 328 } 329 setNetworkType(int networkType)330 public void setNetworkType(int networkType) { 331 this.networkType = networkType; 332 } 333 334 /** 335 * Returns whatever value was set by the last call to {@link #setVoiceNetworkType}, defaulting to 336 * {@link TelephonyManager#NETWORK_TYPE_UNKNOWN} if it was never called. 337 */ 338 @Implementation(minSdk = N) getVoiceNetworkType()339 protected int getVoiceNetworkType() { 340 return voiceNetworkType; 341 } 342 343 /** 344 * Sets the value to be returned by calls to {@link getVoiceNetworkType}. This <b>should</b> 345 * correspond to one of the {@code NETWORK_TYPE_*} constants defined on {@link TelephonyManager}, 346 * but this is not enforced. 347 */ setVoiceNetworkType(int voiceNetworkType)348 public void setVoiceNetworkType(int voiceNetworkType) { 349 this.voiceNetworkType = voiceNetworkType; 350 } 351 352 @Implementation(minSdk = JELLY_BEAN_MR1) getAllCellInfo()353 protected List<CellInfo> getAllCellInfo() { 354 return allCellInfo; 355 } 356 setAllCellInfo(List<CellInfo> allCellInfo)357 public void setAllCellInfo(List<CellInfo> allCellInfo) { 358 this.allCellInfo = allCellInfo; 359 360 if (VERSION.SDK_INT >= JELLY_BEAN_MR1) { 361 for (PhoneStateListener listener : getListenersForFlags(LISTEN_CELL_INFO)) { 362 listener.onCellInfoChanged(allCellInfo); 363 } 364 } 365 } 366 367 @Implementation getCellLocation()368 protected CellLocation getCellLocation() { 369 return this.cellLocation; 370 } 371 setCellLocation(CellLocation cellLocation)372 public void setCellLocation(CellLocation cellLocation) { 373 this.cellLocation = cellLocation; 374 375 for (PhoneStateListener listener : getListenersForFlags(LISTEN_CELL_LOCATION)) { 376 listener.onCellLocationChanged(cellLocation); 377 } 378 } 379 380 @Implementation(minSdk = JELLY_BEAN_MR2) getGroupIdLevel1()381 protected String getGroupIdLevel1() { 382 return this.groupIdLevel1; 383 } 384 setGroupIdLevel1(String groupIdLevel1)385 public void setGroupIdLevel1(String groupIdLevel1) { 386 this.groupIdLevel1 = groupIdLevel1; 387 } 388 initListener(PhoneStateListener listener, int flags)389 private void initListener(PhoneStateListener listener, int flags) { 390 if ((flags & LISTEN_CALL_STATE) != 0) { 391 listener.onCallStateChanged(callState, incomingPhoneNumber); 392 } 393 if ((flags & LISTEN_CELL_INFO) != 0) { 394 if (VERSION.SDK_INT >= JELLY_BEAN_MR1) { 395 listener.onCellInfoChanged(allCellInfo); 396 } 397 } 398 if ((flags & LISTEN_CELL_LOCATION) != 0) { 399 listener.onCellLocationChanged(cellLocation); 400 } 401 } 402 getListenersForFlags(int flags)403 private Iterable<PhoneStateListener> getListenersForFlags(int flags) { 404 return Iterables.filter( 405 phoneStateRegistrations.keySet(), 406 new Predicate<PhoneStateListener>() { 407 @Override 408 public boolean apply(PhoneStateListener input) { 409 // only select PhoneStateListeners with matching flags 410 return (phoneStateRegistrations.get(input) & flags) != 0; 411 } 412 }); 413 } 414 415 // BEGIN-INTERNAL 416 @Implementation(minSdk = Build.VERSION_CODES.Q) 417 protected boolean setAlternativeNetworkState(boolean enable) { 418 isAnasEnabled = enable; 419 return true; 420 } 421 422 @Implementation(minSdk = Build.VERSION_CODES.Q) 423 protected boolean isAlternativeNetworkEnabled() { 424 return isAnasEnabled; 425 } 426 // END-INTERNAL 427 428 /** @return `true` by default, or the value specified via {@link #setIsSmsCapable(boolean)} */ 429 @Implementation 430 protected boolean isSmsCapable() { 431 return isSmsCapable; 432 } 433 434 /** Sets the value returned by {@link TelephonyManager#isSmsCapable()}. */ 435 public void setIsSmsCapable(boolean isSmsCapable) { 436 this.isSmsCapable = isSmsCapable; 437 } 438 439 /** 440 * Returns a new empty {@link PersistableBundle} by default, or the value specified via {@link 441 * #setCarrierConfig(PersistableBundle)}. 442 */ 443 @Implementation(minSdk = O) 444 protected PersistableBundle getCarrierConfig() { 445 return carrierConfig != null ? carrierConfig : new PersistableBundle(); 446 } 447 448 /** 449 * Sets the value returned by {@link TelephonyManager#getCarrierConfig()}. 450 * 451 * @param carrierConfig 452 */ 453 public void setCarrierConfig(PersistableBundle carrierConfig) { 454 this.carrierConfig = carrierConfig; 455 } 456 457 /** 458 * Returns {@code null} by default, or the value specified via {@link 459 * #setVoiceMailNumber(String)}. 460 */ 461 @Implementation 462 protected String getVoiceMailNumber() { 463 return voiceMailNumber; 464 } 465 466 /** Sets the value returned by {@link TelephonyManager#getVoiceMailNumber()}. */ 467 public void setVoiceMailNumber(String voiceMailNumber) { 468 this.voiceMailNumber = voiceMailNumber; 469 } 470 471 /** 472 * Returns {@code null} by default or the value specified via {@link 473 * #setVoiceMailAlphaTag(String)}. 474 */ 475 @Implementation 476 protected String getVoiceMailAlphaTag() { 477 return voiceMailAlphaTag; 478 } 479 480 /** Sets the value returned by {@link TelephonyManager#getVoiceMailAlphaTag()}. */ 481 public void setVoiceMailAlphaTag(String voiceMailAlphaTag) { 482 this.voiceMailAlphaTag = voiceMailAlphaTag; 483 } 484 485 /** Returns 1 by default or the value specified via {@link #setPhoneCount(int)}. */ 486 @Implementation(minSdk = M) 487 protected int getPhoneCount() { 488 return phoneCount; 489 } 490 491 /** Sets the value returned by {@link TelephonyManager#getPhoneCount()}. */ 492 public void setPhoneCount(int phoneCount) { 493 this.phoneCount = phoneCount; 494 } 495 496 /** 497 * Returns {@code null} by default or the value specified via {@link #setDeviceId(int, String)}. 498 */ 499 @Implementation(minSdk = M) 500 protected String getDeviceId(int slot) { 501 return slotIndexToDeviceId.get(slot); 502 } 503 504 /** Sets the value returned by {@link TelephonyManager#getDeviceId(int)}. */ 505 public void setDeviceId(int slot, String deviceId) { 506 slotIndexToDeviceId.put(slot, deviceId); 507 } 508 509 /** 510 * Returns {@code null} by default or the value specified via {@link 511 * #setVoicemailVibrationEnabled(PhoneAccountHandle, boolean)}. 512 */ 513 @Implementation(minSdk = N) 514 protected boolean isVoicemailVibrationEnabled(PhoneAccountHandle handle) { 515 Boolean result = voicemailVibrationEnabledMap.get(handle); 516 return result != null ? result : false; 517 } 518 519 /** 520 * Sets the value returned by {@link 521 * TelephonyManager#isVoicemailVibrationEnabled(PhoneAccountHandle)}. 522 */ 523 @Implementation(minSdk = O) 524 protected void setVoicemailVibrationEnabled(PhoneAccountHandle handle, boolean isEnabled) { 525 voicemailVibrationEnabledMap.put(handle, isEnabled); 526 } 527 528 /** 529 * Returns {@code null} by default or the value specified via {@link 530 * #setVoicemailRingtoneUri(PhoneAccountHandle, Uri)}. 531 */ 532 @Implementation(minSdk = N) 533 protected Uri getVoicemailRingtoneUri(PhoneAccountHandle handle) { 534 return voicemailRingtoneUriMap.get(handle); 535 } 536 537 /** 538 * Sets the value returned by {@link 539 * TelephonyManager#getVoicemailRingtoneUri(PhoneAccountHandle)}. 540 */ 541 @Implementation(minSdk = O) 542 protected void setVoicemailRingtoneUri(PhoneAccountHandle handle, Uri uri) { 543 voicemailRingtoneUriMap.put(handle, uri); 544 } 545 546 /** 547 * Returns {@code null} by default or the value specified via {@link 548 * #setTelephonyManagerForHandle(PhoneAccountHandle, TelephonyManager)}. 549 */ 550 @Implementation(minSdk = O) 551 protected TelephonyManager createForPhoneAccountHandle(PhoneAccountHandle handle) { 552 return phoneAccountToTelephonyManagers.get(handle); 553 } 554 555 /** 556 * Sets the value returned by {@link 557 * TelephonyManager#createForPhoneAccountHandle(PhoneAccountHandle)}. 558 */ 559 public void setTelephonyManagerForHandle( 560 PhoneAccountHandle handle, TelephonyManager telephonyManager) { 561 phoneAccountToTelephonyManagers.put(handle, telephonyManager); 562 } 563 564 /** 565 * Returns {@code null} by default or the value specified via {@link 566 * #setTelephonyManagerForSubscriptionId(int, TelephonyManager)} 567 */ 568 @Implementation(minSdk = N) 569 protected TelephonyManager createForSubscriptionId(int subId) { 570 return subscriptionIdsToTelephonyManagers.get(subId); 571 } 572 573 /** Sets the value returned by {@link TelephonyManager#createForSubscriptionId(int)}. */ 574 public void setTelephonyManagerForSubscriptionId( 575 int subscriptionId, TelephonyManager telephonyManager) { 576 subscriptionIdsToTelephonyManagers.put(subscriptionId, telephonyManager); 577 } 578 579 /** 580 * Returns {@code null} by default or the value specified via {@link 581 * #setServiceState(ServiceState)} 582 */ 583 @Implementation(minSdk = O) 584 protected ServiceState getServiceState() { 585 return serviceState; 586 } 587 588 /** Sets the value returned by {@link TelephonyManager#getServiceState()}. */ 589 public void setServiceState(ServiceState serviceState) { 590 this.serviceState = serviceState; 591 } 592 593 /** 594 * Returns {@code false} by default or the value specified via {@link 595 * #setIsNetworkRoaming(boolean)} 596 */ 597 @Implementation 598 protected boolean isNetworkRoaming() { 599 return isNetworkRoaming; 600 } 601 602 /** Sets the value returned by {@link TelephonyManager#isNetworkRoaming()}. */ 603 public void setIsNetworkRoaming(boolean isNetworkRoaming) { 604 this.isNetworkRoaming = isNetworkRoaming; 605 } 606 607 @Implementation(minSdk = M) 608 @HiddenApi 609 protected int getCurrentPhoneType(int subId) { 610 return currentPhoneTypes.get(subId, TelephonyManager.PHONE_TYPE_NONE); 611 } 612 613 /** Sets the phone type for the given {@code subId}. */ 614 public void setCurrentPhoneType(int subId, int phoneType) { 615 currentPhoneTypes.put(subId, phoneType); 616 } 617 618 /** Removes all {@code subId} to {@code phoneType} mappings. */ 619 public void clearPhoneTypes() { 620 currentPhoneTypes.clear(); 621 } 622 623 @Implementation(minSdk = M) 624 @HiddenApi 625 protected List<String> getCarrierPackageNamesForIntentAndPhone(Intent intent, int phoneId) { 626 return carrierPackageNames.get(phoneId); 627 } 628 629 @Implementation(minSdk = LOLLIPOP) 630 @HiddenApi 631 protected List<String> getCarrierPackageNamesForIntent(Intent intent) { 632 return carrierPackageNames.get(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); 633 } 634 635 /** Sets the {@code packages} for the given {@code phoneId}. */ 636 public void setCarrierPackageNamesForPhone(int phoneId, List<String> packages) { 637 carrierPackageNames.put(phoneId, packages); 638 } 639 640 @Implementation(minSdk = P) 641 protected int getSimCarrierId() { 642 return simCarrierId; 643 } 644 645 /** Sets the value to be returned by {@link #getSimCarrierId()}. */ 646 public void setSimCarrierId(int simCarrierId) { 647 this.simCarrierId = simCarrierId; 648 } 649 650 @Implementation 651 protected String getSubscriberId() { 652 return subscriberId; 653 } 654 655 /** Sets the value to be returned by {@link #getSubscriberId()}. */ 656 public void setSubscriberId(String subscriberId) { 657 this.subscriberId = subscriberId; 658 } 659 } 660