xref: /aosp_15_r20/frameworks/base/ravenwood/api-maintainers.md (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker# Ravenwood for API Maintainers
2*d57664e9SAndroid Build Coastguard Worker
3*d57664e9SAndroid Build Coastguard WorkerBy default, Android APIs aren’t opted-in to Ravenwood, and they default to throwing when called under the Ravenwood environment.
4*d57664e9SAndroid Build Coastguard Worker
5*d57664e9SAndroid Build Coastguard WorkerTo opt-in to supporting an API under Ravenwood, you can use the inline annotations documented below to customize your API behavior when running under Ravenwood.  Because these annotations are inline in the relevant platform source code, they serve as valuable reminders to future API maintainers of Ravenwood support expectations.
6*d57664e9SAndroid Build Coastguard Worker
7*d57664e9SAndroid Build Coastguard Worker> **Note:** to ensure that API teams are well-supported during early Ravenwood onboarding, the Ravenwood team is manually maintaining an allow-list of classes that are able to use Ravenwood annotations.  Please reach out to ravenwood@ so we can offer design advice and allow-list your APIs.
8*d57664e9SAndroid Build Coastguard Worker
9*d57664e9SAndroid Build Coastguard WorkerThese Ravenwood-specific annotations have no bearing on the status of an API being public, `@SystemApi`, `@TestApi`, `@hide`, etc.  Ravenwood annotations are an orthogonal concept that are only consumed by the internal `hoststubgen` tool during a post-processing step that generates the Ravenwood runtime environment.  Teams that own APIs can continue to refactor opted-in `@hide` implementation details, as long as the test-visible behavior continues passing.
10*d57664e9SAndroid Build Coastguard Worker
11*d57664e9SAndroid Build Coastguard WorkerAs described in our Guiding Principles, when a team opts-in an API, we’re requiring that they bring along “bivalent” tests (such as the relevant CTS) to validate that Ravenwood behaves just like a physical device.  At the moment this means adding the bivalent tests to relevant `TEST_MAPPING` files to ensure they remain consistently passing over time.  These bivalent tests are important because they progressively provide the foundation on which higher-level unit tests place their trust.
12*d57664e9SAndroid Build Coastguard Worker
13*d57664e9SAndroid Build Coastguard Worker## Opt-in to supporting a single method while other methods remained opt-out
14*d57664e9SAndroid Build Coastguard Worker
15*d57664e9SAndroid Build Coastguard Worker```
16*d57664e9SAndroid Build Coastguard Worker@RavenwoodKeepPartialClass
17*d57664e9SAndroid Build Coastguard Workerpublic class MyManager {
18*d57664e9SAndroid Build Coastguard Worker    @RavenwoodKeep
19*d57664e9SAndroid Build Coastguard Worker    public static String modeToString(int mode) {
20*d57664e9SAndroid Build Coastguard Worker        // This method implementation runs as-is on both devices and Ravenwood
21*d57664e9SAndroid Build Coastguard Worker    }
22*d57664e9SAndroid Build Coastguard Worker
23*d57664e9SAndroid Build Coastguard Worker    public static void doComplex() {
24*d57664e9SAndroid Build Coastguard Worker        // This method implementation runs as-is on devices, but because there
25*d57664e9SAndroid Build Coastguard Worker        // is no method-level annotation, and the class-level default is
26*d57664e9SAndroid Build Coastguard Worker        // “keep partial”, this method is not supported under Ravenwood and
27*d57664e9SAndroid Build Coastguard Worker        // will throw
28*d57664e9SAndroid Build Coastguard Worker    }
29*d57664e9SAndroid Build Coastguard Worker}
30*d57664e9SAndroid Build Coastguard Worker```
31*d57664e9SAndroid Build Coastguard Worker
32*d57664e9SAndroid Build Coastguard Worker## Opt-in an entire class with opt-out of specific methods
33*d57664e9SAndroid Build Coastguard Worker
34*d57664e9SAndroid Build Coastguard Worker```
35*d57664e9SAndroid Build Coastguard Worker@RavenwoodKeepWholeClass
36*d57664e9SAndroid Build Coastguard Workerpublic class MyStruct {
37*d57664e9SAndroid Build Coastguard Worker    public void doSimple() {
38*d57664e9SAndroid Build Coastguard Worker        // This method implementation runs as-is on both devices and Ravenwood,
39*d57664e9SAndroid Build Coastguard Worker        // implicitly inheriting the class-level annotation
40*d57664e9SAndroid Build Coastguard Worker    }
41*d57664e9SAndroid Build Coastguard Worker
42*d57664e9SAndroid Build Coastguard Worker    @RavenwoodThrow
43*d57664e9SAndroid Build Coastguard Worker    public void doComplex() {
44*d57664e9SAndroid Build Coastguard Worker        // This method implementation runs as-is on devices, but the
45*d57664e9SAndroid Build Coastguard Worker        // method-level annotation overrides the class-level annotation, so
46*d57664e9SAndroid Build Coastguard Worker        // this method is not supported under Ravenwood and will throw
47*d57664e9SAndroid Build Coastguard Worker    }
48*d57664e9SAndroid Build Coastguard Worker}
49*d57664e9SAndroid Build Coastguard Worker```
50*d57664e9SAndroid Build Coastguard Worker
51*d57664e9SAndroid Build Coastguard Worker## Replace a complex method when under Ravenwood
52*d57664e9SAndroid Build Coastguard Worker
53*d57664e9SAndroid Build Coastguard Worker```
54*d57664e9SAndroid Build Coastguard Worker@RavenwoodKeepWholeClass
55*d57664e9SAndroid Build Coastguard Workerpublic class MyStruct {
56*d57664e9SAndroid Build Coastguard Worker    @RavenwoodReplace
57*d57664e9SAndroid Build Coastguard Worker    public void doComplex() {
58*d57664e9SAndroid Build Coastguard Worker        // This method implementation runs as-is on devices, but the
59*d57664e9SAndroid Build Coastguard Worker        // implementation is replaced/substituted by the
60*d57664e9SAndroid Build Coastguard Worker        // doComplex$ravenwood() method implementation under Ravenwood
61*d57664e9SAndroid Build Coastguard Worker    }
62*d57664e9SAndroid Build Coastguard Worker
63*d57664e9SAndroid Build Coastguard Worker    public void doComplex$ravenwood() {
64*d57664e9SAndroid Build Coastguard Worker        // This method implementation only runs under Ravenwood
65*d57664e9SAndroid Build Coastguard Worker    }
66*d57664e9SAndroid Build Coastguard Worker}
67*d57664e9SAndroid Build Coastguard Worker```
68*d57664e9SAndroid Build Coastguard Worker
69*d57664e9SAndroid Build Coastguard Worker## General strategies for side-stepping tricky dependencies
70*d57664e9SAndroid Build Coastguard Worker
71*d57664e9SAndroid Build Coastguard WorkerThe “replace” strategy described above is quite powerful, and can be used in creative ways to sidestep tricky underlying dependencies that aren’t ready yet.
72*d57664e9SAndroid Build Coastguard Worker
73*d57664e9SAndroid Build Coastguard WorkerFor example, consider a constructor or static initializer that relies on unsupported functionality from another team.  By factoring the unsupported logic into a dedicated method, that method can then be replaced under Ravenwood to offer baseline functionality.
74*d57664e9SAndroid Build Coastguard Worker
75*d57664e9SAndroid Build Coastguard Worker## Strategies for JNI
76*d57664e9SAndroid Build Coastguard Worker
77*d57664e9SAndroid Build Coastguard WorkerAt the moment, JNI isn't yet supported under Ravenwood, but you may still want to support APIs that are partially implemented with JNI.  The current approach is to use the “replace” strategy to offer a pure-Java alternative implementation for any JNI-provided logic.
78*d57664e9SAndroid Build Coastguard Worker
79*d57664e9SAndroid Build Coastguard WorkerSince this approach requires potentially complex re-implementation, it should only be considered for core infrastructure that is critical to unblocking widespread testing use-cases.  Other less-common usages of JNI should instead wait for offical JNI support in the Ravenwood environment.
80*d57664e9SAndroid Build Coastguard Worker
81*d57664e9SAndroid Build Coastguard WorkerWhen a pure-Java implementation grows too large or complex to host within the original class, the `@RavenwoodNativeSubstitutionClass` annotation can be used to host it in a separate source file:
82*d57664e9SAndroid Build Coastguard Worker
83*d57664e9SAndroid Build Coastguard Worker```
84*d57664e9SAndroid Build Coastguard Worker@RavenwoodKeepWholeClass
85*d57664e9SAndroid Build Coastguard Worker@RavenwoodNativeSubstitutionClass("com.android.platform.test.ravenwood.nativesubstitution.MyComplexClass_host")
86*d57664e9SAndroid Build Coastguard Workerpublic class MyComplexClass {
87*d57664e9SAndroid Build Coastguard Worker    private static native void nativeDoThing(long nativePtr);
88*d57664e9SAndroid Build Coastguard Worker...
89*d57664e9SAndroid Build Coastguard Worker
90*d57664e9SAndroid Build Coastguard Workerpublic class MyComplexClass_host {
91*d57664e9SAndroid Build Coastguard Worker    public static void nativeDoThing(long nativePtr) {
92*d57664e9SAndroid Build Coastguard Worker        // ...
93*d57664e9SAndroid Build Coastguard Worker    }
94*d57664e9SAndroid Build Coastguard Worker```
95