Loading core/java/android/app/Instrumentation.java +19 −9 Original line number Diff line number Diff line Loading @@ -48,6 +48,9 @@ import android.os.SystemProperties; import android.os.TestLooperManager; import android.os.UserHandle; import android.os.UserManager; import android.ravenwood.annotation.RavenwoodKeep; import android.ravenwood.annotation.RavenwoodKeepPartialClass; import android.ravenwood.annotation.RavenwoodReplace; import android.util.AndroidRuntimeException; import android.util.Log; import android.view.Display; Loading Loading @@ -80,7 +83,7 @@ import java.util.concurrent.TimeoutException; * implementation is described to the system through an AndroidManifest.xml's * <instrumentation> tag. */ @android.ravenwood.annotation.RavenwoodKeepPartialClass @RavenwoodKeepPartialClass public class Instrumentation { /** Loading Loading @@ -134,7 +137,7 @@ public class Instrumentation { private UiAutomation mUiAutomation; private final Object mAnimationCompleteLock = new Object(); @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public Instrumentation() { } Loading @@ -145,7 +148,7 @@ public class Instrumentation { * reflection, but it will serve as noticeable discouragement from * doing such a thing. */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep private void checkInstrumenting(String method) { // Check if we have an instrumentation context, as init should only get called by // the system in startup processes that are being instrumented. Loading @@ -160,7 +163,7 @@ public class Instrumentation { * * @hide */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public boolean isInstrumenting() { // Check if we have an instrumentation context, as init should only get called by // the system in startup processes that are being instrumented. Loading Loading @@ -324,7 +327,7 @@ public class Instrumentation { * * @see #getTargetContext */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public Context getContext() { return mInstrContext; } Loading @@ -349,7 +352,7 @@ public class Instrumentation { * * @see #getContext */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public Context getTargetContext() { return mAppContext; } Loading Loading @@ -2405,10 +2408,11 @@ public class Instrumentation { * * @hide */ @android.ravenwood.annotation.RavenwoodKeep public final void basicInit(Context instrContext, Context appContext) { @RavenwoodKeep public final void basicInit(Context instrContext, Context appContext, UiAutomation ui) { mInstrContext = instrContext; mAppContext = appContext; mUiAutomation = ui; } /** @hide */ Loading Loading @@ -2499,6 +2503,7 @@ public class Instrumentation { * * @see UiAutomation */ @RavenwoodKeep public UiAutomation getUiAutomation() { return getUiAutomation(0); } Loading Loading @@ -2537,6 +2542,7 @@ public class Instrumentation { * * @see UiAutomation */ @RavenwoodReplace public UiAutomation getUiAutomation(@UiAutomationFlags int flags) { boolean mustCreateNewAutomation = (mUiAutomation == null) || (mUiAutomation.isDestroyed()); Loading Loading @@ -2567,11 +2573,15 @@ public class Instrumentation { return null; } private UiAutomation getUiAutomation$ravenwood(@UiAutomationFlags int flags) { return mUiAutomation; } /** * Takes control of the execution of messages on the specified looper until * {@link TestLooperManager#release} is called. */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public TestLooperManager acquireLooperManager(Looper looper) { checkInstrumenting("acquireLooperManager"); return new TestLooperManager(looper); Loading ravenwood/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,8 @@ java_library { "framework-annotations-lib", "ravenwood-helper-framework-runtime", "ravenwood-helper-libcore-runtime", "hoststubgen-helper-runtime.ravenwood", "mockito-ravenwood-prebuilt", ], visibility: ["//frameworks/base"], jarjar_rules: ":ravenwood-services-jarjar-rules", Loading ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +40 −3 Original line number Diff line number Diff line Loading @@ -22,10 +22,14 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOS import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import android.app.ActivityManager; import android.app.Instrumentation; import android.app.ResourcesManager; import android.app.UiAutomation; import android.content.res.Resources; import android.os.Binder; import android.os.Build; Loading @@ -40,6 +44,7 @@ import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import com.android.hoststubgen.hosthelper.HostTestUtils; import com.android.internal.os.RuntimeInit; import com.android.ravenwood.RavenwoodRuntimeNative; import com.android.ravenwood.common.RavenwoodCommonUtils; Loading @@ -52,8 +57,10 @@ import org.junit.runner.Description; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; Loading Loading @@ -125,6 +132,9 @@ public class RavenwoodRuntimeEnvironmentController { private static RavenwoodConfig sConfig; private static RavenwoodSystemProperties sProps; // TODO: use the real UiAutomation class instead of a mock private static UiAutomation sMockUiAutomation; private static Set<String> sAdoptedPermissions = Collections.emptySet(); private static boolean sInitialized = false; /** Loading Loading @@ -171,6 +181,7 @@ public class RavenwoodRuntimeEnvironmentController { "androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner"); assertMockitoVersion(); sMockUiAutomation = createMockUiAutomation(); } /** Loading Loading @@ -261,7 +272,7 @@ public class RavenwoodRuntimeEnvironmentController { // Prepare other fields. config.mInstrumentation = new Instrumentation(); config.mInstrumentation.basicInit(config.mInstContext, config.mTargetContext); config.mInstrumentation.basicInit(instContext, targetContext, sMockUiAutomation); InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY); RavenwoodSystemServer.init(config); Loading Loading @@ -300,12 +311,13 @@ public class RavenwoodRuntimeEnvironmentController { config.mInstrumentation = null; if (config.mInstContext != null) { ((RavenwoodContext) config.mInstContext).cleanUp(); config.mInstContext = null; } if (config.mTargetContext != null) { ((RavenwoodContext) config.mTargetContext).cleanUp(); } config.mInstContext = null; config.mTargetContext = null; } sMockUiAutomation.dropShellPermissionIdentity(); if (config.mProvideMainThread) { Looper.getMainLooper().quit(); Loading Loading @@ -407,6 +419,31 @@ public class RavenwoodRuntimeEnvironmentController { () -> Class.forName("org.mockito.Matchers")); } private static UiAutomation createMockUiAutomation() { var mock = mock(UiAutomation.class, inv -> { HostTestUtils.onThrowMethodCalled(); return null; }); 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; } @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp) private static void checkSystemPropertyAccess(String key, boolean write) { boolean result = write ? sProps.isKeyWritable(key) : sProps.isKeyReadable(key); Loading ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodUiAutomationTest.java 0 → 100644 +84 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 com.android.ravenwoodtest.bivalenttest; import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import android.app.Instrumentation; import android.app.UiAutomation; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import com.android.ravenwood.common.RavenwoodCommonUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Set; @RunWith(AndroidJUnit4.class) public class RavenwoodUiAutomationTest { private Instrumentation mInstrumentation; @Before public void setup() { mInstrumentation = InstrumentationRegistry.getInstrumentation(); } @Test public void testGetUiAutomation() { assertNotNull(mInstrumentation.getUiAutomation()); } @Test public void testGetUiAutomationWithFlags() { assertNotNull(mInstrumentation.getUiAutomation(UiAutomation.FLAG_DONT_USE_ACCESSIBILITY)); } @Test public void testShellPermissionApis() { var uiAutomation = mInstrumentation.getUiAutomation(); assertTrue(uiAutomation.getAdoptedShellPermissions().isEmpty()); uiAutomation.adoptShellPermissionIdentity(); assertEquals(uiAutomation.getAdoptedShellPermissions(), UiAutomation.ALL_PERMISSIONS); uiAutomation.adoptShellPermissionIdentity((String[]) null); assertEquals(uiAutomation.getAdoptedShellPermissions(), UiAutomation.ALL_PERMISSIONS); uiAutomation.adoptShellPermissionIdentity( OVERRIDE_COMPAT_CHANGE_CONFIG, READ_COMPAT_CHANGE_CONFIG); assertEquals(uiAutomation.getAdoptedShellPermissions(), Set.of(OVERRIDE_COMPAT_CHANGE_CONFIG, READ_COMPAT_CHANGE_CONFIG)); uiAutomation.dropShellPermissionIdentity(); assertTrue(uiAutomation.getAdoptedShellPermissions().isEmpty()); } @Test public void testUnsupportedMethod() { // Only unsupported on Ravenwood assumeTrue(RavenwoodCommonUtils.isOnRavenwood()); assertThrows(RuntimeException.class, () -> mInstrumentation.getUiAutomation().executeShellCommand("echo ok")); } } Loading
core/java/android/app/Instrumentation.java +19 −9 Original line number Diff line number Diff line Loading @@ -48,6 +48,9 @@ import android.os.SystemProperties; import android.os.TestLooperManager; import android.os.UserHandle; import android.os.UserManager; import android.ravenwood.annotation.RavenwoodKeep; import android.ravenwood.annotation.RavenwoodKeepPartialClass; import android.ravenwood.annotation.RavenwoodReplace; import android.util.AndroidRuntimeException; import android.util.Log; import android.view.Display; Loading Loading @@ -80,7 +83,7 @@ import java.util.concurrent.TimeoutException; * implementation is described to the system through an AndroidManifest.xml's * <instrumentation> tag. */ @android.ravenwood.annotation.RavenwoodKeepPartialClass @RavenwoodKeepPartialClass public class Instrumentation { /** Loading Loading @@ -134,7 +137,7 @@ public class Instrumentation { private UiAutomation mUiAutomation; private final Object mAnimationCompleteLock = new Object(); @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public Instrumentation() { } Loading @@ -145,7 +148,7 @@ public class Instrumentation { * reflection, but it will serve as noticeable discouragement from * doing such a thing. */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep private void checkInstrumenting(String method) { // Check if we have an instrumentation context, as init should only get called by // the system in startup processes that are being instrumented. Loading @@ -160,7 +163,7 @@ public class Instrumentation { * * @hide */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public boolean isInstrumenting() { // Check if we have an instrumentation context, as init should only get called by // the system in startup processes that are being instrumented. Loading Loading @@ -324,7 +327,7 @@ public class Instrumentation { * * @see #getTargetContext */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public Context getContext() { return mInstrContext; } Loading @@ -349,7 +352,7 @@ public class Instrumentation { * * @see #getContext */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public Context getTargetContext() { return mAppContext; } Loading Loading @@ -2405,10 +2408,11 @@ public class Instrumentation { * * @hide */ @android.ravenwood.annotation.RavenwoodKeep public final void basicInit(Context instrContext, Context appContext) { @RavenwoodKeep public final void basicInit(Context instrContext, Context appContext, UiAutomation ui) { mInstrContext = instrContext; mAppContext = appContext; mUiAutomation = ui; } /** @hide */ Loading Loading @@ -2499,6 +2503,7 @@ public class Instrumentation { * * @see UiAutomation */ @RavenwoodKeep public UiAutomation getUiAutomation() { return getUiAutomation(0); } Loading Loading @@ -2537,6 +2542,7 @@ public class Instrumentation { * * @see UiAutomation */ @RavenwoodReplace public UiAutomation getUiAutomation(@UiAutomationFlags int flags) { boolean mustCreateNewAutomation = (mUiAutomation == null) || (mUiAutomation.isDestroyed()); Loading Loading @@ -2567,11 +2573,15 @@ public class Instrumentation { return null; } private UiAutomation getUiAutomation$ravenwood(@UiAutomationFlags int flags) { return mUiAutomation; } /** * Takes control of the execution of messages on the specified looper until * {@link TestLooperManager#release} is called. */ @android.ravenwood.annotation.RavenwoodKeep @RavenwoodKeep public TestLooperManager acquireLooperManager(Looper looper) { checkInstrumenting("acquireLooperManager"); return new TestLooperManager(looper); Loading
ravenwood/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,8 @@ java_library { "framework-annotations-lib", "ravenwood-helper-framework-runtime", "ravenwood-helper-libcore-runtime", "hoststubgen-helper-runtime.ravenwood", "mockito-ravenwood-prebuilt", ], visibility: ["//frameworks/base"], jarjar_rules: ":ravenwood-services-jarjar-rules", Loading
ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +40 −3 Original line number Diff line number Diff line Loading @@ -22,10 +22,14 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOS import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import android.app.ActivityManager; import android.app.Instrumentation; import android.app.ResourcesManager; import android.app.UiAutomation; import android.content.res.Resources; import android.os.Binder; import android.os.Build; Loading @@ -40,6 +44,7 @@ import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import com.android.hoststubgen.hosthelper.HostTestUtils; import com.android.internal.os.RuntimeInit; import com.android.ravenwood.RavenwoodRuntimeNative; import com.android.ravenwood.common.RavenwoodCommonUtils; Loading @@ -52,8 +57,10 @@ import org.junit.runner.Description; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; Loading Loading @@ -125,6 +132,9 @@ public class RavenwoodRuntimeEnvironmentController { private static RavenwoodConfig sConfig; private static RavenwoodSystemProperties sProps; // TODO: use the real UiAutomation class instead of a mock private static UiAutomation sMockUiAutomation; private static Set<String> sAdoptedPermissions = Collections.emptySet(); private static boolean sInitialized = false; /** Loading Loading @@ -171,6 +181,7 @@ public class RavenwoodRuntimeEnvironmentController { "androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner"); assertMockitoVersion(); sMockUiAutomation = createMockUiAutomation(); } /** Loading Loading @@ -261,7 +272,7 @@ public class RavenwoodRuntimeEnvironmentController { // Prepare other fields. config.mInstrumentation = new Instrumentation(); config.mInstrumentation.basicInit(config.mInstContext, config.mTargetContext); config.mInstrumentation.basicInit(instContext, targetContext, sMockUiAutomation); InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY); RavenwoodSystemServer.init(config); Loading Loading @@ -300,12 +311,13 @@ public class RavenwoodRuntimeEnvironmentController { config.mInstrumentation = null; if (config.mInstContext != null) { ((RavenwoodContext) config.mInstContext).cleanUp(); config.mInstContext = null; } if (config.mTargetContext != null) { ((RavenwoodContext) config.mTargetContext).cleanUp(); } config.mInstContext = null; config.mTargetContext = null; } sMockUiAutomation.dropShellPermissionIdentity(); if (config.mProvideMainThread) { Looper.getMainLooper().quit(); Loading Loading @@ -407,6 +419,31 @@ public class RavenwoodRuntimeEnvironmentController { () -> Class.forName("org.mockito.Matchers")); } private static UiAutomation createMockUiAutomation() { var mock = mock(UiAutomation.class, inv -> { HostTestUtils.onThrowMethodCalled(); return null; }); 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; } @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp) private static void checkSystemPropertyAccess(String key, boolean write) { boolean result = write ? sProps.isKeyWritable(key) : sProps.isKeyReadable(key); Loading
ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodUiAutomationTest.java 0 → 100644 +84 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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 com.android.ravenwoodtest.bivalenttest; import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import android.app.Instrumentation; import android.app.UiAutomation; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.platform.app.InstrumentationRegistry; import com.android.ravenwood.common.RavenwoodCommonUtils; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Set; @RunWith(AndroidJUnit4.class) public class RavenwoodUiAutomationTest { private Instrumentation mInstrumentation; @Before public void setup() { mInstrumentation = InstrumentationRegistry.getInstrumentation(); } @Test public void testGetUiAutomation() { assertNotNull(mInstrumentation.getUiAutomation()); } @Test public void testGetUiAutomationWithFlags() { assertNotNull(mInstrumentation.getUiAutomation(UiAutomation.FLAG_DONT_USE_ACCESSIBILITY)); } @Test public void testShellPermissionApis() { var uiAutomation = mInstrumentation.getUiAutomation(); assertTrue(uiAutomation.getAdoptedShellPermissions().isEmpty()); uiAutomation.adoptShellPermissionIdentity(); assertEquals(uiAutomation.getAdoptedShellPermissions(), UiAutomation.ALL_PERMISSIONS); uiAutomation.adoptShellPermissionIdentity((String[]) null); assertEquals(uiAutomation.getAdoptedShellPermissions(), UiAutomation.ALL_PERMISSIONS); uiAutomation.adoptShellPermissionIdentity( OVERRIDE_COMPAT_CHANGE_CONFIG, READ_COMPAT_CHANGE_CONFIG); assertEquals(uiAutomation.getAdoptedShellPermissions(), Set.of(OVERRIDE_COMPAT_CHANGE_CONFIG, READ_COMPAT_CHANGE_CONFIG)); uiAutomation.dropShellPermissionIdentity(); assertTrue(uiAutomation.getAdoptedShellPermissions().isEmpty()); } @Test public void testUnsupportedMethod() { // Only unsupported on Ravenwood assumeTrue(RavenwoodCommonUtils.isOnRavenwood()); assertThrows(RuntimeException.class, () -> mInstrumentation.getUiAutomation().executeShellCommand("echo ok")); } }