[KeepAnno] Add user guide for @KeepForApi

Bug: b/248408342
Change-Id: I89d9fb871a51ef59a2bf8bd215eef1073778ee27
diff --git a/doc/keepanno-guide.md b/doc/keepanno-guide.md
index d91f5b1..b816d6d 100644
--- a/doc/keepanno-guide.md
+++ b/doc/keepanno-guide.md
@@ -172,7 +172,7 @@
 
 
 ```
-public static class MyClassWithFields implements PrintableFieldInterface {
+public class MyClassWithFields implements PrintableFieldInterface {
   @UsedByReflection final int intField = 42;
 
   @UsedByReflection String stringField = "Hello!";
@@ -197,7 +197,7 @@
 @UsedByReflection(
     kind = KeepItemKind.ONLY_FIELDS,
     constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
-public static class MyClassWithFields implements PrintableFieldInterface {
+public class MyClassWithFields implements PrintableFieldInterface {
   final int intField = 42;
   String stringField = "Hello!";
 }
@@ -224,7 +224,7 @@
     },
     kind = KeepItemKind.ONLY_FIELDS,
     constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
-public static class MyClassWithFields implements PrintableFieldInterface {
+public class MyClassWithFields implements PrintableFieldInterface {
   final int intField = 42;
   String stringField = "Hello!";
 }
@@ -234,7 +234,68 @@
 
 ## Annotating APIs<a id="apis"></a>
 
-TODO
+If your code is being shrunk before release as a library, or if you have an API
+surface that is used via dynamic loading at runtime, then you need to keep the
+API surface. For that you should use the [@KeepForApi](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html) annotation.
+
+When annotating a class the default for [@KeepForApi](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html) is to keep the class as well as all of its
+public and protected members:
+
+
+```
+@KeepForApi
+public class MyApi {
+  public void thisPublicMethodIsKept() {
+    /* ... */
+  }
+
+  protected void thisProtectedMethodIsKept() {
+    /* ... */
+  }
+
+  void thisPackagePrivateMethodIsNotKept() {
+    /* ... */
+  }
+
+  private void thisPrivateMethodIsNotKept() {
+    /* ... */
+  }
+}
+```
+
+
+The default can be changed using the [@KeepForApi.memberAccess](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html#memberAccess()) property:
+
+
+```
+@KeepForApi(
+    memberAccess = {
+      MemberAccessFlags.PUBLIC,
+      MemberAccessFlags.PROTECTED,
+      MemberAccessFlags.PACKAGE_PRIVATE
+    })
+```
+
+
+The [@KeepForApi](https://storage.googleapis.com/r8-releases/raw/main/docs/keepanno/javadoc/com/android/tools/r8/keepanno/annotations/KeepForApi.html) annotation can also be placed directly on members and avoid keeping
+unannotated members. The holder class is implicitly kept. When annotating the members
+directly, the access does not matter as illustrated here by annotating a package private method:
+
+
+```
+public class MyOtherApi {
+
+  public void notKept() {
+    /* ... */
+  }
+
+  @KeepForApi
+  void isKept() {
+    /* ... */
+  }
+}
+```
+
 
 
 ## Migrating rules to annotations<a id="migrating-rules"></a>
diff --git a/doc/keepanno-guide.template.md b/doc/keepanno-guide.template.md
index 7b16a54..104bf02 100644
--- a/doc/keepanno-guide.template.md
+++ b/doc/keepanno-guide.template.md
@@ -108,7 +108,21 @@
 
 ## [Annotating APIs](apis)
 
-TODO
+If your code is being shrunk before release as a library, or if you have an API
+surface that is used via dynamic loading at runtime, then you need to keep the
+API surface. For that you should use the `@KeepForApi` annotation.
+
+[[[INCLUDE DOC:ApiClass]]]
+
+[[[INCLUDE CODE:ApiClass]]]
+
+[[[INCLUDE DOC:ApiClassMemberAccess]]]
+
+[[[INCLUDE CODE:ApiClassMemberAccess]]]
+
+[[[INCLUDE DOC:ApiMember]]]
+
+[[[INCLUDE CODE:ApiMember]]]
 
 
 ## [Migrating rules to annotations](migrating-rules)
diff --git a/src/test/java/com/android/tools/r8/keepanno/doctests/ConditionalMethodRulesAndHorizontalMergingTest.java b/src/test/java/com/android/tools/r8/keepanno/ConditionalMethodRulesAndHorizontalMergingTest.java
similarity index 88%
rename from src/test/java/com/android/tools/r8/keepanno/doctests/ConditionalMethodRulesAndHorizontalMergingTest.java
rename to src/test/java/com/android/tools/r8/keepanno/ConditionalMethodRulesAndHorizontalMergingTest.java
index ee1748d..8bf2e6a 100644
--- a/src/test/java/com/android/tools/r8/keepanno/doctests/ConditionalMethodRulesAndHorizontalMergingTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/ConditionalMethodRulesAndHorizontalMergingTest.java
@@ -1,15 +1,15 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2024, the R8 project authors. Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
-package com.android.tools.r8.keepanno.doctests;
+package com.android.tools.r8.keepanno;
 
 import com.android.tools.r8.TestBase;
 import com.android.tools.r8.TestParameters;
 import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.keepanno.doctests.ConditionalMethodRulesAndHorizontalMergingTest.Example1.BaseClass;
-import com.android.tools.r8.keepanno.doctests.ConditionalMethodRulesAndHorizontalMergingTest.Example1.MyHiddenMethodCaller;
-import com.android.tools.r8.keepanno.doctests.ConditionalMethodRulesAndHorizontalMergingTest.Example2.MyFieldValuePrinter;
-import com.android.tools.r8.keepanno.doctests.ConditionalMethodRulesAndHorizontalMergingTest.Example2.PrintableFieldInterface;
+import com.android.tools.r8.keepanno.ConditionalMethodRulesAndHorizontalMergingTest.Example1.BaseClass;
+import com.android.tools.r8.keepanno.ConditionalMethodRulesAndHorizontalMergingTest.Example1.MyHiddenMethodCaller;
+import com.android.tools.r8.keepanno.ConditionalMethodRulesAndHorizontalMergingTest.Example2.MyFieldValuePrinter;
+import com.android.tools.r8.keepanno.ConditionalMethodRulesAndHorizontalMergingTest.Example2.PrintableFieldInterface;
 import com.android.tools.r8.utils.AndroidApiLevel;
 import com.android.tools.r8.utils.StringUtils;
 import com.google.common.collect.ImmutableList;
diff --git a/src/test/java/com/android/tools/r8/keepanno/doctests/ForApiDocumentationTest.java b/src/test/java/com/android/tools/r8/keepanno/doctests/ForApiDocumentationTest.java
new file mode 100644
index 0000000..7df53d6
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/keepanno/doctests/ForApiDocumentationTest.java
@@ -0,0 +1,210 @@
+// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.keepanno.doctests;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.keepanno.annotations.KeepForApi;
+import com.android.tools.r8.keepanno.annotations.MemberAccessFlags;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class ForApiDocumentationTest extends TestBase {
+
+  static final String EXPECTED_1_REF =
+      StringUtils.joinLines(
+          "thisPackagePrivateMethodIsNotKept",
+          "thisPrivateMethodIsNotKept",
+          "thisProtectedMethodIsKept",
+          "thisPublicMethodIsKept");
+
+  static final String EXPECTED_1_R8 =
+      StringUtils.joinLines("thisProtectedMethodIsKept", "thisPublicMethodIsKept");
+
+  static final String EXPECTED_2_REF =
+      StringUtils.joinLines(
+          "thisPackagePrivateMethodIsKept",
+          "thisPrivateMethodIsNotKept",
+          "thisProtectedMethodIsKept",
+          "thisPublicMethodIsKept");
+
+  static final String EXPECTED_2_R8 =
+      StringUtils.joinLines(
+          "thisPackagePrivateMethodIsKept", "thisProtectedMethodIsKept", "thisPublicMethodIsKept");
+
+  static final String EXPECTED_3_REF = StringUtils.joinLines("isKept", "notKept");
+
+  static final String EXPECTED_3_R8 = StringUtils.joinLines("isKept");
+
+  static final String EXPECTED_REF =
+      StringUtils.lines(EXPECTED_1_REF, EXPECTED_2_REF, EXPECTED_3_REF);
+
+  static final String EXPECTED_R8 = StringUtils.lines(EXPECTED_1_R8, EXPECTED_2_R8, EXPECTED_3_R8);
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withDefaultRuntimes().withApiLevel(AndroidApiLevel.B).build();
+  }
+
+  public ForApiDocumentationTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void testReference() throws Exception {
+    testForRuntime(parameters)
+        .addProgramClasses(TestClass.class)
+        .addProgramClassesAndInnerClasses(getExampleClasses())
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_REF);
+  }
+
+  @Test
+  public void testWithRuleExtraction() throws Exception {
+    testForR8(parameters.getBackend())
+        .enableExperimentalKeepAnnotations()
+        .addProgramClasses(TestClass.class)
+        .addProgramClassesAndInnerClasses(getExampleClasses())
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertSuccessWithOutput(EXPECTED_R8);
+  }
+
+  public List<Class<?>> getExampleClasses() {
+    return ImmutableList.of(Example1.class, Example2.class, Example3.class);
+  }
+
+  static class Example1 {
+
+    /* INCLUDE DOC: ApiClass
+    When annotating a class the default for `@KeepForApi` is to keep the class as well as all of its
+    public and protected members:
+    INCLUDE END */
+
+    static
+    // INCLUDE CODE: ApiClass
+    @KeepForApi
+    public class MyApi {
+      public void thisPublicMethodIsKept() {
+        /* ... */
+      }
+
+      protected void thisProtectedMethodIsKept() {
+        /* ... */
+      }
+
+      void thisPackagePrivateMethodIsNotKept() {
+        /* ... */
+      }
+
+      private void thisPrivateMethodIsNotKept() {
+        /* ... */
+      }
+    }
+
+    // INCLUDE END
+
+    static void run() throws Exception {
+      TestClass.printMethods(MyApi.class);
+    }
+  }
+
+  static class Example2 {
+
+    /* INCLUDE DOC: ApiClassMemberAccess
+    The default can be changed using the `@KeepForApi#memberAccess` property:
+    INCLUDE END */
+
+    // INCLUDE CODE: ApiClassMemberAccess
+    @KeepForApi(
+        memberAccess = {
+          MemberAccessFlags.PUBLIC,
+          MemberAccessFlags.PROTECTED,
+          MemberAccessFlags.PACKAGE_PRIVATE
+        })
+    // INCLUDE END
+    public static class MyApi {
+      public void thisPublicMethodIsKept() {
+        /* ... */
+      }
+
+      protected void thisProtectedMethodIsKept() {
+        /* ... */
+      }
+
+      void thisPackagePrivateMethodIsKept() {
+        /* ... */
+      }
+
+      private void thisPrivateMethodIsNotKept() {
+        /* ... */
+      }
+    }
+
+    static void run() throws Exception {
+      TestClass.printMethods(MyApi.class);
+    }
+  }
+
+  static class Example3 {
+
+    /* INCLUDE DOC: ApiMember
+    The `@KeepForApi` annotation can also be placed directly on members and avoid keeping
+    unannotated members. The holder class is implicitly kept. When annotating the members
+    directly, the access does not matter as illustrated here by annotating a package private method:
+    INCLUDE END */
+
+    static
+    // INCLUDE CODE: ApiMember
+    public class MyOtherApi {
+
+      public void notKept() {
+        /* ... */
+      }
+
+      @KeepForApi
+      void isKept() {
+        /* ... */
+      }
+    }
+
+    // INCLUDE END
+
+    static void run() throws Exception {
+      TestClass.printMethods(MyOtherApi.class);
+    }
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) throws Exception {
+      Example1.run();
+      Example2.run();
+      Example3.run();
+    }
+
+    static void printMethods(Class<?> clazz) {
+      List<String> names = new ArrayList<>();
+      for (Method m : clazz.getDeclaredMethods()) {
+        names.add(m.getName());
+      }
+      names.sort(String::compareTo);
+      for (String name : names) {
+        System.out.println(name);
+      }
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java b/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java
index 034ac0a..98a2f0d 100644
--- a/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java
+++ b/src/test/java/com/android/tools/r8/keepanno/doctests/UsesReflectionDocumentationTest.java
@@ -187,8 +187,9 @@
     We elide it here for brevity.
     INCLUDE END */
 
+    static
     // INCLUDE CODE: UsedByReflectionFieldPrinterOnFields
-    public static class MyClassWithFields implements PrintableFieldInterface {
+    public class MyClassWithFields implements PrintableFieldInterface {
       @UsedByReflection final int intField = 42;
 
       @UsedByReflection String stringField = "Hello!";
@@ -224,11 +225,12 @@
     that the fields are looked up, their names are used/assumed and their values are read.
     INCLUDE END */
 
+    static
     // INCLUDE CODE: UsedByReflectionFieldPrinterOnClass
     @UsedByReflection(
         kind = KeepItemKind.ONLY_FIELDS,
         constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
-    public static class MyClassWithFields implements PrintableFieldInterface {
+    public class MyClassWithFields implements PrintableFieldInterface {
       final int intField = 42;
       String stringField = "Hello!";
     }
@@ -265,6 +267,7 @@
     Luckily we can specify the same precondition using `@UsedByReflection#preconditions`.
     INCLUDE END */
 
+    static
     // INCLUDE CODE: UsedByReflectionFieldPrinterConditional
     @UsedByReflection(
         preconditions = {
@@ -274,7 +277,7 @@
         },
         kind = KeepItemKind.ONLY_FIELDS,
         constraints = {KeepConstraint.LOOKUP, KeepConstraint.NAME, KeepConstraint.FIELD_GET})
-    public static class MyClassWithFields implements PrintableFieldInterface {
+    public class MyClassWithFields implements PrintableFieldInterface {
       final int intField = 42;
       String stringField = "Hello!";
     }
diff --git a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
index 9088752..d8b5730 100644
--- a/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
+++ b/src/test/java/com/android/tools/r8/keepanno/utils/KeepAnnoMarkdownGenerator.java
@@ -20,6 +20,7 @@
 import com.android.tools.r8.keepanno.annotations.UsedByNative;
 import com.android.tools.r8.keepanno.annotations.UsedByReflection;
 import com.android.tools.r8.keepanno.annotations.UsesReflection;
+import com.android.tools.r8.keepanno.doctests.ForApiDocumentationTest;
 import com.android.tools.r8.keepanno.doctests.MainMethodsDocumentationTest;
 import com.android.tools.r8.keepanno.doctests.UsesReflectionDocumentationTest;
 import com.android.tools.r8.keepanno.utils.KeepItemAnnotationGenerator.Generator;
@@ -92,7 +93,9 @@
             MethodAccessFlags.class,
             FieldAccessFlags.class);
     populateCodeAndDocReplacements(
-        UsesReflectionDocumentationTest.class, MainMethodsDocumentationTest.class);
+        UsesReflectionDocumentationTest.class,
+        ForApiDocumentationTest.class,
+        MainMethodsDocumentationTest.class);
   }
 
   private Map<String, String> getTypeLinkReplacements(Class<?>... classes) {