Loading core/java/android/app/UiAutomation.java +13 −0 Original line number Diff line number Diff line Loading @@ -117,6 +117,8 @@ import java.util.concurrent.TimeoutException; * interacting with another application whose behavior depends on that setting. * </p> */ @android.ravenwood.annotation.RavenwoodKeepPartialClass @android.ravenwood.annotation.RavenwoodRedirectionClass("UiAutomation_ravenwood") public final class UiAutomation { private static final String LOG_TAG = UiAutomation.class.getSimpleName(); Loading Loading @@ -284,6 +286,7 @@ public final class UiAutomation { * * @hide */ @android.ravenwood.annotation.RavenwoodKeep public UiAutomation(Context context, IUiAutomationConnection connection) { this(getDisplayId(context), context.getMainLooper(), connection); } Loading @@ -307,6 +310,7 @@ public final class UiAutomation { Log.w(LOG_TAG, "Created with deprecatead constructor, assumes DEFAULT_DISPLAY"); } @android.ravenwood.annotation.RavenwoodKeep private UiAutomation(int displayId, Looper looper, IUiAutomationConnection connection) { Preconditions.checkArgument(looper != null, "Looper cannot be null!"); Preconditions.checkArgument(connection != null, "Connection cannot be null!"); Loading Loading @@ -586,6 +590,7 @@ public final class UiAutomation { * @see #adoptShellPermissionIdentity(String...) * @see #dropShellPermissionIdentity() */ @android.ravenwood.annotation.RavenwoodRedirect public void adoptShellPermissionIdentity() { try { // Calling out without a lock held. Loading @@ -611,6 +616,7 @@ public final class UiAutomation { * @see #adoptShellPermissionIdentity() * @see #dropShellPermissionIdentity() */ @android.ravenwood.annotation.RavenwoodRedirect public void adoptShellPermissionIdentity(@Nullable String... permissions) { try { // Calling out without a lock held. Loading @@ -627,6 +633,7 @@ public final class UiAutomation { * * @see #adoptShellPermissionIdentity() */ @android.ravenwood.annotation.RavenwoodRedirect public void dropShellPermissionIdentity() { try { // Calling out without a lock held. Loading @@ -645,6 +652,7 @@ public final class UiAutomation { */ @TestApi @NonNull @android.ravenwood.annotation.RavenwoodRedirect public Set<String> getAdoptedShellPermissions() { try { final List<String> permissions = mUiAutomationConnection.getAdoptedShellPermissions(); Loading Loading @@ -1884,6 +1892,7 @@ public final class UiAutomation { * <p><b>NOTE: </b> must be a static method because it's called from a constructor to call * another one. */ @android.ravenwood.annotation.RavenwoodReplace(reason = "Always use DEFAULT_DISPLAY") private static int getDisplayId(Context context) { Preconditions.checkArgument(context != null, "Context cannot be null!"); Loading Loading @@ -1917,6 +1926,10 @@ public final class UiAutomation { return userDisplayId; } private static int getDisplayId$ravenwood(Context context) { return DEFAULT_DISPLAY; } private static int getMainDisplayIdAssignedToUser(Context context, UserManager userManager) { if (!userManager.isUserVisible()) { // Should also not happen, but ... Loading core/java/android/app/UiAutomation_ravenwood.java 0 → 100644 +50 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.annotation.Nullable; import java.util.Collections; import java.util.Set; public class UiAutomation_ravenwood { private static Set<String> sAdoptedPermissions = Collections.emptySet(); public static void reset() { sAdoptedPermissions = Collections.emptySet(); } public static void adoptShellPermissionIdentity(UiAutomation self) { sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; } public static void adoptShellPermissionIdentity(UiAutomation self, @Nullable String... permissions) { if (permissions == null) { sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; } else { sAdoptedPermissions = Set.of(permissions); } } public static void dropShellPermissionIdentity(UiAutomation self) { sAdoptedPermissions = Collections.emptySet(); } public static Set<String> getAdoptedShellPermissions(UiAutomation self) { return sAdoptedPermissions; } } ravenwood/Android.bp +0 −1 Original line number Diff line number Diff line Loading @@ -186,7 +186,6 @@ java_library { "ravenwood-helper-framework-runtime", "ravenwood-helper-libcore-runtime", "hoststubgen-helper-runtime.ravenwood", "mockito-ravenwood-prebuilt", ], visibility: [":__subpackages__"], jarjar_rules: ":ravenwood-services-jarjar-rules", Loading ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +6 −43 Original line number Diff line number Diff line Loading @@ -30,17 +30,16 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityThread_ravenwood; import android.app.AppCompatCallbacks; import android.app.IUiAutomationConnection; import android.app.Instrumentation; import android.app.ResourcesManager; import android.app.UiAutomation; import android.app.UiAutomation_ravenwood; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.res.Resources; Loading Loading @@ -83,14 +82,12 @@ import org.junit.runner.Description; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; Loading Loading @@ -198,7 +195,6 @@ public class RavenwoodRuntimeEnvironmentController { /** Map from path -> resources. */ private static final HashMap<File, Resources> sCachedResources = new HashMap<>(); private static Set<String> sAdoptedPermissions = Collections.emptySet(); private static final Object sInitializationLock = new Object(); Loading Loading @@ -416,6 +412,8 @@ public class RavenwoodRuntimeEnvironmentController { var systemServerContext = new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader); var uiAutomation = new UiAutomation(sInstContext, new IUiAutomationConnection.Default()); var instArgs = Bundle.EMPTY; RavenwoodUtils.runOnMainThreadSync(() -> { var instClassName = withDefault(sInstrumentationClass, DEFAULT_INSTRUMENTATION_CLASS); Loading @@ -432,7 +430,7 @@ public class RavenwoodRuntimeEnvironmentController { } } initInstrumentation(); sInstrumentation.basicInit(sInstContext, sTargetContext, uiAutomation); sInstrumentation.onCreate(instArgs); }); InstrumentationRegistry.registerInstance(sInstrumentation, instArgs); Loading Loading @@ -473,19 +471,12 @@ public class RavenwoodRuntimeEnvironmentController { } } private static void initInstrumentation() { // We need to recreate the mocks for each test class, because sometimes tests // will call Mockito.framework().clearInlineMocks() after execution. sInstrumentation.basicInit(sInstContext, sTargetContext, createMockUiAutomation()); } /** * Partially reset and initialize before each test class invocation */ public static void initForRunner() { initInstrumentation(); // Reset some global state UiAutomation_ravenwood.reset(); Process_ravenwood.reset(); DeviceConfig_host.reset(); Binder.restoreCallingIdentity(sCallingIdentity); Loading Loading @@ -731,34 +722,6 @@ public class RavenwoodRuntimeEnvironmentController { () -> Class.forName("org.mockito.Matchers")); } static <T> T makeDefaultThrowMock(Class<T> clazz) { return mock(clazz, inv -> { throw new RavenwoodUnsupportedApiException(); }); } // TODO: use the real UiAutomation class instead of a mock private static UiAutomation createMockUiAutomation() { sAdoptedPermissions = Collections.emptySet(); var mock = makeDefaultThrowMock(UiAutomation.class); doAnswer(inv -> { sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; return null; }).when(mock).adoptShellPermissionIdentity(); doAnswer(inv -> { if (inv.getArgument(0) == null) { sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; } else { sAdoptedPermissions = (Set) Set.of(inv.getArguments()); } return null; }).when(mock).adoptShellPermissionIdentity(any()); doAnswer(inv -> { sAdoptedPermissions = Collections.emptySet(); return null; }).when(mock).dropShellPermissionIdentity(); doAnswer(inv -> sAdoptedPermissions).when(mock).getAdoptedShellPermissions(); return mock; } private static void dumpCommandLineArgs() { Log.i(TAG, "JVM arguments:"); Loading ravenwood/texts/ravenwood-annotation-allowed-classes.txt +1 −0 Original line number Diff line number Diff line Loading @@ -315,6 +315,7 @@ android.app.ComponentOptions android.app.Instrumentation android.app.LocaleConfig android.app.ResourcesManager android.app.UiAutomation android.app.WindowConfiguration android.metrics.LogMaker Loading Loading
core/java/android/app/UiAutomation.java +13 −0 Original line number Diff line number Diff line Loading @@ -117,6 +117,8 @@ import java.util.concurrent.TimeoutException; * interacting with another application whose behavior depends on that setting. * </p> */ @android.ravenwood.annotation.RavenwoodKeepPartialClass @android.ravenwood.annotation.RavenwoodRedirectionClass("UiAutomation_ravenwood") public final class UiAutomation { private static final String LOG_TAG = UiAutomation.class.getSimpleName(); Loading Loading @@ -284,6 +286,7 @@ public final class UiAutomation { * * @hide */ @android.ravenwood.annotation.RavenwoodKeep public UiAutomation(Context context, IUiAutomationConnection connection) { this(getDisplayId(context), context.getMainLooper(), connection); } Loading @@ -307,6 +310,7 @@ public final class UiAutomation { Log.w(LOG_TAG, "Created with deprecatead constructor, assumes DEFAULT_DISPLAY"); } @android.ravenwood.annotation.RavenwoodKeep private UiAutomation(int displayId, Looper looper, IUiAutomationConnection connection) { Preconditions.checkArgument(looper != null, "Looper cannot be null!"); Preconditions.checkArgument(connection != null, "Connection cannot be null!"); Loading Loading @@ -586,6 +590,7 @@ public final class UiAutomation { * @see #adoptShellPermissionIdentity(String...) * @see #dropShellPermissionIdentity() */ @android.ravenwood.annotation.RavenwoodRedirect public void adoptShellPermissionIdentity() { try { // Calling out without a lock held. Loading @@ -611,6 +616,7 @@ public final class UiAutomation { * @see #adoptShellPermissionIdentity() * @see #dropShellPermissionIdentity() */ @android.ravenwood.annotation.RavenwoodRedirect public void adoptShellPermissionIdentity(@Nullable String... permissions) { try { // Calling out without a lock held. Loading @@ -627,6 +633,7 @@ public final class UiAutomation { * * @see #adoptShellPermissionIdentity() */ @android.ravenwood.annotation.RavenwoodRedirect public void dropShellPermissionIdentity() { try { // Calling out without a lock held. Loading @@ -645,6 +652,7 @@ public final class UiAutomation { */ @TestApi @NonNull @android.ravenwood.annotation.RavenwoodRedirect public Set<String> getAdoptedShellPermissions() { try { final List<String> permissions = mUiAutomationConnection.getAdoptedShellPermissions(); Loading Loading @@ -1884,6 +1892,7 @@ public final class UiAutomation { * <p><b>NOTE: </b> must be a static method because it's called from a constructor to call * another one. */ @android.ravenwood.annotation.RavenwoodReplace(reason = "Always use DEFAULT_DISPLAY") private static int getDisplayId(Context context) { Preconditions.checkArgument(context != null, "Context cannot be null!"); Loading Loading @@ -1917,6 +1926,10 @@ public final class UiAutomation { return userDisplayId; } private static int getDisplayId$ravenwood(Context context) { return DEFAULT_DISPLAY; } private static int getMainDisplayIdAssignedToUser(Context context, UserManager userManager) { if (!userManager.isUserVisible()) { // Should also not happen, but ... Loading
core/java/android/app/UiAutomation_ravenwood.java 0 → 100644 +50 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app; import android.annotation.Nullable; import java.util.Collections; import java.util.Set; public class UiAutomation_ravenwood { private static Set<String> sAdoptedPermissions = Collections.emptySet(); public static void reset() { sAdoptedPermissions = Collections.emptySet(); } public static void adoptShellPermissionIdentity(UiAutomation self) { sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; } public static void adoptShellPermissionIdentity(UiAutomation self, @Nullable String... permissions) { if (permissions == null) { sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; } else { sAdoptedPermissions = Set.of(permissions); } } public static void dropShellPermissionIdentity(UiAutomation self) { sAdoptedPermissions = Collections.emptySet(); } public static Set<String> getAdoptedShellPermissions(UiAutomation self) { return sAdoptedPermissions; } }
ravenwood/Android.bp +0 −1 Original line number Diff line number Diff line Loading @@ -186,7 +186,6 @@ java_library { "ravenwood-helper-framework-runtime", "ravenwood-helper-libcore-runtime", "hoststubgen-helper-runtime.ravenwood", "mockito-ravenwood-prebuilt", ], visibility: [":__subpackages__"], jarjar_rules: ":ravenwood-services-jarjar-rules", Loading
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +6 −43 Original line number Diff line number Diff line Loading @@ -30,17 +30,16 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityThread_ravenwood; import android.app.AppCompatCallbacks; import android.app.IUiAutomationConnection; import android.app.Instrumentation; import android.app.ResourcesManager; import android.app.UiAutomation; import android.app.UiAutomation_ravenwood; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.res.Resources; Loading Loading @@ -83,14 +82,12 @@ import org.junit.runner.Description; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Random; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; Loading Loading @@ -198,7 +195,6 @@ public class RavenwoodRuntimeEnvironmentController { /** Map from path -> resources. */ private static final HashMap<File, Resources> sCachedResources = new HashMap<>(); private static Set<String> sAdoptedPermissions = Collections.emptySet(); private static final Object sInitializationLock = new Object(); Loading Loading @@ -416,6 +412,8 @@ public class RavenwoodRuntimeEnvironmentController { var systemServerContext = new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader); var uiAutomation = new UiAutomation(sInstContext, new IUiAutomationConnection.Default()); var instArgs = Bundle.EMPTY; RavenwoodUtils.runOnMainThreadSync(() -> { var instClassName = withDefault(sInstrumentationClass, DEFAULT_INSTRUMENTATION_CLASS); Loading @@ -432,7 +430,7 @@ public class RavenwoodRuntimeEnvironmentController { } } initInstrumentation(); sInstrumentation.basicInit(sInstContext, sTargetContext, uiAutomation); sInstrumentation.onCreate(instArgs); }); InstrumentationRegistry.registerInstance(sInstrumentation, instArgs); Loading Loading @@ -473,19 +471,12 @@ public class RavenwoodRuntimeEnvironmentController { } } private static void initInstrumentation() { // We need to recreate the mocks for each test class, because sometimes tests // will call Mockito.framework().clearInlineMocks() after execution. sInstrumentation.basicInit(sInstContext, sTargetContext, createMockUiAutomation()); } /** * Partially reset and initialize before each test class invocation */ public static void initForRunner() { initInstrumentation(); // Reset some global state UiAutomation_ravenwood.reset(); Process_ravenwood.reset(); DeviceConfig_host.reset(); Binder.restoreCallingIdentity(sCallingIdentity); Loading Loading @@ -731,34 +722,6 @@ public class RavenwoodRuntimeEnvironmentController { () -> Class.forName("org.mockito.Matchers")); } static <T> T makeDefaultThrowMock(Class<T> clazz) { return mock(clazz, inv -> { throw new RavenwoodUnsupportedApiException(); }); } // TODO: use the real UiAutomation class instead of a mock private static UiAutomation createMockUiAutomation() { sAdoptedPermissions = Collections.emptySet(); var mock = makeDefaultThrowMock(UiAutomation.class); doAnswer(inv -> { sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; return null; }).when(mock).adoptShellPermissionIdentity(); doAnswer(inv -> { if (inv.getArgument(0) == null) { sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS; } else { sAdoptedPermissions = (Set) Set.of(inv.getArguments()); } return null; }).when(mock).adoptShellPermissionIdentity(any()); doAnswer(inv -> { sAdoptedPermissions = Collections.emptySet(); return null; }).when(mock).dropShellPermissionIdentity(); doAnswer(inv -> sAdoptedPermissions).when(mock).getAdoptedShellPermissions(); return mock; } private static void dumpCommandLineArgs() { Log.i(TAG, "JVM arguments:"); Loading
ravenwood/texts/ravenwood-annotation-allowed-classes.txt +1 −0 Original line number Diff line number Diff line Loading @@ -315,6 +315,7 @@ android.app.ComponentOptions android.app.Instrumentation android.app.LocaleConfig android.app.ResourcesManager android.app.UiAutomation android.app.WindowConfiguration android.metrics.LogMaker Loading