Add getEmergencyCallbackMode() method to ShadowTelephonyManager

PiperOrigin-RevId: 520121750
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowTelephonyManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowTelephonyManagerTest.java
index cb6359f..8571627 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowTelephonyManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowTelephonyManagerTest.java
@@ -45,6 +45,8 @@
 import static org.robolectric.Shadows.shadowOf;
 import static org.robolectric.shadows.ShadowTelephonyManager.createTelephonyDisplayInfo;
 
+import android.Manifest.permission;
+import android.app.Application;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -101,6 +103,8 @@
   public void setUp() throws Exception {
     telephonyManager = (TelephonyManager) getApplication().getSystemService(TELEPHONY_SERVICE);
     shadowTelephonyManager = Shadow.extract(telephonyManager);
+    shadowOf((Application) ApplicationProvider.getApplicationContext())
+        .grantPermissions(permission.READ_PRIVILEGED_PHONE_STATE);
   }
 
   @Test
@@ -1040,4 +1044,28 @@
 
     assertThrows(IllegalStateException.class, () -> telephonyManager.isEmergencyNumber("911"));
   }
+
+  @Test
+  @Config(minSdk = O)
+  public void
+      getEmergencyCallbackMode_noReadPrivilegedPhoneStatePermission_throwsSecurityException() {
+    shadowOf((Application) ApplicationProvider.getApplicationContext())
+        .denyPermissions(permission.READ_PRIVILEGED_PHONE_STATE);
+
+    assertThrows(SecurityException.class, () -> telephonyManager.getEmergencyCallbackMode());
+  }
+
+  @Test
+  @Config(minSdk = O)
+  public void getEmergencyCallback_wasSetToTrue_returnsTrue() {
+    shadowTelephonyManager.setEmergencyCallbackMode(true);
+
+    assertThat(telephonyManager.getEmergencyCallbackMode()).isTrue();
+  }
+
+  @Test
+  @Config(minSdk = O)
+  public void getEmergencyCallback_notSet_returnsFalse() {
+    assertThat(telephonyManager.getEmergencyCallbackMode()).isFalse();
+  }
 }
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 902669f..0864354 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowTelephonyManager.java
@@ -1,5 +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;
@@ -20,7 +21,9 @@
 import static android.telephony.TelephonyManager.CALL_STATE_IDLE;
 import static android.telephony.TelephonyManager.CALL_STATE_RINGING;
 
+import android.Manifest.permission;
 import android.annotation.CallSuper;
+import android.app.ActivityThread;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -64,11 +67,13 @@
 import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.Executor;
+import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.HiddenApi;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 import org.robolectric.annotation.RealObject;
 import org.robolectric.annotation.Resetter;
+import org.robolectric.shadow.api.Shadow;
 import org.robolectric.util.ReflectionHelpers;
 
 @Implements(value = TelephonyManager.class, looseSignatures = true)
@@ -146,6 +151,7 @@
   private static int callComposerStatus = 0;
   private VisualVoicemailSmsParams lastVisualVoicemailSmsParams;
   private VisualVoicemailSmsFilterSettings visualVoicemailSmsFilterSettings;
+  private boolean emergencyCallbackMode;
 
   /**
    * Should be {@link TelephonyManager.BootstrapAuthenticationCallback} but this object was
@@ -524,6 +530,23 @@
     }
   }
 
+  private void checkReadPrivilegedPhoneStatePermission() {
+    if (!checkPermission(permission.READ_PRIVILEGED_PHONE_STATE)) {
+      throw new SecurityException();
+    }
+  }
+
+  static ShadowInstrumentation getShadowInstrumentation() {
+    ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread();
+    return Shadow.extract(activityThread.getInstrumentation());
+  }
+
+  static boolean checkPermission(String permission) {
+    return getShadowInstrumentation()
+            .checkPermission(permission, android.os.Process.myPid(), android.os.Process.myUid())
+        == PERMISSION_GRANTED;
+  }
+
   @Implementation
   protected int getPhoneType() {
     return phoneType;
@@ -1159,6 +1182,22 @@
     return false;
   }
 
+  /**
+   * Emergency Callback Mode (ECBM) is typically set by the carrier, for a time window of 5 minutes
+   * after the last outgoing emergency call. The user can exit ECBM via a system notification.
+   *
+   * @param emergencyCallbackMode whether the device is in ECBM or not.
+   */
+  public void setEmergencyCallbackMode(boolean emergencyCallbackMode) {
+    this.emergencyCallbackMode = emergencyCallbackMode;
+  }
+
+  @Implementation(minSdk = Build.VERSION_CODES.O)
+  protected boolean getEmergencyCallbackMode() {
+    checkReadPrivilegedPhoneStatePermission();
+    return emergencyCallbackMode;
+  }
+
   @Implementation(minSdk = Build.VERSION_CODES.Q)
   protected boolean isPotentialEmergencyNumber(String number) {
     return isEmergencyNumber(number);