aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRam Peri <ramperi@google.com>2023-01-21 01:34:34 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2023-01-21 01:34:34 +0000
commit99b266f538c792b5c37825d8d695d1748a99bd09 (patch)
treeb1829a2dca2591bcec782fc13a20a9928c97f138
parentd51cb97e9934f408a401b6f945d8bb12b0c3d281 (diff)
parent11d21fe78d6becf9f02451c8a86a884140226bfd (diff)
downloadrobolectric-main-16k-with-phones.tar.gz
Merge "Periodic pull of Robolectric changes from github. Test: atest -c MyRoboTests Bug: 263483214"main-16k-with-phones
-rw-r--r--integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml6
-rw-r--r--integration_tests/androidx_test/src/sharedTest/java/org/robolectric/integrationtests/axt/EspressoTest.java10
-rw-r--r--nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java5
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowContentResolverTest.java23
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowContextWrapperTest.java17
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowDevicePolicyManagerTest.java37
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowEGL14Test.java5
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowPackageManagerTest.java19
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowScrollViewTest.java8
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowServiceManagerTest.java10
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowVibratorTest.java42
-rw-r--r--robolectric/src/test/java/org/robolectric/shadows/ShadowWifiManagerTest.java77
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowApplicationPackageManager.java16
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowContentResolver.java28
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowDevicePolicyManager.java13
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowEGL14.java5
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java18
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowScrollView.java4
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowServiceManager.java2
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowVibrator.java17
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowView.java23
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewRootImpl.java25
-rw-r--r--shadows/framework/src/main/java/org/robolectric/shadows/ShadowWifiManager.java51
23 files changed, 424 insertions, 37 deletions
diff --git a/integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml b/integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml
index 1cbc1979d..c412b90f0 100644
--- a/integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml
+++ b/integration_tests/androidx_test/src/main/res/layout/espresso_activity.xml
@@ -50,4 +50,10 @@
android:layout_height="wrap_content"
android:inputType="phone" />
+ <EditText
+ android:id="@+id/edit_text_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="number" />
+
</LinearLayout>
diff --git a/integration_tests/androidx_test/src/sharedTest/java/org/robolectric/integrationtests/axt/EspressoTest.java b/integration_tests/androidx_test/src/sharedTest/java/org/robolectric/integrationtests/axt/EspressoTest.java
index 494ce82c8..0c0df8b19 100644
--- a/integration_tests/androidx_test/src/sharedTest/java/org/robolectric/integrationtests/axt/EspressoTest.java
+++ b/integration_tests/androidx_test/src/sharedTest/java/org/robolectric/integrationtests/axt/EspressoTest.java
@@ -25,6 +25,7 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
import java.util.concurrent.atomic.AtomicReference;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -129,6 +130,15 @@ public final class EspressoTest {
onView(withId(R.id.edit_text_phone)).check(matches(withText("411")));
}
+ /** use typeText with a inputType number */
+ @Test
+ @Ignore // TODO(#5110): fails
+ public void typeText_number() {
+ onView(withId(R.id.edit_text_number)).perform(typeText("411"));
+
+ onView(withId(R.id.edit_text_number)).check(matches(withText("411")));
+ }
+
@Test
public void textView() {
onView(withText("Text View"))
diff --git a/nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java b/nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java
index 5221a4b8a..3892453dd 100644
--- a/nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java
+++ b/nativeruntime/src/main/java/org/robolectric/nativeruntime/DefaultNativeRuntimeLoader.java
@@ -11,6 +11,7 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.Files;
import com.google.common.io.Resources;
+import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@@ -137,7 +138,9 @@ public class DefaultNativeRuntimeLoader implements NativeRuntimeLoader {
}
}
System.setProperty(
- "robolectric.nativeruntime.fontdir", fontsOutputPath.toAbsolutePath().toString());
+ "robolectric.nativeruntime.fontdir",
+ // Android's FontListParser expects a trailing slash for the base font directory.
+ fontsOutputPath.toAbsolutePath() + File.separator);
if (zipfs != null) {
zipfs.close();
}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowContentResolverTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowContentResolverTest.java
index c77bb1e14..31e0f3243 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowContentResolverTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowContentResolverTest.java
@@ -488,8 +488,25 @@ public class ShadowContentResolverTest {
}
@Test
- public void openOutputStream_shouldReturnAnOutputStream() throws Exception {
- assertThat(contentResolver.openOutputStream(uri21)).isInstanceOf(OutputStream.class);
+ public void openOutputStream_withNoRealOrRegisteredProvider_doesNotThrow() throws Exception {
+ Uri uri = Uri.parse("content://invalidauthority/test/1");
+ assertThat(contentResolver.openOutputStream(uri)).isNotNull();
+ }
+
+ @Test
+ public void openOutputStream_withRealContentProvider_canReadBytesWrittenToOutputStream()
+ throws IOException, RemoteException {
+ Robolectric.setupContentProvider(MyContentProvider.class, AUTHORITY);
+ Uri uri = Uri.parse("content://" + AUTHORITY + "/test/1");
+
+ // Write content through given outputstream
+ try (OutputStream outputStream = contentResolver.openOutputStream(uri)) {
+ outputStream.write("foo".getBytes(UTF_8));
+ }
+
+ // Verify written content can be read back
+ InputStream inputStream = contentResolver.openInputStream(uri);
+ assertThat(new String(inputStream.readAllBytes(), UTF_8)).isEqualTo("foo");
}
@Test
@@ -1177,7 +1194,7 @@ public class ShadowContentResolverTest {
} catch (IOException e) {
throw new RuntimeException("error creating new file", e);
}
- return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
}
}
}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowContextWrapperTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowContextWrapperTest.java
index 02b149da7..4d71cbfb4 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowContextWrapperTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowContextWrapperTest.java
@@ -658,16 +658,25 @@ public class ShadowContextWrapperTest {
}
@Test
- public void checkPermissionUidPid() {
- assertThat(contextWrapper.checkPermission("MY_PERMISSON", 1, 1))
+ public void checkPermission_denied() {
+ assertThat(contextWrapper.checkPermission("MY_PERMISSON", /* pid= */ 1, /* uid= */ 1))
+ .isEqualTo(PackageManager.PERMISSION_DENIED);
+
+ assertThat(contextWrapper.checkPermission("MY_PERMISSON", /* pid= */ -1, /* uid= */ 1))
.isEqualTo(PackageManager.PERMISSION_DENIED);
+ }
+ @Test
+ public void checkPermission_granted() {
shadowContextWrapper.grantPermissions(1, 1, "MY_PERMISSON");
- assertThat(contextWrapper.checkPermission("MY_PERMISSON", 2, 1))
+ assertThat(contextWrapper.checkPermission("MY_PERMISSON", /* pid= */ 1, /* uid= */ 1))
+ .isEqualTo(PackageManager.PERMISSION_GRANTED);
+
+ assertThat(contextWrapper.checkPermission("MY_PERMISSON", /* pid= */ 2, /* uid= */ 1))
.isEqualTo(PackageManager.PERMISSION_DENIED);
- assertThat(contextWrapper.checkPermission("MY_PERMISSON", 1, 1))
+ assertThat(contextWrapper.checkPermission("MY_PERMISSON", /* pid= */ -1, /* uid= */ 1))
.isEqualTo(PackageManager.PERMISSION_GRANTED);
}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowDevicePolicyManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowDevicePolicyManagerTest.java
index 7d36f356a..03d824753 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowDevicePolicyManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowDevicePolicyManagerTest.java
@@ -818,6 +818,43 @@ public final class ShadowDevicePolicyManagerTest {
}
@Test
+ @Config(minSdk = R)
+ public void getAutoTimeEnabledShouldWorkAsIntendedForDeviceOwner() {
+ // GIVEN the caller is the device owner
+ shadowOf(devicePolicyManager).setDeviceOwner(testComponent);
+
+ // WHEN setAutoTimeEnabled is called with true
+ devicePolicyManager.setAutoTimeEnabled(testComponent, true);
+
+ // THEN getAutoTimeEnabled should return true
+ assertThat(devicePolicyManager.getAutoTimeEnabled(testComponent)).isTrue();
+ }
+
+ @Test
+ @Config(minSdk = R)
+ public void getAutoTimeEnabledShouldWorkAsIntendedForProfileOwner() {
+ // GIVEN the caller is the profile owner
+ shadowOf(devicePolicyManager).setProfileOwner(testComponent);
+
+ // WHEN setAutoTimeEnabled is called with false
+ devicePolicyManager.setAutoTimeEnabled(testComponent, false);
+
+ // THEN getAutoTimeEnabled should return false
+ assertThat(devicePolicyManager.getAutoTimeEnabled(testComponent)).isFalse();
+ }
+
+ @Test
+ @Config(minSdk = R)
+ public void getAutoTimeEnabledShouldReturnFalseIfNotSet() {
+ // GIVEN the caller is the device owner
+ shadowOf(devicePolicyManager).setDeviceOwner(testComponent);
+
+ // WHEN setAutoTimeEnabled has not been called
+ // THEN getAutoTimeEnabled should return false
+ assertThat(devicePolicyManager.getAutoTimeEnabled(testComponent)).isFalse();
+ }
+
+ @Test
@Config(minSdk = LOLLIPOP)
public void getAutoTimeRequiredShouldWorkAsIntendedForDeviceOwner() {
// GIVEN the caller is the device owner
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowEGL14Test.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowEGL14Test.java
index e5d5c9dbf..4da4963cd 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowEGL14Test.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowEGL14Test.java
@@ -17,6 +17,11 @@ import org.robolectric.annotation.Config;
@Config(minSdk = VERSION_CODES.LOLLIPOP)
public final class ShadowEGL14Test {
@Test
+ public void eglGetCurrentContext() {
+ assertThat(EGL14.eglGetCurrentContext()).isNotNull();
+ }
+
+ @Test
public void eglGetDisplay() {
assertThat(EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)).isNotNull();
}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowPackageManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowPackageManagerTest.java
index e3242fadd..abb13702e 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowPackageManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowPackageManagerTest.java
@@ -4326,6 +4326,25 @@ public class ShadowPackageManagerTest {
assertThat(packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)).isFalse();
}
+ @Test
+ @Config(minSdk = S)
+ public void getProperty() throws NameNotFoundException {
+ assertThrows(
+ NameNotFoundException.class,
+ () ->
+ packageManager.getProperty(
+ "myproperty", RuntimeEnvironment.getApplication().getPackageName()));
+ }
+
+ @Test
+ @Config(minSdk = S)
+ public void getProperty_component() throws NameNotFoundException {
+ final ComponentName componentName =
+ new ComponentName(RuntimeEnvironment.getApplication().getPackageName(), "mycomponentname");
+ assertThrows(
+ NameNotFoundException.class, () -> packageManager.getProperty("myproperty", componentName));
+ }
+
public String[] setPackagesSuspended(
String[] packageNames,
boolean suspended,
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowScrollViewTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowScrollViewTest.java
index 1b0970be5..536e6dc21 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowScrollViewTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowScrollViewTest.java
@@ -36,7 +36,7 @@ public class ShadowScrollViewTest {
@Test
public void realCode_shouldSmoothScrollTo() {
try {
- System.setProperty("robolectric.nativeruntime.enableGraphics", "true");
+ System.setProperty("robolectric.useRealScrolling", "true");
Activity activity = Robolectric.setupActivity(Activity.class);
ScrollView scrollView = new ScrollView(activity);
View view = new View(activity);
@@ -51,14 +51,14 @@ public class ShadowScrollViewTest {
assertEquals(7, scrollView.getScrollX());
assertEquals(6, scrollView.getScrollY());
} finally {
- System.clearProperty("robolectric.nativeruntime.enableGraphics");
+ System.clearProperty("robolectric.useRealScrolling");
}
}
@Test
public void realCode_shouldSmoothScrollBy() {
try {
- System.setProperty("robolectric.nativeruntime.enableGraphics", "true");
+ System.setProperty("robolectric.useRealScrolling", "true");
Activity activity = Robolectric.setupActivity(Activity.class);
ScrollView scrollView = new ScrollView(activity);
View view = new View(activity);
@@ -74,7 +74,7 @@ public class ShadowScrollViewTest {
assertEquals(17, scrollView.getScrollX());
assertEquals(26, scrollView.getScrollY());
} finally {
- System.clearProperty("robolectric.nativeruntime.enableGraphics");
+ System.clearProperty("robolectric.useRealScrolling");
}
}
}
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowServiceManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowServiceManagerTest.java
index cf1bf5b9c..68778c584 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowServiceManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowServiceManagerTest.java
@@ -4,6 +4,7 @@ import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.SECONDS;
import android.content.Context;
+import android.os.Build.VERSION_CODES;
import android.os.IBinder;
import android.os.ServiceManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -13,17 +14,24 @@ import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
/** Tests for {@link ShadowServiceManager}. */
@RunWith(AndroidJUnit4.class)
public final class ShadowServiceManagerTest {
-
+
@Test
public void getService_available_shouldReturnNonNull() {
assertThat(ServiceManager.getService(Context.INPUT_METHOD_SERVICE)).isNotNull();
}
@Test
+ @Config(sdk = VERSION_CODES.S)
+ public void getSensorPrivacyService_notNull() {
+ assertThat(ServiceManager.getService(Context.SENSOR_PRIVACY_SERVICE)).isNotNull();
+ }
+
+ @Test
public void getService_unavailableService_shouldReturnNull() {
ShadowServiceManager.setServiceAvailability(Context.INPUT_METHOD_SERVICE, false);
assertThat(ServiceManager.getService(Context.INPUT_METHOD_SERVICE)).isNull();
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowVibratorTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowVibratorTest.java
index b8b57851c..bc93daac0 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowVibratorTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowVibratorTest.java
@@ -172,6 +172,48 @@ public class ShadowVibratorTest {
new PrimitiveSegment(EFFECT_CLICK, /* scale= */ 1f, /* delay= */ 2150)));
}
+ @Config(minSdk = S)
+ @Test
+ public void getPrimitiveSegmentsInPrimitiveEffects_composeOnce_shouldReturnSameFragment() {
+ vibrator.vibrate(
+ VibrationEffect.startComposition()
+ .addPrimitive(EFFECT_CLICK, /* scale= */ 0.5f, /* delay= */ 20)
+ .addPrimitive(EFFECT_CLICK, /* scale= */ 0.7f, /* delay= */ 50)
+ .addPrimitive(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150)
+ .compose());
+
+ assertThat(shadowOf(vibrator).getPrimitiveSegmentsInPrimitiveEffects())
+ .isEqualTo(
+ ImmutableList.of(
+ new PrimitiveEffect(EFFECT_CLICK, /* scale= */ 0.5f, /* delay= */ 20),
+ new PrimitiveEffect(EFFECT_CLICK, /* scale= */ 0.7f, /* delay= */ 50),
+ new PrimitiveEffect(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150)));
+ }
+
+ @Config(minSdk = S)
+ @Test
+ public void getPrimitiveSegmentsInPrimitiveEffects_composeTwice_shouldReturnTheLastComposition() {
+ vibrator.vibrate(
+ VibrationEffect.startComposition()
+ .addPrimitive(EFFECT_CLICK, /* scale= */ 0.5f, /* delay= */ 20)
+ .addPrimitive(EFFECT_CLICK, /* scale= */ 0.7f, /* delay= */ 50)
+ .addPrimitive(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150)
+ .compose());
+ vibrator.vibrate(
+ VibrationEffect.startComposition()
+ .addPrimitive(EFFECT_CLICK, /* scale= */ 0.4f, /* delay= */ 120)
+ .addPrimitive(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150)
+ .addPrimitive(EFFECT_CLICK, /* scale= */ 1f, /* delay= */ 2150)
+ .compose());
+
+ assertThat(shadowOf(vibrator).getPrimitiveSegmentsInPrimitiveEffects())
+ .isEqualTo(
+ ImmutableList.of(
+ new PrimitiveEffect(EFFECT_CLICK, /* scale= */ 0.4f, /* delay= */ 120),
+ new PrimitiveEffect(EFFECT_CLICK, /* scale= */ 0.9f, /* delay= */ 150),
+ new PrimitiveEffect(EFFECT_CLICK, /* scale= */ 1f, /* delay= */ 2150)));
+ }
+
@Config(minSdk = R)
@Test
public void areAllPrimitivesSupported_oneSupportedPrimitive_shouldReturnTrue() {
diff --git a/robolectric/src/test/java/org/robolectric/shadows/ShadowWifiManagerTest.java b/robolectric/src/test/java/org/robolectric/shadows/ShadowWifiManagerTest.java
index 299a85333..945c713b2 100644
--- a/robolectric/src/test/java/org/robolectric/shadows/ShadowWifiManagerTest.java
+++ b/robolectric/src/test/java/org/robolectric/shadows/ShadowWifiManagerTest.java
@@ -7,6 +7,7 @@ import static android.os.Build.VERSION_CODES.R;
import static android.os.Build.VERSION_CODES.S;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -14,6 +15,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.robolectric.Shadows.shadowOf;
+import android.app.admin.DeviceAdminService;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
@@ -250,6 +254,69 @@ public class ShadowWifiManagerTest {
}
@Test
+ @Config(minSdk = S)
+ public void
+ getCallerConfiguredNetworks_noAccessWifiStatePermission_shouldThrowSecurityException() {
+ shadowOf(wifiManager).setAccessWifiStatePermission(false);
+
+ assertThrows(SecurityException.class, () -> wifiManager.getCallerConfiguredNetworks());
+ }
+
+ @Test
+ @Config(minSdk = S)
+ public void getCallerConfiguredNetworks_noNetworksConfigured_returnsEmptyList() {
+ assertThat(wifiManager.getCallerConfiguredNetworks()).isEmpty();
+ }
+
+ @Test
+ @Config(minSdk = S)
+ public void getCallerConfiguredNetworks_networksAddedAndRemoved_returnsConfiguredNetworks() {
+ WifiConfiguration wifiConfiguration = new WifiConfiguration();
+ wifiManager.addNetwork(wifiConfiguration);
+
+ assertThat(wifiManager.getCallerConfiguredNetworks()).hasSize(1);
+
+ wifiManager.removeNetwork(0);
+
+ assertThat(wifiManager.getCallerConfiguredNetworks()).isEmpty();
+ }
+
+ @Test
+ @Config(minSdk = S)
+ public void
+ removeNonCallerConfiguredNetworks_noChangeWifiStatePermission_shouldThrowSecurityException() {
+ setDeviceOwner();
+ shadowOf(wifiManager).setChangeWifiStatePermission(false);
+
+ assertThrows(SecurityException.class, () -> wifiManager.removeNonCallerConfiguredNetworks());
+ }
+
+ @Test
+ @Config(minSdk = S)
+ public void removeNonCallerConfiguredNetworks_notDeviceOwner_shouldThrowSecurityException() {
+ assertThrows(SecurityException.class, () -> wifiManager.removeNonCallerConfiguredNetworks());
+ }
+
+ @Test
+ @Config(minSdk = S)
+ public void removeNonCallerConfiguredNetworks_noConfiguredNetworks_returnsFalse() {
+ setDeviceOwner();
+
+ assertThat(wifiManager.removeNonCallerConfiguredNetworks()).isFalse();
+ }
+
+ @Test
+ @Config(minSdk = S)
+ public void removeNonCallerConfiguredNetworks_hasConfiguredNetworks_removesConfiguredNetworks() {
+ setDeviceOwner();
+ wifiManager.addNetwork(new WifiConfiguration());
+ wifiManager.addNetwork(new WifiConfiguration());
+
+ assertThat(wifiManager.removeNonCallerConfiguredNetworks()).isTrue();
+ assertThat(wifiManager.getConfiguredNetworks()).isEmpty();
+ }
+
+ @Test
@Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
public void getPrivilegedConfiguredNetworks_shouldReturnConfiguredNetworks() {
WifiConfiguration wifiConfiguration = new WifiConfiguration();
@@ -764,4 +831,14 @@ public class ShadowWifiManagerTest {
assertThat(shadowOf(wifiManager).getWifiApConfiguration().SSID).isEqualTo("foo");
}
+
+ private void setDeviceOwner() {
+ shadowOf(
+ (DevicePolicyManager)
+ ApplicationProvider.getApplicationContext()
+ .getSystemService(Context.DEVICE_POLICY_SERVICE))
+ .setDeviceOwner(
+ new ComponentName(
+ ApplicationProvider.getApplicationContext(), DeviceAdminService.class));
+ }
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowApplicationPackageManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowApplicationPackageManager.java
index ae8dfb894..8c3193381 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowApplicationPackageManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowApplicationPackageManager.java
@@ -2236,6 +2236,22 @@ public class ShadowApplicationPackageManager extends ShadowPackageManager {
return reflector(ReflectorApplicationPackageManager.class, realObject).getContext();
}
+ /** Stub that will always throw. */
+ @Implementation(minSdk = S)
+ protected Object /* PackageManager.Property */ getProperty(
+ String propertyName, String packageName) throws NameNotFoundException {
+ // TODO: in future read this value from parsed manifest
+ throw new NameNotFoundException("unsupported");
+ }
+
+ /** Stub that will always throw. */
+ @Implementation(minSdk = S)
+ protected Object /* PackageManager.Property */ getProperty(
+ String propertyName, ComponentName name) throws NameNotFoundException {
+ // TODO: in future read this value from parsed manifest
+ throw new NameNotFoundException("unsupported");
+ }
+
/** Reflector interface for {@link ApplicationPackageManager}'s internals. */
@ForType(ApplicationPackageManager.class)
private interface ReflectorApplicationPackageManager {
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowContentResolver.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowContentResolver.java
index 0b86844e6..69b2dae92 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowContentResolver.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowContentResolver.java
@@ -201,7 +201,7 @@ public class ShadowContentResolver {
}
@Implementation
- protected final OutputStream openOutputStream(final Uri uri) {
+ protected final OutputStream openOutputStream(final Uri uri) throws FileNotFoundException {
Supplier<OutputStream> supplier = outputStreamMap.get(uri);
if (supplier != null) {
OutputStream outputStream = supplier.get();
@@ -209,15 +209,23 @@ public class ShadowContentResolver {
return outputStream;
}
}
- return new OutputStream() {
- @Override
- public void write(int arg0) throws IOException {}
-
- @Override
- public String toString() {
- return "outputstream for " + uri;
- }
- };
+ try {
+ return reflector(
+ org.robolectric.shadows.ShadowContentResolver.ContentResolverReflector.class,
+ realContentResolver)
+ .openOutputStream(uri);
+ } catch (SecurityException | FileNotFoundException e) {
+ // This is legacy behavior is only supported because existing users require it.
+ return new OutputStream() {
+ @Override
+ public void write(int arg0) throws IOException {}
+
+ @Override
+ public String toString() {
+ return "outputstream for " + uri;
+ }
+ };
+ }
}
/**
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDevicePolicyManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDevicePolicyManager.java
index dbaa35301..422f96f3b 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDevicePolicyManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDevicePolicyManager.java
@@ -89,6 +89,7 @@ public class ShadowDevicePolicyManager {
private Map<String, Bundle> applicationRestrictionsMap = new HashMap<>();
private CharSequence organizationName;
private int organizationColor;
+ private boolean isAutoTimeEnabled;
private boolean isAutoTimeRequired;
private boolean isAutoTimeZoneEnabled;
private String timeZone;
@@ -602,6 +603,18 @@ public class ShadowDevicePolicyManager {
return organizationColor;
}
+ @Implementation(minSdk = R)
+ protected void setAutoTimeEnabled(ComponentName admin, boolean enabled) {
+ enforceDeviceOwnerOrProfileOwner(admin);
+ isAutoTimeEnabled = enabled;
+ }
+
+ @Implementation(minSdk = R)
+ protected boolean getAutoTimeEnabled(ComponentName admin) {
+ enforceDeviceOwnerOrProfileOwner(admin);
+ return isAutoTimeEnabled;
+ }
+
@Implementation(minSdk = LOLLIPOP)
protected void setAutoTimeRequired(ComponentName admin, boolean required) {
enforceDeviceOwnerOrProfileOwner(admin);
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowEGL14.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowEGL14.java
index 475607a74..4f488b571 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowEGL14.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowEGL14.java
@@ -84,6 +84,11 @@ public class ShadowEGL14 {
}
@Implementation
+ protected static EGLContext eglGetCurrentContext() {
+ return createEglContext(3);
+ }
+
+ @Implementation
protected static boolean eglMakeCurrent(
EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) {
return true;
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java
index e76b7973c..da5070a7a 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java
@@ -1011,10 +1011,20 @@ public class ShadowInstrumentation {
}
int checkPermission(String permission, int pid, int uid) {
- Set<String> grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair(pid, uid));
- return grantedPermissionsForPidUid != null && grantedPermissionsForPidUid.contains(permission)
- ? PERMISSION_GRANTED
- : PERMISSION_DENIED;
+ if (pid == -1) {
+ for (Map.Entry<Pair<Integer, Integer>, Set<String>> entry :
+ grantedPermissionsMap.entrySet()) {
+ if (entry.getKey().second == uid && entry.getValue().contains(permission)) {
+ return PERMISSION_GRANTED;
+ }
+ }
+ return PERMISSION_DENIED;
+ } else {
+ Set<String> grantedPermissionsForPidUid = grantedPermissionsMap.get(new Pair(pid, uid));
+ return grantedPermissionsForPidUid != null && grantedPermissionsForPidUid.contains(permission)
+ ? PERMISSION_GRANTED
+ : PERMISSION_DENIED;
+ }
}
void grantPermissions(String... permissionNames) {
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowScrollView.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowScrollView.java
index a96c00ecd..d7126d2e8 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowScrollView.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowScrollView.java
@@ -16,7 +16,7 @@ public class ShadowScrollView extends ShadowViewGroup {
@Implementation
protected void smoothScrollTo(int x, int y) {
- if (useRealGraphics()) {
+ if (useRealScrolling()) {
reflector(ScrollViewReflector.class, realScrollView).smoothScrollTo(x, y);
} else {
scrollTo(x, y);
@@ -25,7 +25,7 @@ public class ShadowScrollView extends ShadowViewGroup {
@Implementation
protected void smoothScrollBy(int x, int y) {
- if (useRealGraphics()) {
+ if (useRealScrolling()) {
reflector(ScrollViewReflector.class, realScrollView).smoothScrollBy(x, y);
} else {
scrollBy(x, y);
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowServiceManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowServiceManager.java
index aa51c52b8..5c9c05fa0 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowServiceManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowServiceManager.java
@@ -42,6 +42,7 @@ import android.content.integrity.IAppIntegrityManager;
import android.content.pm.ICrossProfileApps;
import android.content.pm.IShortcutService;
import android.content.rollback.IRollbackManager;
+import android.hardware.ISensorPrivacyManager;
import android.hardware.biometrics.IAuthService;
import android.hardware.biometrics.IBiometricService;
import android.hardware.fingerprint.IFingerprintService;
@@ -205,6 +206,7 @@ public class ShadowServiceManager {
addBinderService(Context.UWB_SERVICE, IUwbAdapter.class);
addBinderService(Context.VCN_MANAGEMENT_SERVICE, IVcnManagementService.class);
addBinderService(Context.TRANSLATION_MANAGER_SERVICE, ITranslationManager.class);
+ addBinderService(Context.SENSOR_PRIVACY_SERVICE, ISensorPrivacyManager.class);
}
if (RuntimeEnvironment.getApiLevel() >= TIRAMISU) {
addBinderService(Context.AMBIENT_CONTEXT_SERVICE, IAmbientContextManager.class);
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVibrator.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVibrator.java
index b66a0a414..df299c795 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVibrator.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowVibrator.java
@@ -6,15 +6,18 @@ import android.media.AudioAttributes;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
+import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.VibrationEffectSegment;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
+import org.robolectric.util.ReflectionHelpers;
@Implements(Vibrator.class)
public class ShadowVibrator {
@@ -86,6 +89,20 @@ public class ShadowVibrator {
return vibrationEffectSegments;
}
+ /** Returns the last list of {@link PrimitiveSegment} vibrations in {@link PrimitiveEffect}. */
+ @SuppressWarnings("JdkCollectors") // toImmutableList is only supported in Java 8+.
+ public List<PrimitiveEffect> getPrimitiveSegmentsInPrimitiveEffects() {
+ return vibrationEffectSegments.stream()
+ .filter(segment -> segment instanceof PrimitiveSegment)
+ .map(
+ segment ->
+ new PrimitiveEffect(
+ ReflectionHelpers.getField(segment, "mPrimitiveId"),
+ ReflectionHelpers.getField(segment, "mScale"),
+ ReflectionHelpers.getField(segment, "mDelay")))
+ .collect(Collectors.toList());
+ }
+
/** Returns the last list of {@link PrimitiveEffect}. */
@Nullable
public List<PrimitiveEffect> getPrimitiveEffects() {
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowView.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowView.java
index 70cb36999..1473eb853 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowView.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowView.java
@@ -575,7 +575,7 @@ public class ShadowView {
@Implementation
protected void scrollTo(int x, int y) {
- if (useRealGraphics()) {
+ if (useRealScrolling()) {
reflector(_View_.class, realView).scrollTo(x, y);
} else {
reflector(_View_.class, realView)
@@ -588,7 +588,7 @@ public class ShadowView {
@Implementation
protected void scrollBy(int x, int y) {
- if (useRealGraphics()) {
+ if (useRealScrolling()) {
reflector(_View_.class, realView).scrollBy(x, y);
} else {
scrollTo(getScrollX() + x, getScrollY() + y);
@@ -597,7 +597,7 @@ public class ShadowView {
@Implementation
protected int getScrollX() {
- if (useRealGraphics()) {
+ if (useRealScrolling()) {
return reflector(_View_.class, realView).getScrollX();
} else {
return scrollToCoordinates != null ? scrollToCoordinates.x : 0;
@@ -606,7 +606,7 @@ public class ShadowView {
@Implementation
protected int getScrollY() {
- if (useRealGraphics()) {
+ if (useRealScrolling()) {
return reflector(_View_.class, realView).getScrollY();
} else {
return scrollToCoordinates != null ? scrollToCoordinates.y : 0;
@@ -615,7 +615,7 @@ public class ShadowView {
@Implementation
protected void setScrollX(int scrollX) {
- if (useRealGraphics()) {
+ if (useRealScrolling()) {
reflector(_View_.class, realView).setScrollX(scrollX);
} else {
scrollTo(scrollX, scrollToCoordinates.y);
@@ -624,7 +624,7 @@ public class ShadowView {
@Implementation
protected void setScrollY(int scrollY) {
- if (useRealGraphics()) {
+ if (useRealScrolling()) {
reflector(_View_.class, realView).setScrollY(scrollY);
} else {
scrollTo(scrollToCoordinates.x, scrollY);
@@ -1057,4 +1057,15 @@ public class ShadowView {
static boolean useRealGraphics() {
return Boolean.getBoolean("robolectric.nativeruntime.enableGraphics");
}
+
+ /**
+ * Currently the default View scrolling implementation is broken and low-fidelty. For instance,
+ * even if a View has no children, Robolectric will still happily set the scroll position of a
+ * View. Long-term we want to eliminate this broken behavior, but in the mean time the real
+ * scrolling behavior is enabled when native graphics are enabled, or when a system property is
+ * set.
+ */
+ static boolean useRealScrolling() {
+ return useRealGraphics() || Boolean.getBoolean("robolectric.useRealScrolling");
+ }
}
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewRootImpl.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewRootImpl.java
index cfe2bc7b6..2dd2e9a9e 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewRootImpl.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowViewRootImpl.java
@@ -123,7 +123,30 @@ public class ShadowViewRootImpl {
}
public void callDispatchResized() {
- if (RuntimeEnvironment.getApiLevel() > Build.VERSION_CODES.S_V2) {
+ if (RuntimeEnvironment.getApiLevel() > VERSION_CODES.TIRAMISU) {
+ Display display = getDisplay();
+ Rect frame = new Rect();
+ display.getRectSize(frame);
+
+ ClientWindowFrames frames = new ClientWindowFrames();
+ // set the final field
+ ReflectionHelpers.setField(frames, "frame", frame);
+
+ ReflectionHelpers.callInstanceMethod(
+ ViewRootImpl.class,
+ realObject,
+ "dispatchResized",
+ ClassParameter.from(ClientWindowFrames.class, frames),
+ ClassParameter.from(boolean.class, true), /* reportDraw */
+ ClassParameter.from(
+ MergedConfiguration.class, new MergedConfiguration()), /* mergedConfiguration */
+ ClassParameter.from(InsetsState.class, new InsetsState()), /* insetsState */
+ ClassParameter.from(boolean.class, false), /* forceLayout */
+ ClassParameter.from(boolean.class, false), /* alwaysConsumeSystemBars */
+ ClassParameter.from(int.class, 0), /* displayId */
+ ClassParameter.from(int.class, 0), /* syncSeqId */
+ ClassParameter.from(boolean.class, false) /* dragResizing */);
+ } else if (RuntimeEnvironment.getApiLevel() > Build.VERSION_CODES.S_V2) {
Display display = getDisplay();
Rect frame = new Rect();
display.getRectSize(frame);
diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWifiManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWifiManager.java
index 561ed907f..78de3d1d7 100644
--- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWifiManager.java
+++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowWifiManager.java
@@ -5,7 +5,9 @@ import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
@@ -49,6 +51,7 @@ public class ShadowWifiManager {
private static float sSignalLevelInPercent = 1f;
private boolean accessWifiStatePermission = true;
+ private boolean changeWifiStatePermission = true;
private int wifiState = WifiManager.WIFI_STATE_ENABLED;
private boolean wasSaved = false;
private WifiInfo wifiInfo;
@@ -140,6 +143,19 @@ public class ShadowWifiManager {
return scanResults;
}
+ /**
+ * The original implementation allows this to be called by the Device Owner (DO), Profile Owner
+ * (PO), callers with carrier privilege and system apps, but this shadow can be called by all apps
+ * carrying the ACCESS_WIFI_STATE permission.
+ *
+ * <p>This shadow is a wrapper for getConfiguredNetworks() and does not actually check the caller.
+ */
+ @Implementation(minSdk = S)
+ protected List<WifiConfiguration> getCallerConfiguredNetworks() {
+ checkAccessWifiStatePermission();
+ return getConfiguredNetworks();
+ }
+
@Implementation
protected List<WifiConfiguration> getConfiguredNetworks() {
final ArrayList<WifiConfiguration> wifiConfigurations = new ArrayList<>();
@@ -172,6 +188,21 @@ public class ShadowWifiManager {
}
/**
+ * Removes all configured networks regardless of the app that created the network. Can only be
+ * called by a Device Owner (DO) app.
+ *
+ * @return {@code true} if at least one network is removed, {@code false} otherwise
+ */
+ @Implementation(minSdk = S)
+ protected boolean removeNonCallerConfiguredNetworks() {
+ checkChangeWifiStatePermission();
+ checkDeviceOwner();
+ int previousSize = networkIdToConfiguredNetworks.size();
+ networkIdToConfiguredNetworks.clear();
+ return networkIdToConfiguredNetworks.size() < previousSize;
+ }
+
+ /**
* Adds or updates a network which can later be retrieved with {@link #getWifiConfiguration(int)}
* method. A null {@param config}, or one with a networkId less than 0, or a networkId that had
* its updatePermission removed using the {@link #setUpdateNetworkPermission(int, boolean)} will
@@ -353,6 +384,10 @@ public class ShadowWifiManager {
this.accessWifiStatePermission = accessWifiStatePermission;
}
+ public void setChangeWifiStatePermission(boolean changeWifiStatePermission) {
+ this.changeWifiStatePermission = changeWifiStatePermission;
+ }
+
/**
* Prevents a networkId from being updated using the {@link updateNetwork(WifiConfiguration)}
* method. This is to simulate the case where a separate application creates a network, and the
@@ -397,7 +432,21 @@ public class ShadowWifiManager {
private void checkAccessWifiStatePermission() {
if (!accessWifiStatePermission) {
- throw new SecurityException();
+ throw new SecurityException("Caller does not hold ACCESS_WIFI_STATE permission");
+ }
+ }
+
+ private void checkChangeWifiStatePermission() {
+ if (!changeWifiStatePermission) {
+ throw new SecurityException("Caller does not hold CHANGE_WIFI_STATE permission");
+ }
+ }
+
+ private void checkDeviceOwner() {
+ if (!getContext()
+ .getSystemService(DevicePolicyManager.class)
+ .isDeviceOwnerApp(getContext().getPackageName())) {
+ throw new SecurityException("Caller is not device owner");
}
}