Loading core/java/android/app/ContextImpl_ravenwood.java +18 −1 Original line number Diff line number Diff line Loading @@ -15,8 +15,12 @@ */ package android.app; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.platform.test.ravenwood.RavenwoodExperimentalApiChecker.onExperimentalApiCalled; import android.content.pm.PackageManager; import android.os.FileUtils; import android.os.IBinder; import android.platform.test.ravenwood.RavenwoodPackageManager; import java.io.File; Loading @@ -25,7 +29,7 @@ public class ContextImpl_ravenwood { private static final String TAG = "ContextImpl_ravenwood"; static PackageManager getPackageManagerInner(ContextImpl contextImpl) { return new RavenwoodPackageManager(contextImpl); return RavenwoodPackageManager.create(contextImpl); } static File ensurePrivateDirExists(File file, int mode, int gid, String xattr) { Loading @@ -40,4 +44,17 @@ public class ContextImpl_ravenwood { } return file; } /** Experimental implementation */ static int checkPermission(ContextImpl self, String permission, int pid, int uid) { onExperimentalApiCalled(2); return PERMISSION_GRANTED; } /** Experimental implementation */ static int checkPermission(ContextImpl self, String permission, int pid, int uid, IBinder callerToken) { onExperimentalApiCalled(2); return PERMISSION_GRANTED; } } core/java/android/app/Instrumentation_ravenwood.java +31 −0 Original line number Diff line number Diff line Loading @@ -15,7 +15,15 @@ */ package android.app; import static android.platform.test.ravenwood.RavenwoodExperimentalApiChecker.onExperimentalApiCalled; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiThread; import android.content.Intent; import android.os.Bundle; import android.platform.test.ravenwood.RavenwoodUtils; import android.util.Log; public class Instrumentation_ravenwood { private static final String TAG = "Instrumentation_ravenwood"; Loading @@ -26,4 +34,27 @@ public class Instrumentation_ravenwood { static void checkPendingExceptionOnRavenwood() { RavenwoodUtils.getMainHandler().post(() -> {}); } static Activity startActivitySync(@NonNull Instrumentation inst, @NonNull Intent intent, @Nullable Bundle options) { onExperimentalApiCalled(2); return RavenwoodUtils.runOnMainThreadSync( () -> startActivitySyncOnMain(inst, intent, options)); } @UiThread private static Activity startActivitySyncOnMain( @NonNull Instrumentation inst, @NonNull Intent intent, @Nullable Bundle options) { Log.logStackTrace(Log.LOG_ID_MAIN, Log.INFO, TAG, "startActivity: intent=" + intent + " options=" + options, 2); try { return RavenwoodActivityDriver.getInstance().createResumedActivity(intent, options); } catch (Exception e) { throw new RuntimeException("Failed to start activity. Intent=" + intent, e); } } } core/java/android/app/SystemServiceRegistry_ravenwood.java +75 −0 Original line number Diff line number Diff line Loading @@ -15,16 +15,31 @@ */ package android.app; import static android.platform.test.ravenwood.RavenwoodExperimentalApiChecker.isExperimentalApiEnabled; import android.annotation.NonNull; import android.app.SystemServiceRegistry.CachedServiceFetcher; import android.app.SystemServiceRegistry.ServiceFetcher; import android.content.ClipboardManager; import android.content.Context; import android.hardware.input.InputManager; import android.os.IBinder; import android.os.IUserManager; import android.os.PermissionEnforcer; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.UserManager; import android.platform.test.ravenwood.RavenwoodPermissionEnforcer; import android.ravenwood.example.BlueManager; import android.ravenwood.example.RedManager; import android.view.LayoutInflater; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.autofill.AutofillManager; import android.view.autofill.IAutoFillManager; import android.view.inputmethod.InputMethodManager; import com.android.internal.policy.PhoneLayoutInflater; public class SystemServiceRegistry_ravenwood { private SystemServiceRegistry_ravenwood() { Loading Loading @@ -67,6 +82,8 @@ public class SystemServiceRegistry_ravenwood { return new RavenwoodPermissionEnforcer(); }}); maybeRegisterExperimentalServices(); registerRavenwoodSpecificServices(); } Loading @@ -87,4 +104,62 @@ public class SystemServiceRegistry_ravenwood { } }); } /** * Register "experimental" system services, which are _not_ supported. They're used only for * Ravenwood internal development. */ private static void maybeRegisterExperimentalServices() { if (!isExperimentalApiEnabled()) { return; } registerService(Context.INPUT_SERVICE, InputManager.class, new CachedServiceFetcher<InputManager>() { @Override public InputManager createService(ContextImpl ctx) { return new InputManager(ctx.getOuterContext()); }}); registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class, new ServiceFetcher<InputMethodManager>() { @Override public InputMethodManager getService(ContextImpl ctx) { return InputMethodManager.forContext(ctx.getOuterContext()); }}); registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() { @Override public WindowManager createService(ContextImpl ctx) { return new WindowManagerImpl(ctx); }}); registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, new CachedServiceFetcher<LayoutInflater>() { @Override public LayoutInflater createService(ContextImpl ctx) { return new PhoneLayoutInflater(ctx.getOuterContext()); }}); registerService(Context.USER_SERVICE, UserManager.class, new CachedServiceFetcher<UserManager>() { @Override public UserManager createService(ContextImpl ctx) throws ServiceNotFoundException { IBinder b = ServiceManager.getServiceOrThrow(Context.USER_SERVICE); IUserManager service = IUserManager.Stub.asInterface(b); return new UserManager(ctx, service); }}); registerService(Context.AUTOFILL_MANAGER_SERVICE, AutofillManager.class, new CachedServiceFetcher<AutofillManager>() { @Override public AutofillManager createService(ContextImpl ctx) throws ServiceNotFoundException { // Get the services without throwing as this is an optional feature IBinder b = ServiceManager.getService(Context.AUTOFILL_MANAGER_SERVICE); IAutoFillManager service = IAutoFillManager.Stub.asInterface(b); return new AutofillManager(ctx.getOuterContext(), service); }}); } } ravenwood/junit-impl-src/android/app/RavenwoodActivityDriver.java 0 → 100644 +152 −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.NonNull; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.os.Binder; import android.os.Bundle; import android.platform.test.ravenwood.RavenwoodExperimentalApiChecker; import android.platform.test.ravenwood.RavenwoodUtils; import android.view.Display; import android.view.WindowManager; /** * Experimental implementation of launching activities. */ @SuppressWarnings("unchecked") public class RavenwoodActivityDriver { private static final RavenwoodActivityDriver sInstance = new RavenwoodActivityDriver(); private RavenwoodActivityDriver() { } public static RavenwoodActivityDriver getInstance() { return sInstance; } @SuppressWarnings("unchecked") private static <T extends Activity> Class<T> resolveActivityClass(Intent intent) { try { // TODO: Handle the case where the component name is null. return (Class<T>) Class.forName(intent.getComponent().getClassName()); } catch (Exception e) { throw new RuntimeException("Failed to start an activity", e); } } private static ActivityInfo makeActivityInfo( @NonNull ApplicationInfo appInfo, @NonNull Intent intent) { var clazz = resolveActivityClass(intent); // Here's an example ActivityInfo from the launcher: // http://screen/Beq9oSdYSntJr5k ActivityInfo acti = new ActivityInfo(); acti.applicationInfo = appInfo; acti.enabled = true; acti.exported = true; acti.packageName = appInfo.packageName; acti.labelRes = 0; // TODO acti.launchMode = 2; // from the example acti.resizeMode = 2; // from the example acti.flags = 0; // TODO acti.name = clazz.getName(); acti.processName = appInfo.packageName; return acti; } /** * Instantiate an activity and driver it to the "RESUMED" state. */ public <T extends Activity> T createResumedActivity( Intent intent, Bundle activityOptions // ignored for now. ) throws Exception { RavenwoodExperimentalApiChecker.onExperimentalApiCalled(0); Application app = RavenwoodAppDriver.getInstance().getApplication(); ContextImpl appImpl = ContextImpl.getImpl(app); ApplicationInfo appInfo = appImpl.getApplicationInfo(); ActivityInfo activityInfo = makeActivityInfo(appInfo, intent); Binder token = new Binder(); int displayId = Display.DEFAULT_DISPLAY; ContextImpl activityContextImpl = ContextImpl.createActivityContext( appImpl.mMainThread, appImpl.mPackageInfo, activityInfo, token, displayId, null // overrideConfiguration ); T activity = (T) resolveActivityClass(intent).getConstructor().newInstance(); activity.attach( activityContextImpl, appImpl.mMainThread, appImpl.mMainThread.mInstrumentation, token, 0, // mIdent -- ?? app, intent, activityInfo, "Activity title", null, // parent "embeddedID", // mEmbeddedID -- ?? null, // lastNonConfigurationInstances null, // config "referrer", null, // IVoiceInteractor, null, // window null, // activityConfigCallback null, // assistToken null, // shareableActivityToken null // initialCallerInfoAccessToken ); // ActivityController has this. // return create().start().postCreate(null).resume().visible().topActivityResumed(true); Bundle savedInstanceState = null; activity.performCreate(savedInstanceState); activity.performStart("Called by Ravenwood"); activity.onPostCreate(savedInstanceState); activity.performResume(false, // Followed by pause "Called by Ravenwood"); // ActivityController has // "emulate logic of ActivityThread#handleResumeActivity" // Make visible -- this requires some setup, which is copied from ActivityController. activity.getWindow().getAttributes().type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; activity.mDecor = activity.getWindow().getDecorView(); activity.makeVisible(); activity.performTopResumedActivityChanged( true, // isTop "Called by Ravenwood"); RavenwoodUtils.getMainHandler().post(() -> activity.getWindow().getDecorView().getViewRootImpl().windowFocusChanged(true)); return activity; } } ravenwood/junit-impl-src/android/app/RavenwoodAppDriver.java +4 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,8 @@ public final class RavenwoodAppDriver { private final Instrumentation mInstrumentation; private final RavenwoodActivityDriver mActivityDriver = RavenwoodActivityDriver.getInstance(); /** * Constructor. It essentially simulates the start of an app lifecycle. * Loading Loading @@ -174,6 +176,8 @@ public final class RavenwoodAppDriver { ai.sourceDir = env.getResourcesApkFile(packageName).getAbsolutePath(); ai.publicSourceDir = ai.sourceDir; ai.processName = packageName; // TODO: Set CE/DE data dirs too. return ai; Loading Loading
core/java/android/app/ContextImpl_ravenwood.java +18 −1 Original line number Diff line number Diff line Loading @@ -15,8 +15,12 @@ */ package android.app; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.platform.test.ravenwood.RavenwoodExperimentalApiChecker.onExperimentalApiCalled; import android.content.pm.PackageManager; import android.os.FileUtils; import android.os.IBinder; import android.platform.test.ravenwood.RavenwoodPackageManager; import java.io.File; Loading @@ -25,7 +29,7 @@ public class ContextImpl_ravenwood { private static final String TAG = "ContextImpl_ravenwood"; static PackageManager getPackageManagerInner(ContextImpl contextImpl) { return new RavenwoodPackageManager(contextImpl); return RavenwoodPackageManager.create(contextImpl); } static File ensurePrivateDirExists(File file, int mode, int gid, String xattr) { Loading @@ -40,4 +44,17 @@ public class ContextImpl_ravenwood { } return file; } /** Experimental implementation */ static int checkPermission(ContextImpl self, String permission, int pid, int uid) { onExperimentalApiCalled(2); return PERMISSION_GRANTED; } /** Experimental implementation */ static int checkPermission(ContextImpl self, String permission, int pid, int uid, IBinder callerToken) { onExperimentalApiCalled(2); return PERMISSION_GRANTED; } }
core/java/android/app/Instrumentation_ravenwood.java +31 −0 Original line number Diff line number Diff line Loading @@ -15,7 +15,15 @@ */ package android.app; import static android.platform.test.ravenwood.RavenwoodExperimentalApiChecker.onExperimentalApiCalled; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UiThread; import android.content.Intent; import android.os.Bundle; import android.platform.test.ravenwood.RavenwoodUtils; import android.util.Log; public class Instrumentation_ravenwood { private static final String TAG = "Instrumentation_ravenwood"; Loading @@ -26,4 +34,27 @@ public class Instrumentation_ravenwood { static void checkPendingExceptionOnRavenwood() { RavenwoodUtils.getMainHandler().post(() -> {}); } static Activity startActivitySync(@NonNull Instrumentation inst, @NonNull Intent intent, @Nullable Bundle options) { onExperimentalApiCalled(2); return RavenwoodUtils.runOnMainThreadSync( () -> startActivitySyncOnMain(inst, intent, options)); } @UiThread private static Activity startActivitySyncOnMain( @NonNull Instrumentation inst, @NonNull Intent intent, @Nullable Bundle options) { Log.logStackTrace(Log.LOG_ID_MAIN, Log.INFO, TAG, "startActivity: intent=" + intent + " options=" + options, 2); try { return RavenwoodActivityDriver.getInstance().createResumedActivity(intent, options); } catch (Exception e) { throw new RuntimeException("Failed to start activity. Intent=" + intent, e); } } }
core/java/android/app/SystemServiceRegistry_ravenwood.java +75 −0 Original line number Diff line number Diff line Loading @@ -15,16 +15,31 @@ */ package android.app; import static android.platform.test.ravenwood.RavenwoodExperimentalApiChecker.isExperimentalApiEnabled; import android.annotation.NonNull; import android.app.SystemServiceRegistry.CachedServiceFetcher; import android.app.SystemServiceRegistry.ServiceFetcher; import android.content.ClipboardManager; import android.content.Context; import android.hardware.input.InputManager; import android.os.IBinder; import android.os.IUserManager; import android.os.PermissionEnforcer; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.UserManager; import android.platform.test.ravenwood.RavenwoodPermissionEnforcer; import android.ravenwood.example.BlueManager; import android.ravenwood.example.RedManager; import android.view.LayoutInflater; import android.view.WindowManager; import android.view.WindowManagerImpl; import android.view.autofill.AutofillManager; import android.view.autofill.IAutoFillManager; import android.view.inputmethod.InputMethodManager; import com.android.internal.policy.PhoneLayoutInflater; public class SystemServiceRegistry_ravenwood { private SystemServiceRegistry_ravenwood() { Loading Loading @@ -67,6 +82,8 @@ public class SystemServiceRegistry_ravenwood { return new RavenwoodPermissionEnforcer(); }}); maybeRegisterExperimentalServices(); registerRavenwoodSpecificServices(); } Loading @@ -87,4 +104,62 @@ public class SystemServiceRegistry_ravenwood { } }); } /** * Register "experimental" system services, which are _not_ supported. They're used only for * Ravenwood internal development. */ private static void maybeRegisterExperimentalServices() { if (!isExperimentalApiEnabled()) { return; } registerService(Context.INPUT_SERVICE, InputManager.class, new CachedServiceFetcher<InputManager>() { @Override public InputManager createService(ContextImpl ctx) { return new InputManager(ctx.getOuterContext()); }}); registerService(Context.INPUT_METHOD_SERVICE, InputMethodManager.class, new ServiceFetcher<InputMethodManager>() { @Override public InputMethodManager getService(ContextImpl ctx) { return InputMethodManager.forContext(ctx.getOuterContext()); }}); registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() { @Override public WindowManager createService(ContextImpl ctx) { return new WindowManagerImpl(ctx); }}); registerService(Context.LAYOUT_INFLATER_SERVICE, LayoutInflater.class, new CachedServiceFetcher<LayoutInflater>() { @Override public LayoutInflater createService(ContextImpl ctx) { return new PhoneLayoutInflater(ctx.getOuterContext()); }}); registerService(Context.USER_SERVICE, UserManager.class, new CachedServiceFetcher<UserManager>() { @Override public UserManager createService(ContextImpl ctx) throws ServiceNotFoundException { IBinder b = ServiceManager.getServiceOrThrow(Context.USER_SERVICE); IUserManager service = IUserManager.Stub.asInterface(b); return new UserManager(ctx, service); }}); registerService(Context.AUTOFILL_MANAGER_SERVICE, AutofillManager.class, new CachedServiceFetcher<AutofillManager>() { @Override public AutofillManager createService(ContextImpl ctx) throws ServiceNotFoundException { // Get the services without throwing as this is an optional feature IBinder b = ServiceManager.getService(Context.AUTOFILL_MANAGER_SERVICE); IAutoFillManager service = IAutoFillManager.Stub.asInterface(b); return new AutofillManager(ctx.getOuterContext(), service); }}); } }
ravenwood/junit-impl-src/android/app/RavenwoodActivityDriver.java 0 → 100644 +152 −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.NonNull; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.os.Binder; import android.os.Bundle; import android.platform.test.ravenwood.RavenwoodExperimentalApiChecker; import android.platform.test.ravenwood.RavenwoodUtils; import android.view.Display; import android.view.WindowManager; /** * Experimental implementation of launching activities. */ @SuppressWarnings("unchecked") public class RavenwoodActivityDriver { private static final RavenwoodActivityDriver sInstance = new RavenwoodActivityDriver(); private RavenwoodActivityDriver() { } public static RavenwoodActivityDriver getInstance() { return sInstance; } @SuppressWarnings("unchecked") private static <T extends Activity> Class<T> resolveActivityClass(Intent intent) { try { // TODO: Handle the case where the component name is null. return (Class<T>) Class.forName(intent.getComponent().getClassName()); } catch (Exception e) { throw new RuntimeException("Failed to start an activity", e); } } private static ActivityInfo makeActivityInfo( @NonNull ApplicationInfo appInfo, @NonNull Intent intent) { var clazz = resolveActivityClass(intent); // Here's an example ActivityInfo from the launcher: // http://screen/Beq9oSdYSntJr5k ActivityInfo acti = new ActivityInfo(); acti.applicationInfo = appInfo; acti.enabled = true; acti.exported = true; acti.packageName = appInfo.packageName; acti.labelRes = 0; // TODO acti.launchMode = 2; // from the example acti.resizeMode = 2; // from the example acti.flags = 0; // TODO acti.name = clazz.getName(); acti.processName = appInfo.packageName; return acti; } /** * Instantiate an activity and driver it to the "RESUMED" state. */ public <T extends Activity> T createResumedActivity( Intent intent, Bundle activityOptions // ignored for now. ) throws Exception { RavenwoodExperimentalApiChecker.onExperimentalApiCalled(0); Application app = RavenwoodAppDriver.getInstance().getApplication(); ContextImpl appImpl = ContextImpl.getImpl(app); ApplicationInfo appInfo = appImpl.getApplicationInfo(); ActivityInfo activityInfo = makeActivityInfo(appInfo, intent); Binder token = new Binder(); int displayId = Display.DEFAULT_DISPLAY; ContextImpl activityContextImpl = ContextImpl.createActivityContext( appImpl.mMainThread, appImpl.mPackageInfo, activityInfo, token, displayId, null // overrideConfiguration ); T activity = (T) resolveActivityClass(intent).getConstructor().newInstance(); activity.attach( activityContextImpl, appImpl.mMainThread, appImpl.mMainThread.mInstrumentation, token, 0, // mIdent -- ?? app, intent, activityInfo, "Activity title", null, // parent "embeddedID", // mEmbeddedID -- ?? null, // lastNonConfigurationInstances null, // config "referrer", null, // IVoiceInteractor, null, // window null, // activityConfigCallback null, // assistToken null, // shareableActivityToken null // initialCallerInfoAccessToken ); // ActivityController has this. // return create().start().postCreate(null).resume().visible().topActivityResumed(true); Bundle savedInstanceState = null; activity.performCreate(savedInstanceState); activity.performStart("Called by Ravenwood"); activity.onPostCreate(savedInstanceState); activity.performResume(false, // Followed by pause "Called by Ravenwood"); // ActivityController has // "emulate logic of ActivityThread#handleResumeActivity" // Make visible -- this requires some setup, which is copied from ActivityController. activity.getWindow().getAttributes().type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; activity.mDecor = activity.getWindow().getDecorView(); activity.makeVisible(); activity.performTopResumedActivityChanged( true, // isTop "Called by Ravenwood"); RavenwoodUtils.getMainHandler().post(() -> activity.getWindow().getDecorView().getViewRootImpl().windowFocusChanged(true)); return activity; } }
ravenwood/junit-impl-src/android/app/RavenwoodAppDriver.java +4 −0 Original line number Diff line number Diff line Loading @@ -75,6 +75,8 @@ public final class RavenwoodAppDriver { private final Instrumentation mInstrumentation; private final RavenwoodActivityDriver mActivityDriver = RavenwoodActivityDriver.getInstance(); /** * Constructor. It essentially simulates the start of an app lifecycle. * Loading Loading @@ -174,6 +176,8 @@ public final class RavenwoodAppDriver { ai.sourceDir = env.getResourcesApkFile(packageName).getAbsolutePath(); ai.publicSourceDir = ai.sourceDir; ai.processName = packageName; // TODO: Set CE/DE data dirs too. return ai; Loading