Merge "[BottomAppBar] Fix bar not disappearing entirely when scrolling" into androidx-main
diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml
index cb49084..6e8acfd 100644
--- a/.github/workflows/integration_tests.yml
+++ b/.github/workflows/integration_tests.yml
@@ -16,7 +16,7 @@
           java-version: '17'
           distribution: 'zulu'
       - name: "set output directory"
-        run: echo "::set-output name=output-dir::$(readlink -f .)/outputs"
+        run: echo "output-dir=$(readlink -f .)/outputs" >> $GITHUB_OUTPUT
         id: dirs
       - id: run_tests
         uses: androidX/androidx-ci-action@dist-latest
@@ -48,9 +48,9 @@
           if [ "${{ needs.run_integration_tests.result }}" == "success" ]                  && \
             [ "${{ github.event.workflow_run.conclusion }}" == "success" ]
           then
-            echo "::set-output name=result::true"
+            echo "result=true" >> $GITHUB_OUTPUT
           else
-            echo "::set-output name=result::false"
+            echo "result=false" >> GITHUB_OUTPUT
           fi
 
       - name: Result WebHook
diff --git a/.github/workflows/jbpresubmit.yml b/.github/workflows/jbpresubmit.yml
index 047eb64..4c81d52 100644
--- a/.github/workflows/jbpresubmit.yml
+++ b/.github/workflows/jbpresubmit.yml
@@ -42,20 +42,20 @@
             -Dorg.gradle.internal.repository.initial.backoff=500            \
             -Dorg.gradle.jvmargs=-XX:MaxMetaspaceSize=512m                  \
             --stacktrace"
-          echo "::set-output name=gradlew_flags::$GRADLEW_FLAGS"
+          echo "gradlew_flags=GRADLEW_FLAGS" >> $GITHUB_OUTPUT
       - name: "Compute actions/checkout arguments"
         id: checkout-args
         env:
           REF: ${{ github.event.pull_request.head.ref || github.event.ref}}
         run: |
           set -x
-          echo "::set-output name=ref::$REF"
+          echo "ref=$REF" >> $GITHUB_OUTPUT
 
           REPOSITORY=${{ github.event.pull_request.head.repo.full_name }}
           if [ -z "$REPOSITORY" ]; then
             REPOSITORY=${{ github.repository }}
           fi
-          echo "::set-output name=repository::$REPOSITORY"
+          echo "repository=$REPOSITORY" >> $GITHUB_OUTPUT
 
   lint:
     runs-on: ubuntu-latest
@@ -110,14 +110,14 @@
         run: |
           set -x
           KTFMT_FILES=`echo "${{ steps.changed-files.outputs.files }}" | sed 's|[^ ]* *|--file=../&|g' | grep -v "*.txt"`
-          echo "::set-output name=ktfmt-file-args::$KTFMT_FILES"
+          echo "ktfmt-file-args=KTFMT_FILES" >> $GITHUB_OUTPUT
 
       - name: "Parse changed-files as affected files args"
         id: affected-file-args
         run: |
           set -x
           AFFECTED_FILES=`echo "${{ steps.changed-files.outputs.files_including_removals }}" | sed 's|\([^ ]\+\)|--changedFilePath=\1|g'`
-          echo "::set-output name=files::$AFFECTED_FILES"
+          echo "files=AFFECTED_FILES" >> $GITHUB_OUTPUT
       - name: "warm up gradle"
         id: warm-up-gradle-cache
         uses: gradle/gradle-command-action@v1
diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml
index a422ae8..a99b1d4 100644
--- a/.github/workflows/presubmit.yml
+++ b/.github/workflows/presubmit.yml
@@ -23,7 +23,7 @@
             -Dorg.gradle.internal.repository.max.retries=20                 \
             -Dorg.gradle.internal.repository.initial.backoff=500            \
             --stacktrace"
-          echo "::set-output name=gradlew_flags::$GRADLEW_FLAGS"
+          echo "gradlew_flags=$GRADLEW_FLAGS" >> $GITHUB_OUTPUT
       - name: Publish build scans link
         # No scans are produced for PRs from forked repos, so omit this notice for forked PRs.
         if: ${{ !(github.event.pull_request && github.event.pull_request.head.repo.fork) }}
@@ -80,14 +80,14 @@
         run: |
           set -x
           KTFMT_FILES=`echo "${{ steps.changed-files.outputs.files }}" | sed 's|[^ ]* *|--file=../&|g' | grep -v "*.txt"`
-          echo "::set-output name=ktfmt-file-args::$KTFMT_FILES"
+          echo "ktfmt-file-args=$KTFMT_FILES" >> $GITHUB_OUTPUT
 
       - name: "Parse changed-files as affected files args"
         id: affected-file-args
         run: |
           set -x
           AFFECTED_FILES=`echo "${{ steps.changed-files.outputs.files_including_removals }}" | sed 's|\([^ ]\+\)|--changedFilePath=\1|g'`
-          echo "::set-output name=files::$AFFECTED_FILES"
+          echo "files=$AFFECTED_FILES" >> $GITHUB_OUTPUT
 
       - name: "Setup Gradle"
         uses: gradle/gradle-build-action@v3-beta
@@ -146,7 +146,7 @@
         run: |
           set -x
           RESULT=`.github/ci-control/should_run_project.py --project ${{ matrix.project }} --branch "$BRANCH_REF"`
-          echo "::set-output name=enabled::$RESULT"
+          echo "enabled=$RESULT" >> $GITHUB_OUTPUT
       - name: "Run build"
         uses: ./.github/actions/build-single-project
         if: ${{ steps.check-ci-config.outputs.enabled == 'true' }}
diff --git a/.github/workflows/update_prebuilts.yml b/.github/workflows/update_prebuilts.yml
index e6c5cde..8a32e87 100644
--- a/.github/workflows/update_prebuilts.yml
+++ b/.github/workflows/update_prebuilts.yml
@@ -33,7 +33,7 @@
           set -x
           git status
           HAS_CHANGES=`git status --porcelain|grep playground\.properties|wc -l`
-          echo "::set-output name=has_changes::$HAS_CHANGES"
+          echo "has_changes=HAS_CHANGES" >> $GITHUB_OUTPUT
       - name: "Create a PR"
         shell: bash
         id: "create-pr"
diff --git a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
index 35e4404..a1d8954 100644
--- a/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
+++ b/appsearch/appsearch-local-storage/src/main/java/androidx/appsearch/localstorage/AlwaysSupportedFeatures.java
@@ -83,6 +83,8 @@
             case Features.SET_SCHEMA_REQUEST_ADD_SCHEMA_TYPE_VISIBLE_TO_CONFIG:
                 // fall through
             case Features.SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS:
+                // fall through
+            case Features.BLOB_STORAGE:
                 return true;
             default:
                 return false;
diff --git a/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchTestUtils.java b/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchTestUtils.java
index 7391cbb..1e52384 100644
--- a/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchTestUtils.java
+++ b/appsearch/appsearch-test-util/src/main/java/androidx/appsearch/testutil/AppSearchTestUtils.java
@@ -31,10 +31,13 @@
 import androidx.appsearch.localstorage.visibilitystore.VisibilityChecker;
 import androidx.appsearch.localstorage.visibilitystore.VisibilityStore;
 
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Future;
+import java.util.concurrent.ThreadLocalRandom;
 
 /**
  * Class with helper functions for testing for AppSearch.
@@ -43,9 +46,6 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public class AppSearchTestUtils {
-    private AppSearchTestUtils() {
-    }
-
     /** Checks batch result. */
     @NonNull
     public static <K, V> AppSearchBatchResult<K, V> checkIsBatchResultSuccess(
@@ -169,4 +169,24 @@
             }
         };
     }
+
+    /** Generate an array contains random bytes for the given length.     */
+    @NonNull
+    public static byte[] generateRandomBytes(int length) {
+        byte[] bytes = new byte[length];
+        ThreadLocalRandom.current().nextBytes(bytes);
+        return bytes;
+    }
+
+    /** Calculate the sha-256 digest for the given data.     */
+    @NonNull
+    public static byte[] calculateDigest(@NonNull byte[] data)
+            throws NoSuchAlgorithmException {
+        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+        messageDigest.update(data);
+        return messageDigest.digest();
+    }
+
+    private AppSearchTestUtils() {
+    }
 }
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchBlobHandleInternalTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchBlobHandleInternalTest.java
new file mode 100644
index 0000000..f96487e
--- /dev/null
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/app/AppSearchBlobHandleInternalTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 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 androidx.appsearch.app;
+
+import static androidx.appsearch.testutil.AppSearchTestUtils.calculateDigest;
+import static androidx.appsearch.testutil.AppSearchTestUtils.generateRandomBytes;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+//TODO(b/273591938) move this to cts test once it's public.
+public class AppSearchBlobHandleInternalTest {
+
+    @Test
+    public void testCreateBlobHandle() throws Exception {
+        byte[] data = generateRandomBytes(10); // 10 Bytes
+        byte[] digest = calculateDigest(data);
+        AppSearchBlobHandle blobHandle = AppSearchBlobHandle.createWithSha256(digest, "label123");
+        assertThat(blobHandle.getLabel()).isEqualTo("label123");
+        assertThat(blobHandle.getSha256Digest()).isEqualTo(digest);
+    }
+
+    @Test
+    public void testBlobHandleIdentical() throws Exception {
+        byte[] data1 = {(byte) 1};
+        byte[] data2 = {(byte) 2};
+        byte[] digest1 = calculateDigest(data1);
+        byte[] digest2 = calculateDigest(data2);
+        AppSearchBlobHandle blobHandle1 = AppSearchBlobHandle.createWithSha256(digest1, "label123");
+        AppSearchBlobHandle blobHandle2 = AppSearchBlobHandle.createWithSha256(digest1, "label123");
+        AppSearchBlobHandle blobHandle3 = AppSearchBlobHandle.createWithSha256(digest1, "321lebal");
+        AppSearchBlobHandle blobHandle4 = AppSearchBlobHandle.createWithSha256(digest2, "label123");
+        assertThat(blobHandle1).isEqualTo(blobHandle2);
+        assertThat(blobHandle1).isNotEqualTo(blobHandle3);
+        assertThat(blobHandle1).isNotEqualTo(blobHandle4);
+        assertThat(blobHandle3).isNotEqualTo(blobHandle4);
+        assertThat(blobHandle1.hashCode()).isEqualTo(blobHandle2.hashCode());
+        assertThat(blobHandle1.hashCode()).isNotEqualTo(blobHandle3.hashCode());
+        assertThat(blobHandle1.hashCode()).isNotEqualTo(blobHandle4.hashCode());
+        assertThat(blobHandle3.hashCode()).isNotEqualTo(blobHandle4.hashCode());
+    }
+
+    @Test
+    public void testCreateBlobHandle_invalidDigest() throws Exception {
+        IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
+                () -> AppSearchBlobHandle.createWithSha256(new byte[10], "label123"));
+        assertThat(exception).hasMessageThat().contains("The digest is not a SHA-256 digest");
+    }
+
+    @Test
+    public void testCreateBlobHandle_emptyLabel() throws Exception {
+        byte[] data = {(byte) 1};
+        byte[] digest = calculateDigest(data);
+        AppSearchBlobHandle blobHandle1 = AppSearchBlobHandle.createWithSha256(digest);
+        AppSearchBlobHandle blobHandle2 =
+                AppSearchBlobHandle.createWithSha256(digest, /*label=*/"");
+        assertThat(blobHandle1).isEqualTo(blobHandle2);
+        assertThat(blobHandle1.hashCode()).isEqualTo(blobHandle2.hashCode());
+    }
+}
diff --git a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java
index 06caa38..958b2a7 100644
--- a/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java
+++ b/appsearch/appsearch/src/androidTest/java/androidx/appsearch/flags/FlagsTest.java
@@ -127,4 +127,10 @@
         assertThat(Flags.FLAG_ENABLE_RESULT_ALREADY_EXISTS)
                 .isEqualTo("com.android.appsearch.flags.enable_result_already_exists");
     }
+
+    @Test
+    public void testFlagValue_enableBlobStore() {
+        assertThat(Flags.FLAG_ENABLE_BLOB_STORE)
+                .isEqualTo("com.android.appsearch.flags.enable_blob_store");
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBlobHandle.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBlobHandle.java
new file mode 100644
index 0000000..d6ed1c2
--- /dev/null
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/AppSearchBlobHandle.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 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 androidx.appsearch.app;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresFeature;
+import androidx.annotation.RestrictTo;
+import androidx.appsearch.flags.FlaggedApi;
+import androidx.appsearch.flags.Flags;
+import androidx.appsearch.safeparcel.AbstractSafeParcelable;
+import androidx.appsearch.safeparcel.SafeParcelable;
+import androidx.appsearch.safeparcel.stub.StubCreators.AppSearchBlobHandleCreator;
+import androidx.core.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * An identifier to represent a Blob in AppSearch.
+ *
+ * @exportToFramework:hide
+ */
+// TODO(b/273591938) improve the java doc when we support set blob property in GenericDocument
+// TODO(b/273591938) unhide the API once it read for API review.
+@RequiresFeature(
+        enforcement = "androidx.appsearch.app.Features#isFeatureSupported",
+        name = Features.BLOB_STORAGE)
+@FlaggedApi(Flags.FLAG_ENABLE_BLOB_STORE)
[email protected](creator = "AppSearchBlobHandleCreator")
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+public class AppSearchBlobHandle extends AbstractSafeParcelable {
+    /** The length of the SHA-256 digest in bytes. SHA-256 produces a 256-bit (32-byte) digest. */
+    private static final int SHA_256_DIGEST_BYTE_LENGTH = 32;
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @NonNull
+    public static final Parcelable.Creator<AppSearchBlobHandle> CREATOR =
+            new AppSearchBlobHandleCreator();
+    @NonNull
+    @Field(id = 1, getter = "getSha256Digest")
+    private final byte[] mSha256Digest;
+
+    @NonNull
+    @Field(id = 2, getter = "getLabel")
+    private final String mLabel;
+
+    @Nullable
+    private Integer mHashCode;
+
+    /**
+     * Build an {@link AppSearchBlobHandle}.
+     * @exportToFramework:hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Constructor
+    private AppSearchBlobHandle(
+            @Param(id = 1) @NonNull byte[] sha256Digest,
+            @Param(id = 2) @NonNull String label) {
+        mSha256Digest = Preconditions.checkNotNull(sha256Digest);
+        Preconditions.checkState(sha256Digest.length == SHA_256_DIGEST_BYTE_LENGTH,
+                "The input digest isn't a sha-256 digest.");
+        mLabel = Preconditions.checkNotNull(label);
+    }
+
+    /**
+     * Returns the SHA-256 hash of the blob that this object is representing.
+     *
+     * <p> For two objects of {@link AppSearchBlobHandle} to be considered equal, the {@code digest}
+     * and {@code label} must be equal.
+     */
+    @NonNull
+    public byte[] getSha256Digest() {
+        return mSha256Digest;
+    }
+
+    /**
+     * Returns the label indicating what the blob is with the blob that this object is representing.
+     *
+     * <p> The label is just a simple string which contains more readable information for the
+     * digest. The string is used to indicate and describe the content represented by the digest.
+     * The label cannot be used to search {@link AppSearchBlobHandle}.
+     *
+     * <p> If the label is not set, then this method will return an empty string.
+     *
+     * <p> For two objects of {@link AppSearchBlobHandle} to be considered equal, the {@code digest}
+     * and {@code label} must be equal.
+     */
+    @NonNull
+    public String getLabel() {
+        return mLabel;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AppSearchBlobHandle)) return false;
+
+        AppSearchBlobHandle that = (AppSearchBlobHandle) o;
+        if (!Arrays.equals(mSha256Digest, that.mSha256Digest)) return false;
+        return mLabel.equals(that.mLabel);
+    }
+
+    @Override
+    public int hashCode() {
+        if (mHashCode == null) {
+            mHashCode = Objects.hash(Arrays.hashCode(mSha256Digest), mLabel);
+        }
+        return mHashCode;
+    }
+
+    /**
+     * Create a new AppSearch blob identifier with given digest and empty label.
+     *
+     * <p> For two objects of {@link AppSearchBlobHandle} to be considered equal, the {@code digest}
+     * and {@code label} must be equal.
+     *
+     * @param digest the SHA-256 hash of the blob this is representing.
+     *
+     * @return a new instance of {@link AppSearchBlobHandle} object.
+     */
+    @NonNull
+    public static AppSearchBlobHandle createWithSha256(@NonNull byte[] digest) {
+        return new AppSearchBlobHandle(digest, /*label=*/"");
+    }
+
+    /**
+     * Create a new AppSearch blob identifier with given digest and label.
+     *
+     * <p> The label is just a simple string which contains more readable information for the
+     * digest. The string is used to indicate and describe the content represented by the digest.
+     * The label cannot be used to search {@link AppSearchBlobHandle}.
+     *
+     * <p> For two objects of {@link AppSearchBlobHandle} to be considered equal, the {@code digest}
+     * and {@code label} must be equal.
+     *
+     * @param digest the SHA-256 hash of the blob this is representing.
+     * @param label  a label indicating what the blob is, that can be surfaced to the user. It is
+     *               recommended to keep this brief. The label doesn't need to be distinct.
+     *
+     * @return a new instance of {@link AppSearchBlobHandle} object.
+     */
+    @NonNull
+    public static AppSearchBlobHandle createWithSha256(@NonNull byte[] digest,
+            @NonNull String label) {
+        Preconditions.checkNotNull(digest);
+        Preconditions.checkArgument(digest.length == SHA_256_DIGEST_BYTE_LENGTH,
+                "The digest is not a SHA-256 digest");
+        Preconditions.checkNotNull(label);
+        return new AppSearchBlobHandle(digest, label);
+    }
+
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        AppSearchBlobHandleCreator.writeToParcel(this, dest, flags);
+    }
+}
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java b/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
index ac1de38..cf932e9 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/app/Features.java
@@ -16,6 +16,7 @@
 package androidx.appsearch.app;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
 
 import java.util.Set;
 
@@ -234,6 +235,15 @@
             "SEARCH_SPEC_ADD_INFORMATIONAL_RANKING_EXPRESSIONS";
 
     /**
+     * Feature for {@link #isFeatureSupported(String)}. This feature covers
+     * {@link AppSearchBlobHandle}.
+     */
+    // TODO(b/273591938) improve the java doc when we support set blob property in GenericDocument
+    // TODO(b/273591938) unhide the API once it read for API review.
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    String BLOB_STORAGE = "BLOB_STORAGE";
+
+    /**
      * Returns whether a feature is supported at run-time. Feature support depends on the
      * feature in question, the AppSearch backend being used and the Android version of the
      * device.
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java b/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java
index eaf90b6..3f98aaf 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/flags/Flags.java
@@ -138,6 +138,10 @@
     public static final String FLAG_ENABLE_RESULT_ALREADY_EXISTS =
             FLAG_PREFIX + "enable_result_already_exists";
 
+    /**  Enable {@link androidx.appsearch.app.AppSearchBlobHandle}.  */
+    public static final String FLAG_ENABLE_BLOB_STORE =
+            FLAG_PREFIX + "enable_blob_store";
+
     // Whether the features should be enabled.
     //
     // In Jetpack, those should always return true.
@@ -254,4 +258,9 @@
     public static boolean enableResultAlreadyExists() {
         return true;
     }
+
+    /**  Whether {@link androidx.appsearch.app.AppSearchBlobHandle} should be enabled. */
+    public static boolean enableBlobStore() {
+        return true;
+    }
 }
diff --git a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
index 48d197c..a9b33eb 100644
--- a/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
+++ b/appsearch/appsearch/src/main/java/androidx/appsearch/safeparcel/stub/StubCreators.java
@@ -16,6 +16,7 @@
 package androidx.appsearch.safeparcel.stub;
 
 import androidx.annotation.RestrictTo;
+import androidx.appsearch.app.AppSearchBlobHandle;
 import androidx.appsearch.app.AppSearchSchema;
 import androidx.appsearch.app.EmbeddingVector;
 import androidx.appsearch.app.GetByDocumentIdRequest;
@@ -195,4 +196,9 @@
     public static class EmbeddingVectorCreator extends
             AbstractCreator<EmbeddingVector> {
     }
+
+    /** Stub creator for {@link androidx.appsearch.app.AppSearchBlobHandle}. */
+    public static class AppSearchBlobHandleCreator extends
+            AbstractCreator<AppSearchBlobHandle> {
+    }
 }
diff --git a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
index 2878a0a..fc41186 100644
--- a/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
+++ b/benchmark/benchmark-common/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
@@ -17,6 +17,7 @@
 package androidx.benchmark
 
 import android.Manifest
+import androidx.annotation.RequiresApi
 import androidx.benchmark.BenchmarkState.Companion.ExperimentalExternalReport
 import androidx.benchmark.json.BenchmarkData
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -285,6 +286,7 @@
         assertEquals(expectedReport, ResultWriter.reports.last())
     }
 
+    @RequiresApi(22) // 21 profiler has flaky platform crashes, see b/353716346
     private fun validateProfilerUsage(simplifiedTimingOnlyMode: Boolean?) {
         val config = MicrobenchmarkConfig(profiler = ProfilerConfig.StackSamplingLegacy())
 
@@ -327,11 +329,17 @@
         }
     }
 
-    @Test fun profiler_default() = validateProfilerUsage(null)
+    @SdkSuppress(minSdkVersion = 22) // 21 profiler has flaky platform crashes, see b/353716346
+    @Test
+    fun profiler_default() = validateProfilerUsage(null)
 
-    @Test fun profiler_false() = validateProfilerUsage(false)
+    @SdkSuppress(minSdkVersion = 22) // 21 profiler has flaky platform crashes, see b/353716346
+    @Test
+    fun profiler_false() = validateProfilerUsage(false)
 
-    @Test fun profiler_true() = validateProfilerUsage(true)
+    @SdkSuppress(minSdkVersion = 22) // 21 profiler has flaky platform crashes, see b/353716346
+    @Test
+    fun profiler_true() = validateProfilerUsage(true)
 
     @OptIn(ExperimentalBenchmarkStateApi::class)
     @Test
diff --git a/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/JankCollectionHelperTest.kt b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/JankCollectionHelperTest.kt
new file mode 100644
index 0000000..65f90419
--- /dev/null
+++ b/benchmark/benchmark-macro/src/androidTest/java/androidx/benchmark/macro/JankCollectionHelperTest.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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 androidx.benchmark.macro
+
+import androidx.test.filters.MediumTest
+import org.junit.Test
+
+@MediumTest
+class JankCollectionHelperTest {
+    @Test
+    fun clearGfxInfo_thisProcess() {
+        JankCollectionHelper().clearGfxInfo(Packages.TEST)
+    }
+
+    @Test
+    fun clearGfxInfo_notRunningPackage() {
+        // shouldn't fail, clearing a package that isn't running
+        // (or in this case, installed) is a noop
+        JankCollectionHelper().clearGfxInfo(Packages.MISSING)
+    }
+}
diff --git a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/JankCollectionHelper.java b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/JankCollectionHelper.java
index d0b11e8..fff2766 100644
--- a/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/JankCollectionHelper.java
+++ b/benchmark/benchmark-macro/src/main/java/androidx/benchmark/macro/JankCollectionHelper.java
@@ -282,9 +282,13 @@
             } else {
                 String command = String.format(GFXINFO_COMMAND_RESET, pkg);
                 String output = getDevice().executeShellCommand(command);
-                // Success if the specified package header exists in the output.
-                verifyMatches(output, getHeaderMatcher(pkg), "No package header in output.");
-                Log.v(LOG_TAG, String.format("Cleared %s gfxinfo.", pkg));
+                if (output.trim().equals("No process found for: " + pkg)) {
+                    Log.v(LOG_TAG, "Skipped clearing " + pkg + " gfxinfo, not running.");
+                } else {
+                    // Success if the specified package header exists in the output.
+                    verifyMatches(output, getHeaderMatcher(pkg), "No package header in output.");
+                    Log.v(LOG_TAG, String.format("Cleared %s gfxinfo.", pkg));
+                }
             }
         } catch (IOException e) {
             throw new RuntimeException("Failed to clear gfxinfo.", e);
diff --git a/busytown/androidx_with_metalava.sh b/busytown/androidx_with_metalava.sh
index 98623e8..fa03981 100755
--- a/busytown/androidx_with_metalava.sh
+++ b/busytown/androidx_with_metalava.sh
@@ -4,7 +4,7 @@
 
 # Use this flag to temporarily disable `checkApi`
 # while landing Metalava w/ breaking API changes
-METALAVA_INTEGRATION_ENFORCED=false
+METALAVA_INTEGRATION_ENFORCED=true
 
 # The default targets to build if no arguments
 # are provided on the command line.
diff --git a/camera/camera-camera2-pipe-integration/build.gradle b/camera/camera-camera2-pipe-integration/build.gradle
index 7500b67..d426d2a 100644
--- a/camera/camera-camera2-pipe-integration/build.gradle
+++ b/camera/camera-camera2-pipe-integration/build.gradle
@@ -107,6 +107,5 @@
     description = "A Camera2 Pipe implementation of CameraX, a library providing a consistent " +
             "and reliable camera foundation that enables great camera driven experiences across " +
             "all of Android."
-    legacyDisableKotlinStrictApiMode = true
     doNotDocumentReason = "Not shipped externally"
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/CameraPipeConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/CameraPipeConfig.kt
index 562bad6..0328232 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/CameraPipeConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/CameraPipeConfig.kt
@@ -25,18 +25,18 @@
 import androidx.camera.core.impl.CameraThreadConfig
 
 /** Convenience class for generating a pre-populated CameraPipe based [CameraXConfig]. */
-class CameraPipeConfig private constructor() {
-    companion object {
+public class CameraPipeConfig private constructor() {
+    public companion object {
         /** Creates a [CameraXConfig] containing a default CameraPipe implementation for CameraX. */
         @JvmStatic
-        fun defaultConfig(): CameraXConfig {
+        public fun defaultConfig(): CameraXConfig {
             return from()
         }
 
         /** Creates a [CameraXConfig] using a pre-existing [CameraPipe] instance. */
         @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
         @JvmStatic
-        fun from(
+        public fun from(
             sharedCameraPipe: CameraPipe? = null,
             sharedAppContext: Context? = null,
             sharedThreadConfig: CameraThreadConfig? = null
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt
index 82aab18..b6e55c4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt
@@ -62,7 +62,7 @@
 @SuppressLint("UnsafeOptInUsageError")
 @CameraScope
 @OptIn(ExperimentalCoroutinesApi::class, ExperimentalCamera2Interop::class)
-class CameraControlAdapter
+public class CameraControlAdapter
 @Inject
 constructor(
     private val cameraProperties: CameraProperties,
@@ -74,7 +74,7 @@
     private val threads: UseCaseThreads,
     private val zoomControl: ZoomControl,
     private val zslControl: ZslControl,
-    val camera2cameraControl: Camera2CameraControl,
+    public val camera2cameraControl: Camera2CameraControl,
 ) : CameraControlInternal {
     override fun getSensorRect(): Rect {
         return cameraProperties.metadata[CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE]!!
@@ -162,7 +162,8 @@
         captureConfigs: List<CaptureConfig>,
         @ImageCapture.CaptureMode captureMode: Int,
         @ImageCapture.FlashType flashType: Int,
-    ) = stillCaptureRequestControl.issueCaptureRequests(captureConfigs, captureMode, flashType)
+    ): ListenableFuture<List<Void?>> =
+        stillCaptureRequestControl.issueCaptureRequests(captureConfigs, captureMode, flashType)
 
     override fun getSessionConfig(): SessionConfig {
         warn { "TODO: getSessionConfig is not yet supported" }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlStateAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlStateAdapter.kt
index 17f13b8..3c24880 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlStateAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlStateAdapter.kt
@@ -31,19 +31,19 @@
  */
 @SuppressLint("UnsafeOptInUsageError")
 @CameraScope
-class CameraControlStateAdapter
+public class CameraControlStateAdapter
 @Inject
 constructor(
     private val zoomControl: ZoomControl,
     private val evCompControl: EvCompControl,
     private val torchControl: TorchControl,
 ) {
-    val torchStateLiveData: LiveData<Int>
+    public val torchStateLiveData: LiveData<Int>
         get() = torchControl.torchStateLiveData
 
-    val zoomStateLiveData: LiveData<ZoomState>
+    public val zoomStateLiveData: LiveData<ZoomState>
         get() = zoomControl.zoomStateLiveData
 
-    val exposureState: ExposureState
+    public val exposureState: ExposureState
         get() = evCompControl.exposureState
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
index 00c2e89..b85ea7c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraCoordinatorAdapter.kt
@@ -32,21 +32,24 @@
 import androidx.camera.core.concurrent.CameraCoordinator.CameraOperatingMode
 import androidx.camera.core.impl.CameraInternal
 
-class CameraCoordinatorAdapter(
+public class CameraCoordinatorAdapter(
     private var cameraPipe: CameraPipe?,
     cameraDevices: CameraDevices,
 ) : CameraCoordinator {
-    @VisibleForTesting val cameraInternalMap = mutableMapOf<CameraId, CameraInternalAdapter>()
+    @VisibleForTesting
+    public val cameraInternalMap: MutableMap<CameraId, CameraInternalAdapter> = mutableMapOf()
 
-    @VisibleForTesting var concurrentCameraIdsSet = mutableSetOf<Set<CameraId>>()
+    @VisibleForTesting public var concurrentCameraIdsSet: MutableSet<Set<CameraId>> = mutableSetOf()
 
-    @VisibleForTesting var concurrentCameraIdMap = mutableMapOf<String, MutableList<String>>()
+    @VisibleForTesting
+    public var concurrentCameraIdMap: MutableMap<String, MutableList<String>> = mutableMapOf()
 
-    @VisibleForTesting var activeConcurrentCameraInfosList = mutableListOf<CameraInfo>()
+    @VisibleForTesting
+    public var activeConcurrentCameraInfosList: MutableList<CameraInfo> = mutableListOf()
 
-    @VisibleForTesting var concurrentMode: Int = CAMERA_OPERATING_MODE_UNSPECIFIED
+    @VisibleForTesting public var concurrentMode: Int = CAMERA_OPERATING_MODE_UNSPECIFIED
 
-    @VisibleForTesting var concurrentModeOn = false
+    @VisibleForTesting public var concurrentModeOn: Boolean = false
 
     init {
         val concurrentCameraIds = cameraDevices.awaitConcurrentCameraIds()!!.toMutableSet()
@@ -82,7 +85,7 @@
         }
     }
 
-    fun registerCamera(cameraId: String, cameraInternal: CameraInternal) {
+    public fun registerCamera(cameraId: String, cameraInternal: CameraInternal) {
         cameraInternalMap[CameraId.fromCamera2Id(cameraId)] =
             cameraInternal as CameraInternalAdapter
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryProvider.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryProvider.kt
index b789de8..0c7fec1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryProvider.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraFactoryProvider.kt
@@ -36,7 +36,7 @@
  * share resources across Camera instances. There should generally be one [CameraFactoryProvider]
  * instance per CameraX instance.
  */
-class CameraFactoryProvider(
+public class CameraFactoryProvider(
     private val sharedCameraPipe: CameraPipe? = null,
     private val sharedAppContext: Context? = null,
     private val sharedThreadConfig: CameraThreadConfig? = null
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
index d2284f8..41957a0 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapter.kt
@@ -79,7 +79,7 @@
     "UnsafeOptInUsageError" // Suppressed due to experimental ExposureState
 )
 @CameraScope
-class CameraInfoAdapter
+public class CameraInfoAdapter
 @Inject
 constructor(
     private val cameraProperties: CameraProperties,
@@ -127,7 +127,7 @@
     override fun getLensFacing(): Int =
         getCameraSelectorLensFacing(cameraProperties.metadata[CameraCharacteristics.LENS_FACING]!!)
 
-    override fun getCameraCharacteristics() =
+    override fun getCameraCharacteristics(): CameraCharacteristics =
         cameraProperties.metadata.unwrapAs(CameraCharacteristics::class)!!
 
     override fun getPhysicalCameraCharacteristics(physicalCameraId: String): Any? {
@@ -182,10 +182,12 @@
 
     override fun getCameraState(): LiveData<CameraState> = cameraStateAdapter.cameraState
 
-    override fun addSessionCaptureCallback(executor: Executor, callback: CameraCaptureCallback) =
-        cameraCallbackMap.addCaptureCallback(callback, executor)
+    override fun addSessionCaptureCallback(
+        executor: Executor,
+        callback: CameraCaptureCallback
+    ): Unit = cameraCallbackMap.addCaptureCallback(callback, executor)
 
-    override fun removeSessionCaptureCallback(callback: CameraCaptureCallback) =
+    override fun removeSessionCaptureCallback(callback: CameraCaptureCallback): Unit =
         cameraCallbackMap.removeCaptureCallback(callback)
 
     override fun getImplementationType(): String =
@@ -237,7 +239,7 @@
         return cameraQuirks.quirks
     }
 
-    override fun isFocusMeteringSupported(action: FocusMeteringAction) =
+    override fun isFocusMeteringSupported(action: FocusMeteringAction): Boolean =
         focusMeteringControl.isFocusMeteringSupported(action)
 
     override fun getSupportedFrameRateRanges(): Set<Range<Int>> =
@@ -306,7 +308,7 @@
         }
     }
 
-    companion object {
+    public companion object {
         private val PROFILE_TO_DR_MAP: Map<Long, DynamicRange> =
             mapOf(
                 DynamicRangeProfiles.STANDARD to SDR,
@@ -323,7 +325,7 @@
                 DynamicRangeProfiles.DOLBY_VISION_8B_HDR_REF_PO to DOLBY_VISION_8_BIT,
             )
 
-        fun <T : Any> CameraInfo.unwrapAs(type: KClass<T>): T? =
+        public fun <T : Any> CameraInfo.unwrapAs(type: KClass<T>): T? =
             when (this) {
                 is UnsafeWrapper -> this.unwrapAs(type)
                 is CameraInfoInternal -> {
@@ -336,7 +338,7 @@
                 else -> null
             }
 
-        val CameraInfo.cameraId: CameraId?
+        public val CameraInfo.cameraId: CameraId?
             get() = this.unwrapAs(CameraMetadata::class)?.camera
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
index 21b16df..9143910 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraInternalAdapter.kt
@@ -42,7 +42,7 @@
 
 /** Adapt the [CameraInternal] class to one or more [CameraPipe] based Camera instances. */
 @CameraScope
-class CameraInternalAdapter
+public class CameraInternalAdapter
 @Inject
 constructor(
     config: CameraConfig,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt
index c131a794..59e6342 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraStateAdapter.kt
@@ -35,7 +35,7 @@
 import javax.inject.Inject
 
 @CameraScope
-class CameraStateAdapter @Inject constructor() {
+public class CameraStateAdapter @Inject constructor() {
     private val lock = Any()
 
     internal val cameraInternalState = LiveDataObservable<CameraInternal.State>()
@@ -51,7 +51,7 @@
         postCameraState(CameraInternal.State.CLOSED)
     }
 
-    fun onGraphUpdated(cameraGraph: CameraGraph) =
+    public fun onGraphUpdated(cameraGraph: CameraGraph): Unit =
         synchronized(lock) {
             Log.debug { "Camera graph updated from $currentGraph to $cameraGraph" }
             if (currentCameraInternalState != CameraInternal.State.CLOSED) {
@@ -62,7 +62,7 @@
             currentCameraInternalState = CameraInternal.State.CLOSED
         }
 
-    fun onGraphStateUpdated(cameraGraph: CameraGraph, graphState: GraphState) =
+    public fun onGraphStateUpdated(cameraGraph: CameraGraph, graphState: GraphState): Unit =
         synchronized(lock) {
             Log.debug { "$cameraGraph state updated to $graphState" }
             handleStateTransition(cameraGraph, graphState)
@@ -198,7 +198,7 @@
         val error: CameraState.StateError? = null
     )
 
-    companion object {
+    public companion object {
         internal fun CameraError.toCameraStateError(): CameraState.StateError =
             CameraState.StateError.create(
                 when (this) {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
index b5474c5..2080c58 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraSurfaceAdapter.kt
@@ -41,7 +41,7 @@
  * This class provides Context-specific utility methods for querying and computing supported
  * outputs.
  */
-class CameraSurfaceAdapter(
+public class CameraSurfaceAdapter(
     context: Context,
     cameraComponent: Any?,
     availableCameraIds: Set<String>
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
index 731a966..2bb3546 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
@@ -51,7 +51,7 @@
  * and aspect ratios for the display.
  */
 @Suppress("DEPRECATION")
-class CameraUseCaseAdapter(context: Context) : UseCaseConfigFactory {
+public class CameraUseCaseAdapter(context: Context) : UseCaseConfigFactory {
     private val displayInfoManager by lazy { DisplayInfoManager(context) }
 
     init {
@@ -136,7 +136,7 @@
         return OptionsBundle.from(mutableConfig)
     }
 
-    open class DefaultCaptureOptionsUnpacker : CaptureConfig.OptionUnpacker {
+    public open class DefaultCaptureOptionsUnpacker : CaptureConfig.OptionUnpacker {
         @OptIn(ExperimentalCamera2Interop::class)
         override fun unpack(config: UseCaseConfig<*>, builder: CaptureConfig.Builder) {
             val defaultCaptureConfig = config.getDefaultCaptureConfig(null)
@@ -174,12 +174,12 @@
             builder.addImplementationOptions(camera2Config.captureRequestOptions)
         }
 
-        companion object {
-            val INSTANCE = DefaultCaptureOptionsUnpacker()
+        public companion object {
+            public val INSTANCE: DefaultCaptureOptionsUnpacker = DefaultCaptureOptionsUnpacker()
         }
     }
 
-    class ImageCaptureOptionUnpacker : DefaultCaptureOptionsUnpacker() {
+    public class ImageCaptureOptionUnpacker : DefaultCaptureOptionsUnpacker() {
 
         override fun unpack(config: UseCaseConfig<*>, builder: CaptureConfig.Builder) {
             super.unpack(config, builder)
@@ -189,12 +189,12 @@
             )
         }
 
-        companion object {
-            val INSTANCE = ImageCaptureOptionUnpacker()
+        public companion object {
+            public val INSTANCE: ImageCaptureOptionUnpacker = ImageCaptureOptionUnpacker()
         }
     }
 
-    object DefaultSessionOptionsUnpacker : SessionConfig.OptionUnpacker {
+    public object DefaultSessionOptionsUnpacker : SessionConfig.OptionUnpacker {
         @OptIn(ExperimentalCamera2Interop::class)
         override fun unpack(
             resolution: Size,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapter.kt
index 89d3e9e..20be397 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureConfigAdapter.kt
@@ -45,7 +45,7 @@
  * CameraPipe can submit to the camera.
  */
 @UseCaseCameraScope
-class CaptureConfigAdapter
+public class CaptureConfigAdapter
 @Inject
 constructor(
     cameraProperties: CameraProperties,
@@ -63,7 +63,7 @@
      *   surface is not recognized in [UseCaseGraphConfig.surfaceToStreamMap]
      */
     @OptIn(ExperimentalGetImage::class)
-    fun mapToRequest(
+    public fun mapToRequest(
         captureConfig: CaptureConfig,
         requestTemplate: RequestTemplate,
         sessionConfigOptions: Config,
@@ -151,7 +151,7 @@
         )
     }
 
-    companion object {
+    public companion object {
         internal fun CaptureConfig.getStillCaptureTemplate(
             sessionTemplate: RequestTemplate,
             isLegacyDevice: Boolean,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureFailureAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureFailureAdapter.kt
index 4864851..495f097 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureFailureAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureFailureAdapter.kt
@@ -21,7 +21,7 @@
 import androidx.camera.camera2.pipe.compat.AndroidCaptureFailure
 import androidx.camera.core.impl.CameraCaptureFailure
 
-class CaptureFailureAdapter(
+public class CaptureFailureAdapter(
     private val requestFailure: RequestFailure,
 ) : CameraCaptureFailure(Reason.ERROR) {
     override fun getCaptureFailure(): CaptureFailure =
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt
index 0c5e8fe..bd3da7d 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CaptureResultAdapter.kt
@@ -41,7 +41,7 @@
 import java.nio.BufferUnderflowException
 import kotlin.reflect.KClass
 
-class PartialCaptureResultAdapter(
+public class PartialCaptureResultAdapter(
     private val requestMetadata: RequestMetadata,
     private val frameNumber: FrameNumber,
     private val result: FrameMetadata,
@@ -78,7 +78,7 @@
 }
 
 /** Adapts the [CameraCaptureResult] interface to [CameraPipe]. */
-class CaptureResultAdapter(
+public class CaptureResultAdapter(
     private val requestMetadata: RequestMetadata,
     private val frameNumber: FrameNumber,
     internal val result: FrameInfo
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CoroutineAdapters.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CoroutineAdapters.kt
index 533189e..76d7012 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CoroutineAdapters.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CoroutineAdapters.kt
@@ -31,7 +31,7 @@
  * The return value of the Future is null, and canceling the future will not cancel the Job. The tag
  * field may be used to help debug futures.
  */
-fun Job.asListenableFuture(tag: Any? = "Job.asListenableFuture"): ListenableFuture<Void> {
+public fun Job.asListenableFuture(tag: Any? = "Job.asListenableFuture"): ListenableFuture<Void> {
     val resolver: CallbackToFutureAdapter.Resolver<Void> =
         CallbackToFutureAdapter.Resolver<Void> { completer ->
             this.invokeOnCompletion {
@@ -52,7 +52,7 @@
 
 /** Convert a job into a ListenableFuture<T>. */
 @OptIn(ExperimentalCoroutinesApi::class)
-fun <T> Deferred<T>.asListenableFuture(
+public fun <T> Deferred<T>.asListenableFuture(
     tag: Any? = "Deferred.asListenableFuture"
 ): ListenableFuture<T> {
     val resolver: CallbackToFutureAdapter.Resolver<T> =
@@ -74,12 +74,12 @@
     return CallbackToFutureAdapter.getFuture(resolver)
 }
 
-fun <T> Deferred<T>.propagateTo(destination: CompletableDeferred<T>) {
+public fun <T> Deferred<T>.propagateTo(destination: CompletableDeferred<T>) {
     invokeOnCompletion { propagateOnceTo(destination, it) }
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
-fun <T> Deferred<T>.propagateOnceTo(
+public fun <T> Deferred<T>.propagateOnceTo(
     destination: CompletableDeferred<T>,
     throwable: Throwable?,
 ) {
@@ -100,5 +100,5 @@
  *
  * @return true if `Deferred.await` had completed, false otherwise.
  */
-suspend fun <T> Deferred<T>.awaitUntil(timeoutMillis: Long) =
+public suspend fun <T> Deferred<T>.awaitUntil(timeoutMillis: Long): Boolean =
     withTimeoutOrNull(timeoutMillis) { [email protected]() }?.let { true } ?: false
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EncoderProfilesProviderAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EncoderProfilesProviderAdapter.kt
index 295b087..d8613e1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EncoderProfilesProviderAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EncoderProfilesProviderAdapter.kt
@@ -40,7 +40,7 @@
 
 /** Adapt the [EncoderProfilesProvider] interface to [CameraPipe]. */
 @CameraScope
-class EncoderProfilesProviderAdapter
+public class EncoderProfilesProviderAdapter
 @Inject
 constructor(
     @Named("CameraId") private val cameraIdString: String,
@@ -188,7 +188,7 @@
         }
     }
 
-    companion object {
+    public companion object {
         private const val TAG = "EncoderProfilesProviderAdapter"
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EvCompValue.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EvCompValue.kt
index 49ac2d2..13fcbd5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EvCompValue.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/EvCompValue.kt
@@ -23,7 +23,7 @@
 
 /** Immutable adaptor to the ExposureState interface. */
 @SuppressLint("UnsafeOptInUsageError")
-data class EvCompValue(
+public data class EvCompValue(
     private val supported: Boolean,
     private val index: Int,
     private val range: Range<Int>,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ExposureStateAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ExposureStateAdapter.kt
index 6b00b1b..5a1f8dc 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ExposureStateAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ExposureStateAdapter.kt
@@ -28,7 +28,7 @@
 
 /** Adapt [ExposureState] to a [CameraMetadata] instance. */
 @SuppressLint("UnsafeOptInUsageError")
-class ExposureStateAdapter(
+public class ExposureStateAdapter(
     private val cameraProperties: CameraProperties,
     private val exposureCompensation: Int
 ) : ExposureState {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/GuaranteedConfigurationsUtil.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/GuaranteedConfigurationsUtil.kt
index c7497af..6b6d2ef 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/GuaranteedConfigurationsUtil.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/GuaranteedConfigurationsUtil.kt
@@ -25,9 +25,9 @@
 import androidx.camera.core.impl.SurfaceConfig.ConfigSize
 import androidx.camera.core.impl.SurfaceConfig.ConfigType
 
-object GuaranteedConfigurationsUtil {
+public object GuaranteedConfigurationsUtil {
     @JvmStatic
-    fun getLegacySupportedCombinationList(): List<SurfaceCombination> {
+    public fun getLegacySupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
 
         // (PRIV, MAXIMUM)
@@ -84,7 +84,7 @@
     }
 
     @JvmStatic
-    fun getLimitedSupportedCombinationList(): List<SurfaceCombination> {
+    public fun getLimitedSupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
 
         // (PRIV, PREVIEW) + (PRIV, RECORD)
@@ -139,7 +139,7 @@
     }
 
     @JvmStatic
-    fun getFullSupportedCombinationList(): List<SurfaceCombination> {
+    public fun getFullSupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
 
         // (PRIV, PREVIEW) + (PRIV, MAXIMUM)
@@ -194,7 +194,7 @@
     }
 
     @JvmStatic
-    fun getRAWSupportedCombinationList(): List<SurfaceCombination> {
+    public fun getRAWSupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
 
         // (RAW, MAXIMUM)
@@ -259,7 +259,7 @@
     }
 
     @JvmStatic
-    fun getBurstSupportedCombinationList(): List<SurfaceCombination> {
+    public fun getBurstSupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
         // (PRIV, PREVIEW) + (PRIV, MAXIMUM)
         SurfaceCombination()
@@ -286,7 +286,7 @@
     }
 
     @JvmStatic
-    fun getLevel3SupportedCombinationList(): List<SurfaceCombination> {
+    public fun getLevel3SupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
         // (PRIV, PREVIEW) + (PRIV, VGA) + (YUV, MAXIMUM) + (RAW, MAXIMUM)
         SurfaceCombination()
@@ -310,7 +310,7 @@
     }
 
     @JvmStatic
-    fun getUltraHighResolutionSupportedCombinationList(): List<SurfaceCombination> {
+    public fun getUltraHighResolutionSupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
 
         // (YUV, ULTRA_MAXIMUM) + (PRIV, PREVIEW) + (PRIV, RECORD)
@@ -432,7 +432,7 @@
 
     /** Returns the minimally guaranteed stream combinations for Ultra HDR. */
     @JvmStatic
-    fun getUltraHdrSupportedCombinationList(): List<SurfaceCombination> {
+    public fun getUltraHdrSupportedCombinationList(): List<SurfaceCombination> {
         // Due to the unique characteristics of JPEG/R, some devices might configure an extra 8-bit
         // JPEG stream internally in addition to the 10-bit YUV stream. The 10-bit mandatory
         // stream combination table is actually not suitable for use. Adds only (PRIV, PREVIEW) +
@@ -457,7 +457,7 @@
     }
 
     @JvmStatic
-    fun getConcurrentSupportedCombinationList(): List<SurfaceCombination> {
+    public fun getConcurrentSupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
         // (YUV, s1440p)
         SurfaceCombination()
@@ -517,7 +517,7 @@
     }
 
     @JvmStatic
-    fun generateSupportedCombinationList(
+    public fun generateSupportedCombinationList(
         hardwareLevel: Int,
         isRawSupported: Boolean,
         isBurstCaptureSupported: Boolean
@@ -558,7 +558,7 @@
      * as a 10-bit input.
      */
     @JvmStatic
-    fun get10BitSupportedCombinationList(): List<SurfaceCombination> {
+    public fun get10BitSupportedCombinationList(): List<SurfaceCombination> {
         return listOf(
             // (PRIV, MAXIMUM)
             SurfaceCombination().apply {
@@ -607,7 +607,7 @@
      * Returns the entire supported stream combinations for devices with Stream Use Case capability
      */
     @RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
-    fun getStreamUseCaseSupportedCombinationList(): List<SurfaceCombination> {
+    public fun getStreamUseCaseSupportedCombinationList(): List<SurfaceCombination> {
         return listOf<SurfaceCombination>(
             // (PRIV, s1440p, PREVIEW_VIDEO_STILL)
             SurfaceCombination().apply {
@@ -834,7 +834,7 @@
     }
 
     @JvmStatic
-    fun getPreviewStabilizationSupportedCombinationList(): List<SurfaceCombination> {
+    public fun getPreviewStabilizationSupportedCombinationList(): List<SurfaceCombination> {
         val combinationList: MutableList<SurfaceCombination> = ArrayList()
         // (PRIV, s1440p)
         SurfaceCombination()
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt
index c347ad8..51aa8b8 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/PhysicalCameraInfoAdapter.kt
@@ -44,7 +44,7 @@
 @SuppressLint(
     "UnsafeOptInUsageError" // Suppressed due to experimental API
 )
-class PhysicalCameraInfoAdapter(private val cameraProperties: CameraProperties) :
+public class PhysicalCameraInfoAdapter(private val cameraProperties: CameraProperties) :
     CameraInfo, UnsafeWrapper {
 
     @OptIn(ExperimentalCamera2Interop::class)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/RequestProcessorAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/RequestProcessorAdapter.kt
index fb25e75..91485d9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/RequestProcessorAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/RequestProcessorAdapter.kt
@@ -39,7 +39,7 @@
 import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.CoroutineScope
 
-class RequestProcessorAdapter(
+public class RequestProcessorAdapter(
     private val useCaseGraphConfig: UseCaseGraphConfig,
     private val processorSurfaces: List<SessionProcessorSurface>,
     private val scope: CoroutineScope,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SessionConfigAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SessionConfigAdapter.kt
index 6fe08bc..3830f2d 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SessionConfigAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SessionConfigAdapter.kt
@@ -44,13 +44,13 @@
  * Aggregate the SessionConfig from a List of [UseCase]s, and provide a validated SessionConfig for
  * operation.
  */
-class SessionConfigAdapter(
+public class SessionConfigAdapter(
     private val useCases: Collection<UseCase>,
     private val sessionProcessorConfig: SessionConfig? = null,
     private val isPrimary: Boolean = true,
 ) {
-    val isSessionProcessorEnabled = sessionProcessorConfig != null
-    val surfaceToStreamUseCaseMap: Map<DeferrableSurface, Long> by lazy {
+    public val isSessionProcessorEnabled: Boolean = sessionProcessorConfig != null
+    public val surfaceToStreamUseCaseMap: Map<DeferrableSurface, Long> by lazy {
         val sessionConfigs = mutableListOf<SessionConfig>()
         val useCaseConfigs = mutableListOf<UseCaseConfig<*>>()
         for (useCase in useCases) {
@@ -59,7 +59,7 @@
         }
         getSurfaceToStreamUseCaseMapping(sessionConfigs, useCaseConfigs)
     }
-    val surfaceToStreamUseHintMap: Map<DeferrableSurface, Long> by lazy {
+    public val surfaceToStreamUseHintMap: Map<DeferrableSurface, Long> by lazy {
         val sessionConfigs = useCases.map { it.getSessionConfig(isPrimary) }
         getSurfaceToStreamUseHintMapping(sessionConfigs)
     }
@@ -84,21 +84,21 @@
         validatingBuilder.build()
     }
 
-    val deferrableSurfaces: List<DeferrableSurface> by lazy {
+    public val deferrableSurfaces: List<DeferrableSurface> by lazy {
         check(validatingBuilder.isValid)
 
         sessionConfig.surfaces
     }
 
-    fun getValidSessionConfigOrNull(): SessionConfig? {
+    public fun getValidSessionConfigOrNull(): SessionConfig? {
         return if (isSessionConfigValid()) sessionConfig else null
     }
 
-    fun isSessionConfigValid(): Boolean {
+    public fun isSessionConfigValid(): Boolean {
         return validatingBuilder.isValid
     }
 
-    fun reportSurfaceInvalid(deferrableSurface: DeferrableSurface) {
+    public fun reportSurfaceInvalid(deferrableSurface: DeferrableSurface) {
         debug { "Unavailable $deferrableSurface, notify SessionConfig invalid" }
 
         // Only report error to one SessionConfig, CameraInternal#onUseCaseReset()
@@ -120,7 +120,7 @@
         }
     }
 
-    fun getExpectedFrameRateRange(): Range<Int>? {
+    public fun getExpectedFrameRateRange(): Range<Int>? {
         return if (
             isSessionConfigValid() &&
                 sessionConfig.expectedFrameRateRange != StreamSpec.FRAME_RATE_RANGE_UNSPECIFIED
@@ -137,7 +137,7 @@
      * @return the mapping between surfaces and Stream Use Case flag
      */
     @VisibleForTesting
-    fun getSurfaceToStreamUseCaseMapping(
+    public fun getSurfaceToStreamUseCaseMapping(
         sessionConfigs: Collection<SessionConfig>,
         useCaseConfigs: Collection<UseCaseConfig<*>>,
     ): Map<DeferrableSurface, Long> {
@@ -165,7 +165,7 @@
      * @return the mapping between surfaces and Stream Use Hint flag
      */
     @VisibleForTesting
-    fun getSurfaceToStreamUseHintMapping(
+    public fun getSurfaceToStreamUseHintMapping(
         sessionConfigs: Collection<SessionConfig>
     ): Map<DeferrableSurface, Long> {
         val mapping = mutableMapOf<DeferrableSurface, Long>()
@@ -205,12 +205,12 @@
         }
     }
 
-    companion object {
-        fun SessionConfig.toCamera2ImplConfig(): Camera2ImplConfig {
+    public companion object {
+        public fun SessionConfig.toCamera2ImplConfig(): Camera2ImplConfig {
             return Camera2ImplConfig(implementationOptions)
         }
 
-        fun UseCase.getSessionConfig(isPrimary: Boolean): SessionConfig {
+        public fun UseCase.getSessionConfig(isPrimary: Boolean): SessionConfig {
             return if (isPrimary) sessionConfig else secondarySessionConfig
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
index a5d907a..7259c36 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/SupportedSurfaceCombination.kt
@@ -76,7 +76,7 @@
  */
 @Suppress("DEPRECATION")
 // TODO(b/200306659): Remove and replace with annotation on package-info.java
-class SupportedSurfaceCombination(
+public class SupportedSurfaceCombination(
     context: Context,
     private val cameraMetadata: CameraMetadata,
     private val encoderProfilesProviderAdapter: EncoderProfilesProviderAdapter
@@ -155,7 +155,7 @@
      * @param surfaceConfigList the surface configuration list to be compared
      * @return the check result that whether it could be supported
      */
-    fun checkSupported(
+    public fun checkSupported(
         featureSettings: FeatureSettings,
         surfaceConfigList: List<SurfaceConfig>
     ): Boolean {
@@ -232,7 +232,11 @@
      * @param size the size info for the surface configuration object
      * @return new [SurfaceConfig] object
      */
-    fun transformSurfaceConfig(cameraMode: Int, imageFormat: Int, size: Size): SurfaceConfig {
+    public fun transformSurfaceConfig(
+        cameraMode: Int,
+        imageFormat: Int,
+        size: Size
+    ): SurfaceConfig {
         return SurfaceConfig.transformSurfaceConfig(
             cameraMode,
             imageFormat,
@@ -255,7 +259,7 @@
      * @throws IllegalArgumentException if the suggested solution for newUseCaseConfigs cannot be
      *   found. This may be due to no available output size or no available surface combination.
      */
-    fun getSuggestedStreamSpecifications(
+    public fun getSuggestedStreamSpecifications(
         cameraMode: Int,
         attachedSurfaces: List<AttachedSurfaceInfo>,
         newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>,
@@ -1144,7 +1148,7 @@
      * @see ResolutionCorrector
      */
     @VisibleForTesting
-    fun applyResolutionSelectionOrderRelatedWorkarounds(
+    public fun applyResolutionSelectionOrderRelatedWorkarounds(
         sizeList: List<Size>,
         imageFormat: Int
     ): List<Size> {
@@ -1308,7 +1312,7 @@
 
     /** Updates the surface size definition for the specified format then return it. */
     @VisibleForTesting
-    fun getUpdatedSurfaceSizeDefinitionByFormat(format: Int): SurfaceSizeDefinition {
+    public fun getUpdatedSurfaceSizeDefinitionByFormat(format: Int): SurfaceSizeDefinition {
         if (!surfaceSizeDefinitionFormats.contains(format)) {
             updateS720pOrS1440pSizeByFormat(
                 surfaceSizeDefinition.s720pSizeMap,
@@ -1588,21 +1592,21 @@
      *   [CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT].
      * @param isPreviewStabilizationOn Whether the preview stabilization is enabled.
      */
-    data class FeatureSettings(
+    public data class FeatureSettings(
         @CameraMode.Mode val cameraMode: Int,
         val requiredMaxBitDepth: Int,
         val isPreviewStabilizationOn: Boolean = false,
         val isUltraHdrOn: Boolean = false
     )
 
-    data class BestSizesAndMaxFpsForConfigs(
+    public data class BestSizesAndMaxFpsForConfigs(
         val bestSizes: List<Size>,
         val bestSizesForStreamUseCase: List<Size>?,
         val maxFps: Int,
         val maxFpsForStreamUseCase: Int
     )
 
-    companion object {
+    public companion object {
         private fun isUltraHdrOn(
             attachedSurfaces: List<AttachedSurfaceInfo>,
             newUseCaseConfigsSupportedSizeMap: Map<UseCaseConfig<*>, List<Size>>
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZoomValue.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZoomValue.kt
index 5e4c692..b96e61f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZoomValue.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZoomValue.kt
@@ -21,7 +21,7 @@
 import androidx.camera.core.ZoomState
 
 /** Immutable adaptor to the ZoomState interface. */
-data class ZoomValue(
+public data class ZoomValue(
     private val zoomRatio: Float,
     private val minZoomRatio: Float,
     private val maxZoomRatio: Float,
@@ -34,9 +34,9 @@
      * value to use if the values don't align with conversion values. Secondary constructor with a
      * LinearZoom value wrapper class is used for this purpose.
      */
-    data class LinearZoom(val value: Float)
+    public data class LinearZoom(val value: Float)
 
-    constructor(
+    public constructor(
         linearZoom: LinearZoom,
         minZoomRatio: Float,
         maxZoomRatio: Float,
@@ -58,7 +58,7 @@
 
     override fun getMinZoomRatio(): Float = minZoomRatio
 
-    override fun getLinearZoom() =
+    override fun getLinearZoom(): Float =
         linearZoom
             ?: getLinearZoomFromZoomRatio(
                 zoomRatio = zoomRatio,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZslControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZslControl.kt
index 9c3d7bf..5d46f97 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZslControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZslControl.kt
@@ -43,14 +43,14 @@
 import androidx.camera.core.internal.utils.ZslRingBuffer
 import javax.inject.Inject
 
-interface ZslControl {
+public interface ZslControl {
 
     /**
      * Adds zero-shutter lag config to [SessionConfig].
      *
      * @param sessionConfigBuilder session config builder.
      */
-    fun addZslConfig(sessionConfigBuilder: SessionConfig.Builder)
+    public fun addZslConfig(sessionConfigBuilder: SessionConfig.Builder)
 
     /**
      * Determines whether the provided [DeferrableSurface] belongs to ZSL.
@@ -59,7 +59,7 @@
      * @param sessionConfig The session configuration where its input configuration will be used to
      *   determine whether the deferrable Surface belongs to ZSL.
      */
-    fun isZslSurface(surface: DeferrableSurface, sessionConfig: SessionConfig): Boolean
+    public fun isZslSurface(surface: DeferrableSurface, sessionConfig: SessionConfig): Boolean
 
     /**
      * Sets the flag if zero-shutter lag needs to be disabled by user case config.
@@ -72,14 +72,14 @@
      *   disabled. However, enabling zero-shutter lag needs other conditions e.g. flash mode OFF, so
      *   setting to false doesn't guarantee zero-shutter lag to be always ON.
      */
-    fun setZslDisabledByUserCaseConfig(disabled: Boolean)
+    public fun setZslDisabledByUserCaseConfig(disabled: Boolean)
 
     /**
      * Checks if zero-shutter lag is disabled by user case config.
      *
      * @return True if zero-shutter lag should be disabled. Otherwise, returns false.
      */
-    fun isZslDisabledByUserCaseConfig(): Boolean
+    public fun isZslDisabledByUserCaseConfig(): Boolean
 
     /**
      * Sets the flag if zero-shutter lag needs to be disabled by flash mode.
@@ -91,26 +91,26 @@
      *   and VideoCapture is OFF, so setting to false doesn't guarantee zero-shutter lag to be
      *   always ON.
      */
-    fun setZslDisabledByFlashMode(disabled: Boolean)
+    public fun setZslDisabledByFlashMode(disabled: Boolean)
 
     /**
      * Checks if zero-shutter lag is disabled by flash mode.
      *
      * @return True if zero-shutter lag should be disabled. Otherwise, returns false.
      */
-    fun isZslDisabledByFlashMode(): Boolean
+    public fun isZslDisabledByFlashMode(): Boolean
 
     /**
      * Dequeues [ImageProxy] from ring buffer.
      *
      * @return [ImageProxy].
      */
-    fun dequeueImageFromBuffer(): ImageProxy?
+    public fun dequeueImageFromBuffer(): ImageProxy?
 }
 
 @RequiresApi(Build.VERSION_CODES.M)
 @CameraScope
-class ZslControlImpl @Inject constructor(private val cameraProperties: CameraProperties) :
+public class ZslControlImpl @Inject constructor(private val cameraProperties: CameraProperties) :
     ZslControl {
     private val cameraMetadata = cameraProperties.metadata
     private val streamConfigurationMap: StreamConfigurationMap by lazy {
@@ -269,7 +269,7 @@
         }
     }
 
-    companion object {
+    public companion object {
         // Due to b/232268355 and feedback from pixel team that private format will have better
         // performance, we will use private only for zsl.
         private const val FORMAT = ImageFormat.PRIVATE
@@ -281,18 +281,19 @@
 }
 
 /** No-Op implementation for [ZslControl]. */
-class ZslControlNoOpImpl @Inject constructor() : ZslControl {
+public class ZslControlNoOpImpl @Inject constructor() : ZslControl {
     override fun addZslConfig(sessionConfigBuilder: SessionConfig.Builder) {}
 
-    override fun isZslSurface(surface: DeferrableSurface, sessionConfig: SessionConfig) = false
+    override fun isZslSurface(surface: DeferrableSurface, sessionConfig: SessionConfig): Boolean =
+        false
 
     override fun setZslDisabledByUserCaseConfig(disabled: Boolean) {}
 
-    override fun isZslDisabledByUserCaseConfig() = false
+    override fun isZslDisabledByUserCaseConfig(): Boolean = false
 
     override fun setZslDisabledByFlashMode(disabled: Boolean) {}
 
-    override fun isZslDisabledByFlashMode() = false
+    override fun isZslDisabledByFlashMode(): Boolean = false
 
     override fun dequeueImageFromBuffer(): ImageProxy? {
         return null
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/Camera2CameraControlCompat.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/Camera2CameraControlCompat.kt
index bcbad70..cc4813e 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/Camera2CameraControlCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/Camera2CameraControlCompat.kt
@@ -41,21 +41,24 @@
 private const val TAG_KEY = "Camera2CameraControl.tag"
 
 @ExperimentalCamera2Interop
-interface Camera2CameraControlCompat : Request.Listener {
-    fun addRequestOption(bundle: CaptureRequestOptions)
+public interface Camera2CameraControlCompat : Request.Listener {
+    public fun addRequestOption(bundle: CaptureRequestOptions)
 
-    fun getRequestOption(): CaptureRequestOptions
+    public fun getRequestOption(): CaptureRequestOptions
 
-    fun clearRequestOption()
+    public fun clearRequestOption()
 
-    fun cancelCurrentTask()
+    public fun cancelCurrentTask()
 
-    fun applyAsync(camera: UseCaseCamera?, cancelPreviousTask: Boolean = true): Deferred<Void?>
+    public fun applyAsync(
+        camera: UseCaseCamera?,
+        cancelPreviousTask: Boolean = true
+    ): Deferred<Void?>
 
     @Module
-    abstract class Bindings {
+    public abstract class Bindings {
         @Binds
-        abstract fun bindCamera2CameraControlCompImpl(
+        public abstract fun bindCamera2CameraControlCompImpl(
             impl: Camera2CameraControlCompatImpl
         ): Camera2CameraControlCompat
     }
@@ -63,7 +66,7 @@
 
 @CameraScope
 @ExperimentalCamera2Interop
-class Camera2CameraControlCompatImpl @Inject constructor() : Camera2CameraControlCompat {
+public class Camera2CameraControlCompatImpl @Inject constructor() : Camera2CameraControlCompat {
 
     private val lock = Any()
     private val updateSignalLock = Any()
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
index 157343e..577e4db 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/CameraCompatModule.kt
@@ -36,4 +36,4 @@
             TemplateParamsOverride.Bindings::class,
         ],
 )
-abstract class CameraCompatModule
+public abstract class CameraCompatModule
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/DynamicRangeProfilesCompat.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/DynamicRangeProfilesCompat.kt
index bd5eeaf..7307c45 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/DynamicRangeProfilesCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/DynamicRangeProfilesCompat.kt
@@ -25,10 +25,10 @@
 import androidx.camera.core.DynamicRange
 
 /** Helper for accessing features in DynamicRangeProfiles in a backwards compatible fashion. */
-class DynamicRangeProfilesCompat
+public class DynamicRangeProfilesCompat
 internal constructor(private val impl: DynamicRangeProfilesCompatImpl) {
     /** The set of supported dynamic ranges. */
-    val supportedDynamicRanges: Set<DynamicRange>
+    public val supportedDynamicRanges: Set<DynamicRange>
         get() = impl.supportedDynamicRanges
 
     /**
@@ -50,7 +50,9 @@
      * @throws IllegalArgumentException If the dynamic range argument is not within the set returned
      *   by [supportedDynamicRanges].
      */
-    fun getDynamicRangeCaptureRequestConstraints(dynamicRange: DynamicRange): Set<DynamicRange> {
+    public fun getDynamicRangeCaptureRequestConstraints(
+        dynamicRange: DynamicRange
+    ): Set<DynamicRange> {
         return impl.getDynamicRangeCaptureRequestConstraints(dynamicRange)
     }
 
@@ -69,7 +71,7 @@
      * @throws IllegalArgumentException If the dynamic range argument is not within the set returned
      *   by [supportedDynamicRanges].
      */
-    fun isExtraLatencyPresent(dynamicRange: DynamicRange): Boolean {
+    public fun isExtraLatencyPresent(dynamicRange: DynamicRange): Boolean {
         return impl.isExtraLatencyPresent(dynamicRange)
     }
 
@@ -80,7 +82,7 @@
      *   dynamic range.
      */
     @RequiresApi(33)
-    fun toDynamicRangeProfiles(): DynamicRangeProfiles? {
+    public fun toDynamicRangeProfiles(): DynamicRangeProfiles? {
         checkApi(
             33,
             "DynamicRangesCompat can only be " +
@@ -99,7 +101,7 @@
         fun unwrap(): DynamicRangeProfiles?
     }
 
-    companion object {
+    public companion object {
         /**
          * Returns a [DynamicRangeProfilesCompat] using the capabilities derived from the provided
          * characteristics.
@@ -107,7 +109,7 @@
          * @param cameraMetadata the metaData used to derive dynamic range information.
          * @return a [DynamicRangeProfilesCompat] object.
          */
-        fun fromCameraMetaData(cameraMetadata: CameraMetadata): DynamicRangeProfilesCompat {
+        public fun fromCameraMetaData(cameraMetadata: CameraMetadata): DynamicRangeProfilesCompat {
             var rangesCompat: DynamicRangeProfilesCompat? = null
             if (Build.VERSION.SDK_INT >= 33) {
                 rangesCompat =
@@ -126,7 +128,7 @@
          * @return an equivalent [DynamicRangeProfilesCompat] object.
          */
         @RequiresApi(33)
-        fun toDynamicRangesCompat(
+        public fun toDynamicRangesCompat(
             dynamicRangeProfiles: DynamicRangeProfiles?
         ): DynamicRangeProfilesCompat? {
             if (dynamicRangeProfiles == null) {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/EvCompCompat.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/EvCompCompat.kt
index 6166833..17c69c4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/EvCompCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/EvCompCompat.kt
@@ -40,22 +40,22 @@
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.launch
 
-interface EvCompCompat {
-    val supported: Boolean
-    val range: Range<Int>
-    val step: Rational
+public interface EvCompCompat {
+    public val supported: Boolean
+    public val range: Range<Int>
+    public val step: Rational
 
-    fun stopRunningTask(throwable: Throwable)
+    public fun stopRunningTask(throwable: Throwable)
 
-    fun applyAsync(
+    public fun applyAsync(
         evCompIndex: Int,
         camera: UseCaseCamera,
         cancelPreviousTask: Boolean,
     ): Deferred<Int>
 
     @Module
-    abstract class Bindings {
-        @Binds abstract fun bindEvCompImpl(impl: EvCompImpl): EvCompCompat
+    public abstract class Bindings {
+        @Binds public abstract fun bindEvCompImpl(impl: EvCompImpl): EvCompCompat
     }
 }
 
@@ -67,7 +67,7 @@
  * the [FrameInfo] via the [ComboRequestListener] to monitor the capture result.
  */
 @CameraScope
-class EvCompImpl
+public class EvCompImpl
 @Inject
 constructor(
     private val cameraProperties: CameraProperties,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompat.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompat.kt
index 95d2de8..a4dc034 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/StreamConfigurationMapCompat.kt
@@ -33,7 +33,7 @@
  * @param outputSizesCorrector [OutputSizesCorrector] class to perform correction on sizes.
  */
 @CameraScope
-class StreamConfigurationMapCompat
+public class StreamConfigurationMapCompat
 @Inject
 constructor(map: StreamConfigurationMap?, private val outputSizesCorrector: OutputSizesCorrector) {
     private val tag = "StreamConfigurationMapCompat"
@@ -61,7 +61,7 @@
      * @see [ImageFormat]
      * @see [PixelFormat]
      */
-    fun getOutputFormats(): Array<Int>? {
+    public fun getOutputFormats(): Array<Int>? {
         return impl.getOutputFormats()
     }
 
@@ -73,7 +73,7 @@
      * @param format an image format from [ImageFormat] or [PixelFormat]
      * @return an array of supported sizes, or `null` if the `format` is not a supported output
      */
-    fun getOutputSizes(format: Int): Array<Size>? {
+    public fun getOutputSizes(format: Int): Array<Size>? {
         if (cachedFormatOutputSizes.contains(format)) {
             return cachedFormatOutputSizes[format]?.clone()
         }
@@ -101,7 +101,7 @@
      *   `klass` is not a supported output.
      * @throws NullPointerException if `klass` was `null`
      */
-    fun <T> getOutputSizes(klass: Class<T>): Array<Size>? {
+    public fun <T> getOutputSizes(klass: Class<T>): Array<Size>? {
         if (cachedClassOutputSizes.contains(klass)) {
             return cachedClassOutputSizes[klass]?.clone()
         }
@@ -128,7 +128,7 @@
      * @param format an image format from [ImageFormat] or [PixelFormat]
      * @return an array of supported sizes, or `null` if the `format` is not a supported output
      */
-    fun getHighResolutionOutputSizes(format: Int): Array<Size>? {
+    public fun getHighResolutionOutputSizes(format: Int): Array<Size>? {
         if (cachedFormatHighResolutionOutputSizes.contains(format)) {
             return cachedFormatHighResolutionOutputSizes[format]?.clone()
         }
@@ -144,12 +144,12 @@
         return outputSizes?.clone()
     }
 
-    fun getOutputMinFrameDuration(format: Int, size: Size?): Long? {
+    public fun getOutputMinFrameDuration(format: Int, size: Size?): Long? {
         return impl.getOutputMinFrameDuration(format, size)
     }
 
     /** Returns the [StreamConfigurationMap] represented by this object. */
-    fun toStreamConfigurationMap(): StreamConfigurationMap? {
+    public fun toStreamConfigurationMap(): StreamConfigurationMap? {
         return impl.unwrap()
     }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt
index 5063bdd..43262e0 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt
@@ -31,24 +31,24 @@
 import dagger.Provides
 import kotlinx.coroutines.Deferred
 
-interface ZoomCompat {
-    val minZoomRatio: Float
-    val maxZoomRatio: Float
+public interface ZoomCompat {
+    public val minZoomRatio: Float
+    public val maxZoomRatio: Float
 
-    fun applyAsync(zoomRatio: Float, camera: UseCaseCamera): Deferred<Unit>
+    public fun applyAsync(zoomRatio: Float, camera: UseCaseCamera): Deferred<Unit>
 
     /**
      * Returns the current crop sensor region which should be used for converting
      * [androidx.camera.core.MeteringPoint] to sensor coordinates. Returns the sensor rect if there
      * is no crop region being set.
      */
-    fun getCropSensorRegion(): Rect
+    public fun getCropSensorRegion(): Rect
 
     @Module
-    abstract class Bindings {
-        companion object {
+    public abstract class Bindings {
+        public companion object {
             @Provides
-            fun provideZoomRatio(cameraProperties: CameraProperties): ZoomCompat {
+            public fun provideZoomRatio(cameraProperties: CameraProperties): ZoomCompat {
                 return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                     val range = cameraProperties.metadata.getControlZoomRatioRangeSafely()
                     if (range != null) {
@@ -64,7 +64,7 @@
     }
 }
 
-class CropRegionZoomCompat(private val cameraProperties: CameraProperties) : ZoomCompat {
+public class CropRegionZoomCompat(private val cameraProperties: CameraProperties) : ZoomCompat {
     override val minZoomRatio: Float
         get() = 1.0f
 
@@ -91,7 +91,7 @@
         return camera.setParameterAsync(CaptureRequest.SCALER_CROP_REGION, currentCropRect)
     }
 
-    override fun getCropSensorRegion() =
+    override fun getCropSensorRegion(): Rect =
         currentCropRect
             ?: cameraProperties.metadata[CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE]!!
 
@@ -115,7 +115,7 @@
 }
 
 @RequiresApi(Build.VERSION_CODES.R)
-class AndroidRZoomCompat(
+public class AndroidRZoomCompat(
     private val cameraProperties: CameraProperties,
     private val range: Range<Float>,
 ) : ZoomCompat {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AeFpsRangeLegacyQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AeFpsRangeLegacyQuirk.kt
index 4c58aac..d3f133f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AeFpsRangeLegacyQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AeFpsRangeLegacyQuirk.kt
@@ -39,7 +39,7 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class AeFpsRangeLegacyQuirk(cameraMetadata: CameraMetadata) : Quirk {
+public class AeFpsRangeLegacyQuirk(cameraMetadata: CameraMetadata) : Quirk {
     /**
      * Returns the fps range whose upper is 30 and whose lower is the smallest, or null if no range
      * has an upper equal to 30. The rationale is:
@@ -47,7 +47,7 @@
      * - Range lower contains the smallest supported value so that it can adapt as much as possible
      *   to low light conditions.
      */
-    val range: Range<Int>? by lazy {
+    public val range: Range<Int>? by lazy {
         val availableFpsRanges: Array<out Range<Int>>? =
             cameraMetadata[CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES]
         pickSuitableFpsRange(availableFpsRanges)
@@ -91,7 +91,8 @@
         return Range(newLower, newUpper)
     }
 
-    companion object {
-        fun isEnabled(cameraMetadata: CameraMetadata) = cameraMetadata.isHardwareLevelLegacy
+    public companion object {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean =
+            cameraMetadata.isHardwareLevelLegacy
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AfRegionFlipHorizontallyQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AfRegionFlipHorizontallyQuirk.kt
index 1083b94..1fe95c9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AfRegionFlipHorizontallyQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AfRegionFlipHorizontallyQuirk.kt
@@ -35,9 +35,9 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class AfRegionFlipHorizontallyQuirk : Quirk {
-    companion object {
-        fun isEnabled(cameraMetadata: CameraMetadata) =
+public class AfRegionFlipHorizontallyQuirk : Quirk {
+    public companion object {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean =
             isSamsungDevice() &&
                 Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU && // Samsung fixed it in T.
                 (cameraMetadata[CameraCharacteristics.LENS_FACING] ==
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AspectRatioLegacyApi21Quirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AspectRatioLegacyApi21Quirk.kt
index 72296c4..d0f0354 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AspectRatioLegacyApi21Quirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/AspectRatioLegacyApi21Quirk.kt
@@ -32,15 +32,15 @@
  *
  * @see androidx.camera.camera2.internal.compat.workaround.TargetAspectRatio
  */
-class AspectRatioLegacyApi21Quirk : Quirk {
+public class AspectRatioLegacyApi21Quirk : Quirk {
     /** Get the corrected aspect ratio. */
     @TargetAspectRatio.Ratio
-    fun getCorrectedAspectRatio(): Int {
+    public fun getCorrectedAspectRatio(): Int {
         return TargetAspectRatio.RATIO_MAX_JPEG
     }
 
-    companion object {
-        fun isEnabled(cameraMetadata: CameraMetadata) =
+    public companion object {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean =
             cameraMetadata.isHardwareLevelLegacy && Build.VERSION.SDK_INT == 21
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CamcorderProfileResolutionQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CamcorderProfileResolutionQuirk.kt
index cf107e2..d2258c59 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CamcorderProfileResolutionQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CamcorderProfileResolutionQuirk.kt
@@ -48,7 +48,7 @@
  * TODO: enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class CamcorderProfileResolutionQuirk(
+public class CamcorderProfileResolutionQuirk(
     private val streamConfigurationMapCompat: StreamConfigurationMapCompat
 ) : Quirk {
 
@@ -64,11 +64,12 @@
     }
 
     /** Returns the supported video resolutions. */
-    fun getSupportedResolutions(): List<Size> {
+    public fun getSupportedResolutions(): List<Size> {
         return supportedResolution.toList()
     }
 
-    companion object {
-        fun isEnabled(cameraMetadata: CameraMetadata) = cameraMetadata.isHardwareLevelLegacy
+    public companion object {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean =
+            cameraMetadata.isHardwareLevelLegacy
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraNoResponseWhenEnablingFlashQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraNoResponseWhenEnablingFlashQuirk.kt
index ab11186..e7930e2 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraNoResponseWhenEnablingFlashQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraNoResponseWhenEnablingFlashQuirk.kt
@@ -33,10 +33,10 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class CameraNoResponseWhenEnablingFlashQuirk : UseTorchAsFlashQuirk {
+public class CameraNoResponseWhenEnablingFlashQuirk : UseTorchAsFlashQuirk {
 
-    companion object {
-        val AFFECTED_MODELS =
+    public companion object {
+        public val AFFECTED_MODELS: List<String> =
             listOf(
                 // Enables on all Samsung Galaxy Note 5 devices.
                 "SM-N9200",
@@ -60,7 +60,7 @@
                 "SM-J510FN" // Galaxy J5
             )
 
-        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
             return AFFECTED_MODELS.contains(Build.MODEL.uppercase()) &&
                 cameraMetadata[CameraCharacteristics.LENS_FACING] == LENS_FACING_BACK
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
index 3f78c75..7988db32 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CameraQuirks.kt
@@ -28,7 +28,7 @@
 
 /** Provider of camera specific quirks. */
 @CameraScope
-class CameraQuirks
+public class CameraQuirks
 @Inject
 constructor(
     private val cameraMetadata: CameraMetadata?,
@@ -38,7 +38,7 @@
      * Goes through all defined camera specific quirks, then filters them to retrieve quirks
      * required for the camera identified by the provided [CameraMetadata].
      */
-    val quirks: Quirks by lazy {
+    public val quirks: Quirks by lazy {
         val quirkSettings = QuirkSettingsHolder.instance().get()
         val quirks: MutableList<Quirk> = mutableListOf()
         if (cameraMetadata == null) {
@@ -246,10 +246,10 @@
         }
     }
 
-    companion object {
+    public companion object {
         private const val TAG = "CameraQuirks"
 
-        fun isImmediateSurfaceReleaseAllowed(): Boolean {
+        public fun isImmediateSurfaceReleaseAllowed(): Boolean {
             // TODO(b/285956022): Releasing a Surface too early turns out to cause memory leaks
             //  where an Image may not be eventually closed. When the issue is resolved on an
             //  architectural level, uncomment the following, allowing compliant devices to recycle
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureIntentPreviewQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureIntentPreviewQuirk.kt
index 9bf8a0f..4ebe347 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureIntentPreviewQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureIntentPreviewQuirk.kt
@@ -26,20 +26,20 @@
  * [CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD].
  * - Subclasses of this quirk may contain device specific information.
  */
-interface CaptureIntentPreviewQuirk : Quirk {
+public interface CaptureIntentPreviewQuirk : Quirk {
     /**
      * Returns if the device specific issue can be workaround by using
      * [CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW] to replace
      * [CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD].
      */
-    fun workaroundByCaptureIntentPreview() = true
+    public fun workaroundByCaptureIntentPreview(): Boolean = true
 
-    companion object {
+    public companion object {
         /**
          * Returns if input quirks contains at least one [CaptureIntentPreviewQuirk] which
          * [CaptureIntentPreviewQuirk.workaroundByCaptureIntentPreview] is true.
          */
-        fun workaroundByCaptureIntentPreview(quirks: Quirks): Boolean {
+        public fun workaroundByCaptureIntentPreview(quirks: Quirks): Boolean {
             for (quirk in quirks.getAll(CaptureIntentPreviewQuirk::class.java)) {
                 if (quirk.workaroundByCaptureIntentPreview()) {
                     return true
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionOnClosedNotCalledQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionOnClosedNotCalledQuirk.kt
index 0ff8e52..0ea8bc8 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionOnClosedNotCalledQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionOnClosedNotCalledQuirk.kt
@@ -31,14 +31,14 @@
  * - Device(s): Devices in Android API version <= 22
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class CaptureSessionOnClosedNotCalledQuirk : Quirk {
-    companion object {
+public class CaptureSessionOnClosedNotCalledQuirk : Quirk {
+    public companion object {
         /**
          * The quirk is disabled for CameraPipe, as it intrinsically handles things without the
          * reliance on the onClosed callback. For [androidx.camera.core.impl.DeferrableSurface] that
          * does need this signal for ref-counting, CameraPipe has an extra pipeline that "finalizes"
          * the capture session when a new capture session is created or the camera device is closed.
          */
-        fun isEnabled(): Boolean = false
+        public fun isEnabled(): Boolean = false
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionStuckQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionStuckQuirk.kt
index b4c1390..14519de 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionStuckQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CaptureSessionStuckQuirk.kt
@@ -31,13 +31,13 @@
  * - Device(s): Devices in LEGACY camera hardware level.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class CaptureSessionStuckQuirk : Quirk {
-    companion object {
+public class CaptureSessionStuckQuirk : Quirk {
+    public companion object {
         /**
          * Always return false as CameraPipe handles this automatically. Please refer to
          * [androidx.camera.camera2.pipe.compat.Camera2Quirks.shouldWaitForRepeatingRequest] for the
          * conditions under which the quirk will be applied.
          */
-        fun isEnabled(): Boolean = false
+        public fun isEnabled(): Boolean = false
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCameraDeviceOnCameraGraphCloseQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCameraDeviceOnCameraGraphCloseQuirk.kt
index c8f6246..1d037c7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCameraDeviceOnCameraGraphCloseQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCameraDeviceOnCameraGraphCloseQuirk.kt
@@ -34,10 +34,10 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class CloseCameraDeviceOnCameraGraphCloseQuirk : Quirk {
-    companion object {
+public class CloseCameraDeviceOnCameraGraphCloseQuirk : Quirk {
+    public companion object {
         @JvmStatic
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return Build.HARDWARE == "samsungexynos7870"
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnDisconnectQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnDisconnectQuirk.kt
index 4863422..956dd35 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnDisconnectQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnDisconnectQuirk.kt
@@ -36,9 +36,9 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class CloseCaptureSessionOnDisconnectQuirk : Quirk {
+public class CloseCaptureSessionOnDisconnectQuirk : Quirk {
 
-    companion object {
+    public companion object {
         private val androidTOrNewerSm8150Devices =
             mapOf(
                 "google" to setOf("pixel 4", "pixel 4 xl"),
@@ -46,7 +46,7 @@
             )
 
         @JvmStatic
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             if (CameraQuirks.isImmediateSurfaceReleaseAllowed()) {
                 // If we can release Surfaces immediately, we'll finalize the session when the
                 // camera graph is closed (through FinalizeSessionOnCloseQuirk), and thus we won't
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt
index 06359b0..3f72538 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CloseCaptureSessionOnVideoQuirk.kt
@@ -32,8 +32,8 @@
  * - Device(s): All devices.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class CloseCaptureSessionOnVideoQuirk : Quirk {
-    companion object {
-        fun isEnabled(): Boolean = true
+public class CloseCaptureSessionOnVideoQuirk : Quirk {
+    public companion object {
+        public fun isEnabled(): Boolean = true
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ConfigureSurfaceToSecondarySessionFailQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ConfigureSurfaceToSecondarySessionFailQuirk.kt
index 1059eb2..a4b1257 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ConfigureSurfaceToSecondarySessionFailQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ConfigureSurfaceToSecondarySessionFailQuirk.kt
@@ -34,9 +34,10 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class ConfigureSurfaceToSecondarySessionFailQuirk : Quirk {
+public class ConfigureSurfaceToSecondarySessionFailQuirk : Quirk {
 
-    companion object {
-        fun isEnabled(cameraMetadata: CameraMetadata) = cameraMetadata.isHardwareLevelLegacy
+    public companion object {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean =
+            cameraMetadata.isHardwareLevelLegacy
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirk.kt
index e2f3e06..63fd0a42 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ControlZoomRatioRangeAssertionErrorQuirk.kt
@@ -37,10 +37,11 @@
  *
  * @see androidx.camera.camera2.pipe.integration.compat.workaround.getControlZoomRatioRangeSafely
  */
-@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
-class ControlZoomRatioRangeAssertionErrorQuirk : Quirk {
-    companion object {
-        fun isEnabled() = isJioPhoneNext() || isSamsungA2s() || isVivo2039()
+@SuppressLint("CameraXQuirksClassDetector")
+// TODO(b/270421716): enable when kotlin is supported.
+public class ControlZoomRatioRangeAssertionErrorQuirk : Quirk {
+    public companion object {
+        public fun isEnabled(): Boolean = isJioPhoneNext() || isSamsungA2s() || isVivo2039()
 
         private fun isJioPhoneNext() =
             isJioDevice() && Build.MODEL.startsWith("LS1542QW", ignoreCase = true)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CrashWhenTakingPhotoWithAutoFlashAEModeQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CrashWhenTakingPhotoWithAutoFlashAEModeQuirk.kt
index e7aee8f..4bfc087 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CrashWhenTakingPhotoWithAutoFlashAEModeQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/CrashWhenTakingPhotoWithAutoFlashAEModeQuirk.kt
@@ -33,9 +33,9 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class CrashWhenTakingPhotoWithAutoFlashAEModeQuirk : Quirk {
+public class CrashWhenTakingPhotoWithAutoFlashAEModeQuirk : Quirk {
 
-    companion object {
+    public companion object {
 
         private val AFFECTED_MODELS =
             listOf(
@@ -57,7 +57,7 @@
             )
 
         @JvmStatic
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return AFFECTED_MODELS.contains(Build.MODEL.uppercase())
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Device.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Device.kt
index e71263c..908088f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Device.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Device.kt
@@ -18,34 +18,34 @@
 
 import android.os.Build
 
-object Device {
-    fun isBluDevice() = isDeviceFrom("Blu")
+public object Device {
+    public fun isBluDevice(): Boolean = isDeviceFrom("Blu")
 
-    fun isHuaweiDevice() = isDeviceFrom("Huawei")
+    public fun isHuaweiDevice(): Boolean = isDeviceFrom("Huawei")
 
-    fun isInfinixDevice() = isDeviceFrom("Infinix")
+    public fun isInfinixDevice(): Boolean = isDeviceFrom("Infinix")
 
-    fun isItelDevice() = isDeviceFrom("Itel")
+    public fun isItelDevice(): Boolean = isDeviceFrom("Itel")
 
-    fun isJioDevice() = isDeviceFrom("Jio")
+    public fun isJioDevice(): Boolean = isDeviceFrom("Jio")
 
-    fun isGoogleDevice() = isDeviceFrom("Google")
+    public fun isGoogleDevice(): Boolean = isDeviceFrom("Google")
 
-    fun isMotorolaDevice() = isDeviceFrom("Motorola")
+    public fun isMotorolaDevice(): Boolean = isDeviceFrom("Motorola")
 
-    fun isOnePlusDevice() = isDeviceFrom("OnePlus")
+    public fun isOnePlusDevice(): Boolean = isDeviceFrom("OnePlus")
 
-    fun isOppoDevice() = isDeviceFrom("Oppo")
+    public fun isOppoDevice(): Boolean = isDeviceFrom("Oppo")
 
-    fun isPositivoDevice() = isDeviceFrom("Positivo")
+    public fun isPositivoDevice(): Boolean = isDeviceFrom("Positivo")
 
-    fun isRedmiDevice() = isDeviceFrom("Redmi")
+    public fun isRedmiDevice(): Boolean = isDeviceFrom("Redmi")
 
-    fun isSamsungDevice() = isDeviceFrom("Samsung")
+    public fun isSamsungDevice(): Boolean = isDeviceFrom("Samsung")
 
-    fun isXiaomiDevice() = isDeviceFrom("Xiaomi")
+    public fun isXiaomiDevice(): Boolean = isDeviceFrom("Xiaomi")
 
-    fun isVivoDevice() = isDeviceFrom("Vivo")
+    public fun isVivoDevice(): Boolean = isDeviceFrom("Vivo")
 
     private fun isDeviceFrom(vendor: String) =
         Build.MANUFACTURER.equalsCaseInsensitive(vendor) ||
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirks.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirks.kt
index 716036e..dee0a74 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirks.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirks.kt
@@ -31,11 +31,11 @@
  *
  * Device specific quirks are lazily loaded, i.e. They are loaded the first time they're needed.
  */
-object DeviceQuirks {
+public object DeviceQuirks {
     private const val TAG = "DeviceQuirks"
 
     /** Returns all device specific quirks loaded on the current device. */
-    @Volatile lateinit var all: Quirks
+    @Volatile public lateinit var all: Quirks
 
     init {
         // Direct executor will initialize quirks immediately, guaranteeing it's never null.
@@ -51,7 +51,7 @@
      * @param quirkClass The type of device quirk to retrieve.
      * @return A device [Quirk] instance of the provided type, or `null` if it isn't found.
      */
-    operator fun <T : Quirk?> get(quirkClass: Class<T>): T? {
+    public operator fun <T : Quirk?> get(quirkClass: Class<T>): T? {
         return all.get(quirkClass)
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirksLoader.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirksLoader.kt
index b7cd5c5..a400dfa 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirksLoader.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DeviceQuirksLoader.kt
@@ -20,13 +20,13 @@
 import androidx.camera.core.impl.QuirkSettings
 
 /** Loads all device specific quirks required for the current device. */
-object DeviceQuirksLoader {
+public object DeviceQuirksLoader {
 
     /**
      * Goes through all defined device-specific quirks, and returns those that should be loaded on
      * the current device.
      */
-    fun loadQuirks(quirkSettings: QuirkSettings): List<Quirk> {
+    public fun loadQuirks(quirkSettings: QuirkSettings): List<Quirk> {
         val quirks: MutableList<Quirk> = mutableListOf()
 
         // Load all device specific quirks, preferably in lexicographical order
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DisableAbortCapturesOnStopWithSessionProcessorQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DisableAbortCapturesOnStopWithSessionProcessorQuirk.kt
index d708fd9..57e6b13 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DisableAbortCapturesOnStopWithSessionProcessorQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/DisableAbortCapturesOnStopWithSessionProcessorQuirk.kt
@@ -36,8 +36,8 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class DisableAbortCapturesOnStopWithSessionProcessorQuirk : Quirk {
-    companion object {
-        fun isEnabled() = isSamsungDevice()
+public class DisableAbortCapturesOnStopWithSessionProcessorQuirk : Quirk {
+    public companion object {
+        public fun isEnabled(): Boolean = isSamsungDevice()
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExcludedSupportedSizesQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExcludedSupportedSizesQuirk.kt
index 9dabaf2..0718402 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExcludedSupportedSizesQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExcludedSupportedSizesQuirk.kt
@@ -40,12 +40,12 @@
  * 6T, Huawei P20, Samsung J7 Prime (SM-G610M) API 27, Samsung J7 (SM-J710MN) API 27, Redmi Note 9
  * Pro
  */
-class ExcludedSupportedSizesQuirk : Quirk {
+public class ExcludedSupportedSizesQuirk : Quirk {
     /**
      * Retrieves problematic supported surface sizes that have to be excluded on the current device,
      * for the given camera id and image format.
      */
-    fun getExcludedSizes(cameraId: String, imageFormat: Int): List<Size> {
+    public fun getExcludedSizes(cameraId: String, imageFormat: Int): List<Size> {
         if (isOnePlus6) {
             return getOnePlus6ExcludedSizes(cameraId, imageFormat)
         }
@@ -72,7 +72,7 @@
      * Retrieves problematic supported surface sizes that have to be excluded on the current device,
      * for the given camera id and class type.
      */
-    fun getExcludedSizes(cameraId: String, klass: Class<*>): List<Size> {
+    public fun getExcludedSizes(cameraId: String, klass: Class<*>): List<Size> {
         if (isHuaweiP20Lite) {
             return getHuaweiP20LiteExcludedSizes(cameraId, UNKNOWN_IMAGE_FORMAT, klass)
         }
@@ -222,11 +222,11 @@
         return sizes
     }
 
-    companion object {
+    public companion object {
         private const val TAG: String = "ExcludedSupportedSizesQuirk"
         private const val UNKNOWN_IMAGE_FORMAT: Int = -1
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return (isOnePlus6 ||
                 isOnePlus6T ||
                 isHuaweiP20Lite ||
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirk.kt
index 26db954..30a0b3c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraCroppingQuirk.kt
@@ -37,7 +37,7 @@
  * Galaxy A3 (2017) SM-A320FL, Samsung Galaxy J5 Prime SM-G570M, Samsung Galaxy J7 Prime SM-G610F,
  * Samsung Galaxy J7 Prime SM-G610M
  */
-class ExtraCroppingQuirk : Quirk {
+public class ExtraCroppingQuirk : Quirk {
     /**
      * Get a verified resolution that is guaranteed to work.
      *
@@ -47,7 +47,7 @@
      * @return null if no resolution provided, in which case the calling code should fallback to
      *   user provided target resolution.
      */
-    fun getVerifiedResolution(configType: ConfigType): Size? {
+    public fun getVerifiedResolution(configType: ConfigType): Size? {
         return if (isSamsungDistortion) {
             // The following resolutions are needed for both the front and the back camera.
             when (configType) {
@@ -59,7 +59,7 @@
         } else null
     }
 
-    companion object {
+    public companion object {
         private val SAMSUNG_DISTORTION_MODELS_TO_API_LEVEL_MAP: MutableMap<String, Range<Int>?> =
             mutableMapOf(
                 "SM-T580" to null,
@@ -70,7 +70,7 @@
                 "SM-G610M" to Range(21, 26)
             )
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return isSamsungDistortion
         }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedOutputSizeQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedOutputSizeQuirk.kt
index a09c5b1..1b9d9b6 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedOutputSizeQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedOutputSizeQuirk.kt
@@ -33,9 +33,9 @@
  * TODO: enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class ExtraSupportedOutputSizeQuirk : Quirk {
+public class ExtraSupportedOutputSizeQuirk : Quirk {
     /** Returns the extra supported resolutions on the device. */
-    fun getExtraSupportedResolutions(format: Int): Array<Size> {
+    public fun getExtraSupportedResolutions(format: Int): Array<Size> {
         return if (
             (format == ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE && isMotoE5Play)
         ) {
@@ -46,7 +46,7 @@
     }
 
     /** Returns the extra supported resolutions on the device. */
-    fun <T> getExtraSupportedResolutions(klass: Class<T>): Array<Size> {
+    public fun <T> getExtraSupportedResolutions(klass: Class<T>): Array<Size> {
         return if (StreamConfigurationMap.isOutputSupportedFor(klass) && isMotoE5Play) {
             motoE5PlayExtraSupportedResolutions
         } else {
@@ -65,8 +65,8 @@
                 // SD (640:480 is already included in the original list)
             )
 
-    companion object {
-        fun isEnabled(): Boolean {
+    public companion object {
+        public fun isEnabled(): Boolean {
             return isMotoE5Play
         }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt
index ccf7a2d..c178410 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ExtraSupportedSurfaceCombinationsQuirk.kt
@@ -30,9 +30,9 @@
  * Samsung S7's LIMITED-level camera device can support additional YUV/640x480 + PRIV/PREVIEW +
  * YUV/MAXIMUM combination. Device(s): Samsung S7 devices
  */
-class ExtraSupportedSurfaceCombinationsQuirk : Quirk {
+public class ExtraSupportedSurfaceCombinationsQuirk : Quirk {
     /** Returns the extra supported surface combinations for specific camera on the device. */
-    fun getExtraSupportedSurfaceCombinations(cameraId: String): List<SurfaceCombination> {
+    public fun getExtraSupportedSurfaceCombinations(cameraId: String): List<SurfaceCombination> {
         if (isSamsungS7) {
             return getSamsungS7ExtraCombinations(cameraId)
         }
@@ -66,7 +66,7 @@
         return extraCombinations
     }
 
-    companion object {
+    public companion object {
         private const val TAG = "ExtraSupportedSurfaceCombinationsQuirk"
         private val FULL_LEVEL_YUV_PRIV_YUV_CONFIGURATION = createFullYuvPrivYuvConfiguration()
         private val FULL_LEVEL_YUV_YUV_YUV_CONFIGURATION = createFullYuvYuvYuvConfiguration()
@@ -86,7 +86,7 @@
                 "SCG26", // Galaxy S24 Ultra
             )
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return (isSamsungS7 ||
                 supportExtraLevel3ConfigurationsGoogleDevice() ||
                 supportExtraLevel3ConfigurationsSamsungDevice())
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt
index aeaf5d7..45c4ac5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FinalizeSessionOnCloseQuirk.kt
@@ -37,11 +37,11 @@
  * - Device(s): All devices.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class FinalizeSessionOnCloseQuirk : Quirk {
-    companion object {
-        fun isEnabled() = true
+public class FinalizeSessionOnCloseQuirk : Quirk {
+    public companion object {
+        public fun isEnabled(): Boolean = true
 
-        fun getBehavior() =
+        public fun getBehavior(): FinalizeSessionOnCloseBehavior =
             if (CameraQuirks.isImmediateSurfaceReleaseAllowed()) {
                 // Finalize immediately for devices that allow immediate Surface reuse.
                 FinalizeSessionOnCloseBehavior.IMMEDIATE
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashAvailabilityBufferUnderflowQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashAvailabilityBufferUnderflowQuirk.kt
index 88baebd4..35d9105 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashAvailabilityBufferUnderflowQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashAvailabilityBufferUnderflowQuirk.kt
@@ -35,9 +35,9 @@
  * TODO: enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class FlashAvailabilityBufferUnderflowQuirk : Quirk {
+public class FlashAvailabilityBufferUnderflowQuirk : Quirk {
 
-    companion object {
+    public companion object {
         private val KNOWN_AFFECTED_MODELS =
             setOf(
                 // Devices enumerated as DeviceInfo(Build.MANUFACTURER, Build.MODEL).
@@ -45,14 +45,14 @@
                 DeviceInfo("sprd", "DM20C"),
             )
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return KNOWN_AFFECTED_MODELS.contains(DeviceInfo(Build.MANUFACTURER, Build.MODEL))
         }
     }
 
-    data class DeviceInfo private constructor(val manufacturer: String, val model: String) {
-        companion object {
-            operator fun invoke(manufacturer: String, model: String) =
+    public data class DeviceInfo private constructor(val manufacturer: String, val model: String) {
+        public companion object {
+            public operator fun invoke(manufacturer: String, model: String): DeviceInfo =
                 DeviceInfo(manufacturer.lowercase(Locale.US), model.lowercase(Locale.US))
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashTooSlowQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashTooSlowQuirk.kt
index 7a087a0..4066eae 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashTooSlowQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/FlashTooSlowQuirk.kt
@@ -35,9 +35,9 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class FlashTooSlowQuirk : UseTorchAsFlashQuirk {
+public class FlashTooSlowQuirk : UseTorchAsFlashQuirk {
 
-    companion object {
+    public companion object {
         private val AFFECTED_MODEL_PREFIXES =
             listOf(
                 "PIXEL 3A",
@@ -50,7 +50,7 @@
                 "RMX3231" // Realme C11 2021
             )
 
-        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
             return isAffectedModel() && cameraMetadata[LENS_FACING] == LENS_FACING_BACK
         }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailWithAutoFlashQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailWithAutoFlashQuirk.kt
index 8be0ba8..57a8d52 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailWithAutoFlashQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailWithAutoFlashQuirk.kt
@@ -33,9 +33,9 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class ImageCaptureFailWithAutoFlashQuirk : Quirk {
+public class ImageCaptureFailWithAutoFlashQuirk : Quirk {
 
-    companion object {
+    public companion object {
         /** List of devices with the issue. See b/228800360. */
         private val BUILD_MODELS_FRONT_CAMERA =
             listOf(
@@ -43,7 +43,7 @@
                 "sm-j710f", // Samsung Galaxy J7
             )
 
-        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
             return BUILD_MODELS_FRONT_CAMERA.contains(Build.MODEL.lowercase()) &&
                 cameraMetadata[LENS_FACING] == LENS_FACING_FRONT
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailedForVideoSnapshotQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailedForVideoSnapshotQuirk.kt
index 9d7099a..7f24526 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailedForVideoSnapshotQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailedForVideoSnapshotQuirk.kt
@@ -42,10 +42,10 @@
  *   sm-a035m, sm-f946u1, tecno mobile bf6, Huawei P Smart.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class ImageCaptureFailedForVideoSnapshotQuirk : Quirk {
+public class ImageCaptureFailedForVideoSnapshotQuirk : Quirk {
 
-    companion object {
-        fun isEnabled(): Boolean {
+    public companion object {
+        public fun isEnabled(): Boolean {
             return isUniSocChipsetDevice() || isHuaweiPSmart()
         }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailedWhenVideoCaptureIsBoundQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailedWhenVideoCaptureIsBoundQuirk.kt
index 32ccc91..497505f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailedWhenVideoCaptureIsBoundQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFailedWhenVideoCaptureIsBoundQuirk.kt
@@ -39,11 +39,11 @@
  *   Samsung Tab A8
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class ImageCaptureFailedWhenVideoCaptureIsBoundQuirk :
+public class ImageCaptureFailedWhenVideoCaptureIsBoundQuirk :
     CaptureIntentPreviewQuirk, SurfaceProcessingQuirk {
 
-    companion object {
-        fun isEnabled(): Boolean {
+    public companion object {
+        public fun isEnabled(): Boolean {
             return isBluStudioX10 ||
                 isItelW6004 ||
                 isVivo1805 ||
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFlashNotFireQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFlashNotFireQuirk.kt
index d9dfbce..f410428 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFlashNotFireQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureFlashNotFireQuirk.kt
@@ -32,9 +32,9 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class ImageCaptureFlashNotFireQuirk : UseTorchAsFlashQuirk {
+public class ImageCaptureFlashNotFireQuirk : UseTorchAsFlashQuirk {
 
-    companion object {
+    public companion object {
         // List of devices with the issue. See b/228800360.
         private val BUILD_MODELS =
             listOf(
@@ -46,7 +46,7 @@
                 "sm-j710f", // Samsung Galaxy J7
             )
 
-        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
             val isFrontCameraAffected =
                 BUILD_MODELS_FRONT_CAMERA.contains(Build.MODEL.lowercase()) &&
                     cameraMetadata[LENS_FACING] == LENS_FACING_FRONT
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCapturePixelHDRPlusQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCapturePixelHDRPlusQuirk.kt
index 7c6f247..fab57ed 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCapturePixelHDRPlusQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCapturePixelHDRPlusQuirk.kt
@@ -35,11 +35,11 @@
  * TODO: enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class ImageCapturePixelHDRPlusQuirk : Quirk {
-    companion object {
+public class ImageCapturePixelHDRPlusQuirk : Quirk {
+    public companion object {
         private val BUILD_MODELS = listOf("Pixel 2", "Pixel 2 XL", "Pixel 3", "Pixel 3 XL")
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return BUILD_MODELS.contains(Build.MODEL) &&
                 isGoogleDevice() &&
                 Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWashedOutImageQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWashedOutImageQuirk.kt
index e408e87..4276481 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWashedOutImageQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWashedOutImageQuirk.kt
@@ -33,10 +33,10 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class ImageCaptureWashedOutImageQuirk : UseTorchAsFlashQuirk {
+public class ImageCaptureWashedOutImageQuirk : UseTorchAsFlashQuirk {
 
-    companion object {
-        val BUILD_MODELS =
+    public companion object {
+        public val BUILD_MODELS: List<String> =
             listOf(
                 // List of devices with the issue. See b/181966663.
                 "SM-G9300", // Galaxy S7
@@ -57,7 +57,7 @@
                 "SM-G935P"
             )
 
-        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
             return BUILD_MODELS.contains(Build.MODEL.uppercase()) &&
                 cameraMetadata[LENS_FACING] == LENS_FACING_BACK
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWithFlashUnderexposureQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWithFlashUnderexposureQuirk.kt
index 02ce99e..863e38f 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWithFlashUnderexposureQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ImageCaptureWithFlashUnderexposureQuirk.kt
@@ -37,8 +37,8 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class ImageCaptureWithFlashUnderexposureQuirk : UseTorchAsFlashQuirk {
-    companion object {
+public class ImageCaptureWithFlashUnderexposureQuirk : UseTorchAsFlashQuirk {
+    public companion object {
         // List of devices with the issue. See b/228800282.
         private val BUILD_MODELS =
             listOf(
@@ -50,7 +50,7 @@
                 "sm-j710mn", // Samsung Galaxy J7
             )
 
-        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
             return BUILD_MODELS.contains(Build.MODEL.lowercase()) &&
                 cameraMetadata[LENS_FACING] == LENS_FACING_BACK
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
index b2cacd4..39578eb 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/InvalidVideoProfilesQuirk.kt
@@ -40,9 +40,9 @@
  * TODO: enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class InvalidVideoProfilesQuirk : Quirk {
+public class InvalidVideoProfilesQuirk : Quirk {
 
-    companion object {
+    public companion object {
         private val AFFECTED_PIXEL_MODELS: List<String> =
             listOf(
                 "pixel 4",
@@ -67,7 +67,7 @@
                 "pht110",
             )
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return isAffectedSamsungDevices() ||
                 isAffectedPixelDevices() ||
                 isAffectedXiaomiDevices() ||
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/JpegHalCorruptImageQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/JpegHalCorruptImageQuirk.kt
index db4fdf3..539211a 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/JpegHalCorruptImageQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/JpegHalCorruptImageQuirk.kt
@@ -33,12 +33,13 @@
  *   workaround this issue.
  * - Device(s): Samsung Galaxy S7 (SM-G930T and SM-G930V variants), Alps k61v1_basic_ref
  */
-@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
-class JpegHalCorruptImageQuirk : SoftwareJpegEncodingPreferredQuirk {
-    companion object {
+@SuppressLint("CameraXQuirksClassDetector")
+// TODO(b/270421716): enable when kotlin is supported.
+public class JpegHalCorruptImageQuirk : SoftwareJpegEncodingPreferredQuirk {
+    public companion object {
         private val KNOWN_AFFECTED_DEVICES = listOf("heroqltevzw", "heroqltetmo", "k61v1_basic_ref")
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return KNOWN_AFFECTED_DEVICES.contains(Build.DEVICE.lowercase())
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Nexus4AndroidLTargetAspectRatioQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Nexus4AndroidLTargetAspectRatioQuirk.kt
index b776092..6761f26 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Nexus4AndroidLTargetAspectRatioQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/Nexus4AndroidLTargetAspectRatioQuirk.kt
@@ -29,21 +29,21 @@
  *
  * @see androidx.camera.camera2.internal.compat.workaround.TargetAspectRatio
  */
-class Nexus4AndroidLTargetAspectRatioQuirk : Quirk {
+public class Nexus4AndroidLTargetAspectRatioQuirk : Quirk {
     /** Get the corrected aspect ratio. */
     @TargetAspectRatio.Ratio
-    fun getCorrectedAspectRatio(): Int {
+    public fun getCorrectedAspectRatio(): Int {
         return TargetAspectRatio.RATIO_MAX_JPEG
     }
 
-    companion object {
+    public companion object {
         // List of devices with the issue.
         private val DEVICE_MODELS =
             listOf(
                 "NEXUS 4" // b/158749159
             )
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return isGoogleDevice() &&
                 Build.VERSION.SDK_INT < 23 &&
                 DEVICE_MODELS.contains(Build.MODEL.uppercase())
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewDelayWhenVideoCaptureIsBoundQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewDelayWhenVideoCaptureIsBoundQuirk.kt
index e4caa3e..a0b108d 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewDelayWhenVideoCaptureIsBoundQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewDelayWhenVideoCaptureIsBoundQuirk.kt
@@ -29,7 +29,8 @@
  * - Device(s): Some Huawei devices.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class PreviewDelayWhenVideoCaptureIsBoundQuirk : CaptureIntentPreviewQuirk, SurfaceProcessingQuirk {
+public class PreviewDelayWhenVideoCaptureIsBoundQuirk :
+    CaptureIntentPreviewQuirk, SurfaceProcessingQuirk {
     /*
     Known devices:
             "HWELE",  // P30
@@ -48,7 +49,7 @@
             honor v2
      */
 
-    companion object {
-        fun isEnabled() = isHuaweiDevice()
+    public companion object {
+        public fun isEnabled(): Boolean = isHuaweiDevice()
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewOrientationIncorrectQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewOrientationIncorrectQuirk.kt
index 7bd15d7..82b1cd7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewOrientationIncorrectQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewOrientationIncorrectQuirk.kt
@@ -35,9 +35,10 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class PreviewOrientationIncorrectQuirk : Quirk {
+public class PreviewOrientationIncorrectQuirk : Quirk {
 
-    companion object {
-        fun isEnabled(cameraMetadata: CameraMetadata) = cameraMetadata.isHardwareLevelLegacy
+    public companion object {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean =
+            cameraMetadata.isHardwareLevelLegacy
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewPixelHDRnetQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewPixelHDRnetQuirk.kt
index 0a62165..86da178 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewPixelHDRnetQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewPixelHDRnetQuirk.kt
@@ -38,14 +38,14 @@
  * TODO: enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class PreviewPixelHDRnetQuirk : Quirk {
+public class PreviewPixelHDRnetQuirk : Quirk {
 
-    companion object {
+    public companion object {
 
         /** The devices that support wysiwyg preview in 3rd party apps (go/p20-wysiwyg-hdr) */
         private val SUPPORTED_DEVICES = listOf("sunfish", "bramble", "redfin", "barbet")
 
-        fun isEnabled(): Boolean =
+        public fun isEnabled(): Boolean =
             isGoogleDevice() &&
                 SUPPORTED_DEVICES.contains(Build.DEVICE.lowercase(Locale.getDefault()))
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewStretchWhenVideoCaptureIsBoundQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewStretchWhenVideoCaptureIsBoundQuirk.kt
index b775fc5..c8c57fc 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewStretchWhenVideoCaptureIsBoundQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/PreviewStretchWhenVideoCaptureIsBoundQuirk.kt
@@ -29,10 +29,10 @@
  * - Device(s): Samsung J3, Samsung J5, Samsung J7, Samsung J1 Ace neo and Oppo A37F
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class PreviewStretchWhenVideoCaptureIsBoundQuirk : CaptureIntentPreviewQuirk {
+public class PreviewStretchWhenVideoCaptureIsBoundQuirk : CaptureIntentPreviewQuirk {
 
-    companion object {
-        fun isEnabled(): Boolean {
+    public companion object {
+        public fun isEnabled(): Boolean {
             return isHuaweiP8Lite() ||
                 isSamsungJ3() ||
                 isSamsungJ7() ||
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/RepeatingStreamConstraintForVideoRecordingQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/RepeatingStreamConstraintForVideoRecordingQuirk.kt
index 903102f..335e38e 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/RepeatingStreamConstraintForVideoRecordingQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/RepeatingStreamConstraintForVideoRecordingQuirk.kt
@@ -29,10 +29,11 @@
  *   video recording output. It requires an extra repeating stream in at least 320x240.
  * - Device(s): Huawei Mate 9
  */
-@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
-class RepeatingStreamConstraintForVideoRecordingQuirk : Quirk {
-    companion object {
-        fun isEnabled() = isHuaweiMate9()
+@SuppressLint("CameraXQuirksClassDetector")
+// TODO(b/270421716): enable when kotlin is supported.
+public class RepeatingStreamConstraintForVideoRecordingQuirk : Quirk {
+    public companion object {
+        public fun isEnabled(): Boolean = isHuaweiMate9()
 
         private fun isHuaweiMate9() =
             isHuaweiDevice() && "mha-l29".equals(Build.MODEL, ignoreCase = true)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/StillCaptureFlashStopRepeatingQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/StillCaptureFlashStopRepeatingQuirk.kt
index 2eb7487..41662a9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/StillCaptureFlashStopRepeatingQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/StillCaptureFlashStopRepeatingQuirk.kt
@@ -32,10 +32,11 @@
  *   ahead of still capture and setRepeating again after capture is done can fix the issue.
  * - Device(s): Samsung SM-A716
  */
-@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
-class StillCaptureFlashStopRepeatingQuirk : Quirk {
-    companion object {
-        fun isEnabled(): Boolean {
+@SuppressLint("CameraXQuirksClassDetector")
+// TODO(b/270421716): enable when kotlin is supported.
+public class StillCaptureFlashStopRepeatingQuirk : Quirk {
+    public companion object {
+        public fun isEnabled(): Boolean {
             return isSamsungDevice() && Build.MODEL.uppercase().startsWith("SM-A716")
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/SurfaceOrderQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/SurfaceOrderQuirk.kt
index 28887f1..bf70694 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/SurfaceOrderQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/SurfaceOrderQuirk.kt
@@ -40,12 +40,13 @@
  *
  * @see SurfaceSorter
  */
-@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
-class SurfaceOrderQuirk : Quirk {
-    companion object {
+@SuppressLint("CameraXQuirksClassDetector")
+// TODO(b/270421716): enable when kotlin is supported.
+public class SurfaceOrderQuirk : Quirk {
+    public companion object {
         private val BUILD_HARDWARE_SET = listOf("samsungexynos7570", "samsungexynos7870")
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             // Apply this quirk to all devices to avoid that there are still some other devices with
             // the same quirk. The workaround is only to sort the input surface list when creating
             // CameraCaptureSession, so it does not cost much performance and should be safe to
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TemporalNoiseQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TemporalNoiseQuirk.kt
index 0d9983c..99f265b 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TemporalNoiseQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TemporalNoiseQuirk.kt
@@ -31,10 +31,10 @@
  * - Device(s): Pixel 8.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class TemporalNoiseQuirk : CaptureIntentPreviewQuirk {
+public class TemporalNoiseQuirk : CaptureIntentPreviewQuirk {
 
-    companion object {
-        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+    public companion object {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
             return isPixel8 && cameraMetadata[LENS_FACING] == LENS_FACING_FRONT
         }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TextureViewIsClosedQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TextureViewIsClosedQuirk.kt
index 35bb25fb..280df78 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TextureViewIsClosedQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TextureViewIsClosedQuirk.kt
@@ -33,10 +33,10 @@
  * TODO(b/270421716): enable CameraXQuirksClassDetector lint check when kotlin is supported.
  */
 @SuppressLint("CameraXQuirksClassDetector")
-class TextureViewIsClosedQuirk : Quirk {
-    companion object {
+public class TextureViewIsClosedQuirk : Quirk {
+    public companion object {
         @Suppress("UNUSED_PARAMETER")
-        fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean {
             return Build.VERSION.SDK_INT <= Build.VERSION_CODES.M
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchFlashRequiredFor3aUpdateQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchFlashRequiredFor3aUpdateQuirk.kt
index 897392c..f3cf12e 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchFlashRequiredFor3aUpdateQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchFlashRequiredFor3aUpdateQuirk.kt
@@ -38,17 +38,20 @@
  *   thus setting `FLASH_MODE_TORCH` won't be required.
  * - Device(s): Pixel 6A, 6 PRO, 7, 7A, 7 PRO, 8, 8 PRO.
  */
-@SuppressLint("CameraXQuirksClassDetector") // TODO: b/270421716 - enable when kotlin is supported.
-class TorchFlashRequiredFor3aUpdateQuirk(private val cameraMetadata: CameraMetadata) : Quirk {
+@SuppressLint("CameraXQuirksClassDetector")
+// TODO: b/270421716 - enable when kotlin is supported.
+public class TorchFlashRequiredFor3aUpdateQuirk(private val cameraMetadata: CameraMetadata) :
+    Quirk {
     /**
      * Returns whether [CaptureRequest.FLASH_MODE_TORCH] is required to be set.
      *
      * This will check if the [CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH] is supported, which
      * is more recommended than using a quirk like using `FLASH_MODE_TORCH`.
      */
-    fun isFlashModeTorchRequired() = !cameraMetadata.isExternalFlashAeModeSupported()
+    public fun isFlashModeTorchRequired(): Boolean =
+        !cameraMetadata.isExternalFlashAeModeSupported()
 
-    companion object {
+    public companion object {
         private val AFFECTED_PIXEL_MODELS: List<String> =
             mutableListOf(
                 "PIXEL 6A",
@@ -60,7 +63,8 @@
                 "PIXEL 8 PRO"
             )
 
-        fun isEnabled(cameraMetadata: CameraMetadata) = isAffectedModel(cameraMetadata)
+        public fun isEnabled(cameraMetadata: CameraMetadata): Boolean =
+            isAffectedModel(cameraMetadata)
 
         private fun isAffectedModel(cameraMetadata: CameraMetadata) =
             isAffectedPixelModel() && cameraMetadata.isFrontCamera
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchIsClosedAfterImageCapturingQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchIsClosedAfterImageCapturingQuirk.kt
index 938dc7c..7bc232b 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchIsClosedAfterImageCapturingQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/TorchIsClosedAfterImageCapturingQuirk.kt
@@ -26,10 +26,11 @@
  * - Description: The Torch is unexpectedly turned off after taking a picture.
  * - Device(s): Redmi 4X, Redmi 5A, Redmi Note 5 (Pro), Mi A1, Mi A2, Mi A2 lite and Redmi 6 Pro.
  */
-@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
-class TorchIsClosedAfterImageCapturingQuirk : Quirk {
+@SuppressLint("CameraXQuirksClassDetector")
+// TODO(b/270421716): enable when kotlin is supported.
+public class TorchIsClosedAfterImageCapturingQuirk : Quirk {
 
-    companion object {
+    public companion object {
         // List of devices with the issue. See b/228272227.
         private val BUILD_MODELS =
             listOf(
@@ -43,7 +44,7 @@
                 "redmi 6 pro", // Xiaomi Redmi 6 Pro
             )
 
-        fun isEnabled(): Boolean {
+        public fun isEnabled(): Boolean {
             return BUILD_MODELS.contains(Build.MODEL.lowercase())
         }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/UseTorchAsFlashQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/UseTorchAsFlashQuirk.kt
index addf662..12c67804 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/UseTorchAsFlashQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/UseTorchAsFlashQuirk.kt
@@ -24,4 +24,4 @@
  * Subclasses of this interface can denote the reason why torch is required instead of AE
  * pre-capture.
  */
-interface UseTorchAsFlashQuirk : Quirk
+public interface UseTorchAsFlashQuirk : Quirk
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/YuvImageOnePixelShiftQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/YuvImageOnePixelShiftQuirk.kt
index 23f48d3..4964a39 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/YuvImageOnePixelShiftQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/YuvImageOnePixelShiftQuirk.kt
@@ -30,10 +30,11 @@
  *   data. It leads to the leftmost column degradation when converting YUV to RGB in applications.
  * - Device(s): Motorola MotoG3, Samsung SM-G532F/SM-J700F/SM-J415F/SM-920F, Xiaomi Mi A1
  */
-@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
-class YuvImageOnePixelShiftQuirk : OnePixelShiftQuirk {
-    companion object {
-        fun isEnabled() =
+@SuppressLint("CameraXQuirksClassDetector")
+// TODO(b/270421716): enable when kotlin is supported.
+public class YuvImageOnePixelShiftQuirk : OnePixelShiftQuirk {
+    public companion object {
+        public fun isEnabled(): Boolean =
             isMotorolaMotoG3() ||
                 isSamsungSMG532F() ||
                 isSamsungSMJ700F() ||
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ZslDisablerQuirk.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ZslDisablerQuirk.kt
index a25ebfb..40f7e51 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ZslDisablerQuirk.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/quirk/ZslDisablerQuirk.kt
@@ -29,15 +29,16 @@
  *   to disable zero-shutter lag and return false for [CameraInfo.isZslSupported].
  * - Device(s): Samsung Fold4, Samsung s22, Xiaomi Mi 8
  */
-@SuppressLint("CameraXQuirksClassDetector") // TODO(b/270421716): enable when kotlin is supported.
-class ZslDisablerQuirk : Quirk {
+@SuppressLint("CameraXQuirksClassDetector")
+// TODO(b/270421716): enable when kotlin is supported.
+public class ZslDisablerQuirk : Quirk {
 
-    companion object {
+    public companion object {
         private val AFFECTED_SAMSUNG_MODEL = listOf("SM-F936", "SM-S901U", "SM-S908U", "SM-S908U1")
 
         private val AFFECTED_XIAOMI_MODEL = listOf("MI 8")
 
-        fun load(): Boolean {
+        public fun load(): Boolean {
             return isAffectedSamsungDevices() || isAffectedXiaoMiDevices()
         }
 
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AeFpsRange.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AeFpsRange.kt
index 4dad3e4..a55a8043 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AeFpsRange.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AeFpsRange.kt
@@ -27,7 +27,7 @@
  * exposure.
  */
 @CameraScope
-class AeFpsRange @Inject constructor(cameraQuirks: CameraQuirks) {
+public class AeFpsRange @Inject constructor(cameraQuirks: CameraQuirks) {
     private val aeTargetFpsRange: Range<Int>? by lazy {
         /** Chooses the AE target FPS range on legacy devices. */
         cameraQuirks.quirks[AeFpsRangeLegacyQuirk::class.java]?.range
@@ -37,7 +37,7 @@
      * Sets the [android.hardware.camera2.CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE] option on
      * legacy device when possible.
      */
-    fun getTargetAeFpsRange(): Range<Int>? {
+    public fun getTargetAeFpsRange(): Range<Int>? {
         return aeTargetFpsRange
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AutoFlashAEModeDisabler.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AutoFlashAEModeDisabler.kt
index 27a2971..6e7c528 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AutoFlashAEModeDisabler.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/AutoFlashAEModeDisabler.kt
@@ -29,14 +29,14 @@
  * A workaround to turn off the auto flash AE mode if device has the
  * [CrashWhenTakingPhotoWithAutoFlashAEModeQuirk] or [ImageCaptureFailWithAutoFlashQuirk].
  */
-interface AutoFlashAEModeDisabler {
-    fun getCorrectedAeMode(aeMode: Int): Int
+public interface AutoFlashAEModeDisabler {
+    public fun getCorrectedAeMode(aeMode: Int): Int
 
     @Module
-    abstract class Bindings {
-        companion object {
+    public abstract class Bindings {
+        public companion object {
             @Provides
-            fun provideAEModeDisabler(cameraQuirks: CameraQuirks): AutoFlashAEModeDisabler {
+            public fun provideAEModeDisabler(cameraQuirks: CameraQuirks): AutoFlashAEModeDisabler {
                 val isFailWithAutoFlashQuirkEnabled =
                     cameraQuirks.quirks.contains(ImageCaptureFailWithAutoFlashQuirk::class.java)
 
@@ -51,7 +51,7 @@
     }
 }
 
-object AutoFlashAEModeDisablerImpl : AutoFlashAEModeDisabler {
+public object AutoFlashAEModeDisablerImpl : AutoFlashAEModeDisabler {
 
     /**
      * Get AE mode corrected by the [CrashWhenTakingPhotoWithAutoFlashAEModeQuirk] and
@@ -62,6 +62,6 @@
     }
 }
 
-object NoOpAutoFlashAEModeDisabler : AutoFlashAEModeDisabler {
+public object NoOpAutoFlashAEModeDisabler : AutoFlashAEModeDisabler {
     override fun getCorrectedAeMode(aeMode: Int): Int = aeMode
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CameraMetadataSafeGetter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CameraMetadataSafeGetter.kt
index 660cce0..e16a81b 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CameraMetadataSafeGetter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CameraMetadataSafeGetter.kt
@@ -33,7 +33,7 @@
  * @param key the [CameraCharacteristics.Key] of the characteristic
  * @return the value of the characteristic
  */
-fun <T> CameraMetadata.getSafely(key: CameraCharacteristics.Key<T>): T? {
+public fun <T> CameraMetadata.getSafely(key: CameraCharacteristics.Key<T>): T? {
     if (
         Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
             key == CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE
@@ -58,7 +58,7 @@
  * @return the CONTROL_ZOOM_RATIO_RANGE characteristic value, null in case of [AssertionError].
  */
 @RequiresApi(Build.VERSION_CODES.R)
-fun CameraMetadata.getControlZoomRatioRangeSafely(): Range<Float>? =
+public fun CameraMetadata.getControlZoomRatioRangeSafely(): Range<Float>? =
     try {
         var range = get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE)
         if (range == null) {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CapturePipelineTorchCorrection.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CapturePipelineTorchCorrection.kt
index e82d778..2af7e3a 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CapturePipelineTorchCorrection.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/CapturePipelineTorchCorrection.kt
@@ -46,7 +46,7 @@
  * OFF then ON after the capturing.
  */
 @UseCaseCameraScope
-class CapturePipelineTorchCorrection
+public class CapturePipelineTorchCorrection
 @Inject
 constructor(
     cameraProperties: CameraProperties,
@@ -115,7 +115,8 @@
 
     private fun isTorchOn() = torchControl.torchStateLiveData.value == TorchState.ON
 
-    companion object {
-        val isEnabled = DeviceQuirks[TorchIsClosedAfterImageCapturingQuirk::class.java] != null
+    public companion object {
+        public val isEnabled: Boolean =
+            DeviceQuirks[TorchIsClosedAfterImageCapturingQuirk::class.java] != null
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ExtraSupportedSurfaceCombinationsContainer.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ExtraSupportedSurfaceCombinationsContainer.kt
index 7ec65ef..0909c0d 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ExtraSupportedSurfaceCombinationsContainer.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ExtraSupportedSurfaceCombinationsContainer.kt
@@ -20,12 +20,12 @@
 import androidx.camera.camera2.pipe.integration.compat.quirk.ExtraSupportedSurfaceCombinationsQuirk
 import androidx.camera.core.impl.SurfaceCombination
 
-class ExtraSupportedSurfaceCombinationsContainer {
+public class ExtraSupportedSurfaceCombinationsContainer {
     private val quirk: ExtraSupportedSurfaceCombinationsQuirk? =
         DeviceQuirks[ExtraSupportedSurfaceCombinationsQuirk::class.java]
 
     /** Retrieves the extra surface combinations which can be supported on the device. */
-    operator fun get(cameraId: String): List<SurfaceCombination> {
+    public operator fun get(cameraId: String): List<SurfaceCombination> {
         return quirk?.getExtraSupportedSurfaceCombinations(cameraId) ?: listOf()
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/FlashAvailabilityChecker.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/FlashAvailabilityChecker.kt
index b8766ea..375dee9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/FlashAvailabilityChecker.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/FlashAvailabilityChecker.kt
@@ -35,7 +35,7 @@
  *   checking.
  * @see FlashAvailabilityBufferUnderflowQuirk
  */
-fun CameraProperties.isFlashAvailable(allowRethrowOnError: Boolean = false): Boolean {
+public fun CameraProperties.isFlashAvailable(allowRethrowOnError: Boolean = false): Boolean {
     val flashAvailable =
         try {
             metadata[CameraCharacteristics.FLASH_INFO_AVAILABLE]
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ImageCapturePixelHDRPlus.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ImageCapturePixelHDRPlus.kt
index 00f947a..b6d95c4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ImageCapturePixelHDRPlus.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ImageCapturePixelHDRPlus.kt
@@ -33,7 +33,7 @@
  * @see ImageCapturePixelHDRPlusQuirk
  */
 @SuppressLint("NewApi")
-fun Camera2ImplConfig.Builder.toggleHDRPlus(imageCaptureConfig: ImageCaptureConfig) {
+public fun Camera2ImplConfig.Builder.toggleHDRPlus(imageCaptureConfig: ImageCaptureConfig) {
 
     DeviceQuirks[ImageCapturePixelHDRPlusQuirk::class.java] ?: return
     if (!imageCaptureConfig.hasCaptureMode()) return
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/InactiveSurfaceCloser.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/InactiveSurfaceCloser.kt
index 84b8458..fde838c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/InactiveSurfaceCloser.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/InactiveSurfaceCloser.kt
@@ -32,19 +32,25 @@
  * This workaround will be enabled when one of the [ConfigureSurfaceToSecondarySessionFailQuirk],
  * [PreviewOrientationIncorrectQuirk], [TextureViewIsClosedQuirk] is loaded.
  */
-interface InactiveSurfaceCloser {
+public interface InactiveSurfaceCloser {
 
-    fun configure(streamId: StreamId, deferrableSurface: DeferrableSurface, graph: CameraGraph)
+    public fun configure(
+        streamId: StreamId,
+        deferrableSurface: DeferrableSurface,
+        graph: CameraGraph
+    )
 
-    fun onSurfaceInactive(deferrableSurface: DeferrableSurface)
+    public fun onSurfaceInactive(deferrableSurface: DeferrableSurface)
 
-    fun closeAll()
+    public fun closeAll()
 
     @Module
-    abstract class Bindings {
-        companion object {
+    public abstract class Bindings {
+        public companion object {
             @Provides
-            fun provideInactiveSurfaceCloser(cameraQuirks: CameraQuirks): InactiveSurfaceCloser {
+            public fun provideInactiveSurfaceCloser(
+                cameraQuirks: CameraQuirks
+            ): InactiveSurfaceCloser {
                 val enabled =
                     cameraQuirks.quirks.run {
                         contains(ConfigureSurfaceToSecondarySessionFailQuirk::class.java) ||
@@ -58,7 +64,7 @@
     }
 }
 
-class InactiveSurfaceCloserImpl : InactiveSurfaceCloser {
+public class InactiveSurfaceCloserImpl : InactiveSurfaceCloser {
     private val lock = Any()
     private val configuredOutputs = mutableListOf<ConfiguredOutput>()
 
@@ -83,17 +89,17 @@
         }
     }
 
-    data class ConfiguredOutput(
+    public data class ConfiguredOutput(
         val streamId: StreamId,
         val deferrableSurface: DeferrableSurface,
         val graph: CameraGraph
     ) {
-        fun close() {
+        public fun close() {
             graph.setSurface(streamId, null)
             deferrableSurface.close()
         }
 
-        fun contains(deferrableSurface: DeferrableSurface): Boolean {
+        public fun contains(deferrableSurface: DeferrableSurface): Boolean {
             return this.deferrableSurface == deferrableSurface
         }
     }
@@ -106,7 +112,7 @@
         }
 }
 
-object NoOpInactiveSurfaceCloser : InactiveSurfaceCloser {
+public object NoOpInactiveSurfaceCloser : InactiveSurfaceCloser {
     override fun configure(
         streamId: StreamId,
         deferrableSurface: DeferrableSurface,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MaxPreviewSize.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MaxPreviewSize.kt
index c185f765..f975418 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MaxPreviewSize.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MaxPreviewSize.kt
@@ -22,7 +22,7 @@
 import androidx.camera.core.impl.SurfaceConfig
 
 /** Helper class that overrides the maximum preview size used in surface combination check. */
-class MaxPreviewSize
+public class MaxPreviewSize
 constructor(
     private val extraCroppingQuirk: ExtraCroppingQuirk? =
         DeviceQuirks[ExtraCroppingQuirk::class.java]
@@ -35,7 +35,7 @@
      * select resolution has been manually tested on the device. Otherwise, return the default max
      * resolution.
      */
-    fun getMaxPreviewResolution(defaultMaxPreviewResolution: Size): Size {
+    public fun getMaxPreviewResolution(defaultMaxPreviewResolution: Size): Size {
         if (extraCroppingQuirk == null) {
             return defaultMaxPreviewResolution
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MeteringRegionCorrection.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MeteringRegionCorrection.kt
index 3c08ca5..a1c591c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MeteringRegionCorrection.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/MeteringRegionCorrection.kt
@@ -24,17 +24,17 @@
 import dagger.Module
 import dagger.Provides
 
-interface MeteringRegionCorrection {
-    fun getCorrectedPoint(
+public interface MeteringRegionCorrection {
+    public fun getCorrectedPoint(
         meteringPoint: MeteringPoint,
         @FocusMeteringAction.MeteringMode meteringMode: Int,
     ): PointF
 
     @Module
-    abstract class Bindings {
-        companion object {
+    public abstract class Bindings {
+        public companion object {
             @Provides
-            fun provideMeteringRegionCorrection(
+            public fun provideMeteringRegionCorrection(
                 cameraQuirks: CameraQuirks
             ): MeteringRegionCorrection {
                 return if (cameraQuirks.quirks.contains(AfRegionFlipHorizontallyQuirk::class.java))
@@ -45,19 +45,19 @@
     }
 }
 
-object MeteringRegionQuirkCorrection : MeteringRegionCorrection {
+public object MeteringRegionQuirkCorrection : MeteringRegionCorrection {
     /** Return corrected normalized point by given MeteringPoint, MeteringMode and Quirks. */
     override fun getCorrectedPoint(
         meteringPoint: MeteringPoint,
         @FocusMeteringAction.MeteringMode meteringMode: Int,
-    ) =
+    ): PointF =
         when (meteringMode) {
             FocusMeteringAction.FLAG_AF -> PointF(1f - meteringPoint.x, meteringPoint.y)
             else -> PointF(meteringPoint.x, meteringPoint.y)
         }
 }
 
-object NoOpMeteringRegionCorrection : MeteringRegionCorrection {
-    override fun getCorrectedPoint(meteringPoint: MeteringPoint, meteringMode: Int) =
+public object NoOpMeteringRegionCorrection : MeteringRegionCorrection {
+    override fun getCorrectedPoint(meteringPoint: MeteringPoint, meteringMode: Int): PointF =
         PointF(meteringPoint.x, meteringPoint.y)
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/OutputSizesCorrector.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/OutputSizesCorrector.kt
index 22b145c..5d1572d 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/OutputSizesCorrector.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/OutputSizesCorrector.kt
@@ -32,7 +32,7 @@
  * 2. ExcludedSupportedSizesContainer
  */
 @CameraScope
-class OutputSizesCorrector
+public class OutputSizesCorrector
 @Inject
 constructor(
     private val cameraMetadata: CameraMetadata?,
@@ -45,7 +45,7 @@
         DeviceQuirks[ExtraSupportedOutputSizeQuirk::class.java]
 
     /** Applies the output sizes related quirks onto the input sizes array. */
-    fun applyQuirks(sizes: Array<Size>, format: Int): Array<Size> {
+    public fun applyQuirks(sizes: Array<Size>, format: Int): Array<Size> {
         val sizeList = sizes.toMutableList()
         addExtraSupportedOutputSizesByFormat(sizeList, format)
         excludeProblematicOutputSizesByFormat(sizeList, format)
@@ -56,7 +56,7 @@
     }
 
     /** Applies the output sizes related quirks onto the input sizes array. */
-    fun <T> applyQuirks(sizes: Array<Size>, klass: Class<T>): Array<Size> {
+    public fun <T> applyQuirks(sizes: Array<Size>, klass: Class<T>): Array<Size> {
         val sizeList = sizes.toMutableList()
         addExtraSupportedOutputSizesByClass(sizeList, klass)
         excludeProblematicOutputSizesByClass(sizeList, klass)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/PreviewPixelHDRnet.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/PreviewPixelHDRnet.kt
index 346fbf3..c539079 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/PreviewPixelHDRnet.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/PreviewPixelHDRnet.kt
@@ -31,7 +31,7 @@
  *
  * @see PreviewPixelHDRnetQuirk
  */
-fun SessionConfig.Builder.setupHDRnet(resolution: Size) {
+public fun SessionConfig.Builder.setupHDRnet(resolution: Size) {
     DeviceQuirks[PreviewPixelHDRnetQuirk::class.java] ?: return
 
     if (isAspectRatioMatch(resolution, ASPECT_RATIO_16_9)) return
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrector.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrector.kt
index 87c253f9..b1bb6a7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrector.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/ResolutionCorrector.kt
@@ -25,7 +25,7 @@
  * Helper class that overrides user configured resolution with resolution selected based on device
  * quirks.
  */
-class ResolutionCorrector {
+public class ResolutionCorrector {
     private val extraCroppingQuirk = DeviceQuirks[ExtraCroppingQuirk::class.java]
 
     /**
@@ -38,7 +38,7 @@
      * @param configType the config type based on which the supported resolution is calculated.
      * @param supportedResolutions a ordered list of resolutions calculated by CameraX.
      */
-    fun insertOrPrioritize(
+    public fun insertOrPrioritize(
         configType: ConfigType,
         supportedResolutions: List<Size>,
     ): List<Size> {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/StillCaptureFlow.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/StillCaptureFlow.kt
index 9e0d0a9..868cb6a 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/StillCaptureFlow.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/StillCaptureFlow.kt
@@ -23,7 +23,7 @@
 import androidx.camera.camera2.pipe.integration.compat.quirk.StillCaptureFlashStopRepeatingQuirk
 
 /** Returns whether or not repeating should be stopped before submitting capture request. */
-fun List<Request>.shouldStopRepeatingBeforeCapture(): Boolean {
+public fun List<Request>.shouldStopRepeatingBeforeCapture(): Boolean {
     DeviceQuirks[StillCaptureFlashStopRepeatingQuirk::class.java] ?: return false
 
     var isStillCapture = false
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/SupportedRepeatingSurfaceSize.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/SupportedRepeatingSurfaceSize.kt
index 182ff03..862def6 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/SupportedRepeatingSurfaceSize.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/SupportedRepeatingSurfaceSize.kt
@@ -24,7 +24,7 @@
 private val MINI_PREVIEW_SIZE_HUAWEI_MATE_9 = Size(320, 240)
 private val SIZE_COMPARATOR: Comparator<Size> = CompareSizesByArea()
 
-fun Array<Size>.getSupportedRepeatingSurfaceSizes(): Array<Size> {
+public fun Array<Size>.getSupportedRepeatingSurfaceSizes(): Array<Size> {
     DeviceQuirks[RepeatingStreamConstraintForVideoRecordingQuirk::class.java] ?: return this
 
     val supportedSizes = mutableListOf<Size>()
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TargetAspectRatio.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TargetAspectRatio.kt
index 53f67cd..fa5b087 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TargetAspectRatio.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TargetAspectRatio.kt
@@ -31,10 +31,10 @@
  * @see Nexus4AndroidLTargetAspectRatioQuirk
  * @see AspectRatioLegacyApi21Quirk
  */
-class TargetAspectRatio {
+public class TargetAspectRatio {
     /** Gets corrected target aspect ratio based on device and camera quirks. */
     @Ratio
-    operator fun get(
+    public operator fun get(
         cameraMetadata: CameraMetadata,
         streamConfigurationMapCompat: StreamConfigurationMapCompat
     ): Int {
@@ -54,19 +54,19 @@
     @IntDef(RATIO_4_3, RATIO_16_9, RATIO_MAX_JPEG, RATIO_ORIGINAL)
     @Retention(AnnotationRetention.SOURCE)
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    annotation class Ratio
+    public annotation class Ratio
 
-    companion object {
+    public companion object {
         /** 4:3 standard aspect ratio. */
-        const val RATIO_4_3 = 0
+        public const val RATIO_4_3: Int = 0
 
         /** 16:9 standard aspect ratio. */
-        const val RATIO_16_9 = 1
+        public const val RATIO_16_9: Int = 1
 
         /** The same aspect ratio as the maximum JPEG resolution. */
-        const val RATIO_MAX_JPEG = 2
+        public const val RATIO_MAX_JPEG: Int = 2
 
         /** No correction is needed. */
-        const val RATIO_ORIGINAL = 3
+        public const val RATIO_ORIGINAL: Int = 3
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverride.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverride.kt
index 84e6461..0fa2563 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverride.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/TemplateParamsOverride.kt
@@ -43,15 +43,17 @@
  * @see TemporalNoiseQuirk
  * @see ImageCaptureFailedForVideoSnapshotQuirk
  */
-interface TemplateParamsOverride {
+public interface TemplateParamsOverride {
     /** Returns capture parameters used to override the default parameters of the input template. */
-    fun getOverrideParams(template: RequestTemplate?): Map<CaptureRequest.Key<*>, Any>
+    public fun getOverrideParams(template: RequestTemplate?): Map<CaptureRequest.Key<*>, Any>
 
     @Module
-    abstract class Bindings {
-        companion object {
+    public abstract class Bindings {
+        public companion object {
             @Provides
-            fun provideTemplateParamsOverride(cameraQuirks: CameraQuirks): TemplateParamsOverride {
+            public fun provideTemplateParamsOverride(
+                cameraQuirks: CameraQuirks
+            ): TemplateParamsOverride {
                 val quirks = cameraQuirks.quirks
                 return if (
                     workaroundByCaptureIntentPreview(quirks) ||
@@ -64,7 +66,7 @@
     }
 }
 
-class TemplateParamsQuirkOverride(quirks: Quirks) : TemplateParamsOverride {
+public class TemplateParamsQuirkOverride(quirks: Quirks) : TemplateParamsOverride {
     private val workaroundByCaptureIntentPreview = workaroundByCaptureIntentPreview(quirks)
     private val workaroundByCaptureIntentStillCapture =
         quirks.contains(ImageCaptureFailedForVideoSnapshotQuirk::class.java)
@@ -80,7 +82,7 @@
     }
 }
 
-object NoOpTemplateParamsOverride : TemplateParamsOverride {
+public object NoOpTemplateParamsOverride : TemplateParamsOverride {
     override fun getOverrideParams(template: RequestTemplate?): Map<CaptureRequest.Key<*>, Any> {
         return emptyMap()
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseFlashModeTorchFor3aUpdate.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseFlashModeTorchFor3aUpdate.kt
index 7c7258b..5288b0c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseFlashModeTorchFor3aUpdate.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseFlashModeTorchFor3aUpdate.kt
@@ -27,14 +27,14 @@
  *
  * @see TorchFlashRequiredFor3aUpdateQuirk
  */
-interface UseFlashModeTorchFor3aUpdate {
-    fun shouldUseFlashModeTorch(): Boolean
+public interface UseFlashModeTorchFor3aUpdate {
+    public fun shouldUseFlashModeTorch(): Boolean
 
     @Module
-    abstract class Bindings {
-        companion object {
+    public abstract class Bindings {
+        public companion object {
             @Provides
-            fun provideUseFlashModeTorchFor3aUpdate(
+            public fun provideUseFlashModeTorchFor3aUpdate(
                 cameraQuirks: CameraQuirks
             ): UseFlashModeTorchFor3aUpdate =
                 if (cameraQuirks.quirks.contains(TorchFlashRequiredFor3aUpdateQuirk::class.java))
@@ -44,11 +44,11 @@
     }
 }
 
-object UseFlashModeTorchFor3aUpdateImpl : UseFlashModeTorchFor3aUpdate {
+public object UseFlashModeTorchFor3aUpdateImpl : UseFlashModeTorchFor3aUpdate {
     /** Returns true for torch should be used as flash. */
-    override fun shouldUseFlashModeTorch() = true
+    override fun shouldUseFlashModeTorch(): Boolean = true
 }
 
-object NotUseFlashModeTorchFor3aUpdate : UseFlashModeTorchFor3aUpdate {
-    override fun shouldUseFlashModeTorch() = false
+public object NotUseFlashModeTorchFor3aUpdate : UseFlashModeTorchFor3aUpdate {
+    override fun shouldUseFlashModeTorch(): Boolean = false
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlash.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlash.kt
index d5ba42c..fa092c0 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlash.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/workaround/UseTorchAsFlash.kt
@@ -26,14 +26,14 @@
  *
  * @see UseTorchAsFlashQuirk
  */
-interface UseTorchAsFlash {
-    fun shouldUseTorchAsFlash(): Boolean
+public interface UseTorchAsFlash {
+    public fun shouldUseTorchAsFlash(): Boolean
 
     @Module
-    abstract class Bindings {
-        companion object {
+    public abstract class Bindings {
+        public companion object {
             @Provides
-            fun provideUseTorchAsFlash(cameraQuirks: CameraQuirks): UseTorchAsFlash =
+            public fun provideUseTorchAsFlash(cameraQuirks: CameraQuirks): UseTorchAsFlash =
                 if (cameraQuirks.quirks.contains(UseTorchAsFlashQuirk::class.java))
                     UseTorchAsFlashImpl
                 else NotUseTorchAsFlash
@@ -41,11 +41,11 @@
     }
 }
 
-object UseTorchAsFlashImpl : UseTorchAsFlash {
+public object UseTorchAsFlashImpl : UseTorchAsFlash {
     /** Returns true for torch should be used as flash. */
-    override fun shouldUseTorchAsFlash() = true
+    override fun shouldUseTorchAsFlash(): Boolean = true
 }
 
-object NotUseTorchAsFlash : UseTorchAsFlash {
-    override fun shouldUseTorchAsFlash() = false
+public object NotUseTorchAsFlash : UseTorchAsFlash {
+    override fun shouldUseTorchAsFlash(): Boolean = false
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraAppConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraAppConfig.kt
index c484422..c6ebdf0 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraAppConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraAppConfig.kt
@@ -29,10 +29,10 @@
 
 /** Dependency bindings for adapting a [CameraFactory] instance to [CameraPipe] */
 @Module(subcomponents = [CameraComponent::class])
-abstract class CameraAppModule {
-    companion object {
+public abstract class CameraAppModule {
+    public companion object {
         @Provides
-        fun provideCameraDevices(cameraPipe: CameraPipe): CameraDevices {
+        public fun provideCameraDevices(cameraPipe: CameraPipe): CameraDevices {
             return cameraPipe.cameras()
         }
     }
@@ -40,37 +40,37 @@
 
 /** Configuration properties that are shared across this app process */
 @Module
-class CameraAppConfig(
+public class CameraAppConfig(
     private val context: Context,
     private val cameraThreadConfig: CameraThreadConfig,
     private val cameraPipe: CameraPipe,
     private val camera2InteropCallbacks: CameraInteropStateCallbackRepository
 ) {
-    @Provides fun provideContext(): Context = context
+    @Provides public fun provideContext(): Context = context
 
-    @Provides fun provideCameraThreadConfig(): CameraThreadConfig = cameraThreadConfig
+    @Provides public fun provideCameraThreadConfig(): CameraThreadConfig = cameraThreadConfig
 
-    @Provides fun provideCameraPipe(): CameraPipe = cameraPipe
+    @Provides public fun provideCameraPipe(): CameraPipe = cameraPipe
 
     @Provides
-    fun provideCamera2InteropCallbacks(): CameraInteropStateCallbackRepository =
+    public fun provideCamera2InteropCallbacks(): CameraInteropStateCallbackRepository =
         camera2InteropCallbacks
 }
 
 /** Dagger component for Application (Process) scoped dependencies. */
 @Singleton
 @Component(modules = [CameraAppModule::class, CameraAppConfig::class])
-interface CameraAppComponent {
-    fun cameraBuilder(): CameraComponent.Builder
+public interface CameraAppComponent {
+    public fun cameraBuilder(): CameraComponent.Builder
 
-    fun getCameraPipe(): CameraPipe
+    public fun getCameraPipe(): CameraPipe
 
-    fun getCameraDevices(): CameraDevices
+    public fun getCameraDevices(): CameraDevices
 
     @Component.Builder
-    interface Builder {
-        fun config(config: CameraAppConfig): Builder
+    public interface Builder {
+        public fun config(config: CameraAppConfig): Builder
 
-        fun build(): CameraAppComponent
+        public fun build(): CameraAppComponent
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraConfig.kt
index 4438c4d..8155184 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/CameraConfig.kt
@@ -69,7 +69,7 @@
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.asCoroutineDispatcher
 
-@Scope annotation class CameraScope
+@Scope public annotation class CameraScope
 
 /** Dependency bindings for adapting an individual [CameraInternal] instance to [CameraPipe] */
 @OptIn(ExperimentalCamera2Interop::class)
@@ -89,12 +89,12 @@
         ],
     subcomponents = [UseCaseCameraComponent::class]
 )
-abstract class CameraModule {
-    companion object {
+public abstract class CameraModule {
+    public companion object {
 
         @CameraScope
         @Provides
-        fun provideUseCaseThreads(
+        public fun provideUseCaseThreads(
             cameraConfig: CameraConfig,
             cameraThreadConfig: CameraThreadConfig
         ): UseCaseThreads {
@@ -114,16 +114,19 @@
 
         @CameraScope
         @Provides
-        fun provideCamera2CameraControl(
+        public fun provideCamera2CameraControl(
             compat: Camera2CameraControlCompat,
             threads: UseCaseThreads,
             @VisibleForTesting requestListener: ComboRequestListener,
-        ) = Camera2CameraControl.create(compat, threads, requestListener)
+        ): Camera2CameraControl = Camera2CameraControl.create(compat, threads, requestListener)
 
         @CameraScope
         @Nullable
         @Provides
-        fun provideCameraMetadata(cameraPipe: CameraPipe, config: CameraConfig): CameraMetadata? {
+        public fun provideCameraMetadata(
+            cameraPipe: CameraPipe,
+            config: CameraConfig
+        ): CameraMetadata? {
             try {
                 return cameraPipe.cameras().awaitCameraMetadata(config.cameraId)
             } catch (exception: DoNotDisturbException) {
@@ -135,12 +138,12 @@
         @CameraScope
         @Provides
         @Named("CameraId")
-        fun provideCameraIdString(config: CameraConfig): String = config.cameraId.value
+        public fun provideCameraIdString(config: CameraConfig): String = config.cameraId.value
 
         @CameraScope
         @Nullable
         @Provides
-        fun provideStreamConfigurationMap(
+        public fun provideStreamConfigurationMap(
             cameraMetadata: CameraMetadata?
         ): StreamConfigurationMap? {
             return cameraMetadata?.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
@@ -148,7 +151,7 @@
 
         @CameraScope
         @Provides
-        fun provideCameraGraphFlags(cameraQuirks: CameraQuirks): CameraGraph.Flags {
+        public fun provideCameraGraphFlags(cameraQuirks: CameraQuirks): CameraGraph.Flags {
             if (cameraQuirks.quirks.contains(CaptureSessionStuckQuirk::class.java)) {
                 Log.debug { "CameraPipe should be enabling CaptureSessionStuckQuirk" }
             }
@@ -166,11 +169,12 @@
         @CameraScope
         @Provides
         @Named("cameraQuirksValues")
-        fun provideCameraQuirksValues(cameraQuirks: CameraQuirks): Quirks = cameraQuirks.quirks
+        public fun provideCameraQuirksValues(cameraQuirks: CameraQuirks): Quirks =
+            cameraQuirks.quirks
 
         @CameraScope
         @Provides
-        fun provideZslControl(cameraProperties: CameraProperties): ZslControl {
+        public fun provideZslControl(cameraProperties: CameraProperties): ZslControl {
             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                 return ZslControlImpl(cameraProperties)
             } else {
@@ -179,20 +183,24 @@
         }
     }
 
-    @Binds abstract fun bindCameraProperties(impl: CameraPipeCameraProperties): CameraProperties
+    @Binds
+    public abstract fun bindCameraProperties(impl: CameraPipeCameraProperties): CameraProperties
 
-    @Binds abstract fun bindCameraInternal(adapter: CameraInternalAdapter): CameraInternal
-
-    @Binds abstract fun bindCameraInfoInternal(adapter: CameraInfoAdapter): CameraInfoInternal
+    @Binds public abstract fun bindCameraInternal(adapter: CameraInternalAdapter): CameraInternal
 
     @Binds
-    abstract fun bindCameraControlInternal(adapter: CameraControlAdapter): CameraControlInternal
+    public abstract fun bindCameraInfoInternal(adapter: CameraInfoAdapter): CameraInfoInternal
+
+    @Binds
+    public abstract fun bindCameraControlInternal(
+        adapter: CameraControlAdapter
+    ): CameraControlInternal
 }
 
 /** Configuration properties used when creating a [CameraInternal] instance. */
 @Module
-class CameraConfig(val cameraId: CameraId) {
-    @Provides fun provideCameraConfig(): CameraConfig = this
+public class CameraConfig(public val cameraId: CameraId) {
+    @Provides public fun provideCameraConfig(): CameraConfig = this
 }
 
 /** Dagger subcomponent for a single [CameraInternal] instance. */
@@ -205,13 +213,13 @@
             CameraCompatModule::class,
         ]
 )
-interface CameraComponent {
+public interface CameraComponent {
     @Subcomponent.Builder
-    interface Builder {
-        fun config(config: CameraConfig): Builder
+    public interface Builder {
+        public fun config(config: CameraConfig): Builder
 
-        fun build(): CameraComponent
+        public fun build(): CameraComponent
     }
 
-    fun getCameraInternal(): CameraInternal
+    public fun getCameraInternal(): CameraInternal
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
index 57a7382..609fce5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/config/UseCaseCameraConfig.kt
@@ -39,7 +39,7 @@
 import java.util.concurrent.CancellationException
 import javax.inject.Scope
 
-@Scope annotation class UseCaseCameraScope
+@Scope public annotation class UseCaseCameraScope
 
 /** Dependency bindings for building a [UseCaseCamera] */
 @Module(
@@ -49,13 +49,13 @@
             UseCaseCameraRequestControlImpl.Bindings::class,
         ]
 )
-abstract class UseCaseCameraModule {
+public abstract class UseCaseCameraModule {
     // Used for dagger provider methods that are static.
-    companion object {
+    public companion object {
 
         @UseCaseCameraScope
         @Provides
-        fun provideCapturePipeline(
+        public fun provideCapturePipeline(
             capturePipelineImpl: CapturePipelineImpl,
             capturePipelineTorchCorrection: CapturePipelineTorchCorrection
         ): CapturePipeline {
@@ -70,7 +70,7 @@
 
 /** Dagger module for binding the [UseCase]'s to the [UseCaseCamera]. */
 @Module
-class UseCaseCameraConfig(
+public class UseCaseCameraConfig(
     private val useCases: List<UseCase>,
     private val sessionConfigAdapter: SessionConfigAdapter,
     private val cameraStateAdapter: CameraStateAdapter,
@@ -80,19 +80,19 @@
 ) {
     @UseCaseCameraScope
     @Provides
-    fun provideUseCaseList(): java.util.ArrayList<UseCase> {
+    public fun provideUseCaseList(): java.util.ArrayList<UseCase> {
         return java.util.ArrayList(useCases)
     }
 
     @UseCaseCameraScope
     @Provides
-    fun provideSessionConfigAdapter(): SessionConfigAdapter {
+    public fun provideSessionConfigAdapter(): SessionConfigAdapter {
         return sessionConfigAdapter
     }
 
     @UseCaseCameraScope
     @Provides
-    fun provideSessionProcessorManager(): SessionProcessorManager? {
+    public fun provideSessionProcessorManager(): SessionProcessorManager? {
         return sessionProcessorManager
     }
 
@@ -102,7 +102,7 @@
      */
     @UseCaseCameraScope
     @Provides
-    fun provideUseCaseGraphConfig(
+    public fun provideUseCaseGraphConfig(
         useCaseSurfaceManager: UseCaseSurfaceManager,
         cameraInteropStateCallbackRepository: CameraInteropStateCallbackRepository
     ): UseCaseGraphConfig {
@@ -149,12 +149,14 @@
     }
 }
 
-data class UseCaseGraphConfig(
+public data class UseCaseGraphConfig(
     val graph: CameraGraph,
     val surfaceToStreamMap: Map<DeferrableSurface, StreamId>,
     val cameraStateAdapter: CameraStateAdapter,
 ) {
-    fun getStreamIdsFromSurfaces(deferrableSurfaces: Collection<DeferrableSurface>): Set<StreamId> {
+    public fun getStreamIdsFromSurfaces(
+        deferrableSurfaces: Collection<DeferrableSurface>
+    ): Set<StreamId> {
         val streamIds = mutableSetOf<StreamId>()
         deferrableSurfaces.forEach {
             surfaceToStreamMap[it]?.let { streamId -> streamIds.add(streamId) }
@@ -166,15 +168,15 @@
 /** Dagger subcomponent for a single [UseCaseCamera] instance. */
 @UseCaseCameraScope
 @Subcomponent(modules = [UseCaseCameraModule::class, UseCaseCameraConfig::class])
-interface UseCaseCameraComponent {
-    fun getUseCaseCamera(): UseCaseCamera
+public interface UseCaseCameraComponent {
+    public fun getUseCaseCamera(): UseCaseCamera
 
-    fun getUseCaseGraphConfig(): UseCaseGraphConfig
+    public fun getUseCaseGraphConfig(): UseCaseGraphConfig
 
     @Subcomponent.Builder
-    interface Builder {
-        fun config(config: UseCaseCameraConfig): Builder
+    public interface Builder {
+        public fun config(config: UseCaseCameraConfig): Builder
 
-        fun build(): UseCaseCameraComponent
+        public fun build(): UseCaseCameraComponent
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Camera2ImplConfig.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Camera2ImplConfig.kt
index 88f8d4b..05ea0f7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Camera2ImplConfig.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Camera2ImplConfig.kt
@@ -70,11 +70,11 @@
  * @property config The config that potentially contains Camera2 options.
  */
 @OptIn(ExperimentalCamera2Interop::class)
-class Camera2ImplConfig(config: Config) : CaptureRequestOptions(config) {
+public class Camera2ImplConfig(config: Config) : CaptureRequestOptions(config) {
 
     /** Returns all capture request options contained in this configuration. */
     @get:RestrictTo(RestrictTo.Scope.LIBRARY)
-    val captureRequestOptions: CaptureRequestOptions
+    public val captureRequestOptions: CaptureRequestOptions
         get() = from(config).build()
 
     /**
@@ -86,7 +86,7 @@
      * @return The stored value or `valueIfMissing` if the value does not exist in this
      *   configuration.
      */
-    fun getCaptureRequestTemplate(valueIfMissing: Int): Int {
+    public fun getCaptureRequestTemplate(valueIfMissing: Int): Int {
         return config.retrieveOption(TEMPLATE_TYPE_OPTION, valueIfMissing)!!
     }
 
@@ -100,7 +100,7 @@
      * @see [android.hardware.camera2.params.OutputConfiguration] to see how camera2 framework uses
      *   this.
      */
-    fun getStreamUseCase(valueIfMissing: Long? = null): Long? {
+    public fun getStreamUseCase(valueIfMissing: Long? = null): Long? {
         return config.retrieveOption(STREAM_USE_CASE_OPTION, valueIfMissing)
     }
 
@@ -114,7 +114,7 @@
      * @see [android.hardware.camera2.params.OutputConfiguration] to see how camera2 framework uses
      *   this.
      */
-    fun getStreamUseHint(valueIfMissing: Long? = null): Long? {
+    public fun getStreamUseHint(valueIfMissing: Long? = null): Long? {
         return config.retrieveOption(STREAM_USE_HINT_OPTION, valueIfMissing)
     }
 
@@ -126,7 +126,7 @@
      * @return The stored value or `valueIfMissing` if the value does not exist in this
      *   configuration.
      */
-    fun getDeviceStateCallback(
+    public fun getDeviceStateCallback(
         valueIfMissing: CameraDevice.StateCallback? = null
     ): CameraDevice.StateCallback? {
         return config.retrieveOption(DEVICE_STATE_CALLBACK_OPTION, valueIfMissing)
@@ -140,7 +140,7 @@
      * @return The stored value or `valueIfMissing` if the value does not exist in this
      *   configuration.
      */
-    fun getSessionStateCallback(
+    public fun getSessionStateCallback(
         valueIfMissing: CameraCaptureSession.StateCallback? = null
     ): CameraCaptureSession.StateCallback? {
         return config.retrieveOption(SESSION_STATE_CALLBACK_OPTION, valueIfMissing)
@@ -154,7 +154,9 @@
      * @return The stored value or `valueIfMissing` if the value does not exist in this
      *   configuration.
      */
-    fun getSessionCaptureCallback(valueIfMissing: CaptureCallback? = null): CaptureCallback? {
+    public fun getSessionCaptureCallback(
+        valueIfMissing: CaptureCallback? = null
+    ): CaptureCallback? {
         return config.retrieveOption(SESSION_CAPTURE_CALLBACK_OPTION, valueIfMissing)
     }
 
@@ -166,7 +168,7 @@
      * @return The stored value or `valueIfMissing` if the value does not exist in this
      *   configuration.
      */
-    fun getCaptureRequestTag(valueIfMissing: Any? = null): Any? {
+    public fun getCaptureRequestTag(valueIfMissing: Any? = null): Any? {
         return config.retrieveOption(CAPTURE_REQUEST_TAG_OPTION, valueIfMissing)
     }
 
@@ -178,7 +180,7 @@
      * @return The stored value or `valueIfMissing` if the value does not exist in this
      *   configuration.
      */
-    fun getPhysicalCameraId(valueIfMissing: String? = null): String? {
+    public fun getPhysicalCameraId(valueIfMissing: String? = null): String? {
         return config.retrieveOption(SESSION_PHYSICAL_CAMERA_ID_OPTION, valueIfMissing)
     }
 
@@ -189,7 +191,7 @@
      * [androidx.camera.camera2.pipe.integration.interop.Camera2Interop.Extender] to add Camera2
      * options on existing other [ExtendableBuilder].
      */
-    class Builder : ExtendableBuilder<Camera2ImplConfig?> {
+    public class Builder : ExtendableBuilder<Camera2ImplConfig?> {
 
         private val mutableOptionsBundle = MutableOptionsBundle.create()
 
@@ -198,7 +200,7 @@
         }
 
         /** Inserts new capture request option with specific [CaptureRequest.Key] setting. */
-        fun <ValueT> setCaptureRequestOption(
+        public fun <ValueT> setCaptureRequestOption(
             key: CaptureRequest.Key<ValueT>,
             value: ValueT
         ): Builder {
@@ -211,7 +213,7 @@
          * Inserts new capture request option with specific [CaptureRequest.Key] setting and
          * [Config.OptionPriority].
          */
-        fun <ValueT> setCaptureRequestOptionWithPriority(
+        public fun <ValueT> setCaptureRequestOptionWithPriority(
             key: CaptureRequest.Key<ValueT>,
             value: ValueT,
             priority: Config.OptionPriority
@@ -225,7 +227,7 @@
          * Inserts all capture request options in the map to the setting with
          * [Config.OptionPriority].
          */
-        fun addAllCaptureRequestOptionsWithPriority(
+        public fun addAllCaptureRequestOptionsWithPriority(
             values: Map<CaptureRequest.Key<*>, Any>,
             priority: Config.OptionPriority
         ): Builder {
@@ -237,7 +239,7 @@
         }
 
         /** Inserts options from other [Config] objects. */
-        fun insertAllOptions(config: Config): Builder {
+        public fun insertAllOptions(config: Config): Builder {
             for (option in config.listOptions()) {
                 // Options/values and priority are being copied directly
                 @Suppress("UNCHECKED_CAST") val objectOpt = option as Config.Option<Any>
@@ -271,7 +273,7 @@
 }
 
 /** Convert the Config to the CaptureRequest key-value map. */
-fun Config.toParameters(): Map<CaptureRequest.Key<*>, Any> {
+public fun Config.toParameters(): Map<CaptureRequest.Key<*>, Any> {
     val parameters = mutableMapOf<CaptureRequest.Key<*>, Any>()
     for (configOption in listOptions()) {
         val requestKey = configOption.token as? CaptureRequest.Key<*> ?: continue
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
index f5bd53b..3e26f0e 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
@@ -46,12 +46,12 @@
 
 /** A map of [CameraCaptureCallback] that are invoked on each [Request]. */
 @CameraScope
-class CameraCallbackMap @Inject constructor() : Request.Listener {
+public class CameraCallbackMap @Inject constructor() : Request.Listener {
     private val callbackMap = mutableMapOf<CameraCaptureCallback, Executor>()
 
     @Volatile private var callbacks: Map<CameraCaptureCallback, Executor> = mapOf()
 
-    fun addCaptureCallback(callback: CameraCaptureCallback, executor: Executor) {
+    public fun addCaptureCallback(callback: CameraCaptureCallback, executor: Executor) {
         check(!callbacks.contains(callback)) { "$callback was already registered!" }
 
         synchronized(callbackMap) {
@@ -60,7 +60,7 @@
         }
     }
 
-    fun removeCaptureCallback(callback: CameraCaptureCallback) {
+    public fun removeCaptureCallback(callback: CameraCaptureCallback) {
         synchronized(callbackMap) {
             callbackMap.remove(callback)
             callbacks = callbackMap.toMap()
@@ -290,8 +290,8 @@
         }
     }
 
-    companion object {
-        fun createFor(
+    public companion object {
+        public fun createFor(
             callbacks: Collection<CameraCaptureCallback>,
             executor: Executor
         ): CameraCallbackMap {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraInteropStateCallbackRepository.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraInteropStateCallbackRepository.kt
index 9464cca..124b076 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraInteropStateCallbackRepository.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraInteropStateCallbackRepository.kt
@@ -32,7 +32,7 @@
  * graph is created, CameraX updates these internal callbacks with Camera Interop callbacks so that
  * they may be triggered in camera-pipe.
  */
-class CameraInteropStateCallbackRepository {
+public class CameraInteropStateCallbackRepository {
 
     private val _deviceStateCallback = CameraInteropDeviceStateCallback()
     private val _sessionStateCallback = CameraInteropSessionStateCallback()
@@ -45,18 +45,18 @@
      *
      * @param sessionConfig the final merged sessionConfig used to create camera graph
      */
-    fun updateCallbacks(sessionConfig: SessionConfig) {
+    public fun updateCallbacks(sessionConfig: SessionConfig) {
         _deviceStateCallback.updateCallbacks(sessionConfig)
         _sessionStateCallback.updateCallbacks(sessionConfig)
     }
 
-    val deviceStateCallback
+    public val deviceStateCallback: CameraInteropDeviceStateCallback
         get() = _deviceStateCallback
 
-    val sessionStateCallback
+    public val sessionStateCallback: CameraInteropSessionStateCallback
         get() = _sessionStateCallback
 
-    class CameraInteropDeviceStateCallback : CameraDevice.StateCallback() {
+    public class CameraInteropDeviceStateCallback : CameraDevice.StateCallback() {
 
         private var callbacks: AtomicRef<List<CameraDevice.StateCallback>> = atomic(listOf())
 
@@ -89,7 +89,7 @@
         }
     }
 
-    class CameraInteropSessionStateCallback() : CameraCaptureSession.StateCallback() {
+    public class CameraInteropSessionStateCallback : CameraCaptureSession.StateCallback() {
 
         private var callbacks: AtomicRef<List<CameraCaptureSession.StateCallback>> =
             atomic(listOf())
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraMetadataIntegration.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraMetadataIntegration.kt
index 7ee464c..49c4e93 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraMetadataIntegration.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraMetadataIntegration.kt
@@ -22,7 +22,7 @@
 import androidx.camera.camera2.pipe.CameraMetadata
 
 /** Contains the CameraX-specific logic for [CameraMetadata]. */
-val CameraMetadata.availableAfModes
+public val CameraMetadata.availableAfModes: List<Int>
     get() =
         getOrDefault(
                 CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES,
@@ -30,7 +30,7 @@
             )
             .asList()
 
-val CameraMetadata.availableAeModes
+public val CameraMetadata.availableAeModes: List<Int>
     get() =
         getOrDefault(
                 CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES,
@@ -38,7 +38,7 @@
             )
             .asList()
 
-val CameraMetadata.availableAwbModes
+public val CameraMetadata.availableAwbModes: List<Int>
     get() =
         getOrDefault(
                 CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES,
@@ -47,7 +47,7 @@
             .asList()
 
 /** If preferredMode not available, priority is CONTINUOUS_PICTURE > AUTO > OFF */
-fun CameraMetadata.getSupportedAfMode(preferredMode: Int) =
+public fun CameraMetadata.getSupportedAfMode(preferredMode: Int): Int =
     when {
         availableAfModes.contains(preferredMode) -> {
             preferredMode
@@ -64,7 +64,7 @@
     }
 
 /** If preferredMode not available, priority is AE_ON > AE_OFF */
-fun CameraMetadata.getSupportedAeMode(preferredMode: Int) =
+public fun CameraMetadata.getSupportedAeMode(preferredMode: Int): Int =
     when {
         availableAeModes.contains(preferredMode) -> {
             preferredMode
@@ -80,12 +80,12 @@
 private fun CameraMetadata.isAeModeSupported(aeMode: Int) = getSupportedAeMode(aeMode) == aeMode
 
 /** Returns whether [CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH] is supported. */
-fun CameraMetadata.isExternalFlashAeModeSupported() =
+public fun CameraMetadata.isExternalFlashAeModeSupported(): Boolean =
     Build.VERSION.SDK_INT >= 28 &&
         isAeModeSupported(CaptureRequest.CONTROL_AE_MODE_ON_EXTERNAL_FLASH)
 
 /** If preferredMode not available, priority is AWB_AUTO > AWB_OFF */
-fun CameraMetadata.getSupportedAwbMode(preferredMode: Int) =
+public fun CameraMetadata.getSupportedAwbMode(preferredMode: Int): Int =
     when {
         availableAwbModes.contains(preferredMode) -> {
             preferredMode
@@ -98,5 +98,5 @@
         }
     }
 
-fun <T> CameraMetadata?.getOrDefault(key: CameraCharacteristics.Key<T>, default: T) =
+public fun <T> CameraMetadata?.getOrDefault(key: CameraCharacteristics.Key<T>, default: T): T =
     this?.getOrDefault(key, default) ?: default
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraProperties.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraProperties.kt
index 49518fd..aa16148 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraProperties.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraProperties.kt
@@ -23,15 +23,15 @@
 import javax.inject.Inject
 
 /** Pre-computed camera properties */
-interface CameraProperties {
-    val cameraId: CameraId
-    val metadata: CameraMetadata
+public interface CameraProperties {
+    public val cameraId: CameraId
+    public val metadata: CameraMetadata
 
     // TODO: Consider exposing additional properties, such as quirks.
 }
 
 @CameraScope
-class CameraPipeCameraProperties
+public class CameraPipeCameraProperties
 @Inject
 constructor(
     private val cameraConfig: CameraConfig,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
index 8b64ff7..372696c 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
@@ -85,11 +85,11 @@
 private val CHECK_3A_WITH_FLASH_TIMEOUT_IN_NS = TimeUnit.SECONDS.toNanos(5)
 private val CHECK_3A_WITH_SCREEN_FLASH_TIMEOUT_IN_NS = TimeUnit.SECONDS.toNanos(2)
 
-interface CapturePipeline {
+public interface CapturePipeline {
 
-    var template: Int
+    public var template: Int
 
-    suspend fun submitStillCaptures(
+    public suspend fun submitStillCaptures(
         configs: List<CaptureConfig>,
         requestTemplate: RequestTemplate,
         sessionConfigOptions: Config,
@@ -101,7 +101,7 @@
 
 /** Implementations for the single capture. */
 @UseCaseCameraScope
-class CapturePipelineImpl
+public class CapturePipelineImpl
 @Inject
 constructor(
     private val configAdapter: CaptureConfigAdapter,
@@ -120,7 +120,7 @@
     // If there is no flash unit, skip the flash related task instead of failing the pipeline.
     private val hasFlashUnit = cameraProperties.isFlashAvailable()
 
-    override var template = CameraDevice.TEMPLATE_PREVIEW
+    override var template: Int = CameraDevice.TEMPLATE_PREVIEW
 
     override suspend fun submitStillCaptures(
         configs: List<CaptureConfig>,
@@ -342,7 +342,7 @@
      * @return The previous preferred AE mode in [State3AControl], null if not modified.
      */
     @VisibleForTesting
-    suspend fun invokeScreenFlashPreCaptureTasks(@CaptureMode captureMode: Int) {
+    public suspend fun invokeScreenFlashPreCaptureTasks(@CaptureMode captureMode: Int) {
         flashControl.startScreenFlashCaptureTasks()
 
         graph.acquireSession().use { session ->
@@ -361,7 +361,7 @@
     }
 
     @VisibleForTesting
-    suspend fun invokeScreenFlashPostCaptureTasks(@CaptureMode captureMode: Int) {
+    public suspend fun invokeScreenFlashPostCaptureTasks(@CaptureMode captureMode: Int) {
         flashControl.stopScreenFlashCaptureTasks()
 
         // Unlock 3A
@@ -639,13 +639,13 @@
  *   timeLimitNs is reached.
  * @constructor
  */
-class ResultListener(
+public class ResultListener(
     private val timeLimitNs: Long,
     private val checker: (totalCaptureResult: FrameInfo) -> Boolean,
 ) : Request.Listener {
 
     private val completeSignal = CompletableDeferred<FrameInfo?>()
-    val result: Deferred<FrameInfo?>
+    public val result: Deferred<FrameInfo?>
         get() = completeSignal
 
     @Volatile private var timestampOfFirstUpdateNs: Long? = null
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt
index 0e02cd9..4f4137a 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt
@@ -35,12 +35,12 @@
  * propagate to the registered [Request.Listener]s.
  */
 @CameraScope
-class ComboRequestListener @Inject constructor() : Request.Listener {
+public class ComboRequestListener @Inject constructor() : Request.Listener {
     private val requestListeners = mutableMapOf<Request.Listener, Executor>()
 
     @Volatile private var listeners: Map<Request.Listener, Executor> = mapOf()
 
-    fun addListener(listener: Request.Listener, executor: Executor) {
+    public fun addListener(listener: Request.Listener, executor: Executor) {
         check(!listeners.contains(listener)) { "$listener was already registered!" }
         synchronized(requestListeners) {
             requestListeners[listener] = executor
@@ -48,7 +48,7 @@
         }
     }
 
-    fun removeListener(listener: Request.Listener) {
+    public fun removeListener(listener: Request.Listener) {
         synchronized(requestListeners) {
             requestListeners.remove(listener)
             listeners = requestListeners.toMap()
@@ -153,7 +153,7 @@
     }
 }
 
-fun RequestMetadata.containsTag(tagKey: String, tagValue: Any): Boolean =
+public fun RequestMetadata.containsTag(tagKey: String, tagValue: Any): Boolean =
     getOrDefault(CAMERAX_TAG_BUNDLE, TagBundle.emptyBundle()).getTag(tagKey).let {
         return it == tagValue
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DeviceInfoLogger.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DeviceInfoLogger.kt
index 3d47344..ed972c7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DeviceInfoLogger.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DeviceInfoLogger.kt
@@ -22,8 +22,8 @@
 /**
  * Logs the required device info, e.g. camera hardware level required by CameraXHardwareLevelPlugin.
  */
-object DeviceInfoLogger {
-    fun logDeviceInfo(cameraProperties: CameraProperties) {
+public object DeviceInfoLogger {
+    public fun logDeviceInfo(cameraProperties: CameraProperties) {
         // Extend by adding logging here as needed.
         logDeviceLevel(cameraProperties)
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt
index 3b9b734..9501444 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/DisplayInfoManager.kt
@@ -30,11 +30,11 @@
 
 @Suppress("DEPRECATION") // getRealSize
 @Singleton
-class DisplayInfoManager @Inject constructor(context: Context) {
+public class DisplayInfoManager @Inject constructor(context: Context) {
     private val MAX_PREVIEW_SIZE = Size(1920, 1080)
     private val maxPreviewSize: MaxPreviewSize = MaxPreviewSize()
 
-    companion object {
+    public companion object {
         private var lazyMaxDisplay: Display? = null
         private var lazyPreviewSize: Size? = null
 
@@ -66,13 +66,13 @@
         }
     }
 
-    val defaultDisplay: Display
+    public val defaultDisplay: Display
         get() = getMaxSizeDisplay()
 
     private var previewSize: Size? = null
 
     /** Update the preview size according to current display size. */
-    fun refresh() {
+    public fun refresh() {
         previewSize = calculatePreviewSize()
     }
 
@@ -80,7 +80,7 @@
      * PREVIEW refers to the best size match to the device's screen resolution, or to 1080p
      * (1920x1080), whichever is smaller.
      */
-    fun getPreviewSize(): Size {
+    public fun getPreviewSize(): Size {
         // Use cached value to speed up since this would be called multiple times.
         if (previewSize != null) {
             return previewSize as Size
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/EvCompControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/EvCompControl.kt
index acfa6c5..23817fa 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/EvCompControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/EvCompControl.kt
@@ -38,7 +38,7 @@
  * [CameraControl.OperationCanceledException] if the camera is closed.
  */
 @CameraScope
-class EvCompControl
+public class EvCompControl
 @Inject
 constructor(
     private val compat: EvCompCompat,
@@ -49,7 +49,7 @@
             exposureState = exposureState.updateIndex(value)
         }
 
-    var exposureState =
+    public var exposureState: EvCompValue =
         EvCompValue(
             compat.supported,
             evCompIndex,
@@ -70,7 +70,7 @@
         updateAsync(DEFAULT_EXPOSURE_COMPENSATION)
     }
 
-    fun updateAsync(exposureIndex: Int, cancelPreviousTask: Boolean = true): Deferred<Int> {
+    public fun updateAsync(exposureIndex: Int, cancelPreviousTask: Boolean = true): Deferred<Int> {
         if (!compat.supported) {
             return createFailureResult(
                 IllegalArgumentException("ExposureCompensation is not supported")
@@ -103,9 +103,9 @@
         CompletableDeferred<Int>().apply { completeExceptionally(exception) }
 
     @Module
-    abstract class Bindings {
+    public abstract class Bindings {
         @Binds
         @IntoSet
-        abstract fun provideControls(evCompControl: EvCompControl): UseCaseCameraControl
+        public abstract fun provideControls(evCompControl: EvCompControl): UseCaseCameraControl
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FlashControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FlashControl.kt
index b5044ec..1c9f0e7 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FlashControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FlashControl.kt
@@ -44,7 +44,7 @@
 
 /** Implementation of Flash control exposed by [CameraControlInternal]. */
 @CameraScope
-class FlashControl
+public class FlashControl
 @Inject
 constructor(
     private val cameraProperties: CameraProperties,
@@ -71,19 +71,19 @@
     @Volatile @ImageCapture.FlashMode private var _flashMode: Int = DEFAULT_FLASH_MODE
 
     @ImageCapture.FlashMode
-    var flashMode: Int = _flashMode
+    public var flashMode: Int = _flashMode
         get() = _flashMode
         private set
 
     @Volatile private var _screenFlash: ScreenFlash? = null
 
-    var screenFlash: ScreenFlash? = _screenFlash
+    public var screenFlash: ScreenFlash? = _screenFlash
         get() = _screenFlash
         private set
 
     private var _updateSignal: CompletableDeferred<Unit>? = null
 
-    var updateSignal: Deferred<Unit> = CompletableDeferred(Unit)
+    public var updateSignal: Deferred<Unit> = CompletableDeferred(Unit)
         get() =
             if (_updateSignal != null) {
                 _updateSignal!!
@@ -92,7 +92,7 @@
             }
         private set
 
-    fun setFlashAsync(
+    public fun setFlashAsync(
         @ImageCapture.FlashMode flashMode: Int,
         cancelPreviousTask: Boolean = true
     ): Deferred<Unit> {
@@ -139,11 +139,11 @@
         _updateSignal = null
     }
 
-    fun setScreenFlash(screenFlash: ScreenFlash?) {
+    public fun setScreenFlash(screenFlash: ScreenFlash?) {
         _screenFlash = screenFlash
     }
 
-    suspend fun startScreenFlashCaptureTasks() {
+    public suspend fun startScreenFlashCaptureTasks() {
         val pendingTasks = mutableListOf<Deferred<Unit>>()
 
         // Invoke ScreenFlash#apply and wait later for its listener to be completed
@@ -245,7 +245,7 @@
         }
     }
 
-    suspend fun stopScreenFlashCaptureTasks() {
+    public suspend fun stopScreenFlashCaptureTasks() {
         withContext(Dispatchers.Main) {
             screenFlash?.clear()
             debug { "screenFlashPostCapture: ScreenFlash.clear() invoked" }
@@ -262,9 +262,9 @@
     }
 
     @Module
-    abstract class Bindings {
+    public abstract class Bindings {
         @Binds
         @IntoSet
-        abstract fun provideControls(flashControl: FlashControl): UseCaseCameraControl
+        public abstract fun provideControls(flashControl: FlashControl): UseCaseCameraControl
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
index b5eb887..9e69cb2 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
@@ -58,7 +58,7 @@
 /** Implementation of focus and metering controls exposed by [CameraControlInternal]. */
 @OptIn(ExperimentalCoroutinesApi::class)
 @CameraScope
-class FocusMeteringControl
+public class FocusMeteringControl
 @Inject
 constructor(
     private val cameraProperties: CameraProperties,
@@ -113,7 +113,7 @@
     private var focusTimeoutJob: Job? = null
     private var autoCancelJob: Job? = null
 
-    fun startFocusAndMetering(
+    public fun startFocusAndMetering(
         action: FocusMeteringAction,
         autoFocusTimeoutMs: Long = AUTO_FOCUS_TIMEOUT_DURATION,
     ): ListenableFuture<FocusMeteringResult> {
@@ -314,7 +314,7 @@
         }
     }
 
-    fun isFocusMeteringSupported(action: FocusMeteringAction): Boolean {
+    public fun isFocusMeteringSupported(action: FocusMeteringAction): Boolean {
         val rectanglesAe =
             meteringRegionsFromMeteringPoints(
                 action.meteringPointsAe,
@@ -363,7 +363,7 @@
         } else AeMode.OFF
     }
 
-    fun cancelFocusAndMeteringAsync(): Deferred<Result3A?> {
+    public fun cancelFocusAndMeteringAsync(): Deferred<Result3A?> {
         val signal = CompletableDeferred<Result3A?>()
         useCaseCamera?.let { useCaseCamera ->
             threads.sequentialScope.launch {
@@ -441,11 +441,11 @@
         return modes.contains(afMode)
     }
 
-    companion object {
-        const val METERING_WEIGHT_DEFAULT = MeteringRectangle.METERING_WEIGHT_MAX
-        const val AUTO_FOCUS_TIMEOUT_DURATION = 5000L
+    public companion object {
+        public const val METERING_WEIGHT_DEFAULT: Int = MeteringRectangle.METERING_WEIGHT_MAX
+        public const val AUTO_FOCUS_TIMEOUT_DURATION: Long = 5000L
 
-        fun meteringRegionsFromMeteringPoints(
+        public fun meteringRegionsFromMeteringPoints(
             meteringPoints: List<MeteringPoint>,
             maxRegionCount: Int,
             cropSensorRegion: Rect,
@@ -552,10 +552,10 @@
     }
 
     @Module
-    abstract class Bindings {
+    public abstract class Bindings {
         @Binds
         @IntoSet
-        abstract fun provideControls(
+        public abstract fun provideControls(
             focusMeteringControl: FocusMeteringControl
         ): UseCaseCameraControl
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
index 5dd8c6b..82da040 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/MeteringRepeating.kt
@@ -54,7 +54,7 @@
  * enabled, since taking a picture may require a repeating surface to perform pre-capture checks,
  * mainly around 3A.
  */
-class MeteringRepeating(
+public class MeteringRepeating(
     private val cameraProperties: CameraProperties,
     config: MeteringRepeatingConfig,
     private val displayInfoManager: DisplayInfoManager
@@ -68,10 +68,12 @@
 
     @GuardedBy("deferrableSurfaceLock") private var deferrableSurface: DeferrableSurface? = null
 
-    override fun getDefaultConfig(applyDefaultConfig: Boolean, factory: UseCaseConfigFactory) =
-        Builder(cameraProperties, displayInfoManager).useCaseConfig
+    override fun getDefaultConfig(
+        applyDefaultConfig: Boolean,
+        factory: UseCaseConfigFactory
+    ): MeteringRepeatingConfig = Builder(cameraProperties, displayInfoManager).useCaseConfig
 
-    override fun getUseCaseConfigBuilder(config: Config) =
+    override fun getUseCaseConfigBuilder(config: Config): Builder =
         Builder(cameraProperties, displayInfoManager)
 
     override fun onSuggestedStreamSpecUpdated(
@@ -91,7 +93,7 @@
     }
 
     /** Sets up the use case's session configuration, mainly its [DeferrableSurface]. */
-    fun setupSession() {
+    public fun setupSession() {
         // The suggested stream spec passed to `updateSuggestedStreamSpec` doesn't matter since
         // this use case uses the min preview size.
         updateSuggestedStreamSpec(StreamSpec.builder(DEFAULT_PREVIEW_SIZE).build(), null)
@@ -194,7 +196,7 @@
         return outputSizes[0]
     }
 
-    class MeteringRepeatingConfig : UseCaseConfig<MeteringRepeating>, ImageInputConfig {
+    public class MeteringRepeatingConfig : UseCaseConfig<MeteringRepeating>, ImageInputConfig {
         private val config =
             MutableOptionsBundle.create().apply {
                 insertOption(
@@ -204,41 +206,47 @@
                 insertOption(OPTION_CAPTURE_TYPE, CaptureType.METERING_REPEATING)
             }
 
-        override fun getCaptureType() = UseCaseConfigFactory.CaptureType.METERING_REPEATING
+        override fun getCaptureType(): CaptureType =
+            UseCaseConfigFactory.CaptureType.METERING_REPEATING
 
-        override fun getConfig() = config
+        override fun getConfig(): MutableOptionsBundle = config
 
-        override fun getInputFormat() = ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
+        override fun getInputFormat(): Int =
+            ImageFormatConstants.INTERNAL_DEFINED_IMAGE_FORMAT_PRIVATE
     }
 
-    class Builder(
+    public class Builder(
         private val cameraProperties: CameraProperties,
         private val displayInfoManager: DisplayInfoManager
     ) : UseCaseConfig.Builder<MeteringRepeating, MeteringRepeatingConfig, Builder> {
 
-        override fun getMutableConfig() = MutableOptionsBundle.create()
+        override fun getMutableConfig(): MutableOptionsBundle = MutableOptionsBundle.create()
 
-        override fun getUseCaseConfig() = MeteringRepeatingConfig()
+        override fun getUseCaseConfig(): MeteringRepeatingConfig = MeteringRepeatingConfig()
 
-        override fun setTargetClass(targetClass: Class<MeteringRepeating>) = this
+        override fun setTargetClass(targetClass: Class<MeteringRepeating>): Builder = this
 
-        override fun setTargetName(targetName: String) = this
+        override fun setTargetName(targetName: String): Builder = this
 
-        override fun setDefaultSessionConfig(sessionConfig: SessionConfig) = this
+        override fun setDefaultSessionConfig(sessionConfig: SessionConfig): Builder = this
 
-        override fun setDefaultCaptureConfig(captureConfig: CaptureConfig) = this
+        override fun setDefaultCaptureConfig(captureConfig: CaptureConfig): Builder = this
 
-        override fun setSessionOptionUnpacker(optionUnpacker: SessionConfig.OptionUnpacker) = this
+        override fun setSessionOptionUnpacker(
+            optionUnpacker: SessionConfig.OptionUnpacker
+        ): Builder = this
 
-        override fun setCaptureOptionUnpacker(optionUnpacker: CaptureConfig.OptionUnpacker) = this
+        override fun setCaptureOptionUnpacker(
+            optionUnpacker: CaptureConfig.OptionUnpacker
+        ): Builder = this
 
-        override fun setSurfaceOccupancyPriority(priority: Int) = this
+        override fun setSurfaceOccupancyPriority(priority: Int): Builder = this
 
-        override fun setZslDisabled(disabled: Boolean) = this
+        override fun setZslDisabled(disabled: Boolean): Builder = this
 
-        override fun setHighResolutionDisabled(disabled: Boolean) = this
+        override fun setHighResolutionDisabled(disabled: Boolean): Builder = this
 
-        override fun setCaptureType(captureType: UseCaseConfigFactory.CaptureType) = this
+        override fun setCaptureType(captureType: UseCaseConfigFactory.CaptureType): Builder = this
 
         override fun build(): MeteringRepeating {
             return MeteringRepeating(cameraProperties, useCaseConfig, displayInfoManager)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/SessionProcessorManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/SessionProcessorManager.kt
index 12911e0..b42ad5b 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/SessionProcessorManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/SessionProcessorManager.kt
@@ -50,14 +50,14 @@
 import kotlinx.coroutines.withTimeoutOrNull
 
 @OptIn(ExperimentalCamera2Interop::class)
-class SessionProcessorManager(
+public class SessionProcessorManager(
     private val sessionProcessor: SessionProcessor,
     private val cameraInfoInternal: CameraInfoInternal,
     private val scope: CoroutineScope,
 ) {
     private val lock = Any()
 
-    enum class State {
+    public enum class State {
         /**
          * [CREATED] is the initial state, and indicates that the [SessionProcessorManager] has been
          * created but not initialized yet.
@@ -394,7 +394,7 @@
         sessionProcessor.setParameters(builder.build())
     }
 
-    companion object {
+    public companion object {
         private suspend fun getSurfaces(
             deferrableSurfaces: List<DeferrableSurface>,
             timeoutMillis: Long,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Sizes.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Sizes.kt
index 8a3731ee..89007e8 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Sizes.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Sizes.kt
@@ -19,17 +19,20 @@
 import android.graphics.Point
 import android.util.Size
 
-fun Size.area(): Int = this.width * this.height
+public fun Size.area(): Int = this.width * this.height
 
-fun Size.asLandscape(): Size =
+public fun Size.asLandscape(): Size =
     if (this.width >= this.height) this else Size(this.height, this.width)
 
-fun Size.asPortrait(): Size = if (this.width <= this.height) this else Size(this.height, this.width)
+public fun Size.asPortrait(): Size =
+    if (this.width <= this.height) this else Size(this.height, this.width)
 
-fun minByArea(left: Size, right: Size) = if (left.area() < right.area()) left else right
+public fun minByArea(left: Size, right: Size): Size =
+    if (left.area() < right.area()) left else right
 
-fun maxByArea(left: Size, right: Size) = if (left.area() > right.area()) left else right
+public fun maxByArea(left: Size, right: Size): Size =
+    if (left.area() > right.area()) left else right
 
-fun Point.area(): Int = this.x * this.y
+public fun Point.area(): Int = this.x * this.y
 
-fun Point.toSize() = Size(this.x, this.y)
+public fun Point.toSize(): Size = Size(this.x, this.y)
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/State3AControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/State3AControl.kt
index 2969c95..25456dc 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/State3AControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/State3AControl.kt
@@ -40,10 +40,10 @@
 import kotlinx.coroutines.Deferred
 
 @CameraScope
-class State3AControl
+public class State3AControl
 @Inject
 constructor(
-    val cameraProperties: CameraProperties,
+    public val cameraProperties: CameraProperties,
     private val aeModeDisabler: AutoFlashAEModeDisabler,
     private val aeFpsRange: AeFpsRange,
 ) : UseCaseCameraControl, UseCaseCamera.RunningUseCasesChangeListener {
@@ -78,15 +78,16 @@
     @GuardedBy("lock") private val updateSignals = mutableSetOf<CompletableDeferred<Unit>>()
 
     @GuardedBy("lock")
-    var updateSignal: Deferred<Unit>? = null
+    public var updateSignal: Deferred<Unit>? = null
         private set
 
-    var flashMode by updateOnPropertyChange(DEFAULT_FLASH_MODE)
-    var template by updateOnPropertyChange(DEFAULT_REQUEST_TEMPLATE)
-    var tryExternalFlashAeMode: Boolean by updateOnPropertyChange(false)
-    var preferredAeMode: Int? by updateOnPropertyChange(null)
-    var preferredFocusMode: Int? by updateOnPropertyChange(null)
-    var preferredAeFpsRange: Range<Int>? by updateOnPropertyChange(aeFpsRange.getTargetAeFpsRange())
+    public var flashMode: Int by updateOnPropertyChange(DEFAULT_FLASH_MODE)
+    public var template: Int by updateOnPropertyChange(DEFAULT_REQUEST_TEMPLATE)
+    public var tryExternalFlashAeMode: Boolean by updateOnPropertyChange(false)
+    public var preferredAeMode: Int? by updateOnPropertyChange(null)
+    public var preferredFocusMode: Int? by updateOnPropertyChange(null)
+    public var preferredAeFpsRange: Range<Int>? by
+        updateOnPropertyChange(aeFpsRange.getTargetAeFpsRange())
 
     override fun reset() {
         synchronized(lock) { updateSignals.toList() }.cancelAll()
@@ -141,7 +142,7 @@
         return preferAeMode
     }
 
-    fun invalidate() {
+    public fun invalidate() {
         // TODO(b/276779600): Refactor and move the setting of these parameter to
         //  CameraGraph.Config(requiredParameters = mapOf(....)).
         synchronized(invalidateLock) {
@@ -206,9 +207,9 @@
     }
 
     @Module
-    abstract class Bindings {
+    public abstract class Bindings {
         @Binds
         @IntoSet
-        abstract fun provideControls(state3AControl: State3AControl): UseCaseCameraControl
+        public abstract fun provideControls(state3AControl: State3AControl): UseCaseCameraControl
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestControl.kt
index 711fbce..d09a401 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestControl.kt
@@ -40,7 +40,7 @@
 import kotlinx.coroutines.sync.withLock
 
 @CameraScope
-class StillCaptureRequestControl
+public class StillCaptureRequestControl
 @Inject
 constructor(
     private val flashControl: FlashControl,
@@ -56,7 +56,7 @@
             _useCaseCamera?.let { submitPendingRequests() }
         }
 
-    data class CaptureRequest(
+    public data class CaptureRequest(
         val captureConfigs: List<CaptureConfig>,
         @ImageCapture.CaptureMode val captureMode: Int,
         @ImageCapture.FlashType val flashType: Int,
@@ -89,7 +89,7 @@
         }
     }
 
-    fun issueCaptureRequests(
+    public fun issueCaptureRequests(
         captureConfigs: List<CaptureConfig>,
         @ImageCapture.CaptureMode captureMode: Int,
         @ImageCapture.FlashType flashType: Int,
@@ -202,9 +202,11 @@
     }
 
     @Module
-    abstract class Bindings {
+    public abstract class Bindings {
         @Binds
         @IntoSet
-        abstract fun provideControls(control: StillCaptureRequestControl): UseCaseCameraControl
+        public abstract fun provideControls(
+            control: StillCaptureRequestControl
+        ): UseCaseCameraControl
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Tags.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Tags.kt
index d93dd08..216aeb9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Tags.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/Tags.kt
@@ -20,5 +20,7 @@
 import androidx.camera.core.impl.TagBundle
 
 /** Custom tags that can be passed used by CameraPipe */
-public val CAMERAX_TAG_BUNDLE = Metadata.Key.create<TagBundle>("camerax.tag_bundle")
-val USE_CASE_CAMERA_STATE_CUSTOM_TAG = Metadata.Key.create<Int>("use_case_camera_state.tag")
+public val CAMERAX_TAG_BUNDLE: Metadata.Key<TagBundle> =
+    Metadata.Key.create<TagBundle>("camerax.tag_bundle")
+public val USE_CASE_CAMERA_STATE_CUSTOM_TAG: Metadata.Key<Int> =
+    Metadata.Key.create<Int>("use_case_camera_state.tag")
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/TorchControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/TorchControl.kt
index e7e93fb..2fc55ab3 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/TorchControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/TorchControl.kt
@@ -36,7 +36,7 @@
 
 /** Implementation of Torch control exposed by [CameraControlInternal]. */
 @CameraScope
-class TorchControl
+public class TorchControl
 @Inject
 constructor(
     cameraProperties: CameraProperties,
@@ -68,7 +68,7 @@
     private val hasFlashUnit: Boolean = cameraProperties.isFlashAvailable()
 
     private val _torchState = MutableLiveData(TorchState.OFF)
-    val torchStateLiveData: LiveData<Int>
+    public val torchStateLiveData: LiveData<Int>
         get() = _torchState
 
     private var _updateSignal: CompletableDeferred<Unit>? = null
@@ -81,7 +81,7 @@
      * @param ignoreFlashUnitAvailability Whether to ignore the flash unit availability. When true,
      *   torch mode setting will be attempted even if a physical flash unit is not available.
      */
-    fun setTorchAsync(
+    public fun setTorchAsync(
         torch: Boolean,
         cancelPreviousTask: Boolean = true,
         ignoreFlashUnitAvailability: Boolean = false
@@ -152,9 +152,9 @@
         }
 
     @Module
-    abstract class Bindings {
+    public abstract class Bindings {
         @Binds
         @IntoSet
-        abstract fun provideControls(torchControl: TorchControl): UseCaseCameraControl
+        public abstract fun provideControls(torchControl: TorchControl): UseCaseCameraControl
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
index e60f9ba..b19bfe9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
@@ -47,42 +47,43 @@
 internal val defaultOptionPriority = Config.OptionPriority.OPTIONAL
 internal const val defaultTemplate = CameraDevice.TEMPLATE_PREVIEW
 
-interface UseCaseCamera {
+public interface UseCaseCamera {
     // UseCases
-    var runningUseCases: Set<UseCase>
+    public var runningUseCases: Set<UseCase>
 
-    var isPrimary: Boolean
+    public var isPrimary: Boolean
 
-    interface RunningUseCasesChangeListener {
+    public interface RunningUseCasesChangeListener {
         /** Invoked when value of [UseCaseCamera.runningUseCases] has been changed. */
-        fun onRunningUseCasesChanged()
+        public fun onRunningUseCasesChanged()
     }
 
     // RequestControl of the UseCaseCamera
-    val requestControl: UseCaseCameraRequestControl
+    public val requestControl: UseCaseCameraRequestControl
 
     // Parameters
-    fun <T> setParameterAsync(
+    public fun <T> setParameterAsync(
         key: CaptureRequest.Key<T>,
         value: T,
         priority: Config.OptionPriority = defaultOptionPriority,
     ): Deferred<Unit>
 
-    fun setParametersAsync(
+    public fun setParametersAsync(
         values: Map<CaptureRequest.Key<*>, Any>,
         priority: Config.OptionPriority = defaultOptionPriority,
     ): Deferred<Unit>
 
-    fun setActiveResumeMode(enabled: Boolean) {}
+    public fun setActiveResumeMode(enabled: Boolean) {}
 
     // Lifecycle
-    fun close(): Job
+    public fun close(): Job
 }
 
 /** API for interacting with a [CameraGraph] that has been configured with a set of [UseCase]'s */
 @UseCaseCameraScope
-@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") // Java version required for Dagger
-class UseCaseCameraImpl
+@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
+// Java version required for Dagger
+public class UseCaseCameraImpl
 @Inject
 constructor(
     private val controls: java.util.Set<UseCaseCameraControl>,
@@ -97,7 +98,7 @@
     private val debugId = useCaseCameraIds.incrementAndGet()
     private val closed = atomic(false)
 
-    override var runningUseCases = setOf<UseCase>()
+    override var runningUseCases: Set<UseCase> = setOf<UseCase>()
         set(value) {
             field = value
 
@@ -237,13 +238,13 @@
     override fun toString(): String = "UseCaseCamera-$debugId"
 
     @Module
-    abstract class Bindings {
+    public abstract class Bindings {
         @UseCaseCameraScope
         @Binds
-        abstract fun provideUseCaseCamera(useCaseCamera: UseCaseCameraImpl): UseCaseCamera
+        public abstract fun provideUseCaseCamera(useCaseCamera: UseCaseCameraImpl): UseCaseCamera
     }
 
-    companion object {
+    public companion object {
         private val canceledResult = CompletableDeferred<Unit>().apply { cancel() }
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraControl.kt
index 082ab20..e08f92a 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraControl.kt
@@ -16,8 +16,8 @@
 
 package androidx.camera.camera2.pipe.integration.impl
 
-interface UseCaseCameraControl {
-    var useCaseCamera: UseCaseCamera?
+public interface UseCaseCameraControl {
+    public var useCaseCamera: UseCaseCamera?
 
-    fun reset()
+    public fun reset()
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
index a2df1ff..9024f2d 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraRequestControl.kt
@@ -57,9 +57,9 @@
  * The parameters can be stored for the different types of config respectively. Each type of the
  * config can be removed or overridden respectively without interfering with the other types.
  */
-interface UseCaseCameraRequestControl {
+public interface UseCaseCameraRequestControl {
     /** The declaration order is the ordering to merge. */
-    enum class Type {
+    public enum class Type {
         SESSION_CONFIG,
         DEFAULT,
         CAMERA2_CAMERA_CONTROL,
@@ -84,7 +84,7 @@
      *   similar to the [CaptureRequest.Builder.setTag].
      * @param listeners to receive the capture results.
      */
-    fun addParametersAsync(
+    public fun addParametersAsync(
         type: Type = Type.DEFAULT,
         values: Map<CaptureRequest.Key<*>, Any> = emptyMap(),
         optionPriority: Config.OptionPriority = defaultOptionPriority,
@@ -110,7 +110,7 @@
      *   will use the [RequestTemplate] that is previously specified.
      * @param listeners to receive the capture results.
      */
-    fun setConfigAsync(
+    public fun setConfigAsync(
         type: Type,
         config: Config? = null,
         tags: Map<String, Any> = emptyMap(),
@@ -121,9 +121,9 @@
     ): Deferred<Unit>
 
     // 3A
-    suspend fun setTorchAsync(enabled: Boolean): Deferred<Result3A>
+    public suspend fun setTorchAsync(enabled: Boolean): Deferred<Result3A>
 
-    suspend fun startFocusAndMeteringAsync(
+    public suspend fun startFocusAndMeteringAsync(
         aeRegions: List<MeteringRectangle>? = null,
         afRegions: List<MeteringRectangle>? = null,
         awbRegions: List<MeteringRectangle>? = null,
@@ -134,10 +134,10 @@
         timeLimitNs: Long = CameraGraph.Constants3A.DEFAULT_TIME_LIMIT_NS,
     ): Deferred<Result3A>
 
-    suspend fun cancelFocusAndMeteringAsync(): Deferred<Result3A>
+    public suspend fun cancelFocusAndMeteringAsync(): Deferred<Result3A>
 
     // Capture
-    suspend fun issueSingleCaptureAsync(
+    public suspend fun issueSingleCaptureAsync(
         captureSequence: List<CaptureConfig>,
         @ImageCapture.CaptureMode captureMode: Int,
         @ImageCapture.FlashType flashType: Int,
@@ -152,17 +152,17 @@
      *
      * @see [CameraGraph.Session.update3A]
      */
-    suspend fun update3aRegions(
+    public suspend fun update3aRegions(
         aeRegions: List<MeteringRectangle>? = null,
         afRegions: List<MeteringRectangle>? = null,
         awbRegions: List<MeteringRectangle>? = null,
     ): Deferred<Result3A>
 
-    fun close()
+    public fun close()
 }
 
 @UseCaseCameraScope
-class UseCaseCameraRequestControlImpl
+public class UseCaseCameraRequestControlImpl
 @Inject
 constructor(
     private val capturePipeline: CapturePipeline,
@@ -276,7 +276,7 @@
             }
         } ?: submitFailedResult
 
-    override suspend fun cancelFocusAndMeteringAsync() =
+    override suspend fun cancelFocusAndMeteringAsync(): Deferred<Result3A> =
         runIfNotClosed {
             useGraphSessionOrFailed { it.unlock3A(ae = true, af = true, awb = true) }.await()
 
@@ -294,7 +294,7 @@
         @ImageCapture.CaptureMode captureMode: Int,
         @ImageCapture.FlashType flashType: Int,
         @ImageCapture.FlashMode flashMode: Int,
-    ) =
+    ): List<Deferred<Void?>> =
         runIfNotClosed {
             if (captureSequence.hasInvalidSurface()) {
                 failedResults(captureSequence.size, "Capture request failed due to invalid surface")
@@ -324,7 +324,7 @@
         aeRegions: List<MeteringRectangle>?,
         afRegions: List<MeteringRectangle>?,
         awbRegions: List<MeteringRectangle>?
-    ) =
+    ): Deferred<Result3A> =
         runIfNotClosed {
             useGraphSessionOrFailed {
                 it.update3A(
@@ -428,22 +428,22 @@
         }
 
     @Module
-    abstract class Bindings {
+    public abstract class Bindings {
         @UseCaseCameraScope
         @Binds
-        abstract fun provideRequestControls(
+        public abstract fun provideRequestControls(
             requestControl: UseCaseCameraRequestControlImpl
         ): UseCaseCameraRequestControl
     }
 
-    companion object {
+    public companion object {
         private val submitFailedResult =
             CompletableDeferred(Result3A(Result3A.Status.SUBMIT_FAILED))
         private val canceledResult = CompletableDeferred<Unit>().apply { cancel() }
     }
 }
 
-fun TagBundle.toMap(): Map<String, Any> =
+public fun TagBundle.toMap(): Map<String, Any> =
     mutableMapOf<String, Any>().also {
         listKeys().forEach { tagKey -> it[tagKey] = getTag(tagKey) as Any }
     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
index 0d6bc31..ad6d9fd 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
@@ -59,7 +59,7 @@
  * camera graph as fast as the camera is capable of consuming them.
  */
 @UseCaseCameraScope
-class UseCaseCameraState
+public class UseCaseCameraState
 @Inject
 constructor(
     useCaseGraphConfig: UseCaseGraphConfig,
@@ -75,7 +75,7 @@
 
     @GuardedBy("lock") private val submittedRequestCounter = atomic(0)
 
-    data class RequestSignal(val requestNo: Int, val signal: CompletableDeferred<Unit>)
+    public data class RequestSignal(val requestNo: Int, val signal: CompletableDeferred<Unit>)
 
     @GuardedBy("lock") private var updateSignals = ArrayDeque<RequestSignal>()
 
@@ -108,7 +108,7 @@
      *
      * @return A [Deferred] signal to represent if the update operation has been completed.
      */
-    fun updateAsync(
+    public fun updateAsync(
         parameters: Map<CaptureRequest.Key<*>, Any>? = null,
         appendParameters: Boolean = true,
         internalParameters: Map<Metadata.Key<*>, Any>? = null,
@@ -161,7 +161,7 @@
         return result
     }
 
-    fun update(
+    public fun update(
         parameters: Map<CaptureRequest.Key<*>, Any>? = null,
         appendParameters: Boolean = true,
         internalParameters: Map<Metadata.Key<*>, Any>? = null,
@@ -189,7 +189,7 @@
         submitLatest()
     }
 
-    fun capture(requests: List<Request>) {
+    public fun capture(requests: List<Request>) {
         threads.sequentialScope.launch(start = CoroutineStart.UNDISPATCHED) {
             cameraGraph.acquireSession().use { it.submit(requests) }
         }
@@ -245,7 +245,7 @@
      * Tries to invoke [androidx.camera.camera2.pipe.CameraGraph.Session.startRepeating] with
      * current (the most recent) set of values.
      */
-    fun tryStartRepeating() = submitLatest()
+    public fun tryStartRepeating(): Unit = submitLatest()
 
     private fun submitLatest() {
         if (sessionProcessorManager != null) {
@@ -411,7 +411,7 @@
         setCaptureRequestOption(key, value as T)
     }
 
-    inner class RequestListener : Request.Listener {
+    public inner class RequestListener : Request.Listener {
         override fun onTotalCaptureResult(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
index 0b760d2..d064344 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseManager.kt
@@ -106,7 +106,7 @@
  */
 @OptIn(ExperimentalCamera2Interop::class)
 @CameraScope
-class UseCaseManager
+public class UseCaseManager
 @Inject
 constructor(
     private val cameraPipe: CameraPipe,
@@ -171,10 +171,10 @@
     }
 
     @Volatile private var _activeComponent: UseCaseCameraComponent? = null
-    val camera: UseCaseCamera?
+    public val camera: UseCaseCamera?
         get() = _activeComponent?.getUseCaseCamera()
 
-    val useCaseGraphConfig: UseCaseGraphConfig?
+    public val useCaseGraphConfig: UseCaseGraphConfig?
         get() = _activeComponent?.getUseCaseGraphConfig()
 
     private val closingCameraJobs = mutableListOf<Job>()
@@ -199,7 +199,7 @@
      * changes are identified (i.e., a new use case is added), the subsequent actions would trigger
      * a recreation of the current CameraGraph if there is one.
      */
-    fun attach(useCases: List<UseCase>): Unit =
+    public fun attach(useCases: List<UseCase>): Unit =
         synchronized(lock) {
             if (useCases.isEmpty()) {
                 Log.warn { "Attach [] from $this (Ignored)" }
@@ -237,7 +237,7 @@
      * changes are identified (i.e., an existing use case is removed), the subsequent actions would
      * trigger a recreation of the current CameraGraph.
      */
-    fun detach(useCases: List<UseCase>): Unit =
+    public fun detach(useCases: List<UseCase>): Unit =
         synchronized(lock) {
             if (useCases.isEmpty()) {
                 Log.warn { "Detaching [] from $this (Ignored)" }
@@ -282,7 +282,7 @@
      * latest set of "running" (attached and active) use cases, which will in turn trigger actions
      * for SessionConfig updates.
      */
-    fun activate(useCase: UseCase) =
+    public fun activate(useCase: UseCase): Unit =
         synchronized(lock) {
             if (activeUseCases.add(useCase)) {
                 refreshRunningUseCases()
@@ -295,38 +295,38 @@
      * latest set of "running" (attached and active) use cases, which will in turn trigger actions
      * for SessionConfig updates.
      */
-    fun deactivate(useCase: UseCase) =
+    public fun deactivate(useCase: UseCase): Unit =
         synchronized(lock) {
             if (activeUseCases.remove(useCase)) {
                 refreshRunningUseCases()
             }
         }
 
-    fun update(useCase: UseCase) =
+    public fun update(useCase: UseCase): Unit =
         synchronized(lock) {
             if (attachedUseCases.contains(useCase)) {
                 refreshRunningUseCases()
             }
         }
 
-    fun reset(useCase: UseCase) =
+    public fun reset(useCase: UseCase): Unit =
         synchronized(lock) {
             if (attachedUseCases.contains(useCase)) {
                 refreshAttachedUseCases(attachedUseCases)
             }
         }
 
-    fun setPrimary(isPrimary: Boolean) {
+    public fun setPrimary(isPrimary: Boolean) {
         synchronized(lock) { this.isPrimary = isPrimary }
     }
 
-    fun setActiveResumeMode(enabled: Boolean) =
+    public fun setActiveResumeMode(enabled: Boolean): Unit? =
         synchronized(lock) {
             activeResumeEnabled = enabled
             camera?.setActiveResumeMode(enabled)
         }
 
-    suspend fun close() {
+    public suspend fun close() {
         val closingJobs =
             synchronized(lock) {
                 if (attachedUseCases.isNotEmpty()) {
@@ -689,7 +689,7 @@
         cameraControl.setZslDisabledByUserCaseConfig(disableZsl)
     }
 
-    companion object {
+    public companion object {
         internal data class UseCaseManagerConfig(
             val useCases: List<UseCase>,
             val sessionConfigAdapter: SessionConfigAdapter,
@@ -697,11 +697,11 @@
             val streamConfigMap: MutableMap<CameraStream.Config, DeferrableSurface>
         )
 
-        fun SessionConfig.toCamera2ImplConfig(): Camera2ImplConfig {
+        public fun SessionConfig.toCamera2ImplConfig(): Camera2ImplConfig {
             return Camera2ImplConfig(implementationOptions)
         }
 
-        fun createCameraGraphConfig(
+        public fun createCameraGraphConfig(
             sessionConfigAdapter: SessionConfigAdapter,
             streamConfigMap: MutableMap<CameraStream.Config, DeferrableSurface>,
             callbackMap: CameraCallbackMap,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseSurfaceManager.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseSurfaceManager.kt
index db5b4e4..aa4e070 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseSurfaceManager.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseSurfaceManager.kt
@@ -42,7 +42,7 @@
 
 /** Configure the [DeferrableSurface]s to the [CameraGraph] and monitor the usage. */
 @UseCaseCameraScope
-class UseCaseSurfaceManager
+public class UseCaseSurfaceManager
 @Inject
 constructor(
     private val threads: UseCaseThreads,
@@ -63,7 +63,7 @@
     @GuardedBy("lock") private var _sessionConfigAdapter: SessionConfigAdapter? = null
 
     /** Async set up the Surfaces to the [CameraGraph] */
-    fun setupAsync(
+    public fun setupAsync(
         graph: CameraGraph,
         sessionConfigAdapter: SessionConfigAdapter,
         surfaceToStreamMap: Map<DeferrableSurface, StreamId>,
@@ -117,7 +117,7 @@
     }
 
     /** Cancel the Surface set up and stop the monitoring of Surface usage. */
-    fun stopAsync(): Deferred<Unit> {
+    public fun stopAsync(): Deferred<Unit> {
         setupSurfaceDeferred?.cancel()
 
         return synchronized(lock) {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseThreads.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseThreads.kt
index feeeaf1..205defa 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseThreads.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseThreads.kt
@@ -25,14 +25,15 @@
 import kotlinx.coroutines.asCoroutineDispatcher
 
 /** Collection of threads and scope(s) that have been configured and tuned. */
-class UseCaseThreads(
-    val scope: CoroutineScope,
-    val backgroundExecutor: Executor,
-    val backgroundDispatcher: CoroutineDispatcher,
+public class UseCaseThreads(
+    public val scope: CoroutineScope,
+    public val backgroundExecutor: Executor,
+    public val backgroundDispatcher: CoroutineDispatcher,
 ) {
-    val sequentialExecutor = CameraXExecutors.newSequentialExecutor(backgroundExecutor)
+    public val sequentialExecutor: Executor =
+        CameraXExecutors.newSequentialExecutor(backgroundExecutor)
     private val sequentialDispatcher = sequentialExecutor.asCoroutineDispatcher()
-    var sequentialScope: CoroutineScope =
+    public var sequentialScope: CoroutineScope =
         CoroutineScope(scope.coroutineContext + SupervisorJob() + sequentialDispatcher)
         @VisibleForTesting set
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
index aeeb4db..4d4ad3a 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
@@ -39,10 +39,10 @@
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
 
-const val DEFAULT_ZOOM_RATIO = 1.0f
+public const val DEFAULT_ZOOM_RATIO: Float = 1.0f
 
 @CameraScope
-class ZoomControl
+public class ZoomControl
 @Inject
 constructor(
     private val threads: UseCaseThreads,
@@ -50,18 +50,20 @@
 ) : UseCaseCameraControl {
     // NOTE: minZoom may be lower than 1.0
     // NOTE: Default zoom ratio is 1.0 (DEFAULT_ZOOM_RATIO)
-    val minZoomRatio: Float = zoomCompat.minZoomRatio
-    val maxZoomRatio: Float = zoomCompat.maxZoomRatio
+    public val minZoomRatio: Float = zoomCompat.minZoomRatio
+    public val maxZoomRatio: Float = zoomCompat.maxZoomRatio
 
-    val defaultZoomState by lazy { ZoomValue(DEFAULT_ZOOM_RATIO, minZoomRatio, maxZoomRatio) }
+    public val defaultZoomState: ZoomValue by lazy {
+        ZoomValue(DEFAULT_ZOOM_RATIO, minZoomRatio, maxZoomRatio)
+    }
 
     private val _zoomState by lazy { MutableLiveData<ZoomState>(defaultZoomState) }
 
-    val zoomStateLiveData: LiveData<ZoomState>
+    public val zoomStateLiveData: LiveData<ZoomState>
         get() = _zoomState
 
     /** Linear zoom is between 0.0f and 1.0f */
-    fun toLinearZoom(zoomRatio: Float) =
+    public fun toLinearZoom(zoomRatio: Float): Float =
         getLinearZoomFromZoomRatio(
             zoomRatio = zoomRatio,
             minZoomRatio = minZoomRatio,
@@ -99,7 +101,7 @@
         }
     }
 
-    fun setLinearZoom(linearZoom: Float): ListenableFuture<Void> {
+    public fun setLinearZoom(linearZoom: Float): ListenableFuture<Void> {
         if (linearZoom > 1.0f || linearZoom < 0f) {
             val outOfRangeDesc = "Requested linearZoom $linearZoom is not within valid range [0, 1]"
             return Futures.immediateFailedFuture(IllegalArgumentException(outOfRangeDesc))
@@ -114,7 +116,7 @@
         return applyZoomState(zoomValue)
     }
 
-    fun setZoomRatio(zoomRatio: Float): ListenableFuture<Void> {
+    public fun setZoomRatio(zoomRatio: Float): ListenableFuture<Void> {
         if (zoomRatio > maxZoomRatio || zoomRatio < minZoomRatio) {
             val outOfRangeDesc =
                 "Requested zoomRatio $zoomRatio is not within valid range" +
@@ -131,7 +133,7 @@
         return applyZoomState(zoomValue)
     }
 
-    fun applyZoomState(
+    public fun applyZoomState(
         zoomState: ZoomState,
         cancelPreviousTask: Boolean = true,
     ): ListenableFuture<Void> {
@@ -173,7 +175,9 @@
     }
 
     @Module
-    abstract class Bindings {
-        @Binds @IntoSet abstract fun provideControls(zoomControl: ZoomControl): UseCaseCameraControl
+    public abstract class Bindings {
+        @Binds
+        @IntoSet
+        public abstract fun provideControls(zoomControl: ZoomControl): UseCaseCameraControl
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraCompatibilityFilter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraCompatibilityFilter.kt
index 2941bab..b027722 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraCompatibilityFilter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraCompatibilityFilter.kt
@@ -28,10 +28,10 @@
  * The [CameraCompatibilityFilter] is responsible for filtering out Cameras that doesn't contains
  * REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE capability.
  */
-object CameraCompatibilityFilter {
+public object CameraCompatibilityFilter {
 
     @JvmStatic
-    fun getBackwardCompatibleCameraIds(
+    public fun getBackwardCompatibleCameraIds(
         cameraDevices: CameraDevices,
         availableCameraIds: List<String>
     ): List<String> {
@@ -50,7 +50,7 @@
     }
 
     @JvmStatic
-    fun isBackwardCompatible(cameraId: String, cameraDevices: CameraDevices): Boolean {
+    public fun isBackwardCompatible(cameraId: String, cameraDevices: CameraDevices): Boolean {
         // Always returns true to not break robolectric tests because the cameras setup in
         // robolectric don't have REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE capability
         // by default.
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraFovInfo.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraFovInfo.kt
index 25da0bd..4994daa 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraFovInfo.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/CameraFovInfo.kt
@@ -31,7 +31,7 @@
 import kotlin.math.atan
 
 @CameraScope
-class CameraFovInfo
+public class CameraFovInfo
 @Inject
 constructor(
     private val cameraDevices: CameraDevices,
@@ -142,7 +142,7 @@
      * @throws IllegalStateException If a valid view angle could not be found.
      */
     @Throws(IllegalStateException::class)
-    fun getDefaultViewAngleDegrees(): Int {
+    public fun getDefaultViewAngleDegrees(): Int {
         try {
             return focalLengthToViewAngleDegrees(
                 getDefaultFocalLength(),
@@ -164,7 +164,7 @@
      * @throws IllegalStateException If a valid view angle could not be found.
      */
     @Throws(IllegalStateException::class)
-    fun getDefaultCameraDefaultViewAngleDegrees(): Int {
+    public fun getDefaultCameraDefaultViewAngleDegrees(): Int {
         try {
             val cameraIds =
                 Preconditions.checkNotNull(
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeResolver.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeResolver.kt
index 3e4e55a..7e96cf4 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeResolver.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/DynamicRangeResolver.kt
@@ -11,7 +11,7 @@
 import androidx.camera.core.impl.UseCaseConfig
 import androidx.core.util.Preconditions
 
-class DynamicRangeResolver(val cameraMetadata: CameraMetadata) {
+public class DynamicRangeResolver(public val cameraMetadata: CameraMetadata) {
     private val is10BitSupported: Boolean
     private val dynamicRangesInfo: DynamicRangeProfilesCompat
 
@@ -26,7 +26,7 @@
     }
 
     /** Returns whether 10-bit dynamic ranges are supported on this device. */
-    fun is10BitDynamicRangeSupported(): Boolean = is10BitSupported
+    public fun is10BitDynamicRangeSupported(): Boolean = is10BitSupported
 
     /**
      * Returns a set of supported dynamic ranges for the dynamic ranges requested by the list of
@@ -35,7 +35,7 @@
      * If a new use case requests a dynamic range that isn't supported, an IllegalArgumentException
      * will be thrown.
      */
-    fun resolveAndValidateDynamicRanges(
+    public fun resolveAndValidateDynamicRanges(
         existingSurfaces: List<AttachedSurfaceInfo>,
         newUseCaseConfigs: List<UseCaseConfig<*>>,
         useCasePriorityOrder: List<Int>
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseUtil.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseUtil.kt
index f964eea..71533f3 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseUtil.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/StreamUseCaseUtil.kt
@@ -48,10 +48,10 @@
 import androidx.camera.core.streamsharing.StreamSharingConfig
 import androidx.core.util.Preconditions.checkState
 
-object StreamUseCaseUtil {
+public object StreamUseCaseUtil {
 
     @VisibleForTesting
-    val STREAM_USE_CASE_STREAM_SPEC_OPTION: Config.Option<Long> =
+    public val STREAM_USE_CASE_STREAM_SPEC_OPTION: Config.Option<Long> =
         Config.Option.create("camera2.streamSpec.streamUseCase", Long::class.javaPrimitiveType!!)
     private val STREAM_USE_CASE_TO_ELIGIBLE_CAPTURE_TYPES_MAP: Map<Long, Set<CaptureType>> =
         buildMap {
@@ -97,7 +97,7 @@
      * @param sessionConfigs collection of all session configs for this capture session
      * @param streamUseCaseMap the mapping between surfaces and Stream Use Case flag
      */
-    fun populateSurfaceToStreamUseCaseMapping(
+    public fun populateSurfaceToStreamUseCaseMapping(
         sessionConfigs: Collection<SessionConfig>,
         useCaseConfigs: Collection<UseCaseConfig<*>>,
         streamUseCaseMap: MutableMap<DeferrableSurface, Long>
@@ -162,7 +162,9 @@
      * Populate all implementation options needed to determine the StreamUseCase option in the
      * StreamSpec for this UseCaseConfig
      */
-    fun getStreamSpecImplementationOptions(useCaseConfig: UseCaseConfig<*>): Camera2ImplConfig {
+    public fun getStreamSpecImplementationOptions(
+        useCaseConfig: UseCaseConfig<*>
+    ): Camera2ImplConfig {
         val optionsBundle = MutableOptionsBundle.create()
         if (useCaseConfig.containsOption(STREAM_USE_CASE_OPTION)) {
             optionsBundle.insertOption(
@@ -192,7 +194,7 @@
     }
 
     /** Return true if the given camera characteristics support stream use case */
-    fun isStreamUseCaseSupported(cameraMetadata: CameraMetadata): Boolean {
+    public fun isStreamUseCaseSupported(cameraMetadata: CameraMetadata): Boolean {
         if (Build.VERSION.SDK_INT < 33) {
             return false
         }
@@ -202,7 +204,7 @@
     }
 
     /** Return true if the given feature settings is appropriate for stream use case usage. */
-    fun shouldUseStreamUseCase(
+    public fun shouldUseStreamUseCase(
         featureSettings: SupportedSurfaceCombination.FeatureSettings
     ): Boolean {
         return (featureSettings.cameraMode == CameraMode.DEFAULT &&
@@ -223,7 +225,7 @@
      *   whose StreamSpecs needs to be updated
      * @return true if StreamSpec options are populated. False if not.
      */
-    fun populateStreamUseCaseStreamSpecOptionWithInteropOverride(
+    public fun populateStreamUseCaseStreamSpecOptionWithInteropOverride(
         cameraMetadata: CameraMetadata,
         attachedSurfaces: List<AttachedSurfaceInfo>,
         suggestedStreamSpecMap: MutableMap<UseCaseConfig<*>, StreamSpec>,
@@ -290,7 +292,7 @@
      * Return true if the stream use cases in the given surface configurations are available for the
      * device.
      */
-    fun areStreamUseCasesAvailableForSurfaceConfigs(
+    public fun areStreamUseCasesAvailableForSurfaceConfigs(
         cameraMetadata: CameraMetadata,
         surfaceConfigs: List<SurfaceConfig>
     ): Boolean {
@@ -364,7 +366,7 @@
      * @param surfaceConfigsWithStreamUseCase the supported surfaceConfigs that contains accurate
      *   streamUseCases
      */
-    fun areCaptureTypesEligible(
+    public fun areCaptureTypesEligible(
         surfaceConfigIndexAttachedSurfaceInfoMap: Map<Int, AttachedSurfaceInfo?>,
         surfaceConfigIndexUseCaseConfigMap: Map<Int, UseCaseConfig<*>>,
         surfaceConfigsWithStreamUseCase: List<SurfaceConfig>
@@ -416,7 +418,7 @@
      * @param surfaceConfigsWithStreamUseCase the supported surfaceConfigs that contains accurate
      *   streamUseCases
      */
-    fun populateStreamUseCaseStreamSpecOptionWithSupportedSurfaceConfigs(
+    public fun populateStreamUseCaseStreamSpecOptionWithSupportedSurfaceConfigs(
         suggestedStreamSpecMap: MutableMap<UseCaseConfig<*>, StreamSpec>,
         attachedSurfaceStreamSpecMap: MutableMap<AttachedSurfaceInfo, StreamSpec>,
         surfaceConfigIndexAttachedSurfaceInfoMap: Map<Int, AttachedSurfaceInfo>,
@@ -482,7 +484,7 @@
     }
 
     /** Return true if any one of the existing or new UseCases is ZSL. */
-    fun containsZslUseCase(
+    public fun containsZslUseCase(
         attachedSurfaces: List<AttachedSurfaceInfo>,
         newUseCaseConfigs: List<UseCaseConfig<*>>
     ): Boolean {
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/ZoomMath.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/ZoomMath.kt
index 2839cd7..f255be0 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/ZoomMath.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/ZoomMath.kt
@@ -32,8 +32,8 @@
  * cropWidth = 1000 As observed, zoomRatio = 5.5f does not yield cropWidth = 5500 which would be the
  * actual zooming amount middle point.
  */
-object ZoomMath {
-    fun getLinearZoomFromZoomRatio(
+public object ZoomMath {
+    public fun getLinearZoomFromZoomRatio(
         zoomRatio: Float,
         minZoomRatio: Float,
         maxZoomRatio: Float
@@ -71,7 +71,7 @@
         return MathUtils.clamp(linearZoom, 0f, 1.0f)
     }
 
-    fun getZoomRatioFromLinearZoom(
+    public fun getZoomRatioFromLinearZoom(
         linearZoom: Float,
         minZoomRatio: Float,
         maxZoomRatio: Float
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt
index 8327798..4a53c52 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraControl.kt
@@ -45,7 +45,7 @@
  */
 @SuppressWarnings("HiddenSuperclass")
 @ExperimentalCamera2Interop
-class Camera2CameraControl
+public class Camera2CameraControl
 private constructor(
     private val compat: Camera2CameraControlCompat,
     private val threads: UseCaseThreads,
@@ -53,7 +53,7 @@
 ) : UseCaseCameraControl {
 
     private var _useCaseCamera: UseCaseCamera? = null
-    override var useCaseCamera
+    override var useCaseCamera: UseCaseCamera?
         @RestrictTo(RestrictTo.Scope.LIBRARY) get() = _useCaseCamera
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         set(value) {
@@ -91,7 +91,7 @@
      *   is a no-op.
      */
     @SuppressWarnings("AsyncSuffixFuture")
-    fun setCaptureRequestOptions(bundle: CaptureRequestOptions): ListenableFuture<Void?> {
+    public fun setCaptureRequestOptions(bundle: CaptureRequestOptions): ListenableFuture<Void?> {
         compat.clearRequestOption()
         compat.addRequestOption(bundle)
         return updateAsync("setCaptureRequestOptions")
@@ -115,7 +115,7 @@
      *   or camera is closed before the current request completes.
      */
     @SuppressWarnings("AsyncSuffixFuture")
-    fun addCaptureRequestOptions(bundle: CaptureRequestOptions): ListenableFuture<Void?> {
+    public fun addCaptureRequestOptions(bundle: CaptureRequestOptions): ListenableFuture<Void?> {
         compat.addRequestOption(bundle)
         return updateAsync("addCaptureRequestOptions")
     }
@@ -128,7 +128,7 @@
      *
      * @return The [CaptureRequestOptions].
      */
-    fun getCaptureRequestOptions(): CaptureRequestOptions = compat.getRequestOption()
+    public fun getCaptureRequestOptions(): CaptureRequestOptions = compat.getRequestOption()
 
     /**
      * Clears all capture request options that is currently applied by the [Camera2CameraControl].
@@ -139,7 +139,7 @@
      *   or camera is closed before the current request completes.
      */
     @SuppressWarnings("AsyncSuffixFuture")
-    fun clearCaptureRequestOptions(): ListenableFuture<Void?> {
+    public fun clearCaptureRequestOptions(): ListenableFuture<Void?> {
         compat.clearRequestOption()
         return updateAsync("clearCaptureRequestOptions")
     }
@@ -151,7 +151,7 @@
                 .asListenableFuture(tag)
         )
 
-    companion object {
+    public companion object {
 
         /**
          * Gets the [Camera2CameraControl] from a [CameraControl].
@@ -170,7 +170,7 @@
          *   [androidx.camera.camera2.pipe.integration.CameraPipeConfig]).
          */
         @JvmStatic
-        fun from(cameraControl: CameraControl): Camera2CameraControl {
+        public fun from(cameraControl: CameraControl): Camera2CameraControl {
             var cameraControlImpl = (cameraControl as CameraControlInternal).implementation
             Preconditions.checkArgument(
                 cameraControlImpl is CameraControlAdapter,
@@ -182,7 +182,7 @@
         /** This is the workaround to prevent constructor from being added to public API. */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
-        fun create(
+        public fun create(
             compat: Camera2CameraControlCompat,
             threads: UseCaseThreads,
             requestListener: ComboRequestListener,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
index 48bbef7..6a6c5ce 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2CameraInfo.kt
@@ -25,7 +25,7 @@
 
 /** An interface for retrieving Camera2-related camera information. */
 @ExperimentalCamera2Interop
-class Camera2CameraInfo
+public class Camera2CameraInfo
 private constructor(
     private val cameraProperties: CameraProperties,
 ) {
@@ -40,7 +40,7 @@
      * @param key The [CameraCharacteristics.Key] of the characteristic.
      * @return the value of the characteristic. </T>
      */
-    fun <T> getCameraCharacteristic(key: CameraCharacteristics.Key<T>): T? {
+    public fun <T> getCameraCharacteristic(key: CameraCharacteristics.Key<T>): T? {
         return cameraProperties.metadata.getSafely(key)
     }
 
@@ -60,9 +60,9 @@
      * @throws IllegalStateException if the camera info does not contain the camera 2 camera ID
      *   (e.g., if CameraX was not initialized with a [androidx.camera.camera2.Camera2Config]).
      */
-    fun getCameraId(): String = cameraProperties.cameraId.value
+    public fun getCameraId(): String = cameraProperties.cameraId.value
 
-    companion object {
+    public companion object {
 
         /**
          * Gets the [Camera2CameraInfo] from a [CameraInfo].
@@ -74,7 +74,7 @@
          *   [androidx.camera.camera2.Camera2Config]).
          */
         @JvmStatic
-        fun from(cameraInfo: CameraInfo): Camera2CameraInfo {
+        public fun from(cameraInfo: CameraInfo): Camera2CameraInfo {
             val camera2CameraInfo = cameraInfo.unwrapAs(Camera2CameraInfo::class)
             requireNotNull(camera2CameraInfo) {
                 "Could not unwrap $cameraInfo as Camera2CameraInfo!"
@@ -85,6 +85,7 @@
         /** This is the workaround to prevent constructor from being added to public API. */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
         @JvmStatic
-        fun create(cameraProperties: CameraProperties) = Camera2CameraInfo(cameraProperties)
+        public fun create(cameraProperties: CameraProperties): Camera2CameraInfo =
+            Camera2CameraInfo(cameraProperties)
     }
 }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2Interop.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2Interop.kt
index 2bda6d1..a548dff 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2Interop.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/Camera2Interop.kt
@@ -38,7 +38,7 @@
  * @constructor Private constructor to ensure this class isn't instantiated.
  */
 @ExperimentalCamera2Interop
-class Camera2Interop private constructor() {
+public class Camera2Interop private constructor() {
 
     /**
      * Extends a [ExtendableBuilder] to add Camera2 options.
@@ -47,7 +47,7 @@
      * @constructor Creates an Extender that can be used to add Camera2 options to another Builder.
      * @property baseBuilder The builder being extended.
      */
-    class Extender<T>(private var baseBuilder: ExtendableBuilder<T>) {
+    public class Extender<T>(private var baseBuilder: ExtendableBuilder<T>) {
 
         /**
          * Sets a [CaptureRequest.Key] and Value on the configuration.
@@ -60,7 +60,7 @@
          * @param ValueT The type of the value.
          * @return The current Extender.
          */
-        fun <ValueT> setCaptureRequestOption(
+        public fun <ValueT> setCaptureRequestOption(
             key: CaptureRequest.Key<ValueT>,
             value: ValueT
         ): Extender<T> {
@@ -87,7 +87,7 @@
          * @return The current Extender.
          */
         @RestrictTo(RestrictTo.Scope.LIBRARY)
-        fun setCaptureRequestTemplate(templateType: Int): Extender<T> {
+        public fun setCaptureRequestTemplate(templateType: Int): Extender<T> {
             baseBuilder.mutableConfig.insertOption(TEMPLATE_TYPE_OPTION, templateType)
             return this
         }
@@ -111,7 +111,7 @@
          *   Camera2 framework uses this.
          */
         @RequiresApi(33)
-        fun setStreamUseCase(streamUseCase: Long): Extender<T> {
+        public fun setStreamUseCase(streamUseCase: Long): Extender<T> {
             baseBuilder.mutableConfig.insertOption(STREAM_USE_CASE_OPTION, streamUseCase)
             return this
         }
@@ -131,7 +131,7 @@
          * @return The current Extender.
          */
         @SuppressLint("ExecutorRegistration")
-        fun setDeviceStateCallback(stateCallback: CameraDevice.StateCallback): Extender<T> {
+        public fun setDeviceStateCallback(stateCallback: CameraDevice.StateCallback): Extender<T> {
             baseBuilder.mutableConfig.insertOption(DEVICE_STATE_CALLBACK_OPTION, stateCallback)
             return this
         }
@@ -152,7 +152,7 @@
          * @return The current Extender.
          */
         @SuppressLint("ExecutorRegistration")
-        fun setSessionStateCallback(
+        public fun setSessionStateCallback(
             stateCallback: CameraCaptureSession.StateCallback
         ): Extender<T> {
             baseBuilder.mutableConfig.insertOption(SESSION_STATE_CALLBACK_OPTION, stateCallback)
@@ -177,7 +177,7 @@
          * @return The current Extender.
          */
         @SuppressLint("ExecutorRegistration")
-        fun setSessionCaptureCallback(captureCallback: CaptureCallback): Extender<T> {
+        public fun setSessionCaptureCallback(captureCallback: CaptureCallback): Extender<T> {
             baseBuilder.mutableConfig.insertOption(SESSION_CAPTURE_CALLBACK_OPTION, captureCallback)
             return this
         }
@@ -203,7 +203,9 @@
          * @return The current Extender.
          */
         @RequiresApi(28)
-        fun setPhysicalCameraId(@Suppress("UNUSED_PARAMETER") cameraId: String): Extender<T> {
+        public fun setPhysicalCameraId(
+            @Suppress("UNUSED_PARAMETER") cameraId: String
+        ): Extender<T> {
             baseBuilder.mutableConfig.insertOption(SESSION_PHYSICAL_CAMERA_ID_OPTION, cameraId)
             return this
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/CaptureRequestOptions.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/CaptureRequestOptions.kt
index ff6159a..7dc6976 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/CaptureRequestOptions.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/CaptureRequestOptions.kt
@@ -34,7 +34,7 @@
  * @property config The config that potentially contains Camera2 capture request options.
  */
 @ExperimentalCamera2Interop
-open class CaptureRequestOptions
+public open class CaptureRequestOptions
 private constructor(private val config: Config, @Suppress("UNUSED_PARAMETER") unused: Boolean) :
     ReadableConfig {
 
@@ -47,7 +47,7 @@
      * @param ValueT The type of the value.
      * @return The stored value or null if the value does not exist in this configuration.
      */
-    open fun <ValueT> getCaptureRequestOption(key: CaptureRequest.Key<ValueT>): ValueT? {
+    public open fun <ValueT> getCaptureRequestOption(key: CaptureRequest.Key<ValueT>): ValueT? {
         // Type should have been only set via Builder#setCaptureRequestOption()
         @Suppress("UNCHECKED_CAST")
         val opt = key.createCaptureRequestOption() as Config.Option<ValueT>
@@ -80,10 +80,10 @@
     }
 
     /** Builder for creating [CaptureRequestOptions] instance. */
-    class Builder : ExtendableBuilder<CaptureRequestOptions?> {
+    public class Builder : ExtendableBuilder<CaptureRequestOptions?> {
         private val mutableOptionsBundle = MutableOptionsBundle.create()
 
-        companion object {
+        public companion object {
             /**
              * Generates a Builder from another Config object.
              *
@@ -92,7 +92,7 @@
              */
             @JvmStatic
             @RestrictTo(RestrictTo.Scope.LIBRARY)
-            fun from(config: Config): Builder {
+            public fun from(config: Config): Builder {
                 val bundleBuilder = Builder()
                 config.findOptions(CAPTURE_REQUEST_ID_STEM) {
                     // Erase the type of the option. Capture request options should only be
@@ -117,7 +117,7 @@
         }
 
         /** Inserts new capture request option with specific [CaptureRequest.Key] setting. */
-        fun <ValueT> setCaptureRequestOption(
+        public fun <ValueT> setCaptureRequestOption(
             key: CaptureRequest.Key<ValueT>,
             value: ValueT
         ): Builder {
@@ -127,7 +127,7 @@
         }
 
         /** Removes a capture request option with specific [CaptureRequest.Key] setting. */
-        fun <ValueT> clearCaptureRequestOption(key: CaptureRequest.Key<ValueT>): Builder {
+        public fun <ValueT> clearCaptureRequestOption(key: CaptureRequest.Key<ValueT>): Builder {
             val opt = key.createCaptureRequestOption()
             mutableOptionsBundle.removeOption(opt)
             return this
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/ExperimentalCamera2Interop.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/ExperimentalCamera2Interop.kt
index 02b233a..7d42603b5 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/ExperimentalCamera2Interop.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/interop/ExperimentalCamera2Interop.kt
@@ -31,4 +31,6 @@
  *
  * These will be changed in future release possibly, hence add @Experimental annotation.
  */
-@Retention(AnnotationRetention.BINARY) @RequiresOptIn annotation class ExperimentalCamera2Interop
+@Retention(AnnotationRetention.BINARY)
+@RequiresOptIn
+public annotation class ExperimentalCamera2Interop
diff --git a/camera/camera-camera2-pipe-testing/build.gradle b/camera/camera-camera2-pipe-testing/build.gradle
index 6ca4bf4..9dfa1cd 100644
--- a/camera/camera-camera2-pipe-testing/build.gradle
+++ b/camera/camera-camera2-pipe-testing/build.gradle
@@ -73,6 +73,5 @@
     description = "Testing components for the Camera2 Pipe Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +
             "experiences across all of Android."
-    legacyDisableKotlinStrictApiMode = true
     doNotDocumentReason = "Not shipped externally"
 }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt
index 3450164..cdee6ee 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraControllerSimulator.kt
@@ -40,7 +40,7 @@
  * actively submit requests. This mirrors the underlying behavior of an actual Camera, which may
  * take time to configure and become ready.
  */
-class CameraControllerSimulator(
+public class CameraControllerSimulator(
     cameraContext: CameraContext,
     private val graphId: CameraGraphId,
     private val graphConfig: CameraGraph.Config,
@@ -53,27 +53,27 @@
     override val cameraGraphId: CameraGraphId
         get() = graphId
 
-    override var isForeground = false
+    override var isForeground: Boolean = false
 
     private val lock = Any()
     private var currentSurfaceMap: Map<StreamId, Surface> = emptyMap()
     private var currentGraphRequestProcessor: GraphRequestProcessor? = null
 
     private var _closed = false
-    var closed: Boolean
+    public var closed: Boolean
         get() = _closed
         private set(value) {
             _closed = value
         }
 
     private var _started = false
-    var started: Boolean
+    public var started: Boolean
         get() = _started
         private set(value) {
             _started = value
         }
 
-    var currentCaptureSequenceProcessor: FakeCaptureSequenceProcessor? = null
+    public var currentCaptureSequenceProcessor: FakeCaptureSequenceProcessor? = null
         private set
 
     init {
@@ -89,7 +89,7 @@
         }
     }
 
-    fun simulateCameraStarted() {
+    public fun simulateCameraStarted() {
         synchronized(lock) {
             check(!closed) {
                 "Attempted to invoke simulateStarted after the CameraController was closed."
@@ -106,7 +106,7 @@
         }
     }
 
-    fun simulateCameraStopped() {
+    public fun simulateCameraStopped() {
         synchronized(lock) {
             check(!closed) {
                 "Attempted to invoke simulateCameraStopped after the CameraController was closed."
@@ -123,7 +123,7 @@
         }
     }
 
-    fun simulateCameraModified() {
+    public fun simulateCameraModified() {
         synchronized(lock) {
             val captureSequenceProcessor = currentCaptureSequenceProcessor
             val graphRequestProcessor = currentGraphRequestProcessor
@@ -137,7 +137,7 @@
         }
     }
 
-    fun simulateCameraError(graphStateError: GraphStateError) {
+    public fun simulateCameraError(graphStateError: GraphStateError) {
         synchronized(lock) {
             check(!closed) {
                 "Attempted to invoke simulateCameraError after the CameraController was closed."
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
index 001ba21..63a3e39 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
@@ -55,21 +55,21 @@
  * of a [CameraGraph]. Tests using CameraGraphSimulators should also close them after they've
  * completed their use of the simulator.
  */
-class CameraGraphSimulator
+public class CameraGraphSimulator
 internal constructor(
     private val cameraMetadata: CameraMetadata,
     private val cameraController: CameraControllerSimulator,
     private val fakeImageReaders: FakeImageReaders,
     private val fakeImageSources: FakeImageSources,
     private val realCameraGraph: CameraGraph,
-    val config: CameraGraph.Config,
+    public val config: CameraGraph.Config,
 ) : CameraGraph by realCameraGraph, AutoCloseable {
 
     @Deprecated("CameraGraphSimulator directly implements CameraGraph")
-    val cameraGraph: CameraGraph
+    public val cameraGraph: CameraGraph
         get() = this
 
-    companion object {
+    public companion object {
         /**
          * Create a CameraGraphSimulator using the current [TestScope] provided by a Kotlin
          * `runTest` block. This will create the [CameraPipe] and [CameraGraph] using the parent
@@ -77,7 +77,7 @@
          * test completes and allows the test to provide more fine grained control over the
          * interactions.
          */
-        fun create(
+        public fun create(
             testScope: TestScope,
             testContext: Context,
             cameraMetadata: CameraMetadata,
@@ -105,7 +105,7 @@
     private val fakeSurfaces = FakeSurfaces()
 
     /** Return true if this [CameraGraphSimulator] has been closed. */
-    val isClosed: Boolean
+    public val isClosed: Boolean
         get() = closed.value
 
     override fun close() {
@@ -115,22 +115,22 @@
         }
     }
 
-    fun simulateCameraStarted() {
+    public fun simulateCameraStarted() {
         check(!closed.value) { "Cannot call simulateCameraStarted on $this after close." }
         cameraController.simulateCameraStarted()
     }
 
-    fun simulateCameraStopped() {
+    public fun simulateCameraStopped() {
         check(!closed.value) { "Cannot call simulateCameraStopped on $this after close." }
         cameraController.simulateCameraStopped()
     }
 
-    fun simulateCameraModified() {
+    public fun simulateCameraModified() {
         check(!closed.value) { "Cannot call simulateCameraModified on $this after close." }
         cameraController.simulateCameraModified()
     }
 
-    fun simulateCameraError(graphStateError: GraphStateError) {
+    public fun simulateCameraError(graphStateError: GraphStateError) {
         check(!closed.value) { "Cannot call simulateCameraError on $this after close." }
         cameraController.simulateCameraError(graphStateError)
     }
@@ -139,7 +139,7 @@
      * Configure all streams in the CameraGraph with fake surfaces that match the size of the first
      * output stream.
      */
-    fun initializeSurfaces() {
+    public fun initializeSurfaces() {
         check(!closed.value) {
             "Cannot call simulateFakeSurfaceConfiguration on $this after close."
         }
@@ -166,7 +166,7 @@
         }
     }
 
-    suspend fun simulateNextFrame(
+    public suspend fun simulateNextFrame(
         advanceClockByNanos: Long = 33_366_666 // (2_000_000_000 / (60  / 1.001))
     ): FrameSimulator =
         generateNextFrame().also {
@@ -196,7 +196,7 @@
     }
 
     /** Utility function to simulate the production of a [FakeImage]s for one or more streams. */
-    fun simulateImage(
+    public fun simulateImage(
         streamId: StreamId,
         imageTimestamp: Long,
         outputId: OutputId? = null,
@@ -212,7 +212,11 @@
      * [physicalCameraId] should be used to select the correct output id when simulating images from
      * multi-resolution [ImageReader]s and [ImageSource]s
      */
-    fun simulateImages(request: Request, imageTimestamp: Long, physicalCameraId: CameraId? = null) {
+    public fun simulateImages(
+        request: Request,
+        imageTimestamp: Long,
+        physicalCameraId: CameraId? = null
+    ) {
         var imageSimulated = false
         for (streamId in request.streams) {
             val outputId =
@@ -265,17 +269,17 @@
      * each frame, and allows tests to simulate unusual ordering or delays that may appear under
      * real conditions.
      */
-    inner class FrameSimulator
+    public inner class FrameSimulator
     internal constructor(
-        val request: Request,
-        val requestSequence: FakeCaptureSequence,
+        public val request: Request,
+        public val requestSequence: FakeCaptureSequence,
     ) {
         private val requestMetadata = requestSequence.requestMetadata[request]!!
 
-        val frameNumber: FrameNumber = FrameNumber(frameCounter.incrementAndGet())
-        var timestampNanos: Long? = null
+        public val frameNumber: FrameNumber = FrameNumber(frameCounter.incrementAndGet())
+        public var timestampNanos: Long? = null
 
-        fun simulateStarted(timestampNanos: Long) {
+        public fun simulateStarted(timestampNanos: Long) {
             this.timestampNanos = timestampNanos
 
             requestSequence.invokeOnRequest(requestMetadata) {
@@ -283,7 +287,7 @@
             }
         }
 
-        fun simulatePartialCaptureResult(
+        public fun simulatePartialCaptureResult(
             resultMetadata: Map<CaptureResult.Key<*>, Any?>,
             extraResultMetadata: Map<Metadata.Key<*>, Any?> = emptyMap(),
             extraMetadata: Map<*, Any?> = emptyMap<Any, Any>()
@@ -300,7 +304,7 @@
             }
         }
 
-        fun simulateTotalCaptureResult(
+        public fun simulateTotalCaptureResult(
             resultMetadata: Map<CaptureResult.Key<*>, Any?>,
             extraResultMetadata: Map<Metadata.Key<*>, Any?> = emptyMap(),
             extraMetadata: Map<*, Any?> = emptyMap<Any, Any>(),
@@ -324,7 +328,7 @@
             }
         }
 
-        fun simulateComplete(
+        public fun simulateComplete(
             resultMetadata: Map<CaptureResult.Key<*>, Any?>,
             extraResultMetadata: Map<Metadata.Key<*>, Any?> = emptyMap(),
             extraMetadata: Map<*, Any?> = emptyMap<Any, Any>(),
@@ -348,23 +352,23 @@
             }
         }
 
-        fun simulateFailure(requestFailure: RequestFailure) {
+        public fun simulateFailure(requestFailure: RequestFailure) {
             requestSequence.invokeOnRequest(requestMetadata) {
                 it.onFailed(requestMetadata, frameNumber, requestFailure)
             }
         }
 
-        fun simulateBufferLoss(streamId: StreamId) {
+        public fun simulateBufferLoss(streamId: StreamId) {
             requestSequence.invokeOnRequest(requestMetadata) {
                 it.onBufferLost(requestMetadata, frameNumber, streamId)
             }
         }
 
-        fun simulateAbort() {
+        public fun simulateAbort() {
             requestSequence.invokeOnRequest(requestMetadata) { it.onAborted(request) }
         }
 
-        fun simulateImage(
+        public fun simulateImage(
             streamId: StreamId,
             imageTimestamp: Long? = null,
             outputId: OutputId? = null,
@@ -381,7 +385,10 @@
          * Utility function to simulate the production of [FakeImage]s for all outputs on a Frame.
          * Use [simulateImage] to directly control simulation of each individual image.
          */
-        fun simulateImages(imageTimestamp: Long? = null, physicalCameraId: CameraId? = null) {
+        public fun simulateImages(
+            imageTimestamp: Long? = null,
+            physicalCameraId: CameraId? = null
+        ) {
             val timestamp = imageTimestamp ?: timestampNanos
             checkNotNull(timestamp) {
                 "Cannot simulate an image without a timestamp! Provide an " +
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulator.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulator.kt
index 89c2da0..5f650ee 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulator.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraPipeSimulator.kt
@@ -36,17 +36,17 @@
  * test complicated interactions with [CameraPipe] and [CameraGraph] and to simulate how code
  * responds to a range of behaviors by the underlying camera within unit tests.
  */
-class CameraPipeSimulator
+public class CameraPipeSimulator
 private constructor(
     private val cameraPipeInternal: CameraPipe,
     private val fakeCameraBackend: FakeCameraBackend,
-    val fakeSurfaces: FakeSurfaces,
-    val fakeImageReaders: FakeImageReaders,
-    val fakeImageSources: FakeImageSources,
+    public val fakeSurfaces: FakeSurfaces,
+    public val fakeImageReaders: FakeImageReaders,
+    public val fakeImageSources: FakeImageSources,
 ) : CameraPipe, AutoCloseable {
     private val closed = atomic(false)
     private val _cameraGraphs = mutableListOf<CameraGraphSimulator>()
-    val cameraGraphs: List<CameraGraphSimulator>
+    public val cameraGraphs: List<CameraGraphSimulator>
         get() = _cameraGraphs
 
     override fun create(config: CameraGraph.Config): CameraGraphSimulator {
@@ -91,7 +91,7 @@
         }
 
     /** Directly create and return a new [CameraGraph] and [CameraGraphSimulator]. */
-    fun createCameraGraphSimulator(graphConfig: CameraGraph.Config): CameraGraphSimulator {
+    public fun createCameraGraphSimulator(graphConfig: CameraGraph.Config): CameraGraphSimulator {
         check(!closed.value) { "Cannot interact with CameraPipeSimulator after close!" }
         val cameraGraph = cameraPipeInternal.create(graphConfig)
         val cameraController =
@@ -103,7 +103,7 @@
     }
 
     /** Directly create and return a new set of [CameraGraph]s and [CameraGraphSimulator]s. */
-    fun createCameraGraphSimulators(
+    public fun createCameraGraphSimulators(
         config: CameraGraph.ConcurrentConfig
     ): List<CameraGraphSimulator> = config.graphConfigs.map { createCameraGraphSimulator(it) }
 
@@ -129,17 +129,17 @@
         return cameraGraphSimulator
     }
 
-    fun checkImageReadersClosed() {
+    public fun checkImageReadersClosed() {
         fakeImageSources.checkImageSourcesClosed()
         fakeImageReaders.checkImageReadersClosed()
     }
 
-    fun checkImagesClosed() {
+    public fun checkImagesClosed() {
         fakeImageSources.checkImagesClosed()
         fakeImageReaders.checkImagesClosed()
     }
 
-    fun checkCameraGraphsClosed() {
+    public fun checkCameraGraphsClosed() {
         for (cameraGraph in _cameraGraphs) {
             check(cameraGraph.isClosed) { "$cameraGraph was not closed!" }
         }
@@ -155,8 +155,8 @@
         return "CameraPipeSimulator($cameraPipeInternal)"
     }
 
-    companion object {
-        fun create(
+    public companion object {
+        public fun create(
             testScope: TestScope,
             testContext: Context,
             fakeCameras: List<CameraMetadata> = listOf(FakeCameraMetadata())
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt
index c5f8d26..12ecb03 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraBackend.kt
@@ -33,12 +33,13 @@
 import kotlinx.coroutines.flow.MutableSharedFlow
 
 /** The FakeCameraBackend implements [CameraBackend] and creates [CameraControllerSimulator]s. */
-class FakeCameraBackend(private val fakeCameras: Map<CameraId, CameraMetadata>) : CameraBackend {
+public class FakeCameraBackend(private val fakeCameras: Map<CameraId, CameraMetadata>) :
+    CameraBackend {
     private val lock = Any()
     private val fakeCameraIds = fakeCameras.keys.toList()
 
     private val _cameraControllers = mutableListOf<CameraControllerSimulator>()
-    val cameraControllers: List<CameraControllerSimulator>
+    public val cameraControllers: List<CameraControllerSimulator>
         get() = synchronized(lock) { _cameraControllers.toList() }
 
     override val id: CameraBackendId
@@ -99,8 +100,8 @@
         _cameraControllers.forEach { it.simulateCameraStopped() }
     }
 
-    companion object {
-        val FAKE_CAMERA_BACKEND_ID =
+    public companion object {
+        public val FAKE_CAMERA_BACKEND_ID: CameraBackendId =
             CameraBackendId("androidx.camera.camera2.pipe.testing.FakeCameraBackend")
     }
 }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraDevices.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraDevices.kt
index cb1bc6a..9c0c611 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraDevices.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraDevices.kt
@@ -26,7 +26,7 @@
 /**
  * This provides a fake implementation of [CameraDevices] for tests with a fixed list of Cameras.
  */
-class FakeCameraDevices(
+public class FakeCameraDevices(
     private val defaultCameraBackendId: CameraBackendId,
     private val concurrentCameraBackendIds: Set<Set<CameraBackendId>>,
     private val cameraMetadataMap: Map<CameraBackendId, List<CameraMetadata>>
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraMetadata.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraMetadata.kt
index 359a02d..1280187 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraMetadata.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCameraMetadata.kt
@@ -21,20 +21,11 @@
 import android.hardware.camera2.CameraCharacteristics
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.CaptureResult
-import android.hardware.camera2.TotalCaptureResult
-import android.view.Surface
+import android.util.Size
 import androidx.camera.camera2.pipe.CameraExtensionMetadata
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
-import androidx.camera.camera2.pipe.FrameInfo
-import androidx.camera.camera2.pipe.FrameMetadata
-import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.Metadata
-import androidx.camera.camera2.pipe.Request
-import androidx.camera.camera2.pipe.RequestMetadata
-import androidx.camera.camera2.pipe.RequestNumber
-import androidx.camera.camera2.pipe.RequestTemplate
-import androidx.camera.camera2.pipe.StreamId
 import kotlin.reflect.KClass
 import kotlinx.atomicfu.atomic
 
@@ -43,21 +34,14 @@
 internal fun nextFakeCameraId(): CameraId =
     CameraId("FakeCamera-${fakeCameraIds.incrementAndGet()}")
 
-private val fakeRequestNumbers = atomic(0L)
-
-internal fun nextFakeRequestNumber(): RequestNumber =
-    RequestNumber(fakeRequestNumbers.incrementAndGet())
-
-private val fakeFrameNumbers = atomic(0L)
-
-internal fun nextFakeFrameNumber(): FrameNumber = FrameNumber(fakeFrameNumbers.incrementAndGet())
-
 /** Utility class for interacting with objects that require pre-populated Metadata. */
-open class FakeMetadata(private val metadata: Map<Metadata.Key<*>, Any?> = emptyMap()) : Metadata {
-    companion object {
-        @JvmField val TEST_KEY: Metadata.Key<Int> = Metadata.Key.create("test.key")
+public open class FakeMetadata(private val metadata: Map<Metadata.Key<*>, Any?> = emptyMap()) :
+    Metadata {
+    public companion object {
+        @JvmField public val TEST_KEY: Metadata.Key<Int> = Metadata.Key.create("test.key")
 
-        @JvmField val TEST_KEY_ABSENT: Metadata.Key<Int> = Metadata.Key.create("test.key.absent")
+        @JvmField
+        public val TEST_KEY_ABSENT: Metadata.Key<Int> = Metadata.Key.create("test.key.absent")
     }
 
     override fun <T> get(key: Metadata.Key<T>): T? = metadata[key] as T?
@@ -69,7 +53,7 @@
 }
 
 /** Utility class for interacting with objects require specific [CameraCharacteristics] metadata. */
-class FakeCameraMetadata(
+public class FakeCameraMetadata(
     private val characteristics: Map<CameraCharacteristics.Key<*>, Any?> = emptyMap(),
     metadata: Map<Metadata.Key<*>, Any?> = emptyMap(),
     cameraId: CameraId = nextFakeCameraId(),
@@ -77,9 +61,9 @@
     override val requestKeys: Set<CaptureRequest.Key<*>> = emptySet(),
     override val resultKeys: Set<CaptureResult.Key<*>> = emptySet(),
     override val sessionKeys: Set<CaptureRequest.Key<*>> = emptySet(),
-    val physicalMetadata: Map<CameraId, CameraMetadata> = emptyMap(),
+    public val physicalMetadata: Map<CameraId, CameraMetadata> = emptyMap(),
     override val physicalRequestKeys: Set<CaptureRequest.Key<*>> = emptySet(),
-    override val supportedExtensions: Set<Int> = emptySet(),
+    private val extensions: Map<Int, FakeCameraExtensionMetadata> = emptyMap(),
 ) : FakeMetadata(metadata), CameraMetadata {
 
     override fun <T> get(key: CameraCharacteristics.Key<T>): T? = characteristics[key] as T?
@@ -91,6 +75,8 @@
     override val isRedacted: Boolean = false
 
     override val physicalCameraIds: Set<CameraId> = physicalMetadata.keys
+    override val supportedExtensions: Set<Int>
+        get() = extensions.keys
 
     override suspend fun getPhysicalMetadata(cameraId: CameraId): CameraMetadata =
         physicalMetadata[cameraId]!!
@@ -99,11 +85,11 @@
         physicalMetadata[cameraId]!!
 
     override suspend fun getExtensionMetadata(extension: Int): CameraExtensionMetadata {
-        TODO("b/299356087 - Add support for fake extension metadata")
+        return extensions[extension]!!
     }
 
     override fun awaitExtensionMetadata(extension: Int): CameraExtensionMetadata {
-        TODO("b/299356087 - Add support for fake extension metadata")
+        return extensions[extension]!!
     }
 
     override fun <T : Any> unwrapAs(type: KClass<T>): T? = null
@@ -111,81 +97,43 @@
     override fun toString(): String = "FakeCameraMetadata(camera: ${camera.value})"
 }
 
-/** Utility class for interacting with objects require specific [CaptureRequest] metadata. */
-class FakeRequestMetadata(
-    private val requestParameters: Map<CaptureRequest.Key<*>, Any?> = emptyMap(),
+/** Utility class for interacting with objects require [CameraExtensionMetadata] */
+public class FakeCameraExtensionMetadata(
+    override val camera: CameraId,
+    override val cameraExtension: Int,
     metadata: Map<Metadata.Key<*>, Any?> = emptyMap(),
-    override val template: RequestTemplate = RequestTemplate(0),
-    override val streams: Map<StreamId, Surface> = mapOf(),
-    override val repeating: Boolean = false,
-    override val request: Request = Request(listOf()),
-    override val requestNumber: RequestNumber = nextFakeRequestNumber()
-) : FakeMetadata(request.extras.plus(metadata)), RequestMetadata {
-
-    override fun <T> get(key: CaptureRequest.Key<T>): T? = requestParameters[key] as T?
-
-    override fun <T> getOrDefault(key: CaptureRequest.Key<T>, default: T): T = get(key) ?: default
-
-    override fun <T : Any> unwrapAs(type: KClass<T>): T? = null
-
-    companion object {
-        /** Initialize FakeRequestMetadata based on a specific [Request] object. */
-        fun from(
-            request: Request,
-            streamToSurfaces: Map<StreamId, Surface>,
-            repeating: Boolean = false
-        ): FakeRequestMetadata {
-            check(streamToSurfaces.keys.containsAll(request.streams))
-            return FakeRequestMetadata(
-                requestParameters = request.parameters,
-                template = request.template ?: RequestTemplate(0),
-                streams = request.streams.map { it to streamToSurfaces[it]!! }.toMap(),
-                repeating = repeating,
-                request = request
-            )
-        }
+    private val characteristics: Map<CameraCharacteristics.Key<*>, Any?> = emptyMap(),
+    override val requestKeys: Set<CaptureRequest.Key<*>> = emptySet(),
+    override val resultKeys: Set<CaptureResult.Key<*>> = emptySet(),
+    private val captureOutputSizes: Map<Int, Set<Size>> = emptyMap(),
+    private val previewOutputSizes: Map<Class<*>, Set<Size>> = emptyMap(),
+    private val postviewSizes: Map<Int, Map<Size, Set<Size>>> = emptyMap(),
+    override val isRedacted: Boolean = false,
+    override val isPostviewSupported: Boolean = false,
+    override val isCaptureProgressSupported: Boolean = false
+) : FakeMetadata(metadata), CameraExtensionMetadata {
+    override fun getOutputSizes(imageFormat: Int): Set<Size> {
+        return captureOutputSizes[imageFormat] ?: emptySet()
     }
 
-    override fun toString(): String =
-        "FakeRequestMetadata(requestNumber: ${requestNumber.value}, request: $request)"
-}
+    override fun getOutputSizes(klass: Class<*>): Set<Size> {
+        return previewOutputSizes[klass] ?: emptySet()
+    }
 
-/** Utility class for interacting with objects require specific [CaptureResult] metadata */
-class FakeFrameMetadata(
-    private val resultMetadata: Map<CaptureResult.Key<*>, Any?> = emptyMap(),
-    extraResultMetadata: Map<Metadata.Key<*>, Any?> = emptyMap(),
-    override val camera: CameraId = nextFakeCameraId(),
-    override val frameNumber: FrameNumber = nextFakeFrameNumber(),
-    override val extraMetadata: Map<*, Any?> = emptyMap<Any, Any>()
-) : FakeMetadata(extraResultMetadata), FrameMetadata {
+    override fun getPostviewSizes(captureSize: Size, format: Int): Set<Size> {
+        return postviewSizes[format]?.get(captureSize) ?: emptySet()
+    }
 
-    override fun <T> get(key: CaptureResult.Key<T>): T? =
-        extraMetadata[key] as T? ?: resultMetadata[key] as T?
+    override fun <T> get(key: CameraCharacteristics.Key<T>): T? = characteristics[key] as T?
 
-    override fun <T> getOrDefault(key: CaptureResult.Key<T>, default: T): T = get(key) ?: default
+    override fun <T> getOrDefault(key: CameraCharacteristics.Key<T>, default: T): T =
+        get(key) ?: default
+
+    override val keys: Set<CameraCharacteristics.Key<*>>
+        get() = characteristics.keys
 
     override fun <T : Any> unwrapAs(type: KClass<T>): T? = null
 
     override fun toString(): String =
-        "FakeFrameMetadata(camera: ${camera.value}, frameNumber: ${frameNumber.value})"
-}
-
-/** Utility class for interacting with objects require specific [TotalCaptureResult] metadata */
-class FakeFrameInfo(
-    override val metadata: FrameMetadata = FakeFrameMetadata(),
-    override val requestMetadata: RequestMetadata = FakeRequestMetadata(),
-    private val physicalMetadata: Map<CameraId, FrameMetadata> = emptyMap()
-) : FrameInfo {
-    override fun get(camera: CameraId): FrameMetadata? = physicalMetadata[camera]
-
-    override val camera: CameraId
-        get() = metadata.camera
-
-    override val frameNumber: FrameNumber
-        get() = metadata.frameNumber
-
-    override fun <T : Any> unwrapAs(type: KClass<T>): T? = null
-
-    override fun toString(): String =
-        "FakeFrameInfo(camera: ${camera.value}, frameNumber: ${frameNumber.value})"
+        "FakeCameraExtensionMetadata(camera: ${camera.value}, extension: $cameraExtension)"
 }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequence.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequence.kt
index 3149a00..0d27ccf4 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequence.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequence.kt
@@ -24,7 +24,7 @@
 import androidx.camera.camera2.pipe.RequestMetadata
 
 /** A CaptureSequence used for testing interactions with a [FakeCaptureSequenceProcessor] */
-data class FakeCaptureSequence(
+public data class FakeCaptureSequence(
     override val repeating: Boolean,
     override val cameraId: CameraId,
     override val captureRequestList: List<Request>,
@@ -36,19 +36,20 @@
     override val sequenceListener: CaptureSequence.CaptureSequenceListener,
     override var sequenceNumber: Int,
 ) : CaptureSequence<Request> {
-    fun invokeOnSequenceCreated() = invokeOnRequests { requestMetadata, _, listener ->
+    public fun invokeOnSequenceCreated(): Unit = invokeOnRequests { requestMetadata, _, listener ->
         listener.onRequestSequenceCreated(requestMetadata)
     }
 
-    fun invokeOnSequenceSubmitted() = invokeOnRequests { requestMetadata, _, listener ->
-        listener.onRequestSequenceSubmitted(requestMetadata)
-    }
+    public fun invokeOnSequenceSubmitted(): Unit =
+        invokeOnRequests { requestMetadata, _, listener ->
+            listener.onRequestSequenceSubmitted(requestMetadata)
+        }
 
-    fun invokeOnSequenceAborted() = invokeOnRequests { requestMetadata, _, listener ->
+    public fun invokeOnSequenceAborted(): Unit = invokeOnRequests { requestMetadata, _, listener ->
         listener.onRequestSequenceAborted(requestMetadata)
     }
 
-    fun invokeOnSequenceCompleted(frameNumber: FrameNumber) =
+    public fun invokeOnSequenceCompleted(frameNumber: FrameNumber): Unit =
         invokeOnRequests { requestMetadata, _, listener ->
             listener.onRequestSequenceCompleted(requestMetadata, frameNumber)
         }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceListener.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceListener.kt
index 21f4a1f..f6041b8 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceListener.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceListener.kt
@@ -23,8 +23,8 @@
  * Fake [CaptureSequenceListener] instance that can be used to check to see if
  * [onCaptureSequenceComplete] has been invoked.
  */
-class FakeCaptureSequenceListener : CaptureSequenceListener {
-    var isComplete: Boolean = false
+public class FakeCaptureSequenceListener : CaptureSequenceListener {
+    public var isComplete: Boolean = false
         private set
 
     override fun onCaptureSequenceComplete(captureSequence: CaptureSequence<*>) {
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceProcessor.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceProcessor.kt
index 2647ab5..ff09344 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceProcessor.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeCaptureSequenceProcessor.kt
@@ -40,7 +40,7 @@
  * This allows kotlin tests to check sequences of interactions that dispatch in the background
  * without blocking between events.
  */
-class FakeCaptureSequenceProcessor(
+public class FakeCaptureSequenceProcessor(
     private val cameraId: CameraId = CameraId("test-camera"),
     private val defaultTemplate: RequestTemplate = RequestTemplate(1)
 ) : CaptureSequenceProcessor<Request, FakeCaptureSequence> {
@@ -57,14 +57,14 @@
 
     @GuardedBy("lock") private var _rejectRequests = false
 
-    var rejectRequests: Boolean
+    public var rejectRequests: Boolean
         get() = synchronized(lock) { _rejectRequests }
         set(value) {
             synchronized(lock) { _rejectRequests = value }
         }
 
     private var _surfaceMap: Map<StreamId, Surface> = emptyMap()
-    var surfaceMap: Map<StreamId, Surface>
+    public var surfaceMap: Map<StreamId, Surface>
         get() = synchronized(lock) { _surfaceMap }
         set(value) =
             synchronized(lock) {
@@ -147,10 +147,10 @@
     }
 
     /** Get the next event from queue with an option to specify a timeout for tests. */
-    suspend fun nextEvent(timeMillis: Long = 500): Event =
+    public suspend fun nextEvent(timeMillis: Long = 500): Event =
         withTimeout(timeMillis) { eventChannel.receive() }
 
-    suspend fun nextRequestSequence(): FakeCaptureSequence {
+    public suspend fun nextRequestSequence(): FakeCaptureSequence {
         while (true) {
             val pending: Deferred<FakeCaptureSequence>
             synchronized(lock) {
@@ -260,7 +260,7 @@
     }
 
     /** TODO: It's probably better to model this as a sealed class. */
-    data class Event(
+    public data class Event(
         val requestSequence: FakeCaptureSequence? = null,
         val rejected: Boolean = false,
         val abort: Boolean = false,
@@ -269,8 +269,8 @@
         val submit: Boolean = false
     )
 
-    companion object {
-        suspend fun FakeCaptureSequenceProcessor.awaitEvent(
+    public companion object {
+        public suspend fun FakeCaptureSequenceProcessor.awaitEvent(
             request: Request? = null,
             filter: (event: Event) -> Boolean
         ): Event {
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeFrameMetadata.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeFrameMetadata.kt
new file mode 100644
index 0000000..90465f2
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeFrameMetadata.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 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 androidx.camera.camera2.pipe.testing
+
+import android.hardware.camera2.CaptureResult
+import android.hardware.camera2.TotalCaptureResult
+import androidx.camera.camera2.pipe.CameraId
+import androidx.camera.camera2.pipe.FrameInfo
+import androidx.camera.camera2.pipe.FrameMetadata
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.Metadata
+import androidx.camera.camera2.pipe.RequestMetadata
+import kotlin.reflect.KClass
+import kotlinx.atomicfu.atomic
+
+private val fakeFrameNumbers = atomic(0L)
+
+internal fun nextFakeFrameNumber(): FrameNumber = FrameNumber(fakeFrameNumbers.incrementAndGet())
+
+/** Utility class for interacting with objects require specific [CaptureResult] metadata */
+public class FakeFrameMetadata(
+    private val resultMetadata: Map<CaptureResult.Key<*>, Any?> = emptyMap(),
+    extraResultMetadata: Map<Metadata.Key<*>, Any?> = emptyMap(),
+    override val camera: CameraId = nextFakeCameraId(),
+    override val frameNumber: FrameNumber = nextFakeFrameNumber(),
+    override val extraMetadata: Map<*, Any?> = emptyMap<Any, Any>()
+) : FakeMetadata(extraResultMetadata), FrameMetadata {
+
+    @Suppress("UNCHECKED_CAST")
+    override fun <T> get(key: CaptureResult.Key<T>): T? =
+        extraMetadata[key] as T? ?: resultMetadata[key] as T?
+
+    override fun <T> getOrDefault(key: CaptureResult.Key<T>, default: T): T = get(key) ?: default
+
+    override fun <T : Any> unwrapAs(type: KClass<T>): T? = null
+
+    override fun toString(): String =
+        "FakeFrameMetadata(camera: ${camera.value}, frameNumber: ${frameNumber.value})"
+}
+
+/** Utility class for interacting with objects require specific [TotalCaptureResult] metadata */
+public class FakeFrameInfo(
+    override val metadata: FrameMetadata = FakeFrameMetadata(),
+    override val requestMetadata: RequestMetadata = FakeRequestMetadata(),
+    private val physicalMetadata: Map<CameraId, FrameMetadata> = emptyMap()
+) : FrameInfo {
+    override fun get(camera: CameraId): FrameMetadata? = physicalMetadata[camera]
+
+    override val camera: CameraId
+        get() = metadata.camera
+
+    override val frameNumber: FrameNumber
+        get() = metadata.frameNumber
+
+    override fun <T : Any> unwrapAs(type: KClass<T>): T? = null
+
+    override fun toString(): String =
+        "FakeFrameInfo(camera: ${camera.value}, frameNumber: ${frameNumber.value})"
+}
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImage.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImage.kt
index ee4eb1a..8a01cac 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImage.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImage.kt
@@ -22,7 +22,7 @@
 import kotlinx.atomicfu.atomic
 
 /** FakeImage that can be used for testing classes that accept [ImageWrapper]. */
-class FakeImage(
+public class FakeImage(
     override val width: Int,
     override val height: Int,
     override val format: Int,
@@ -30,7 +30,7 @@
 ) : ImageWrapper {
     private val debugId = debugIds.incrementAndGet()
     private val closed = atomic(false)
-    val isClosed: Boolean
+    public val isClosed: Boolean
         get() = closed.value
 
     override val planes: List<ImagePlane>
@@ -49,7 +49,7 @@
 
     override fun toString(): String = "FakeImage-$debugId"
 
-    companion object {
+    public companion object {
         private val debugIds = atomic(0)
     }
 }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReader.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReader.kt
index 18e17d8..1b81e4b 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReader.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReader.kt
@@ -27,12 +27,12 @@
 import kotlinx.atomicfu.atomic
 
 /** Utility class for simulating [FakeImage] and testing code that uses an [ImageReaderWrapper]. */
-class FakeImageReader
+public class FakeImageReader
 private constructor(
     private val format: StreamFormat,
     override val capacity: Int,
     override val surface: Surface,
-    val streamId: StreamId,
+    public val streamId: StreamId,
     private val outputs: Map<OutputId, Size>
 ) : ImageReaderWrapper {
     private val debugId = debugIds.incrementAndGet()
@@ -43,17 +43,17 @@
     private val _images = mutableListOf<FakeImage>()
 
     /** Retrieve a list of every image that has been created from this FakeImageReader. */
-    val images: List<FakeImage>
+    public val images: List<FakeImage>
         get() = synchronized(lock) { _images.toMutableList() }
 
-    val isClosed: Boolean
+    public val isClosed: Boolean
         get() = closed.value
 
     /**
      * Simulate an image at a specific [imageTimestamp] for a particular (optional) [OutputId]. The
      * timebase for an imageReader is left undefined.
      */
-    fun simulateImage(imageTimestamp: Long, outputId: OutputId? = null): FakeImage {
+    public fun simulateImage(imageTimestamp: Long, outputId: OutputId? = null): FakeImage {
         val output = outputId ?: outputs.keys.single()
         val size =
             checkNotNull(outputs[output]) { "Unexpected $output! Available outputs are $outputs" }
@@ -66,7 +66,7 @@
      * Simulate an image using a specific [ImageWrapper] for the given outputId. The size must
      * match.
      */
-    fun simulateImage(image: ImageWrapper, outputId: OutputId) {
+    public fun simulateImage(image: ImageWrapper, outputId: OutputId) {
         val size =
             checkNotNull(outputs[outputId]) {
                 "Unexpected $outputId! Available outputs are $outputs"
@@ -104,7 +104,7 @@
     override fun toString(): String = "FakeImageReader-$debugId"
 
     /** [check] that all images produced by this [FakeImageReader] have been closed. */
-    fun checkImagesClosed() {
+    public fun checkImagesClosed() {
         for ((i, fakeImage) in images.withIndex()) {
             check(fakeImage.isClosed) {
                 "Failed to close image $i / ${images.size} $fakeImage from $this"
@@ -112,11 +112,11 @@
         }
     }
 
-    companion object {
+    public companion object {
         private val debugIds = atomic(0)
 
         /** Create a [FakeImageReader] that can simulate images. */
-        fun create(
+        public fun create(
             format: StreamFormat,
             streamId: StreamId,
             outputId: OutputId,
@@ -127,7 +127,7 @@
             create(format, streamId, mapOf(outputId to size), capacity, fakeSurfaces)
 
         /** Create a [FakeImageReader] that can simulate different sized images. */
-        fun create(
+        public fun create(
             format: StreamFormat,
             streamId: StreamId,
             outputIdMap: Map<OutputId, Size>,
@@ -146,14 +146,14 @@
     }
 }
 
-class FakeOnImageListener : ImageReaderWrapper.OnImageListener {
-    val onImageEvents = mutableListOf<OnImageEvent>()
+public class FakeOnImageListener : ImageReaderWrapper.OnImageListener {
+    public val onImageEvents: MutableList<OnImageEvent> = mutableListOf<OnImageEvent>()
 
     override fun onImage(streamId: StreamId, outputId: OutputId, image: ImageWrapper) {
         onImageEvents.add(OnImageEvent(streamId, outputId, image))
     }
 
-    data class OnImageEvent(
+    public data class OnImageEvent(
         val streamId: StreamId,
         val outputId: OutputId,
         val image: ImageWrapper
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReaders.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReaders.kt
index 29253bc..0deb3ef 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReaders.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageReaders.kt
@@ -31,21 +31,21 @@
  * retrieved based on [Surface] or by [StreamId], and supports both single and
  * MultiResolutionImageReader-like implementations.
  */
-class FakeImageReaders(private val fakeSurfaces: FakeSurfaces) {
+public class FakeImageReaders(private val fakeSurfaces: FakeSurfaces) {
     private val lock = Any()
 
     @GuardedBy("lock") private val fakeImageReaders = mutableListOf<FakeImageReader>()
 
-    operator fun get(surface: Surface): FakeImageReader? {
+    public operator fun get(surface: Surface): FakeImageReader? {
         return synchronized(lock) { fakeImageReaders.find { it.surface == surface } }
     }
 
-    operator fun get(streamId: StreamId): FakeImageReader? {
+    public operator fun get(streamId: StreamId): FakeImageReader? {
         return synchronized(lock) { fakeImageReaders.find { it.streamId == streamId } }
     }
 
     /** Create a [FakeImageReader] based on a single [CameraStream]. */
-    fun create(cameraStream: CameraStream, capacity: Int) =
+    public fun create(cameraStream: CameraStream, capacity: Int): FakeImageReader =
         create(
             cameraStream.outputs.first().format,
             cameraStream.id,
@@ -54,7 +54,7 @@
         )
 
     /** Create a [FakeImageReader] from its properties. */
-    fun create(
+    public fun create(
         format: StreamFormat,
         streamId: StreamId,
         outputIdMap: Map<OutputId, Size>,
@@ -72,7 +72,7 @@
     }
 
     /** Create a [FakeImageReader] based on a [CameraStream] and an [ImageSourceConfig]. */
-    fun create(
+    public fun create(
         cameraStream: CameraStream,
         imageSourceConfig: ImageSourceConfig
     ): ImageReaderWrapper =
@@ -85,14 +85,14 @@
         )
 
     /** [check] that all [FakeImageReader]s are closed. */
-    fun checkImageReadersClosed() {
+    public fun checkImageReadersClosed() {
         for (fakeImageReader in fakeImageReaders) {
             check(fakeImageReader.isClosed) { "Failed to close ImageReader: $fakeImageReader" }
         }
     }
 
     /** [check] that all images from all [FakeImageReader]s are closed. */
-    fun checkImagesClosed() {
+    public fun checkImagesClosed() {
         for (fakeImageReader in fakeImageReaders) {
             fakeImageReader.checkImagesClosed()
         }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSource.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSource.kt
index a51af8d..2c6cc4d 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSource.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSource.kt
@@ -24,24 +24,24 @@
 import androidx.camera.camera2.pipe.media.ImageSource
 import kotlinx.atomicfu.atomic
 
-class FakeImageSource
+public class FakeImageSource
 private constructor(
     private val fakeImageReader: FakeImageReader,
     private val imageSource: ImageSource
 ) : ImageSource by imageSource {
     private val debugId = debugIds.incrementAndGet()
     private val closed = atomic<Boolean>(false)
-    val isClosed: Boolean
+    public val isClosed: Boolean
         get() = closed.value
 
-    val streamId: StreamId
+    public val streamId: StreamId
         get() = fakeImageReader.streamId
 
     /** Retrieve a list of every image that has been created from this FakeImageSource. */
-    val images: List<FakeImage>
+    public val images: List<FakeImage>
         get() = fakeImageReader.images
 
-    fun simulateImage(timestamp: Long, outputId: OutputId? = null): FakeImage {
+    public fun simulateImage(timestamp: Long, outputId: OutputId? = null): FakeImage {
         return fakeImageReader.simulateImage(timestamp, outputId)
     }
 
@@ -53,10 +53,10 @@
 
     override fun toString(): String = "FakeImageSource-$debugId"
 
-    companion object {
+    public companion object {
         private val debugIds = atomic(0)
 
-        fun create(
+        public fun create(
             streamFormat: StreamFormat,
             streamId: StreamId,
             outputs: Map<OutputId, Size>,
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSources.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSources.kt
index ae74060..ba088a5 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSources.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeImageSources.kt
@@ -29,16 +29,16 @@
  * be retrieved based on [Surface] or by [StreamId], and supports both single and
  * MultiResolutionImageReader-like implementations.
  */
-class FakeImageSources(private val fakeImageReaders: FakeImageReaders) : ImageSources {
+public class FakeImageSources(private val fakeImageReaders: FakeImageReaders) : ImageSources {
     private val lock = Any()
 
     @GuardedBy("lock") private val fakeImageSources = mutableListOf<FakeImageSource>()
 
-    operator fun get(surface: Surface): FakeImageSource? {
+    public operator fun get(surface: Surface): FakeImageSource? {
         return synchronized(lock) { fakeImageSources.find { it.surface == surface } }
     }
 
-    operator fun get(streamId: StreamId): FakeImageSource? {
+    public operator fun get(streamId: StreamId): FakeImageSource? {
         return synchronized(lock) { fakeImageSources.find { it.streamId == streamId } }
     }
 
@@ -62,7 +62,7 @@
     }
 
     /** [check] that all [FakeImageSource]s are closed. */
-    fun checkImageSourcesClosed() =
+    public fun checkImageSourcesClosed(): Unit =
         synchronized(lock) {
             for (fakeImageSource in fakeImageSources) {
                 check(fakeImageSource.isClosed) { "Failed to close ImageSource!: $fakeImageSource" }
@@ -70,7 +70,7 @@
         }
 
     /** [check] that all images from all [FakeImageReader]s are closed. */
-    fun checkImagesClosed() =
+    public fun checkImagesClosed(): Unit =
         synchronized(lock) {
             for ((i, fakeImageSource) in fakeImageSources.withIndex()) {
                 for ((j, fakeImage) in fakeImageSource.images.withIndex()) {
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestFailure.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestFailure.kt
index 7b8a898..4502deb 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestFailure.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestFailure.kt
@@ -22,7 +22,7 @@
 import kotlin.reflect.KClass
 
 /** Utility class for testing code that depends on [RequestFailure] with reasonable defaults. */
-class FakeRequestFailure(
+public class FakeRequestFailure(
     override val requestMetadata: RequestMetadata,
     override val frameNumber: FrameNumber,
     override val reason: Int = 0,
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt
index 1479971..28d62af 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt
@@ -25,6 +25,7 @@
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.StreamId
 import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.asSharedFlow
 
 /**
@@ -34,36 +35,38 @@
  * sent.
  */
 @Suppress("ListenerInterface")
-class FakeRequestListener(private val replayBuffer: Int = 10) : Request.Listener {
+public class FakeRequestListener(private val replayBuffer: Int = 10) : Request.Listener {
 
     private val _onStartedFlow = MutableSharedFlow<OnStarted>(replay = replayBuffer)
-    val onStartedFlow = _onStartedFlow.asSharedFlow()
+    public val onStartedFlow: SharedFlow<OnStarted> = _onStartedFlow.asSharedFlow()
 
     private val _onPartialCaptureResultFlow =
         MutableSharedFlow<OnPartialCaptureResult>(replay = replayBuffer)
-    val onPartialCaptureResultFlow = _onPartialCaptureResultFlow.asSharedFlow()
+    public val onPartialCaptureResultFlow: SharedFlow<OnPartialCaptureResult> =
+        _onPartialCaptureResultFlow.asSharedFlow()
 
     private val _onTotalCaptureResultFlow =
         MutableSharedFlow<OnTotalCaptureResult>(replay = replayBuffer)
-    val onTotalCaptureResultFlow = _onTotalCaptureResultFlow.asSharedFlow()
+    public val onTotalCaptureResultFlow: SharedFlow<OnTotalCaptureResult> =
+        _onTotalCaptureResultFlow.asSharedFlow()
 
     private val _onCompleteFlow = MutableSharedFlow<OnComplete>(replay = replayBuffer)
-    val onCompleteFlow = _onCompleteFlow.asSharedFlow()
+    public val onCompleteFlow: SharedFlow<OnComplete> = _onCompleteFlow.asSharedFlow()
 
     private val _onBufferLostFlow = MutableSharedFlow<OnBufferLost>(replay = replayBuffer)
-    val onBufferLostFlow = _onBufferLostFlow.asSharedFlow()
+    public val onBufferLostFlow: SharedFlow<OnBufferLost> = _onBufferLostFlow.asSharedFlow()
 
     private val _onAbortedFlow = MutableSharedFlow<OnAborted>(replay = replayBuffer)
-    val onAbortedFlow = _onAbortedFlow.asSharedFlow()
+    public val onAbortedFlow: SharedFlow<OnAborted> = _onAbortedFlow.asSharedFlow()
 
     private val _onFailedFlow = MutableSharedFlow<OnFailed>(replay = replayBuffer)
-    val onFailedFlow = _onFailedFlow.asSharedFlow()
+    public val onFailedFlow: SharedFlow<OnFailed> = _onFailedFlow.asSharedFlow()
 
     override fun onStarted(
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
         timestamp: CameraTimestamp
-    ) =
+    ): Unit =
         check(_onStartedFlow.tryEmit(OnStarted(requestMetadata, frameNumber, timestamp))) {
             "Failed to emit onStarted event! The size of the replay buffer" +
                 "($replayBuffer) may need to be increased."
@@ -73,7 +76,7 @@
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
         captureResult: FrameMetadata
-    ) =
+    ): Unit =
         check(
             _onPartialCaptureResultFlow.tryEmit(
                 OnPartialCaptureResult(requestMetadata, frameNumber, captureResult)
@@ -87,7 +90,7 @@
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
         totalCaptureResult: FrameInfo
-    ) =
+    ): Unit =
         check(
             _onTotalCaptureResultFlow.tryEmit(
                 OnTotalCaptureResult(requestMetadata, frameNumber, totalCaptureResult)
@@ -101,13 +104,13 @@
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
         result: FrameInfo
-    ) =
+    ): Unit =
         check(_onCompleteFlow.tryEmit(OnComplete(requestMetadata, frameNumber, result))) {
             "Failed to emit onComplete event! The size of the replay buffer" +
                 "($replayBuffer) may need to be increased."
         }
 
-    override fun onAborted(request: Request) =
+    override fun onAborted(request: Request): Unit =
         check(_onAbortedFlow.tryEmit(OnAborted(request))) {
             "Failed to emit OnAborted event! The size of the replay buffer" +
                 "($replayBuffer) may need to be increased."
@@ -117,7 +120,7 @@
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
         stream: StreamId
-    ) =
+    ): Unit =
         check(_onBufferLostFlow.tryEmit(OnBufferLost(requestMetadata, frameNumber, stream))) {
             "Failed to emit OnBufferLost event! The size of the replay buffer" +
                 "($replayBuffer) may need to be increased."
@@ -127,49 +130,49 @@
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
         requestFailure: RequestFailure
-    ) =
+    ): Unit =
         check(_onFailedFlow.tryEmit(OnFailed(requestMetadata, frameNumber, requestFailure))) {
             "Failed to emit OnFailed event! The size of the replay buffer" +
                 "($replayBuffer) may need to be increased."
         }
 }
 
-sealed class RequestListenerEvent
+public sealed class RequestListenerEvent
 
-class OnStarted(
-    val requestMetadata: RequestMetadata,
-    val frameNumber: FrameNumber,
-    val timestamp: CameraTimestamp
+public class OnStarted(
+    public val requestMetadata: RequestMetadata,
+    public val frameNumber: FrameNumber,
+    public val timestamp: CameraTimestamp
 ) : RequestListenerEvent()
 
-class OnPartialCaptureResult(
-    val requestMetadata: RequestMetadata,
-    val frameNumber: FrameNumber,
-    val frameMetadata: FrameMetadata
+public class OnPartialCaptureResult(
+    public val requestMetadata: RequestMetadata,
+    public val frameNumber: FrameNumber,
+    public val frameMetadata: FrameMetadata
 ) : RequestListenerEvent()
 
-class OnTotalCaptureResult(
-    val requestMetadata: RequestMetadata,
-    val frameNumber: FrameNumber,
-    val frameInfo: FrameInfo
+public class OnTotalCaptureResult(
+    public val requestMetadata: RequestMetadata,
+    public val frameNumber: FrameNumber,
+    public val frameInfo: FrameInfo
 ) : RequestListenerEvent()
 
-class OnComplete(
-    val requestMetadata: RequestMetadata,
-    val frameNumber: FrameNumber,
-    val frameInfo: FrameInfo
+public class OnComplete(
+    public val requestMetadata: RequestMetadata,
+    public val frameNumber: FrameNumber,
+    public val frameInfo: FrameInfo
 ) : RequestListenerEvent()
 
-class OnAborted(val request: Request) : RequestListenerEvent()
+public class OnAborted(public val request: Request) : RequestListenerEvent()
 
-class OnBufferLost(
-    val requestMetadata: RequestMetadata,
-    val frameNumber: FrameNumber,
-    val streamId: StreamId
+public class OnBufferLost(
+    public val requestMetadata: RequestMetadata,
+    public val frameNumber: FrameNumber,
+    public val streamId: StreamId
 ) : RequestListenerEvent()
 
-class OnFailed(
-    val requestMetadata: RequestMetadata,
-    val frameNumber: FrameNumber,
-    val requestFailure: RequestFailure
+public class OnFailed(
+    public val requestMetadata: RequestMetadata,
+    public val frameNumber: FrameNumber,
+    public val requestFailure: RequestFailure
 ) : RequestListenerEvent()
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestMetadata.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestMetadata.kt
new file mode 100644
index 0000000..0f075bc
--- /dev/null
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestMetadata.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 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 androidx.camera.camera2.pipe.testing
+
+import android.hardware.camera2.CaptureRequest
+import android.view.Surface
+import androidx.camera.camera2.pipe.Metadata
+import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestMetadata
+import androidx.camera.camera2.pipe.RequestNumber
+import androidx.camera.camera2.pipe.RequestTemplate
+import androidx.camera.camera2.pipe.StreamId
+import kotlin.reflect.KClass
+import kotlinx.atomicfu.atomic
+
+private val fakeRequestNumbers = atomic(0L)
+
+internal fun nextFakeRequestNumber(): RequestNumber =
+    RequestNumber(fakeRequestNumbers.incrementAndGet())
+
+/** Utility class for interacting with objects require specific [CaptureRequest] metadata. */
+public class FakeRequestMetadata(
+    private val requestParameters: Map<CaptureRequest.Key<*>, Any?> = emptyMap(),
+    metadata: Map<Metadata.Key<*>, Any?> = emptyMap(),
+    override val template: RequestTemplate = RequestTemplate(0),
+    override val streams: Map<StreamId, Surface> = mapOf(),
+    override val repeating: Boolean = false,
+    override val request: Request = Request(listOf()),
+    override val requestNumber: RequestNumber = nextFakeRequestNumber()
+) : FakeMetadata(request.extras.plus(metadata)), RequestMetadata {
+
+    @Suppress("UNCHECKED_CAST")
+    override fun <T> get(key: CaptureRequest.Key<T>): T? = requestParameters[key] as T?
+
+    override fun <T> getOrDefault(key: CaptureRequest.Key<T>, default: T): T = get(key) ?: default
+
+    override fun <T : Any> unwrapAs(type: KClass<T>): T? = null
+
+    public companion object {
+        /** Initialize FakeRequestMetadata based on a specific [Request] object. */
+        public fun from(
+            request: Request,
+            streamToSurfaces: Map<StreamId, Surface>,
+            repeating: Boolean = false
+        ): FakeRequestMetadata {
+            check(streamToSurfaces.keys.containsAll(request.streams))
+            return FakeRequestMetadata(
+                requestParameters = request.parameters,
+                template = request.template ?: RequestTemplate(0),
+                streams = request.streams.map { it to streamToSurfaces[it]!! }.toMap(),
+                repeating = repeating,
+                request = request
+            )
+        }
+    }
+
+    override fun toString(): String =
+        "FakeRequestMetadata(requestNumber: ${requestNumber.value}, request: $request)"
+}
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeSurfaces.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeSurfaces.kt
index c4039f4..0fb154c 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeSurfaces.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeSurfaces.kt
@@ -26,10 +26,10 @@
  *
  * Close this object to release all surfaces during tests.
  */
-class FakeSurfaces : AutoCloseable {
+public class FakeSurfaces : AutoCloseable {
     private val fakeSurfaces = mutableListOf<Surface>()
 
-    fun createFakeSurface(size: Size = Size(640, 480)): Surface {
+    public fun createFakeSurface(size: Size = Size(640, 480)): Surface {
         val surface = create(size)
         synchronized(fakeSurfaces) { fakeSurfaces.add(surface) }
         return surface
@@ -44,10 +44,10 @@
         }
     }
 
-    companion object {
+    public companion object {
         private val fakeSurfaceTextureNames = atomic(0)
 
-        fun create(size: Size = Size(640, 480)): Surface {
+        public fun create(size: Size = Size(640, 480)): Surface {
             return Surface(
                 SurfaceTexture(fakeSurfaceTextureNames.getAndIncrement()).also {
                     it.setDefaultBufferSize(size.width, size.height)
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeThreads.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeThreads.kt
index fcf69f6..d11a438 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeThreads.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeThreads.kt
@@ -25,13 +25,13 @@
 import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
 
-object FakeThreads {
-    fun fromDispatcher(dispatcher: CoroutineDispatcher): Threads {
+public object FakeThreads {
+    public fun fromDispatcher(dispatcher: CoroutineDispatcher): Threads {
         val scope = CoroutineScope(dispatcher + CoroutineName("CXCP-TestScope"))
         return create(scope, dispatcher)
     }
 
-    fun fromTestScope(scope: TestScope): Threads {
+    public fun fromTestScope(scope: TestScope): Threads {
         val dispatcher = StandardTestDispatcher(scope.testScheduler, "CXCP-TestScope")
         return create(scope, dispatcher)
     }
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeTimeSource.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeTimeSource.kt
index 11ef423..f0d3ac1 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeTimeSource.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeTimeSource.kt
@@ -19,8 +19,8 @@
 import androidx.camera.camera2.pipe.core.TimeSource
 import androidx.camera.camera2.pipe.core.TimestampNs
 
-class FakeTimeSource : TimeSource {
-    public var currentTimestamp = TimestampNs(0L)
+public class FakeTimeSource : TimeSource {
+    public var currentTimestamp: TimestampNs = TimestampNs(0L)
 
-    override fun now() = currentTimestamp
+    override fun now(): TimestampNs = currentTimestamp
 }
diff --git a/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulatorTest.kt b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulatorTest.kt
index f0cf2e8..fe5b689 100644
--- a/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulatorTest.kt
+++ b/camera/camera-camera2-pipe-testing/src/test/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulatorTest.kt
@@ -58,7 +58,8 @@
     private val testScope = TestScope()
     private val metadata =
         FakeCameraMetadata(
-            mapOf(CameraCharacteristics.LENS_FACING to CameraCharacteristics.LENS_FACING_FRONT)
+            characteristics =
+                mapOf(CameraCharacteristics.LENS_FACING to CameraCharacteristics.LENS_FACING_FRONT)
         )
 
     private val streamConfig = CameraStream.Config.create(Size(640, 480), StreamFormat.YUV_420_888)
diff --git a/camera/camera-camera2-pipe/build.gradle b/camera/camera-camera2-pipe/build.gradle
index 8e3af81..54f2fce 100644
--- a/camera/camera-camera2-pipe/build.gradle
+++ b/camera/camera-camera2-pipe/build.gradle
@@ -79,6 +79,5 @@
     inceptionYear = "2020"
     description = "A set of opinionated camera interfaces and implementations on top of Camera2 " +
             "that will form a flexible shim layer to power Frameserver and CameraX."
-    legacyDisableKotlinStrictApiMode = true
     doNotDocumentReason = "Not shipped externally"
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt
index a034faa..513473b 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraBackend.kt
@@ -23,7 +23,7 @@
 /** This is used to uniquely identify a specific backend implementation. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class CameraBackendId(val value: String)
+public value class CameraBackendId(public val value: String)
 
 /**
  * A CameraStatusMonitors monitors the status of the cameras, and emits updates when the status of
@@ -31,15 +31,15 @@
  * camera has become available.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CameraStatusMonitor {
-    val cameraStatus: Flow<CameraStatus>
+public interface CameraStatusMonitor {
+    public val cameraStatus: Flow<CameraStatus>
 
-    abstract class CameraStatus internal constructor() {
-        object CameraPrioritiesChanged : CameraStatus() {
+    public abstract class CameraStatus internal constructor() {
+        public object CameraPrioritiesChanged : CameraStatus() {
             override fun toString(): String = "CameraPrioritiesChanged"
         }
 
-        class CameraAvailable(val cameraId: CameraId) : CameraStatus() {
+        public class CameraAvailable(public val cameraId: CameraId) : CameraStatus() {
             override fun toString(): String = "CameraAvailable(camera=$cameraId"
         }
     }
@@ -58,34 +58,34 @@
  * [CameraBackend.createCameraController].
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CameraBackend {
-    val id: CameraBackendId
+public interface CameraBackend {
+    public val id: CameraBackendId
 
     /**
      * A flow of camera statuses that provide camera status updates such as when the camera access
      * priorities have changed, or a certain camera has become available.
      */
-    val cameraStatus: Flow<CameraStatusMonitor.CameraStatus>
+    public val cameraStatus: Flow<CameraStatusMonitor.CameraStatus>
 
     /**
      * Read out a list of _openable_ [CameraId]s for this backend. The backend may be able to report
      * Metadata for non-openable cameras. However, these cameras should not appear the list of
      * cameras returned by [getCameraIds].
      */
-    suspend fun getCameraIds(): List<CameraId>? = awaitCameraIds()
+    public suspend fun getCameraIds(): List<CameraId>? = awaitCameraIds()
 
     /** Thread-blocking version of [getCameraIds] for compatibility. */
-    fun awaitCameraIds(): List<CameraId>?
+    public fun awaitCameraIds(): List<CameraId>?
 
     /**
      * Read out a set of [CameraId] sets that can be operated concurrently. When multiple cameras
      * are open, the number of configurable streams, as well as their sizes, might be considerably
      * limited.
      */
-    suspend fun getConcurrentCameraIds(): Set<Set<CameraId>>? = awaitConcurrentCameraIds()
+    public suspend fun getConcurrentCameraIds(): Set<Set<CameraId>>? = awaitConcurrentCameraIds()
 
     /** Thread-blocking version of [getConcurrentCameraIds] for compatibility. */
-    fun awaitConcurrentCameraIds(): Set<Set<CameraId>>?
+    public fun awaitConcurrentCameraIds(): Set<Set<CameraId>>?
 
     /**
      * Retrieve [CameraMetadata] for this backend. Backends may cache the results of these calls.
@@ -94,11 +94,11 @@
      * [getCameraIds]. For some backends, it may be possible to retrieve metadata for cameras that
      * cannot be opened directly.
      */
-    suspend fun getCameraMetadata(cameraId: CameraId): CameraMetadata? =
+    public suspend fun getCameraMetadata(cameraId: CameraId): CameraMetadata? =
         awaitCameraMetadata(cameraId)
 
     /** Thread-blocking version of [getCameraMetadata] for compatibility. */
-    fun awaitCameraMetadata(cameraId: CameraId): CameraMetadata?
+    public fun awaitCameraMetadata(cameraId: CameraId): CameraMetadata?
 
     /**
      * Stops all active [CameraController]s, which may disconnect any cached camera connection(s).
@@ -109,7 +109,7 @@
      * Subsequent [CameraController]s may still be created after invoking [disconnectAllAsync], and
      * existing [CameraController]s may attempt to restart.
      */
-    fun disconnectAllAsync(): Deferred<Unit>
+    public fun disconnectAllAsync(): Deferred<Unit>
 
     /**
      * Shutdown this backend, closing active [CameraController]s, and clearing any cached resources.
@@ -118,7 +118,7 @@
      * camera lists, metadata, and state. Once a backend instance has been shut down it should not
      * be reused, and a new instance must be recreated.
      */
-    fun shutdownAsync(): Deferred<Unit>
+    public fun shutdownAsync(): Deferred<Unit>
 
     /**
      * Creates a new [CameraController] instance that can be used to initialize and interact with a
@@ -126,7 +126,7 @@
      * should _not_ begin opening or interacting with the Camera until [CameraController.start] is
      * called.
      */
-    fun createCameraController(
+    public fun createCameraController(
         cameraContext: CameraContext,
         graphId: CameraGraphId,
         graphConfig: CameraGraph.Config,
@@ -135,19 +135,19 @@
     ): CameraController
 
     /** Connects and starts the underlying camera */
-    fun prewarm(cameraId: CameraId)
+    public fun prewarm(cameraId: CameraId)
 
     /** Disconnects the underlying camera. */
-    fun disconnect(cameraId: CameraId)
+    public fun disconnect(cameraId: CameraId)
 
     /**
      * Disconnects the underlying camera. Once the connection is closed, the returned [Deferred]
      * should be completed.
      */
-    fun disconnectAsync(cameraId: CameraId): Deferred<Unit>
+    public fun disconnectAsync(cameraId: CameraId): Deferred<Unit>
 
     /** Disconnects all active Cameras. */
-    fun disconnectAll()
+    public fun disconnectAll()
 }
 
 /**
@@ -158,9 +158,9 @@
  * and release previously created [CameraBackend]s.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun interface CameraBackendFactory {
+public fun interface CameraBackendFactory {
     /** Create a new [CameraBackend] instance based on the provided [CameraContext]. */
-    fun create(cameraContext: CameraContext): CameraBackend
+    public fun create(cameraContext: CameraContext): CameraBackend
 }
 
 /**
@@ -168,29 +168,29 @@
  * [CameraPipe] instance.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CameraBackends {
+public interface CameraBackends {
     /**
      * This provides access to the default [CameraBackend]. Accessing this property will create the
      * backend if it is not already created.
      */
-    val default: CameraBackend
+    public val default: CameraBackend
 
     /**
      * This provides a list of all available [CameraBackend] instances, including the default one.
      * Accessing this set will not create or initialize [CameraBackend] instances.
      */
-    val allIds: Set<CameraBackendId>
+    public val allIds: Set<CameraBackendId>
 
     /**
      * This provides a list of [CameraBackend] instances that have been loaded, including the
      * default camera backend. Accessing this set will not create or initialize [CameraBackend]
      * instances.
      */
-    val activeIds: Set<CameraBackendId>
+    public val activeIds: Set<CameraBackendId>
 
     /**
      * Get a previously created [CameraBackend] instance, or create a new one. If the backend fails
      * to load or is not available, this method will return null.
      */
-    operator fun get(backendId: CameraBackendId): CameraBackend?
+    public operator fun get(backendId: CameraBackendId): CameraBackend?
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraContext.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraContext.kt
index 0766513..8aa5f27e 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraContext.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraContext.kt
@@ -28,8 +28,8 @@
  * between [CameraController] instances.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CameraContext {
-    val appContext: Context
-    val threads: Threads
-    val cameraBackends: CameraBackends
+public interface CameraContext {
+    public val appContext: Context
+    public val threads: Threads
+    public val cameraBackends: CameraBackends
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt
index 21e0146..fa3e7ca 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraController.kt
@@ -37,52 +37,52 @@
  * Once [close] is invoked, this instance should not respond to any additional events.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CameraController {
+public interface CameraController {
     /** Represents the primary CameraId this CameraController is associated with. */
-    val cameraId: CameraId
+    public val cameraId: CameraId
 
     /** Represents the primary CameraGraphId that this [CameraController] is associated with. */
-    val cameraGraphId: CameraGraphId
+    public val cameraGraphId: CameraGraphId
 
     /**
      * Whether the camera is being used in a foreground setting, and thus should be kept open on a
      * best-effort basis, for example continuously retrying on a longer timeout.
      */
-    var isForeground: Boolean
+    public var isForeground: Boolean
 
     /**
      * Connect and start the underlying camera. This may be called on the main thread and should not
      * make long blocking calls. This may be called opportunistically (eg, whenever a lifecycle
      * indicates the camera should be in a running state)
      */
-    fun start()
+    public fun start()
 
     /**
      * Disconnect from the underlying camera and stop this session. This may be called on the main
      * thread and should not make long blocking calls.
      */
-    fun stop()
+    public fun stop()
 
     /**
      * Restart the current session. This should basically perform stop() then start(). However, the
      * implementation should handle its internal states correctly, and only restart under the right
      * [CameraStatusMonitor.CameraStatus] and [ControllerState].
      */
-    fun tryRestart(cameraStatus: CameraStatusMonitor.CameraStatus)
+    public fun tryRestart(cameraStatus: CameraStatusMonitor.CameraStatus)
 
     /**
      * Close this instance. [start] and [stop] should not be invoked, and any additional calls will
      * be ignored once this method returns. Depending on implementation the underlying camera
      * connection may not be terminated immediately, depending on the [CameraBackend]
      */
-    fun close()
+    public fun close()
 
     /**
      * Tell the [CameraController] the current mapping between [StreamId] and [Surface]s. This map
      * should always contain at least one entry, and should never contain [StreamId]s that were
      * missing from the [StreamGraph] that was used to create this [CameraController].
      */
-    fun updateSurfaceMap(surfaceMap: Map<StreamId, Surface>)
+    public fun updateSurfaceMap(surfaceMap: Map<StreamId, Surface>)
 
     /**
      * ControllerState indicates the internal state of a [CameraController]. These states are needed
@@ -100,23 +100,23 @@
      *      '-----------------------------------------------------'
      *   ```
      */
-    abstract class ControllerState internal constructor() {
+    public abstract class ControllerState internal constructor() {
         /** When the CameraController is started. This is set immediately as start() is called. */
-        object STARTED : ControllerState()
+        public object STARTED : ControllerState()
 
         /** When the CameraController is stopping. This is set immediately as stop() is called. */
-        object STOPPING : ControllerState()
+        public object STOPPING : ControllerState()
 
         /** When the camera is stopped normally. */
-        object STOPPED : ControllerState()
+        public object STOPPED : ControllerState()
 
         /** When the camera is disconnected and can be later "reconnected". */
-        object DISCONNECTED : ControllerState()
+        public object DISCONNECTED : ControllerState()
 
         /** When the camera shuts down with an unrecoverable error. */
-        object ERROR : ControllerState()
+        public object ERROR : ControllerState()
 
         /** When the CameraController is closed, and no further operations can done on it. */
-        object CLOSED : ControllerState()
+        public object CLOSED : ControllerState()
     }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraControls.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraControls.kt
index e963715..2d032b7 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraControls.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraControls.kt
@@ -27,92 +27,99 @@
 /** An enum to match the CameraMetadata.CONTROL_AF_MODE_* constants. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class AfMode(val value: Int) {
-    fun isOn(): Boolean {
+public value class AfMode(public val value: Int) {
+    public fun isOn(): Boolean {
         return value != CameraMetadata.CONTROL_AF_MODE_OFF
     }
 
-    fun isContinuous(): Boolean {
+    public fun isContinuous(): Boolean {
         return value == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO ||
             value == CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE
     }
 
-    companion object {
-        val OFF = AfMode(CameraMetadata.CONTROL_AF_MODE_OFF)
-        val AUTO = AfMode(CameraMetadata.CONTROL_AF_MODE_AUTO)
-        val MACRO = AfMode(CameraMetadata.CONTROL_AF_MODE_MACRO)
-        val CONTINUOUS_VIDEO = AfMode(CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO)
-        val CONTINUOUS_PICTURE = AfMode(CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
-        val EDOF = AfMode(CameraMetadata.CONTROL_AF_MODE_EDOF)
+    public companion object {
+        public val OFF: AfMode = AfMode(CameraMetadata.CONTROL_AF_MODE_OFF)
+        public val AUTO: AfMode = AfMode(CameraMetadata.CONTROL_AF_MODE_AUTO)
+        public val MACRO: AfMode = AfMode(CameraMetadata.CONTROL_AF_MODE_MACRO)
+        public val CONTINUOUS_VIDEO: AfMode =
+            AfMode(CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO)
+        public val CONTINUOUS_PICTURE: AfMode =
+            AfMode(CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
+        public val EDOF: AfMode = AfMode(CameraMetadata.CONTROL_AF_MODE_EDOF)
+        public val values: List<AfMode> =
+            listOf(OFF, AUTO, MACRO, CONTINUOUS_VIDEO, CONTINUOUS_PICTURE, EDOF)
 
-        val values = listOf(OFF, AUTO, MACRO, CONTINUOUS_VIDEO, CONTINUOUS_PICTURE, EDOF)
-
-        @JvmStatic fun fromIntOrNull(value: Int): AfMode? = values.firstOrNull { it.value == value }
+        @JvmStatic
+        public fun fromIntOrNull(value: Int): AfMode? = values.firstOrNull { it.value == value }
     }
 }
 
 /** An enum to match the CameraMetadata.CONTROL_AE_MODE_* constants. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class AeMode(val value: Int) {
-    fun isOn(): Boolean {
+public value class AeMode(public val value: Int) {
+    public fun isOn(): Boolean {
         return value != CameraMetadata.CONTROL_AE_MODE_OFF
     }
 
-    companion object {
-        val OFF = AeMode(CameraMetadata.CONTROL_AE_MODE_OFF)
-        val ON = AeMode(CameraMetadata.CONTROL_AE_MODE_ON)
-        val ON_ALWAYS_FLASH = AeMode(CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH)
-        val ON_AUTO_FLASH = AeMode(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH)
-        val ON_AUTO_FLASH_REDEYE = AeMode(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE)
-        val ON_EXTERNAL_FLASH = AeMode(CameraMetadata.CONTROL_AE_MODE_ON_EXTERNAL_FLASH)
+    public companion object {
+        public val OFF: AeMode = AeMode(CameraMetadata.CONTROL_AE_MODE_OFF)
+        public val ON: AeMode = AeMode(CameraMetadata.CONTROL_AE_MODE_ON)
+        public val ON_ALWAYS_FLASH: AeMode = AeMode(CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH)
+        public val ON_AUTO_FLASH: AeMode = AeMode(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH)
+        public val ON_AUTO_FLASH_REDEYE: AeMode =
+            AeMode(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE)
+        public val ON_EXTERNAL_FLASH: AeMode =
+            AeMode(CameraMetadata.CONTROL_AE_MODE_ON_EXTERNAL_FLASH)
 
-        val values =
+        public val values: List<AeMode> =
             listOf(OFF, ON, ON_AUTO_FLASH, ON_ALWAYS_FLASH, ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH)
 
-        @JvmStatic fun fromIntOrNull(value: Int): AeMode? = values.firstOrNull { it.value == value }
+        @JvmStatic
+        public fun fromIntOrNull(value: Int): AeMode? = values.firstOrNull { it.value == value }
     }
 }
 
 /** An enum to match the CameraMetadata.CONTROL_AWB_MODE_* constants. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class AwbMode(val value: Int) {
-    fun isOn(): Boolean {
+public value class AwbMode(public val value: Int) {
+    public fun isOn(): Boolean {
         return value != CameraMetadata.CONTROL_AWB_MODE_OFF
     }
 
-    companion object {
-        val OFF = AwbMode(CameraMetadata.CONTROL_AWB_MODE_OFF)
-        val AUTO = AwbMode(CameraMetadata.CONTROL_AWB_MODE_AUTO)
-        val CLOUDY_DAYLIGHT = AwbMode(CameraMetadata.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT)
-        val DAYLIGHT = AwbMode(CameraMetadata.CONTROL_AWB_MODE_DAYLIGHT)
-        val INCANDESCENT = AwbMode(CameraMetadata.CONTROL_AWB_MODE_INCANDESCENT)
-        val FLUORESCENT = AwbMode(CameraMetadata.CONTROL_AWB_MODE_FLUORESCENT)
-        val SHADE = AwbMode(CameraMetadata.CONTROL_AWB_MODE_SHADE)
-        val TWILIGHT = AwbMode(CameraMetadata.CONTROL_AWB_MODE_TWILIGHT)
+    public companion object {
+        public val OFF: AwbMode = AwbMode(CameraMetadata.CONTROL_AWB_MODE_OFF)
+        public val AUTO: AwbMode = AwbMode(CameraMetadata.CONTROL_AWB_MODE_AUTO)
+        public val CLOUDY_DAYLIGHT: AwbMode =
+            AwbMode(CameraMetadata.CONTROL_AWB_MODE_CLOUDY_DAYLIGHT)
+        public val DAYLIGHT: AwbMode = AwbMode(CameraMetadata.CONTROL_AWB_MODE_DAYLIGHT)
+        public val INCANDESCENT: AwbMode = AwbMode(CameraMetadata.CONTROL_AWB_MODE_INCANDESCENT)
+        public val FLUORESCENT: AwbMode = AwbMode(CameraMetadata.CONTROL_AWB_MODE_FLUORESCENT)
+        public val SHADE: AwbMode = AwbMode(CameraMetadata.CONTROL_AWB_MODE_SHADE)
+        public val TWILIGHT: AwbMode = AwbMode(CameraMetadata.CONTROL_AWB_MODE_TWILIGHT)
 
-        val values =
+        public val values: List<AwbMode> =
             listOf(OFF, AUTO, CLOUDY_DAYLIGHT, DAYLIGHT, INCANDESCENT, FLUORESCENT, SHADE, TWILIGHT)
 
         @JvmStatic
-        fun fromIntOrNull(value: Int): AwbMode? = values.firstOrNull { it.value == value }
+        public fun fromIntOrNull(value: Int): AwbMode? = values.firstOrNull { it.value == value }
     }
 }
 
 /** An enum to match the CameraMetadata.FLASH_MODE_* constants. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class FlashMode(val value: Int) {
-    companion object {
-        val OFF = FlashMode(CameraMetadata.FLASH_MODE_OFF)
-        val SINGLE = FlashMode(CameraMetadata.FLASH_MODE_SINGLE)
-        val TORCH = FlashMode(CameraMetadata.FLASH_MODE_TORCH)
+public value class FlashMode(public val value: Int) {
+    public companion object {
+        public val OFF: FlashMode = FlashMode(CameraMetadata.FLASH_MODE_OFF)
+        public val SINGLE: FlashMode = FlashMode(CameraMetadata.FLASH_MODE_SINGLE)
+        public val TORCH: FlashMode = FlashMode(CameraMetadata.FLASH_MODE_TORCH)
 
         private val values = listOf(OFF, SINGLE, TORCH)
 
         @JvmStatic
-        fun fromIntOrNull(value: Int): FlashMode? = values.firstOrNull { it.value == value }
+        public fun fromIntOrNull(value: Int): FlashMode? = values.firstOrNull { it.value == value }
     }
 }
 
@@ -127,18 +134,18 @@
  * #CONTROL_AE_MODE_ON
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class TorchState private constructor() {
-    companion object {
-        val ON = TorchState()
-        val OFF = TorchState()
+public class TorchState private constructor() {
+    public companion object {
+        public val ON: TorchState = TorchState()
+        public val OFF: TorchState = TorchState()
     }
 }
 
 /** Requirement to consider prior to locking auto-exposure, auto-focus and auto-whitebalance. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class Lock3ABehavior private constructor(val value: Int) {
-    companion object {
+public value class Lock3ABehavior private constructor(public val value: Int) {
+    public companion object {
         /**
          * This requirement means that we want to lock the values for 3A immediately.
          *
@@ -150,16 +157,16 @@
          * [android.hardware.camera2.CaptureRequest.CONTROL_AF_TRIGGER] to
          * [android.hardware.camera2.CaptureRequest.CONTROL_AF_TRIGGER_START].
          */
-        val IMMEDIATE = Lock3ABehavior(1)
+        public val IMMEDIATE: Lock3ABehavior = Lock3ABehavior(1)
 
         /**
          * Lock 3A values after their current scan is finished. If there is no active ongoing scan
          * then the values will be locked to the current values.
          */
-        val AFTER_CURRENT_SCAN = Lock3ABehavior(2)
+        public val AFTER_CURRENT_SCAN: Lock3ABehavior = Lock3ABehavior(2)
 
         /** Initiate a new scan, and then lock the values once the scan is done. */
-        val AFTER_NEW_SCAN = Lock3ABehavior(3)
+        public val AFTER_NEW_SCAN: Lock3ABehavior = Lock3ABehavior(3)
     }
 }
 
@@ -174,7 +181,7 @@
  *   pairs associated with the final result i.e. [TotalCaptureResult] of this frame.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-data class Result3A(val status: Status, val frameMetadata: FrameMetadata? = null) {
+public data class Result3A(val status: Status, val frameMetadata: FrameMetadata? = null) {
     /**
      * Enum to know the status of 3A operation in case the method returns before the desired
      * operation is complete. The reason could be that the operation was talking a lot longer and an
@@ -182,13 +189,13 @@
      * etc.
      */
     @JvmInline
-    value class Status private constructor(val value: Int) {
-        companion object {
-            val OK = Status(0)
-            val FRAME_LIMIT_REACHED = Status(1)
-            val TIME_LIMIT_REACHED = Status(2)
-            val SUBMIT_CANCELLED = Status(3)
-            val SUBMIT_FAILED = Status(4)
+    public value class Status private constructor(public val value: Int) {
+        public companion object {
+            public val OK: Status = Status(0)
+            public val FRAME_LIMIT_REACHED: Status = Status(1)
+            public val TIME_LIMIT_REACHED: Status = Status(2)
+            public val SUBMIT_CANCELLED: Status = Status(3)
+            public val SUBMIT_FAILED: Status = Status(4)
         }
     }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraDevices.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraDevices.kt
index 539d012..90adc9c 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraDevices.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraDevices.kt
@@ -25,27 +25,27 @@
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 /** Methods for querying, iterating, and selecting the Cameras that are available on the device. */
-interface CameraDevices {
+public interface CameraDevices {
     /**
      * Read the list of currently openable [CameraId]s from the provided CameraBackend, suspending
      * if needed. By default this will load the list of openable [CameraId]s from the default
      * backend.
      */
-    suspend fun getCameraIds(cameraBackendId: CameraBackendId? = null): List<CameraId>?
+    public suspend fun getCameraIds(cameraBackendId: CameraBackendId? = null): List<CameraId>?
 
     /**
      * Read the list of currently openable [CameraId]s from the provided CameraBackend, blocking the
      * thread if needed. By default this will load the list of openable [CameraId]s from the default
      * backend.
      */
-    fun awaitCameraIds(cameraBackendId: CameraBackendId? = null): List<CameraId>?
+    public fun awaitCameraIds(cameraBackendId: CameraBackendId? = null): List<CameraId>?
 
     /**
      * Read the set of [CameraId] sets that can be operated concurrently from the provided
      * CameraBackend, suspending if needed. By default this will load the set of [CameraId] sets
      * from the default backend.
      */
-    suspend fun getConcurrentCameraIds(
+    public suspend fun getConcurrentCameraIds(
         cameraBackendId: CameraBackendId? = null
     ): Set<Set<CameraId>>?
 
@@ -54,13 +54,15 @@
      * CameraBackend, blocking the thread if needed. By default this will load the set of [CameraId]
      * sets from the default backend.
      */
-    fun awaitConcurrentCameraIds(cameraBackendId: CameraBackendId? = null): Set<Set<CameraId>>?
+    public fun awaitConcurrentCameraIds(
+        cameraBackendId: CameraBackendId? = null
+    ): Set<Set<CameraId>>?
 
     /**
      * Read metadata for a specific camera id, suspending if needed. By default, this method will
      * query metadata from the default backend if one is not specified.
      */
-    suspend fun getCameraMetadata(
+    public suspend fun getCameraMetadata(
         cameraId: CameraId,
         cameraBackendId: CameraBackendId? = null
     ): CameraMetadata?
@@ -69,7 +71,7 @@
      * Read metadata for a specific camera id, blocking if needed. By default, this method will
      * query metadata from the default backend if one is not specified.
      */
-    fun awaitCameraMetadata(
+    public fun awaitCameraMetadata(
         cameraId: CameraId,
         cameraBackendId: CameraBackendId? = null
     ): CameraMetadata?
@@ -78,29 +80,29 @@
      * Opens the camera device indicated by the cameraId, so that any subsequent open calls will
      * potentially have a better latency.
      */
-    fun prewarm(cameraId: CameraId, cameraBackendId: CameraBackendId? = null)
+    public fun prewarm(cameraId: CameraId, cameraBackendId: CameraBackendId? = null)
 
     /** Non blocking operation that disconnects the underlying active Camera. */
-    fun disconnect(cameraId: CameraId, cameraBackendId: CameraBackendId? = null)
+    public fun disconnect(cameraId: CameraId, cameraBackendId: CameraBackendId? = null)
 
     /**
      * Disconnects the underlying active Camera. Once fully closed, the returned [Deferred] should
      * be completed. It is synchronous with the other operations within this class.
      */
-    fun disconnectAsync(
+    public fun disconnectAsync(
         cameraId: CameraId,
         cameraBackendId: CameraBackendId? = null
     ): Deferred<Unit>
 
     /** Non blocking operation that disconnects all active Cameras. */
-    fun disconnectAll(cameraBackendId: CameraBackendId? = null)
+    public fun disconnectAll(cameraBackendId: CameraBackendId? = null)
 
     /**
      * Non blocking operation that disconnects all active Cameras. Once all connections are fully
      * closed, the returned [Deferred] should be completed. It is synchronous with the other
      * operations within this class.
      */
-    fun disconnectAllAsync(cameraBackendId: CameraBackendId? = null): Deferred<Unit>
+    public fun disconnectAllAsync(cameraBackendId: CameraBackendId? = null): Deferred<Unit>
 
     /**
      * Iterate and return a list of CameraId's on the device that are capable of being opened. Some
@@ -112,7 +114,7 @@
         replaceWith = ReplaceWith("awaitCameraIds"),
         level = DeprecationLevel.WARNING
     )
-    fun findAll(): List<CameraId>
+    public fun findAll(): List<CameraId>
 
     /**
      * Load the list of CameraIds from the Camera2 CameraManager, suspending if the list of
@@ -123,7 +125,7 @@
         replaceWith = ReplaceWith("getCameraIds"),
         level = DeprecationLevel.WARNING
     )
-    suspend fun ids(): List<CameraId>
+    public suspend fun ids(): List<CameraId>
 
     /**
      * Load CameraMetadata for a specific CameraId. Loading CameraMetadata can take a non-zero
@@ -135,7 +137,7 @@
         replaceWith = ReplaceWith("getCameraMetadata"),
         level = DeprecationLevel.WARNING
     )
-    suspend fun getMetadata(camera: CameraId): CameraMetadata
+    public suspend fun getMetadata(camera: CameraId): CameraMetadata
 
     /**
      * Load CameraMetadata for a specific CameraId and block the calling thread until the result is
@@ -146,21 +148,21 @@
         replaceWith = ReplaceWith("awaitCameraMetadata"),
         level = DeprecationLevel.WARNING
     )
-    fun awaitMetadata(camera: CameraId): CameraMetadata
+    public fun awaitMetadata(camera: CameraId): CameraMetadata
 }
 
 /** CameraId represents a typed identifier for a camera represented as a non-blank String. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class CameraId(val value: String) {
+public value class CameraId(public val value: String) {
     init {
         require(value.isNotBlank()) { "CameraId cannot be null or blank!" }
     }
 
-    companion object {
-        inline fun fromCamera2Id(value: String): CameraId = CameraId(value)
+    public companion object {
+        public inline fun fromCamera2Id(value: String): CameraId = CameraId(value)
 
-        inline fun fromCamera1Id(value: Int): CameraId = CameraId("$value")
+        public inline fun fromCamera1Id(value: Int): CameraId = CameraId("$value")
     }
 
     /**
@@ -168,7 +170,7 @@
      *
      * @return The parsed Camera1 id, or null if the value cannot be parsed as a Camera1 id.
      */
-    inline fun toCamera1Id(): Int? = value.toIntOrNull()
+    public inline fun toCamera1Id(): Int? = value.toIntOrNull()
 
     override fun toString(): String = "CameraId-$value"
 }
@@ -179,7 +181,7 @@
  * last.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun CameraDevices.find(
+public fun CameraDevices.find(
     cameraBackendId: CameraBackendId? = null,
     includePhysicalCameraMetadata: Boolean = false
 ): Flow<CameraMetadata> = flow {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraError.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraError.kt
index e709702..3073d4a 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraError.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraError.kt
@@ -29,20 +29,20 @@
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class CameraError private constructor(val value: Int) {
-    companion object {
+public value class CameraError private constructor(public val value: Int) {
+    public companion object {
         /**
          * Convenient placeholder for errors like CameraAccessException.CAMERA_ERROR where the cause
          * of the error will be determined later through onError().
          */
-        val ERROR_UNDETERMINED = CameraError(0)
+        public val ERROR_UNDETERMINED: CameraError = CameraError(0)
 
         /**
          * Indicates that the camera is in use. This can occur when:
          * - Camera is in use by another app or process.
          * - Camera is in use by a higher priority process
          */
-        val ERROR_CAMERA_IN_USE = CameraError(1)
+        public val ERROR_CAMERA_IN_USE: CameraError = CameraError(1)
 
         /**
          * The system-wide limit for number of open cameras or camera resources has been reached.
@@ -50,21 +50,21 @@
          *   number of cameras has reached the system-wide limit, and the app attempts to open
          *   another one, this error can occur.
          */
-        val ERROR_CAMERA_LIMIT_EXCEEDED = CameraError(2)
+        public val ERROR_CAMERA_LIMIT_EXCEEDED: CameraError = CameraError(2)
 
         /**
          * The camera is disabled and cannot be opened. This can occur when:
          * - Camera(s) are disabled due to security policy on the device.
          * - Camera(s) are disabled because the app is not considered "foreground".
          */
-        val ERROR_CAMERA_DISABLED = CameraError(3)
+        public val ERROR_CAMERA_DISABLED: CameraError = CameraError(3)
 
         /**
          * The camera device has encountered a fatal error. This can occur when:
          * - A critical error took place in the camera HAL.
          * - A critical error in the driver or the underlying hardware.
          */
-        val ERROR_CAMERA_DEVICE = CameraError(4)
+        public val ERROR_CAMERA_DEVICE: CameraError = CameraError(4)
 
         /**
          * The camera service (framework) has encountered a fatal error. This can occur when:
@@ -73,7 +73,7 @@
          * - We have Native-level crashes that brought down the camera service.
          * - There are persistent hardware issues preventing the service from running.
          */
-        val ERROR_CAMERA_SERVICE = CameraError(5)
+        public val ERROR_CAMERA_SERVICE: CameraError = CameraError(5)
 
         /**
          * The camera has been disconnected for one of the following potential causes:
@@ -81,39 +81,39 @@
          * - The camera ID is no longer valid.
          * - The camera has been taken for a higher-priority process.
          */
-        val ERROR_CAMERA_DISCONNECTED = CameraError(6)
+        public val ERROR_CAMERA_DISCONNECTED: CameraError = CameraError(6)
 
         /** This indicates that we received IllegalArgumentException while opening the camera. */
-        val ERROR_ILLEGAL_ARGUMENT_EXCEPTION = CameraError(7)
+        public val ERROR_ILLEGAL_ARGUMENT_EXCEPTION: CameraError = CameraError(7)
 
         /** This indicates that we received SecurityException while opening the camera. */
-        val ERROR_SECURITY_EXCEPTION = CameraError(8)
+        public val ERROR_SECURITY_EXCEPTION: CameraError = CameraError(8)
 
         /**
          * This indicates we've encountered an error while configuring our camera graph, such as
          * creating, finalizing capture sessions, and creating, submitting capture requests.
          */
-        val ERROR_GRAPH_CONFIG = CameraError(9)
+        public val ERROR_GRAPH_CONFIG: CameraError = CameraError(9)
 
         /**
          * The camera cannot be opened because Do Not Disturb (DND) mode is on. This is actually a
          * quirk for legacy devices on P, where we encounter RuntimeExceptions while opening the
          * camera when it tries to enable shutter sound.
          */
-        val ERROR_DO_NOT_DISTURB_ENABLED = CameraError(10)
+        public val ERROR_DO_NOT_DISTURB_ENABLED: CameraError = CameraError(10)
 
         /**
          * The CameraManager.openCamera() call threw an undocumented Exception. This can happen on
          * camera devices that encountered an unhandled error, and thereby throwing an unknown
          * Exception.
          */
-        val ERROR_UNKNOWN_EXCEPTION = CameraError(11)
+        public val ERROR_UNKNOWN_EXCEPTION: CameraError = CameraError(11)
 
         /**
          * The internal camera manager at CameraPipe has encountered an error, and isn't able to
          * handle the incoming camera request.
          */
-        val ERROR_CAMERA_OPENER = CameraError(12)
+        public val ERROR_CAMERA_OPENER: CameraError = CameraError(12)
 
         internal fun from(throwable: Throwable) =
             when (throwable) {
@@ -189,8 +189,8 @@
 
 // TODO(b/276918807): When we have CameraProperties, handle the exception on a more granular level.
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class DoNotDisturbException(message: String) : RuntimeException(message)
+public class DoNotDisturbException(message: String) : RuntimeException(message)
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 // An exception indicating that the CameraDevice.close() call has stalled.
-class CameraCloseStallException(message: String) : RuntimeException(message)
+public class CameraCloseStallException(message: String) : RuntimeException(message)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraExtensionMetadata.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraExtensionMetadata.kt
index db923ec..fd788e9 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraExtensionMetadata.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraExtensionMetadata.kt
@@ -14,15 +14,13 @@
  * limitations under the License.
  */
 
-@file:RequiresApi(31) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-
 package androidx.camera.camera2.pipe
 
+import android.hardware.camera2.CameraCharacteristics
 import android.hardware.camera2.CameraExtensionCharacteristics
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.CaptureResult
 import android.util.Size
-import androidx.annotation.RequiresApi
 import androidx.annotation.RestrictTo
 
 /**
@@ -36,18 +34,28 @@
  * easier to test and reason about.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CameraExtensionMetadata : Metadata, UnsafeWrapper {
-    val camera: CameraId
-    val isRedacted: Boolean
-    val cameraExtension: Int
-    val isPostviewSupported: Boolean
+public interface CameraExtensionMetadata : Metadata, UnsafeWrapper {
+    public operator fun <T> get(key: CameraCharacteristics.Key<T>): T?
 
-    val requestKeys: Set<CaptureRequest.Key<*>>
-    val resultKeys: Set<CaptureResult.Key<*>>
+    public fun <T> getOrDefault(key: CameraCharacteristics.Key<T>, default: T): T
 
-    fun getOutputSizes(imageFormat: Int): Set<Size>
+    public val camera: CameraId
+    public val cameraExtension: Int
 
-    fun getOutputSizes(klass: Class<*>): Set<Size>
+    public val isRedacted: Boolean
+    public val isPostviewSupported: Boolean
+    public val isCaptureProgressSupported: Boolean
 
-    fun getPostviewSizes(captureSize: Size, format: Int): Set<Size>
+    public val keys: Set<CameraCharacteristics.Key<*>>
+    public val requestKeys: Set<CaptureRequest.Key<*>>
+    public val resultKeys: Set<CaptureResult.Key<*>>
+
+    /** Get output sizes that can be used for high-quality capture requests. */
+    public fun getOutputSizes(imageFormat: Int): Set<Size>
+
+    /** Get output sizes that can be used for repeating preview requests. */
+    public fun getOutputSizes(klass: Class<*>): Set<Size>
+
+    /** Get sizes that may be used for the postview stream. */
+    public fun getPostviewSizes(captureSize: Size, format: Int): Set<Size>
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
index 34e6fc6..f876917 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraph.kt
@@ -42,35 +42,35 @@
 
 /** A [CameraGraph] represents the combined configuration and state of a camera. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CameraGraph : AutoCloseable {
+public interface CameraGraph : AutoCloseable {
     /**
      * A unique identifier for this CameraGraph instance. This can be used to identify the graph
      * without holding a hard reference to the CameraGraph itself.
      */
-    val id: CameraGraphId
+    public val id: CameraGraphId
 
     /** The [StreamGraph] for this CameraGraph instance. */
-    val streams: StreamGraph
+    public val streams: StreamGraph
 
     /**
      * Returns the state flow of [GraphState], which emits the current state of the [CameraGraph],
      * including when a [CameraGraph] is stopped, starting or started.
      */
-    val graphState: StateFlow<GraphState>
+    public val graphState: StateFlow<GraphState>
 
     /**
      * This is a hint an app can give to a camera graph to indicate whether the camera is being used
      * in a foreground setting, for example whether the user could see the app itself. This would
      * inform the underlying implementation to open cameras more actively (e.g., longer timeout).
      */
-    var isForeground: Boolean
+    public var isForeground: Boolean
 
     /**
      * This will cause the [CameraGraph] to start opening the [CameraDevice] and configuring a
      * [CameraCaptureSession]. While the CameraGraph is alive it will attempt to keep the camera
      * open, active, and in a configured running state.
      */
-    fun start()
+    public fun start()
 
     /**
      * This will cause the [CameraGraph] to stop executing requests and close the current Camera2
@@ -78,7 +78,7 @@
      * preserved, and any calls to submit a request to a session will be enqueued. To stop requests
      * from being enqueued, close the [CameraGraph].
      */
-    fun stop()
+    public fun stop()
 
     /**
      * Used exclusively interact with the camera via a [Session] from within an existing suspending
@@ -88,7 +88,7 @@
      *
      * The returned [Session] **must** be closed.
      */
-    suspend fun acquireSession(): Session
+    public suspend fun acquireSession(): Session
 
     /**
      * Immediately try to acquire access to the internal mutex lock, and return null if it is not
@@ -96,7 +96,7 @@
      *
      * The returned [Session] **must** be closed.
      */
-    fun acquireSessionOrNull(): Session?
+    public fun acquireSessionOrNull(): Session?
 
     /**
      * Used exclusively interact with the camera via a [Session] from within an existing suspending
@@ -119,7 +119,7 @@
      * }
      * ```
      */
-    suspend fun <T> useSession(action: suspend CoroutineScope.(Session) -> T): T
+    public suspend fun <T> useSession(action: suspend CoroutineScope.(Session) -> T): T
 
     /**
      * Used to exclusively interact with the camera from a normal function with a [Session] by
@@ -146,7 +146,7 @@
      * }
      * ```
      */
-    fun <T> useSessionIn(
+    public fun <T> useSessionIn(
         scope: CoroutineScope,
         action: suspend CoroutineScope.(Session) -> T
     ): Deferred<T>
@@ -156,7 +156,7 @@
      *
      * Changing a surface may cause the camera to stall and/or reconfigure.
      */
-    fun setSurface(stream: StreamId, surface: Surface?)
+    public fun setSurface(stream: StreamId, surface: Surface?)
 
     /**
      * CameraPipe allows setting the global audio restriction through [CameraPipe] and audio
@@ -166,7 +166,7 @@
      *
      * Sets the audio restriction of CameraGraph.
      */
-    fun updateAudioRestrictionMode(mode: AudioRestrictionMode)
+    public fun updateAudioRestrictionMode(mode: AudioRestrictionMode)
 
     /**
      * This defines the configuration, flags, and pre-defined structure of a [CameraGraph] instance.
@@ -198,7 +198,7 @@
      *   _only_ this [CameraGraph]. This cannot be defined if [cameraBackendId] is defined.
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    data class Config(
+    public data class Config(
         val camera: CameraId,
         val streams: List<CameraStream.Config>,
         val exclusiveStreamGroups: List<List<CameraStream.Config>> = listOf(),
@@ -226,8 +226,8 @@
         }
     }
 
-    class ConcurrentConfig(graphConfigs: List<Config>) {
-        val graphConfigs: List<Config>
+    public class ConcurrentConfig(graphConfigs: List<Config>) {
+        public val graphConfigs: List<Config>
 
         init {
             check(graphConfigs.size >= 2) {
@@ -258,7 +258,7 @@
      * specific devices to be faster or to work around bad behavior.
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    data class Flags(
+    public data class Flags(
         val configureBlankSessionOnStop: Boolean = false,
 
         /**
@@ -332,28 +332,31 @@
     ) {
 
         @JvmInline
-        value class FinalizeSessionOnCloseBehavior private constructor(val value: Int) {
-            companion object {
+        public value class FinalizeSessionOnCloseBehavior
+        private constructor(public val value: Int) {
+            public companion object {
                 /**
                  * OFF indicates that the CameraGraph only finalizes capture session under regular
                  * conditions, i.e., when the camera device is closed, or when a new capture session
                  * is created.
                  */
-                val OFF = FinalizeSessionOnCloseBehavior(0)
+                public val OFF: FinalizeSessionOnCloseBehavior = FinalizeSessionOnCloseBehavior(0)
 
                 /**
                  * IMMEDIATE indicates that the CameraGraph will finalize the current session
                  * immediately when the CameraGraph is stopped or closed. This should be the default
                  * behavior for devices that allows for immediate Surface reuse.
                  */
-                val IMMEDIATE = FinalizeSessionOnCloseBehavior(1)
+                public val IMMEDIATE: FinalizeSessionOnCloseBehavior =
+                    FinalizeSessionOnCloseBehavior(1)
 
                 /**
                  * TIMEOUT indicates that the CameraGraph will finalize the current session on a 2s
                  * timeout when the CameraGraph is stopped or closed. This should only be enabled
                  * for devices that require waiting for Surfaces to be released.
                  */
-                val TIMEOUT = FinalizeSessionOnCloseBehavior(2)
+                public val TIMEOUT: FinalizeSessionOnCloseBehavior =
+                    FinalizeSessionOnCloseBehavior(2)
             }
         }
     }
@@ -369,13 +372,13 @@
      *   significant limitations in order to produce specific kinds of camera results.
      */
     @JvmInline
-    value class OperatingMode private constructor(internal val mode: Int) {
-        companion object {
-            val NORMAL = OperatingMode(0)
-            val HIGH_SPEED = OperatingMode(1)
-            val EXTENSION = OperatingMode(2)
+    public value class OperatingMode private constructor(internal val mode: Int) {
+        public companion object {
+            public val NORMAL: OperatingMode = OperatingMode(0)
+            public val HIGH_SPEED: OperatingMode = OperatingMode(1)
+            public val EXTENSION: OperatingMode = OperatingMode(2)
 
-            fun custom(mode: Int): OperatingMode {
+            public fun custom(mode: Int): OperatingMode {
                 require(mode != NORMAL.mode && mode != HIGH_SPEED.mode) {
                     Log.error { "Custom operating mode $mode conflicts with standard modes" }
                 }
@@ -384,25 +387,25 @@
         }
     }
 
-    object Constants3A {
+    public object Constants3A {
         // Constants related to controlling the time or frame budget a 3A operation should get.
-        const val DEFAULT_FRAME_LIMIT: Int = 60
-        const val DEFAULT_TIME_LIMIT_MS: Int = 3_000
-        const val DEFAULT_TIME_LIMIT_NS: Long = 3_000_000_000L
+        public const val DEFAULT_FRAME_LIMIT: Int = 60
+        public const val DEFAULT_TIME_LIMIT_MS: Int = 3_000
+        public const val DEFAULT_TIME_LIMIT_NS: Long = 3_000_000_000L
 
         // Constants related to metering regions.
         /** No metering region is specified. */
-        val METERING_REGIONS_EMPTY: Array<MeteringRectangle> = emptyArray()
+        public val METERING_REGIONS_EMPTY: Array<MeteringRectangle> = emptyArray()
 
         /**
          * No-op metering regions, this will tell camera device to pick the right metering region
          * for us.
          */
-        val METERING_REGIONS_DEFAULT: Array<MeteringRectangle> =
+        public val METERING_REGIONS_DEFAULT: Array<MeteringRectangle> =
             arrayOf(MeteringRectangle(0, 0, 0, 0, 0))
 
         /** Placeholder frame number for [Result3A] when a 3A method encounters an error. */
-        val FRAME_NUMBER_INVALID: FrameNumber = FrameNumber(-1L)
+        public val FRAME_NUMBER_INVALID: FrameNumber = FrameNumber(-1L)
     }
 
     /**
@@ -417,16 +420,16 @@
      * Example: A [Session] should *not* be held during video recording.
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    interface Session : AutoCloseable {
+    public interface Session : AutoCloseable {
         /**
          * Causes the CameraGraph to start or update the current repeating request with the provided
          * [Request] object. The [Request] object may be cached, and may be used for other
          * interactions with the camera (such as updating 3A, or issuing 3A triggers).
          */
-        fun startRepeating(request: Request)
+        public fun startRepeating(request: Request)
 
         /** Stop the current repeating request. */
-        fun stopRepeating()
+        public fun stopRepeating()
 
         /**
          * Submit the [Request] to the camera. Requests are issued to the Camera, in order, on a
@@ -434,7 +437,7 @@
          * unless the request is invalid, or unless the requests are aborted via [abort]. The same
          * request can be submitted multiple times.
          */
-        fun submit(request: Request)
+        public fun submit(request: Request)
 
         /**
          * Submit the [Request]s to the camera. [Request]s are issued to the Camera, in order, on a
@@ -442,7 +445,7 @@
          * exactly once unless the one or more of the requests are invalid, or unless the requests
          * are aborted via [abort]. The same list of [Request]s may be submitted multiple times.
          */
-        fun submit(requests: List<Request>)
+        public fun submit(requests: List<Request>)
 
         /**
          * Submit the [Request] to the camera, and aggregate the results into a [FrameCapture],
@@ -450,7 +453,7 @@
          *
          * The [FrameCapture] **must** be closed, or it will result in a memory leak.
          */
-        fun capture(request: Request): FrameCapture
+        public fun capture(request: Request): FrameCapture
 
         /**
          * Submit the [Request]s to the camera, and aggregate the results into a list of
@@ -459,14 +462,14 @@
          *
          * Each [FrameCapture] **must** be closed, or it will result in a memory leak.
          */
-        fun capture(requests: List<Request>): List<FrameCapture>
+        public fun capture(requests: List<Request>): List<FrameCapture>
 
         /**
          * Abort in-flight requests. This will abort *all* requests in the current
          * CameraCaptureSession as well as any requests that are enqueued, but that have not yet
          * been submitted to the camera.
          */
-        fun abort()
+        public fun abort()
 
         /**
          * Applies the given 3A parameters to the camera device.
@@ -475,7 +478,7 @@
          *   these parameters were applied. It may be cancelled with a [CancellationException] if a
          *   newer request is submitted before completion.
          */
-        fun update3A(
+        public fun update3A(
             aeMode: AeMode? = null,
             afMode: AfMode? = null,
             awbMode: AwbMode? = null,
@@ -489,7 +492,7 @@
          *
          * @return the FrameNumber for which these parameters were applied.
          */
-        suspend fun submit3A(
+        public suspend fun submit3A(
             aeMode: AeMode? = null,
             afMode: AfMode? = null,
             awbMode: AwbMode? = null,
@@ -512,7 +515,7 @@
          * @return the FrameNumber at which the turn was fully turned on if switch was ON, or the
          *   FrameNumber at which it was completely turned off when the switch was OFF.
          */
-        fun setTorch(torchState: TorchState): Deferred<Result3A>
+        public fun setTorch(torchState: TorchState): Deferred<Result3A>
 
         /**
          * Locks the auto-exposure, auto-focus and auto-whitebalance as per the given desired
@@ -546,7 +549,7 @@
          *   skips the initial state of the new mode's state machine and stays locks in the new mode
          *   as well.
          */
-        suspend fun lock3A(
+        public suspend fun lock3A(
             aeMode: AeMode? = null,
             afMode: AfMode? = null,
             awbMode: AwbMode? = null,
@@ -585,7 +588,7 @@
          * @return [Result3A], which will contain the latest frame number at which the auto-focus,
          *   auto-exposure, auto-white balance were unlocked as per the method arguments.
          */
-        suspend fun unlock3A(
+        public suspend fun unlock3A(
             ae: Boolean? = null,
             af: Boolean? = null,
             awb: Boolean? = null,
@@ -614,7 +617,7 @@
          *   applied or the frame number at which the method returned early because either frame
          *   limit or time limit was reached.
          */
-        suspend fun lock3AForCapture(
+        public suspend fun lock3AForCapture(
             lockedCondition: ((FrameMetadata) -> Boolean)? = null,
             frameLimit: Int = DEFAULT_FRAME_LIMIT,
             timeLimitNs: Long = DEFAULT_TIME_LIMIT_NS,
@@ -638,7 +641,7 @@
          *   applied or the frame number at which the method returned early because either frame
          *   limit or time limit was reached.
          */
-        suspend fun lock3AForCapture(
+        public suspend fun lock3AForCapture(
             triggerAf: Boolean = true,
             waitForAwb: Boolean = false,
             frameLimit: Int = DEFAULT_FRAME_LIMIT,
@@ -654,7 +657,7 @@
          *
          * @param cancelAf Whether to trigger AF cancel, enabled by default.
          */
-        suspend fun unlock3APostCapture(cancelAf: Boolean = true): Deferred<Result3A>
+        public suspend fun unlock3APostCapture(cancelAf: Boolean = true): Deferred<Result3A>
     }
 }
 
@@ -665,36 +668,38 @@
  * states are produced by the underlying camera as a result of these start/stop calls.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-abstract class GraphState internal constructor() {
+public abstract class GraphState internal constructor() {
     /**
      * When the [CameraGraph] is starting. This means we're in the process of opening a (virtual)
      * camera and creating a capture session.
      */
-    object GraphStateStarting : GraphState()
+    public object GraphStateStarting : GraphState()
 
     /**
      * When the [CameraGraph] is started. This means a capture session has been successfully created
      * for the [CameraGraph].
      */
-    object GraphStateStarted : GraphState()
+    public object GraphStateStarted : GraphState()
 
     /**
      * When the [CameraGraph] is stopping. This means we're in the process of stopping the graph.
      */
-    object GraphStateStopping : GraphState()
+    public object GraphStateStopping : GraphState()
 
     /**
      * When the [CameraGraph] hasn't been started, or stopped. This does not guarantee the closure
      * of the capture session or the camera device itself.
      */
-    object GraphStateStopped : GraphState()
+    public object GraphStateStopped : GraphState()
 
     /**
      * When the [CameraGraph] has encountered an error. If [willAttemptRetry] is true, CameraPipe
      * will retry opening the camera (and creating a capture session).
      */
-    class GraphStateError(val cameraError: CameraError, val willAttemptRetry: Boolean) :
-        GraphState() {
+    public class GraphStateError(
+        public val cameraError: CameraError,
+        public val willAttemptRetry: Boolean
+    ) : GraphState() {
         override fun toString(): String =
             super.toString() + "(cameraError=$cameraError, willAttemptRetry=$willAttemptRetry)"
     }
@@ -703,10 +708,10 @@
 /** @see [CameraDevice.AUDIO_RESTRICTION_NONE] and other constants. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class AudioRestrictionMode internal constructor(val value: Int) {
-    companion object {
-        val AUDIO_RESTRICTION_NONE = AudioRestrictionMode(0)
-        val AUDIO_RESTRICTION_VIBRATION = AudioRestrictionMode(1)
-        val AUDIO_RESTRICTION_VIBRATION_SOUND = AudioRestrictionMode(3)
+public value class AudioRestrictionMode internal constructor(public val value: Int) {
+    public companion object {
+        public val AUDIO_RESTRICTION_NONE: AudioRestrictionMode = AudioRestrictionMode(0)
+        public val AUDIO_RESTRICTION_VIBRATION: AudioRestrictionMode = AudioRestrictionMode(1)
+        public val AUDIO_RESTRICTION_VIBRATION_SOUND: AudioRestrictionMode = AudioRestrictionMode(3)
     }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraphId.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraphId.kt
index 27a35ab..365fa1e 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraphId.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraGraphId.kt
@@ -25,10 +25,10 @@
  * memory leaks and circular dependencies.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class CameraGraphId private constructor(private val name: String) {
+public class CameraGraphId private constructor(private val name: String) {
     override fun toString(): String = name
 
-    companion object {
+    public companion object {
         private val cameraGraphIds = atomic(0)
 
         /**
@@ -37,7 +37,7 @@
          * directly as the toString representation for a [CameraGraph].
          */
         @JvmStatic
-        fun nextId(): CameraGraphId {
+        public fun nextId(): CameraGraphId {
             return CameraGraphId("CameraGraph-${cameraGraphIds.incrementAndGet()}")
         }
     }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraMetadata.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraMetadata.kt
index afdae50..f2d4c3c 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraMetadata.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraMetadata.kt
@@ -37,118 +37,118 @@
  * makes behavior that depends on [CameraMetadata] easier to test and reason about.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CameraMetadata : Metadata, UnsafeWrapper {
-    operator fun <T> get(key: CameraCharacteristics.Key<T>): T?
+public interface CameraMetadata : Metadata, UnsafeWrapper {
+    public operator fun <T> get(key: CameraCharacteristics.Key<T>): T?
 
-    fun <T> getOrDefault(key: CameraCharacteristics.Key<T>, default: T): T
+    public fun <T> getOrDefault(key: CameraCharacteristics.Key<T>, default: T): T
 
-    val camera: CameraId
-    val isRedacted: Boolean
+    public val camera: CameraId
+    public val isRedacted: Boolean
 
-    val keys: Set<CameraCharacteristics.Key<*>>
-    val requestKeys: Set<CaptureRequest.Key<*>>
-    val resultKeys: Set<CaptureResult.Key<*>>
-    val sessionKeys: Set<CaptureRequest.Key<*>>
+    public val keys: Set<CameraCharacteristics.Key<*>>
+    public val requestKeys: Set<CaptureRequest.Key<*>>
+    public val resultKeys: Set<CaptureResult.Key<*>>
+    public val sessionKeys: Set<CaptureRequest.Key<*>>
 
-    val physicalCameraIds: Set<CameraId>
-    val physicalRequestKeys: Set<CaptureRequest.Key<*>>
-    val supportedExtensions: Set<Int>
+    public val physicalCameraIds: Set<CameraId>
+    public val physicalRequestKeys: Set<CaptureRequest.Key<*>>
+    public val supportedExtensions: Set<Int>
 
-    suspend fun getPhysicalMetadata(cameraId: CameraId): CameraMetadata
+    public suspend fun getPhysicalMetadata(cameraId: CameraId): CameraMetadata
 
-    fun awaitPhysicalMetadata(cameraId: CameraId): CameraMetadata
+    public fun awaitPhysicalMetadata(cameraId: CameraId): CameraMetadata
 
-    suspend fun getExtensionMetadata(extension: Int): CameraExtensionMetadata
+    public suspend fun getExtensionMetadata(extension: Int): CameraExtensionMetadata
 
-    fun awaitExtensionMetadata(extension: Int): CameraExtensionMetadata
+    public fun awaitExtensionMetadata(extension: Int): CameraExtensionMetadata
 
-    companion object {
+    public companion object {
         /**
          * Extension properties for querying the available capabilities of a camera device across
          * all API levels.
          */
-        var EMPTY_INT_ARRAY = IntArray(0)
+        public var EMPTY_INT_ARRAY: IntArray = IntArray(0)
 
-        const val CAPABILITIES_MANUAL_SENSOR = 1
-        const val CAPABILITIES_MANUAL_POST_PROCESSING = 2
-        const val CAPABILITIES_RAW = 3
-        const val CAPABILITIES_PRIVATE_REPROCESSING = 4
-        const val CAPABILITIES_READ_SENSOR_SETTINGS = 5
-        const val CAPABILITIES_BURST_CAPTURE = 6
-        const val CAPABILITIES_YUV_REPROCESSING = 7
-        const val CAPABILITIES_DEPTH_OUTPUT = 8
-        const val CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9
-        const val CAPABILITIES_MOTION_TRACKING = 10
-        const val CAPABILITIES_LOGICAL_MULTI_CAMERA = 11
-        const val CAPABILITIES_MONOCHROME = 12
-        const val CAPABILITIES_SECURE_IMAGE_DATA = 13
-        const val CAPABILITIES_SYSTEM_CAMERA = 14
-        const val CAPABILITIES_OFFLINE_REPROCESSING = 15
+        public const val CAPABILITIES_MANUAL_SENSOR: Int = 1
+        public const val CAPABILITIES_MANUAL_POST_PROCESSING: Int = 2
+        public const val CAPABILITIES_RAW: Int = 3
+        public const val CAPABILITIES_PRIVATE_REPROCESSING: Int = 4
+        public const val CAPABILITIES_READ_SENSOR_SETTINGS: Int = 5
+        public const val CAPABILITIES_BURST_CAPTURE: Int = 6
+        public const val CAPABILITIES_YUV_REPROCESSING: Int = 7
+        public const val CAPABILITIES_DEPTH_OUTPUT: Int = 8
+        public const val CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO: Int = 9
+        public const val CAPABILITIES_MOTION_TRACKING: Int = 10
+        public const val CAPABILITIES_LOGICAL_MULTI_CAMERA: Int = 11
+        public const val CAPABILITIES_MONOCHROME: Int = 12
+        public const val CAPABILITIES_SECURE_IMAGE_DATA: Int = 13
+        public const val CAPABILITIES_SYSTEM_CAMERA: Int = 14
+        public const val CAPABILITIES_OFFLINE_REPROCESSING: Int = 15
 
-        val CameraMetadata.availableCapabilities: IntArray
+        public val CameraMetadata.availableCapabilities: IntArray
             get() = this[CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES] ?: EMPTY_INT_ARRAY
 
-        val CameraMetadata.isHardwareLevelExternal: Boolean
+        public val CameraMetadata.isHardwareLevelExternal: Boolean
             get() = this[INFO_SUPPORTED_HARDWARE_LEVEL] == INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL
 
-        val CameraMetadata.isHardwareLevelLegacy: Boolean
+        public val CameraMetadata.isHardwareLevelLegacy: Boolean
             get() = this[INFO_SUPPORTED_HARDWARE_LEVEL] == INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
 
-        val CameraMetadata.isHardwareLevelLimited: Boolean
+        public val CameraMetadata.isHardwareLevelLimited: Boolean
             get() = this[INFO_SUPPORTED_HARDWARE_LEVEL] == INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
 
-        val CameraMetadata.isHardwareLevelFull: Boolean
+        public val CameraMetadata.isHardwareLevelFull: Boolean
             get() = this[INFO_SUPPORTED_HARDWARE_LEVEL] == INFO_SUPPORTED_HARDWARE_LEVEL_FULL
 
-        val CameraMetadata.isHardwareLevel3: Boolean
+        public val CameraMetadata.isHardwareLevel3: Boolean
             get() = this[INFO_SUPPORTED_HARDWARE_LEVEL] == INFO_SUPPORTED_HARDWARE_LEVEL_3
 
-        val CameraMetadata.supportsManualSensor: Boolean
+        public val CameraMetadata.supportsManualSensor: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_MANUAL_SENSOR)
 
-        val CameraMetadata.supportsManualPostProcessing: Boolean
+        public val CameraMetadata.supportsManualPostProcessing: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_MANUAL_POST_PROCESSING)
 
-        val CameraMetadata.supportsRaw: Boolean
+        public val CameraMetadata.supportsRaw: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_RAW)
 
-        val CameraMetadata.supportsPrivateReprocessing: Boolean
+        public val CameraMetadata.supportsPrivateReprocessing: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_PRIVATE_REPROCESSING)
 
-        val CameraMetadata.supportsSensorSettings: Boolean
+        public val CameraMetadata.supportsSensorSettings: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_READ_SENSOR_SETTINGS)
 
-        val CameraMetadata.supportsBurstCapture: Boolean
+        public val CameraMetadata.supportsBurstCapture: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_BURST_CAPTURE)
 
-        val CameraMetadata.supportsYuvReprocessing: Boolean
+        public val CameraMetadata.supportsYuvReprocessing: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_YUV_REPROCESSING)
 
-        val CameraMetadata.supportsDepthOutput: Boolean
+        public val CameraMetadata.supportsDepthOutput: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_DEPTH_OUTPUT)
 
-        val CameraMetadata.supportsHighSpeedVideo: Boolean
+        public val CameraMetadata.supportsHighSpeedVideo: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO)
 
-        val CameraMetadata.supportsMotionTracking: Boolean
+        public val CameraMetadata.supportsMotionTracking: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_MOTION_TRACKING)
 
-        val CameraMetadata.supportsLogicalMultiCamera: Boolean
+        public val CameraMetadata.supportsLogicalMultiCamera: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_LOGICAL_MULTI_CAMERA)
 
-        val CameraMetadata.supportsMonochrome: Boolean
+        public val CameraMetadata.supportsMonochrome: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_MONOCHROME)
 
-        val CameraMetadata.supportsSecureImageData: Boolean
+        public val CameraMetadata.supportsSecureImageData: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_SECURE_IMAGE_DATA)
 
-        val CameraMetadata.supportsSystemCamera: Boolean
+        public val CameraMetadata.supportsSystemCamera: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_SYSTEM_CAMERA)
 
-        val CameraMetadata.supportsOfflineReprocessing: Boolean
+        public val CameraMetadata.supportsOfflineReprocessing: Boolean
             get() = this.availableCapabilities.contains(CAPABILITIES_OFFLINE_REPROCESSING)
 
-        val CameraMetadata.supportsAutoFocusTrigger: Boolean
+        public val CameraMetadata.supportsAutoFocusTrigger: Boolean
             get() {
                 val minFocusDistance = this[CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE]
                 if (minFocusDistance != null) {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt
index 57c90726..724ff98 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraPipe.kt
@@ -53,38 +53,38 @@
  * the [CameraGraph] interface.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CameraPipe {
+public interface CameraPipe {
 
     /**
      * This creates a new [CameraGraph] that can be used to interact with a single Camera on the
      * device. Multiple [CameraGraph]s can be created, but only one should be active at a time.
      */
-    fun create(config: CameraGraph.Config): CameraGraph
+    public fun create(config: CameraGraph.Config): CameraGraph
 
     /**
      * This creates a list of [CameraGraph]s that can be used to interact with multiple cameras on
      * the device concurrently. Device-specific constraints may apply, such as the set of cameras
      * that can be operated concurrently, or the combination of sizes we're allowed to configure.
      */
-    fun createCameraGraphs(config: CameraGraph.ConcurrentConfig): List<CameraGraph>
+    public fun createCameraGraphs(config: CameraGraph.ConcurrentConfig): List<CameraGraph>
 
     /** This provides access to information about the available cameras on the device. */
-    fun cameras(): CameraDevices
+    public fun cameras(): CameraDevices
 
     /** This returns [CameraSurfaceManager] which tracks the lifetime of Surfaces in CameraPipe. */
-    fun cameraSurfaceManager(): CameraSurfaceManager
+    public fun cameraSurfaceManager(): CameraSurfaceManager
 
     /**
      * This gets and sets the global [AudioRestrictionMode] tracked by [AudioRestrictionController].
      */
-    var globalAudioRestrictionMode: AudioRestrictionMode
+    public var globalAudioRestrictionMode: AudioRestrictionMode
 
     /**
      * Application level configuration for [CameraPipe]. Nullable values are optional and reasonable
      * defaults will be provided if values are not specified.
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    data class Config(
+    public data class Config(
         val appContext: Context,
         val threadConfig: ThreadConfig = ThreadConfig(),
         val cameraMetadataConfig: CameraMetadataConfig = CameraMetadataConfig(),
@@ -97,7 +97,7 @@
      * Application level configuration for Camera2Interop callbacks. If set, these callbacks will be
      * triggered at the appropriate places in CameraPipe.
      */
-    data class CameraInteropConfig(
+    public data class CameraInteropConfig(
         val cameraDeviceStateCallback: CameraDevice.StateCallback? = null,
         val cameraSessionStateCallback: CameraCaptureSession.StateCallback? = null,
         val cameraOpenRetryMaxTimeoutNs: DurationNs? = null
@@ -117,7 +117,7 @@
      * - [testOnlyScope] is used for testing to overwrite the internal global scope with the test
      *   method scope.
      */
-    data class ThreadConfig(
+    public data class ThreadConfig(
         val defaultLightweightExecutor: Executor? = null,
         val defaultBackgroundExecutor: Executor? = null,
         val defaultBlockingExecutor: Executor? = null,
@@ -135,9 +135,10 @@
      * @param cameraCacheBlocklist is used to prevent the metadata backend from caching the results
      *   of specific keys for specific cameraIds.
      */
-    class CameraMetadataConfig(
-        val cacheBlocklist: Set<CameraCharacteristics.Key<*>> = emptySet(),
-        val cameraCacheBlocklist: Map<CameraId, Set<CameraCharacteristics.Key<*>>> = emptyMap()
+    public class CameraMetadataConfig(
+        public val cacheBlocklist: Set<CameraCharacteristics.Key<*>> = emptySet(),
+        public val cameraCacheBlocklist: Map<CameraId, Set<CameraCharacteristics.Key<*>>> =
+            emptyMap()
     )
 
     /**
@@ -152,10 +153,10 @@
      * @param cameraBackends defines a map of unique [CameraBackendFactory] that may be used to
      *   create, query, and operate cameras via [CameraPipe].
      */
-    class CameraBackendConfig(
-        val internalBackend: CameraBackend? = null,
-        val defaultBackend: CameraBackendId? = null,
-        val cameraBackends: Map<CameraBackendId, CameraBackendFactory> = emptyMap()
+    public class CameraBackendConfig(
+        public val internalBackend: CameraBackend? = null,
+        public val defaultBackend: CameraBackendId? = null,
+        public val cameraBackends: Map<CameraBackendId, CameraBackendFactory> = emptyMap()
     ) {
         init {
             check(defaultBackend == null || cameraBackends.containsKey(defaultBackend)) {
@@ -172,7 +173,7 @@
     @Deprecated(
         "CameraPipe.External is deprecated, use customCameraBackend on " + "GraphConfig instead."
     )
-    class External(threadConfig: ThreadConfig = ThreadConfig()) {
+    public class External(threadConfig: ThreadConfig = ThreadConfig()) {
         private val component: ExternalCameraPipeComponent =
             DaggerExternalCameraPipeComponent.builder()
                 .threadConfigModule(ThreadConfigModule(threadConfig))
@@ -187,7 +188,7 @@
             "CameraPipe.External is deprecated, use customCameraBackend on " +
                 "GraphConfig instead."
         )
-        fun create(
+        public fun create(
             config: CameraGraph.Config,
             cameraMetadata: CameraMetadata,
             requestProcessor: RequestProcessor
@@ -207,8 +208,8 @@
         }
     }
 
-    companion object {
-        fun create(config: Config): CameraPipe {
+    public companion object {
+        public fun create(config: Config): CameraPipe {
             val cameraPipeComponent =
                 Debug.trace("CameraPipe") {
                     DaggerCameraPipeComponent.builder()
@@ -224,7 +225,7 @@
 
 /** Utility constructor for existing classes that construct CameraPipe directly */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun CameraPipe(config: Config): CameraPipe = CameraPipe.create(config)
+public fun CameraPipe(config: Config): CameraPipe = CameraPipe.create(config)
 
 internal class CameraPipeImpl(private val component: CameraPipeComponent) : CameraPipe {
     private val debugId = cameraPipeIds.incrementAndGet()
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraSurfaceManager.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraSurfaceManager.kt
index 082245d..a00f9cf 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraSurfaceManager.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CameraSurfaceManager.kt
@@ -42,7 +42,7 @@
  * Essentially each token means a single use on a [Surface].
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class CameraSurfaceManager {
+public class CameraSurfaceManager {
 
     private val lock = Any()
 
@@ -54,7 +54,7 @@
      * A new [SurfaceToken] is issued when a [Surface] is registered in CameraSurfaceManager. When
      * all [SurfaceToken]s issued for a [Surface] is closed, the [Surface] is considered "inactive".
      */
-    inner class SurfaceToken(internal val surface: Surface) : AutoCloseable {
+    public inner class SurfaceToken(internal val surface: Surface) : AutoCloseable {
         private val debugId = surfaceTokenDebugIds.incrementAndGet()
         private val closed = atomic(false)
 
@@ -65,10 +65,10 @@
             }
         }
 
-        override fun toString() = "SurfaceToken-$debugId"
+        override fun toString(): String = "SurfaceToken-$debugId"
     }
 
-    interface SurfaceListener {
+    public interface SurfaceListener {
         /**
          * Called when a [Surface] is in use by a [CameraGraph]. Calling [CameraGraph.setSurface]
          * will cause [onSurfaceActive] to be called on any currently registered listener. The
@@ -76,7 +76,7 @@
          * been released (Normally this means that it will remain in use until the camera device is
          * closed, or until the CaptureSession that uses it is replaced).
          */
-        fun onSurfaceActive(surface: Surface)
+        public fun onSurfaceActive(surface: Surface)
 
         /**
          * Called when a [Surface] is considered "inactive" and no longer in use by [CameraGraph].
@@ -87,14 +87,14 @@
          * 3. [CameraGraph] is closed, and the [Surface] isn't not in use by some other camera
          *    subsystem.
          */
-        fun onSurfaceInactive(surface: Surface)
+        public fun onSurfaceInactive(surface: Surface)
     }
 
     /**
      * Adds a [SurfaceListener] to receive [Surface] lifetime updates. When a listener is added, it
      * will receive [SurfaceListener.onSurfaceActive] for all active Surfaces.
      */
-    fun addListener(listener: SurfaceListener) {
+    public fun addListener(listener: SurfaceListener) {
         val activeSurfaces =
             synchronized(lock) {
                 listeners.add(listener)
@@ -105,7 +105,7 @@
     }
 
     /** Removes a [SurfaceListener] to stop receiving [Surface] lifetime updates. */
-    fun removeListener(listener: SurfaceListener) {
+    public fun removeListener(listener: SurfaceListener) {
         synchronized(lock) { listeners.remove(listener) }
     }
 
@@ -166,8 +166,8 @@
         listenersToInvoke?.forEach { it.onSurfaceInactive(surface) }
     }
 
-    companion object {
-        const val DEBUG = false
+    public companion object {
+        public const val DEBUG: Boolean = false
 
         internal val surfaceTokenDebugIds = atomic(0)
     }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequence.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequence.kt
index 682e6d8..fa664a9 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequence.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequence.kt
@@ -26,30 +26,30 @@
  * A CaptureSequence should be created from a [CaptureSequenceProcessor].
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CaptureSequence<out TCaptureRequest> {
-    val cameraId: CameraId
-    val repeating: Boolean
-    val captureRequestList: List<TCaptureRequest>
-    val captureMetadataList: List<RequestMetadata>
-    val listeners: List<Request.Listener>
-    val sequenceListener: CaptureSequenceListener
+public interface CaptureSequence<out TCaptureRequest> {
+    public val cameraId: CameraId
+    public val repeating: Boolean
+    public val captureRequestList: List<TCaptureRequest>
+    public val captureMetadataList: List<RequestMetadata>
+    public val listeners: List<Request.Listener>
+    public val sequenceListener: CaptureSequenceListener
 
     /** This value must be set to the return value of [CaptureSequenceProcessor.submit] */
-    var sequenceNumber: Int
+    public var sequenceNumber: Int
 
-    interface CaptureSequenceListener {
-        fun onCaptureSequenceComplete(captureSequence: CaptureSequence<*>)
+    public interface CaptureSequenceListener {
+        public fun onCaptureSequenceComplete(captureSequence: CaptureSequence<*>)
     }
 }
 
 /** Utility functions for interacting with [CaptureSequence] callbacks and listeners. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-object CaptureSequences {
+public object CaptureSequences {
     /**
      * Efficient, inlined utility function for invoking a call on each of the listeners defined on a
      * [CaptureSequence] instance using the provided [RequestMetadata] object.
      */
-    inline fun <T> CaptureSequence<T>.invokeOnRequests(
+    public inline fun <T> CaptureSequence<T>.invokeOnRequests(
         crossinline fn: (RequestMetadata, Int, Request.Listener) -> Any
     ) {
         Debug.traceStart { "InvokeInternalListeners" }
@@ -80,7 +80,7 @@
      * Efficient, inlined utility function for invoking a call on each of the listeners defined on a
      * [CaptureSequence] instance using the provided [RequestMetadata] object.
      */
-    inline fun <T> CaptureSequence<T>.invokeOnRequest(
+    public inline fun <T> CaptureSequence<T>.invokeOnRequest(
         request: RequestMetadata,
         crossinline fn: (Request.Listener) -> Any
     ) {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequenceProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequenceProcessor.kt
index 1adfc504..5b4721e 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequenceProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/CaptureSequenceProcessor.kt
@@ -20,7 +20,7 @@
 
 /** Create and submit [CaptureSequence]s to an active camera instance. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface CaptureSequenceProcessor<
+public interface CaptureSequenceProcessor<
     out TCaptureRequest,
     TCaptureSequence : CaptureSequence<TCaptureRequest>
 > {
@@ -46,7 +46,7 @@
      *   been closed or disconnected, and will throw unchecked exceptions if invalid values are
      *   passed to the [build] call.
      */
-    fun build(
+    public fun build(
         isRepeating: Boolean,
         requests: List<Request>,
         defaultParameters: Map<*, Any?>,
@@ -56,20 +56,20 @@
     ): TCaptureSequence?
 
     /** Issue a previously created [CaptureSequence] to the active camera instance. */
-    fun submit(captureSequence: TCaptureSequence): Int?
+    public fun submit(captureSequence: TCaptureSequence): Int?
 
     /**
      * Opportunistically abort any ongoing captures by the camera. This may or may not complete
      * quickly depending on the underlying camera.
      */
-    fun abortCaptures()
+    public fun abortCaptures()
 
     /** Opportunistically cancel any currently active repeating [TCaptureSequence]. */
-    fun stopRepeating()
+    public fun stopRepeating()
 
     /**
      * Signal that this [CaptureSequenceProcessor] is no longer in use. Active requests may continue
      * to be processed, and [abortCaptures] and [stopRepeating] may still be invoked.
      */
-    fun close()
+    public fun close()
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frame.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frame.kt
index 5f0ad22..7485d71 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frame.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frame.kt
@@ -68,7 +68,7 @@
  * ```
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface Frame : FrameReference, AutoCloseable {
+public interface Frame : FrameReference, AutoCloseable {
     /**
      * Return the [FrameInfo], if available or suspend until the FrameInfo has been resolved.
      *
@@ -76,13 +76,13 @@
      * closed. If frameInfo is not available, [frameInfoStatus] can be used to understand why this
      * metadata is not available.
      */
-    suspend fun awaitFrameInfo(): FrameInfo?
+    public suspend fun awaitFrameInfo(): FrameInfo?
 
     /**
      * Return the [FrameInfo], if available, for this Frame. This method does not block and will
      * return null if the Frame has been closed, or if the [FrameInfo] has not yet been produced.
      */
-    fun getFrameInfo(): FrameInfo?
+    public fun getFrameInfo(): FrameInfo?
 
     /**
      * Return the [OutputImage] for this [streamId], if available or suspend until the output for
@@ -93,7 +93,7 @@
      * was not produced by the camera. Each call produces a unique [OutputImage] that *must* be
      * closed to avoid memory leaks.
      */
-    suspend fun awaitImage(streamId: StreamId): OutputImage?
+    public suspend fun awaitImage(streamId: StreamId): OutputImage?
 
     /**
      * Return the [OutputImage] for this [streamId], if available.
@@ -103,86 +103,88 @@
      * was not produced by the camera. Each call produces a unique [OutputImage] that *must* be
      * closed to avoid memory leaks.
      */
-    fun getImage(streamId: StreamId): OutputImage?
+    public fun getImage(streamId: StreamId): OutputImage?
 
     /**
      * Listener for non-coroutine based applications that may need to be notified when the state of
      * this [Frame] changes.
      */
-    fun addListener(listener: Listener)
+    public fun addListener(listener: Listener)
 
     /** Listener for events about an [Frame] */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    interface Listener {
+    public interface Listener {
         /**
          * Invoked after an [Frame] has been created and has started.
          *
          * @param frameNumber is the camera-provided identifier for this Frame.
          * @param frameTimestamp is the primary camera-provided timestamp for this Frame.
          */
-        fun onFrameStarted(frameNumber: FrameNumber, frameTimestamp: CameraTimestamp)
+        public fun onFrameStarted(frameNumber: FrameNumber, frameTimestamp: CameraTimestamp)
 
         /** Invoked after [FrameInfo] is available, or has failed to be produced. */
-        fun onFrameInfoAvailable()
+        public fun onFrameInfoAvailable()
 
         /** Invoked after the output for a given [StreamId] has been produced. */
-        fun onImageAvailable(streamId: StreamId)
+        public fun onImageAvailable(streamId: StreamId)
 
         /**
          * Invoked after *all* outputs for this [Frame] have been produced. This method will be
          * invoked after [onImageAvailable] has been invoked for all relevant streams, and will be
          * invoked immediately after [onFrameStarted] for frames that do not produce outputs.
          */
-        fun onImagesAvailable()
+        public fun onImagesAvailable()
 
         /** Invoked after the [FrameInfo] and all outputs have been completed for this [Frame]. */
-        fun onFrameComplete()
+        public fun onFrameComplete()
     }
 
-    companion object {
-        val Frame.request
+    public companion object {
+        public val Frame.request: Request
             get() = this.requestMetadata.request
 
-        val FrameReference.isFrameInfoAvailable
+        public val FrameReference.isFrameInfoAvailable: Boolean
             get() = this.frameInfoStatus == OutputStatus.AVAILABLE
 
-        fun FrameReference.isImageAvailable(streamId: StreamId) =
+        public fun FrameReference.isImageAvailable(streamId: StreamId): Boolean =
             this.imageStatus(streamId) == OutputStatus.AVAILABLE
     }
 }
 
 /** A [FrameId] a unique identifier that represents the order a [Frame] was produced in. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @JvmInline value class FrameId(val value: Long)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@JvmInline
+public value class FrameId(public val value: Long)
 
 /** Represents the status of an output from the camera with enum-like values. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class OutputStatus internal constructor(val value: Int) {
-    companion object {
+public value class OutputStatus internal constructor(public val value: Int) {
+    public companion object {
         /** Output is not yet available. */
-        val PENDING = OutputStatus(0)
+        public val PENDING: OutputStatus = OutputStatus(0)
 
         /** Output has arrived and is available. */
-        val AVAILABLE = OutputStatus(1)
+        public val AVAILABLE: OutputStatus = OutputStatus(1)
 
         /**
          * Output has been resolved, and is not available for some reason that is not due to Camera
          * operation, error, or other internal behavior. For example, if the object holding an
          * output is closed, the method to get the output may return [UNAVAILABLE].
          */
-        val UNAVAILABLE = OutputStatus(2)
+        public val UNAVAILABLE: OutputStatus = OutputStatus(2)
 
         /** Output is not available because the Camera reported an error for this output. */
-        val ERROR_OUTPUT_FAILED = OutputStatus(10)
+        public val ERROR_OUTPUT_FAILED: OutputStatus = OutputStatus(10)
 
         /** Output is not available because it was intentionally aborted, or arrived after close. */
-        val ERROR_OUTPUT_ABORTED = OutputStatus(11)
+        public val ERROR_OUTPUT_ABORTED: OutputStatus = OutputStatus(11)
 
         /**
          * Output is not available because it was unexpectedly dropped or failed to arrive from the
          * camera without some other kind of explicit error.
          */
-        val ERROR_OUTPUT_MISSING = OutputStatus(12)
+        public val ERROR_OUTPUT_MISSING: OutputStatus = OutputStatus(12)
 
         /**
          * Output is not available because it was intentionally dropped due to rate limiting. This
@@ -190,7 +192,7 @@
          * under normal usage, it can also indicate that some bit of code is not correctly closing
          * frames and/or images.
          */
-        val ERROR_OUTPUT_DROPPED = OutputStatus(13)
+        public val ERROR_OUTPUT_DROPPED: OutputStatus = OutputStatus(13)
     }
 }
 
@@ -229,12 +231,12 @@
  * ```
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface FrameCapture : AutoCloseable {
+public interface FrameCapture : AutoCloseable {
     /** The [Request] that was used to issue this [FrameCapture]. */
-    val request: Request
+    public val request: Request
 
     /** Get the status of the pending [Frame]. */
-    val status: OutputStatus
+    public val status: OutputStatus
 
     /**
      * Get or suspend until the [Frame] that will be produced by the camera for this [request] is
@@ -243,7 +245,7 @@
      * Invoking this multiple times will produce distinct Frame instances that will need to be
      * individually closed.
      */
-    suspend fun awaitFrame(): Frame?
+    public suspend fun awaitFrame(): Frame?
 
     /**
      * Get the [Frame] that will was produced by the camera for this [request] or null if the
@@ -252,10 +254,10 @@
      * Invoking this multiple times will produce distinct Frame instances that will need to be
      * individually closed.
      */
-    fun getFrame(): Frame?
+    public fun getFrame(): Frame?
 
     /** Adds a [Frame.Listener] that will be invoked for each of the subsequent [Frame] events. */
-    fun addListener(listener: Frame.Listener)
+    public fun addListener(listener: Frame.Listener)
 }
 
 /**
@@ -263,32 +265,32 @@
  * being closed or released unless the frame is acquired via [acquire] or [tryAcquire].
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface FrameReference {
+public interface FrameReference {
     /**
      * Metadata about the request that produced this [Frame].
      *
      * [RequestMetadata] includes any modifications to the original request that were made due to
      * 3A, Zoom, default and required parameters defined, and more.
      */
-    val requestMetadata: RequestMetadata
+    public val requestMetadata: RequestMetadata
 
     /**
      * The unique, sequential identifier defined by CameraPipe for this Frame. This identifier is
      * incremented each time a new exposure starts from the Camera.
      */
-    val frameId: FrameId
+    public val frameId: FrameId
 
     /** The original camera provided [FrameNumber] from this [Frame] */
-    val frameNumber: FrameNumber
+    public val frameNumber: FrameNumber
 
     /** The original camera provided [CameraTimestamp] from this [Frame] */
-    val frameTimestamp: CameraTimestamp
+    public val frameTimestamp: CameraTimestamp
 
     /** Get the current [OutputStatus] for the FrameInfo of this Frame. */
-    val frameInfoStatus: OutputStatus
+    public val frameInfoStatus: OutputStatus
 
     /** Get the current [OutputStatus] of the output for a given [streamId]. */
-    fun imageStatus(streamId: StreamId): OutputStatus
+    public fun imageStatus(streamId: StreamId): OutputStatus
 
     /**
      * [StreamId]'s that can be used to access [OutputImage]s from this [Frame] via [Frame.getImage]
@@ -296,20 +298,20 @@
      * **This may be different from the list of streams defined in the original [Request]!** since
      * this list will only include streams that were internally created and managed by CameraPipe.
      */
-    val imageStreams: Set<StreamId>
+    public val imageStreams: Set<StreamId>
 
     /**
      * Acquire a reference to a [Frame] that can be independently managed or closed. A filter can be
      * provided to limit which outputs are available.
      */
-    fun tryAcquire(streamFilter: Set<StreamId>? = null): Frame?
+    public fun tryAcquire(streamFilter: Set<StreamId>? = null): Frame?
 
-    companion object {
+    public companion object {
         /**
          * Acquire a [Frame] from a [FrameReference]. The outputs can be limited by specifying a
          * filter to restrict which outputs are acquired.
          */
-        fun FrameReference.acquire(streamFilter: Set<StreamId>? = null): Frame {
+        public fun FrameReference.acquire(streamFilter: Set<StreamId>? = null): Frame {
             return checkNotNull(tryAcquire(streamFilter)) {
                 "Failed to acquire a strong reference to $this!"
             }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frames.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frames.kt
index a434bb4..6079272 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frames.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Frames.kt
@@ -25,39 +25,41 @@
  * increase within a specific CameraCaptureSession, and are not created until the HAL begins
  * processing a request.
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @JvmInline value class FrameNumber(val value: Long)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@JvmInline
+public value class FrameNumber(public val value: Long)
 
 /** [FrameInfo] is a wrapper around [TotalCaptureResult]. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface FrameInfo : UnsafeWrapper {
-    val metadata: FrameMetadata
+public interface FrameInfo : UnsafeWrapper {
+    public val metadata: FrameMetadata
 
     /**
      * If this [FrameInfo] was produced from a logical camera there will be metadata associated with
      * the physical streams that were sent to the camera.
      */
-    operator fun get(camera: CameraId): FrameMetadata?
+    public operator fun get(camera: CameraId): FrameMetadata?
 
-    val camera: CameraId
-    val frameNumber: FrameNumber
-    val requestMetadata: RequestMetadata
+    public val camera: CameraId
+    public val frameNumber: FrameNumber
+    public val requestMetadata: RequestMetadata
 }
 
 /** [FrameMetadata] is a wrapper around [CaptureResult]. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface FrameMetadata : Metadata, UnsafeWrapper {
-    operator fun <T> get(key: CaptureResult.Key<T>): T?
+public interface FrameMetadata : Metadata, UnsafeWrapper {
+    public operator fun <T> get(key: CaptureResult.Key<T>): T?
 
-    fun <T> getOrDefault(key: CaptureResult.Key<T>, default: T): T
+    public fun <T> getOrDefault(key: CaptureResult.Key<T>, default: T): T
 
-    val camera: CameraId
-    val frameNumber: FrameNumber
+    public val camera: CameraId
+    public val frameNumber: FrameNumber
 
     /**
      * Extra metadata will override values defined by the wrapped CaptureResult object. This is
      * exposed separately to allow other systems to know what is altered relative to Camera2.
      */
-    val extraMetadata: Map<*, Any?>
+    public val extraMetadata: Map<*, Any?>
 }
 
 /**
@@ -68,7 +70,7 @@
  * metadata.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-data class MetadataTransform(
+public data class MetadataTransform(
     /**
      * This defines the number of historical [TotalCaptureResult] objects this transform is allowed
      * to look at. Setting this value to > 0 increases the number of [TotalCaptureResult] the
@@ -97,8 +99,8 @@
         check(future >= 0)
     }
 
-    interface TransformFn {
-        fun computeOverridesFor(
+    public interface TransformFn {
+        public fun computeOverridesFor(
             result: FrameInfo,
             camera: CameraId,
             related: List<FrameInfo?>
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt
index 72393ff..c9b4048 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Metadata.kt
@@ -27,22 +27,22 @@
  * These interfaces are read-only.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface Metadata {
-    operator fun <T> get(key: Key<T>): T?
+public interface Metadata {
+    public operator fun <T> get(key: Key<T>): T?
 
-    fun <T> getOrDefault(key: Key<T>, default: T): T
+    public fun <T> getOrDefault(key: Key<T>, default: T): T
 
     /** Metadata keys provide values or controls that are provided or computed by CameraPipe. */
     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-    class Key<T> private constructor(private val name: String) {
-        companion object {
+    public class Key<T> private constructor(private val name: String) {
+        public companion object {
             @JvmStatic internal val keys: MutableSet<String> = HashSet()
 
             /**
              * This will create a new Key instance, and will check to see that the key has not been
              * previously created somewhere else.
              */
-            fun <T> create(name: String): Key<T> {
+            public fun <T> create(name: String): Key<T> {
                 synchronized(keys) { check(keys.add(name)) { "$name is already defined!" } }
                 return Key(name)
             }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/RequestProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/RequestProcessor.kt
index 019d27b..a6b8514 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/RequestProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/RequestProcessor.kt
@@ -36,7 +36,7 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @Deprecated("Use CaptureSequence and CaptureSequenceProcessor instead.")
-interface RequestProcessor {
+public interface RequestProcessor {
 
     /**
      * Set the repeating [Request] with an optional set of parameters and listeners. Parameters are
@@ -54,7 +54,7 @@
      *   addition to listeners that are specified on each [Request]
      * @return false if the repeating request was not successfully updated.
      */
-    fun startRepeating(
+    public fun startRepeating(
         request: Request,
         defaultParameters: Map<*, Any?>,
         requiredParameters: Map<*, Any?>,
@@ -65,7 +65,7 @@
      * Stops the current repeating request, but does *not* close the session. The current repeating
      * request can be resumed by invoking [startRepeating] again.
      */
-    fun stopRepeating()
+    public fun stopRepeating()
 
     /**
      * Submit a single [Request] with optional sets of parameters and listeners. Parameters are
@@ -83,7 +83,7 @@
      *   addition to listeners that are specified on each [Request]
      * @return false if this request was not submitted to the camera for any reason.
      */
-    fun submit(
+    public fun submit(
         request: Request,
         defaultParameters: Map<*, Any?>,
         requiredParameters: Map<*, Any?>,
@@ -106,7 +106,7 @@
      *   addition to listeners that are specified on each [Request]
      * @return false if these requests were not submitted to the camera for any reason.
      */
-    fun submit(
+    public fun submit(
         requests: List<Request>,
         defaultParameters: Map<*, Any?>,
         requiredParameters: Map<*, Any?>,
@@ -114,11 +114,11 @@
     ): Boolean
 
     /** Abort requests that have been submitted but not completed. */
-    fun abortCaptures()
+    public fun abortCaptures()
 
     /**
      * Puts the RequestProcessor into a closed state where it should immediately reject all incoming
      * requests. This should NOT call stopRepeating() or abortCaptures().
      */
-    fun close()
+    public fun close()
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
index fcc3b6b..36d683a 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
@@ -30,7 +30,9 @@
  * A [RequestNumber] is an artificial identifier that is created for each request that is submitted
  * to the Camera.
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @JvmInline value class RequestNumber(val value: Long)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@JvmInline
+public value class RequestNumber(public val value: Long)
 
 /**
  * A [Request] is an immutable package of outputs and parameters needed to issue a [CaptureRequest]
@@ -50,17 +52,17 @@
  * @param streams The list of streams to submit. Each request *must* have 1 or more valid streams.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class Request(
-    val streams: List<StreamId>,
-    val parameters: Map<CaptureRequest.Key<*>, Any> = emptyMap(),
-    val extras: Map<Metadata.Key<*>, Any> = emptyMap(),
-    val listeners: List<Listener> = emptyList(),
-    val template: RequestTemplate? = null,
-    val inputRequest: InputRequest? = null
+public class Request(
+    public val streams: List<StreamId>,
+    public val parameters: Map<CaptureRequest.Key<*>, Any> = emptyMap(),
+    public val extras: Map<Metadata.Key<*>, Any> = emptyMap(),
+    public val listeners: List<Listener> = emptyList(),
+    public val template: RequestTemplate? = null,
+    public val inputRequest: InputRequest? = null
 ) {
-    operator fun <T> get(key: CaptureRequest.Key<T>): T? = getUnchecked(key)
+    public operator fun <T> get(key: CaptureRequest.Key<T>): T? = getUnchecked(key)
 
-    operator fun <T> get(key: Metadata.Key<T>): T? = getUnchecked(key)
+    public operator fun <T> get(key: Metadata.Key<T>): T? = getUnchecked(key)
 
     /**
      * This listener is used to observe the state and progress of a [Request] that has been issued
@@ -69,7 +71,7 @@
      * in a repeating request may be issued multiple times within the same session, and should not
      * rely on [onRequestSequenceSubmitted] from being invoked only once.
      */
-    interface Listener {
+    public interface Listener {
         /**
          * This event indicates that the camera sensor has started exposing the frame associated
          * with this Request. The timestamp will either be the beginning or end of the sensors
@@ -81,7 +83,7 @@
          * @param timestamp the android timestamp in nanos for this exposure
          * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureStarted
          */
-        fun onStarted(
+        public fun onStarted(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
             timestamp: CameraTimestamp
@@ -97,7 +99,7 @@
          * @param captureResult the current android capture result for this exposure
          * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureStarted
          */
-        fun onPartialCaptureResult(
+        public fun onPartialCaptureResult(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
             captureResult: FrameMetadata
@@ -114,7 +116,7 @@
          * @param totalCaptureResult the final android capture result for this exposure
          * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureStarted
          */
-        fun onTotalCaptureResult(
+        public fun onTotalCaptureResult(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
             totalCaptureResult: FrameInfo
@@ -131,7 +133,7 @@
          * @param frameNumber the android frame number for this exposure
          * @param result the package of metadata associated with this result.
          */
-        fun onComplete(
+        public fun onComplete(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
             result: FrameInfo
@@ -148,7 +150,7 @@
          * @param requestFailure the android [RequestFailure] data wrapper
          * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureFailed
          */
-        fun onFailed(
+        public fun onFailed(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
             requestFailure: RequestFailure
@@ -164,7 +166,7 @@
          * @param timestamp the android timestamp in nanos at the start of camera data readout.
          * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onReadoutStarted
          */
-        fun onReadoutStarted(
+        public fun onReadoutStarted(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
             timestamp: SensorTimestamp
@@ -180,7 +182,7 @@
          * @param stream the internal stream that will not receive a buffer for this frame.
          * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureBufferLost
          */
-        fun onBufferLost(
+        public fun onBufferLost(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
             stream: StreamId
@@ -195,7 +197,7 @@
          *
          * @param request information about this specific request.
          */
-        fun onAborted(request: Request) {}
+        public fun onAborted(request: Request) {}
 
         /**
          * Invoked after the CaptureRequest(s) have been created, but before the request is
@@ -204,7 +206,7 @@
          *
          * @param requestMetadata information about this specific request.
          */
-        fun onRequestSequenceCreated(requestMetadata: RequestMetadata) {}
+        public fun onRequestSequenceCreated(requestMetadata: RequestMetadata) {}
 
         /**
          * Invoked after the CaptureRequest(s) has been submitted. This method may be invoked
@@ -212,7 +214,7 @@
          *
          * @param requestMetadata the data about the camera2 request that was sent to the camera.
          */
-        fun onRequestSequenceSubmitted(requestMetadata: RequestMetadata) {}
+        public fun onRequestSequenceSubmitted(requestMetadata: RequestMetadata) {}
 
         /**
          * Invoked by Camera2 if the request was aborted after having been submitted. This method is
@@ -222,7 +224,7 @@
          * @see
          *   android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureSequenceAborted
          */
-        fun onRequestSequenceAborted(requestMetadata: RequestMetadata) {}
+        public fun onRequestSequenceAborted(requestMetadata: RequestMetadata) {}
 
         /**
          * Invoked by Camera2 if the request was completed after having been submitted. This method
@@ -234,7 +236,7 @@
          * @see
          *   android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureSequenceCompleted
          */
-        fun onRequestSequenceCompleted(
+        public fun onRequestSequenceCompleted(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber
         ) {}
@@ -269,22 +271,22 @@
  * prevents directly creating an instance of it.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface RequestFailure : UnsafeWrapper {
+public interface RequestFailure : UnsafeWrapper {
     /** Metadata about the request that has failed. */
-    val requestMetadata: RequestMetadata
+    public val requestMetadata: RequestMetadata
 
     /** The Camera [FrameNumber] for the request that has failed. */
-    val frameNumber: FrameNumber
+    public val frameNumber: FrameNumber
 
     /** Indicates the reason the particular request failed, see [CaptureFailure] for details. */
-    val reason: Int
+    public val reason: Int
 
     /**
      * Indicates if images were still captured for this request. If this is true, the camera should
      * invoke [Request.Listener.onBufferLost] individually for each output that failed. If this is
      * false, these outputs will never arrive, and the individual callbacks will not be invoked.
      */
-    val wasImageCaptured: Boolean
+    public val wasImageCaptured: Boolean
 }
 
 /**
@@ -293,8 +295,8 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class RequestTemplate(val value: Int) {
-    val name: String
+public value class RequestTemplate(public val value: Int) {
+    public val name: String
         get() {
             return when (value) {
                 1 -> "TEMPLATE_PREVIEW"
@@ -314,7 +316,7 @@
  * reprocessing.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-data class InputRequest(val image: ImageWrapper, val frameInfo: FrameInfo)
+public data class InputRequest(val image: ImageWrapper, val frameInfo: FrameInfo)
 
 /**
  * RequestMetadata wraps together all of the information about a specific CaptureRequest that was
@@ -325,29 +327,29 @@
  * different) from the request that was used to create the Camera2 [CaptureRequest].
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface RequestMetadata : Metadata, UnsafeWrapper {
-    operator fun <T> get(key: CaptureRequest.Key<T>): T?
+public interface RequestMetadata : Metadata, UnsafeWrapper {
+    public operator fun <T> get(key: CaptureRequest.Key<T>): T?
 
-    fun <T> getOrDefault(key: CaptureRequest.Key<T>, default: T): T
+    public fun <T> getOrDefault(key: CaptureRequest.Key<T>, default: T): T
 
     /** The actual Camera2 template that was used when creating this [CaptureRequest] */
-    val template: RequestTemplate
+    public val template: RequestTemplate
 
     /**
      * A Map of StreamId(s) that were submitted with this CaptureRequest and the Surface(s) used for
      * this request. It's possible that not all of the streamId's specified in the [Request] are
      * present in the [CaptureRequest].
      */
-    val streams: Map<StreamId, Surface>
+    public val streams: Map<StreamId, Surface>
 
     /** Returns true if this is used in a repeating request. */
-    val repeating: Boolean
+    public val repeating: Boolean
 
     /** The request object that was used to create this [CaptureRequest] */
-    val request: Request
+    public val request: Request
 
     /** An internal number used to identify a specific [CaptureRequest] */
-    val requestNumber: RequestNumber
+    public val requestNumber: RequestNumber
 }
 
 /**
@@ -361,26 +363,31 @@
  * operate based on a real-time clock, while audio/visual systems commonly operate based on a
  * monotonic clock.
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @JvmInline value class CameraTimestamp(val value: Long)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@JvmInline
+public value class CameraTimestamp(public val value: Long)
 
 /**
  * This is a timestamp happen at start of readout for a regular request, or the timestamp at the
  * input image's start of readout for a reprocess request, in nanoseconds.
  */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @JvmInline value class SensorTimestamp(val value: Long)
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+@JvmInline
+public value class SensorTimestamp(public val value: Long)
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun <T> Request.getOrDefault(key: Metadata.Key<T>, default: T): T = this[key] ?: default
+public fun <T> Request.getOrDefault(key: Metadata.Key<T>, default: T): T = this[key] ?: default
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun <T> Request.getOrDefault(key: CaptureRequest.Key<T>, default: T): T = this[key] ?: default
+public fun <T> Request.getOrDefault(key: CaptureRequest.Key<T>, default: T): T =
+    this[key] ?: default
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun Request.formatForLogs(): String = "Request($streams)@${Integer.toHexString(hashCode())}"
+public fun Request.formatForLogs(): String = "Request($streams)@${Integer.toHexString(hashCode())}"
 
 /** Utility function to help deal with the unsafe nature of the typed Key/Value pairs. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun CaptureRequest.Builder.writeParameters(parameters: Map<*, Any?>) {
+public fun CaptureRequest.Builder.writeParameters(parameters: Map<*, Any?>) {
     for ((key, value) in parameters) {
         writeParameter(key, value)
     }
@@ -388,7 +395,7 @@
 
 /** Utility function to help deal with the unsafe nature of the typed Key/Value pairs. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun CaptureRequest.Builder.writeParameter(key: Any?, value: Any?) {
+public fun CaptureRequest.Builder.writeParameter(key: Any?, value: Any?) {
     if (key != null && key is CaptureRequest.Key<*>) {
         try {
             @Suppress("UNCHECKED_CAST") this.set(key as CaptureRequest.Key<Any>, value)
@@ -407,6 +414,6 @@
  * cast is necessary since CameraGraph.Config uses Map<*, Any?> as the standard type for parameters.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-fun MutableMap<Any, Any?>.putAllMetadata(metadata: Map<*, Any?>) {
+public fun MutableMap<Any, Any?>.putAllMetadata(metadata: Map<*, Any?>) {
     @Suppress("UNCHECKED_CAST") this.putAll(metadata as Map<Any, Any?>)
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamFormat.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamFormat.kt
index fe76337..c18f657 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamFormat.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamFormat.kt
@@ -27,36 +27,36 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class StreamFormat(val value: Int) {
-    companion object {
-        val UNKNOWN: StreamFormat = StreamFormat(0)
-        val PRIVATE: StreamFormat = StreamFormat(0x22)
+public value class StreamFormat(public val value: Int) {
+    public companion object {
+        public val UNKNOWN: StreamFormat = StreamFormat(0)
+        public val PRIVATE: StreamFormat = StreamFormat(0x22)
 
-        val DEPTH16: StreamFormat = StreamFormat(0x44363159)
-        val DEPTH_JPEG: StreamFormat = StreamFormat(0x69656963)
-        val DEPTH_POINT_CLOUD: StreamFormat = StreamFormat(0x101)
-        val FLEX_RGB_888: StreamFormat = StreamFormat(0x29)
-        val FLEX_RGBA_8888: StreamFormat = StreamFormat(0x2A)
-        val HEIC: StreamFormat = StreamFormat(0x48454946)
-        val JPEG: StreamFormat = StreamFormat(0x100)
-        val JPEG_R: StreamFormat = StreamFormat(0x1005)
-        val NV16: StreamFormat = StreamFormat(0x10)
-        val NV21: StreamFormat = StreamFormat(0x11)
-        val RAW10: StreamFormat = StreamFormat(0x25)
-        val RAW12: StreamFormat = StreamFormat(0x26)
-        val RAW_DEPTH: StreamFormat = StreamFormat(0x1002)
-        val RAW_PRIVATE: StreamFormat = StreamFormat(0x24)
-        val RAW_SENSOR: StreamFormat = StreamFormat(0x20)
-        val RGB_565: StreamFormat = StreamFormat(4)
-        val Y12: StreamFormat = StreamFormat(0x32315659)
-        val Y16: StreamFormat = StreamFormat(0x20363159)
-        val Y8: StreamFormat = StreamFormat(0x20203859)
-        val YCBCR_P010: StreamFormat = StreamFormat(0x36)
-        val YUV_420_888: StreamFormat = StreamFormat(0x23)
-        val YUV_422_888: StreamFormat = StreamFormat(0x27)
-        val YUV_444_888: StreamFormat = StreamFormat(0x28)
-        val YUY2: StreamFormat = StreamFormat(0x14)
-        val YV12: StreamFormat = StreamFormat(0x32315659)
+        public val DEPTH16: StreamFormat = StreamFormat(0x44363159)
+        public val DEPTH_JPEG: StreamFormat = StreamFormat(0x69656963)
+        public val DEPTH_POINT_CLOUD: StreamFormat = StreamFormat(0x101)
+        public val FLEX_RGB_888: StreamFormat = StreamFormat(0x29)
+        public val FLEX_RGBA_8888: StreamFormat = StreamFormat(0x2A)
+        public val HEIC: StreamFormat = StreamFormat(0x48454946)
+        public val JPEG: StreamFormat = StreamFormat(0x100)
+        public val JPEG_R: StreamFormat = StreamFormat(0x1005)
+        public val NV16: StreamFormat = StreamFormat(0x10)
+        public val NV21: StreamFormat = StreamFormat(0x11)
+        public val RAW10: StreamFormat = StreamFormat(0x25)
+        public val RAW12: StreamFormat = StreamFormat(0x26)
+        public val RAW_DEPTH: StreamFormat = StreamFormat(0x1002)
+        public val RAW_PRIVATE: StreamFormat = StreamFormat(0x24)
+        public val RAW_SENSOR: StreamFormat = StreamFormat(0x20)
+        public val RGB_565: StreamFormat = StreamFormat(4)
+        public val Y12: StreamFormat = StreamFormat(0x32315659)
+        public val Y16: StreamFormat = StreamFormat(0x20363159)
+        public val Y8: StreamFormat = StreamFormat(0x20203859)
+        public val YCBCR_P010: StreamFormat = StreamFormat(0x36)
+        public val YUV_420_888: StreamFormat = StreamFormat(0x23)
+        public val YUV_422_888: StreamFormat = StreamFormat(0x27)
+        public val YUV_444_888: StreamFormat = StreamFormat(0x28)
+        public val YUY2: StreamFormat = StreamFormat(0x14)
+        public val YV12: StreamFormat = StreamFormat(0x32315659)
     }
 
     override fun toString(): String {
@@ -69,7 +69,7 @@
      * @return the number of bits per pixel or -1 if the format does not have a well defined number
      *   of bits per pixel.
      */
-    val bitsPerPixel: Int
+    public val bitsPerPixel: Int
         get() {
             when (this) {
                 DEPTH16 -> return 16
@@ -101,7 +101,7 @@
      *
      * @return a human readable string representation of the StreamFormat.
      */
-    val name: String
+    public val name: String
         get() {
             when (this) {
                 UNKNOWN -> return "UNKNOWN"
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamGraph.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamGraph.kt
index 324363a..37fefc4 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamGraph.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/StreamGraph.kt
@@ -24,15 +24,15 @@
  * [CameraStream]s can be used to build [Request]s that are sent to a [CameraGraph].
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface StreamGraph {
-    val streams: List<CameraStream>
-    val streamIds: Set<StreamId>
-    val inputs: List<InputStream>
-    val outputs: List<OutputStream>
+public interface StreamGraph {
+    public val streams: List<CameraStream>
+    public val streamIds: Set<StreamId>
+    public val inputs: List<InputStream>
+    public val outputs: List<OutputStream>
 
-    operator fun get(config: CameraStream.Config): CameraStream?
+    public operator fun get(config: CameraStream.Config): CameraStream?
 
-    operator fun get(streamId: StreamId): CameraStream? = streams.find { it.id == streamId }
+    public operator fun get(streamId: StreamId): CameraStream? = streams.find { it.id == streamId }
 
-    operator fun get(outputId: OutputId): OutputStream? = outputs.find { it.id == outputId }
+    public operator fun get(outputId: OutputId): OutputStream? = outputs.find { it.id == outputId }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Streams.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Streams.kt
index b1690ae..805aff9 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Streams.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Streams.kt
@@ -66,14 +66,15 @@
  *   ```
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class CameraStream internal constructor(val id: StreamId, val outputs: List<OutputStream>) {
+public class CameraStream
+internal constructor(public val id: StreamId, public val outputs: List<OutputStream>) {
     override fun toString(): String = id.toString()
 
     /** Configuration that may be used to define a [CameraStream] on a [CameraGraph] */
-    class Config
+    public class Config
     internal constructor(
-        val outputs: List<OutputStream.Config>,
-        val imageSourceConfig: ImageSourceConfig? = null
+        public val outputs: List<OutputStream.Config>,
+        public val imageSourceConfig: ImageSourceConfig? = null
     ) {
         init {
             val firstOutput = outputs.first()
@@ -82,9 +83,9 @@
             }
         }
 
-        companion object {
+        public companion object {
             /** Create a simple [CameraStream] to [OutputStream] configuration */
-            fun create(
+            public fun create(
                 size: Size,
                 format: StreamFormat,
                 camera: CameraId? = null,
@@ -117,18 +118,20 @@
              * Create a simple [CameraStream] using a previously defined [OutputStream.Config]. This
              * allows multiple [CameraStream]s to share the same [OutputConfiguration].
              */
-            fun create(output: OutputStream.Config, imageSourceConfig: ImageSourceConfig? = null) =
-                Config(listOf(output), imageSourceConfig)
+            public fun create(
+                output: OutputStream.Config,
+                imageSourceConfig: ImageSourceConfig? = null
+            ): Config = Config(listOf(output), imageSourceConfig)
 
             /**
              * Create a [CameraStream] from multiple [OutputStream.Config]s. This is used to to
              * define a [CameraStream] that may produce one or more of the outputs when used in a
              * request to the camera.
              */
-            fun create(
+            public fun create(
                 outputs: List<OutputStream.Config>,
                 imageSourceConfig: ImageSourceConfig? = null
-            ) = Config(outputs, imageSourceConfig)
+            ): Config = Config(outputs, imageSourceConfig)
         }
     }
 }
@@ -138,7 +141,7 @@
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class StreamId(val value: Int) {
+public value class StreamId(public val value: Int) {
     override fun toString(): String = "Stream-$value"
 }
 
@@ -149,20 +152,20 @@
  * represents one of those potential outputs.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface OutputStream {
+public interface OutputStream {
     // Every output comes from one, and exactly one, CameraStream
-    val stream: CameraStream
+    public val stream: CameraStream
 
-    val id: OutputId
-    val size: Size
-    val format: StreamFormat
-    val camera: CameraId
-    val mirrorMode: MirrorMode?
-    val timestampBase: TimestampBase?
-    val dynamicRangeProfile: DynamicRangeProfile?
-    val streamUseCase: StreamUseCase?
-    val outputType: OutputType?
-    val streamUseHint: StreamUseHint?
+    public val id: OutputId
+    public val size: Size
+    public val format: StreamFormat
+    public val camera: CameraId
+    public val mirrorMode: MirrorMode?
+    public val timestampBase: TimestampBase?
+    public val dynamicRangeProfile: DynamicRangeProfile?
+    public val streamUseCase: StreamUseCase?
+    public val outputType: OutputType?
+    public val streamUseHint: StreamUseHint?
 
     // TODO: Consider adding sensor mode and/or other metadata
 
@@ -170,19 +173,19 @@
      * Configuration object that provides the parameters for a specific input / output stream on
      * Camera.
      */
-    sealed class Config(
-        val size: Size,
-        val format: StreamFormat,
-        val camera: CameraId?,
-        val mirrorMode: MirrorMode?,
-        val timestampBase: TimestampBase?,
-        val dynamicRangeProfile: DynamicRangeProfile?,
-        val streamUseCase: StreamUseCase?,
-        val streamUseHint: StreamUseHint?,
-        val sensorPixelModes: List<SensorPixelMode>,
+    public sealed class Config(
+        public val size: Size,
+        public val format: StreamFormat,
+        public val camera: CameraId?,
+        public val mirrorMode: MirrorMode?,
+        public val timestampBase: TimestampBase?,
+        public val dynamicRangeProfile: DynamicRangeProfile?,
+        public val streamUseCase: StreamUseCase?,
+        public val streamUseHint: StreamUseHint?,
+        public val sensorPixelModes: List<SensorPixelMode>,
     ) {
-        companion object {
-            fun create(
+        public companion object {
+            public fun create(
                 size: Size,
                 format: StreamFormat,
                 camera: CameraId? = null,
@@ -227,7 +230,7 @@
 
             /** Create a stream configuration from an externally created [OutputConfiguration] */
             @RequiresApi(33)
-            fun external(
+            public fun external(
                 size: Size,
                 format: StreamFormat,
                 camera: CameraId? = null,
@@ -335,11 +338,11 @@
             )
     }
 
-    class OutputType private constructor() {
-        companion object {
-            val SURFACE = OutputType()
-            val SURFACE_VIEW = OutputType()
-            val SURFACE_TEXTURE = OutputType()
+    public class OutputType private constructor() {
+        public companion object {
+            public val SURFACE: OutputType = OutputType()
+            public val SURFACE_VIEW: OutputType = OutputType()
+            public val SURFACE_TEXTURE: OutputType = OutputType()
         }
     }
 
@@ -352,12 +355,12 @@
      * See the documentation on [OutputConfiguration.setMirrorMode] for more details.
      */
     @JvmInline
-    value class MirrorMode(val value: Int) {
-        companion object {
-            val MIRROR_MODE_AUTO = MirrorMode(0)
-            val MIRROR_MODE_NONE = MirrorMode(1)
-            val MIRROR_MODE_H = MirrorMode(2)
-            val MIRROR_MODE_V = MirrorMode(3)
+    public value class MirrorMode(public val value: Int) {
+        public companion object {
+            public val MIRROR_MODE_AUTO: MirrorMode = MirrorMode(0)
+            public val MIRROR_MODE_NONE: MirrorMode = MirrorMode(1)
+            public val MIRROR_MODE_H: MirrorMode = MirrorMode(2)
+            public val MIRROR_MODE_V: MirrorMode = MirrorMode(3)
         }
     }
 
@@ -369,13 +372,13 @@
      * See the documentation on [OutputConfiguration.setTimestampBase] for more details.
      */
     @JvmInline
-    value class TimestampBase(val value: Int) {
-        companion object {
-            val TIMESTAMP_BASE_DEFAULT = TimestampBase(0)
-            val TIMESTAMP_BASE_SENSOR = TimestampBase(1)
-            val TIMESTAMP_BASE_MONOTONIC = TimestampBase(2)
-            val TIMESTAMP_BASE_REALTIME = TimestampBase(3)
-            val TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED = TimestampBase(4)
+    public value class TimestampBase(public val value: Int) {
+        public companion object {
+            public val TIMESTAMP_BASE_DEFAULT: TimestampBase = TimestampBase(0)
+            public val TIMESTAMP_BASE_SENSOR: TimestampBase = TimestampBase(1)
+            public val TIMESTAMP_BASE_MONOTONIC: TimestampBase = TimestampBase(2)
+            public val TIMESTAMP_BASE_REALTIME: TimestampBase = TimestampBase(3)
+            public val TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED: TimestampBase = TimestampBase(4)
         }
     }
 
@@ -387,21 +390,21 @@
      * See the documentation on [OutputConfiguration.setDynamicRangeProfile] for more details.
      */
     @JvmInline
-    value class DynamicRangeProfile(val value: Long) {
-        companion object {
-            val STANDARD = DynamicRangeProfile(1)
-            val HLG10 = DynamicRangeProfile(2)
-            val HDR10 = DynamicRangeProfile(4)
-            val HDR10_PLUS = DynamicRangeProfile(8)
-            val DOLBY_VISION_10B_HDR_REF = DynamicRangeProfile(16)
-            val DOLBY_VISION_10B_HDR_REF_PO = DynamicRangeProfile(32)
-            val DOLBY_VISION_10B_HDR_OEM = DynamicRangeProfile(64)
-            val DOLBY_VISION_10B_HDR_OEM_PO = DynamicRangeProfile(128)
-            val DOLBY_VISION_8B_HDR_REF = DynamicRangeProfile(256)
-            val DOLBY_VISION_8B_HDR_REF_PO = DynamicRangeProfile(512)
-            val DOLBY_VISION_8B_HDR_OEM = DynamicRangeProfile(1024)
-            val DOLBY_VISION_8B_HDR_OEM_PO = DynamicRangeProfile(2048)
-            val PUBLIC_MAX = DynamicRangeProfile(4096)
+    public value class DynamicRangeProfile(public val value: Long) {
+        public companion object {
+            public val STANDARD: DynamicRangeProfile = DynamicRangeProfile(1)
+            public val HLG10: DynamicRangeProfile = DynamicRangeProfile(2)
+            public val HDR10: DynamicRangeProfile = DynamicRangeProfile(4)
+            public val HDR10_PLUS: DynamicRangeProfile = DynamicRangeProfile(8)
+            public val DOLBY_VISION_10B_HDR_REF: DynamicRangeProfile = DynamicRangeProfile(16)
+            public val DOLBY_VISION_10B_HDR_REF_PO: DynamicRangeProfile = DynamicRangeProfile(32)
+            public val DOLBY_VISION_10B_HDR_OEM: DynamicRangeProfile = DynamicRangeProfile(64)
+            public val DOLBY_VISION_10B_HDR_OEM_PO: DynamicRangeProfile = DynamicRangeProfile(128)
+            public val DOLBY_VISION_8B_HDR_REF: DynamicRangeProfile = DynamicRangeProfile(256)
+            public val DOLBY_VISION_8B_HDR_REF_PO: DynamicRangeProfile = DynamicRangeProfile(512)
+            public val DOLBY_VISION_8B_HDR_OEM: DynamicRangeProfile = DynamicRangeProfile(1024)
+            public val DOLBY_VISION_8B_HDR_OEM_PO: DynamicRangeProfile = DynamicRangeProfile(2048)
+            public val PUBLIC_MAX: DynamicRangeProfile = DynamicRangeProfile(4096)
         }
     }
 
@@ -410,11 +413,11 @@
      * temporarily be used to give a hint on the purpose of the stream.
      */
     @JvmInline
-    value class StreamUseHint(val value: Long) {
+    public value class StreamUseHint(public val value: Long) {
 
-        companion object {
-            val DEFAULT = StreamUseHint(0)
-            val VIDEO_RECORD = StreamUseHint(1)
+        public companion object {
+            public val DEFAULT: StreamUseHint = StreamUseHint(0)
+            public val VIDEO_RECORD: StreamUseHint = StreamUseHint(1)
         }
     }
 
@@ -427,14 +430,14 @@
      * See the documentation on [OutputConfiguration.setStreamUseCase] for more details.
      */
     @JvmInline
-    value class StreamUseCase(val value: Long) {
-        companion object {
-            val DEFAULT = StreamUseCase(0)
-            val PREVIEW = StreamUseCase(1)
-            val STILL_CAPTURE = StreamUseCase(2)
-            val VIDEO_RECORD = StreamUseCase(3)
-            val PREVIEW_VIDEO_STILL = StreamUseCase(4)
-            val VIDEO_CALL = StreamUseCase(5)
+    public value class StreamUseCase(public val value: Long) {
+        public companion object {
+            public val DEFAULT: StreamUseCase = StreamUseCase(0)
+            public val PREVIEW: StreamUseCase = StreamUseCase(1)
+            public val STILL_CAPTURE: StreamUseCase = StreamUseCase(2)
+            public val VIDEO_RECORD: StreamUseCase = StreamUseCase(3)
+            public val PREVIEW_VIDEO_STILL: StreamUseCase = StreamUseCase(4)
+            public val VIDEO_CALL: StreamUseCase = StreamUseCase(5)
         }
     }
 
@@ -444,10 +447,10 @@
      * See the documentation on [OutputConfiguration.addSensorPixelModeUsed] for more details.
      */
     @JvmInline
-    value class SensorPixelMode(val value: Int) {
-        companion object {
-            val DEFAULT = SensorPixelMode(0)
-            val MAXIMUM_RESOLUTION = SensorPixelMode(1)
+    public value class SensorPixelMode(public val value: Int) {
+        public companion object {
+            public val DEFAULT: SensorPixelMode = SensorPixelMode(0)
+            public val MAXIMUM_RESOLUTION: SensorPixelMode = SensorPixelMode(1)
         }
     }
 
@@ -460,7 +463,7 @@
      * [Camera2 reference]
      * [https://developer.android.com/reference/android/hardware/camera2/CameraDevice#constrained-high-speed-recording]
      */
-    fun isValidForHighSpeedOperatingMode(): Boolean {
+    public fun isValidForHighSpeedOperatingMode(): Boolean {
         return this.streamUseCase == null ||
             this.streamUseCase == OutputStream.StreamUseCase.DEFAULT ||
             this.streamUseCase == OutputStream.StreamUseCase.PREVIEW ||
@@ -473,37 +476,37 @@
 
 /** Configuration for a CameraStream that will be internally configured to produce images. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class ImageSourceConfig(
-    val capacity: Int,
-    val usageFlags: Long? = null,
-    val defaultDataSpace: Int? = null,
-    val defaultHardwareBufferFormat: Int? = null
+public class ImageSourceConfig(
+    public val capacity: Int,
+    public val usageFlags: Long? = null,
+    public val defaultDataSpace: Int? = null,
+    public val defaultHardwareBufferFormat: Int? = null
 )
 
 /** This identifies a single output. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class OutputId(val value: Int) {
+public value class OutputId(public val value: Int) {
     override fun toString(): String = "Output-$value"
 }
 
 /** Configuration for defining the properties of a Camera2 InputStream for reprocessing requests. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface InputStream {
-    val id: InputStreamId
-    val maxImages: Int
-    val format: StreamFormat
+public interface InputStream {
+    public val id: InputStreamId
+    public val maxImages: Int
+    public val format: StreamFormat
 
-    class Config(
-        val stream: CameraStream.Config,
-        val maxImages: Int,
-        var streamFormat: StreamFormat
+    public class Config(
+        public val stream: CameraStream.Config,
+        public val maxImages: Int,
+        public var streamFormat: StreamFormat
     )
 }
 
 /** This identifies a single input. */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @JvmInline
-value class InputStreamId(val value: Int) {
+public value class InputStreamId(public val value: Int) {
     override fun toString(): String = "Input-$value"
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/UnsafeWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/UnsafeWrapper.kt
index 8366037..e59878b 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/UnsafeWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/UnsafeWrapper.kt
@@ -28,7 +28,7 @@
  * of the object is managed by CameraPipe.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-interface UnsafeWrapper {
+public interface UnsafeWrapper {
     /**
      * Attempt to unwrap this object into an underlying type.
      *
@@ -39,5 +39,5 @@
      *
      * @return unwrapped object matching T or null
      */
-    fun <T : Any> unwrapAs(type: KClass<T>): T?
+    public fun <T : Any> unwrapAs(type: KClass<T>): T?
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt
index 765625a..1b9077f 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt
@@ -26,7 +26,7 @@
  * This class implements the [RequestFailure] interface by passing the package-private
  * [CaptureFailure] object.
  */
-class AndroidCaptureFailure(
+public class AndroidCaptureFailure(
     override val requestMetadata: RequestMetadata,
     private val captureFailure: CaptureFailure
 ) : RequestFailure {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt
index 781ba85..3ab4eb5 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ApiCompat.kt
@@ -497,6 +497,12 @@
     ): Boolean = extensionCharacteristics.isPostviewAvailable(extension)
 
     @JvmStatic
+    fun isCaptureProcessProgressAvailable(
+        extensionCharacteristics: CameraExtensionCharacteristics,
+        extension: Int
+    ): Boolean = extensionCharacteristics.isCaptureProcessProgressAvailable(extension)
+
+    @JvmStatic
     fun getPostviewSupportedSizes(
         extensionCharacteristics: CameraExtensionCharacteristics,
         extension: Int,
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AudioRestrictionController.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AudioRestrictionController.kt
index cba71e8..5a1ac48 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AudioRestrictionController.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AudioRestrictionController.kt
@@ -31,34 +31,37 @@
  * AudioRestrictionController keeps the global audio restriction mode and audio restriction mode on
  * each CameraGraph, and computes the final audio restriction mode based on the settings.
  */
-interface AudioRestrictionController {
+public interface AudioRestrictionController {
     /** Public global audio restriction mode across all CameraGraph instances. */
-    var globalAudioRestrictionMode: AudioRestrictionMode
+    public var globalAudioRestrictionMode: AudioRestrictionMode
 
     /** Update the audio restriction mode of the given CameraGraph. */
-    fun updateCameraGraphAudioRestrictionMode(cameraGraph: CameraGraph, mode: AudioRestrictionMode)
+    public fun updateCameraGraphAudioRestrictionMode(
+        cameraGraph: CameraGraph,
+        mode: AudioRestrictionMode
+    )
 
     /** Removes the CameraGraph from the local CameraGraph to audio restriction mode mapping. */
-    fun removeCameraGraph(cameraGraph: CameraGraph)
+    public fun removeCameraGraph(cameraGraph: CameraGraph)
 
     /** Adds the listener to the controller's stored collection of listeners. */
-    fun addListener(listener: Listener)
+    public fun addListener(listener: Listener)
 
     /** Removes the listener to the controller's stored collection of listeners. */
-    fun removeListener(listener: Listener)
+    public fun removeListener(listener: Listener)
 
     /**
      * [CameraDeviceWrapper] extends the [Listener]. When audio restriction mode changes, the
      * listener's update method would be invoked.
      */
-    interface Listener {
+    public interface Listener {
         /** @see CameraDevice.getCameraAudioRestriction */
-        fun onCameraAudioRestrictionUpdated(mode: AudioRestrictionMode)
+        public fun onCameraAudioRestrictionUpdated(mode: AudioRestrictionMode)
     }
 }
 
 @Singleton
-class AudioRestrictionControllerImpl @Inject constructor() : AudioRestrictionController {
+public class AudioRestrictionControllerImpl @Inject constructor() : AudioRestrictionController {
     private val lock = Any()
     override var globalAudioRestrictionMode: AudioRestrictionMode = AUDIO_RESTRICTION_NONE
         get() = synchronized(lock) { field }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraExtensionMetadata.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraExtensionMetadata.kt
index 26b11b3..bb778e1 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraExtensionMetadata.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraExtensionMetadata.kt
@@ -16,6 +16,7 @@
 
 package androidx.camera.camera2.pipe.compat
 
+import android.hardware.camera2.CameraCharacteristics
 import android.hardware.camera2.CameraExtensionCharacteristics
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.CaptureResult
@@ -26,8 +27,8 @@
 import androidx.camera.camera2.pipe.CameraExtensionMetadata
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.Metadata
-import androidx.camera.camera2.pipe.core.Debug
-import androidx.camera.camera2.pipe.core.Log
+import androidx.camera.camera2.pipe.core.lazyOrEmptySet
+import androidx.camera.camera2.pipe.core.lazyOrFalse
 import kotlin.reflect.KClass
 
 /**
@@ -36,7 +37,6 @@
  * This allows all fields to be accessed and return reasonable values on all OS versions.
  */
 @RequiresApi(Build.VERSION_CODES.S)
-// TODO(b/200306659): Remove and replace with annotation on package-info.java
 internal class Camera2CameraExtensionMetadata(
     override val camera: CameraId,
     override val isRedacted: Boolean,
@@ -53,9 +53,16 @@
     @GuardedBy("supportedPostviewSizes")
     private val supportedPostviewSizes = mutableMapOf<Size, Lazy<Set<Size>>>()
 
-    // TODO: b/299356087 - this here may need a switch statement on the key
+    override fun <T> get(key: CameraCharacteristics.Key<T>): T? {
+        return null // TODO: Add support for this when VIC can be targeted in AndroidX
+    }
+
     @Suppress("UNCHECKED_CAST") override fun <T> get(key: Metadata.Key<T>): T? = metadata[key] as T?
 
+    override fun <T> getOrDefault(key: CameraCharacteristics.Key<T>, default: T): T {
+        return default // TODO: Add support for this when VIC can be targeted in AndroidX
+    }
+
     @Suppress("UNCHECKED_CAST")
     override fun <T> getOrDefault(key: Metadata.Key<T>, default: T): T =
         metadata[key] as T? ?: default
@@ -70,6 +77,12 @@
     override val isPostviewSupported: Boolean
         get() = _isPostviewSupported.value
 
+    override val isCaptureProgressSupported: Boolean
+        get() = _isCaptureProgressSupported.value
+
+    override val keys: Set<CameraCharacteristics.Key<*>>
+        get() = emptySet() // TODO: Add support for this when VIC can be targeted in AndroidX
+
     override val requestKeys: Set<CaptureRequest.Key<*>>
         get() = _requestKeys.value
 
@@ -77,10 +90,10 @@
         get() = _resultKeys.value
 
     override fun getOutputSizes(imageFormat: Int): Set<Size> {
-        val supportedExtensionSizes =
+        val lazySizes =
             synchronized(supportedExtensionSizesByFormat) {
                 supportedExtensionSizesByFormat.getOrPut(imageFormat) {
-                    lazy(LazyThreadSafetyMode.PUBLICATION) {
+                    lazyOrEmptySet({ "$camera#getExtensionSupportedSizes(${imageFormat})" }) {
                         Api31Compat.getExtensionSupportedSizes(
                                 extensionCharacteristics,
                                 cameraExtension,
@@ -90,14 +103,14 @@
                     }
                 }
             }
-        return supportedExtensionSizes.value
+        return lazySizes.value
     }
 
     override fun getOutputSizes(klass: Class<*>): Set<Size> {
-        val supportedExtensionSizes =
+        val lazySizes =
             synchronized(supportedExtensionSizesByClass) {
                 supportedExtensionSizesByClass.getOrPut(klass) {
-                    lazy(LazyThreadSafetyMode.PUBLICATION) {
+                    lazyOrEmptySet("$camera#getExtensionSupportedSizes(${klass.name})") {
                         Api31Compat.getExtensionSupportedSizes(
                                 extensionCharacteristics,
                                 cameraExtension,
@@ -107,82 +120,71 @@
                     }
                 }
             }
-        return supportedExtensionSizes.value
+        return lazySizes.value
     }
 
     override fun getPostviewSizes(captureSize: Size, format: Int): Set<Size> {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
-            val supportedPostviewSizes =
-                synchronized(supportedPostviewSizes) {
-                    supportedPostviewSizes.getOrPut(captureSize) {
-                        lazy(LazyThreadSafetyMode.PUBLICATION) {
-                            Api34Compat.getPostviewSupportedSizes(
-                                    extensionCharacteristics,
-                                    cameraExtension,
-                                    captureSize,
-                                    format
-                                )
-                                .toSet()
-                        }
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+            return emptySet()
+        }
+
+        val lazySizes =
+            synchronized(supportedPostviewSizes) {
+                supportedPostviewSizes.getOrPut(captureSize) {
+                    lazyOrEmptySet("$camera#getPostviewSupportedSizes($captureSize, $format)") {
+                        Api34Compat.getPostviewSupportedSizes(
+                                extensionCharacteristics,
+                                cameraExtension,
+                                captureSize,
+                                format
+                            )
+                            .toSet()
                     }
                 }
-            return supportedPostviewSizes.value
-        }
-        return emptySet()
+            }
+        return lazySizes.value
     }
 
     private val _requestKeys: Lazy<Set<CaptureRequest.Key<*>>> =
-        lazy(LazyThreadSafetyMode.PUBLICATION) {
-            try {
-                Debug.trace("Camera-$camera#availableCaptureRequestKeys") {
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-                        Api33Compat.getAvailableCaptureRequestKeys(
-                                extensionCharacteristics,
-                                cameraExtension
-                            )
-                            .toSet()
-                    } else {
-                        emptySet()
-                    }
-                }
-            } catch (e: AssertionError) {
-                Log.warn(e) { "Failed to getAvailableCaptureRequestKeys from Camera-$camera" }
+        lazyOrEmptySet({ "$camera#availableCaptureRequestKeys" }) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                Api33Compat.getAvailableCaptureRequestKeys(
+                        extensionCharacteristics,
+                        cameraExtension
+                    )
+                    .toSet()
+            } else {
                 emptySet()
             }
         }
 
     private val _resultKeys: Lazy<Set<CaptureResult.Key<*>>> =
-        lazy(LazyThreadSafetyMode.PUBLICATION) {
-            try {
-                Debug.trace("Camera-$camera#availableCaptureResultKeys") {
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
-                        Api33Compat.getAvailableCaptureResultKeys(
-                                extensionCharacteristics,
-                                cameraExtension
-                            )
-                            .toSet()
-                    } else {
-                        emptySet()
-                    }
-                }
-            } catch (e: AssertionError) {
-                Log.warn(e) { "Failed to getAvailableCaptureResultKeys from Camera-$camera" }
+        lazyOrEmptySet({ "$camera#availableCaptureResultKeys" }) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+                Api33Compat.getAvailableCaptureResultKeys(extensionCharacteristics, cameraExtension)
+                    .toSet()
+            } else {
                 emptySet()
             }
         }
 
     private val _isPostviewSupported: Lazy<Boolean> =
-        lazy(LazyThreadSafetyMode.PUBLICATION) {
-            try {
-                Debug.trace("Camera-$camera#isPostviewSupported") {
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
-                        Api34Compat.isPostviewAvailable(extensionCharacteristics, cameraExtension)
-                    } else {
-                        false
-                    }
-                }
-            } catch (e: AssertionError) {
-                Log.warn(e) { "Failed to get isPostviewSupported from Camera-$camera" }
+        lazyOrFalse({ "$camera#isPostviewSupported" }) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+                Api34Compat.isPostviewAvailable(extensionCharacteristics, cameraExtension)
+            } else {
+                false
+            }
+        }
+
+    private val _isCaptureProgressSupported: Lazy<Boolean> =
+        lazyOrFalse({ "$camera#isCaptureProgressSupported" }) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+                Api34Compat.isCaptureProcessProgressAvailable(
+                    extensionCharacteristics,
+                    cameraExtension
+                )
+            } else {
                 false
             }
         }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraMetadata.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraMetadata.kt
index 5c82c66..eef6f5a 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraMetadata.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CameraMetadata.kt
@@ -17,7 +17,6 @@
 package androidx.camera.camera2.pipe.compat
 
 import android.hardware.camera2.CameraCharacteristics
-import android.hardware.camera2.CameraExtensionCharacteristics
 import android.hardware.camera2.CaptureRequest
 import android.hardware.camera2.CaptureResult
 import android.os.Build
@@ -130,10 +129,6 @@
         return metadataProvider.awaitCameraMetadata(cameraId)
     }
 
-    private fun getExtensionCharacteristics(): CameraExtensionCharacteristics {
-        return metadataProvider.getCameraExtensionCharacteristics(camera)
-    }
-
     override suspend fun getExtensionMetadata(extension: Int): CameraExtensionMetadata {
         val existing = synchronized(extensionCache) { extensionCache[extension] }
         return if (existing != null) {
@@ -160,12 +155,7 @@
         lazy(LazyThreadSafetyMode.PUBLICATION) {
             try {
                 Debug.trace("Camera-$camera#supportedExtensions") {
-                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
-                        val extensionCharacteristics = getExtensionCharacteristics()
-                        Api31Compat.getSupportedExtensions(extensionCharacteristics).toSet()
-                    } else {
-                        emptySet()
-                    }
+                    metadataProvider.getSupportedCameraExtensions(camera)
                 }
             } catch (e: AssertionError) {
                 Log.warn(e) { "Failed to getSupportedExtensions from Camera-$camera" }
@@ -176,7 +166,10 @@
     private val _keys: Lazy<Set<CameraCharacteristics.Key<*>>> =
         lazy(LazyThreadSafetyMode.PUBLICATION) {
             try {
-                Debug.trace("$camera#keys") { characteristics.keys.orEmpty().toSet() }
+                Debug.trace("$camera#keys") {
+                    @Suppress("UselessCallOnNotNull") // Untrusted API
+                    characteristics.keys.orEmpty().toSet()
+                }
             } catch (e: AssertionError) {
                 Log.warn(e) { "Failed to getKeys from $camera}" }
                 emptySet()
@@ -187,6 +180,7 @@
         lazy(LazyThreadSafetyMode.PUBLICATION) {
             try {
                 Debug.trace("$camera#availableCaptureRequestKeys") {
+                    @Suppress("UselessCallOnNotNull") // Untrusted API
                     characteristics.availableCaptureRequestKeys.orEmpty().toSet()
                 }
             } catch (e: AssertionError) {
@@ -199,6 +193,7 @@
         lazy(LazyThreadSafetyMode.PUBLICATION) {
             try {
                 Debug.trace("$camera#availableCaptureResultKeys") {
+                    @Suppress("UselessCallOnNotNull") // Untrusted API
                     characteristics.availableCaptureResultKeys.orEmpty().toSet()
                 }
             } catch (e: AssertionError) {
@@ -271,7 +266,7 @@
             return this.get(key)
         } catch (exception: AssertionError) {
             throw IllegalStateException(
-                "Failed to get characteristic for $key: " + "Framework throw an AssertionError"
+                "Failed to get characteristic for $key: Framework throw an AssertionError"
             )
         }
     }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2ErrorProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2ErrorProcessor.kt
index 8c6f47d..048d080 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2ErrorProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2ErrorProcessor.kt
@@ -30,7 +30,7 @@
  * should update CameraErrorProcessor with the [VirtualCameraState] that came with the open request.
  */
 @Singleton
-class Camera2ErrorProcessor @Inject constructor() : CameraErrorListener {
+public class Camera2ErrorProcessor @Inject constructor() : CameraErrorListener {
     private val lock = Any()
 
     @GuardedBy("lock")
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataCache.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataCache.kt
index d32fad1..3ee3ee4 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataCache.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataCache.kt
@@ -138,6 +138,14 @@
         }
     }
 
+    override fun getSupportedCameraExtensions(cameraId: CameraId): Set<Int> {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+            val extensionCharacteristics = getCameraExtensionCharacteristics(cameraId)
+            return Api31Compat.getSupportedExtensions(extensionCharacteristics).toSet()
+        }
+        return emptySet()
+    }
+
     private fun createCameraMetadata(cameraId: CameraId, redacted: Boolean): Camera2CameraMetadata {
         val start = Timestamps.now(timeSource)
 
@@ -240,7 +248,7 @@
                 return@trace extensionMetadata
             } catch (throwable: Throwable) {
                 throw IllegalStateException(
-                    "Failed to load extension metadata " + "for $cameraId!",
+                    "Failed to load extension metadata for $cameraId!",
                     throwable
                 )
             }
@@ -248,7 +256,7 @@
     }
 
     @RequiresApi(Build.VERSION_CODES.S)
-    override fun getCameraExtensionCharacteristics(
+    private fun getCameraExtensionCharacteristics(
         cameraId: CameraId
     ): CameraExtensionCharacteristics {
         synchronized(extensionCharacteristicsCache) {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataProvider.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataProvider.kt
index 9ebad5a..0968c3c 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataProvider.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2MetadataProvider.kt
@@ -16,13 +16,13 @@
 
 package androidx.camera.camera2.pipe.compat
 
-import android.hardware.camera2.CameraExtensionCharacteristics
 import androidx.camera.camera2.pipe.CameraExtensionMetadata
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
 
 /** Interface that can be used to query for [CameraMetadata] using an existing [CameraId]. */
 internal interface Camera2MetadataProvider {
+
     /** Attempt to retrieve [CameraMetadata], suspending the caller if it is not yet available. */
     suspend fun getCameraMetadata(cameraId: CameraId): CameraMetadata
 
@@ -31,9 +31,6 @@
      */
     fun awaitCameraMetadata(cameraId: CameraId): CameraMetadata
 
-    /** Attempt to retrieve [CameraExtensionCharacteristics] */
-    fun getCameraExtensionCharacteristics(cameraId: CameraId): CameraExtensionCharacteristics
-
     /**
      * Attempt to retrieve [CameraExtensionMetadata], blocking the calling thread if it is not yet
      * available.
@@ -48,4 +45,7 @@
      * available.
      */
     fun awaitCameraExtensionMetadata(cameraId: CameraId, extension: Int): CameraExtensionMetadata
+
+    /** Retrieve the set of supported Camera2 Extensions for the given camera. */
+    fun getSupportedCameraExtensions(cameraId: CameraId): Set<Int>
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraPipeKeys.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraPipeKeys.kt
index 5b646b5..45d895d 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraPipeKeys.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraPipeKeys.kt
@@ -19,21 +19,21 @@
 import android.hardware.camera2.CameraExtensionCharacteristics
 import androidx.camera.camera2.pipe.Metadata
 
-object CameraPipeKeys {
+public object CameraPipeKeys {
 
     /** Keys for sessionParameters when creating Extension sessions. */
-    val camera2ExtensionMode =
+    public val camera2ExtensionMode: Metadata.Key<Int> =
         Metadata.Key.create<Int>("androidx.camera.camera2.pipe.ExtensionMode")
 
     /** Key for configuring the tag for a Camera2 CaptureRequest. */
-    val camera2CaptureRequestTag =
+    public val camera2CaptureRequestTag: Metadata.Key<Any> =
         Metadata.Key.create<Any>("androidx.camera.camera2.pipe.CaptureRequestTag")
 
     /**
      * Key for defaultParameters and requiredParameters that allows the users to ignore the required
      * 3A parameters stipulated by the 3A controller in CameraPipe.
      */
-    val ignore3ARequiredParameters =
+    public val ignore3ARequiredParameters: Metadata.Key<Boolean> =
         Metadata.Key.create<Boolean>("androidx.camera.camera2.pipe.Ignore3ARequiredParameters")
 
     /**
@@ -41,30 +41,30 @@
      * or NIGHT depending on the current lighting and environment conditions. See
      * [CameraExtensionCharacteristics.EXTENSION_AUTOMATIC]
      */
-    const val CAMERA2_EXTENSION_MODE_AUTOMATIC = 0
+    public const val CAMERA2_EXTENSION_MODE_AUTOMATIC: Int = 0
 
     /**
      * [CAMERA2_EXTENSION_MODE_FACE_RETOUCH]: Smooth skin and apply other cosmetic effects to faces.
      * See [CameraExtensionCharacteristics.EXTENSION_FACE_RETOUCH]
      */
-    const val CAMERA2_EXTENSION_MODE_FACE_RETOUCH = 1
+    public const val CAMERA2_EXTENSION_MODE_FACE_RETOUCH: Int = 1
 
     /**
      * [CAMERA2_EXTENSION_MODE_BOKEH]: Blur certain regions of the final image thereby "enhancing"
      * focus for all remaining non-blurred parts. See
      * [CameraExtensionCharacteristics.EXTENSION_BOKEH]
      */
-    const val CAMERA2_EXTENSION_MODE_BOKEH = 2
+    public const val CAMERA2_EXTENSION_MODE_BOKEH: Int = 2
 
     /**
      * [CAMERA2_EXTENSION_MODE_HDR]: Enhance the dynamic range of the final image. See
      * [CameraExtensionCharacteristics.EXTENSION_HDR]
      */
-    const val CAMERA2_EXTENSION_MODE_HDR = 3
+    public const val CAMERA2_EXTENSION_MODE_HDR: Int = 3
 
     /**
      * [CAMERA2_EXTENSION_MODE_NIGHT]: Suppress noise and improve the overall image quality under
      * low light conditions. See [CameraExtensionCharacteristics.EXTENSION_NIGHT]
      */
-    const val CAMERA2_EXTENSION_MODE_NIGHT = 4
+    public const val CAMERA2_EXTENSION_MODE_NIGHT: Int = 4
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionState.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionState.kt
index b44a7cb..6865b04 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionState.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionState.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:RequiresApi(31) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-
 package androidx.camera.camera2.pipe.compat
 
 import android.hardware.camera2.CameraCaptureSession
@@ -34,6 +32,7 @@
  * @param captureSessionState The [CaptureSessionState] instance to delegate the callback methods
  *   to.
  */
+@RequiresApi(31)
 internal class ExtensionSessionState(private val captureSessionState: CaptureSessionState) :
     CameraExtensionSessionWrapper.StateCallback {
     override fun onConfigured(session: CameraExtensionSessionWrapper) {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
index f3bc8756..0c86a32 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:RequiresApi(31) // TODO(b/200306659): Remove and replace with annotation on package-info.java
-
 package androidx.camera.camera2.pipe.compat
 
 import android.hardware.camera2.CameraCaptureSession
@@ -67,7 +65,7 @@
     }
 }
 
-@RequiresApi(31) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@RequiresApi(31)
 internal class AndroidExtensionSessionStateCallback(
     private val device: CameraDeviceWrapper,
     private val stateCallback: CameraExtensionSessionWrapper.StateCallback,
@@ -137,7 +135,7 @@
     }
 }
 
-@RequiresApi(31) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+@RequiresApi(31)
 internal open class AndroidCameraExtensionSession(
     override val device: CameraDeviceWrapper,
     private val cameraExtensionSession: CameraExtensionSession,
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt
index 7a824d9..23f8c0b 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExternalRequestProcessor.kt
@@ -40,7 +40,7 @@
 import kotlin.reflect.KClass
 import kotlinx.atomicfu.atomic
 
-class ExternalCameraController(
+public class ExternalCameraController(
     private val graphId: CameraGraphId,
     private val graphConfig: CameraGraph.Config,
     private val graphListener: GraphListener,
@@ -57,7 +57,7 @@
     override val cameraGraphId: CameraGraphId
         get() = graphId
 
-    override var isForeground = false
+    override var isForeground: Boolean = false
 
     override fun start() {
         if (started.compareAndSet(expect = false, update = true)) {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/OnSessionFinalized.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/OnSessionFinalized.kt
index 610ceff..4ec420e 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/OnSessionFinalized.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/OnSessionFinalized.kt
@@ -17,7 +17,7 @@
 package androidx.camera.camera2.pipe.compat
 
 /** Base class for CameraCaptureSession.StateCallback() */
-interface OnSessionFinalized {
+public interface OnSessionFinalized {
     /**
      * Artificial event indicating the session is no longer in use and may be called several times.
      * onClosed() and [onConfigureFailed() methods should call this method directly. This method
@@ -26,5 +26,5 @@
      *
      * See b/249258992 for more details.
      */
-    fun onSessionFinalized()
+    public fun onSessionFinalized()
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt
index 09dbcec..4035977 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/config/ExternalCameraGraphComponent.kt
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-// TODO(b/200306659): Remove and replace with annotation on package-info.java
 @file:Suppress("DEPRECATION")
 
 package androidx.camera.camera2.pipe.config
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt
index 545cacb..b805289 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Debug.kt
@@ -32,10 +32,10 @@
 import androidx.camera.camera2.pipe.core.Timestamps.formatMs
 
 /** Internal debug utilities, constants, and checks. */
-object Debug {
+public object Debug {
     internal val systemTimeSource = SystemTimeSource()
-    const val ENABLE_LOGGING: Boolean = true
-    const val ENABLE_TRACING: Boolean = true
+    public const val ENABLE_LOGGING: Boolean = true
+    public const val ENABLE_TRACING: Boolean = true
 
     /**
      * Wrap the specified [block] in calls to [Trace.beginSection] (with the supplied [label]) and
@@ -44,7 +44,7 @@
      * @param label A name of the code section to appear in the trace.
      * @param block A block of code which is being traced.
      */
-    inline fun <T> trace(label: String, crossinline block: () -> T): T {
+    public inline fun <T> trace(label: String, block: () -> T): T {
         try {
             traceStart { label }
             return block()
@@ -54,7 +54,7 @@
     }
 
     /** Wrap the specified [block] in a trace and timing calls. */
-    internal inline fun <T> instrument(label: String, crossinline block: () -> T): T {
+    internal inline fun <T> instrument(label: String, block: () -> T): T {
         val start = systemTimeSource.now()
         try {
             traceStart { label }
@@ -67,14 +67,14 @@
     }
 
     /** Forwarding call to [Trace.beginSection] that can be statically disabled at compile time. */
-    inline fun traceStart(crossinline label: () -> String) {
+    public inline fun traceStart(crossinline label: () -> String) {
         if (ENABLE_TRACING) {
             Trace.beginSection(label())
         }
     }
 
     /** Forwarding call to [Trace.endSection] that can be statically disabled at compile time. */
-    inline fun traceStop() {
+    public inline fun traceStop() {
         if (ENABLE_TRACING) {
             Trace.endSection()
         }
@@ -98,7 +98,7 @@
      *
      * Example: `[abc.xyz=1, abc.zyx=something]`
      */
-    fun formatParameterMap(parameters: Map<*, Any?>, limit: Int = -1): String {
+    public fun formatParameterMap(parameters: Map<*, Any?>, limit: Int = -1): String {
         return parametersToSortedStringPairs(parameters).joinToString(
             prefix = "{",
             postfix = "}",
@@ -130,7 +130,7 @@
             else -> value.toString()
         }
 
-    fun formatCameraGraphProperties(
+    public fun formatCameraGraphProperties(
         metadata: CameraMetadata,
         graphConfig: CameraGraph.Config,
         cameraGraph: CameraGraph
@@ -222,27 +222,32 @@
  *
  * Example: checkApi(Build.VERSION_CODES.LOLLIPOP, "createCameraDevice")
  */
-inline fun checkApi(requiredApi: Int, methodName: String) {
+public inline fun checkApi(requiredApi: Int, methodName: String) {
     check(Build.VERSION.SDK_INT >= requiredApi) {
         "$methodName is not supported on API ${Build.VERSION.SDK_INT} (requires API $requiredApi)"
     }
 }
 
 /** Asserts that this method was invoked on Android L (API 21) or higher. */
-inline fun checkLOrHigher(methodName: String): Unit =
+public inline fun checkLOrHigher(methodName: String): Unit =
     checkApi(Build.VERSION_CODES.LOLLIPOP, methodName)
 
 /** Asserts that this method was invoked on Android M (API 23) or higher. */
-inline fun checkMOrHigher(methodName: String): Unit = checkApi(Build.VERSION_CODES.M, methodName)
+public inline fun checkMOrHigher(methodName: String): Unit =
+    checkApi(Build.VERSION_CODES.M, methodName)
 
 /** Asserts that this method was invoked on Android N (API 24) or higher. */
-inline fun checkNOrHigher(methodName: String): Unit = checkApi(Build.VERSION_CODES.N, methodName)
+public inline fun checkNOrHigher(methodName: String): Unit =
+    checkApi(Build.VERSION_CODES.N, methodName)
 
 /** Asserts that this method was invoked on Android O (API 26) or higher. */
-inline fun checkOOrHigher(methodName: String): Unit = checkApi(Build.VERSION_CODES.O, methodName)
+public inline fun checkOOrHigher(methodName: String): Unit =
+    checkApi(Build.VERSION_CODES.O, methodName)
 
 /** Asserts that this method was invoked on Android P (API 28) or higher. */
-inline fun checkPOrHigher(methodName: String): Unit = checkApi(Build.VERSION_CODES.P, methodName)
+public inline fun checkPOrHigher(methodName: String): Unit =
+    checkApi(Build.VERSION_CODES.P, methodName)
 
 /** Asserts that this method was invoked on Android Q (API 29) or higher. */
-inline fun checkQOrHigher(methodName: String): Unit = checkApi(Build.VERSION_CODES.Q, methodName)
+public inline fun checkQOrHigher(methodName: String): Unit =
+    checkApi(Build.VERSION_CODES.Q, methodName)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Lazy.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Lazy.kt
new file mode 100644
index 0000000..6de6a8d
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Lazy.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 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 androidx.camera.camera2.pipe.core
+
+/**
+ * Utility function for creating inline [Lazy] instances that default to false if the block throws
+ * an exception while trying to read or compute the value.
+ */
+internal inline fun lazyOrFalse(
+    crossinline blockNameFn: () -> String,
+    crossinline block: () -> Boolean
+): Lazy<Boolean> =
+    lazy(LazyThreadSafetyMode.PUBLICATION) {
+        val blockName = blockNameFn()
+        try {
+            Debug.trace(blockName) { block() }
+        } catch (e: Throwable) {
+            Log.warn(e) { "Failed to get $blockName! Caching false and ignoring exception." }
+            false
+        }
+    }
+
+internal inline fun lazyOrFalse(
+    blockName: String,
+    crossinline block: () -> Boolean
+): Lazy<Boolean> = lazyOrFalse({ blockName }, block)
+
+/**
+ * Utility function for creating [Lazy] instances that default to an empty set if the block throws
+ * an exception while trying to read or compute the value.
+ */
+internal inline fun <T> lazyOrEmptySet(
+    crossinline blockNameFn: () -> String,
+    crossinline block: () -> Set<T>?
+): Lazy<Set<T>> =
+    lazy(LazyThreadSafetyMode.PUBLICATION) {
+        val blockName = blockNameFn()
+        try {
+            Debug.trace(blockName) { block() ?: emptySet() }
+        } catch (e: Throwable) {
+            Log.warn(e) { "Failed to get $blockName! Caching {} and ignoring exception." }
+            emptySet()
+        }
+    }
+
+internal inline fun <T> lazyOrEmptySet(
+    blockName: String,
+    crossinline block: () -> Set<T>?
+): Lazy<Set<T>> = lazyOrEmptySet({ blockName }, block)
+
+/**
+ * Utility function for creating [Lazy] instances that default to an empty list if the block throws
+ * an exception while trying to read or compute the value.
+ */
+internal inline fun <T> lazyOrEmptyList(
+    crossinline blockNameFn: () -> String,
+    crossinline block: () -> List<T>?
+): Lazy<List<T>> =
+    lazy(LazyThreadSafetyMode.PUBLICATION) {
+        val blockName = blockNameFn()
+        try {
+            Debug.trace(blockName) { block() ?: emptyList() }
+        } catch (e: Throwable) {
+            Log.warn(e) { "Failed to get $blockName! Caching [] and ignoring exception." }
+            emptyList()
+        }
+    }
+
+internal inline fun <T> lazyOrEmptyList(
+    blockName: String,
+    crossinline block: () -> List<T>
+): Lazy<List<T>> = lazyOrEmptyList({ blockName }, block)
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Log.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Log.kt
index 1cc3d96..db81aa9 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Log.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Log.kt
@@ -27,8 +27,8 @@
  *
  * Log.debug { "This is a log message with a $value" }
  */
-object Log {
-    const val TAG: String = "CXCP"
+public object Log {
+    public const val TAG: String = "CXCP"
 
     private const val LOG_LEVEL_DEBUG = 1
     private const val LOG_LEVEL_INFO = 2
@@ -38,26 +38,28 @@
     // This indicates the lowest log level that will always log.
     private const val LOG_LEVEL = LOG_LEVEL_DEBUG
 
-    val DEBUG_LOGGABLE: Boolean = LOG_LEVEL <= LOG_LEVEL_DEBUG || Log.isLoggable(TAG, Log.DEBUG)
-    val INFO_LOGGABLE: Boolean = LOG_LEVEL <= LOG_LEVEL_INFO || Log.isLoggable(TAG, Log.INFO)
-    val WARN_LOGGABLE: Boolean = LOG_LEVEL <= LOG_LEVEL_WARN || Log.isLoggable(TAG, Log.WARN)
-    val ERROR_LOGGABLE: Boolean = LOG_LEVEL <= LOG_LEVEL_ERROR || Log.isLoggable(TAG, Log.ERROR)
+    public val DEBUG_LOGGABLE: Boolean =
+        LOG_LEVEL <= LOG_LEVEL_DEBUG || Log.isLoggable(TAG, Log.DEBUG)
+    public val INFO_LOGGABLE: Boolean = LOG_LEVEL <= LOG_LEVEL_INFO || Log.isLoggable(TAG, Log.INFO)
+    public val WARN_LOGGABLE: Boolean = LOG_LEVEL <= LOG_LEVEL_WARN || Log.isLoggable(TAG, Log.WARN)
+    public val ERROR_LOGGABLE: Boolean =
+        LOG_LEVEL <= LOG_LEVEL_ERROR || Log.isLoggable(TAG, Log.ERROR)
 
     /** Debug functions log noisy information related to the internals of the system. */
-    inline fun debug(crossinline msg: () -> String) {
+    public inline fun debug(crossinline msg: () -> String) {
         if (Debug.ENABLE_LOGGING && DEBUG_LOGGABLE) Log.d(TAG, msg())
     }
 
-    inline fun debug(throwable: Throwable, crossinline msg: () -> String) {
+    public inline fun debug(throwable: Throwable, crossinline msg: () -> String) {
         if (Debug.ENABLE_LOGGING && DEBUG_LOGGABLE) Log.d(TAG, msg(), throwable)
     }
 
     /** Info functions log standard, useful information about the state of the system. */
-    inline fun info(crossinline msg: () -> String) {
+    public inline fun info(crossinline msg: () -> String) {
         if (Debug.ENABLE_LOGGING && INFO_LOGGABLE) Log.i(TAG, msg())
     }
 
-    inline fun info(throwable: Throwable, crossinline msg: () -> String) {
+    public inline fun info(throwable: Throwable, crossinline msg: () -> String) {
         if (Debug.ENABLE_LOGGING && INFO_LOGGABLE) Log.i(TAG, msg(), throwable)
     }
 
@@ -65,22 +67,22 @@
      * Warning functions are used when something unexpected may lead to a crash or fatal exception
      * later on as a result if the unusual circumstances
      */
-    inline fun warn(crossinline msg: () -> String) {
+    public inline fun warn(crossinline msg: () -> String) {
         if (Debug.ENABLE_LOGGING && WARN_LOGGABLE) Log.w(TAG, msg())
     }
 
-    inline fun warn(throwable: Throwable, crossinline msg: () -> String) {
+    public inline fun warn(throwable: Throwable, crossinline msg: () -> String) {
         if (Debug.ENABLE_LOGGING && WARN_LOGGABLE) Log.w(TAG, msg(), throwable)
     }
 
     /**
      * Error functions are reserved for something unexpected that will lead to a crash or data loss.
      */
-    inline fun error(crossinline msg: () -> String) {
+    public inline fun error(crossinline msg: () -> String) {
         if (Debug.ENABLE_LOGGING && ERROR_LOGGABLE) Log.e(TAG, msg())
     }
 
-    inline fun error(throwable: Throwable, crossinline msg: () -> String) {
+    public inline fun error(throwable: Throwable, crossinline msg: () -> String) {
         if (Debug.ENABLE_LOGGING && ERROR_LOGGABLE) Log.e(TAG, msg(), throwable)
     }
 
@@ -92,7 +94,7 @@
      * @return Original returned value of `block` in case of no exception.
      * @throws Exception that is caught while executing `block`.
      */
-    inline fun <T> rethrowExceptionAfterLogging(msg: String, crossinline block: () -> T) =
+    public inline fun <T> rethrowExceptionAfterLogging(msg: String, crossinline block: () -> T): T =
         try {
             block()
         } catch (e: Exception) {
@@ -101,7 +103,7 @@
         }
 
     /** Read the stack trace of a calling method and join it to a formatted string. */
-    fun readStackTrace(limit: Int = 4): String {
+    public fun readStackTrace(limit: Int = 4): String {
         val elements = Thread.currentThread().stackTrace
         // Ignore the first 3 elements, which ignores:
         // VMStack.getThreadStackTrace
@@ -120,8 +122,8 @@
      * Note that the message constants here may be used to parse test data, so these constant values
      * should be changed with caution. See b/356108571 for details.
      */
-    object MonitoredLogMessages {
-        const val REPEATING_REQUEST_STARTED_TIMEOUT =
+    public object MonitoredLogMessages {
+        public const val REPEATING_REQUEST_STARTED_TIMEOUT: String =
             "awaitStarted on last repeating request timed out"
     }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Mutexes.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Mutexes.kt
index 07b675b..7bad079 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Mutexes.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Mutexes.kt
@@ -44,7 +44,7 @@
  * entire duration. In addition, kotlin [Mutex] objects are non-reentrant, unlike standard java
  * `synchronized` locks.
  */
-class CoroutineMutex {
+public class CoroutineMutex {
     internal val mutex = Mutex()
 }
 
@@ -52,7 +52,7 @@
  * Execute the provided [block] within the current [CoroutineMutex] while ensuring that only one
  * operation is executed at a time.
  */
-fun <T> CoroutineMutex.withLockAsync(
+public fun <T> CoroutineMutex.withLockAsync(
     scope: CoroutineScope,
     block: suspend CoroutineScope.() -> T
 ): Deferred<T> {
@@ -74,7 +74,7 @@
  * Execute the provided [block] after acquiring a lock to the [CoroutineMutex] in the provided
  * [CoroutineScope].
  */
-fun CoroutineMutex.withLockLaunch(
+public fun CoroutineMutex.withLockLaunch(
     scope: CoroutineScope,
     block: suspend CoroutineScope.() -> Unit
 ): Job {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Threads.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Threads.kt
index 4933f49..6d4f1d9 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Threads.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Threads.kt
@@ -25,23 +25,23 @@
  * This collection pre-configured executors, dispatchers, and scopes that are used throughout this
  * library.
  */
-class Threads(
-    val globalScope: CoroutineScope,
-    val blockingExecutor: Executor,
-    val blockingDispatcher: CoroutineDispatcher,
-    val backgroundExecutor: Executor,
-    val backgroundDispatcher: CoroutineDispatcher,
-    val lightweightExecutor: Executor,
-    val lightweightDispatcher: CoroutineDispatcher,
+public class Threads(
+    public val globalScope: CoroutineScope,
+    public val blockingExecutor: Executor,
+    public val blockingDispatcher: CoroutineDispatcher,
+    public val backgroundExecutor: Executor,
+    public val backgroundDispatcher: CoroutineDispatcher,
+    public val lightweightExecutor: Executor,
+    public val lightweightDispatcher: CoroutineDispatcher,
     camera2Handler: () -> Handler,
     camera2Executor: () -> Executor
 ) {
     private val _camera2Handler = lazy { camera2Handler() }
     private val _camera2Executor = lazy { camera2Executor() }
 
-    val camera2Handler: Handler
+    public val camera2Handler: Handler
         get() = _camera2Handler.value
 
-    val camera2Executor: Executor
+    public val camera2Executor: Executor
         get() = _camera2Executor.value
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Timestamps.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Timestamps.kt
index 093beb1..a64cb8a 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Timestamps.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/core/Timestamps.kt
@@ -24,21 +24,25 @@
 
 /** A nanosecond timestamp */
 @JvmInline
-value class TimestampNs constructor(val value: Long) {
-    inline operator fun minus(other: TimestampNs): DurationNs = DurationNs(value - other.value)
+public value class TimestampNs constructor(public val value: Long) {
+    public inline operator fun minus(other: TimestampNs): DurationNs =
+        DurationNs(value - other.value)
 
-    inline operator fun plus(other: DurationNs): TimestampNs = TimestampNs(value + other.value)
+    public inline operator fun plus(other: DurationNs): TimestampNs =
+        TimestampNs(value + other.value)
 }
 
 @JvmInline
-value class DurationNs(val value: Long) {
-    inline operator fun minus(other: DurationNs): DurationNs = DurationNs(value - other.value)
+public value class DurationNs(public val value: Long) {
+    public inline operator fun minus(other: DurationNs): DurationNs =
+        DurationNs(value - other.value)
 
-    inline operator fun plus(other: DurationNs): DurationNs = DurationNs(value + other.value)
+    public inline operator fun plus(other: DurationNs): DurationNs = DurationNs(value + other.value)
 
-    inline operator fun plus(other: TimestampNs): TimestampNs = TimestampNs(value + other.value)
+    public inline operator fun plus(other: TimestampNs): TimestampNs =
+        TimestampNs(value + other.value)
 
-    operator fun compareTo(other: DurationNs): Int {
+    public operator fun compareTo(other: DurationNs): Int {
         return if (value == other.value) {
             0
         } else if (value < other.value) {
@@ -48,32 +52,33 @@
         }
     }
 
-    companion object {
-        inline fun fromMs(durationMs: Long) = DurationNs(durationMs * 1_000_000L)
+    public companion object {
+        public inline fun fromMs(durationMs: Long): DurationNs = DurationNs(durationMs * 1_000_000L)
     }
 }
 
-interface TimeSource {
-    fun now(): TimestampNs
+public interface TimeSource {
+    public fun now(): TimestampNs
 }
 
 @Singleton
-class SystemTimeSource @Inject constructor() : TimeSource {
-    override fun now() = TimestampNs(SystemClock.elapsedRealtimeNanos())
+public class SystemTimeSource @Inject constructor() : TimeSource {
+    override fun now(): TimestampNs = TimestampNs(SystemClock.elapsedRealtimeNanos())
 }
 
-object Timestamps {
-    inline fun now(timeSource: TimeSource): TimestampNs = timeSource.now()
+public object Timestamps {
+    public inline fun now(timeSource: TimeSource): TimestampNs = timeSource.now()
 
-    inline fun DurationNs.formatNs(): String = "$this ns"
+    public inline fun DurationNs.formatNs(): String = "$this ns"
 
-    inline fun DurationNs.formatMs(decimals: Int = 3): String =
+    public inline fun DurationNs.formatMs(decimals: Int = 3): String =
         "%.${decimals}f ms".format(null, this.value / 1_000_000.0)
 
-    inline fun TimestampNs.formatNs(): String = "$this ns"
+    public inline fun TimestampNs.formatNs(): String = "$this ns"
 
-    inline fun TimestampNs.formatMs(): String = "${this.value / 1_000_000} ms"
+    public inline fun TimestampNs.formatMs(): String = "${this.value / 1_000_000} ms"
 
-    inline fun TimestampNs.measureNow(timeSource: TimeSource = SystemTimeSource()) =
-        now(timeSource) - this
+    public inline fun TimestampNs.measureNow(
+        timeSource: TimeSource = SystemTimeSource()
+    ): DurationNs = now(timeSource) - this
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphListener.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphListener.kt
index 4e6162e..3121d7e 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphListener.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphListener.kt
@@ -19,37 +19,37 @@
 import androidx.camera.camera2.pipe.CameraGraph
 import androidx.camera.camera2.pipe.GraphState.GraphStateError
 
-interface GraphListener {
+public interface GraphListener {
     /**
      * Used to indicate that the graph is starting. This is called immediately when a [CameraGraph]
      * is being started.
      */
-    fun onGraphStarting() {}
+    public fun onGraphStarting() {}
 
     /**
      * Used to indicate that the graph has been initialized and is ready to actively process
      * requests using the provided [GraphRequestProcessor] interface.
      */
-    fun onGraphStarted(requestProcessor: GraphRequestProcessor)
+    public fun onGraphStarted(requestProcessor: GraphRequestProcessor)
 
     /**
      * Used to indicate that the graph is stopping. This is called immediately when a [CameraGraph]
      * is being stopped.
      */
-    fun onGraphStopping() {}
+    public fun onGraphStopping() {}
 
     /**
      * Used to indicate that a previously initialized [GraphRequestProcessor] is no longer
      * available.
      */
-    fun onGraphStopped(requestProcessor: GraphRequestProcessor?)
+    public fun onGraphStopped(requestProcessor: GraphRequestProcessor?)
 
     /**
      * Used to indicate that the internal state of the [GraphRequestProcessor] has changed. This is
      * a signal that previously queued requests may now succeed if they previously failed.
      */
-    fun onGraphModified(requestProcessor: GraphRequestProcessor)
+    public fun onGraphModified(requestProcessor: GraphRequestProcessor)
 
     /** Used to indicate that the graph has encountered an error. */
-    fun onGraphError(graphStateError: GraphStateError)
+    public fun onGraphError(graphStateError: GraphStateError)
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphRequestProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphRequestProcessor.kt
index b11a292..12b1f1e 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphRequestProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/GraphRequestProcessor.kt
@@ -36,13 +36,15 @@
  * GraphRequestProcessors are intended to be in conjunction with a [GraphListener].
  */
 @Suppress("NOTHING_TO_INLINE")
-class GraphRequestProcessor
+public class GraphRequestProcessor
 private constructor(
     private val captureSequenceProcessor: CaptureSequenceProcessor<Any, CaptureSequence<Any>>
 ) {
-    companion object {
+    public companion object {
         /** Create a [GraphRequestProcessor] from a [CaptureSequenceProcessor] instance. */
-        fun from(captureSequenceProcessor: CaptureSequenceProcessor<*, *>): GraphRequestProcessor {
+        public fun from(
+            captureSequenceProcessor: CaptureSequenceProcessor<*, *>
+        ): GraphRequestProcessor {
             @Suppress("UNCHECKED_CAST")
             return GraphRequestProcessor(
                 captureSequenceProcessor as CaptureSequenceProcessor<Any, CaptureSequence<Any>>
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraErrorListener.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraErrorListener.kt
index c0a6076..f34f005 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraErrorListener.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/internal/CameraErrorListener.kt
@@ -23,8 +23,8 @@
  * Interface intended to be used to report camera errors. It will ensure only the current
  * [androidx.camera.camera2.pipe.graph.GraphListener] is notified of the error.
  */
-interface CameraErrorListener {
-    fun onCameraError(
+public interface CameraErrorListener {
+    public fun onCameraError(
         cameraId: CameraId,
         cameraError: CameraError,
         willAttemptRetry: Boolean = false
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImage.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImage.kt
index 1db7fdb40..71a32a4 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImage.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImage.kt
@@ -27,9 +27,9 @@
  * Note: [Image] is not thread-safe, so all interactions with the underlying properties must be
  * copied into local fields or guarded by a lock.
  */
-class AndroidImage(private val image: Image) : ImageWrapper {
+public class AndroidImage(private val image: Image) : ImageWrapper {
     /** A [Plane] backed by an [ImagePlane]. */
-    class Plane(private val imagePlane: Image.Plane) : ImagePlane {
+    public class Plane(private val imagePlane: Image.Plane) : ImagePlane {
         // Copying out the contents of the Image.Plane means that this Plane
         // implementation can be thread-safe (without requiring any locking)
         // and can have getters which do not throw a RuntimeException if
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImageReaders.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImageReaders.kt
index 4888e59..48f05a2 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImageReaders.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImageReaders.kt
@@ -36,7 +36,7 @@
 import kotlinx.atomicfu.atomic
 
 /** Implements an [ImageReaderWrapper] using an [ImageReader]. */
-class AndroidImageReader
+public class AndroidImageReader
 private constructor(
     private val imageReader: ImageReader,
     override val capacity: Int,
@@ -63,7 +63,7 @@
         }
     }
 
-    override fun close() = imageReader.close()
+    override fun close(): Unit = imageReader.close()
 
     override fun flush() {
         // acquireLatestImage will acquire the most recent image and internally close any image that
@@ -92,7 +92,7 @@
             "-w${imageReader.width}h${imageReader.height}"
     }
 
-    companion object {
+    public companion object {
         // See: b/172464059
         //
         // The ImageReader has an internal limit of 64 images by design, but depending on the device
@@ -110,7 +110,7 @@
          *
          * See [ImageReader.newInstance] for details.
          */
-        fun create(
+        public fun create(
             width: Int,
             height: Int,
             format: Int,
@@ -198,7 +198,7 @@
 
 /** Implements an [ImageReaderWrapper] using a [MultiResolutionImageReader]. */
 @RequiresApi(Build.VERSION_CODES.S)
-class AndroidMultiResolutionImageReader(
+public class AndroidMultiResolutionImageReader(
     private val multiResolutionImageReader: MultiResolutionImageReader,
     private val streamFormat: StreamFormat,
     override val capacity: Int,
@@ -241,7 +241,7 @@
         }
     }
 
-    override fun close() = multiResolutionImageReader.close()
+    override fun close(): Unit = multiResolutionImageReader.close()
 
     override fun flush() {
         // ImageReaders are pools of shared memory that is not actively released until the
@@ -267,9 +267,9 @@
             "-$sizeString"
     }
 
-    companion object {
+    public companion object {
         @RequiresApi(Build.VERSION_CODES.S)
-        fun create(
+        public fun create(
             outputFormat: Int,
             streamId: StreamId,
             outputIdMap: Map<MultiResolutionStreamInfo, OutputId>,
@@ -306,7 +306,7 @@
         }
 
         @RequiresApi(Build.VERSION_CODES.S)
-        fun create(
+        public fun create(
             cameraStream: CameraStream,
             capacity: Int,
             executor: Executor
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImageWriter.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImageWriter.kt
index eb7bb0a..171ad15 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImageWriter.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/AndroidImageWriter.kt
@@ -32,7 +32,7 @@
 
 /** Implements an [ImageWriterWrapper] using an [ImageWriter]. */
 @RequiresApi(Build.VERSION_CODES.M)
-class AndroidImageWriter
+public class AndroidImageWriter
 private constructor(
     private val imageWriter: ImageWriter,
     private val inputStreamId: InputStreamId
@@ -76,7 +76,7 @@
         onImageReleasedListener.value?.onImageReleased(inputStreamId)
     }
 
-    override fun close() = imageWriter.close()
+    override fun close(): Unit = imageWriter.close()
 
     @Suppress("UNCHECKED_CAST")
     override fun <T : Any> unwrapAs(type: KClass<T>): T? =
@@ -89,13 +89,13 @@
         return "ImageWriter-${StreamFormat(imageWriter.format).name}-$inputStreamId"
     }
 
-    companion object {
+    public companion object {
         /**
          * Create and configure a new ImageWriter instance as an [ImageWriter].
          *
          * See [ImageWriter.newInstance] for details.
          */
-        fun create(
+        public fun create(
             surface: Surface,
             inputStreamId: InputStreamId,
             maxImages: Int,
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/Finalizer.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/Finalizer.kt
index 4897946..c81c965 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/Finalizer.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/Finalizer.kt
@@ -22,18 +22,18 @@
  * This is primarily used by classes that may need to intercept and do something with an
  * intermediate object before it is fully closed.
  */
-interface Finalizer<in T> {
-    fun finalize(value: T?)
+public interface Finalizer<in T> {
+    public fun finalize(value: T?)
 }
 
 /** Simple [Finalizer] that can be used with [AutoCloseable] objects to close and release them. */
-object ClosingFinalizer : Finalizer<AutoCloseable> {
+public object ClosingFinalizer : Finalizer<AutoCloseable> {
     override fun finalize(value: AutoCloseable?) {
         value?.close()
     }
 }
 
 /** Simple [Finalizer] that does nothing. Often used for objects that do not need to be closed. */
-object NoOpFinalizer : Finalizer<Any?> {
+public object NoOpFinalizer : Finalizer<Any?> {
     override fun finalize(value: Any?) {}
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderImageSource.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderImageSource.kt
index c9bad0b..a2fc73d 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderImageSource.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderImageSource.kt
@@ -125,15 +125,16 @@
 }
 
 /** An ImageReaderImageSource implements an [ImageSource] using an [ImageReader] */
-class ImageReaderImageSource(
+public class ImageReaderImageSource(
     private val imageReader: ImageReaderWrapper,
     private val maxImages: Int,
 ) : ImageSource {
-    companion object {
+    public companion object {
         private const val IMAGE_SOURCE_CAPACITY_MARGIN = 2
-        const val IMAGE_SOURCE_CAPACITY = IMAGEREADER_MAX_CAPACITY - IMAGE_SOURCE_CAPACITY_MARGIN
+        public const val IMAGE_SOURCE_CAPACITY: Int =
+            IMAGEREADER_MAX_CAPACITY - IMAGE_SOURCE_CAPACITY_MARGIN
 
-        fun create(imageReader: ImageReaderWrapper): ImageSource {
+        public fun create(imageReader: ImageReaderWrapper): ImageSource {
             // Reduce the maxImages of the ImageSource relative to the ImageReader to ensure there
             // is enough headroom to avoid acquiring too many images that could otherwise stall the
             // camera or trigger IllegalStateExceptions from the underlying ImageReader.
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderWrapper.kt
index 895ea26..b8dc328 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageReaderWrapper.kt
@@ -24,13 +24,13 @@
 import androidx.camera.camera2.pipe.UnsafeWrapper
 
 /** Simplified wrapper for [ImageReader]-like classes. */
-interface ImageReaderWrapper : UnsafeWrapper, AutoCloseable {
+public interface ImageReaderWrapper : UnsafeWrapper, AutoCloseable {
     /**
      * Get a Surface that can be used to produce images for this ImageReader.
      *
      * @see [ImageReader.getSurface]
      */
-    val surface: Surface
+    public val surface: Surface
 
     /**
      * Get the maximum number of images that can be produced before stalling or throwing exceptions.
@@ -38,12 +38,12 @@
      * @see [ImageReader.acquireNextImage]
      * @see [ImageReader.acquireLatestImage]
      */
-    val capacity: Int
+    public val capacity: Int
 
     /**
      * Set the [OnImageListener]. Setting additional listeners will override the previous listener.]
      */
-    fun setOnImageListener(onImageListener: OnImageListener)
+    public fun setOnImageListener(onImageListener: OnImageListener)
 
     /**
      * Discard free buffers from the internal memory pool.
@@ -51,14 +51,14 @@
      * @see [ImageReader.discardFreeBuffers]
      * @see [MultiResolutionImageReader.flush]
      */
-    fun flush()
+    public fun flush()
 
     /**
      * The OnNextImageListener adapts the standard [ImageReader.OnImageAvailableListener] to push
      * images into a consumer. This consumer is responsible for processing and/or closing images
      * when they are no longer needed.
      */
-    fun interface OnImageListener {
+    public fun interface OnImageListener {
         /**
          * Handle the next [ImageWrapper] from an [ImageReaderWrapper]. Implementations are
          * responsible for closing images when they are no longer in use.
@@ -66,6 +66,6 @@
          * [ImageWrapper.timestamp] is not guaranteed to be in order when used with a multi-sensor
          * camera system, but should *usually* be in order
          */
-        fun onImage(streamId: StreamId, outputId: OutputId, image: ImageWrapper)
+        public fun onImage(streamId: StreamId, outputId: OutputId, image: ImageWrapper)
     }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt
index cf2082c..4dd69a8 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageSource.kt
@@ -51,27 +51,32 @@
  * Implementations are expected to be thread safe, and to associate each image with the [OutputId]
  * it is associated with.
  */
-interface ImageSource : UnsafeWrapper, AutoCloseable {
+public interface ImageSource : UnsafeWrapper, AutoCloseable {
     /** The graphics surface that the Camera produces images into. */
-    val surface: Surface
+    public val surface: Surface
 
-    fun setListener(listener: ImageSourceListener)
+    public fun setListener(listener: ImageSourceListener)
 }
 
 /** Listener for handling [ImageWrapper]s as they are produced. */
-fun interface ImageSourceListener {
+public fun interface ImageSourceListener {
     /**
      * Handle the next image from the [ImageSource]. Implementations *must* close the [image] when
      * they are done with it. Receiving a null [image] indicates the that an image was produced, but
      * that this image source is at capacity and that the image was dropped and closed to avoid
      * stalling the camera.
      */
-    fun onImage(streamId: StreamId, outputId: OutputId, outputTimestamp: Long, image: ImageWrapper?)
+    public fun onImage(
+        streamId: StreamId,
+        outputId: OutputId,
+        outputTimestamp: Long,
+        image: ImageWrapper?
+    )
 }
 
 /** Provider for creating an [ImageSource] based on an [ImageSourceConfig] */
-fun interface ImageSources {
-    fun createImageSource(
+public fun interface ImageSources {
+    public fun createImageSource(
         cameraStream: CameraStream,
         imageSourceConfig: ImageSourceConfig,
     ): ImageSource
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageWrapper.kt
index 2477156..0c57966 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageWrapper.kt
@@ -22,30 +22,30 @@
 /**
  * Wrapper interfaces that mirrors the primary read-only properties of {@link android.media.Image}.
  */
-interface ImageWrapper : UnsafeWrapper, AutoCloseable {
+public interface ImageWrapper : UnsafeWrapper, AutoCloseable {
     /** @see {@link android.media.Image.getWidth} */
-    val width: Int
+    public val width: Int
 
     /** @see {@link android.media.Image.getHeight} */
-    val height: Int
+    public val height: Int
 
     /** @see {@link android.media.Image.getFormat} */
-    val format: Int
+    public val format: Int
 
     /** @see {@link android.media.Image.getPlanes} */
-    val planes: List<ImagePlane>
+    public val planes: List<ImagePlane>
 
     /** @see {@link android.media.Image.getTimestamp} */
-    val timestamp: Long
+    public val timestamp: Long
 }
 
-interface ImagePlane : UnsafeWrapper {
+public interface ImagePlane : UnsafeWrapper {
     /** @see {@link android.media.Image.Plane.getRowStride */
-    val rowStride: Int
+    public val rowStride: Int
 
     /** @see {@link android.media.Image.Plane.getPixelStride */
-    val pixelStride: Int
+    public val pixelStride: Int
 
     /** @see {@link android.media.Image.Plane.getBuffer */
-    val buffer: ByteBuffer?
+    public val buffer: ByteBuffer?
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageWriterWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageWriterWrapper.kt
index fe8fdb6..cdb9a0b 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageWriterWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/ImageWriterWrapper.kt
@@ -24,52 +24,52 @@
 
 /** Simplified wrapper for [ImageWriter]-like classes. */
 @RequiresApi(Build.VERSION_CODES.M)
-interface ImageWriterWrapper : UnsafeWrapper, AutoCloseable {
+public interface ImageWriterWrapper : UnsafeWrapper, AutoCloseable {
 
     /**
      * Get the ImageWriter format.
      *
      * @see [ImageWriter.getFormat]
      */
-    val format: Int
+    public val format: Int
 
     /**
      * Get the maximum number of images that can be dequeued from the ImageWriter simultaneously.
      *
      * @see [ImageWriter.getMaxImages]
      */
-    val maxImages: Int
+    public val maxImages: Int
 
     /**
      * Queue an input Image back to ImageWriter for the downstream consumer to access.
      *
      * @see [ImageWriter.queueInputImage]
      */
-    fun queueInputImage(image: ImageWrapper): Boolean
+    public fun queueInputImage(image: ImageWrapper): Boolean
 
     /**
      * Dequeue the next available input Image for the application to produce data into.
      *
      * @see [ImageWriter.dequeueInputImage]
      */
-    fun dequeueInputImage(): ImageWrapper
+    public fun dequeueInputImage(): ImageWrapper
 
     /**
      * Set the [OnImageReleasedListener]. Setting additional listeners will override the previous
      * listener.]
      */
-    fun setOnImageReleasedListener(onImageReleasedListener: OnImageReleasedListener)
+    public fun setOnImageReleasedListener(onImageReleasedListener: OnImageReleasedListener)
 
     /**
      * The OnImageListener adapts the standard [ImageWriter.OnImageReleasedListener] to retrieve
      * images returned to the ImageWriter.
      */
-    fun interface OnImageReleasedListener {
+    public fun interface OnImageReleasedListener {
         /** Handle the [ImageWrapper] that has been released back to [ImageWriterWrapper]. */
-        fun onImageReleased(inputStreamId: InputStreamId)
+        public fun onImageReleased(inputStreamId: InputStreamId)
     }
 
-    interface Builder {
-        fun build(): ImageWriterWrapper
+    public interface Builder {
+        public fun build(): ImageWriterWrapper
     }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputImage.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputImage.kt
index 804b580..96a268e 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputImage.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/OutputImage.kt
@@ -24,12 +24,12 @@
  * An OutputImage is a reference to an [ImageWrapper] that was produced from CameraPipe for a
  * specific [StreamId]/[OutputId] combination.
  */
-interface OutputImage : ImageWrapper {
-    val streamId: StreamId
-    val outputId: OutputId
+public interface OutputImage : ImageWrapper {
+    public val streamId: StreamId
+    public val outputId: OutputId
 
-    companion object {
-        fun from(streamId: StreamId, outputId: OutputId, image: ImageWrapper): OutputImage {
+    public companion object {
+        public fun from(streamId: StreamId, outputId: OutputId, image: ImageWrapper): OutputImage {
             if (image is OutputImage) return image
             return OutputImageImpl(streamId, outputId, image)
         }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/SharedOutputImage.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/SharedOutputImage.kt
index 323d72c..f4cddf4 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/SharedOutputImage.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/media/SharedOutputImage.kt
@@ -27,18 +27,18 @@
  * [setFinalizer] to get access to the underlying image once all outstanding references have been
  * closed.
  */
-interface SharedOutputImage : OutputImage {
+public interface SharedOutputImage : OutputImage {
     /**
      * Create a new [SharedOutputImage] copy that can be independently managed or closed. Throws an
      * exception if this reference is already closed.
      */
-    fun acquire(): SharedOutputImage
+    public fun acquire(): SharedOutputImage
 
     /**
      * Create a new [SharedOutputImage] copy that can be independently managed or closed. Returns
      * null if this image has already been finalized.
      */
-    fun acquireOrNull(): SharedOutputImage?
+    public fun acquireOrNull(): SharedOutputImage?
 
     /**
      * Set a finalizer that is responsible for closing the underlying [OutputImage] when all
@@ -46,12 +46,12 @@
      * the previous finalizer will receive [Finalizer.finalize] with null to indicate it will not
      * receive the [OutputImage].
      */
-    fun setFinalizer(finalizer: Finalizer<OutputImage>)
+    public fun setFinalizer(finalizer: Finalizer<OutputImage>)
 
-    companion object {
+    public companion object {
 
         /** Create a new [SharedOutputImage] from an [OutputImage] */
-        fun from(image: OutputImage): SharedOutputImage {
+        public fun from(image: OutputImage): SharedOutputImage {
             if (image is SharedOutputImage) {
                 return image.acquire()
             }
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
index eceb3d8..70cf322 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.graphics.SurfaceTexture
-import android.hardware.camera2.CameraExtensionCharacteristics
 import android.os.Build
 import android.os.Looper
 import android.util.Size
@@ -55,7 +54,6 @@
 import dagger.Module
 import dagger.Provides
 import javax.inject.Singleton
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runTest
 import org.junit.After
 import org.junit.Test
@@ -66,7 +64,6 @@
 
 @RunWith(RobolectricCameraPipeTestRunner::class)
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
-@OptIn(ExperimentalCoroutinesApi::class)
 internal class CaptureSessionFactoryTest {
     private val context = ApplicationProvider.getApplicationContext() as Context
     private val mainLooper = Shadows.shadowOf(Looper.getMainLooper())
@@ -212,24 +209,22 @@
                 return fakeCamera.metadata
             }
 
-            override fun getCameraExtensionCharacteristics(
-                cameraId: CameraId
-            ): CameraExtensionCharacteristics {
-                TODO("b/299356087 - Add support for fake extension metadata")
-            }
-
             override suspend fun getCameraExtensionMetadata(
                 cameraId: CameraId,
                 extension: Int
             ): CameraExtensionMetadata {
-                TODO("b/299356087 - Add support for fake extension metadata")
+                throw UnsupportedOperationException("Unused for internal tests")
             }
 
             override fun awaitCameraExtensionMetadata(
                 cameraId: CameraId,
                 extension: Int
             ): CameraExtensionMetadata {
-                TODO("b/299356087 - Add support for fake extension metadata")
+                throw UnsupportedOperationException("Unused for internal tests")
+            }
+
+            override fun getSupportedCameraExtensions(cameraId: CameraId): Set<Int> {
+                throw UnsupportedOperationException("Unused for internal tests")
             }
         }
 }
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
index 7ce7123..dce35b4 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
@@ -18,7 +18,6 @@
 
 import android.hardware.camera2.CameraAccessException
 import android.hardware.camera2.CameraDevice
-import android.hardware.camera2.CameraExtensionCharacteristics
 import android.os.Build
 import androidx.camera.camera2.pipe.CameraError
 import androidx.camera.camera2.pipe.CameraError.Companion.ERROR_CAMERA_DISABLED
@@ -70,24 +69,22 @@
             override fun awaitCameraMetadata(cameraId: CameraId): CameraMetadata =
                 FakeCameraMetadata(cameraId = cameraId)
 
-            override fun getCameraExtensionCharacteristics(
-                cameraId: CameraId
-            ): CameraExtensionCharacteristics {
-                TODO("b/299356087 - Add support for fake extension metadata")
-            }
-
             override suspend fun getCameraExtensionMetadata(
                 cameraId: CameraId,
                 extension: Int
             ): CameraExtensionMetadata {
-                TODO("b/299356087 - Add support for fake extension metadata")
+                throw UnsupportedOperationException("Not supported for this test")
             }
 
             override fun awaitCameraExtensionMetadata(
                 cameraId: CameraId,
                 extension: Int
             ): CameraExtensionMetadata {
-                TODO("b/299356087 - Add support for fake extension metadata")
+                throw UnsupportedOperationException("Not supported for this test")
+            }
+
+            override fun getSupportedCameraExtensions(cameraId: CameraId): Set<Int> {
+                throw UnsupportedOperationException("Not supported for this test")
             }
         }
 
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraMetadataProvider.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCamera2MetadataProvider.kt
similarity index 72%
rename from camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraMetadataProvider.kt
rename to camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCamera2MetadataProvider.kt
index 900c91c..f5dc394 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraMetadataProvider.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCamera2MetadataProvider.kt
@@ -16,16 +16,14 @@
 
 package androidx.camera.camera2.pipe.testing
 
-import android.hardware.camera2.CameraExtensionCharacteristics
 import androidx.camera.camera2.pipe.CameraExtensionMetadata
 import androidx.camera.camera2.pipe.CameraId
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.compat.Camera2MetadataProvider
 
 /** Utility class for providing fake metadata for tests. */
-class FakeCameraMetadataProvider(
-    private val fakeMetadata: Map<CameraId, CameraMetadata> = emptyMap(),
-    private val fakeExtensionMetadata: Map<CameraId, CameraExtensionMetadata> = emptyMap()
+class FakeCamera2MetadataProvider(
+    private val fakeMetadata: Map<CameraId, CameraMetadata> = emptyMap()
 ) : Camera2MetadataProvider {
     override suspend fun getCameraMetadata(cameraId: CameraId): CameraMetadata =
         awaitCameraMetadata(cameraId)
@@ -35,12 +33,6 @@
             "Failed to find metadata for $cameraId. Available fakeMetadata is $fakeMetadata"
         }
 
-    override fun getCameraExtensionCharacteristics(
-        cameraId: CameraId
-    ): CameraExtensionCharacteristics {
-        TODO("b/299356087 - Add support for fake extension metadata")
-    }
-
     override suspend fun getCameraExtensionMetadata(
         cameraId: CameraId,
         extension: Int
@@ -49,9 +41,8 @@
     override fun awaitCameraExtensionMetadata(
         cameraId: CameraId,
         extension: Int
-    ): CameraExtensionMetadata =
-        checkNotNull(fakeExtensionMetadata[cameraId]) {
-            "Failed to find extension metadata for $cameraId. Available " +
-                "fakeExtensionMetadata is $fakeExtensionMetadata"
-        }
+    ): CameraExtensionMetadata = awaitCameraMetadata(cameraId).awaitExtensionMetadata(extension)
+
+    override fun getSupportedCameraExtensions(cameraId: CameraId): Set<Int> =
+        awaitCameraMetadata(cameraId).supportedExtensions
 }
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/RobolectricCameras.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/RobolectricCameras.kt
index 65a5440..de2ec07 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/RobolectricCameras.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/RobolectricCameras.kt
@@ -32,7 +32,7 @@
 import androidx.camera.camera2.pipe.CameraMetadata
 import androidx.camera.camera2.pipe.compat.Camera2CameraMetadata
 import androidx.test.core.app.ApplicationProvider
-import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import kotlinx.atomicfu.atomic
 import org.junit.After
 import org.junit.Test
@@ -104,7 +104,7 @@
                 cameraId,
                 false,
                 characteristics,
-                FakeCameraMetadataProvider(),
+                FakeCamera2MetadataProvider(),
                 emptyMap(),
                 emptySet()
             )
@@ -164,7 +164,6 @@
 @RunWith(RobolectricCameraPipeTestRunner::class)
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
 class RobolectricCamerasTest {
-    private val context = ApplicationProvider.getApplicationContext() as Context
     private val mainLooper = shadowOf(Looper.getMainLooper())
 
     @Test
@@ -175,13 +174,13 @@
             )
         val fakeCamera = RobolectricCameras.open(fakeCameraId)
 
-        Truth.assertThat(fakeCamera).isNotNull()
-        Truth.assertThat(fakeCamera.cameraId).isEqualTo(fakeCameraId)
-        Truth.assertThat(fakeCamera.cameraDevice).isNotNull()
-        Truth.assertThat(fakeCamera.characteristics).isNotNull()
-        Truth.assertThat(fakeCamera.characteristics[CameraCharacteristics.LENS_FACING]).isNotNull()
-        Truth.assertThat(fakeCamera.metadata).isNotNull()
-        Truth.assertThat(fakeCamera.metadata[CameraCharacteristics.LENS_FACING]).isNotNull()
+        assertThat(fakeCamera).isNotNull()
+        assertThat(fakeCamera.cameraId).isEqualTo(fakeCameraId)
+        assertThat(fakeCamera.cameraDevice).isNotNull()
+        assertThat(fakeCamera.characteristics).isNotNull()
+        assertThat(fakeCamera.characteristics[CameraCharacteristics.LENS_FACING]).isNotNull()
+        assertThat(fakeCamera.metadata).isNotNull()
+        assertThat(fakeCamera.metadata[CameraCharacteristics.LENS_FACING]).isNotNull()
     }
 
     @After
diff --git a/camera/camera-camera2/build.gradle b/camera/camera-camera2/build.gradle
index 4d4aa8a..0401f79 100644
--- a/camera/camera-camera2/build.gradle
+++ b/camera/camera-camera2/build.gradle
@@ -104,5 +104,4 @@
     description = "Camera2 implementation and extensions for the Jetpack Camera Library, a " +
             "library providing a consistent and reliable camera foundation that enables great " +
             "camera driven experiences across all of Android."
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
index 8467c8ba..9fa6e99 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2CameraImpl.java
@@ -46,6 +46,7 @@
 import androidx.camera.camera2.internal.compat.quirk.DeviceQuirks;
 import androidx.camera.camera2.internal.compat.quirk.LegacyCameraOutputConfigNullPointerQuirk;
 import androidx.camera.camera2.internal.compat.quirk.LegacyCameraSurfaceCleanupQuirk;
+import androidx.camera.camera2.internal.compat.workaround.CloseCameraBeforeCreateNewSession;
 import androidx.camera.camera2.interop.ExperimentalCamera2Interop;
 import androidx.camera.core.CameraState;
 import androidx.camera.core.CameraUnavailableException;
@@ -185,7 +186,7 @@
     @NonNull final CameraCoordinator mCameraCoordinator;
     @NonNull final CameraStateRegistry mCameraStateRegistry;
 
-    private final boolean mCloseCameraBeforeCreateNewSessionQuirk;
+    private final boolean mShouldCloseCameraBeforeCreateNewSession;
     private final boolean mConfigAndCloseQuirk;
     private boolean mIsConfigAndCloseRequired = false;
     private boolean mIsConfiguringForClose = false;
@@ -281,8 +282,9 @@
         mCaptureSessionOpenerBuilder = new SynchronizedCaptureSession.OpenerBuilder(mExecutor,
                 mScheduledExecutorService, schedulerHandler, mCaptureSessionRepository,
                 cameraInfoImpl.getCameraQuirks(), DeviceQuirks.getAll());
-        mCloseCameraBeforeCreateNewSessionQuirk = cameraInfoImpl.getCameraQuirks().contains(
-                LegacyCameraOutputConfigNullPointerQuirk.class);
+        mShouldCloseCameraBeforeCreateNewSession =
+                CloseCameraBeforeCreateNewSession.shouldCloseCamera(
+                        cameraInfoImpl.getCameraQuirks());
         mConfigAndCloseQuirk = cameraInfoImpl.getCameraQuirks().contains(
                 LegacyCameraSurfaceCleanupQuirk.class);
 
@@ -1675,7 +1677,7 @@
         mCaptureSession.issueCaptureRequests(unissuedCaptureConfigs);
         switch (mState) {
             case OPENED:
-                if (mCloseCameraBeforeCreateNewSessionQuirk && oldCaptureSession.isInOpenState()) {
+                if (mShouldCloseCameraBeforeCreateNewSession && oldCaptureSession.isInOpenState()) {
                     debugLog("Close camera before creating new session");
                     setState(InternalState.REOPENING_QUIRK);
                 }
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/VideoUsageControl.kt b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/VideoUsageControl.kt
index 8d440e7..55bef02 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/VideoUsageControl.kt
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/VideoUsageControl.kt
@@ -25,7 +25,7 @@
 private const val LOG_TAG = "VideoUsageControl"
 
 /** Tracks the video usage count of whether a camera is being used for a video output or not. */
-class VideoUsageControl(@CameraExecutor private val executor: Executor) {
+internal class VideoUsageControl(@CameraExecutor private val executor: Executor) {
     /**
      * An AtomicInteger for tracking the video usage count.
      *
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsCompat.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsCompat.java
index ffbb516..15934dd 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsCompat.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/CameraCharacteristicsCompat.java
@@ -158,6 +158,14 @@
     }
 
     /**
+     * Returns the camera id associated with the camera characteristics.
+     */
+    @NonNull
+    public String getCameraId() {
+        return mCameraId;
+    }
+
+    /**
      * CameraCharacteristic Implementation Interface
      */
     public interface CameraCharacteristicsCompatImpl {
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraQuirks.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraQuirks.java
index 6bad65e..447c30b 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraQuirks.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CameraQuirks.java
@@ -161,6 +161,12 @@
                 ImageCaptureFailedForVideoSnapshotQuirk.load())) {
             quirks.add(new ImageCaptureFailedForVideoSnapshotQuirk());
         }
+        if (quirkSettings.shouldEnableQuirk(
+                CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk.class,
+                CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk.load(
+                        cameraCharacteristicsCompat))) {
+            quirks.add(new CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk());
+        }
 
         Quirks cameraQuirks = new Quirks(quirks);
         Logger.d(TAG, "camera2 CameraQuirks = " + Quirks.toString(cameraQuirks));
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk.java
new file mode 100644
index 0000000..2cd2728
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/quirk/CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 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 androidx.camera.camera2.internal.compat.quirk;
+
+import android.os.Build;
+
+import androidx.annotation.NonNull;
+import androidx.camera.camera2.internal.compat.CameraCharacteristicsCompat;
+import androidx.camera.core.impl.Quirk;
+
+/**
+ * Quirk addressing the capture session stuck issue when creating new one before closing the camera
+ * device first.
+ *
+ * <p>QuirkSummary
+ *    Bug Id: 359062845
+ *    Description: Camera can't stream the image normally if the new capture session is created
+ *    before closing the camera device first when a capture session has been opened. This can
+ *    happen if the apps bind the UseCases sequentially. The workaround is to close the camera
+ *    device before creating the new capture session.
+ *    Device(s): Moto e20's front camera.
+ */
+public class CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk implements Quirk {
+
+    static boolean load(@NonNull CameraCharacteristicsCompat characteristicsCompat) {
+        return shouldLoadForMotoE20(characteristicsCompat);
+    }
+
+    private static boolean shouldLoadForMotoE20(
+            @NonNull CameraCharacteristicsCompat characteristicsCompat) {
+        return "motorola".equalsIgnoreCase(Build.BRAND) && "moto e20".equalsIgnoreCase(Build.MODEL)
+                && characteristicsCompat.getCameraId().equals("1");
+    }
+}
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/workaround/CloseCameraBeforeCreateNewSession.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/workaround/CloseCameraBeforeCreateNewSession.java
new file mode 100644
index 0000000..7935871
--- /dev/null
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/compat/workaround/CloseCameraBeforeCreateNewSession.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 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 androidx.camera.camera2.internal.compat.workaround;
+
+import androidx.annotation.NonNull;
+import androidx.camera.camera2.internal.compat.quirk.CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk;
+import androidx.camera.camera2.internal.compat.quirk.LegacyCameraOutputConfigNullPointerQuirk;
+import androidx.camera.core.impl.Quirks;
+
+/**
+ * A workaround to determine whether the camera device should be closed before creating a new
+ * capture session when a capture session has been opened.
+ *
+ * @see LegacyCameraOutputConfigNullPointerQuirk
+ * @see CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk
+ */
+public class CloseCameraBeforeCreateNewSession {
+    /**
+     * Determines whether the camera device should be closed before creating a new capture session.
+     */
+    public static boolean shouldCloseCamera(@NonNull Quirks quirks) {
+        return quirks.contains(LegacyCameraOutputConfigNullPointerQuirk.class) || quirks.contains(
+                CaptureSessionStuckWhenCreatingBeforeClosingCameraQuirk.class);
+    }
+
+    private CloseCameraBeforeCreateNewSession() {}
+}
diff --git a/camera/camera-core/build.gradle b/camera/camera-core/build.gradle
index d523b9f..0cc031d 100644
--- a/camera/camera-core/build.gradle
+++ b/camera/camera-core/build.gradle
@@ -135,7 +135,6 @@
     description = "Core components for the Jetpack Camera Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +
             "experiences across all of Android."
-    legacyDisableKotlinStrictApiMode = true
     extraLicense {
         name = "BSD License"
         url = "https://chromium.googlesource.com/libyuv/libyuv/+/refs/heads/main/README.chromium"
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureExt.kt b/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureExt.kt
index 009c703..b09fe43 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureExt.kt
+++ b/camera/camera-core/src/main/java/androidx/camera/core/ImageCaptureExt.kt
@@ -41,7 +41,7 @@
  * @see ImageCapture.takePicture
  * @see ImageCapture.OnImageCapturedCallback
  */
-suspend fun ImageCapture.takePicture(
+public suspend fun ImageCapture.takePicture(
     onCaptureStarted: (() -> Unit)? = null,
     onCaptureProcessProgressed: ((Int) -> Unit)? = null,
     onPostviewBitmapAvailable: ((Bitmap) -> Unit)? = null,
@@ -92,7 +92,7 @@
  * @see ImageCapture.takePicture
  * @see ImageCapture.OnImageSavedCallback
  */
-suspend fun ImageCapture.takePicture(
+public suspend fun ImageCapture.takePicture(
     outputFileOptions: ImageCapture.OutputFileOptions,
     onCaptureStarted: (() -> Unit)? = null,
     onCaptureProcessProgressed: ((Int) -> Unit)? = null,
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/DynamicRanges.kt b/camera/camera-core/src/main/java/androidx/camera/core/impl/DynamicRanges.kt
index daa2c7a..e7a13b1 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/DynamicRanges.kt
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/DynamicRanges.kt
@@ -19,7 +19,7 @@
 import androidx.core.util.Preconditions
 
 /** Utility methods for handling dynamic range. */
-object DynamicRanges {
+public object DynamicRanges {
 
     /**
      * Returns `true` if the test dynamic range can resolve to the fully specified dynamic range
@@ -30,7 +30,7 @@
      * specified dynamic range.
      */
     @JvmStatic
-    fun canResolve(
+    public fun canResolve(
         dynamicRangeToTest: DynamicRange,
         fullySpecifiedDynamicRanges: Set<DynamicRange>,
     ): Boolean {
@@ -52,7 +52,7 @@
      * fully specified dynamic range.
      */
     @JvmStatic
-    fun findAllPossibleMatches(
+    public fun findAllPossibleMatches(
         dynamicRangesToTest: Set<DynamicRange>,
         fullySpecifiedDynamicRanges: Set<DynamicRange>
     ): Set<DynamicRange> {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/ScreenFlashWrapper.kt b/camera/camera-core/src/main/java/androidx/camera/core/internal/ScreenFlashWrapper.kt
index 1f3e67b..788dd43 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/ScreenFlashWrapper.kt
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/ScreenFlashWrapper.kt
@@ -27,7 +27,8 @@
  * This allows us to clean up properly in case a capture is cancelled earlier (e.g. ImageCapture is
  * unbound after [apply] is invoked but [clear] is not).
  */
-class ScreenFlashWrapper private constructor(private val screenFlash: ScreenFlash?) : ScreenFlash {
+internal class ScreenFlashWrapper private constructor(private val screenFlash: ScreenFlash?) :
+    ScreenFlash {
     private val lock = Object()
 
     @GuardedBy("lock") private var isClearScreenFlashPending: Boolean = false
diff --git a/camera/camera-effects-still-portrait/build.gradle b/camera/camera-effects-still-portrait/build.gradle
index 35b7c44..2c057af 100644
--- a/camera/camera-effects-still-portrait/build.gradle
+++ b/camera/camera-effects-still-portrait/build.gradle
@@ -35,5 +35,4 @@
     runApiTasks = new RunApiTasks.Yes()
     description = "A post-processing effect that works with CameraX Library, providing a portrait" +
             " mode effect that applies to still image captures."
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-effects/build.gradle b/camera/camera-effects/build.gradle
index 56fec95..6f6a6b0 100644
--- a/camera/camera-effects/build.gradle
+++ b/camera/camera-effects/build.gradle
@@ -57,5 +57,4 @@
     inceptionYear = "2023"
     description = "Camera effects components for the Jetpack Camera Library, a library providing " +
             "camera post-processing features such as drawing overlay with the CameraX library."
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java
index 3d35e5a..37c64c7 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceConfigurationImpl.java
@@ -54,7 +54,7 @@
     @Nullable
     OutputSurfaceImpl getPostviewOutputSurface();
 
-    /*
+    /**
      * Gets the color space.
      *
      * @since 1.5
diff --git a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java
index af5fd98..5d6a91a 100644
--- a/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java
+++ b/camera/camera-extensions-stub/src/main/java/androidx/camera/extensions/impl/advanced/OutputSurfaceImpl.java
@@ -37,6 +37,14 @@
      */
     long USAGE_UNSPECIFIED = -1;
 
+
+    /**
+     * This indicates the dataSpace is not specified which could happen in the apps that use older
+     * version of CameraX extensions where getDataspace() was not added yet.
+     *
+     */
+    int DATASPACE_UNSPECIFIED = -1;
+
     /**
      * Gets the surface. It returns null if output surface is not specified.
      */
@@ -56,6 +64,15 @@
     int getImageFormat();
 
     /**
+     * Gets the dataspace. It returns {#link #DATASPACE_UNSPECIFIED} if not specified.
+     *
+     * @since 1.5
+     */
+    default int getDataspace() {
+        return DATASPACE_UNSPECIFIED;
+    }
+
+    /**
      * Gets the surface usage bits. It returns {@link #USAGE_UNSPECIFIED} if not specified.
      *
      * @since 1.5
diff --git a/camera/camera-extensions/build.gradle b/camera/camera-extensions/build.gradle
index 97acbb4..034920a 100644
--- a/camera/camera-extensions/build.gradle
+++ b/camera/camera-extensions/build.gradle
@@ -94,5 +94,4 @@
     inceptionYear = "2019"
     description = "OEM Extensions for the Jetpack Camera Library, a library providing interfaces" +
             " to integrate with OEM specific camera features."
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-feature-combination-query-play-services/build.gradle b/camera/camera-feature-combination-query-play-services/build.gradle
index b3e6ba6..c971fd1 100644
--- a/camera/camera-feature-combination-query-play-services/build.gradle
+++ b/camera/camera-feature-combination-query-play-services/build.gradle
@@ -59,5 +59,4 @@
     runApiTasks = new RunApiTasks.Yes()
     description = "Camera feature combination components for the Jetpack Camera Library, a " +
             "library providing camera feature combination with Google Play Services dependencies."
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-feature-combination-query/build.gradle b/camera/camera-feature-combination-query/build.gradle
index 8e2b743..8276df3 100644
--- a/camera/camera-feature-combination-query/build.gradle
+++ b/camera/camera-feature-combination-query/build.gradle
@@ -53,5 +53,4 @@
     runApiTasks = new RunApiTasks.Yes()
     description = "Camera feature combination components for the Jetpack Camera Library, a library " +
             "providing a seamless experience for querying camera features across all of Android."
-    legacyDisableKotlinStrictApiMode = true
 }
\ No newline at end of file
diff --git a/camera/camera-lifecycle/build.gradle b/camera/camera-lifecycle/build.gradle
index c6aea3a..825351d 100644
--- a/camera/camera-lifecycle/build.gradle
+++ b/camera/camera-lifecycle/build.gradle
@@ -75,6 +75,5 @@
     description = "Lifecycle components for the Jetpack Camera Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +
             "experiences across all of Android."
-    legacyDisableKotlinStrictApiMode = true
     samples(project(":camera:camera-lifecycle:camera-lifecycle-samples"))
 }
diff --git a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraRepositoryTest.java b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraRepositoryTest.java
index b137a04..a297fd8 100644
--- a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraRepositoryTest.java
+++ b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/LifecycleCameraRepositoryTest.java
@@ -333,14 +333,6 @@
         assertThat(useCase.isDetached()).isTrue();
     }
 
-    @Test(expected = IllegalArgumentException.class)
-    public void exception_whenCreatingWithDestroyedLifecycle() {
-        mLifecycle.destroy();
-
-        // Should throw IllegalArgumentException
-        mRepository.createLifecycleCamera(mLifecycle, mCameraUseCaseAdapter);
-    }
-
     @Test
     public void lifecycleCameraIsStopped_whenNewLifecycleIsStarted() {
         // Starts first lifecycle and check LifecycleCamera active state is true.
@@ -617,6 +609,17 @@
         assertThat(firstLifecycleCamera.isActive()).isTrue();
     }
 
+    @Test
+    public void lifecycleCameraIsInactive_createAndBindToLifecycleCamera_AfterLifecycleDestroyed() {
+        mLifecycle.destroy();
+        LifecycleCamera lifecycleCamera = mRepository.createLifecycleCamera(mLifecycle,
+                mCameraUseCaseAdapter);
+        mRepository.bindToLifecycleCamera(lifecycleCamera, null, emptyList(),
+                Collections.singletonList(new FakeUseCase()), mCameraCoordinator);
+
+        assertThat(lifecycleCamera.isActive()).isFalse();
+    }
+
     private CameraUseCaseAdapter createNewCameraUseCaseAdapter() {
         String cameraId = String.valueOf(++mCameraId);
         CameraInternal fakeCamera = new FakeCamera(cameraId);
diff --git a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
index a214cd9..0688972 100644
--- a/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
+++ b/camera/camera-lifecycle/src/androidTest/java/androidx/camera/lifecycle/ProcessCameraProviderTest.kt
@@ -380,22 +380,6 @@
     }
 
     @Test
-    fun exception_withDestroyedLifecycle() {
-        ProcessCameraProvider.configureInstance(FakeAppConfig.create())
-
-        runBlocking(MainScope().coroutineContext) {
-            provider = ProcessCameraProvider.getInstance(context).await()
-
-            lifecycleOwner0.destroy()
-
-            assertThrows<IllegalArgumentException> {
-                provider.bindToLifecycle(lifecycleOwner0, CameraSelector.DEFAULT_BACK_CAMERA)
-            }
-            assertThat(provider.isConcurrentCameraModeOn).isFalse()
-        }
-    }
-
-    @Test
     fun bind_returnTheSameCameraForSameSelectorAndLifecycleOwner() {
         ProcessCameraProvider.configureInstance(FakeAppConfig.create())
 
@@ -581,6 +565,23 @@
     }
 
     @Test
+    fun lifecycleCameraIsNotActive_bindAfterLifecycleDestroyed() {
+        ProcessCameraProvider.configureInstance(FakeAppConfig.create())
+        runBlocking(MainScope().coroutineContext) {
+            provider = ProcessCameraProvider.getInstance(context).await()
+            val useCase = Preview.Builder().setSessionOptionUnpacker { _, _, _ -> }.build()
+            lifecycleOwner0.destroy()
+            val camera: LifecycleCamera =
+                provider.bindToLifecycle(
+                    lifecycleOwner0,
+                    CameraSelector.DEFAULT_BACK_CAMERA,
+                    useCase
+                ) as LifecycleCamera
+            assertThat(camera.isActive).isFalse()
+        }
+    }
+
+    @Test
     fun lifecycleCameraIsNotActive_unbindUseCase() {
         ProcessCameraProvider.configureInstance(FakeAppConfig.create())
         runBlocking(MainScope().coroutineContext) {
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
index 7b6e1e2..244c443 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/LifecycleCameraRepository.java
@@ -106,11 +106,6 @@
             Preconditions.checkArgument(mCameraMap.get(key) == null, "LifecycleCamera already "
                     + "exists for the given LifecycleOwner and set of cameras");
 
-            if (lifecycleOwner.getLifecycle().getCurrentState() == State.DESTROYED) {
-                throw new IllegalArgumentException(
-                        "Trying to create LifecycleCamera with destroyed lifecycle.");
-            }
-
             // Need to add observer before creating LifecycleCamera to make sure
             // it can be stopped before the latest active one is started.'
             lifecycleCamera = new LifecycleCamera(lifecycleOwner, cameraUseCaseAdaptor);
@@ -118,6 +113,12 @@
             if (cameraUseCaseAdaptor.getUseCases().isEmpty()) {
                 lifecycleCamera.suspend();
             }
+
+            // If the lifecycle is already DESTROYED, we don't need to register the camera.
+            if (lifecycleOwner.getLifecycle().getCurrentState() == State.DESTROYED) {
+                return lifecycleCamera;
+            }
+
             registerCamera(lifecycleCamera);
         }
         return lifecycleCamera;
@@ -280,6 +281,10 @@
             // LifecycleOwner.
             LifecycleCameraRepositoryObserver observer =
                     getLifecycleCameraRepositoryObserver(lifecycleOwner);
+            if (observer == null) {
+                // LifecycleCamera is not registered due to lifecycle destroyed, simply do nothing.
+                return;
+            }
             Set<Key> lifecycleCameraKeySet = mLifecycleObserverMap.get(observer);
 
             // Bypass the use cases lifecycle owner validation when concurrent camera mode is on.
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.kt b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.kt
index 1ac2586..259b94e 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.kt
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProvider.kt
@@ -85,7 +85,7 @@
  *
  * This is the standard provider for applications to use.
  */
-class ProcessCameraProvider private constructor() : LifecycleCameraProvider {
+public class ProcessCameraProvider private constructor() : LifecycleCameraProvider {
     private val mLock = Any()
 
     @GuardedBy("mLock") private var mCameraXConfigProvider: CameraXConfig.Provider? = null
@@ -116,7 +116,7 @@
      *   a no-op.
      */
     @VisibleForTesting
-    fun shutdownAsync(): ListenableFuture<Void> {
+    public fun shutdownAsync(): ListenableFuture<Void> {
         Threads.runOnMainSync {
             unbindAll()
             mLifecycleCameraRepository.clear()
@@ -196,7 +196,7 @@
      * @throws UnsupportedOperationException If the camera is configured in concurrent mode.
      */
     @MainThread
-    fun bindToLifecycle(
+    public fun bindToLifecycle(
         lifecycleOwner: LifecycleOwner,
         cameraSelector: CameraSelector,
         vararg useCases: UseCase?
@@ -236,7 +236,7 @@
      * @throws UnsupportedOperationException If the camera is configured in concurrent mode.
      */
     @MainThread
-    fun bindToLifecycle(
+    public fun bindToLifecycle(
         lifecycleOwner: LifecycleOwner,
         cameraSelector: CameraSelector,
         useCaseGroup: UseCaseGroup
@@ -323,7 +323,7 @@
      */
     @OptIn(ExperimentalCameraInfo::class)
     @MainThread
-    fun bindToLifecycle(singleCameraConfigs: List<SingleCameraConfig?>): ConcurrentCamera =
+    public fun bindToLifecycle(singleCameraConfigs: List<SingleCameraConfig?>): ConcurrentCamera =
         trace("CX:bindToLifecycle-Concurrent") {
             if (singleCameraConfigs.size < 2) {
                 throw IllegalArgumentException("Concurrent camera needs two camera configs.")
@@ -661,7 +661,7 @@
      * @throws UnsupportedOperationException If called in concurrent mode.
      */
     @MainThread
-    override fun unbind(vararg useCases: UseCase?) =
+    public override fun unbind(vararg useCases: UseCase?): Unit =
         trace("CX:unbind") {
             Threads.checkMainThread()
 
@@ -675,7 +675,7 @@
         }
 
     @MainThread
-    override fun unbindAll() =
+    public override fun unbindAll(): Unit =
         trace("CX:unbindAll") {
             Threads.checkMainThread()
             cameraOperatingMode = CAMERA_OPERATING_MODE_UNSPECIFIED
@@ -717,7 +717,7 @@
             return@trace availableCameraInfos
         }
 
-    val availableConcurrentCameraInfos: List<List<CameraInfo>>
+    public val availableConcurrentCameraInfos: List<List<CameraInfo>>
         /**
          * Returns list of [CameraInfo] instances of the available concurrent cameras.
          *
@@ -780,7 +780,7 @@
             return@trace restrictedCameraInfo!!
         }
 
-    val isConcurrentCameraModeOn: Boolean
+    public val isConcurrentCameraModeOn: Boolean
         /**
          * Returns whether there is a [ConcurrentCamera] bound.
          *
@@ -906,7 +906,7 @@
             mCameraX!!.cameraFactory.cameraCoordinator.activeConcurrentCameraInfos = cameraInfos
         }
 
-    companion object {
+    public companion object {
         private val sAppInstance = ProcessCameraProvider()
 
         /**
@@ -945,7 +945,7 @@
          */
         @Suppress("AsyncSuffixFuture")
         @JvmStatic
-        fun getInstance(context: Context): ListenableFuture<ProcessCameraProvider> {
+        public fun getInstance(context: Context): ListenableFuture<ProcessCameraProvider> {
             Preconditions.checkNotNull(context)
             return Futures.transform(
                 sAppInstance.getOrCreateCameraXInstance(context),
@@ -988,7 +988,7 @@
          */
         @JvmStatic
         @ExperimentalCameraProviderConfiguration
-        fun configureInstance(cameraXConfig: CameraXConfig) =
+        public fun configureInstance(cameraXConfig: CameraXConfig): Unit =
             trace("CX:configureInstance") { sAppInstance.configureInstanceInternal(cameraXConfig) }
     }
 }
diff --git a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProviderExt.kt b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProviderExt.kt
index b99b120..b9f1991 100644
--- a/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProviderExt.kt
+++ b/camera/camera-lifecycle/src/main/java/androidx/camera/lifecycle/ProcessCameraProviderExt.kt
@@ -32,5 +32,6 @@
  *   [InitializationException.cause] to get the error cause.
  * @see ProcessCameraProvider.getInstance
  */
-suspend fun ProcessCameraProvider.Companion.awaitInstance(context: Context) =
-    getInstance(context).await()
+public suspend fun ProcessCameraProvider.Companion.awaitInstance(
+    context: Context
+): ProcessCameraProvider = getInstance(context).await()
diff --git a/camera/camera-mlkit-vision/build.gradle b/camera/camera-mlkit-vision/build.gradle
index 770f44e..c7fa94a 100644
--- a/camera/camera-mlkit-vision/build.gradle
+++ b/camera/camera-mlkit-vision/build.gradle
@@ -59,5 +59,4 @@
     description = "MLKit vision components for the Jetpack Camera Library, a library providing a " +
             "seamless integration that enables camera driven computer vision features " +
             "across all of Android."
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-testing/build.gradle b/camera/camera-testing/build.gradle
index 358d69f..fc7e587 100644
--- a/camera/camera-testing/build.gradle
+++ b/camera/camera-testing/build.gradle
@@ -105,5 +105,4 @@
     description = "Testing components for the Jetpack Camera Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +"" +
             "experiences across all of Android."
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/Camera2Util.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/Camera2Util.kt
index 808b56c..6b299be 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/Camera2Util.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/Camera2Util.kt
@@ -29,10 +29,10 @@
 import kotlinx.coroutines.CompletableDeferred
 
 /** Convenient suspend functions for invoking camera2 APIs. */
-object Camera2Util {
+public object Camera2Util {
     /** Open the camera device and return the [CameraDevice] instance. */
     @DoNotInline
-    suspend fun openCameraDevice(
+    public suspend fun openCameraDevice(
         cameraManager: CameraManager,
         cameraId: String,
         handler: Handler
@@ -61,7 +61,7 @@
     }
 
     /** Creates and returns a configured [CameraCaptureSession]. */
-    suspend fun openCaptureSession(
+    public suspend fun openCaptureSession(
         cameraDevice: CameraDevice,
         surfaceList: List<Surface>,
         handler: Handler
@@ -89,7 +89,7 @@
      * Submits a single capture request to the [CameraCaptureSession] and returns the
      * [TotalCaptureResult].
      */
-    suspend fun submitSingleRequest(
+    public suspend fun submitSingleRequest(
         cameraDevice: CameraDevice,
         session: CameraCaptureSession,
         surfaces: List<Surface>,
@@ -127,7 +127,7 @@
     /**
      * Starts the repeating request, and invokes the given block when [TotalCaptureResult] arrives.
      */
-    fun startRepeating(
+    public fun startRepeating(
         cameraDevice: CameraDevice,
         session: CameraCaptureSession,
         surfaces: List<Surface>,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraPipeConfigTestRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraPipeConfigTestRule.kt
index c5272be..4cd34ba 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraPipeConfigTestRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraPipeConfigTestRule.kt
@@ -49,8 +49,8 @@
  *
  * @property active true to activate this rule.
  */
-class CameraPipeConfigTestRule(
-    val active: Boolean,
+public class CameraPipeConfigTestRule(
+    public val active: Boolean,
 ) : TestRule {
 
     override fun apply(base: Statement, description: Description): Statement =
@@ -109,7 +109,7 @@
             }
         }
 
-    companion object {
+    private companion object {
         private const val CAMERA2_TEST_DISABLE = "CAMERA2_TEST_DISABLE"
         private const val CAMERA_PIPE_TEST_FLAG = "CAMERA_PIPE_TESTING"
         private const val CAMERA_PIPE_MH_FLAG = "CameraPipeMH"
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraTaskTrackingExecutor.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraTaskTrackingExecutor.kt
index ddd3b22..1635035 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraTaskTrackingExecutor.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CameraTaskTrackingExecutor.kt
@@ -32,7 +32,7 @@
  * CameraX uses when user doesn't set any executor via [CameraXConfig.Builder.setCameraExecutor].
  */
 @VisibleForTesting
-class CameraTaskTrackingExecutor : Executor {
+public class CameraTaskTrackingExecutor : Executor {
     private val lock = Object()
     private val cameraTasks = mutableListOf<CompletableDeferred<Unit>>()
     private val cameraExecutor = CameraExecutor()
@@ -55,7 +55,7 @@
     }
 
     /** Waits for all submitted tasks to be completed. */
-    suspend fun awaitIdle() {
+    public suspend fun awaitIdle() {
         synchronized(lock) { cameraTasks }.awaitAll()
     }
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CaptureSimulation.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CaptureSimulation.kt
index 41e08b9..e960662 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CaptureSimulation.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CaptureSimulation.kt
@@ -36,14 +36,16 @@
 private const val TAG = "CaptureSimulation"
 
 /** Simulates a capture frame being drawn on all of the provided surfaces. */
-suspend fun List<DeferrableSurface>.simulateCaptureFrame() = forEach { it.simulateCaptureFrame() }
+public suspend fun List<DeferrableSurface>.simulateCaptureFrame(): Unit = forEach {
+    it.simulateCaptureFrame()
+}
 
 /**
  * Simulates a capture frame being drawn on the provided surface.
  *
  * @throws IllegalStateException If [DeferrableSurface.getSurface] provides a null surface.
  */
-suspend fun DeferrableSurface.simulateCaptureFrame() {
+public suspend fun DeferrableSurface.simulateCaptureFrame() {
     val deferred = CompletableDeferred<Unit>()
 
     Futures.addCallback(
@@ -86,7 +88,7 @@
  * @return A [ListenableFuture] representing when the operation has been completed.
  */
 @JvmOverloads
-fun List<DeferrableSurface>.simulateCaptureFrameAsync(
+public fun List<DeferrableSurface>.simulateCaptureFrameAsync(
     executor: Executor = Dispatchers.Default.asExecutor()
 ): ListenableFuture<Void> {
     val scope = CoroutineScope(SupervisorJob() + executor.asCoroutineDispatcher())
@@ -102,7 +104,7 @@
  * @return A [ListenableFuture] representing when the operation has been completed.
  */
 @JvmOverloads
-fun DeferrableSurface.simulateCaptureFrameAsync(
+public fun DeferrableSurface.simulateCaptureFrameAsync(
     executor: Executor = Dispatchers.Default.asExecutor()
 ): ListenableFuture<Void> {
     val scope = CoroutineScope(SupervisorJob() + executor.asCoroutineDispatcher())
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CoroutineAdapters.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CoroutineAdapters.kt
index 64ddd5e..2b5d7d7 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CoroutineAdapters.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/CoroutineAdapters.kt
@@ -33,7 +33,7 @@
  * The return value of the Future is null, and canceling the future will not cancel the Job. The tag
  * field may be used to help debug futures.
  */
-fun Job.asListenableFuture(tag: Any? = "Job.asListenableFuture"): ListenableFuture<Void> {
+public fun Job.asListenableFuture(tag: Any? = "Job.asListenableFuture"): ListenableFuture<Void> {
     val resolver: CallbackToFutureAdapter.Resolver<Void> =
         CallbackToFutureAdapter.Resolver<Void> { completer ->
             this.invokeOnCompletion {
@@ -54,7 +54,7 @@
 
 /** Convert a job into a ListenableFuture<T>. */
 @OptIn(ExperimentalCoroutinesApi::class)
-fun <T> Deferred<T>.asListenableFuture(
+public fun <T> Deferred<T>.asListenableFuture(
     tag: Any? = "Deferred.asListenableFuture"
 ): ListenableFuture<T> {
     val resolver: CallbackToFutureAdapter.Resolver<T> =
@@ -76,12 +76,12 @@
     return CallbackToFutureAdapter.getFuture(resolver)
 }
 
-fun <T> Deferred<T>.propagateTo(destination: CompletableDeferred<T>) {
+public fun <T> Deferred<T>.propagateTo(destination: CompletableDeferred<T>) {
     invokeOnCompletion { propagateOnceTo(destination, it) }
 }
 
 @OptIn(ExperimentalCoroutinesApi::class)
-fun <T> Deferred<T>.propagateOnceTo(
+public fun <T> Deferred<T>.propagateOnceTo(
     destination: CompletableDeferred<T>,
     throwable: Throwable?,
 ) {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/EncoderProfilesUtil.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/EncoderProfilesUtil.kt
index 30e6b11..80b5ffa 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/EncoderProfilesUtil.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/EncoderProfilesUtil.kt
@@ -28,108 +28,112 @@
  * Utility methods for testing [EncoderProfiles] related classes, including predefined resolutions,
  * attributes and [EncoderProfilesProxy], which can be used directly on the unit tests.
  */
-object EncoderProfilesUtil {
+public object EncoderProfilesUtil {
     /** Resolution for QCIF. */
-    val RESOLUTION_QCIF = Size(176, 144)
+    public val RESOLUTION_QCIF: Size = Size(176, 144)
 
     /** Resolution for QVGA. */
-    val RESOLUTION_QVGA = Size(320, 240)
+    public val RESOLUTION_QVGA: Size = Size(320, 240)
 
     /** Resolution for CIF. */
-    val RESOLUTION_CIF = Size(352, 288)
+    public val RESOLUTION_CIF: Size = Size(352, 288)
 
     /** Resolution for VGA. */
-    val RESOLUTION_VGA = Size(640, 480)
+    public val RESOLUTION_VGA: Size = Size(640, 480)
 
     /** Resolution for 480P. */
-    val RESOLUTION_480P = Size(720, 480) /* 640, 704 or 720 x 480 */
+    public val RESOLUTION_480P: Size = Size(720, 480) /* 640, 704 or 720 x 480 */
 
     /** Resolution for 720P. */
-    val RESOLUTION_720P = Size(1280, 720)
+    public val RESOLUTION_720P: Size = Size(1280, 720)
 
     /** Resolution for 1080P. */
-    val RESOLUTION_1080P = Size(1920, 1080) /* 1920 x 1080 or 1088 */
+    public val RESOLUTION_1080P: Size = Size(1920, 1080) /* 1920 x 1080 or 1088 */
 
     /** Resolution for 2K. */
-    val RESOLUTION_2K = Size(2048, 1080)
+    public val RESOLUTION_2K: Size = Size(2048, 1080)
 
     /** Resolution for QHD. */
-    val RESOLUTION_QHD = Size(2560, 1440)
+    public val RESOLUTION_QHD: Size = Size(2560, 1440)
 
     /** Resolution for 2160P. */
-    val RESOLUTION_2160P = Size(3840, 2160)
+    public val RESOLUTION_2160P: Size = Size(3840, 2160)
 
     /** Resolution for 4KDCI. */
-    val RESOLUTION_4KDCI = Size(4096, 2160)
+    public val RESOLUTION_4KDCI: Size = Size(4096, 2160)
 
     /** Default duration. */
-    const val DEFAULT_DURATION = 30
+    public const val DEFAULT_DURATION: Int = 30
 
     /** Default output format. */
-    const val DEFAULT_OUTPUT_FORMAT = MediaRecorder.OutputFormat.MPEG_4
+    public const val DEFAULT_OUTPUT_FORMAT: Int = MediaRecorder.OutputFormat.MPEG_4
 
     /** Default video codec. */
-    const val DEFAULT_VIDEO_CODEC = MediaRecorder.VideoEncoder.H264
+    public const val DEFAULT_VIDEO_CODEC: Int = MediaRecorder.VideoEncoder.H264
 
     /** Default media type. */
-    const val DEFAULT_VIDEO_MEDIA_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC
+    public const val DEFAULT_VIDEO_MEDIA_TYPE: String = MediaFormat.MIMETYPE_VIDEO_AVC
 
     /** Default video bitrate. */
-    const val DEFAULT_VIDEO_BITRATE = 8 * 1024 * 1024
+    public const val DEFAULT_VIDEO_BITRATE: Int = 8 * 1024 * 1024
 
     /** Default video frame rate. */
-    const val DEFAULT_VIDEO_FRAME_RATE = 30
+    public const val DEFAULT_VIDEO_FRAME_RATE: Int = 30
 
     /** Default video code profile. */
-    const val DEFAULT_VIDEO_PROFILE = EncoderProfilesProxy.CODEC_PROFILE_NONE
+    public const val DEFAULT_VIDEO_PROFILE: Int = EncoderProfilesProxy.CODEC_PROFILE_NONE
 
     /** Default bit depth. */
-    const val DEFAULT_VIDEO_BIT_DEPTH = VideoProfileProxy.BIT_DEPTH_8
+    public const val DEFAULT_VIDEO_BIT_DEPTH: Int = VideoProfileProxy.BIT_DEPTH_8
 
     /** Default chroma subsampling. */
-    const val DEFAULT_VIDEO_CHROMA_SUBSAMPLING = EncoderProfiles.VideoProfile.YUV_420
+    public const val DEFAULT_VIDEO_CHROMA_SUBSAMPLING: Int = EncoderProfiles.VideoProfile.YUV_420
 
     /** Default hdr format. */
-    const val DEFAULT_VIDEO_HDR_FORMAT = EncoderProfiles.VideoProfile.HDR_NONE
+    public const val DEFAULT_VIDEO_HDR_FORMAT: Int = EncoderProfiles.VideoProfile.HDR_NONE
 
     /** Default audio codec. */
-    const val DEFAULT_AUDIO_CODEC = MediaRecorder.AudioEncoder.AAC
+    public const val DEFAULT_AUDIO_CODEC: Int = MediaRecorder.AudioEncoder.AAC
 
     /** Default media type. */
-    const val DEFAULT_AUDIO_MEDIA_TYPE = MediaFormat.MIMETYPE_AUDIO_AAC
+    public const val DEFAULT_AUDIO_MEDIA_TYPE: String = MediaFormat.MIMETYPE_AUDIO_AAC
 
     /** Default audio bitrate. */
-    const val DEFAULT_AUDIO_BITRATE = 192000
+    public const val DEFAULT_AUDIO_BITRATE: Int = 192000
 
     /** Default audio sample rate. */
-    const val DEFAULT_AUDIO_SAMPLE_RATE = 48000
+    public const val DEFAULT_AUDIO_SAMPLE_RATE: Int = 48000
 
     /** Default channel count. */
-    const val DEFAULT_AUDIO_CHANNELS = 1
+    public const val DEFAULT_AUDIO_CHANNELS: Int = 1
 
     /** Default audio code profile. */
-    const val DEFAULT_AUDIO_PROFILE = EncoderProfilesProxy.CODEC_PROFILE_NONE
-    val PROFILES_QCIF =
+    public const val DEFAULT_AUDIO_PROFILE: Int = EncoderProfilesProxy.CODEC_PROFILE_NONE
+    public val PROFILES_QCIF: EncoderProfilesProxy =
         createFakeEncoderProfilesProxy(RESOLUTION_QCIF.width, RESOLUTION_QCIF.height)
-    val PROFILES_QVGA =
+    public val PROFILES_QVGA: EncoderProfilesProxy =
         createFakeEncoderProfilesProxy(RESOLUTION_QVGA.width, RESOLUTION_QVGA.height)
-    val PROFILES_CIF = createFakeEncoderProfilesProxy(RESOLUTION_CIF.width, RESOLUTION_CIF.height)
-    val PROFILES_VGA = createFakeEncoderProfilesProxy(RESOLUTION_VGA.width, RESOLUTION_VGA.height)
-    val PROFILES_480P =
+    public val PROFILES_CIF: EncoderProfilesProxy =
+        createFakeEncoderProfilesProxy(RESOLUTION_CIF.width, RESOLUTION_CIF.height)
+    public val PROFILES_VGA: EncoderProfilesProxy =
+        createFakeEncoderProfilesProxy(RESOLUTION_VGA.width, RESOLUTION_VGA.height)
+    public val PROFILES_480P: EncoderProfilesProxy =
         createFakeEncoderProfilesProxy(RESOLUTION_480P.width, RESOLUTION_480P.height)
-    val PROFILES_720P =
+    public val PROFILES_720P: EncoderProfilesProxy =
         createFakeEncoderProfilesProxy(RESOLUTION_720P.width, RESOLUTION_720P.height)
-    val PROFILES_1080P =
+    public val PROFILES_1080P: EncoderProfilesProxy =
         createFakeEncoderProfilesProxy(RESOLUTION_1080P.width, RESOLUTION_1080P.height)
-    val PROFILES_2K = createFakeEncoderProfilesProxy(RESOLUTION_2K.width, RESOLUTION_2K.height)
-    val PROFILES_QHD = createFakeEncoderProfilesProxy(RESOLUTION_QHD.width, RESOLUTION_QHD.height)
-    val PROFILES_2160P =
+    public val PROFILES_2K: EncoderProfilesProxy =
+        createFakeEncoderProfilesProxy(RESOLUTION_2K.width, RESOLUTION_2K.height)
+    public val PROFILES_QHD: EncoderProfilesProxy =
+        createFakeEncoderProfilesProxy(RESOLUTION_QHD.width, RESOLUTION_QHD.height)
+    public val PROFILES_2160P: EncoderProfilesProxy =
         createFakeEncoderProfilesProxy(RESOLUTION_2160P.width, RESOLUTION_2160P.height)
-    val PROFILES_4KDCI =
+    public val PROFILES_4KDCI: EncoderProfilesProxy =
         createFakeEncoderProfilesProxy(RESOLUTION_4KDCI.width, RESOLUTION_4KDCI.height)
 
     /** A utility method to create an EncoderProfilesProxy with some default values. */
-    fun createFakeEncoderProfilesProxy(
+    public fun createFakeEncoderProfilesProxy(
         videoFrameWidth: Int,
         videoFrameHeight: Int
     ): EncoderProfilesProxy {
@@ -144,7 +148,7 @@
     }
 
     /** A utility method to create a VideoProfileProxy with some default values. */
-    fun createFakeVideoProfileProxy(
+    public fun createFakeVideoProfileProxy(
         videoFrameWidth: Int,
         videoFrameHeight: Int,
         videoCodec: Int = DEFAULT_VIDEO_CODEC,
@@ -168,7 +172,7 @@
     }
 
     /** A utility method to create an AudioProfileProxy with some default values. */
-    fun createFakeAudioProfileProxy(): AudioProfileProxy {
+    public fun createFakeAudioProfileProxy(): AudioProfileProxy {
         return AudioProfileProxy.create(
             DEFAULT_AUDIO_CODEC,
             DEFAULT_AUDIO_MEDIA_TYPE,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/FileUtil.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/FileUtil.kt
index 47c04e7..333384d 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/FileUtil.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/FileUtil.kt
@@ -37,7 +37,7 @@
 private const val EXTENSION_MP4 = "mp4"
 private const val EXTENSION_TEXT = "txt"
 
-object FileUtil {
+public object FileUtil {
 
     /**
      * Write the given text to the external storage.
@@ -49,7 +49,7 @@
      * @return the [FileOutputOptions] instance.
      */
     @JvmStatic
-    fun writeTextToExternalFile(
+    public fun writeTextToExternalFile(
         text: String,
         fileName: String,
         extension: String = EXTENSION_TEXT
@@ -80,7 +80,7 @@
      * @see MediaStoreVideoCannotWrite
      */
     @JvmStatic
-    fun canDeviceWriteToMediaStore(): Boolean {
+    public fun canDeviceWriteToMediaStore(): Boolean {
         return DeviceQuirks.get(MediaStoreVideoCannotWrite::class.java) == null
     }
 
@@ -93,7 +93,7 @@
      * @return the [FileOutputOptions] instance.
      */
     @JvmStatic
-    fun generateVideoFileOutputOptions(
+    public fun generateVideoFileOutputOptions(
         fileName: String,
         extension: String = EXTENSION_MP4
     ): FileOutputOptions {
@@ -113,7 +113,7 @@
      * @return the [MediaStoreOutputOptions] instance.
      */
     @JvmStatic
-    fun generateVideoMediaStoreOptions(
+    public fun generateVideoMediaStoreOptions(
         contentResolver: ContentResolver,
         fileName: String
     ): MediaStoreOutputOptions =
@@ -136,7 +136,7 @@
      * @return `true` if the given file name is valid, otherwise `false`.
      */
     @JvmStatic
-    fun isFileNameValid(fileName: String?): Boolean {
+    public fun isFileNameValid(fileName: String?): Boolean {
         return !fileName.isNullOrBlank()
     }
 
@@ -148,7 +148,7 @@
      *   existing parent folder path is not a folder or failed to create.
      */
     @JvmStatic
-    fun createParentFolder(filePath: String): Boolean {
+    public fun createParentFolder(filePath: String): Boolean {
         return createParentFolder(File(filePath))
     }
 
@@ -160,7 +160,8 @@
      *   existing parent folder path is not a folder or failed to create.
      */
     @JvmStatic
-    fun createParentFolder(file: File): Boolean = file.parentFile?.let { createFolder(it) } ?: false
+    public fun createParentFolder(file: File): Boolean =
+        file.parentFile?.let { createFolder(it) } ?: false
 
     /**
      * Creates folder for the input file.
@@ -170,7 +171,7 @@
      *   existing folder path is not a folder or failed to create.
      */
     @JvmStatic
-    fun createFolder(file: File): Boolean =
+    public fun createFolder(file: File): Boolean =
         if (file.exists()) {
             file.isDirectory
         } else {
@@ -185,7 +186,7 @@
      * @return the file path of the content uri or null if not found.
      */
     @JvmStatic
-    fun getAbsolutePathFromUri(resolver: ContentResolver, contentUri: Uri): String? {
+    public fun getAbsolutePathFromUri(resolver: ContentResolver, contentUri: Uri): String? {
         // MediaStore.Video.Media.DATA was deprecated in API level 29.
         val column = MediaStore.Video.Media.DATA
         try {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IdlingResourceUtil.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IdlingResourceUtil.kt
index 7a18645..9f16e26 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IdlingResourceUtil.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IdlingResourceUtil.kt
@@ -20,7 +20,7 @@
 import androidx.test.espresso.IdlingRegistry
 import androidx.test.espresso.IdlingResource
 
-fun IdlingResource?.waitForIdle() {
+public fun IdlingResource?.waitForIdle() {
     if (this != null) {
         IdlingRegistry.getInstance().register(this)
         Espresso.onIdle()
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IgnoreAudioProblematicDeviceRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IgnoreAudioProblematicDeviceRule.kt
index 20bb81e..79bade4 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IgnoreAudioProblematicDeviceRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IgnoreAudioProblematicDeviceRule.kt
@@ -24,7 +24,7 @@
 import org.junit.runners.model.Statement
 
 /** Test class to set the TestRule that should not be run on the audio problematically devices. */
-class IgnoreAudioProblematicDeviceRule : TestRule {
+public class IgnoreAudioProblematicDeviceRule : TestRule {
     private val isProblematicDevices = isPixel2Api26Emulator || isPixel2Api30Emulator
 
     override fun apply(base: Statement, description: Description): Statement {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IgnoreProblematicDeviceRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IgnoreProblematicDeviceRule.kt
index 85cfee3..416c89b 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IgnoreProblematicDeviceRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/IgnoreProblematicDeviceRule.kt
@@ -25,7 +25,7 @@
 import org.junit.runners.model.Statement
 
 /** Test class to set the TestRule should not be run on the problematic devices. */
-class IgnoreProblematicDeviceRule : TestRule {
+public class IgnoreProblematicDeviceRule : TestRule {
     private val api21Emulator = isEmulator && Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
 
     private val isProblematicDevices = isPixel2Api26Emulator || api21Emulator
@@ -48,7 +48,7 @@
         }
     }
 
-    companion object {
+    public companion object {
         // Sync from TestRequestBuilder.RequiresDeviceFilter
         private const val EMULATOR_HARDWARE_GOLDFISH = "goldfish"
         private const val EMULATOR_HARDWARE_RANCHU = "ranchu"
@@ -66,12 +66,12 @@
                 ""
             }
 
-        val isEmulator = emulatorHardwareNames.contains(Build.HARDWARE.lowercase())
-        val isPixel2Api26Emulator =
+        public val isEmulator: Boolean = emulatorHardwareNames.contains(Build.HARDWARE.lowercase())
+        public val isPixel2Api26Emulator: Boolean =
             isEmulator &&
                 avdName.contains("Pixel2", ignoreCase = true) &&
                 Build.VERSION.SDK_INT == Build.VERSION_CODES.O
-        val isPixel2Api30Emulator =
+        public val isPixel2Api30Emulator: Boolean =
             isEmulator &&
                 avdName.contains("Pixel2", ignoreCase = true) &&
                 Build.VERSION.SDK_INT == Build.VERSION_CODES.R
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/LabTestRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/LabTestRule.kt
index 311351e..841c3e2 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/LabTestRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/LabTestRule.kt
@@ -59,7 +59,7 @@
  * adb shell setprop log.tag.rearCameraE2E DEBUG
  * ```
  */
-class LabTestRule : TestRule {
+public class LabTestRule : TestRule {
 
     /**
      * The annotation for tests that only want to run on the CameraX lab environment. Local device
@@ -68,7 +68,7 @@
      */
     @Target(AnnotationTarget.FUNCTION)
     @Retention(AnnotationRetention.RUNTIME)
-    annotation class LabTestOnly()
+    public annotation class LabTestOnly()
 
     /**
      * The annotation for tests that only want to run on the CameraX lab environment with enabling
@@ -77,7 +77,7 @@
      */
     @Target(AnnotationTarget.FUNCTION)
     @Retention(AnnotationRetention.RUNTIME)
-    annotation class LabTestFrontCamera()
+    public annotation class LabTestFrontCamera()
 
     /**
      * The annotation for tests that only want to run on the CameraX lab environment with enabling
@@ -86,9 +86,9 @@
      */
     @Target(AnnotationTarget.FUNCTION)
     @Retention(AnnotationRetention.RUNTIME)
-    annotation class LabTestRearCamera()
+    public annotation class LabTestRearCamera()
 
-    class LabTestStatement(private val statement: Statement) : Statement() {
+    public class LabTestStatement(private val statement: Statement) : Statement() {
 
         @Throws(Throwable::class)
         override fun evaluate() {
@@ -100,7 +100,7 @@
         }
     }
 
-    class LabTestFrontCameraStatement(private val statement: Statement) : Statement() {
+    public class LabTestFrontCameraStatement(private val statement: Statement) : Statement() {
 
         @Throws(Throwable::class)
         override fun evaluate() {
@@ -111,7 +111,7 @@
         }
     }
 
-    class LabTestRearCameraStatement(private val statement: Statement) : Statement() {
+    public class LabTestRearCameraStatement(private val statement: Statement) : Statement() {
 
         @Throws(Throwable::class)
         override fun evaluate() {
@@ -135,9 +135,9 @@
         }
     }
 
-    companion object {
+    public companion object {
         @JvmStatic
-        fun isInLabTest(): Boolean {
+        public fun isInLabTest(): Boolean {
             return Log.isLoggable("MH", Log.DEBUG)
         }
 
@@ -152,7 +152,9 @@
          * @return if enabled camera is in same direction as [lensFacing] in CameraX lab environment
          */
         @JvmStatic
-        fun isLensFacingEnabledInLabTest(@CameraSelector.LensFacing lensFacing: Int) =
+        public fun isLensFacingEnabledInLabTest(
+            @CameraSelector.LensFacing lensFacing: Int
+        ): Boolean =
             when (lensFacing) {
                 CameraSelector.LENS_FACING_BACK -> Log.isLoggable("rearCameraE2E", Log.DEBUG)
                 CameraSelector.LENS_FACING_FRONT -> Log.isLoggable("frontCameraE2E", Log.DEBUG)
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/MetadataRetrieverExt.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/MetadataRetrieverExt.kt
index 920d7e8..f497775 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/MetadataRetrieverExt.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/MetadataRetrieverExt.kt
@@ -30,7 +30,7 @@
 import androidx.camera.core.impl.utils.TransformUtils.is90or270
 import androidx.camera.core.impl.utils.TransformUtils.rotateSize
 
-fun MediaMetadataRetriever.useAndRelease(block: (MediaMetadataRetriever) -> Unit) {
+public fun MediaMetadataRetriever.useAndRelease(block: (MediaMetadataRetriever) -> Unit) {
     try {
         block(this)
     } finally {
@@ -38,28 +38,34 @@
     }
 }
 
-fun MediaMetadataRetriever.hasAudio(): Boolean = extractMetadata(METADATA_KEY_HAS_AUDIO) == "yes"
+public fun MediaMetadataRetriever.hasAudio(): Boolean =
+    extractMetadata(METADATA_KEY_HAS_AUDIO) == "yes"
 
-fun MediaMetadataRetriever.hasVideo(): Boolean = extractMetadata(METADATA_KEY_HAS_VIDEO) == "yes"
+public fun MediaMetadataRetriever.hasVideo(): Boolean =
+    extractMetadata(METADATA_KEY_HAS_VIDEO) == "yes"
 
-fun MediaMetadataRetriever.getDurationMs(): Long = extractMetadata(METADATA_KEY_DURATION)!!.toLong()
+public fun MediaMetadataRetriever.getDurationMs(): Long =
+    extractMetadata(METADATA_KEY_DURATION)!!.toLong()
 
-fun MediaMetadataRetriever.getRotation(): Int =
+public fun MediaMetadataRetriever.getRotation(): Int =
     extractMetadata(METADATA_KEY_VIDEO_ROTATION)?.toInt() ?: 0
 
-fun MediaMetadataRetriever.getWidth(): Int = extractMetadata(METADATA_KEY_VIDEO_WIDTH)!!.toInt()
+public fun MediaMetadataRetriever.getWidth(): Int =
+    extractMetadata(METADATA_KEY_VIDEO_WIDTH)!!.toInt()
 
-fun MediaMetadataRetriever.getHeight(): Int = extractMetadata(METADATA_KEY_VIDEO_HEIGHT)!!.toInt()
+public fun MediaMetadataRetriever.getHeight(): Int =
+    extractMetadata(METADATA_KEY_VIDEO_HEIGHT)!!.toInt()
 
-fun MediaMetadataRetriever.getResolution(): Size = Size(getWidth(), getHeight())
+public fun MediaMetadataRetriever.getResolution(): Size = Size(getWidth(), getHeight())
 
-fun MediaMetadataRetriever.getRotatedResolution(): Size = rotateSize(getResolution(), getRotation())
+public fun MediaMetadataRetriever.getRotatedResolution(): Size =
+    rotateSize(getResolution(), getRotation())
 
-fun MediaMetadataRetriever.getAspectRatio(): Rational = Rational(getWidth(), getHeight())
+public fun MediaMetadataRetriever.getAspectRatio(): Rational = Rational(getWidth(), getHeight())
 
-fun MediaMetadataRetriever.getRotatedAspectRatio(): Rational =
+public fun MediaMetadataRetriever.getRotatedAspectRatio(): Rational =
     if (is90or270(getRotation())) Rational(getHeight(), getWidth()) else getAspectRatio()
 
-fun MediaMetadataRetriever.getMimeType(): String = extractMetadata(METADATA_KEY_MIMETYPE)!!
+public fun MediaMetadataRetriever.getMimeType(): String = extractMetadata(METADATA_KEY_MIMETYPE)!!
 
-fun MediaMetadataRetriever.getLocation(): String = extractMetadata(METADATA_KEY_LOCATION)!!
+public fun MediaMetadataRetriever.getLocation(): String = extractMetadata(METADATA_KEY_LOCATION)!!
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/RequiresDevice.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/RequiresDevice.kt
index 998df1b..8b38cea 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/RequiresDevice.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/RequiresDevice.kt
@@ -33,4 +33,4 @@
 @CustomFilter(filterClass = RequiresDeviceFilter::class)
 @Retention(AnnotationRetention.RUNTIME)
 @Target(AnnotationTarget.FUNCTION)
-annotation class RequiresDevice
+public annotation class RequiresDevice
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/RequiresDeviceFilter.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/RequiresDeviceFilter.kt
index e6e4465..bd733cc 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/RequiresDeviceFilter.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/RequiresDeviceFilter.kt
@@ -27,9 +27,9 @@
  * The detection conditions of emulator should be the same as
  * androidx.test.internal.runner.TestRequestBuilder.RequiresDeviceFilter.
  */
-class RequiresDeviceFilter : AbstractFilter() {
+public class RequiresDeviceFilter : AbstractFilter() {
 
-    companion object {
+    private companion object {
         private val annotationClass = RequiresDevice::class.java
     }
 
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/StressTestRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/StressTestRule.kt
index 872f34b..c0b5286 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/StressTestRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/StressTestRule.kt
@@ -21,7 +21,7 @@
 import org.junit.runner.Description
 import org.junit.runners.model.Statement
 
-class StressTestRule : TestRule {
+public class StressTestRule : TestRule {
     override fun apply(base: Statement, description: Description): Statement =
         object : Statement() {
             override fun evaluate() {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt
index 9e7c9ef..d066f3c 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/WakelockEmptyActivityRule.kt
@@ -39,7 +39,8 @@
  * By default, all brands will be enabled (brandsToEnable == null). Caller can specify the brand
  * list to enable the rule via the [brandsToEnable] parameter.
  */
-class WakelockEmptyActivityRule(val brandsToEnable: List<String>? = null) : TestRule {
+public class WakelockEmptyActivityRule(private val brandsToEnable: List<String>? = null) :
+    TestRule {
     override fun apply(base: Statement, description: Description): Statement =
         object : Statement() {
             override fun evaluate() {
@@ -101,12 +102,12 @@
 }
 
 @RequiresApi(Build.VERSION_CODES.O_MR1)
-object Api27Impl {
-    fun Activity.setShowWhenLocked() {
+public object Api27Impl {
+    public fun Activity.setShowWhenLocked() {
         setShowWhenLocked(true)
     }
 
-    fun Activity.setTurnScreenOn() {
+    public fun Activity.setTurnScreenOn() {
         setTurnScreenOn(true)
     }
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/activity/EmptyActivity.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/activity/EmptyActivity.kt
index c0ca714..9cbb838 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/activity/EmptyActivity.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/activity/EmptyActivity.kt
@@ -19,4 +19,4 @@
 import androidx.appcompat.app.AppCompatActivity
 
 /** An empty Activity. */
-class EmptyActivity : AppCompatActivity()
+public class EmptyActivity : AppCompatActivity()
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeCameraConfig.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeCameraConfig.kt
index 09df7c8..1ad3e24 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeCameraConfig.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeCameraConfig.kt
@@ -25,7 +25,7 @@
 import androidx.camera.core.impl.UseCaseConfigFactory
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class FakeCameraConfig(
+public class FakeCameraConfig(
     private val sessionProcessor: SessionProcessor? = null,
     private val postviewSupported: Boolean = false,
     private val captureProcessProgressSupported: Boolean = false
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeCameraFilter.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeCameraFilter.kt
index 32180d8..94747b4 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeCameraFilter.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeCameraFilter.kt
@@ -22,7 +22,7 @@
 import androidx.camera.core.impl.Identifier
 
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-class FakeCameraFilter(val id: Identifier = CameraFilter.DEFAULT_ID) : CameraFilter {
+public class FakeCameraFilter(private val id: Identifier = CameraFilter.DEFAULT_ID) : CameraFilter {
 
     override fun filter(cameraInfos: List<CameraInfo>): List<CameraInfo> {
         return ArrayList(cameraInfos)
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeEncoderInfo.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeEncoderInfo.kt
index 3617556..58245a7 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeEncoderInfo.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeEncoderInfo.kt
@@ -18,6 +18,6 @@
 
 import androidx.camera.video.internal.encoder.EncoderInfo
 
-open class FakeEncoderInfo(var _name: String = "fake.encoder") : EncoderInfo {
+public open class FakeEncoderInfo(private var _name: String = "fake.encoder") : EncoderInfo {
     override fun getName(): String = _name
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt
index 1fb9757..b600a40 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeImageCaptureCallback.kt
@@ -31,9 +31,10 @@
 private const val CAPTURE_TIMEOUT = 15_000.toLong() //  15 seconds
 
 /** A fake implementation of the [ImageCapture.OnImageCapturedCallback] and used for test. */
-class FakeImageCaptureCallback(captureCount: Int = 1) : ImageCapture.OnImageCapturedCallback() {
+public class FakeImageCaptureCallback(captureCount: Int = 1) :
+    ImageCapture.OnImageCapturedCallback() {
     /** Data class of various image properties which are tested. */
-    data class ImageProperties(
+    public data class ImageProperties(
         val size: Size? = null,
         val format: Int = -1,
         val rotationDegrees: Int = -1,
@@ -42,8 +43,8 @@
     )
 
     private val latch = CountdownDeferred(captureCount)
-    val results = mutableListOf<ImageProperties>()
-    val errors = mutableListOf<ImageCaptureException>()
+    public val results: MutableList<ImageProperties> = mutableListOf()
+    public val errors: MutableList<ImageCaptureException> = mutableListOf()
 
     override fun onCaptureSuccess(image: ImageProxy) {
         results.add(
@@ -75,11 +76,11 @@
         return null
     }
 
-    suspend fun awaitCaptures(timeout: Long = CAPTURE_TIMEOUT) {
+    public suspend fun awaitCaptures(timeout: Long = CAPTURE_TIMEOUT) {
         Truth.assertThat(withTimeoutOrNull(timeout) { latch.await() }).isNotNull()
     }
 
-    suspend fun awaitCapturesAndAssert(
+    public suspend fun awaitCapturesAndAssert(
         timeout: Long = CAPTURE_TIMEOUT,
         capturedImagesCount: Int = 0,
         errorsCount: Int = 0
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeSessionConfigOptionUnpacker.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeSessionConfigOptionUnpacker.kt
index 7429810..429fb3b 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeSessionConfigOptionUnpacker.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeSessionConfigOptionUnpacker.kt
@@ -22,7 +22,7 @@
 import androidx.camera.core.impl.SessionConfig
 import androidx.camera.core.impl.UseCaseConfig
 
-class FakeSessionConfigOptionUnpacker : SessionConfig.OptionUnpacker {
+public class FakeSessionConfigOptionUnpacker : SessionConfig.OptionUnpacker {
     override fun unpack(
         resolution: Size,
         config: UseCaseConfig<*>,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeSessionProcessor.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeSessionProcessor.kt
index c9de12e..715e8bb 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeSessionProcessor.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeSessionProcessor.kt
@@ -49,13 +49,13 @@
 import kotlinx.coroutines.withTimeout
 import kotlinx.coroutines.withTimeoutOrNull
 
-const val FAKE_CAPTURE_SEQUENCE_ID = 1
+private const val FAKE_CAPTURE_SEQUENCE_ID = 1
 
 @RequiresApi(23) // ImageWriter requires API 23+
-class FakeSessionProcessor(
-    val inputFormatPreview: Int? = null,
-    val inputFormatCapture: Int? = null,
-    val postviewSupportedSizes: Map<Int, List<Size>>? = null,
+public class FakeSessionProcessor(
+    private val inputFormatPreview: Int? = null,
+    private val inputFormatCapture: Int? = null,
+    private val postviewSupportedSizes: Map<Int, List<Size>>? = null,
 ) : SessionProcessor {
     private lateinit var previewProcessorSurface: DeferrableSurface
     private lateinit var captureProcessorSurface: DeferrableSurface
@@ -89,18 +89,19 @@
     private var rotationDegrees = 0
     private var jpegQuality = 100
 
-    @RestrictedCameraInfo.CameraOperation var restrictedCameraOperations: Set<Int> = emptySet()
+    @RestrictedCameraInfo.CameraOperation
+    public var restrictedCameraOperations: Set<Int> = emptySet()
 
-    fun releaseSurfaces() {
+    public fun releaseSurfaces() {
         intermediaPreviewImageReader?.close()
         intermediaCaptureImageReader?.close()
     }
 
-    fun runAfterInitSession(block: () -> Unit) {
+    public fun runAfterInitSession(block: () -> Unit) {
         blockRunAfterInitSession = block
     }
 
-    fun setStillCaptureFailedImmediately(failedImmediately: Boolean) {
+    public fun setStillCaptureFailedImmediately(failedImmediately: Boolean) {
         failStillCaptureImmediately = failedImmediately
     }
 
@@ -244,7 +245,7 @@
         onCaptureSessionEndCalled.complete(SystemClock.elapsedRealtimeNanos())
     }
 
-    fun getLatestParameters(): Config {
+    public fun getLatestParameters(): Config {
         return latestParameters
     }
 
@@ -413,52 +414,52 @@
 
     override fun abortCapture(captureSequenceId: Int) {}
 
-    suspend fun assertInitSessionInvoked(): Long {
+    public suspend fun assertInitSessionInvoked(): Long {
         return initSessionCalled.awaitWithTimeout(3000)
     }
 
-    suspend fun awaitInitSessionOutputSurfaceConfiguration(): OutputSurfaceConfiguration {
+    public suspend fun awaitInitSessionOutputSurfaceConfiguration(): OutputSurfaceConfiguration {
         return initSessionOutputSurfaceConfiguration.awaitWithTimeout(3000)
     }
 
-    suspend fun assertDeInitSessionInvoked(): Long {
+    public suspend fun assertDeInitSessionInvoked(): Long {
         return deInitSessionCalled.awaitWithTimeout(3000)
     }
 
-    suspend fun assertOnCaptureSessionStartInvoked(): Long {
+    public suspend fun assertOnCaptureSessionStartInvoked(): Long {
         return onCaptureSessionStartCalled.awaitWithTimeout(3000)
     }
 
-    suspend fun wasOnCaptureSessionStartInvoked(): Boolean {
+    public suspend fun wasOnCaptureSessionStartInvoked(): Boolean {
         val result = withTimeoutOrNull(3000) { onCaptureSessionStartCalled.await() }
         return result != null
     }
 
-    suspend fun assertOnCaptureEndInvoked(): Long {
+    public suspend fun assertOnCaptureEndInvoked(): Long {
         return onCaptureSessionEndCalled.awaitWithTimeout(3000)
     }
 
-    suspend fun assertStartRepeatingInvoked(): Long {
+    public suspend fun assertStartRepeatingInvoked(): Long {
         return startRepeatingCalled.awaitWithTimeout(3000)
     }
 
-    suspend fun assertStartCaptureInvoked(): Long {
+    public suspend fun assertStartCaptureInvoked(): Long {
         return startCaptureCalled.awaitWithTimeout(3000)
     }
 
-    suspend fun assertStartCapturePostviewEnabled() {
+    public suspend fun assertStartCapturePostviewEnabled() {
         assertThat(startCapturePostviewEnabled.awaitWithTimeout(3000)).isTrue()
     }
 
-    suspend fun assertSetParametersInvoked(): Config {
+    public suspend fun assertSetParametersInvoked(): Config {
         return setParametersCalled.awaitWithTimeout(3000)
     }
 
-    suspend fun assertStartTriggerInvoked(): Config {
+    public suspend fun assertStartTriggerInvoked(): Config {
         return startTriggerCalled.awaitWithTimeout(3000)
     }
 
-    suspend fun assertStopRepeatingInvoked(): Long {
+    public suspend fun assertStopRepeatingInvoked(): Long {
         return stopRepeatingCalled.awaitWithTimeout(3000)
     }
 
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeVideoEncoderInfo.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeVideoEncoderInfo.kt
index 51b1dc0..c60c319 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeVideoEncoderInfo.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/fakes/FakeVideoEncoderInfo.kt
@@ -19,20 +19,20 @@
 import android.util.Range
 import androidx.camera.video.internal.encoder.VideoEncoderInfo
 
-class FakeVideoEncoderInfo(
-    @JvmField var canSwapWidthHeight: Boolean = true,
-    @JvmField var supportedWidths: Range<Int> = Range.create(0, Integer.MAX_VALUE),
-    @JvmField var supportedHeights: Range<Int> = Range.create(0, Integer.MAX_VALUE),
-    @JvmField var widthAlignment: Int = 2,
-    @JvmField var heightAlignment: Int = 2,
-    @JvmField var supportedBitrateRange: Range<Int> = Range(1, Int.MAX_VALUE)
+public class FakeVideoEncoderInfo(
+    @JvmField public var canSwapWidthHeight: Boolean = true,
+    @JvmField public var supportedWidths: Range<Int> = Range.create(0, Integer.MAX_VALUE),
+    @JvmField public var supportedHeights: Range<Int> = Range.create(0, Integer.MAX_VALUE),
+    @JvmField public var widthAlignment: Int = 2,
+    @JvmField public var heightAlignment: Int = 2,
+    @JvmField public var supportedBitrateRange: Range<Int> = Range(1, Int.MAX_VALUE)
 ) : FakeEncoderInfo(), VideoEncoderInfo {
 
     override fun canSwapWidthHeight(): Boolean {
         return canSwapWidthHeight
     }
 
-    override fun isSizeSupported(width: Int, height: Int) =
+    override fun isSizeSupported(width: Int, height: Int): Boolean =
         supportedWidths.contains(width) &&
             supportedHeights.contains(height) &&
             width.mod(widthAlignment) == 0 &&
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumerExtensions.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumerExtensions.kt
index 0f7d83a..584993c 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumerExtensions.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockConsumerExtensions.kt
@@ -34,7 +34,7 @@
  *   condition is met.
  * @see [MockConsumer.verifyAcceptCall]
  */
-fun <T> MockConsumer<T>.verifyAcceptCallExt(
+public fun <T> MockConsumer<T>.verifyAcceptCallExt(
     classType: Class<*>,
     inOrder: Boolean = false,
     timeoutMs: Long = NO_TIMEOUT,
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashListener.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashListener.kt
index 766aa4f..931b8a7 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashListener.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/mocks/MockScreenFlashListener.kt
@@ -22,7 +22,7 @@
 import java.util.concurrent.TimeUnit
 
 /** A mock implementations of [ScreenFlashListener] for testing purpose. */
-class MockScreenFlashListener : ScreenFlashListener {
+public class MockScreenFlashListener : ScreenFlashListener {
     private val lock = Object()
 
     @GuardedBy("lock") private var completeCount: Int = 0
@@ -34,7 +34,7 @@
     }
 
     /** Gets the number of times [onCompleted] was invoked. */
-    fun getCompleteCount() = synchronized(lock) { completeCount }
+    public fun getCompleteCount(): Int = synchronized(lock) { completeCount }
 
     /**
      * Waits for [onCompleted] to be invoked once.
@@ -42,7 +42,7 @@
      * @param timeoutInMillis The timeout of waiting in milliseconds.
      * @return True if [onCompleted] was invoked, false if timed out.
      */
-    fun awaitComplete(timeoutInMillis: Long): Boolean {
+    public fun awaitComplete(timeoutInMillis: Long): Boolean {
         return completeLatch.await(timeoutInMillis, TimeUnit.MILLISECONDS)
     }
 }
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/AudioChecker.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/AudioChecker.kt
index 43d25aa..b7392a1 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/AudioChecker.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/AudioChecker.kt
@@ -28,12 +28,12 @@
 import androidx.camera.video.internal.config.AudioSettingsDefaultResolver
 import kotlinx.coroutines.runBlocking
 
-class AudioChecker {
+public class AudioChecker {
 
-    companion object {
+    public companion object {
         private const val TAG = "AudioChecker"
 
-        fun canAudioStreamBeStarted(
+        public fun canAudioStreamBeStarted(
             videoCapabilities: VideoCapabilities,
             qualitySelector: QualitySelector
         ): Boolean {
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/Recording.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/Recording.kt
index d3dd4cf..a1d2fa5 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/Recording.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/Recording.kt
@@ -49,7 +49,7 @@
 import java.util.concurrent.Executor
 import kotlinx.coroutines.CompletableDeferred
 
-class Recording
+public class Recording
 internal constructor(
     private val context: Context,
     private val recorder: Recorder,
@@ -81,9 +81,9 @@
         }
     private lateinit var recording: androidx.camera.video.Recording
     private val listener = MockConsumer<VideoRecordEvent>()
-    val stoppedDeferred = CompletableDeferred<Unit>()
+    public val stoppedDeferred: CompletableDeferred<Unit> = CompletableDeferred()
 
-    fun start(): Recording {
+    public fun start(): Recording {
         recording =
             pendingRecording.start(callbackExecutor) {
                 if (it is Finalize) {
@@ -94,7 +94,7 @@
         return this
     }
 
-    fun startAndVerify(
+    public fun startAndVerify(
         statusCount: Int = defaultVerifyStatusCount,
     ): Recording {
         start()
@@ -103,7 +103,7 @@
         return this
     }
 
-    fun verifyStart() {
+    public fun verifyStart() {
         try {
             listener.verifyEvent(Start::class.java).single()
         } catch (t: Throwable) {
@@ -111,7 +111,7 @@
         }
     }
 
-    fun verifyStatus(
+    public fun verifyStatus(
         statusCount: Int = defaultVerifyStatusCount,
     ): List<Status> {
         try {
@@ -123,7 +123,7 @@
         }
     }
 
-    fun stop() {
+    public fun stop() {
         if (this::recording.isInitialized) {
             recordingStopStrategy.invoke(recording, recorder)
         } else {
@@ -131,17 +131,17 @@
         }
     }
 
-    fun stopAndVerify(error: Int? = ERROR_NONE): RecordingResult {
+    public fun stopAndVerify(error: Int? = ERROR_NONE): RecordingResult {
         stop()
         return verifyFinalize(error = error)
     }
 
-    fun recordAndVerify(error: Int? = ERROR_NONE): RecordingResult {
+    public fun recordAndVerify(error: Int? = ERROR_NONE): RecordingResult {
         startAndVerify()
         return stopAndVerify(error = error)
     }
 
-    fun verifyFinalize(
+    public fun verifyFinalize(
         timeoutMs: Long = defaultVerifyTimeoutMs,
         error: Int? = ERROR_NONE,
     ): RecordingResult {
@@ -170,18 +170,18 @@
         }
     }
 
-    fun pause(): Recording {
+    public fun pause(): Recording {
         recording.pause()
         return this
     }
 
-    fun pauseAndVerify(): Recording {
+    public fun pauseAndVerify(): Recording {
         pause()
         verifyPause()
         return this
     }
 
-    fun verifyPause() {
+    public fun verifyPause() {
         try {
             listener.verifyEvent(Pause::class.java).single()
         } catch (t: Throwable) {
@@ -189,12 +189,12 @@
         }
     }
 
-    fun resume(): Recording {
+    public fun resume(): Recording {
         recording.resume()
         return this
     }
 
-    fun resumeAndVerify(statusCount: Int = defaultVerifyStatusCount): Recording {
+    public fun resumeAndVerify(statusCount: Int = defaultVerifyStatusCount): Recording {
         resume()
         verifyResume()
         verifyStatus(statusCount)
@@ -210,12 +210,12 @@
         }
     }
 
-    fun mute(muted: Boolean): Recording {
+    public fun mute(muted: Boolean): Recording {
         recording.mute(muted)
         return this
     }
 
-    fun muteAndVerify(muted: Boolean): Recording {
+    public fun muteAndVerify(muted: Boolean): Recording {
         mute(muted)
         verifyMute(muted)
         return this
@@ -243,13 +243,14 @@
         }
     }
 
-    fun clearEvents() = listener.clearAcceptCalls()
+    public fun clearEvents(): Unit = listener.clearAcceptCalls()
 
-    fun getAllEvents(): List<VideoRecordEvent> = listener.getAllEvents(VideoRecordEvent::class.java)
+    public fun getAllEvents(): List<VideoRecordEvent> =
+        listener.getAllEvents(VideoRecordEvent::class.java)
 
-    fun getStatusEvents(): List<Status> = listener.getAllEvents(Status::class.java)
+    public fun getStatusEvents(): List<Status> = listener.getAllEvents(Status::class.java)
 
-    fun verifyNoMoreEvent() = listener.verifyNoMoreAcceptCalls(/* inOrder= */ true)
+    public fun verifyNoMoreEvent(): Unit = listener.verifyNoMoreAcceptCalls(/* inOrder= */ true)
 
     private fun MockConsumer<VideoRecordEvent>.verifyStatus(
         eventCount: Int = defaultVerifyStatusCount,
@@ -275,12 +276,12 @@
         eventType: Class<in T>
     ): List<T> = verifyEvent(eventType, CallTimesAtLeast(1), inOrder = false)
 
-    class RecordingResult(val finalize: Finalize) {
+    public class RecordingResult(public val finalize: Finalize) {
 
-        val uri: Uri
+        public val uri: Uri
             get() = finalize.outputResults.outputUri
 
-        val file: File by lazy {
+        public val file: File by lazy {
             when (finalize.outputOptions) {
                 is FileOutputOptions -> File(uri.path!!)
                 else -> throw AssertionError()
diff --git a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/RecordingSession.kt b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/RecordingSession.kt
index 0c3fb9c..f8866da 100644
--- a/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/RecordingSession.kt
+++ b/camera/camera-testing/src/main/java/androidx/camera/testing/impl/video/RecordingSession.kt
@@ -25,10 +25,10 @@
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.withTimeoutOrNull
 
-class RecordingSession(
+public class RecordingSession(
     private val defaults: Defaults,
 ) {
-    data class Defaults(
+    public data class Defaults(
         val context: Context,
         val recorder: Recorder,
         val outputOptionsProvider: () -> OutputOptions,
@@ -45,7 +45,7 @@
 
     private val recordingsToStop = mutableListOf<Recording>()
 
-    fun createRecording(
+    public fun createRecording(
         context: Context = defaults.context,
         recorder: Recorder = defaults.recorder,
         outputOptions: OutputOptions = defaults.outputOptionsProvider.invoke(),
@@ -68,7 +68,7 @@
     }
 
     // Intentionally made a non-suspend function, which is convenient at the end of most tests.
-    fun release(timeoutMs: Long) = runBlocking {
+    public fun release(timeoutMs: Long): Unit = runBlocking {
         withTimeoutOrNull(timeoutMs) {
             recordingsToStop
                 .filter { !it.stoppedDeferred.isCompleted }
diff --git a/camera/camera-video/build.gradle b/camera/camera-video/build.gradle
index 5fd497e..ed18c77 100644
--- a/camera/camera-video/build.gradle
+++ b/camera/camera-video/build.gradle
@@ -90,5 +90,4 @@
     description = "Video components for the Jetpack Camera Library, a library providing a " +
             "consistent and reliable camera foundation that enables great camera driven " +
             "experiences across all of Android."
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-view/build.gradle b/camera/camera-view/build.gradle
index 640ce6fa..251128f 100644
--- a/camera/camera-view/build.gradle
+++ b/camera/camera-view/build.gradle
@@ -89,5 +89,4 @@
     description = "UI tools for the Jetpack Camera Library, a library providing a consistent and " +
             "reliable camera foundation that enables great camera driven experiences across all " +
             "of Android."
-    legacyDisableKotlinStrictApiMode = true
 }
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/impl/ZoomGestureDetector.kt b/camera/camera-view/src/main/java/androidx/camera/view/impl/ZoomGestureDetector.kt
index 2677423..c8cd13d 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/impl/ZoomGestureDetector.kt
+++ b/camera/camera-view/src/main/java/androidx/camera/view/impl/ZoomGestureDetector.kt
@@ -53,7 +53,7 @@
  * @constructor Creates a ZoomGestureDetector for detecting zooming gesture.
  */
 // TODO(b/357735083): Remove the class and use the public release in camera-viewfinder-core.
-class ZoomGestureDetector
+public class ZoomGestureDetector
 @SuppressLint("ExecutorRegistration")
 @JvmOverloads
 constructor(
@@ -70,11 +70,11 @@
      * @param focusX The X coordinate of the current gesture's focal point in pixels.
      * @param focusY The Y coordinate of the current gesture's focal point in pixels.
      */
-    abstract class ZoomEvent
+    public abstract class ZoomEvent
     private constructor(
-        @IntRange(from = 0) val eventTime: Long,
-        @Px @IntRange(from = 0) val focusX: Int,
-        @Px @IntRange(from = 0) val focusY: Int
+        @IntRange(from = 0) public val eventTime: Long,
+        @Px @IntRange(from = 0) public val focusX: Int,
+        @Px @IntRange(from = 0) public val focusY: Int
     ) {
         /**
          * The beginning of a zoom gesture. Reported by new pointers going down.
@@ -84,7 +84,7 @@
          * @param focusX The X coordinate of the current gesture's focal point in pixels.
          * @param focusY The Y coordinate of the current gesture's focal point in pixels.
          */
-        class Begin(
+        public class Begin(
             @IntRange(from = 0) eventTime: Long,
             @Px @IntRange(from = 0) focusX: Int,
             @Px @IntRange(from = 0) focusY: Int
@@ -101,11 +101,11 @@
          *   current event. The value will be less than `1.0` when zooming out (larger FOV) and will
          *   be larger than `1.0` when zooming in (narrower FOV).
          */
-        class Move(
+        public class Move(
             @IntRange(from = 0) eventTime: Long,
             @Px @IntRange(from = 0) focusX: Int,
             @Px @IntRange(from = 0) focusY: Int,
-            @FloatRange(from = 0.0, fromInclusive = false) val incrementalScaleFactor: Float
+            @FloatRange(from = 0.0, fromInclusive = false) public val incrementalScaleFactor: Float
         ) : ZoomEvent(eventTime, focusX, focusY)
 
         /**
@@ -119,11 +119,11 @@
          *   current event. The value will be less than `1.0` when zooming out (larger FOV) and will
          *   be larger than `1.0` when zooming in (narrower FOV).
          */
-        class End(
+        public class End(
             @IntRange(from = 0) eventTime: Long,
             @Px @IntRange(from = 0) focusX: Int,
             @Px @IntRange(from = 0) focusY: Int,
-            @FloatRange(from = 0.0, fromInclusive = false) val incrementalScaleFactor: Float
+            @FloatRange(from = 0.0, fromInclusive = false) public val incrementalScaleFactor: Float
         ) : ZoomEvent(eventTime, focusX, focusY)
     }
 
@@ -135,7 +135,7 @@
      * - Zero or more [ZoomEvent.Move]
      * - One [ZoomEvent.End]
      */
-    fun interface OnZoomGestureListener {
+    public fun interface OnZoomGestureListener {
         /**
          * Responds to the events of a zooming gesture.
          *
@@ -156,7 +156,7 @@
          * @param zoomEvent The zoom event that contains extended info about event state.
          * @return Whether or not the detector should consider this event as handled.
          */
-        @UiThread fun onZoomEvent(zoomEvent: ZoomEvent): Boolean
+        @UiThread public fun onZoomEvent(zoomEvent: ZoomEvent): Boolean
     }
 
     /**
@@ -181,7 +181,7 @@
      *
      * If not set, this is enabled by default.
      */
-    var isQuickZoomEnabled: Boolean = true
+    public var isQuickZoomEnabled: Boolean = true
 
     /**
      * Whether the stylus zoom gesture, in which the user uses a stylus and presses the button,
@@ -189,7 +189,7 @@
      *
      * If not set, this is enabled by default.
      */
-    var isStylusZoomEnabled = true
+    public var isStylusZoomEnabled: Boolean = true
 
     /**
      * The average distance in pixels between each of the pointers forming the gesture in progress
@@ -268,7 +268,7 @@
      *   case.
      */
     @UiThread
-    fun onTouchEvent(event: MotionEvent): Boolean {
+    public fun onTouchEvent(event: MotionEvent): Boolean {
         eventTime = event.eventTime
 
         val action = event.actionMasked
@@ -465,7 +465,7 @@
         return if (previousSpan > 0) currentSpan / previousSpan else 1.0f
     }
 
-    val timeDelta: Long
+    public val timeDelta: Long
         /**
          * Returns the time difference in milliseconds between the previous accepted zooming event
          * and the current zooming event.
@@ -474,7 +474,7 @@
          */
         get() = eventTime - prevTime
 
-    companion object {
+    public companion object {
         // The default minimum span that the detector interprets a zooming event with. It's set to 0
         // to give the most responsiveness.
         // TODO(b/314702145): define a different span if appropriate.
diff --git a/compose/foundation/foundation/api/current.txt b/compose/foundation/foundation/api/current.txt
index 2c49d91..8f80daa 100644
--- a/compose/foundation/foundation/api/current.txt
+++ b/compose/foundation/foundation/api/current.txt
@@ -1880,6 +1880,7 @@
     method @androidx.compose.runtime.Composable public static androidx.compose.foundation.text.input.TextFieldState rememberTextFieldState(optional String initialText, optional long initialSelection);
     method public static void setTextAndPlaceCursorAtEnd(androidx.compose.foundation.text.input.TextFieldState, String text);
     method public static void setTextAndSelectAll(androidx.compose.foundation.text.input.TextFieldState, String text);
+    method public static androidx.compose.foundation.text.input.TextFieldBuffer toTextFieldBuffer(androidx.compose.foundation.text.input.TextFieldState);
   }
 
   @kotlin.jvm.JvmInline public final value class TextObfuscationMode {
diff --git a/compose/foundation/foundation/api/restricted_current.txt b/compose/foundation/foundation/api/restricted_current.txt
index 1148c67..46ea9c3 100644
--- a/compose/foundation/foundation/api/restricted_current.txt
+++ b/compose/foundation/foundation/api/restricted_current.txt
@@ -1885,6 +1885,7 @@
     method @androidx.compose.runtime.Composable public static androidx.compose.foundation.text.input.TextFieldState rememberTextFieldState(optional String initialText, optional long initialSelection);
     method public static void setTextAndPlaceCursorAtEnd(androidx.compose.foundation.text.input.TextFieldState, String text);
     method public static void setTextAndSelectAll(androidx.compose.foundation.text.input.TextFieldState, String text);
+    method public static androidx.compose.foundation.text.input.TextFieldBuffer toTextFieldBuffer(androidx.compose.foundation.text.input.TextFieldState);
   }
 
   @kotlin.jvm.JvmInline public final value class TextObfuscationMode {
diff --git a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt
index ff04f60..d8c4977 100644
--- a/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt
+++ b/compose/foundation/foundation/samples/src/main/java/androidx/compose/foundation/samples/BasicTextFieldSamples.kt
@@ -52,6 +52,7 @@
 import androidx.compose.foundation.text.input.rememberTextFieldState
 import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
 import androidx.compose.foundation.text.input.then
+import androidx.compose.foundation.text.input.toTextFieldBuffer
 import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.Text
@@ -563,3 +564,17 @@
         }
     )
 }
+
+@Suppress("UNUSED_VARIABLE")
+@Sampled
+@Composable
+fun TextFieldStateApplyOutputTransformation() {
+    val state = TextFieldState("Hello, World")
+    val outputTransformation = OutputTransformation { insert(0, "> ") }
+
+    val buffer = state.toTextFieldBuffer()
+    with(outputTransformation) { buffer.transformOutput() }
+
+    val transformedText = buffer.asCharSequence()
+    val transformedSelection = buffer.selection
+}
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableParameterizedKeyInputTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableParameterizedKeyInputTest.kt
index 65b960f..151bf4c 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableParameterizedKeyInputTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/CombinedClickableParameterizedKeyInputTest.kt
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalTestApi::class)
+
 package androidx.compose.foundation
 
 import androidx.compose.foundation.interaction.FocusInteraction
@@ -43,6 +45,7 @@
 import androidx.compose.ui.platform.LocalInputModeManager
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.ExperimentalTestApi
+import androidx.compose.ui.test.InjectionScope
 import androidx.compose.ui.test.assertIsNotEnabled
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
@@ -85,7 +88,6 @@
     @get:Rule val rule = createComposeRule()
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun clickWithKey() {
         var counter = 0
         val focusRequester = FocusRequester()
@@ -115,7 +117,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun clickWithKey_notInvokedIfFocusIsLostWhilePressed() {
         var counter = 0
         val outerFocusRequester = FocusRequester()
@@ -154,7 +155,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun clickWithKey_notInvokedIfCorrespondingDownEventWasNotReceived() {
         var counter = 0
         val outerFocusRequester = FocusRequester()
@@ -200,7 +200,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun longClickWithKey() {
         var clickCounter = 0
         var longClickCounter = 0
@@ -237,7 +236,6 @@
         }
     }
 
-    @OptIn(ExperimentalTestApi::class)
     @Test
     @LargeTest
     fun longClickWithKey_doesNotTriggerHapticFeedback() {
@@ -289,7 +287,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun longClickWithKey_notInvokedIfFocusIsLostWhilePressed() {
         var counter = 0
         val outerFocusRequester = FocusRequester()
@@ -331,7 +328,209 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
+    fun doubleClickWithKey_withinTimeout_aboveMinimumDuration() {
+        var clickCounter = 0
+        var doubleClickCounter = 0
+        val focusRequester = FocusRequester()
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            BasicText(
+                "ClickableText",
+                modifier =
+                    Modifier.testTag("myClickable")
+                        .focusRequester(focusRequester)
+                        .combinedClickable(
+                            onDoubleClick = { ++doubleClickCounter },
+                            onClick = { ++clickCounter }
+                        )
+            )
+        }
+        rule.runOnIdle {
+            inputModeManager.requestInputMode(Keyboard)
+            focusRequester.requestFocus()
+        }
+
+        rule.onNodeWithTag("myClickable").performKeyInput {
+            keyDown(key)
+            keyUp(key)
+            advanceEventTime(doubleTapDelay)
+            keyDown(key)
+            keyUp(key)
+        }
+
+        // Double click should not trigger click, and the double click should be immediately invoked
+        rule.runOnIdle {
+            assertThat(clickCounter).isEqualTo(0)
+            assertThat(doubleClickCounter).isEqualTo(1)
+        }
+    }
+
+    @Test
+    fun doubleClickWithKey_withinTimeout_belowMinimumDuration() {
+        var clickCounter = 0
+        var doubleClickCounter = 0
+        val focusRequester = FocusRequester()
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            BasicText(
+                "ClickableText",
+                modifier =
+                    Modifier.testTag("myClickable")
+                        .focusRequester(focusRequester)
+                        .combinedClickable(
+                            onDoubleClick = { ++doubleClickCounter },
+                            onClick = { ++clickCounter }
+                        )
+            )
+        }
+        rule.runOnIdle {
+            inputModeManager.requestInputMode(Keyboard)
+            focusRequester.requestFocus()
+        }
+
+        var doubleTapTimeoutDelay: Long = 0
+
+        rule.onNodeWithTag("myClickable").performKeyInput {
+            doubleTapTimeoutDelay = viewConfiguration.doubleTapTimeoutMillis + 100
+            keyDown(key)
+            keyUp(key)
+            // Send a second press below the minimum time required for a double tap
+            val minimumDuration = viewConfiguration.doubleTapMinTimeMillis
+            advanceEventTime(minimumDuration / 2)
+            keyDown(key)
+            keyUp(key)
+        }
+
+        // Because the second tap was below the timeout, this should instead be treated as two
+        // clicks, but the second click won't be invoked until after the timeout
+        rule.runOnIdle {
+            assertThat(clickCounter).isEqualTo(1)
+            assertThat(doubleClickCounter).isEqualTo(0)
+        }
+
+        // After the timeout has run out, the second click will be invoked, and no double click will
+        // be invoked
+        rule.mainClock.advanceTimeBy(doubleTapTimeoutDelay)
+        rule.runOnIdle {
+            assertThat(clickCounter).isEqualTo(2)
+            assertThat(doubleClickCounter).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun doubleClickWithKey_outsideTimeout() {
+        var clickCounter = 0
+        var doubleClickCounter = 0
+        val focusRequester = FocusRequester()
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            BasicText(
+                "ClickableText",
+                modifier =
+                    Modifier.testTag("myClickable")
+                        .focusRequester(focusRequester)
+                        .combinedClickable(
+                            onDoubleClick = { ++doubleClickCounter },
+                            onClick = { ++clickCounter }
+                        )
+            )
+        }
+        rule.runOnIdle {
+            inputModeManager.requestInputMode(Keyboard)
+            focusRequester.requestFocus()
+        }
+
+        var delay: Long = 0
+
+        rule.onNodeWithTag("myClickable").performKeyInput {
+            // Delay slightly past the timeout
+            delay = viewConfiguration.doubleTapTimeoutMillis + 100
+            keyDown(key)
+            keyUp(key)
+        }
+
+        // The click should not be invoked until the timeout has run out
+        rule.runOnIdle {
+            assertThat(clickCounter).isEqualTo(0)
+            assertThat(doubleClickCounter).isEqualTo(0)
+        }
+
+        // After the timeout has run out, the click will be invoked
+        rule.mainClock.advanceTimeBy(delay)
+        rule.runOnIdle {
+            assertThat(clickCounter).isEqualTo(1)
+            assertThat(doubleClickCounter).isEqualTo(0)
+        }
+
+        // Perform a second click, after the timeout has elapsed - this should not trigger a double
+        // click
+        rule.onNodeWithTag("myClickable").performKeyInput {
+            keyDown(key)
+            keyUp(key)
+        }
+
+        // The second click should not be invoked until the timeout has run out
+        rule.runOnIdle {
+            assertThat(clickCounter).isEqualTo(1)
+            assertThat(doubleClickCounter).isEqualTo(0)
+        }
+
+        // After the timeout has run out, the second click will be invoked, and no double click will
+        // be invoked
+        rule.mainClock.advanceTimeBy(delay)
+        rule.runOnIdle {
+            assertThat(clickCounter).isEqualTo(2)
+            assertThat(doubleClickCounter).isEqualTo(0)
+        }
+    }
+
+    @Test
+    fun doubleClickWithKey_secondClickIsALongClick() {
+        var clickCounter = 0
+        var doubleClickCounter = 0
+        var longClickCounter = 0
+        val focusRequester = FocusRequester()
+        lateinit var inputModeManager: InputModeManager
+        rule.setContent {
+            inputModeManager = LocalInputModeManager.current
+            BasicText(
+                "ClickableText",
+                modifier =
+                    Modifier.testTag("myClickable")
+                        .focusRequester(focusRequester)
+                        .combinedClickable(
+                            onDoubleClick = { ++doubleClickCounter },
+                            onClick = { ++clickCounter },
+                            onLongClick = { ++longClickCounter }
+                        )
+            )
+        }
+        rule.runOnIdle {
+            inputModeManager.requestInputMode(Keyboard)
+            focusRequester.requestFocus()
+        }
+
+        rule.onNodeWithTag("myClickable").performKeyInput {
+            keyDown(key)
+            keyUp(key)
+            advanceEventTime(doubleTapDelay)
+            keyDown(key)
+            // Advance past long click timeout
+            advanceEventTime(1000)
+        }
+
+        // Long click should cancel double click and click
+        rule.runOnIdle {
+            assertThat(clickCounter).isEqualTo(0)
+            assertThat(doubleClickCounter).isEqualTo(0)
+            assertThat(longClickCounter).isEqualTo(1)
+        }
+    }
+
+    @Test
     fun keyPress_emitsInteraction() {
         val interactionSource = MutableInteractionSource()
         val focusRequester = FocusRequester()
@@ -377,7 +576,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun keyPress_emitsCancelInteractionWhenFocusIsRemovedWhilePressed() {
         val interactionSource = MutableInteractionSource()
         val outerFocusRequester = FocusRequester()
@@ -428,7 +626,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun doubleKeyPress_emitsFurtherInteractions() {
         val interactionSource = MutableInteractionSource()
         val focusRequester = FocusRequester()
@@ -489,7 +686,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun repeatKeyEvents_doNotEmitFurtherInteractions() {
         val interactionSource = MutableInteractionSource()
         val focusRequester = FocusRequester()
@@ -548,7 +744,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun interruptedKeyClick_emitsCancelInteraction() {
         val interactionSource = MutableInteractionSource()
         val focusRequester = FocusRequester()
@@ -614,7 +809,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun updateOnLongClickListenerBetweenKeyDownAndUp_callsNewListener() {
         var clickCounter = 0
         var longClickCounter = 0
@@ -662,7 +856,6 @@
     }
 
     @Test
-    @OptIn(ExperimentalTestApi::class)
     fun modifierReusedBetweenKeyDownAndKeyUp_doesNotCallListeners() {
         var clickCounter = 0
         var longClickCounter = 0
@@ -710,3 +903,7 @@
         }
     }
 }
+
+/** Average of the min time and timeout for the delay between clicks for a double click */
+private val InjectionScope.doubleTapDelay
+    get() = with(viewConfiguration) { (doubleTapMinTimeMillis + doubleTapTimeoutMillis) / 2 }
diff --git a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProviderTest.kt b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProviderTest.kt
index f36ca53..6787fcf 100644
--- a/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProviderTest.kt
+++ b/compose/foundation/foundation/src/androidInstrumentedTest/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProviderTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.compose.foundation.gestures.snapping
 
+import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.scrollBy
 import androidx.compose.foundation.layout.Box
@@ -28,6 +29,7 @@
 import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.times
 import androidx.test.filters.LargeTest
 import kotlin.math.roundToInt
 import kotlin.test.assertEquals
@@ -39,8 +41,8 @@
 
 @LargeTest
 @RunWith(Parameterized::class)
-class LazyListSnapLayoutInfoProviderTest(orientation: Orientation) :
-    BaseLazyListTestWithOrientation(orientation) {
+class LazyListSnapLayoutInfoProviderTest(val config: Config) :
+    BaseLazyListTestWithOrientation(config.orientation) {
 
     lateinit var layoutInfoProvider: SnapLayoutInfoProvider
     lateinit var scope: CoroutineScope
@@ -59,7 +61,7 @@
             state = rememberLazyListState(initialFirstVisibleItemIndex = 100)
             layoutInfoProvider =
                 remember(state) { SnapLayoutInfoProvider(state, SnapPosition.Start) }
-            MainLayout()
+            MainLayout(config.useHeader)
         }
 
         rule.runOnIdle {
@@ -86,7 +88,7 @@
             state = rememberLazyListState(initialFirstVisibleItemIndex = 100)
             layoutInfoProvider =
                 remember(state) { SnapLayoutInfoProvider(state, SnapPosition.Start) }
-            MainLayout()
+            MainLayout(config.useHeader)
         }
 
         rule.runOnIdle {
@@ -119,7 +121,7 @@
             state = rememberLazyListState(initialFirstVisibleItemIndex = 100)
             layoutInfoProvider =
                 remember(state) { SnapLayoutInfoProvider(state, SnapPosition.Start) }
-            MainLayout()
+            MainLayout(config.useHeader)
         }
 
         rule.runOnIdle {
@@ -144,19 +146,31 @@
         }
     }
 
+    @OptIn(ExperimentalFoundationApi::class)
     @Composable
-    private fun MainLayout() {
+    private fun MainLayout(includeHeader: Boolean = false) {
         LazyColumnOrRow(
             state = state,
             flingBehavior = rememberSnapFlingBehavior(layoutInfoProvider)
         ) {
+            if (includeHeader) {
+                stickyHeader { Box(modifier = Modifier.size(2 * itemSizeDp)) }
+            }
             items(200) { Box(modifier = Modifier.size(itemSizeDp)) }
         }
     }
 
     companion object {
+        class Config(val orientation: Orientation, val useHeader: Boolean)
+
         @JvmStatic
         @Parameterized.Parameters(name = "{0}")
-        fun params() = arrayOf(Orientation.Vertical, Orientation.Horizontal)
+        fun params() =
+            arrayOf(
+                Config(Orientation.Vertical, true),
+                Config(Orientation.Vertical, false),
+                Config(Orientation.Horizontal, true),
+                Config(Orientation.Horizontal, false)
+            )
     }
 }
diff --git a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/TextFieldBufferTest.kt b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/TextFieldBufferTest.kt
index 27d8af2..92f72a5a 100644
--- a/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/TextFieldBufferTest.kt
+++ b/compose/foundation/foundation/src/androidUnitTest/kotlin/androidx/compose/foundation/text/input/TextFieldBufferTest.kt
@@ -590,6 +590,30 @@
         }
     }
 
+    @Test
+    fun createFromTextFieldState() {
+        val state = TextFieldState("Hello", TextRange(3))
+        val buffer = state.toTextFieldBuffer()
+
+        assertThat(buffer.asCharSequence().toString()).isEqualTo("Hello")
+        assertThat(buffer.selection).isEqualTo(TextRange(3))
+        assertThat(buffer.changes.changeCount).isEqualTo(0)
+
+        // guarding against future changes
+        assertThat(buffer.originalValue).isEqualTo(state.value)
+    }
+
+    @Test
+    fun changesToBuffer_doesNotPropagateToTextFieldState() {
+        val state = TextFieldState("Hello", TextRange(3))
+        val buffer = state.toTextFieldBuffer()
+
+        buffer.replace(0, 5, "World")
+
+        assertThat(buffer.asCharSequence().toString()).isEqualTo("World")
+        assertThat(state.text.toString()).isEqualTo("Hello")
+    }
+
     private fun testSelectionAdjustment(
         initial: String,
         transform: TextFieldBuffer.() -> Unit,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
index ee11b6e..8727b51 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/Clickable.kt
@@ -808,7 +808,12 @@
         role,
         onClick
     ) {
+    class DoubleKeyClickState(val job: Job) {
+        var doubleTapMinTimeMillisElapsed: Boolean = false
+    }
+
     private val longKeyPressJobs = mutableLongObjectMapOf<Job>()
+    private val doubleKeyClickStates = mutableLongObjectMapOf<DoubleKeyClickState>()
 
     override suspend fun PointerInputScope.clickPointerInput() {
         detectTapGestures(
@@ -905,6 +910,7 @@
 
     override fun onClickKeyDownEvent(event: KeyEvent): Boolean {
         val keyCode = event.key.keyCode
+        var handledByLongClick = false
         if (onLongClick != null) {
             if (longKeyPressJobs[keyCode] == null) {
                 longKeyPressJobs[keyCode] =
@@ -912,24 +918,84 @@
                         delay(currentValueOf(LocalViewConfiguration).longPressTimeoutMillis)
                         onLongClick?.invoke()
                     }
-                return true
+                handledByLongClick = true
             }
         }
-        return false
+        val doubleClickState = doubleKeyClickStates[keyCode]
+        // This is the second down event, so it might be a double click
+        if (doubleClickState != null) {
+            // Within the allowed timeout, so check if this is above the minimum time needed for
+            // a double click
+            if (doubleClickState.job.isActive) {
+                doubleClickState.job.cancel()
+                // If the second down was before the minimum double tap time, don't track this as
+                // a double click. Instead, we need to invoke onClick for the previous click, since
+                // that is now counted as a standalone click instead of the first of a double click.
+                if (!doubleClickState.doubleTapMinTimeMillisElapsed) {
+                    onClick()
+                    doubleKeyClickStates.remove(keyCode)
+                }
+            } else {
+                // We already invoked onClick because we passed the timeout, so stop tracking this
+                // as a double click
+                doubleKeyClickStates.remove(keyCode)
+            }
+        }
+        return handledByLongClick
     }
 
     override fun onClickKeyUpEvent(event: KeyEvent): Boolean {
         val keyCode = event.key.keyCode
+        var longClickInvoked = false
         if (longKeyPressJobs[keyCode] != null) {
             longKeyPressJobs[keyCode]?.let {
                 if (it.isActive) {
                     it.cancel()
-                    onClick()
+                } else {
+                    // If we already passed the timeout, we invoked long click already, and so
+                    // we shouldn't invoke onClick in this case
+                    longClickInvoked = true
                 }
             }
             longKeyPressJobs.remove(keyCode)
+        }
+        if (onDoubleClick != null) {
+            when {
+                // First click
+                doubleKeyClickStates[keyCode] == null -> {
+                    // We only track the second click if the first click was not a long click
+                    if (!longClickInvoked) {
+                        doubleKeyClickStates[keyCode] =
+                            DoubleKeyClickState(
+                                coroutineScope.launch {
+                                    val configuration = currentValueOf(LocalViewConfiguration)
+                                    val minTime = configuration.doubleTapMinTimeMillis
+                                    val timeout = configuration.doubleTapTimeoutMillis
+                                    delay(minTime)
+                                    doubleKeyClickStates[keyCode]?.doubleTapMinTimeMillisElapsed =
+                                        true
+                                    // Delay the remainder until we are at timeout
+                                    delay(timeout - minTime)
+                                    // If there was no second key press after the timeout, invoke
+                                    // onClick as normal
+                                    onClick()
+                                }
+                            )
+                    }
+                }
+                // Second click
+                else -> {
+                    // Invoke onDoubleClick if the second click was not a long click
+                    if (!longClickInvoked) {
+                        onDoubleClick?.invoke()
+                    }
+                    doubleKeyClickStates.remove(keyCode)
+                }
+            }
         } else {
-            onClick()
+            if (!longClickInvoked) {
+                onClick()
+            }
         }
         return true
     }
@@ -948,6 +1014,10 @@
             forEachValue { it.cancel() }
             clear()
         }
+        doubleKeyClickStates.apply {
+            forEachValue { it.job.cancel() }
+            clear()
+        }
     }
 }
 
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
index 062e0e5..e044f9f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/gestures/snapping/LazyListSnapLayoutInfoProvider.kt
@@ -20,6 +20,7 @@
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.lazy.LazyListLayoutInfo
 import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.foundation.lazy.layout.LazyLayoutMeasuredItem
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.unit.Density
@@ -66,6 +67,7 @@
             var upperBoundOffset = Float.POSITIVE_INFINITY
 
             layoutInfo.visibleItemsInfo.fastForEach { item ->
+                if ((item as? LazyLayoutMeasuredItem)?.nonScrollableItem == true) return@fastForEach
                 val offset =
                     calculateDistanceToDesiredSnapPosition(
                         mainAxisViewPortSize = layoutInfo.singleAxisViewportSize,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt
index d23fdb7..aaa5947 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/text/input/TextFieldState.kt
@@ -738,3 +738,23 @@
         }
         else -> emptyList()
     }
+
+/**
+ * Creates a temporary, mutable [TextFieldBuffer] representing the current state of this
+ * [TextFieldState].
+ *
+ * Use a [TextFieldBuffer] to:
+ * * Apply transformations for testing purposes
+ * * Preview how the TextField would render with a specific [OutputTransformation]
+ *
+ * This is similar to calling [TextFieldState.edit], but without committing the changes back to the
+ * [TextFieldState].
+ *
+ * **Important:** A [TextFieldBuffer] is intended for short-term use. Let the garbage collecter
+ * dispose of it when you're finished to avoid unnecessary memory usage.
+ *
+ * @sample androidx.compose.foundation.samples.TextFieldStateApplyOutputTransformation
+ */
+fun TextFieldState.toTextFieldBuffer(): TextFieldBuffer {
+    return TextFieldBuffer(value)
+}
diff --git a/compose/integration-tests/hero/macrobenchmark/src/main/java/androidx/compose/integration/hero/macrobenchmark/jetsnack/JetsnackScrollBenchmark.kt b/compose/integration-tests/hero/macrobenchmark/src/main/java/androidx/compose/integration/hero/macrobenchmark/jetsnack/JetsnackScrollBenchmark.kt
index 72aea3d..d4e9bea 100644
--- a/compose/integration-tests/hero/macrobenchmark/src/main/java/androidx/compose/integration/hero/macrobenchmark/jetsnack/JetsnackScrollBenchmark.kt
+++ b/compose/integration-tests/hero/macrobenchmark/src/main/java/androidx/compose/integration/hero/macrobenchmark/jetsnack/JetsnackScrollBenchmark.kt
@@ -18,6 +18,8 @@
 
 import android.content.Intent
 import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
 import androidx.benchmark.macro.FrameTimingMetric
 import androidx.benchmark.macro.MacrobenchmarkScope
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule
@@ -69,10 +71,11 @@
             }
         )
 
+    @OptIn(ExperimentalMetricApi::class)
     private fun benchmarkScroll(action: String, measureBlock: MacrobenchmarkScope.() -> Unit) =
         benchmarkRule.measureRepeated(
             packageName = PACKAGE_NAME,
-            metrics = listOf(FrameTimingMetric()),
+            metrics = listOf(FrameTimingMetric(), FrameTimingGfxInfoMetric()),
             compilationMode = compilationMode,
             iterations = ITERATIONS,
             measureBlock = {
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt
index 1f58898..ad51c92 100644
--- a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/ComplexNestedListsScrollBenchmark.kt
@@ -19,6 +19,8 @@
 import android.content.Intent
 import android.graphics.Point
 import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
 import androidx.benchmark.macro.FrameTimingMetric
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule
 import androidx.test.platform.app.InstrumentationRegistry
@@ -40,11 +42,12 @@
         device = UiDevice.getInstance(instrumentation)
     }
 
+    @OptIn(ExperimentalMetricApi::class)
     @Test
     fun start() {
         benchmarkRule.measureRepeated(
             packageName = PACKAGE_NAME,
-            metrics = listOf(FrameTimingMetric()),
+            metrics = listOf(FrameTimingMetric(), FrameTimingGfxInfoMetric()),
             compilationMode = CompilationMode.Full(),
             iterations = 8,
             setupBlock = {
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/DifferentTypesListScrollBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/DifferentTypesListScrollBenchmark.kt
index 7580f47..d39fb36 100644
--- a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/DifferentTypesListScrollBenchmark.kt
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/DifferentTypesListScrollBenchmark.kt
@@ -19,6 +19,8 @@
 import android.content.Intent
 import android.graphics.Point
 import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
 import androidx.benchmark.macro.FrameTimingMetric
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule
 import androidx.test.filters.LargeTest
@@ -42,11 +44,12 @@
         device = UiDevice.getInstance(instrumentation)
     }
 
+    @OptIn(ExperimentalMetricApi::class)
     @Test
     fun start() {
         benchmarkRule.measureRepeated(
             packageName = PACKAGE_NAME,
-            metrics = listOf(FrameTimingMetric()),
+            metrics = listOf(FrameTimingMetric(), FrameTimingGfxInfoMetric()),
             compilationMode = CompilationMode.Full(),
             iterations = 10,
             setupBlock = {
diff --git a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/FrameExperimentBenchmark.kt b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/FrameExperimentBenchmark.kt
index 099cff4..8cff25b 100644
--- a/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/FrameExperimentBenchmark.kt
+++ b/compose/integration-tests/macrobenchmark/src/main/java/androidx/compose/integration/macrobenchmark/FrameExperimentBenchmark.kt
@@ -18,6 +18,8 @@
 
 import android.content.Intent
 import androidx.benchmark.macro.CompilationMode
+import androidx.benchmark.macro.ExperimentalMetricApi
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
 import androidx.benchmark.macro.FrameTimingMetric
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -53,10 +55,11 @@
 
     @Test fun prefetchSomeFrames() = benchmark(FrameMode.PrefetchSomeFrames)
 
+    @OptIn(ExperimentalMetricApi::class)
     private fun benchmark(mode: FrameMode) {
         benchmarkRule.measureRepeated(
             packageName = PACKAGE_NAME,
-            metrics = listOf(FrameTimingMetric()),
+            metrics = listOf(FrameTimingMetric(), FrameTimingGfxInfoMetric()),
             compilationMode = CompilationMode.DEFAULT,
             iterations = 1,
             setupBlock = {
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
index be850bb..546b0a8 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/OutlinedTextField.kt
@@ -32,11 +32,11 @@
 import androidx.compose.foundation.text.input.InputTransformation
 import androidx.compose.foundation.text.input.KeyboardActionHandler
 import androidx.compose.foundation.text.input.OutputTransformation
-import androidx.compose.foundation.text.input.TextFieldBuffer
 import androidx.compose.foundation.text.input.TextFieldLineLimits
 import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
 import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
 import androidx.compose.foundation.text.input.TextFieldState
+import androidx.compose.foundation.text.input.toTextFieldBuffer
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -219,13 +219,10 @@
                 if (outputTransformation == null) {
                     state.text.toString()
                 } else {
-                    // TODO: use constructor to create TextFieldBuffer from TextFieldState when
-                    // available
-                    lateinit var buffer: TextFieldBuffer
-                    state.edit { buffer = this }
+                    val buffer = state.toTextFieldBuffer()
                     // after edit completes, mutations on buffer are ineffective
                     with(outputTransformation) { buffer.transformOutput() }
-                    buffer.asCharSequence().toString()
+                    buffer.toString()
                 }
 
             TextFieldDefaults.OutlinedTextFieldDecorationBox(
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
index 1064835..ce10bc3 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt
@@ -34,11 +34,11 @@
 import androidx.compose.foundation.text.input.InputTransformation
 import androidx.compose.foundation.text.input.KeyboardActionHandler
 import androidx.compose.foundation.text.input.OutputTransformation
-import androidx.compose.foundation.text.input.TextFieldBuffer
 import androidx.compose.foundation.text.input.TextFieldLineLimits
 import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
 import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
 import androidx.compose.foundation.text.input.TextFieldState
+import androidx.compose.foundation.text.input.toTextFieldBuffer
 import androidx.compose.material.TextFieldDefaults.indicatorLine
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
@@ -226,13 +226,10 @@
                 if (outputTransformation == null) {
                     state.text.toString()
                 } else {
-                    // TODO: use constructor to create TextFieldBuffer from TextFieldState when
-                    // available
-                    lateinit var buffer: TextFieldBuffer
-                    state.edit { buffer = this }
+                    val buffer = state.toTextFieldBuffer()
                     // after edit completes, mutations on buffer are ineffective
                     with(outputTransformation) { buffer.transformOutput() }
-                    buffer.asCharSequence().toString()
+                    buffer.toString()
                 }
 
             TextFieldDefaults.TextFieldDecorationBox(
diff --git a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt
index 424bd9e..8f3d4a6 100644
--- a/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt
+++ b/compose/material3/benchmark/src/androidTest/java/androidx/compose/material3/benchmark/SplitButtonBenchmark.kt
@@ -114,7 +114,7 @@
             SplitButtonType.Filled ->
                 FilledSplitButton(
                     onLeadingButtonClick = {},
-                    expanded = false,
+                    checked = false,
                     onTrailingButtonClick = { /* Do Nothing */ },
                     leadingContent = { leadingContent() },
                     trailingContent = { trailingContent() },
@@ -122,7 +122,7 @@
             SplitButtonType.Tonal ->
                 TonalSplitButton(
                     onLeadingButtonClick = {},
-                    expanded = false,
+                    checked = false,
                     onTrailingButtonClick = { /* Do Nothing */ },
                     leadingContent = { leadingContent() },
                     trailingContent = { trailingContent() },
@@ -130,7 +130,7 @@
             SplitButtonType.Elevated ->
                 ElevatedSplitButton(
                     onLeadingButtonClick = {},
-                    expanded = false,
+                    checked = false,
                     onTrailingButtonClick = { /* Do Nothing */ },
                     leadingContent = { leadingContent() },
                     trailingContent = { trailingContent() },
@@ -138,7 +138,7 @@
             SplitButtonType.Outlined ->
                 OutlinedSplitButton(
                     onLeadingButtonClick = {},
-                    expanded = false,
+                    checked = false,
                     onTrailingButtonClick = { /* Do Nothing */ },
                     leadingContent = { leadingContent() },
                     trailingContent = { trailingContent() },
diff --git a/compose/material3/material3/api/current.txt b/compose/material3/material3/api/current.txt
index a765c6e..5f949cf 100644
--- a/compose/material3/material3/api/current.txt
+++ b/compose/material3/material3/api/current.txt
@@ -2091,7 +2091,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class SplitButtonDefaults {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void AnimatedTrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize startCornerSize, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void AnimatedTrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize startCornerSize, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void LeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional boolean enabled, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method public androidx.compose.foundation.shape.CornerSize getInnerCornerSize();
@@ -2114,11 +2114,11 @@
   }
 
   public final class SplitButtonKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ElevatedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FilledSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void OutlinedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ElevatedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FilledSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void OutlinedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> leadingButton, kotlin.jvm.functions.Function0<kotlin.Unit> trailingButton, optional androidx.compose.ui.Modifier modifier, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void TonalSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void TonalSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
   }
 
   public final class SuggestionChipDefaults {
diff --git a/compose/material3/material3/api/restricted_current.txt b/compose/material3/material3/api/restricted_current.txt
index a765c6e..5f949cf 100644
--- a/compose/material3/material3/api/restricted_current.txt
+++ b/compose/material3/material3/api/restricted_current.txt
@@ -2091,7 +2091,7 @@
   }
 
   @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi public final class SplitButtonDefaults {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void AnimatedTrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize startCornerSize, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void AnimatedTrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize startCornerSize, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void LeadingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.ui.graphics.Shape shape, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.layout.PaddingValues contentPadding, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public void TrailingButton(kotlin.jvm.functions.Function0<kotlin.Unit> onClick, optional androidx.compose.ui.Modifier modifier, optional androidx.compose.ui.graphics.Shape shape, optional boolean enabled, optional androidx.compose.material3.ButtonColors colors, optional androidx.compose.material3.ButtonElevation? elevation, optional androidx.compose.foundation.BorderStroke? border, optional androidx.compose.foundation.interaction.MutableInteractionSource? interactionSource, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> content);
     method public androidx.compose.foundation.shape.CornerSize getInnerCornerSize();
@@ -2114,11 +2114,11 @@
   }
 
   public final class SplitButtonKt {
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ElevatedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FilledSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void OutlinedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void ElevatedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void FilledSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void OutlinedSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
     method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void SplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> leadingButton, kotlin.jvm.functions.Function0<kotlin.Unit> trailingButton, optional androidx.compose.ui.Modifier modifier, optional float spacing);
-    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void TonalSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean expanded, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
+    method @SuppressCompatibility @androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @androidx.compose.runtime.Composable public static void TonalSplitButton(kotlin.jvm.functions.Function0<kotlin.Unit> onLeadingButtonClick, kotlin.jvm.functions.Function0<kotlin.Unit> onTrailingButtonClick, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> leadingContent, kotlin.jvm.functions.Function1<? super androidx.compose.foundation.layout.RowScope,kotlin.Unit> trailingContent, boolean checked, optional androidx.compose.ui.Modifier modifier, optional boolean enabled, optional androidx.compose.foundation.shape.CornerSize innerCornerSize, optional float spacing);
   }
 
   public final class SuggestionChipDefaults {
diff --git a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt
index 2bf7fe3..13c3a29 100644
--- a/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt
+++ b/compose/material3/material3/samples/src/main/java/androidx/compose/material3/samples/SplitButtonSamples.kt
@@ -50,7 +50,7 @@
 @Composable
 @Preview
 fun SplitButtonSample() {
-    var expanded by remember { mutableStateOf(false) }
+    var checked by remember { mutableStateOf(false) }
 
     SplitButton(
         leadingButton = {
@@ -68,17 +68,17 @@
         },
         trailingButton = {
             SplitButtonDefaults.AnimatedTrailingButton(
-                onClick = { expanded = !expanded },
-                expanded = expanded,
+                onClick = { checked = !checked },
+                checked = checked,
                 modifier =
                     Modifier.semantics {
-                        stateDescription = if (expanded) "Expanded" else "Collapsed"
+                        stateDescription = if (checked) "Checked" else "Unchecked"
                         contentDescription = "Toggle Button"
                     },
             ) {
                 val rotation: Float by
                     animateFloatAsState(
-                        targetValue = if (expanded) 180f else 0f,
+                        targetValue = if (checked) 180f else 0f,
                         label = "Trailing Icon Rotation"
                     )
                 Icon(
@@ -99,12 +99,12 @@
 @Composable
 @Preview
 fun FilledSplitButtonSample() {
-    var expanded by remember { mutableStateOf(false) }
+    var checked by remember { mutableStateOf(false) }
 
     FilledSplitButton(
         onLeadingButtonClick = {},
-        expanded = expanded,
-        onTrailingButtonClick = { expanded = !expanded },
+        checked = checked,
+        onTrailingButtonClick = { checked = !checked },
         leadingContent = {
             Icon(
                 Icons.Outlined.Edit,
@@ -117,7 +117,7 @@
         trailingContent = {
             val rotation: Float by
                 animateFloatAsState(
-                    targetValue = if (expanded) 180f else 0f,
+                    targetValue = if (checked) 180f else 0f,
                     label = "Trailing Icon Rotation"
                 )
             Icon(
@@ -137,12 +137,12 @@
 @Composable
 @Preview
 fun TonalSplitButtonSample() {
-    var expanded by remember { mutableStateOf(false) }
+    var checked by remember { mutableStateOf(false) }
 
     TonalSplitButton(
         onLeadingButtonClick = {},
-        expanded = expanded,
-        onTrailingButtonClick = { expanded = !expanded },
+        checked = checked,
+        onTrailingButtonClick = { checked = !checked },
         leadingContent = {
             Icon(
                 Icons.Outlined.Edit,
@@ -155,7 +155,7 @@
         trailingContent = {
             val rotation: Float by
                 animateFloatAsState(
-                    targetValue = if (expanded) 180f else 0f,
+                    targetValue = if (checked) 180f else 0f,
                     label = "Trailing Icon Rotation"
                 )
             Icon(
@@ -175,12 +175,12 @@
 @Composable
 @Preview
 fun ElevatedSplitButtonSample() {
-    var expanded by remember { mutableStateOf(false) }
+    var checked by remember { mutableStateOf(false) }
 
     ElevatedSplitButton(
         onLeadingButtonClick = {},
-        expanded = expanded,
-        onTrailingButtonClick = { expanded = !expanded },
+        checked = checked,
+        onTrailingButtonClick = { checked = !checked },
         leadingContent = {
             Icon(
                 Icons.Outlined.Edit,
@@ -193,7 +193,7 @@
         trailingContent = {
             val rotation: Float by
                 animateFloatAsState(
-                    targetValue = if (expanded) 180f else 0f,
+                    targetValue = if (checked) 180f else 0f,
                     label = "Trailing Icon Rotation"
                 )
             Icon(
@@ -213,12 +213,12 @@
 @Composable
 @Preview
 fun OutlinedSplitButtonSample() {
-    var expanded by remember { mutableStateOf(false) }
+    var checked by remember { mutableStateOf(false) }
 
     OutlinedSplitButton(
         onLeadingButtonClick = {},
-        expanded = expanded,
-        onTrailingButtonClick = { expanded = !expanded },
+        checked = checked,
+        onTrailingButtonClick = { checked = !checked },
         leadingContent = {
             Icon(
                 Icons.Outlined.Edit,
@@ -231,7 +231,7 @@
         trailingContent = {
             val rotation: Float by
                 animateFloatAsState(
-                    targetValue = if (expanded) 180f else 0f,
+                    targetValue = if (checked) 180f else 0f,
                     label = "Trailing Icon Rotation"
                 )
             Icon(
@@ -251,7 +251,7 @@
 @Composable
 @Preview
 fun SplitButtonWithTextSample() {
-    var expanded by remember { mutableStateOf(false) }
+    var checked by remember { mutableStateOf(false) }
 
     SplitButton(
         leadingButton = {
@@ -263,12 +263,12 @@
         },
         trailingButton = {
             SplitButtonDefaults.AnimatedTrailingButton(
-                onClick = { expanded = !expanded },
-                expanded = expanded,
+                onClick = { checked = !checked },
+                checked = checked,
             ) {
                 val rotation: Float by
                     animateFloatAsState(
-                        targetValue = if (expanded) 180f else 0f,
+                        targetValue = if (checked) 180f else 0f,
                         label = "Trailing Icon Rotation"
                     )
                 Icon(
@@ -289,7 +289,7 @@
 @Composable
 @Preview
 fun SplitButtonWithIconSample() {
-    var expanded by remember { mutableStateOf(false) }
+    var checked by remember { mutableStateOf(false) }
 
     SplitButton(
         leadingButton = {
@@ -305,12 +305,12 @@
         },
         trailingButton = {
             SplitButtonDefaults.AnimatedTrailingButton(
-                onClick = { expanded = !expanded },
-                expanded = expanded,
+                onClick = { checked = !checked },
+                checked = checked,
             ) {
                 val rotation: Float by
                     animateFloatAsState(
-                        targetValue = if (expanded) 180f else 0f,
+                        targetValue = if (checked) 180f else 0f,
                         label = "Trailing Icon Rotation"
                     )
                 Icon(
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt
index 5b87c0c..343bbe2 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonScreenshotTest.kt
@@ -74,7 +74,7 @@
                     trailingButton = {
                         SplitButtonDefaults.AnimatedTrailingButton(
                             onClick = {},
-                            expanded = false,
+                            checked = false,
                         ) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
@@ -97,7 +97,7 @@
                 FilledSplitButton(
                     onLeadingButtonClick = {},
                     onTrailingButtonClick = {},
-                    expanded = false,
+                    checked = false,
                     leadingContent = {
                         Icon(
                             Icons.Outlined.Edit,
@@ -127,12 +127,12 @@
     }
 
     @Test
-    fun filledSplitButtonExpanded() {
+    fun filledSplitButtonChecked() {
         rule.setMaterialContent(scheme.colorScheme) {
             Box(wrap.testTag(wrapperTestTag)) {
                 FilledSplitButton(
                     onLeadingButtonClick = {},
-                    expanded = true,
+                    checked = true,
                     onTrailingButtonClick = {},
                     leadingContent = {
                         Icon(
@@ -161,7 +161,7 @@
             }
         }
 
-        assertAgainstGolden("filledSplitButton_expanded_${scheme.name}")
+        assertAgainstGolden("filledSplitButton_checked_${scheme.name}")
     }
 
     @Test
@@ -171,7 +171,7 @@
                 TonalSplitButton(
                     onLeadingButtonClick = {},
                     onTrailingButtonClick = {},
-                    expanded = false,
+                    checked = false,
                     leadingContent = {
                         Icon(
                             Icons.Outlined.Edit,
@@ -204,7 +204,7 @@
                 ElevatedSplitButton(
                     onLeadingButtonClick = {},
                     onTrailingButtonClick = {},
-                    expanded = false,
+                    checked = false,
                     leadingContent = {
                         Icon(
                             Icons.Outlined.Edit,
@@ -237,7 +237,7 @@
                 OutlinedSplitButton(
                     onLeadingButtonClick = {},
                     onTrailingButtonClick = {},
-                    expanded = false,
+                    checked = false,
                     leadingContent = {
                         Icon(
                             Icons.Outlined.Edit,
@@ -282,7 +282,7 @@
                     trailingButton = {
                         SplitButtonDefaults.AnimatedTrailingButton(
                             onClick = {},
-                            expanded = false,
+                            checked = false,
                         ) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
@@ -311,7 +311,7 @@
                         }
                     },
                     trailingButton = {
-                        SplitButtonDefaults.AnimatedTrailingButton(onClick = {}, expanded = false) {
+                        SplitButtonDefaults.AnimatedTrailingButton(onClick = {}, checked = false) {
                             Icon(
                                 Icons.Outlined.KeyboardArrowDown,
                                 contentDescription = "Localized description",
diff --git a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonTest.kt b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonTest.kt
index f394a26..021104b 100644
--- a/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonTest.kt
+++ b/compose/material3/material3/src/androidInstrumentedTest/kotlin/androidx/compose/material3/SplitButtonTest.kt
@@ -160,7 +160,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             FilledSplitButton(
                 onLeadingButtonClick = {},
-                expanded = false,
+                checked = false,
                 onTrailingButtonClick = {},
                 leadingContent = {
                     Icon(
@@ -187,7 +187,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             TonalSplitButton(
                 onLeadingButtonClick = {},
-                expanded = false,
+                checked = false,
                 onTrailingButtonClick = {},
                 leadingContent = {
                     Icon(
@@ -214,7 +214,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             ElevatedSplitButton(
                 onLeadingButtonClick = {},
-                expanded = false,
+                checked = false,
                 onTrailingButtonClick = {},
                 leadingContent = {
                     Icon(
@@ -241,7 +241,7 @@
         rule.setMaterialContent(lightColorScheme()) {
             OutlinedSplitButton(
                 onLeadingButtonClick = {},
-                expanded = false,
+                checked = false,
                 onTrailingButtonClick = {},
                 leadingContent = {
                     Icon(
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt
index 27367eb..16a7573 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/SplitButton.kt
@@ -122,8 +122,8 @@
  *   provided internally to offer the standard design and style for a [FilledSplitButton].
  * @param trailingContent the content to be placed inside the trailing button. A container is
  *   provided internally to ensure the standard design and style of a [FilledSplitButton]. The
- *   container corner radius morphs to `full` when the [expanded] state changes to `true`.
- * @param expanded indicates if the trailing button is toggled. This can be used to indicate a new
+ *   container corner radius morphs to `full` when the [checked] state changes to `true`.
+ * @param checked indicates if the trailing button is toggled. This can be used to indicate a new
  *   state that's a result of [onTrailingButtonClick]. For example, a drop down menu or pop up.
  * @param modifier the [Modifier] to be applied to this this split button.
  * @param enabled controls the enabled state of the split button. When `false`, this component will
@@ -145,7 +145,7 @@
     onTrailingButtonClick: () -> Unit,
     leadingContent: @Composable RowScope.() -> Unit,
     trailingContent: @Composable RowScope.() -> Unit,
-    expanded: Boolean,
+    checked: Boolean,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     innerCornerSize: CornerSize = SplitButtonDefaults.InnerCornerSize,
@@ -167,7 +167,7 @@
                 onClick = onTrailingButtonClick,
                 modifier = Modifier,
                 enabled = enabled,
-                expanded = expanded,
+                checked = checked,
                 startCornerSize = innerCornerSize,
                 content = trailingContent,
             )
@@ -194,8 +194,8 @@
  *   provided internally to offer the standard design and style for a [TonalSplitButton].
  * @param trailingContent the content to be placed inside the trailing button. A container is
  *   provided internally to ensure the standard design and style of a [TonalSplitButton]. The
- *   container corner radius morphs to full when the [expanded] state changes to `true`.
- * @param expanded indicates if the trailing button is toggled. This can be used to indicate a new
+ *   container corner radius morphs to full when the [checked] state changes to `true`.
+ * @param checked indicates if the trailing button is toggled. This can be used to indicate a new
  *   state that's a result of [onTrailingButtonClick]. For example, a drop down menu or pop up.
  * @param modifier the [Modifier] to be applied to this split button.
  * @param enabled controls the enabled state of the split button. When `false`, this component will
@@ -217,7 +217,7 @@
     onTrailingButtonClick: () -> Unit,
     leadingContent: @Composable RowScope.() -> Unit,
     trailingContent: @Composable RowScope.() -> Unit,
-    expanded: Boolean,
+    checked: Boolean,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     innerCornerSize: CornerSize = SplitButtonDefaults.InnerCornerSize,
@@ -238,7 +238,7 @@
                 modifier = Modifier,
                 enabled = enabled,
                 startCornerSize = innerCornerSize,
-                expanded = expanded,
+                checked = checked,
                 content = trailingContent,
             )
         },
@@ -267,8 +267,8 @@
  *   provided internally to offer the standard design and style for a [ElevatedSplitButton].
  * @param trailingContent the content to be placed inside the trailing button. A container is
  *   provided internally to ensure the standard design and style of a [ElevatedSplitButton]. The
- *   container corner radius morphs to full when the [expanded] state changes to `true`.
- * @param expanded indicates if the trailing button is toggled. This can be used to indicate a new
+ *   container corner radius morphs to full when the [checked] state changes to `true`.
+ * @param checked indicates if the trailing button is toggled. This can be used to indicate a new
  *   state that's a result of [onTrailingButtonClick]. For example, a drop down menu or pop up.
  * @param modifier the [Modifier] to be applied to this split button.
  * @param enabled controls the enabled state of the split button. When `false`, this component will
@@ -290,7 +290,7 @@
     onTrailingButtonClick: () -> Unit,
     leadingContent: @Composable RowScope.() -> Unit,
     trailingContent: @Composable RowScope.() -> Unit,
-    expanded: Boolean,
+    checked: Boolean,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     innerCornerSize: CornerSize = SplitButtonDefaults.InnerCornerSize,
@@ -311,7 +311,7 @@
                 modifier = Modifier,
                 enabled = enabled,
                 startCornerSize = innerCornerSize,
-                expanded = expanded,
+                checked = checked,
                 content = trailingContent
             )
         },
@@ -340,8 +340,8 @@
  *   provided internally to offer the standard design and style for a [OutlinedSplitButton].
  * @param trailingContent the content to be placed inside the trailing button. A container is
  *   provided internally to ensure the standard design and style of a [OutlinedSplitButton]. The
- *   container corner radius morphs to full when the [expanded] state changes to `true`.
- * @param expanded indicates if the trailing button is toggled. This can be used to indicate a new
+ *   container corner radius morphs to full when the [checked] state changes to `true`.
+ * @param checked indicates if the trailing button is toggled. This can be used to indicate a new
  *   state that's a result of [onTrailingButtonClick]. For example, a drop down menu or pop up.
  * @param modifier the [Modifier] to be applied to this split button.
  * @param enabled controls the enabled state of the split button. When `false`, this component will
@@ -363,7 +363,7 @@
     onTrailingButtonClick: () -> Unit,
     leadingContent: @Composable RowScope.() -> Unit,
     trailingContent: @Composable RowScope.() -> Unit,
-    expanded: Boolean,
+    checked: Boolean,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     innerCornerSize: CornerSize = SplitButtonDefaults.InnerCornerSize,
@@ -384,7 +384,7 @@
                 modifier = Modifier,
                 enabled = enabled,
                 startCornerSize = innerCornerSize,
-                expanded = expanded,
+                checked = checked,
                 content = trailingContent
             )
         },
@@ -500,7 +500,7 @@
     /** Default minimum width of the [TrailingButton]. */
     private val TrailingButtonMinWidth = LeadingButtonMinWidth
 
-    /** Trailng button state layer alpha when in expanded state */
+    /** Trailng button state layer alpha when in checked state */
     private const val TrailingButtonStateLayerAlpha = StateTokens.PressedStateLayerOpacity
 
     /**
@@ -663,7 +663,7 @@
 
     /**
      * Create a animated `trailing` button that has the same visual as a Filled[Button]. When
-     * [expanded] is updated from `false` to `true`, the buttons corners will morph to `full`.
+     * [checked] is updated from `false` to `true`, the buttons corners will morph to `full`.
      *
      * To create a `tonal`, `outlined`, or `elevated` version, the default value of [Button] params
      * can be passed in. For example, [ElevatedButton].
@@ -671,7 +671,7 @@
      * The default text style for internal [Text] components will be set to [Typography.labelLarge].
      *
      * @param onClick called when the button is clicked
-     * @param expanded indicates whether the button is toggled to a `expanded` state. This will
+     * @param checked indicates whether the button is toggled to a `checked` state. This will
      *   trigger the corner morphing animation to reflect the updated state.
      * @param modifier the [Modifier] to be applied to this button.
      * @param enabled controls the enabled state of the split button. When `false`, this component
@@ -696,7 +696,7 @@
     @ExperimentalMaterial3ExpressiveApi
     fun AnimatedTrailingButton(
         onClick: () -> Unit,
-        expanded: Boolean,
+        checked: Boolean,
         modifier: Modifier = Modifier,
         enabled: Boolean = true,
         startCornerSize: CornerSize = InnerCornerSize,
@@ -707,7 +707,7 @@
         interactionSource: MutableInteractionSource? = null,
         content: @Composable RowScope.() -> Unit
     ) {
-        val cornerMorphProgress: Float by animateFloatAsState(if (expanded) 1f else 0f)
+        val cornerMorphProgress: Float by animateFloatAsState(if (checked) 1f else 0f)
         @Suppress("NAME_SHADOWING")
         val interactionSource = interactionSource ?: remember { MutableInteractionSource() }
         val density = LocalDensity.current
@@ -718,7 +718,7 @@
             modifier =
                 modifier.drawWithContent {
                     drawContent()
-                    if (expanded) {
+                    if (checked) {
                         drawOutline(
                             outline = shape.createOutline(size, layoutDirection, density),
                             color = colors.contentColor,
@@ -736,7 +736,7 @@
             Row(
                 modifier =
                     modifier.opticalCentering(
-                        trailingButtonShape(if (expanded) OuterCornerSize else startCornerSize),
+                        trailingButtonShape(if (checked) OuterCornerSize else startCornerSize),
                         contentPadding
                     ),
                 content = content
@@ -814,7 +814,7 @@
 @Composable
 private fun TonalTrailingButton(
     onClick: () -> Unit,
-    expanded: Boolean,
+    checked: Boolean,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     startCornerSize: CornerSize,
@@ -825,7 +825,7 @@
         onClick = onClick,
         enabled = enabled,
         startCornerSize = startCornerSize,
-        expanded = expanded,
+        checked = checked,
         colors = ButtonDefaults.filledTonalButtonColors(),
         elevation = ButtonDefaults.filledTonalButtonElevation(),
         border = null,
@@ -858,7 +858,7 @@
 @Composable
 private fun OutlinedTrailingButton(
     onClick: () -> Unit,
-    expanded: Boolean,
+    checked: Boolean,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     startCornerSize: CornerSize,
@@ -869,7 +869,7 @@
         onClick = onClick,
         enabled = enabled,
         startCornerSize = startCornerSize,
-        expanded = expanded,
+        checked = checked,
         colors = ButtonDefaults.outlinedButtonColors(),
         elevation = null,
         border = ButtonDefaults.outlinedButtonBorder(enabled),
@@ -902,7 +902,7 @@
 @Composable
 private fun ElevatedTrailingButton(
     onClick: () -> Unit,
-    expanded: Boolean,
+    checked: Boolean,
     modifier: Modifier = Modifier,
     enabled: Boolean = true,
     startCornerSize: CornerSize,
@@ -913,7 +913,7 @@
         onClick = onClick,
         enabled = enabled,
         startCornerSize = startCornerSize,
-        expanded = expanded,
+        checked = checked,
         colors = ButtonDefaults.elevatedButtonColors(),
         elevation = ButtonDefaults.elevatedButtonElevation(),
         border = null,
diff --git a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
index 2e9ff2c..66dba58 100644
--- a/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
+++ b/compose/material3/material3/src/commonMain/kotlin/androidx/compose/material3/TextFieldDefaults.kt
@@ -27,12 +27,12 @@
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.text.BasicTextField
 import androidx.compose.foundation.text.input.OutputTransformation
-import androidx.compose.foundation.text.input.TextFieldBuffer
 import androidx.compose.foundation.text.input.TextFieldDecorator
 import androidx.compose.foundation.text.input.TextFieldLineLimits
 import androidx.compose.foundation.text.input.TextFieldLineLimits.MultiLine
 import androidx.compose.foundation.text.input.TextFieldLineLimits.SingleLine
 import androidx.compose.foundation.text.input.TextFieldState
+import androidx.compose.foundation.text.input.toTextFieldBuffer
 import androidx.compose.foundation.text.selection.LocalTextSelectionColors
 import androidx.compose.foundation.text.selection.TextSelectionColors
 import androidx.compose.material3.internal.CommonDecorationBox
@@ -181,13 +181,10 @@
         val visualText =
             if (outputTransformation == null) state.text
             else {
-                // TODO: use constructor to create TextFieldBuffer from TextFieldState when
-                // available
-                lateinit var buffer: TextFieldBuffer
-                state.edit { buffer = this }
+                val buffer = state.toTextFieldBuffer()
                 // after edit completes, mutations on buffer are ineffective
                 with(outputTransformation) { buffer.transformOutput() }
-                buffer.asCharSequence()
+                buffer.toString()
             }
 
         CommonDecorationBox(
@@ -996,13 +993,10 @@
         val visualText =
             if (outputTransformation == null) state.text
             else {
-                // TODO: use constructor to create TextFieldBuffer from TextFieldState when
-                // available
-                lateinit var buffer: TextFieldBuffer
-                state.edit { buffer = this }
+                val buffer = state.toTextFieldBuffer()
                 // after edit completes, mutations on buffer are ineffective
                 with(outputTransformation) { buffer.transformOutput() }
-                buffer.asCharSequence()
+                buffer.toString()
             }
 
         CommonDecorationBox(
diff --git a/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewScrollFrameRateTest.java b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewScrollFrameRateTest.java
new file mode 100644
index 0000000..ee110d6
--- /dev/null
+++ b/core/core/src/androidTest/java/androidx/core/widget/NestedScrollViewScrollFrameRateTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 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 androidx.core.widget;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.greaterThan;
+
+import android.content.Context;
+import android.os.Build;
+import android.support.v4.BaseInstrumentationTestCase;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.core.test.R;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SdkSuppress;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
+public class NestedScrollViewScrollFrameRateTest extends
+        BaseInstrumentationTestCase<TestContentViewActivity>  {
+
+    private static final int CHILD_HEIGHT = 800;
+    private static final int NSV_HEIGHT = 400;
+    private static final int WIDTH = 400;
+    private static final int TOTAL_SCROLL_DISTANCE = CHILD_HEIGHT - NSV_HEIGHT;
+
+    private NestedScrollView mNestedScrollView;
+    private View mChild;
+
+    public NestedScrollViewScrollFrameRateTest() {
+        super(TestContentViewActivity.class);
+    }
+
+    @Before
+    public void setup() {
+        Context context = mActivityTestRule.getActivity();
+
+        mChild = new View(context);
+        mChild.setMinimumWidth(WIDTH);
+        mChild.setMinimumHeight(CHILD_HEIGHT);
+        mChild.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, CHILD_HEIGHT));
+
+        mNestedScrollView = new NestedScrollView(context);
+        mNestedScrollView.setLayoutParams(new ViewGroup.LayoutParams(WIDTH, NSV_HEIGHT));
+        mNestedScrollView.addView(mChild);
+    }
+
+    @Test
+    public void smoothScrollByFrameRateBoost() throws Throwable {
+        setChildMargins(20, 30);
+        attachToActivity();
+
+        final int scrollDistance = TOTAL_SCROLL_DISTANCE + 20 + 30;
+        mActivityTestRule.runOnUiThread(() -> {
+            mNestedScrollView.setOnScrollChangeListener(
+                    (NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY,
+                            oldScrollX, oldScrollY) -> {
+                        if (scrollY >= oldScrollX + 10) {
+                            assertThat(mNestedScrollView.getFrameContentVelocity(),
+                                    greaterThan(0f));
+                        }
+                    });
+            mNestedScrollView.smoothScrollBy(0, scrollDistance);
+        });
+    }
+
+    @Test
+    public void flingFrameRateBoost() throws Throwable {
+        setChildMargins(20, 30);
+        attachToActivity();
+
+        mActivityTestRule.runOnUiThread(() -> {
+            mNestedScrollView.setOnScrollChangeListener(
+                    (NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY,
+                            oldScrollX, oldScrollY) -> {
+                        if (scrollY >= oldScrollX + 10) {
+                            assertThat(mNestedScrollView.getFrameContentVelocity(),
+                                    greaterThan(0f));
+                        }
+                    });
+            mNestedScrollView.fling(1000);
+        });
+    }
+
+    @SuppressWarnings("SameParameterValue")
+    private void setChildMargins(int top, int bottom) {
+        ViewGroup.LayoutParams currentLayoutParams = mChild.getLayoutParams();
+        NestedScrollView.LayoutParams childLayoutParams = new NestedScrollView.LayoutParams(
+                currentLayoutParams.width, currentLayoutParams.height);
+        childLayoutParams.topMargin = top;
+        childLayoutParams.bottomMargin = bottom;
+        mChild.setLayoutParams(childLayoutParams);
+    }
+
+    private void attachToActivity() throws Throwable {
+        final TestContentView testContentView =
+                mActivityTestRule.getActivity().findViewById(R.id.testContentView);
+        testContentView.expectLayouts(1);
+        mActivityTestRule.runOnUiThread(() -> testContentView.addView(mNestedScrollView));
+        testContentView.awaitLayouts(2);
+    }
+}
diff --git a/core/core/src/main/java/androidx/core/widget/NestedScrollView.java b/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
index c7dd035..cbe5e8c 100644
--- a/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
+++ b/core/core/src/main/java/androidx/core/widget/NestedScrollView.java
@@ -54,6 +54,7 @@
 import androidx.annotation.RestrictTo;
 import androidx.annotation.VisibleForTesting;
 import androidx.core.R;
+import androidx.core.os.BuildCompat;
 import androidx.core.view.AccessibilityDelegateCompat;
 import androidx.core.view.DifferentialMotionFlingController;
 import androidx.core.view.DifferentialMotionFlingTarget;
@@ -1960,6 +1961,11 @@
 
         final int range = getScrollRange();
 
+        if (BuildCompat.isAtLeastV()) {
+            Api35Impl.setFrameContentVelocity(NestedScrollView.this,
+                    Math.abs(mScroller.getCurrVelocity()));
+        }
+
         if (unconsumed != 0) {
             // Internal Scroll
             final int oldScrollY = getScrollY();
@@ -2316,6 +2322,10 @@
                     Integer.MIN_VALUE, Integer.MAX_VALUE, // y
                     0, 0); // overscroll
             runAnimatedScroll(true);
+            if (BuildCompat.isAtLeastV()) {
+                Api35Impl.setFrameContentVelocity(NestedScrollView.this,
+                        Math.abs(mScroller.getCurrVelocity()));
+            }
         }
     }
 
@@ -2603,4 +2613,15 @@
             return viewGroup.getClipToPadding();
         }
     }
+
+    @RequiresApi(35)
+    private static final class Api35Impl {
+        public static void setFrameContentVelocity(View view, float velocity) {
+            try {
+                view.setFrameContentVelocity(velocity);
+            } catch (LinkageError e) {
+                // The setFrameContentVelocity method is unavailable on this device.
+            }
+        }
+    }
 }
diff --git a/development/build_log_simplifier/messages.ignore b/development/build_log_simplifier/messages.ignore
index f3f71c3..619c5f9 100644
--- a/development/build_log_simplifier/messages.ignore
+++ b/development/build_log_simplifier/messages.ignore
@@ -328,3 +328,8 @@
 # https://youtrack.jetbrains.com/issue/KT-69929
 e: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0\. The daemon process output:
     1\. Kotlin compile daemon is ready
+# b/359449191
+> Transform sdk\-interface\-descriptors\.jar \(project :privacysandbox:tools:integration\-tests:testsdk\-asb\) with ExtractCompileSdkShimTransform
+WARN: Attempt to load key 'java\.correct\.class\.type\.by\.place\.resolve\.scope' for not yet loaded registry
+warning: unable to find kotlin\-stdlib\.jar in the Kotlin home directory\. Pass either '\-no\-stdlib' to prevent adding it to the classpath, or the correct '\-kotlin\-home'
+warning: unable to find kotlin\-script\-runtime\.jar in the Kotlin home directory\. Pass either '\-no\-stdlib' to prevent adding it to the classpath, or the correct '\-kotlin\-home'
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyActivity.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyActivity.kt
index b61fa50..e85e3a4 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyActivity.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyActivity.kt
@@ -32,7 +32,11 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         window.setBackgroundDrawable(ColorDrawable(Color.WHITE))
         super.onCreate(savedInstanceState)
-        mLowLatencyCanvasView = LowLatencyCanvasView(this).apply { setContentView(this) }
+        mLowLatencyCanvasView = LowLatencyCanvasView(this)
+    }
+
+    fun attachLowLatencyView() {
+        setContentView(mLowLatencyCanvasView)
     }
 
     fun setOnDestroyCallback(callback: () -> Unit) {
diff --git a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyCanvasViewTest.kt b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyCanvasViewTest.kt
index 4378cdf..f046ada 100644
--- a/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyCanvasViewTest.kt
+++ b/graphics/graphics-core/src/androidTest/java/androidx/graphics/lowlatency/LowLatencyCanvasViewTest.kt
@@ -40,8 +40,6 @@
 @SmallTest
 class LowLatencyCanvasViewTest {
 
-    private val executor = Executors.newSingleThreadExecutor()
-
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
     fun testFrontBufferRender() {
@@ -65,9 +63,7 @@
                     }
                 },
             scenarioCallback = { scenario ->
-                scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
-                    it.getLowLatencyCanvasView().renderFrontBufferedLayer()
-                }
+                scenario.onActivity { it.getLowLatencyCanvasView().renderFrontBufferedLayer() }
                 assertTrue(frontBufferRenderLatch.await(3000, TimeUnit.MILLISECONDS))
             },
             validateBitmap = { bitmap, left, top, right, bottom ->
@@ -79,23 +75,21 @@
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
     @Test
     fun testRedrawScene() {
+        val redrawSceneLatch = CountDownLatch(1)
         lowLatencyViewTest(
             renderCallbacks =
                 object : LowLatencyCanvasView.Callback {
                     override fun onRedrawRequested(canvas: Canvas, width: Int, height: Int) {
                         canvas.drawColor(Color.RED)
+                        redrawSceneLatch.countDown()
                     }
 
                     override fun onDrawFrontBufferedLayer(canvas: Canvas, width: Int, height: Int) {
                         // NO-OP
                     }
                 },
-            scenarioCallback = { scenario ->
-                val drawLatch = CountDownLatch(1)
-                scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
-                    it.getLowLatencyCanvasView().post { drawLatch.countDown() }
-                }
-                drawLatch.await(3000, TimeUnit.MILLISECONDS)
+            scenarioCallback = { _ ->
+                assertTrue(redrawSceneLatch.await(3000, TimeUnit.MILLISECONDS))
             },
             validateBitmap = { bitmap, left, top, right, bottom ->
                 Color.RED == bitmap.getPixel(left + (right - left) / 2, top + (bottom - top) / 2)
@@ -118,12 +112,15 @@
                     }
                 },
             scenarioCallback = { scenario ->
+                val resumeLatch = CountDownLatch(1)
                 val drawLatch = CountDownLatch(1)
                 scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
                     val view = it.getLowLatencyCanvasView()
                     view.clear()
                     view.post { drawLatch.countDown() }
+                    resumeLatch.countDown()
                 }
+                assertTrue(resumeLatch.await(3000, TimeUnit.MILLISECONDS))
                 assertTrue(drawLatch.await(3000, TimeUnit.MILLISECONDS))
             },
             validateBitmap = { bitmap, left, top, right, bottom ->
@@ -137,11 +134,13 @@
     fun testCancel() {
         val cancelLatch = CountDownLatch(1)
         val frontBufferRenderLatch = CountDownLatch(1)
+        val redrawLatch = CountDownLatch(1)
         lowLatencyViewTest(
             renderCallbacks =
                 object : LowLatencyCanvasView.Callback {
                     override fun onRedrawRequested(canvas: Canvas, width: Int, height: Int) {
                         canvas.drawColor(Color.RED)
+                        redrawLatch.countDown()
                     }
 
                     override fun onDrawFrontBufferedLayer(canvas: Canvas, width: Int, height: Int) {
@@ -150,15 +149,16 @@
                     }
                 },
             scenarioCallback = { scenario ->
-                val renderLatch = CountDownLatch(1)
                 var lowLatencyView: LowLatencyCanvasView? = null
-                scenario.moveToState(Lifecycle.State.RESUMED).onActivity {
-                    lowLatencyView = it.getLowLatencyCanvasView()
-                    lowLatencyView!!.post { renderLatch.countDown() }
-                }
-                assertTrue(renderLatch.await(3000, TimeUnit.MILLISECONDS))
+                scenario.onActivity { lowLatencyView = it.getLowLatencyCanvasView() }
+                assertTrue(redrawLatch.await(3000, TimeUnit.MILLISECONDS))
 
-                lowLatencyView!!.execute { cancelLatch.await() }
+                val executeLatch = CountDownLatch(1)
+                lowLatencyView!!.execute {
+                    executeLatch.countDown()
+                    cancelLatch.await()
+                }
+                assertTrue(executeLatch.await(3000, TimeUnit.MILLISECONDS))
 
                 repeat(3) { lowLatencyView!!.renderFrontBufferedLayer() }
 
@@ -215,6 +215,7 @@
         validateBitmap: (Bitmap, Int, Int, Int, Int) -> Boolean
     ) {
         val renderLatch = CountDownLatch(1)
+        val executor = Executors.newSingleThreadExecutor()
         val callbacks =
             object : LowLatencyCanvasView.Callback {
                 override fun onRedrawRequested(canvas: Canvas, width: Int, height: Int) {
@@ -247,16 +248,21 @@
                     }
                 }
             }
+        val createdLatch = CountDownLatch(1)
         val destroyLatch = CountDownLatch(1)
         var lowLatencyView: LowLatencyCanvasView? = null
         val scenario =
-            ActivityScenario.launch(LowLatencyActivity::class.java)
-                .moveToState(Lifecycle.State.CREATED)
-                .onActivity { activity ->
-                    with(activity) { setOnDestroyCallback { destroyLatch.countDown() } }
+            ActivityScenario.launch(LowLatencyActivity::class.java).onActivity { activity ->
+                with(activity) {
+                    setOnDestroyCallback { destroyLatch.countDown() }
                     lowLatencyView =
                         activity.getLowLatencyCanvasView().apply { setRenderCallback(callbacks) }
+                    attachLowLatencyView()
                 }
+
+                createdLatch.countDown()
+            }
+        assertTrue(createdLatch.await(3000, TimeUnit.MILLISECONDS))
         scenarioCallback(scenario)
 
         val coords = IntArray(2)
@@ -279,6 +285,7 @@
         } finally {
             scenario.moveToState(Lifecycle.State.DESTROYED)
             assertTrue(destroyLatch.await(3000, TimeUnit.MILLISECONDS))
+            executor.shutdownNow()
         }
     }
 }
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRendererV29.kt b/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRendererV29.kt
index b13b6da..97e69a5 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRendererV29.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/CanvasBufferedRendererV29.kt
@@ -37,6 +37,7 @@
 import androidx.hardware.SyncFenceCompat
 import androidx.hardware.SyncFenceV33
 import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicBoolean
 import java.util.concurrent.locks.ReentrantLock
 import kotlin.concurrent.withLock
 
@@ -44,26 +45,44 @@
 internal class CanvasBufferedRendererV29(
     private val mWidth: Int,
     private val mHeight: Int,
-    format: Int,
-    usage: Long,
+    private val mFormat: Int,
+    private val mUsage: Long,
     private val mMaxBuffers: Int,
-    preservationStrategy: Int,
+    private val mPreservationConfig: Int,
 ) : CanvasBufferedRenderer.Impl {
 
-    private val mPreservedRenderStrategy = createPreservationStrategy(preservationStrategy)
+    private var mPreservedRenderStrategy: PreservedRenderStrategy? = null
 
-    private val mImageReader =
+    private var mImageReader: ImageReader? = null
+
+    private var mHardwareRenderer: HardwareRenderer? = null
+
+    private fun createImageReader(preserveStrategy: PreservedRenderStrategy?): ImageReader =
         ImageReader.newInstance(
             mWidth,
             mHeight,
-            format,
+            mFormat,
             // If the device does not support preserving contents when we are rendering to a single
             // buffer, use the fallback of leveraging 2 but redrawing the contents from the previous
             // frame into the next frame
-            if (mMaxBuffers == 1) mPreservedRenderStrategy.maxImages else mMaxBuffers,
-            usage
+            if (mMaxBuffers == 1 && preserveStrategy != null) {
+                preserveStrategy.maxImages
+            } else {
+                mMaxBuffers
+            },
+            mUsage
         )
 
+    private fun createHardwareRenderer(imageReader: ImageReader): HardwareRenderer =
+        HardwareRenderer().apply {
+            // HardwareRenderer will preserve contents of the buffers if the isOpaque flag is true
+            // otherwise it will clear contents across subsequent renders
+            isOpaque = true
+            setContentRoot(mRootRenderNode)
+            setSurface(imageReader.surface)
+            start()
+        }
+
     private val mRootRenderNode =
         RenderNode("rootNode").apply {
             setPosition(0, 0, mWidth, mHeight)
@@ -71,6 +90,13 @@
         }
 
     private var mContentRoot: RenderNode? = null
+    private var mLightX: Float = 0f
+    private var mLightY: Float = 0f
+    private var mLightZ: Float = 0f
+    private var mLightRadius: Float = 0f
+
+    private var mAmbientShadowAlpha: Float = 0f
+    private var mSpotShadowAlpha: Float = 0f
 
     private var mBufferTransform = BufferTransformHintResolver.UNKNOWN_TRANSFORM
     private val mTransform = Matrix()
@@ -95,16 +121,6 @@
      */
     private val mAllocatedBuffers = HashMap<HardwareBuffer, Image>()
 
-    private var mHardwareRenderer: HardwareRenderer? =
-        HardwareRenderer().apply {
-            // HardwareRenderer will preserve contents of the buffers if the isOpaque flag is true
-            // otherwise it will clear contents across subsequent renders
-            isOpaque = true
-            setContentRoot(mRootRenderNode)
-            setSurface(mImageReader.surface)
-            start()
-        }
-
     private fun closeBuffers() =
         mBufferLock.withLock {
             for (entry in mAllocatedBuffers) {
@@ -113,20 +129,24 @@
             }
             mAllocatedBuffers.clear()
             mBufferSignal.signal()
+            mImageReader?.close()
+            mImageReader = null
+            mHardwareRenderer?.let { renderer ->
+                renderer.stop()
+                renderer.destroy()
+            }
+            mHardwareRenderer = null
         }
 
+    private val mIsReleased = AtomicBoolean(false)
+
     override fun close() {
         closeBuffers()
-        mImageReader.close()
-        mHardwareRenderer?.let { renderer ->
-            renderer.stop()
-            renderer.destroy()
-        }
-        mHardwareRenderer = null
         mRootRenderNode.discardDisplayList()
+        mIsReleased.set(true)
     }
 
-    override fun isClosed(): Boolean = mHardwareRenderer == null
+    override fun isClosed(): Boolean = mIsReleased.get()
 
     override fun draw(
         request: CanvasBufferedRenderer.RenderRequest,
@@ -144,32 +164,65 @@
             recordContent(content, updateTransform(transform), request.preserveContents)
         }
 
-        val renderer = mHardwareRenderer
-        if (renderer != null && !isClosed()) {
-            with(renderer) {
-                var result = 0
-                val renderRequest =
-                    createRenderRequest().setFrameCommitCallback(executor) {
-                        acquireBuffer { buffer, fence ->
-                            executor.execute {
-                                mPreservedRenderStrategy.onRenderComplete(buffer, fence)
-                                callback.accept(
-                                    CanvasBufferedRenderer.RenderResult(
-                                        buffer,
-                                        fence,
-                                        if (isSuccess(result)) SUCCESS else ERROR_UNKNOWN
-                                    )
-                                )
-                                if (mMaxBuffers == 1) {
-                                    releaseBuffer(buffer, fence)
-                                }
-                            }
+        val lightX = mLightX
+        val lightY = mLightY
+        val lightZ = mLightZ
+        val lightRadius = mLightRadius
+        val ambientShadowAlpha = mAmbientShadowAlpha
+        val spotShadowAlpha = mSpotShadowAlpha
+        val preserveContents = request.preserveContents
+        executor.execute {
+            if (!isClosed()) {
+                mBufferLock.withLock {
+                    var preservedRenderStrategy = mPreservedRenderStrategy
+                    if (preserveContents && mMaxBuffers == 1 && preservedRenderStrategy == null) {
+                        closeBuffers()
+                        preservedRenderStrategy = createPreservationStrategy(mPreservationConfig)
+                        mPreservedRenderStrategy = preservedRenderStrategy
+                    }
+                    val renderer =
+                        obtainHardwareRenderer(obtainImageReader(preservedRenderStrategy))
+                    renderer.apply {
+                        setLightSourceAlpha(ambientShadowAlpha, spotShadowAlpha)
+                        setLightSourceGeometry(lightX, lightY, lightZ, lightRadius)
+                    }
+                    dispatchRender(executor, renderer, preservedRenderStrategy, callback)
+                }
+            }
+        }
+    }
+
+    private fun obtainImageReader(preserveStrategy: PreservedRenderStrategy?): ImageReader =
+        mImageReader ?: createImageReader(preserveStrategy).also { mImageReader = it }
+
+    private fun obtainHardwareRenderer(imageReader: ImageReader): HardwareRenderer =
+        mHardwareRenderer ?: createHardwareRenderer(imageReader).also { mHardwareRenderer = it }
+
+    private fun dispatchRender(
+        executor: Executor,
+        renderer: HardwareRenderer,
+        preservedRenderStrategy: PreservedRenderStrategy?,
+        callback: Consumer<CanvasBufferedRenderer.RenderResult>
+    ) {
+        with(renderer) {
+            var result = 0
+            val renderRequest =
+                createRenderRequest().setFrameCommitCallback(executor) {
+                    acquireBuffer { buffer, fence ->
+                        preservedRenderStrategy?.onRenderComplete(buffer, fence)
+                        callback.accept(
+                            CanvasBufferedRenderer.RenderResult(
+                                buffer,
+                                fence,
+                                if (isSuccess(result)) SUCCESS else ERROR_UNKNOWN
+                            )
+                        )
+                        if (mMaxBuffers == 1) {
+                            releaseBuffer(buffer, fence)
                         }
                     }
-                result = renderRequest.syncAndDraw()
-            }
-        } else {
-            Log.v(TAG, "mHardwareRenderer is null")
+                }
+            result = renderRequest.syncAndDraw()
         }
     }
 
@@ -197,7 +250,7 @@
     ) {
         val canvas = mRootRenderNode.beginRecording()
         if (preserveContents) {
-            mPreservedRenderStrategy.restoreContents(canvas)
+            mBufferLock.withLock { mPreservedRenderStrategy?.restoreContents(canvas) }
         } else {
             canvas.drawColor(Color.BLACK, BlendMode.CLEAR)
         }
@@ -215,7 +268,8 @@
     }
 
     override fun setLightSourceAlpha(ambientShadowAlpha: Float, spotShadowAlpha: Float) {
-        mHardwareRenderer?.setLightSourceAlpha(ambientShadowAlpha, spotShadowAlpha)
+        mAmbientShadowAlpha = ambientShadowAlpha
+        mSpotShadowAlpha = spotShadowAlpha
     }
 
     /**
@@ -227,10 +281,12 @@
     private inline fun acquireBuffer(block: (HardwareBuffer, SyncFenceCompat?) -> Unit) {
         mBufferLock.withLock {
             // Block until the number of outstanding Images is less than the maximum specified
-            while (mAllocatedBuffers.size >= mImageReader.maxImages) {
+            val reader = mImageReader ?: return
+            while (mAllocatedBuffers.size >= reader.maxImages) {
                 mBufferSignal.await()
             }
-            val image = mImageReader.acquireNextImage()
+
+            val image = reader.acquireNextImage()
             if (image != null) {
                 // Be sure to call Image#getHardwareBuffer once as each call creates a new java
                 // object
@@ -244,7 +300,7 @@
                     val fence = image.getFenceCompat()
                     block(buffer, fence)
                     // If we are leveraging single buffered rendering, release the buffer right away
-                    if (mImageReader.maxImages == 1) {
+                    if (reader.maxImages == 1) {
                         releaseBuffer(buffer, fence)
                     }
                 } else {
@@ -279,7 +335,10 @@
         lightZ: Float,
         lightRadius: Float
     ) {
-        mHardwareRenderer?.setLightSourceGeometry(lightX, lightY, lightZ, lightRadius)
+        mLightX = lightX
+        mLightY = lightY
+        mLightZ = lightZ
+        mLightRadius = lightRadius
     }
 
     private fun Image.getFenceCompat(): SyncFenceCompat? =
@@ -353,6 +412,9 @@
     companion object {
         const val TAG = "BufferRendererV29"
 
+        private val verifiedPreservation = AtomicBoolean(false)
+        private val supportsPreservation = AtomicBoolean(false)
+
         internal fun createPreservationStrategy(
             preservationStrategy: Int
         ): PreservedRenderStrategy =
@@ -369,10 +431,13 @@
                     RedrawBufferStrategy(true)
                 }
                 else -> {
-                    val verifier = PreservedBufferContentsVerifier()
-                    val preserveContents = verifier.supportsPreservedRenderedContent()
-                    verifier.release()
-                    if (preserveContents) {
+                    if (!verifiedPreservation.getAndSet(true)) {
+                        val verifier = PreservedBufferContentsVerifier()
+                        supportsPreservation.set(verifier.supportsPreservedRenderedContent())
+                        verifier.release()
+                    }
+
+                    if (supportsPreservation.get()) {
                         Log.v(TAG, "Device supports persisted canvas optimizations")
                         SingleBufferedStrategy()
                     } else {
diff --git a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/LowLatencyCanvasView.kt b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/LowLatencyCanvasView.kt
index d56f4c3..ecf83e5 100644
--- a/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/LowLatencyCanvasView.kt
+++ b/graphics/graphics-core/src/main/java/androidx/graphics/lowlatency/LowLatencyCanvasView.kt
@@ -211,7 +211,6 @@
                 mFrontBufferTarget.set(false)
                 mDrawCompleteRunnable.set(drawingFinished)
                 mFrontBufferedRenderer?.render(Unit)
-                hideFrontBuffer()
             }
         }
 
@@ -223,6 +222,7 @@
                 holder.addCallback(mSurfaceHolderCallbacks)
             }
         mSurfaceView = surfaceView
+        hideFrontBuffer()
         addView(surfaceView)
     }
 
diff --git a/kruth/kruth/bcv/native/current.txt b/kruth/kruth/bcv/native/current.txt
index fc71af7..c38a4c9 100644
--- a/kruth/kruth/bcv/native/current.txt
+++ b/kruth/kruth/bcv/native/current.txt
@@ -1,5 +1,5 @@
 // Klib ABI Dump
-// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosSimulatorArm64, watchosX64]
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosSimulatorArm64, watchosX64]
 // Rendering settings:
 // - Signature version: 2
 // - Show manifest properties: true
diff --git a/kruth/kruth/build.gradle b/kruth/kruth/build.gradle
index d2776cda..a12d78a 100644
--- a/kruth/kruth/build.gradle
+++ b/kruth/kruth/build.gradle
@@ -41,6 +41,7 @@
     watchos()
     tvos()
     androidNative()
+    mingwX64()
 
     defaultPlatform(PlatformIdentifier.JVM)
 
diff --git a/libraryversions.toml b/libraryversions.toml
index f6c0c9a..c7e0f8c 100644
--- a/libraryversions.toml
+++ b/libraryversions.toml
@@ -166,7 +166,7 @@
 WEAR_TILES = "1.5.0-alpha01"
 WEAR_TOOLING_PREVIEW = "1.0.0-rc01"
 WEAR_WATCHFACE = "1.3.0-alpha03"
-WEBKIT = "1.12.0-alpha02"
+WEBKIT = "1.12.0-beta01"
 # Adding a comment to prevent merge conflicts for Window artifact
 WINDOW = "1.4.0-alpha01"
 WINDOW_EXTENSIONS = "1.4.0-alpha01"
diff --git a/mediarouter/mediarouter/src/androidTest/AndroidManifest.xml b/mediarouter/mediarouter/src/androidTest/AndroidManifest.xml
index 92a343a..6460082 100644
--- a/mediarouter/mediarouter/src/androidTest/AndroidManifest.xml
+++ b/mediarouter/mediarouter/src/androidTest/AndroidManifest.xml
@@ -18,6 +18,10 @@
 
     <application
         android:supportsRtl="true">
+
+        <receiver android:name="androidx.mediarouter.media.MediaTransferReceiver"
+            android:exported="true" />
+
         <activity
             android:name="androidx.mediarouter.app.MediaRouteChooserDialogTestActivity"
             android:label="MediaRouteChooserDialogTestActivity"
@@ -29,7 +33,6 @@
             android:exported="true">
             <intent-filter>
                 <action android:name="android.media.MediaRouteProviderService" />
-                <action android:name="android.media.MediaRoute2ProviderService" />
             </intent-filter>
         </service>
 
diff --git a/navigation/navigation-dynamic-features-runtime/src/main/res/raw/keep.xml b/navigation/navigation-dynamic-features-runtime/src/main/res/raw/navigation_keep_all.xml
similarity index 100%
rename from navigation/navigation-dynamic-features-runtime/src/main/res/raw/keep.xml
rename to navigation/navigation-dynamic-features-runtime/src/main/res/raw/navigation_keep_all.xml
diff --git a/pdf/integration-tests/testapp/src/main/res/values/themes.xml b/pdf/integration-tests/testapp/src/main/res/values/themes.xml
index b12dd9b..c9e7c70 100644
--- a/pdf/integration-tests/testapp/src/main/res/values/themes.xml
+++ b/pdf/integration-tests/testapp/src/main/res/values/themes.xml
@@ -1,10 +1,4 @@
-<resources xmlns:tools="http://schemas.android.com/tools">
+<resources>
     <!-- Base application theme. -->
-    <style name="BaseAppTheme" parent="Theme.Material3.Dark.NoActionBar">
-        <item name="colorPrimary">@color/google_grey</item>
-        <item name="colorPrimaryDark">@color/black</item>
-        <item name="colorAccent">@color/google_white</item>
-    </style>
-
-    <style name="AppTheme" parent="BaseAppTheme" />
+    <style name="AppTheme" parent="Theme.Material3.DayNight.NoActionBar" />
 </resources>
\ No newline at end of file
diff --git a/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
index d1da45e..66ef0c3 100644
--- a/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
+++ b/pdf/pdf-viewer-fragment/src/main/java/androidx/pdf/viewer/fragment/PdfViewerFragment.kt
@@ -378,7 +378,7 @@
         }
 
         if (paginatedView != null && paginatedView?.childCount!! > 0) {
-            pdfLoaderCallbacks?.loadPageAssets(zoomView?.zoomScroll()?.get()!!)
+            zoomView?.let { layoutHandler?.let { it1 -> it.loadPageAssets(it1, viewState) } }
         }
     }
 
diff --git a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PdfDocumentRemote.aidl b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PdfDocumentRemote.aidl
index fac5a69..c337372 100644
--- a/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PdfDocumentRemote.aidl
+++ b/pdf/pdf-viewer/api/aidlRelease/current/androidx/pdf/models/PdfDocumentRemote.aidl
@@ -34,4 +34,5 @@
   int getFormType();
   boolean cloneWithoutSecurity(in ParcelFileDescriptor destination);
   boolean saveAs(in ParcelFileDescriptor destination);
+  void releasePage(int pageNum);
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentService.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentService.java
index c6e1956..6cc8b74 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentService.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfDocumentService.java
@@ -91,42 +91,47 @@
 
         @Override
         public Dimensions getPageDimensions(int pageNum) {
-            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+            PdfPageAdapter pageAdapter = null;
+            try {
+                pageAdapter = mAdapter.openPage(pageNum, false);
                 return new Dimensions(pageAdapter.getWidth(),
                         pageAdapter.getHeight());
+            } finally {
+                mAdapter.releasePage(pageAdapter, pageNum);
             }
         }
 
         @Override
         public Bitmap renderPage(int pageNum, int pageWidth, int pageHeight,
                 boolean hideTextAnnots) {
-            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
-                Bitmap output = Bitmap.createBitmap(pageWidth, pageHeight, Bitmap.Config.ARGB_8888);
-                output.eraseColor(Color.WHITE);
-                pageAdapter.render(output);
-                return output;
-            }
+            Bitmap output = Bitmap.createBitmap(pageWidth, pageHeight, Bitmap.Config.ARGB_8888);
+            output.eraseColor(Color.WHITE);
+            mAdapter.openPage(pageNum, true).render(output);
+            return output;
         }
 
         @Override
         public Bitmap renderTile(int pageNum, int tileWidth, int tileHeight, int scaledPageWidth,
                 int scaledPageHeight, int left, int top, boolean hideTextAnnots) {
-            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
-                Bitmap output = Bitmap.createBitmap(tileWidth, tileHeight, Bitmap.Config.ARGB_8888);
-                output.eraseColor(Color.WHITE);
-                pageAdapter.renderTile(output, left, top, scaledPageWidth, scaledPageHeight);
-                return output;
-            }
+            Bitmap output = Bitmap.createBitmap(tileWidth, tileHeight, Bitmap.Config.ARGB_8888);
+            output.eraseColor(Color.WHITE);
+            mAdapter.openPage(pageNum, true)
+                    .renderTile(output, left, top, scaledPageWidth, scaledPageHeight);
+            return output;
         }
 
 
         @Override
         public String getPageText(int pageNum) {
             if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
-                try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                PdfPageAdapter pageAdapter = null;
+                try {
+                    pageAdapter = mAdapter.openPage(pageNum, false);
                     List<PdfPageTextContent> textPdfContentList = pageAdapter.getPageTextContents();
                     // TODO: Add list handling instead of taking its first element
                     return textPdfContentList.get(0).getText();
+                } finally {
+                    mAdapter.releasePage(pageAdapter, pageNum);
                 }
             }
             throw new UnsupportedOperationException("Operation support above S");
@@ -135,10 +140,14 @@
         @Override
         public List<String> getPageAltText(int pageNum) {
             if (SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S) >= 13) {
-                try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+                PdfPageAdapter pageAdapter = null;
+                try {
+                    pageAdapter = mAdapter.openPage(pageNum, false);
                     List<PdfPageImageContent> text = pageAdapter.getPageImageContents();
                     return text.stream().map(PdfPageImageContent::getAltText).collect(
                             Collectors.toList());
+                } finally {
+                    mAdapter.releasePage(pageAdapter, pageNum);
                 }
             }
             throw new UnsupportedOperationException("Operation support above S");
@@ -146,16 +155,22 @@
 
         @Override
         public MatchRects searchPageText(int pageNum, String query) {
-            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+            PdfPageAdapter pageAdapter = null;
+            try {
+                pageAdapter = mAdapter.openPage(pageNum, false);
                 List<PageMatchBounds> searchResultList = pageAdapter.searchPageText(query);
                 return MatchRects.flattenList(searchResultList);
+            } finally {
+                mAdapter.releasePage(pageAdapter, pageNum);
             }
         }
 
         @Override
         public PageSelection selectPageText(int pageNum, SelectionBoundary start,
                 SelectionBoundary stop) {
-            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+            PdfPageAdapter pageAdapter = null;
+            try {
+                pageAdapter = mAdapter.openPage(pageNum, false);
                 android.graphics.pdf.models.selection.PageSelection pageSelection =
                         pageAdapter.selectPageText(SelectionBoundary.convert(start),
                                 SelectionBoundary.convert(stop));
@@ -163,20 +178,28 @@
                     return PageSelection.convert(pageSelection);
                 }
                 return null;
+            } finally {
+                mAdapter.releasePage(pageAdapter, pageNum);
             }
         }
 
         @Override
         public LinkRects getPageLinks(int pageNum) {
-            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+            PdfPageAdapter pageAdapter = null;
+            try {
+                pageAdapter = mAdapter.openPage(pageNum, false);
                 List<PdfPageLinkContent> pageLinks = pageAdapter.getPageLinks();
                 return LinkRects.flattenList(pageLinks);
+            } finally {
+                mAdapter.releasePage(pageAdapter, pageNum);
             }
         }
 
         @Override
         public List<GotoLink> getPageGotoLinks(int pageNum) {
-            try (PdfPageAdapter pageAdapter = mAdapter.openPage(pageNum)) {
+            PdfPageAdapter pageAdapter = null;
+            try {
+                pageAdapter = mAdapter.openPage(pageNum, false);
                 List<PdfPageGotoLinkContent> gotoLinks = pageAdapter.getPageGotoLinks();
                 if (!gotoLinks.isEmpty()) {
                     List<GotoLink> list = new ArrayList<>();
@@ -187,10 +210,17 @@
                     return list;
                 }
                 return null;
+            } finally {
+                mAdapter.releasePage(pageAdapter, pageNum);
             }
         }
 
         @Override
+        public void releasePage(int pageNum) {
+            mAdapter.releasePage(null, pageNum);
+        }
+
+        @Override
         public boolean isPdfLinearized() {
             return mAdapter.getDocumentLinearizationType()
                     == PdfRendererPreV.DOCUMENT_LINEARIZED_TYPE_LINEARIZED;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfRendererAdapter.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfRendererAdapter.java
index ea7714e..ccbdd89 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfRendererAdapter.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/service/PdfRendererAdapter.java
@@ -29,11 +29,15 @@
 import androidx.core.util.Supplier;
 
 import java.io.IOException;
+import java.util.Map;
 
 @RestrictTo(RestrictTo.Scope.LIBRARY)
 class PdfRendererAdapter implements AutoCloseable {
     private PdfRenderer mPdfRenderer;
     private PdfRendererPreV mPdfRendererPreV;
+    @SuppressLint({"UseSparseArrays", "BanConcurrentHashMap"})
+    private final Map<Integer, PdfPageAdapter> mCachedPageMap =
+            new java.util.concurrent.ConcurrentHashMap<>();
 
     @SuppressLint("ObsoleteSdkInt") // TODO: Remove after sdk extension 13 release
     PdfRendererAdapter(@NonNull ParcelFileDescriptor parcelFileDescriptor,
@@ -50,15 +54,50 @@
         }
     }
 
-    /**  */
+    /**
+     * Caller should use {@link #releasePage(PdfPageAdapter, int)} to close the page resource
+     * reliably after usage.
+     */
     @NonNull
-    PdfPageAdapter openPage(int pageNum) {
+    PdfPageAdapter openPage(int pageNum, boolean useCache) {
         if (mPdfRenderer != null) {
+            if (useCache) {
+                return openPageWithCache(pageNum);
+            }
             return new PdfPageAdapter(mPdfRenderer, pageNum);
         }
         return new PdfPageAdapter(mPdfRendererPreV, pageNum);
     }
 
+    @NonNull
+    private PdfPageAdapter openPageWithCache(int pageNum) {
+        if (mPdfRenderer != null) {
+            // Fetched either from cache or native layer.
+            PdfPageAdapter page = mCachedPageMap.get(pageNum);
+            if (page != null) {
+                return page;
+            }
+            page = new PdfPageAdapter(mPdfRenderer, pageNum);
+            mCachedPageMap.put(pageNum, page);
+            return page;
+        } else {
+            return new PdfPageAdapter(mPdfRendererPreV, pageNum);
+        }
+    }
+
+    /** Closes the page. Also removes and clears the cached instance, if held. */
+    public void releasePage(PdfPageAdapter pageAdapter, int pageNum) {
+        if (mPdfRenderer != null) {
+            if (pageAdapter != null) {
+                pageAdapter.close();
+            }
+            PdfPageAdapter removedPage = mCachedPageMap.remove(pageNum);
+            if (removedPage != null) {
+                removedPage.close();
+            }
+        }
+    }
+
     @SuppressLint("ObsoleteSdkInt") // TODO: Remove after sdk extension 13 release
     public int getPageCount() {
         if (mPdfRenderer != null && Build.VERSION.SDK_INT >= 35) {
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java
index 06335ea..5af4b14 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PaginatedView.java
@@ -384,6 +384,7 @@
             // even for pages we can't see right now. Form filling operations should always
             // be executed against the document, even if the user has scrolled away from the page.
             mPdfLoader.cancelExceptSearchAndFormFilling(page);
+            mPdfLoader.releasePage(page);
             if (clearViews) {
                 removeViewAt(page);
             }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
index d681274..0fe8777 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/PdfViewer.java
@@ -315,7 +315,7 @@
         }
 
         if (mPaginatedView != null && mPaginatedView.getChildCount() > 0) {
-            loadPageAssets(mZoomView.zoomScroll().get());
+            mZoomView.loadPageAssets(mLayoutHandler, null);
         }
     }
 
@@ -538,31 +538,6 @@
         pageView.setOverlay(selection.getOverlay());
     }
 
-    private void loadPageAssets(ZoomScroll position) {
-        // Change the resolution of the bitmaps only when a gesture is not in progress.
-        if (position.stable || mZoomView.getStableZoom() == 0) {
-            mZoomView.setStableZoom(position.zoom);
-        }
-
-        mPaginationModel.setViewArea(mZoomView.getVisibleAreaInContentCoords());
-        mPaginatedView.refreshPageRangeInVisibleArea(position, mZoomView.getHeight());
-        mPaginatedView.handleGonePages(/* clearViews= */ false);
-        mPaginatedView.loadInvisibleNearPageRange(mZoomView.getStableZoom());
-
-        // The step (4) below requires page Views to be created and laid out. So we create them here
-        // and set this flag if that operation needs to wait for a layout pass.
-        boolean requiresLayoutPass = mPaginatedView.createPageViewsForVisiblePageRange();
-
-        // 4. Refresh tiles and/or full pages.
-        if (position.stable) {
-            // Perform a full refresh on all visible pages
-            mPaginatedView.handleGonePages(/* clearViews= */ true);
-        }
-
-        mLayoutHandler.maybeLayoutPages(
-                mPaginatedView.getPageRangeHandler().getVisiblePages().getLast());
-    }
-
     /** Show the loading spinner. */
     @UiThread
     public void showSpinner() {
@@ -753,7 +728,7 @@
                                 mLayoutHandler.maybeLayoutPages(newRange.getLast());
                             } else if (newRange.contains(pageNum)) {
                                 // The new page is visible, fetch its assets.
-                                loadPageAssets(mZoomView.zoomScroll().get());
+                                mZoomView.loadPageAssets(mLayoutHandler, null);
                             }
                         }
                     }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java
index 1baeb08..037f875 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/ZoomScrollValueObserver.java
@@ -72,7 +72,7 @@
                 || position == null || mPaginatedView.getPaginationModel().getSize() == 0) {
             return;
         }
-        loadPageAssets(position);
+        mZoomView.loadPageAssets(mLayoutHandler, mViewState);
 
         if (oldPosition.scrollY > position.scrollY) {
             mIsPageScrollingUp = true;
@@ -141,42 +141,6 @@
         mAnnotationButton.setVisibility(View.VISIBLE);
     }
 
-    private void loadPageAssets(ZoomView.ZoomScroll position) {
-        if (!mPaginatedView.getPaginationModel().isInitialized()) {
-            return;
-        }
-        // Change the resolution of the bitmaps only when a gesture is not in progress.
-        if (position.stable || mZoomView.getStableZoom() == 0) {
-            mZoomView.setStableZoom(position.zoom);
-        }
-
-        mPaginatedView.getPaginationModel().setViewArea(mZoomView.getVisibleAreaInContentCoords());
-        mPaginatedView.refreshPageRangeInVisibleArea(position, mZoomView.getHeight());
-        mPaginatedView.handleGonePages(/* clearViews= */ false);
-        mPaginatedView.loadInvisibleNearPageRange(mZoomView.getStableZoom());
-
-        // The step (4) below requires page Views to be created and laid out. So we create them here
-        // and set this flag if that operation needs to wait for a layout pass.
-        boolean requiresLayoutPass = mPaginatedView.createPageViewsForVisiblePageRange();
-
-        // 4. Refresh tiles and/or full pages.
-        if (position.stable) {
-            // Perform a full refresh on all visible pages
-            mPaginatedView.refreshVisiblePages(requiresLayoutPass, mViewState.get(),
-                    mZoomView.getStableZoom());
-            mPaginatedView.handleGonePages(/* clearViews= */ true);
-        } else if (mZoomView.getStableZoom() == position.zoom) {
-            // Just load a few more tiles in case of tile-scroll
-            mPaginatedView.refreshVisibleTiles(requiresLayoutPass, mViewState.get());
-        }
-
-        if (mPaginatedView.getPageRangeHandler().getVisiblePages() != null) {
-            mLayoutHandler.maybeLayoutPages(
-                    mPaginatedView.getPageRangeHandler().getVisiblePages().getLast());
-        }
-    }
-
-
     /** Exposing a function to clear the handler when PDFViewer Fragment is destroyed. */
     public void clearAnnotationHandler() {
         mAnnotationButtonHandler.removeCallbacksAndMessages(null);
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
index e444f31..e154fca 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoader.java
@@ -170,6 +170,11 @@
         getPageLoader(pageNum).cancelExceptSearchAndFormFilling();
     }
 
+    /** Releases object in memory related to a page when that page is no longer visible. */
+    public void releasePage(int pageNum) {
+        getPageLoader(pageNum).releasePage();
+    }
+
     /**
      * Loads page dimensions for the given page - once it is ready, will call the
      * {@link PdfLoaderCallbacks#setPageDimensions} callback.
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
index 5a2d44d..c8b00b8 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfLoaderCallbacksImpl.kt
@@ -136,42 +136,6 @@
             .setOverlay(selection.overlay)
     }
 
-    public fun loadPageAssets(position: ZoomScroll) {
-        // Change the resolution of the bitmaps only when a gesture is not in progress.
-        if (position.stable || zoomView.stableZoom == 0f) {
-            zoomView.stableZoom = position.zoom
-        }
-
-        zoomView.let {
-            paginatedView.paginationModel.setViewArea(it.visibleAreaInContentCoords)
-            paginatedView.refreshPageRangeInVisibleArea(position, it.height)
-            paginatedView.handleGonePages(/* clearViews= */ false)
-            paginatedView.loadInvisibleNearPageRange(it.stableZoom)
-        }
-
-        // The step (4) below requires page Views to be created and laid out. So we create them here
-        // and set this flag if that operation needs to wait for a layout pass.
-        val requiresLayoutPass: Boolean = paginatedView.createPageViewsForVisiblePageRange()
-
-        // 4. Refresh tiles and/or full pages.
-        if (position.stable) {
-            // Perform a full refresh on all visible pages
-            viewState.get()?.let {
-                zoomView.let { it1 ->
-                    paginatedView.refreshVisiblePages(requiresLayoutPass, it, it1.stableZoom)
-                }
-            }
-            paginatedView.handleGonePages(/* clearViews= */ true)
-        } else if (zoomView.stableZoom == position.zoom) {
-            // Just load a few more tiles in case of tile-scroll
-            viewState.get()?.let { paginatedView.refreshVisibleTiles(requiresLayoutPass, it) }
-        }
-
-        paginatedView.pageRangeHandler.visiblePages?.let {
-            layoutHandler!!.maybeLayoutPages(it.last)
-        }
-    }
-
     private fun isPageCreated(pageNum: Int): Boolean {
         return pageNum < paginatedView.paginationModel.size &&
             paginatedView.getViewAt(pageNum) != null
@@ -332,7 +296,7 @@
                 layoutHandler!!.maybeLayoutPages(newRange.last)
             } else if (newRange.contains(pageNum)) {
                 // The new page is visible, fetch its assets.
-                loadPageAssets(zoomView.zoomScroll().get()!!)
+                zoomView.loadPageAssets(layoutHandler!!, viewState)
             }
         }
     }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java
index d446bf0..e8fe78f 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/PdfPageLoader.java
@@ -67,6 +67,7 @@
     SelectionTask mSelectionTask;
     GetPageLinksTask mLinksTask;
     GetPageGotoLinksTask mGotoLinksTask;
+    ReleasePageTask mReleasePageTask;
 
     /**
      * All currently scheduled tile tasks.
@@ -257,6 +258,14 @@
         }
     }
 
+    /** Releases object in memory related to a page when that page is no longer visible. */
+    public void releasePage() {
+        if (mReleasePageTask == null) {
+            mReleasePageTask = new ReleasePageTask();
+            mParent.mExecutor.schedule(mReleasePageTask);
+        }
+    }
+
     /**
      *
      */
@@ -366,6 +375,40 @@
         }
     }
 
+    /** AsyncTask for releasing page objects from memory after it is no longer visible. */
+    class ReleasePageTask extends AbstractPdfTask<Void> {
+        ReleasePageTask() {
+            super(mParent, Priority.RELEASE);
+        }
+
+        @Override
+        protected String getLogTag() {
+            return "ReleasePageTask";
+        }
+
+        @Override
+        protected Void doInBackground(PdfDocumentRemoteProto pdfDocument) throws RemoteException {
+            pdfDocument.getPdfDocumentRemote().releasePage(mPageNum);
+            return null;
+        }
+
+        @Override
+        protected void doCallback(PdfLoaderCallbacks callbacks, Void unused) {
+            /* no-op */
+        }
+
+        @Override
+        protected void cleanup() {
+            mReleasePageTask = null;
+        }
+
+        @NonNull
+        @Override
+        public String toString() {
+            return String.format("ReleasePageTask(page=%d)", mPageNum);
+        }
+    }
+
     /** AsyncTask for rendering a tile as a bitmap. */
     class RenderTileTask extends AbstractPdfTask<Bitmap> {
         final Dimensions mPageSize;
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/Priority.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/Priority.java
index a88acb8..24884b9 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/Priority.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/viewer/loader/Priority.java
@@ -38,5 +38,6 @@
     COMMENT_ANCHORS,
     PAGE_CLICK,
     SET_FORM_FIELD_VALUE,
-    FORM_WIDGET_INFO
+    FORM_WIDGET_INFO,
+    RELEASE
 }
diff --git a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java
index e6cc6cf..d7755ef 100644
--- a/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java
+++ b/pdf/pdf-viewer/src/main/java/androidx/pdf/widget/ZoomView.java
@@ -45,6 +45,7 @@
 import androidx.annotation.VisibleForTesting;
 import androidx.core.view.ViewCompat;
 import androidx.pdf.R;
+import androidx.pdf.ViewState;
 import androidx.pdf.util.GestureTracker;
 import androidx.pdf.util.GestureTrackingView;
 import androidx.pdf.util.MathUtils;
@@ -55,6 +56,8 @@
 import androidx.pdf.util.ThreadUtils;
 import androidx.pdf.util.ZoomScrollRestorer;
 import androidx.pdf.util.ZoomUtils;
+import androidx.pdf.viewer.LayoutHandler;
+import androidx.pdf.viewer.PaginatedView;
 import androidx.pdf.viewer.PdfSelectionModel;
 
 import com.google.android.material.motion.MotionUtils;
@@ -836,10 +839,15 @@
                 scaleYAnimator
                 // It's important for scaleYAnimator to be last, since it has the listener.
         );
-        animatorSet.setDuration(
-                MotionUtils.resolveThemeDuration(
-                        getContext(), com.google.android.material.R.attr.motionDurationMedium1,
-                        FALLBACK_ZOOM_ANIMATION_DURATION_MS));
+        int themeDuration = FALLBACK_ZOOM_ANIMATION_DURATION_MS;
+        try {
+            themeDuration = MotionUtils.resolveThemeDuration(
+                    getContext(), com.google.android.material.R.attr.motionDurationMedium1,
+                    FALLBACK_ZOOM_ANIMATION_DURATION_MS);
+        } catch (NoClassDefFoundError ignored) {
+            // Material resources not present in host app, fallback to default
+        }
+        animatorSet.setDuration(themeDuration);
         animatorSet.setInterpolator(interpolator);
         animatorSet.addListener(
                 new AnimatorListenerAdapter() {
@@ -887,6 +895,60 @@
     }
 
     /**
+     * Loads and refreshes page assets based on the current zoom and scroll state.
+     */
+    public void loadPageAssets(@NonNull LayoutHandler layoutHandler,
+            @Nullable ObservableValue<ViewState> viewState) {
+
+        PaginatedView paginatedView = this.findViewById(R.id.pdf_view);
+        ZoomScroll position = this.zoomScroll().get();
+        if (position == null || !paginatedView.getPaginationModel().isInitialized()) {
+            return;
+        }
+
+        // Change the resolution of the bitmaps only when a gesture is not in progress.
+        if (position.stable || this.getStableZoom() == 0f) {
+            this.setStableZoom(position.zoom);
+        }
+
+        paginatedView.getPaginationModel().setViewArea(this.getVisibleAreaInContentCoords());
+        paginatedView.refreshPageRangeInVisibleArea(position, this.getHeight());
+        paginatedView.handleGonePages(false);
+        paginatedView.loadInvisibleNearPageRange(this.getStableZoom());
+
+        // The step (4) below requires page Views to be created and laid out.
+
+        // So we create them here and set this flag
+        // if that operation needs to wait for a layout pass.
+        boolean requiresLayoutPass = paginatedView.createPageViewsForVisiblePageRange();
+
+        // 4. Refresh tiles and/or full pages.
+        if (position.stable) {
+            if (viewState != null) {
+                // Perform a full refresh on all visible pages
+                ViewState currentViewState = viewState.get();
+                if (currentViewState != null) {
+                    paginatedView.refreshVisiblePages(
+                            requiresLayoutPass, currentViewState, this.getStableZoom());
+                }
+            }
+            paginatedView.handleGonePages(true);
+        } else if (this.getStableZoom() == position.zoom) {
+            // Just load a few more tiles in case of tile-scroll
+            ViewState currentViewState = viewState.get();
+            if (currentViewState != null) {
+                paginatedView.refreshVisibleTiles(requiresLayoutPass, currentViewState);
+            }
+        }
+
+        if (paginatedView.getPageRangeHandler().getVisiblePages() != null) {
+            layoutHandler.maybeLayoutPages(
+                    paginatedView.getPageRangeHandler().getVisiblePages().getLast()
+            );
+        }
+    }
+
+    /**
      * Given a point in the zoom-view's co-ordinates, convert it to the content's co-ordinates,
      * using the current zoom and scroll position of the zoomview.
      */
diff --git a/pdf/pdf-viewer/src/main/res/layout/pdf_viewer_container.xml b/pdf/pdf-viewer/src/main/res/layout/pdf_viewer_container.xml
index 4f91158..4ed1c38 100644
--- a/pdf/pdf-viewer/src/main/res/layout/pdf_viewer_container.xml
+++ b/pdf/pdf-viewer/src/main/res/layout/pdf_viewer_container.xml
@@ -39,7 +39,7 @@
         android:layout_gravity="bottom|end"
         android:visibility="gone"
         android:src="@drawable/edit_fab"
-        app:backgroundTint="#5D4200"
-        app:tint="#FFDEA5"/>
+        app:backgroundTint="?attr/colorPrimaryContainer"
+        app:tint="?attr/colorOnPrimaryContainer" />
 
 </FrameLayout>
\ No newline at end of file
diff --git a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PdfDocumentRemote.aidl b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PdfDocumentRemote.aidl
index 7f09642..9b90b1d 100644
--- a/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PdfDocumentRemote.aidl
+++ b/pdf/pdf-viewer/src/main/stableAidl/androidx/pdf/models/PdfDocumentRemote.aidl
@@ -41,5 +41,7 @@
 
     boolean saveAs(in ParcelFileDescriptor destination);
 
+    void releasePage(int pageNum);
+
     // The PdfDocument is destroyed when this service is destroyed.
 }
\ No newline at end of file
diff --git a/playground-common/playground.properties b/playground-common/playground.properties
index b0c866271..e3086fc 100644
--- a/playground-common/playground.properties
+++ b/playground-common/playground.properties
@@ -26,5 +26,5 @@
 # Disable docs
 androidx.enableDocumentation=false
 androidx.playground.snapshotBuildId=11349412
-androidx.playground.metalavaBuildId=12109034
+androidx.playground.metalavaBuildId=12216051
 androidx.studio.type=playground
\ No newline at end of file
diff --git a/privacysandbox/ads/ads-adservices/api/1.1.0-beta10.txt b/privacysandbox/ads/ads-adservices/api/1.1.0-beta10.txt
index 55bf72b..055f38b 100644
--- a/privacysandbox/ads/ads-adservices/api/1.1.0-beta10.txt
+++ b/privacysandbox/ads/ads-adservices/api/1.1.0-beta10.txt
@@ -84,6 +84,7 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataOutcome {
+    ctor public GetAdSelectionDataOutcome(long adSelectionId);
     ctor public GetAdSelectionDataOutcome(long adSelectionId, optional byte[]? adSelectionData);
     method public byte[]? getAdSelectionData();
     method public long getAdSelectionId();
@@ -101,6 +102,8 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class PersistAdSelectionResultRequest {
+    ctor public PersistAdSelectionResultRequest(long adSelectionId);
+    ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller);
     ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller, optional byte[]? adSelectionResult);
     method public long getAdSelectionId();
     method public byte[]? getAdSelectionResult();
@@ -111,6 +114,7 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class ReportEventRequest {
+    ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations);
     ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations, optional android.view.InputEvent? inputEvent);
     method public long getAdSelectionId();
     method public String getEventData();
@@ -320,6 +324,10 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class FetchAndJoinCustomAudienceRequest {
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime);
     ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals);
     method public java.time.Instant? getActivationTime();
     method public java.time.Instant? getExpirationTime();
diff --git a/privacysandbox/ads/ads-adservices/api/current.txt b/privacysandbox/ads/ads-adservices/api/current.txt
index 55bf72b..055f38b 100644
--- a/privacysandbox/ads/ads-adservices/api/current.txt
+++ b/privacysandbox/ads/ads-adservices/api/current.txt
@@ -84,6 +84,7 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataOutcome {
+    ctor public GetAdSelectionDataOutcome(long adSelectionId);
     ctor public GetAdSelectionDataOutcome(long adSelectionId, optional byte[]? adSelectionData);
     method public byte[]? getAdSelectionData();
     method public long getAdSelectionId();
@@ -101,6 +102,8 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class PersistAdSelectionResultRequest {
+    ctor public PersistAdSelectionResultRequest(long adSelectionId);
+    ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller);
     ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller, optional byte[]? adSelectionResult);
     method public long getAdSelectionId();
     method public byte[]? getAdSelectionResult();
@@ -111,6 +114,7 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class ReportEventRequest {
+    ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations);
     ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations, optional android.view.InputEvent? inputEvent);
     method public long getAdSelectionId();
     method public String getEventData();
@@ -320,6 +324,10 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class FetchAndJoinCustomAudienceRequest {
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime);
     ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals);
     method public java.time.Instant? getActivationTime();
     method public java.time.Instant? getExpirationTime();
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta10.txt b/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta10.txt
index 55bf72b..055f38b 100644
--- a/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta10.txt
+++ b/privacysandbox/ads/ads-adservices/api/restricted_1.1.0-beta10.txt
@@ -84,6 +84,7 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataOutcome {
+    ctor public GetAdSelectionDataOutcome(long adSelectionId);
     ctor public GetAdSelectionDataOutcome(long adSelectionId, optional byte[]? adSelectionData);
     method public byte[]? getAdSelectionData();
     method public long getAdSelectionId();
@@ -101,6 +102,8 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class PersistAdSelectionResultRequest {
+    ctor public PersistAdSelectionResultRequest(long adSelectionId);
+    ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller);
     ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller, optional byte[]? adSelectionResult);
     method public long getAdSelectionId();
     method public byte[]? getAdSelectionResult();
@@ -111,6 +114,7 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class ReportEventRequest {
+    ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations);
     ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations, optional android.view.InputEvent? inputEvent);
     method public long getAdSelectionId();
     method public String getEventData();
@@ -320,6 +324,10 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class FetchAndJoinCustomAudienceRequest {
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime);
     ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals);
     method public java.time.Instant? getActivationTime();
     method public java.time.Instant? getExpirationTime();
diff --git a/privacysandbox/ads/ads-adservices/api/restricted_current.txt b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
index 55bf72b..055f38b 100644
--- a/privacysandbox/ads/ads-adservices/api/restricted_current.txt
+++ b/privacysandbox/ads/ads-adservices/api/restricted_current.txt
@@ -84,6 +84,7 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class GetAdSelectionDataOutcome {
+    ctor public GetAdSelectionDataOutcome(long adSelectionId);
     ctor public GetAdSelectionDataOutcome(long adSelectionId, optional byte[]? adSelectionData);
     method public byte[]? getAdSelectionData();
     method public long getAdSelectionId();
@@ -101,6 +102,8 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class PersistAdSelectionResultRequest {
+    ctor public PersistAdSelectionResultRequest(long adSelectionId);
+    ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller);
     ctor public PersistAdSelectionResultRequest(long adSelectionId, optional androidx.privacysandbox.ads.adservices.common.AdTechIdentifier? seller, optional byte[]? adSelectionResult);
     method public long getAdSelectionId();
     method public byte[]? getAdSelectionResult();
@@ -111,6 +114,7 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext8OptIn public final class ReportEventRequest {
+    ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations);
     ctor public ReportEventRequest(long adSelectionId, String eventKey, String eventData, int reportingDestinations, optional android.view.InputEvent? inputEvent);
     method public long getAdSelectionId();
     method public String getEventData();
@@ -320,6 +324,10 @@
   }
 
   @SuppressCompatibility @androidx.privacysandbox.ads.adservices.common.ExperimentalFeatures.Ext10OptIn public final class FetchAndJoinCustomAudienceRequest {
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime);
+    ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime);
     ctor public FetchAndJoinCustomAudienceRequest(android.net.Uri fetchUri, optional String? name, optional java.time.Instant? activationTime, optional java.time.Instant? expirationTime, optional androidx.privacysandbox.ads.adservices.common.AdSelectionSignals? userBiddingSignals);
     method public java.time.Instant? getActivationTime();
     method public java.time.Instant? getExpirationTime();
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/GetAdSelectionDataOutcome.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/GetAdSelectionDataOutcome.kt
index c4fe698..a30b450 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/GetAdSelectionDataOutcome.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/GetAdSelectionDataOutcome.kt
@@ -33,6 +33,7 @@
  */
 @ExperimentalFeatures.Ext10OptIn
 class GetAdSelectionDataOutcome
+@JvmOverloads
 public constructor(val adSelectionId: Long, val adSelectionData: ByteArray? = null) {
 
     /** Checks whether two [GetAdSelectionDataOutcome] objects contain the same information. */
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/PersistAdSelectionResultRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/PersistAdSelectionResultRequest.kt
index e5fe38f..b8c47bf 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/PersistAdSelectionResultRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/PersistAdSelectionResultRequest.kt
@@ -33,6 +33,7 @@
  */
 @ExperimentalFeatures.Ext10OptIn
 class PersistAdSelectionResultRequest
+@JvmOverloads
 public constructor(
     val adSelectionId: Long,
     val seller: AdTechIdentifier? = null,
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/ReportEventRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/ReportEventRequest.kt
index bb207b2..de63e6b 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/ReportEventRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/adselection/ReportEventRequest.kt
@@ -41,6 +41,7 @@
 @OptIn(ExperimentalFeatures.Ext10OptIn::class)
 @ExperimentalFeatures.Ext8OptIn
 class ReportEventRequest
+@JvmOverloads
 public constructor(
     val adSelectionId: Long,
     val eventKey: String,
diff --git a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/FetchAndJoinCustomAudienceRequest.kt b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/FetchAndJoinCustomAudienceRequest.kt
index 59ea6b4..4967f6b 100644
--- a/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/FetchAndJoinCustomAudienceRequest.kt
+++ b/privacysandbox/ads/ads-adservices/src/main/java/androidx/privacysandbox/ads/adservices/customaudience/FetchAndJoinCustomAudienceRequest.kt
@@ -37,6 +37,7 @@
  */
 @ExperimentalFeatures.Ext10OptIn
 class FetchAndJoinCustomAudienceRequest
+@JvmOverloads
 public constructor(
     val fetchUri: Uri,
     val name: String? = null,
diff --git a/privacysandbox/sdkruntime/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/sdkruntime/integration/testaidl/IAppSdk.aidl b/privacysandbox/sdkruntime/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/sdkruntime/integration/testaidl/IAppSdk.aidl
new file mode 100644
index 0000000..cd5d87a
--- /dev/null
+++ b/privacysandbox/sdkruntime/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/sdkruntime/integration/testaidl/IAppSdk.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright 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 androidx.privacysandbox.sdkruntime.integration.testaidl;
+
+interface IAppSdk {
+    String getMessage(int value);
+}
diff --git a/privacysandbox/sdkruntime/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/sdkruntime/integration/testaidl/ISdkApi.aidl b/privacysandbox/sdkruntime/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/sdkruntime/integration/testaidl/ISdkApi.aidl
index 5e4c101..eeace79 100644
--- a/privacysandbox/sdkruntime/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/sdkruntime/integration/testaidl/ISdkApi.aidl
+++ b/privacysandbox/sdkruntime/integration-tests/testaidl/src/main/aidl/androidx/privacysandbox/sdkruntime/integration/testaidl/ISdkApi.aidl
@@ -17,5 +17,6 @@
 package androidx.privacysandbox.sdkruntime.integration.testaidl;
 
 interface ISdkApi {
+    String getMessage();
     boolean invert(boolean value);
 }
diff --git a/privacysandbox/sdkruntime/integration-tests/testapp/build.gradle b/privacysandbox/sdkruntime/integration-tests/testapp/build.gradle
index ae356c3..f8eebda 100644
--- a/privacysandbox/sdkruntime/integration-tests/testapp/build.gradle
+++ b/privacysandbox/sdkruntime/integration-tests/testapp/build.gradle
@@ -34,6 +34,7 @@
     implementation(project(':privacysandbox:sdkruntime:integration-tests:testaidl'))
     implementation(project(':privacysandbox:sdkruntime:integration-tests:testsdk-asb'))
 
+    implementation 'androidx.appcompat:appcompat:1.6.0'
     implementation(project(':privacysandbox:sdkruntime:sdkruntime-client'))
 
     androidTestImplementation(project(':internal-testutils-runtime'))
diff --git a/privacysandbox/sdkruntime/integration-tests/testapp/src/androidTest/java/androidx/privacysandbox/sdkruntime/integration/testapp/SimpleTest.kt b/privacysandbox/sdkruntime/integration-tests/testapp/src/androidTest/java/androidx/privacysandbox/sdkruntime/integration/testapp/SimpleTest.kt
index 834a5ad..92df8ca 100644
--- a/privacysandbox/sdkruntime/integration-tests/testapp/src/androidTest/java/androidx/privacysandbox/sdkruntime/integration/testapp/SimpleTest.kt
+++ b/privacysandbox/sdkruntime/integration-tests/testapp/src/androidTest/java/androidx/privacysandbox/sdkruntime/integration/testapp/SimpleTest.kt
@@ -16,6 +16,8 @@
 
 package androidx.privacysandbox.sdkruntime.integration.testapp
 
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
 import androidx.test.ext.junit.rules.ActivityScenarioRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -35,15 +37,22 @@
 
     @After
     fun tearDown() {
-        activityScenarioRule.withActivity { unloadAllSdks() }
+        activityScenarioRule.withActivity { api.unloadAllSdks() }
     }
 
     @Test
     fun simpleTest() {
         activityScenarioRule.withActivity {
-            val api = runBlocking { loadSdk() }
-            val apiResult = api.invert(false)
+            val testSdkApi = runBlocking { api.loadTestSdk() }
+            val apiResult = testSdkApi.invert(false)
             assertThat(apiResult).isTrue()
         }
     }
+
+    private fun TestAppApi.unloadAllSdks() {
+        getSandboxedSdks()
+            .mapNotNull(SandboxedSdkCompat::getSdkInfo)
+            .map(SandboxedSdkInfo::name)
+            .forEach(::unloadSdk)
+    }
 }
diff --git a/privacysandbox/sdkruntime/integration-tests/testapp/src/main/AndroidManifest.xml b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/AndroidManifest.xml
index c0e0a1b..6072134 100644
--- a/privacysandbox/sdkruntime/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -24,7 +24,8 @@
         <activity
             android:name=".TestMainActivity"
             android:exported="true"
-            android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
+            android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
+            android:theme="@style/Base.Theme.AppCompat">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/AppOwnedSdk.kt b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/AppOwnedSdk.kt
new file mode 100644
index 0000000..fc14cb1
--- /dev/null
+++ b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/AppOwnedSdk.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 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 androidx.privacysandbox.sdkruntime.integration.testapp
+
+import androidx.privacysandbox.sdkruntime.integration.testaidl.IAppSdk
+
+class AppOwnedSdk : IAppSdk.Stub() {
+    override fun getMessage(value: Int): String = "Message from AppOwnedSdk. Value is $value"
+}
diff --git a/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/TestAppApi.kt b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/TestAppApi.kt
new file mode 100644
index 0000000..5918e27
--- /dev/null
+++ b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/TestAppApi.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 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 androidx.privacysandbox.sdkruntime.integration.testapp
+
+import android.content.Context
+import android.os.Bundle
+import android.util.Log
+import androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
+import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
+import androidx.privacysandbox.sdkruntime.integration.testaidl.ISdkApi
+
+/**
+ * Wrapper around test app functionality.
+ *
+ * Shared between UI in test app and functional/integration tests.
+ */
+class TestAppApi(appContext: Context) {
+
+    private val sdkSandboxManager = SdkSandboxManagerCompat.from(appContext)
+
+    suspend fun loadTestSdk(): ISdkApi {
+        val loadedSdk = loadSdk(TEST_SDK_NAME)
+        return ISdkApi.Stub.asInterface(loadedSdk.getInterface())
+    }
+
+    suspend fun loadSdk(sdkName: String, params: Bundle = Bundle()): SandboxedSdkCompat {
+        Log.i(TAG, "Loading SDK ($sdkName)")
+        val loadedSdk = sdkSandboxManager.loadSdk(sdkName, params)
+        Log.i(TAG, "SDK Loaded successfully ($sdkName)")
+        return loadedSdk
+    }
+
+    fun unloadTestSdk() = unloadSdk(TEST_SDK_NAME)
+
+    fun unloadSdk(sdkName: String) {
+        sdkSandboxManager.unloadSdk(sdkName)
+    }
+
+    fun registerAppOwnedSdk(appOwnedSdk: AppOwnedSdkSandboxInterfaceCompat) {
+        sdkSandboxManager.registerAppOwnedSdkSandboxInterface(appOwnedSdk)
+    }
+
+    fun unregisterAppOwnedSdk(appOwnedSdkName: String) {
+        sdkSandboxManager.unregisterAppOwnedSdkSandboxInterface(appOwnedSdkName)
+    }
+
+    fun getSandboxedSdks() = sdkSandboxManager.getSandboxedSdks()
+
+    fun getAppOwnedSdks() = sdkSandboxManager.getAppOwnedSdkSandboxInterfaces()
+
+    companion object {
+        private const val TAG = "TestAppApi"
+
+        /** Name of the Test SDK to be loaded. */
+        private const val TEST_SDK_NAME = "androidx.privacysandbox.sdkruntime.integrationtest.sdk"
+    }
+}
diff --git a/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/TestMainActivity.kt b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/TestMainActivity.kt
index d4a24b3..7528486 100644
--- a/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/TestMainActivity.kt
+++ b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/java/androidx/privacysandbox/sdkruntime/integration/testapp/TestMainActivity.kt
@@ -16,43 +16,147 @@
 
 package androidx.privacysandbox.sdkruntime.integration.testapp
 
-import android.app.Activity
 import android.os.Bundle
+import android.os.IBinder
+import android.text.method.ScrollingMovementMethod
 import android.util.Log
-import androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat
-import androidx.privacysandbox.sdkruntime.core.SandboxedSdkCompat
-import androidx.privacysandbox.sdkruntime.core.SandboxedSdkInfo
+import android.widget.Button
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.lifecycleScope
+import androidx.privacysandbox.sdkruntime.core.AppOwnedSdkSandboxInterfaceCompat
+import androidx.privacysandbox.sdkruntime.core.LoadSdkCompatException
+import androidx.privacysandbox.sdkruntime.integration.testaidl.IAppSdk
 import androidx.privacysandbox.sdkruntime.integration.testaidl.ISdkApi
+import kotlinx.coroutines.launch
 
-class TestMainActivity : Activity() {
+class TestMainActivity : AppCompatActivity() {
 
-    private lateinit var sdkSandboxManager: SdkSandboxManagerCompat
+    lateinit var api: TestAppApi
+    private lateinit var logView: TextView
+
+    private val appOwnedSdk =
+        AppOwnedSdkSandboxInterfaceCompat(
+            name = "AppOwnedSdk",
+            version = 42,
+            binder = AppOwnedSdk()
+        )
 
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-        sdkSandboxManager = SdkSandboxManagerCompat.from(applicationContext)
+        setContentView(R.layout.activity_main)
+        api = TestAppApi(applicationContext)
+
+        logView = findViewById(R.id.logView)
+        logView.setMovementMethod(ScrollingMovementMethod())
+
+        setupLoadSdkButton()
+        setupUnloadSdkButton()
+        setupRegisterAppSdkButton()
+        setupUnregisterAppSdkButton()
+        setupGetSandboxedSdksButton()
+        setupGetAppSdksButton()
     }
 
-    suspend fun loadSdk(): ISdkApi {
-        Log.i(TAG, "Loading SDK")
-        val loadedSdk = sdkSandboxManager.loadSdk(SDK_NAME, Bundle())
-        val sdkApi = ISdkApi.Stub.asInterface(loadedSdk.getInterface())
-        Log.i(TAG, "Loaded successfully")
-        return sdkApi
+    private fun addLogMessage(message: String) {
+        Log.i(TAG, message)
+        logView.append(message + System.lineSeparator())
     }
 
-    fun unloadAllSdks() {
-        sdkSandboxManager
-            .getSandboxedSdks()
-            .mapNotNull(SandboxedSdkCompat::getSdkInfo)
-            .map(SandboxedSdkInfo::name)
-            .forEach(sdkSandboxManager::unloadSdk)
+    private fun setupLoadSdkButton() {
+        val loadSdkButton = findViewById<Button>(R.id.loadSdkButton)
+        loadSdkButton.setOnClickListener {
+            lifecycleScope.launch {
+                try {
+                    addLogMessage("Loading TestSDK...")
+                    val testSdk = api.loadTestSdk()
+                    addLogMessage("TestSDK Message: " + testSdk.getMessage())
+                    addLogMessage("Successfully loaded TestSDK")
+                } catch (ex: LoadSdkCompatException) {
+                    addLogMessage("Failed to load TestSDK: " + ex.message)
+                }
+            }
+        }
+    }
+
+    private fun setupUnloadSdkButton() {
+        val unloadSdkButton = findViewById<Button>(R.id.unloadSdkButton)
+        unloadSdkButton.setOnClickListener {
+            api.unloadTestSdk()
+            addLogMessage("Unloaded TestSDK")
+        }
+    }
+
+    private fun setupRegisterAppSdkButton() {
+        val registerAppSdkButton = findViewById<Button>(R.id.registerAppSdkButton)
+        registerAppSdkButton.setOnClickListener {
+            try {
+                addLogMessage("Registering AppOwnedSdk...")
+                api.registerAppOwnedSdk(appOwnedSdk)
+                addLogMessage("Successfully registered AppOwnedSdk")
+            } catch (ex: Throwable) {
+                addLogMessage("Failed to register AppOwnedSdk: " + ex.message)
+            }
+        }
+    }
+
+    private fun setupUnregisterAppSdkButton() {
+        val unregisterAppSdkButton = findViewById<Button>(R.id.unregisterAppSdkButton)
+        unregisterAppSdkButton.setOnClickListener {
+            api.unregisterAppOwnedSdk(appOwnedSdk.getName())
+            addLogMessage("Unregistered AppOwnedSdk")
+        }
+    }
+
+    private fun setupGetSandboxedSdksButton() {
+        val getSandboxedSdksButton = findViewById<Button>(R.id.getSandboxedSdksButton)
+        getSandboxedSdksButton.setOnClickListener {
+            val sdks = api.getSandboxedSdks()
+            addLogMessage("GetSandboxedSdks results (${sdks.size}):")
+            sdks.forEach {
+                addLogMessage("   SDK Package: ${it.getSdkInfo()?.name}")
+                addLogMessage("   SDK Version: ${it.getSdkInfo()?.version}")
+                val testSdk = toTestSdk(it.getInterface())
+                if (testSdk != null) {
+                    addLogMessage("   SDK Message: ${testSdk.getMessage()}")
+                }
+            }
+        }
+    }
+
+    private fun setupGetAppSdksButton() {
+        val getAppSdksButton = findViewById<Button>(R.id.getAppSdksButton)
+        getAppSdksButton.setOnClickListener {
+            val sdks = api.getAppOwnedSdks()
+            addLogMessage("GetAppSdks results (${sdks.size}):")
+            sdks.forEach {
+                addLogMessage("   AppOwned SDK Package: ${it.getName()}")
+                addLogMessage("   AppOwned SDK Version: ${it.getVersion()}")
+                val appOwnedSdk = toAppOwnedSdk(it.getInterface())
+                if (appOwnedSdk != null) {
+                    addLogMessage("   AppOwned SDK Message: ${appOwnedSdk.getMessage(42)}")
+                }
+            }
+        }
+    }
+
+    private fun toTestSdk(sdkInterface: IBinder?): ISdkApi? {
+        return if (ISdkApi.DESCRIPTOR == sdkInterface?.interfaceDescriptor) {
+            ISdkApi.Stub.asInterface(sdkInterface)
+        } else {
+            null
+        }
+    }
+
+    private fun toAppOwnedSdk(appInterface: IBinder?): IAppSdk? {
+        return if (IAppSdk.DESCRIPTOR == appInterface?.interfaceDescriptor) {
+            IAppSdk.Stub.asInterface(appInterface)
+        } else {
+            null
+        }
     }
 
     companion object {
         private const val TAG = "TestMainActivity"
-
-        /** Name of the SDK to be loaded. */
-        private const val SDK_NAME = "androidx.privacysandbox.sdkruntime.integrationtest.sdk"
     }
 }
diff --git a/privacysandbox/sdkruntime/integration-tests/testapp/src/main/res/layout/activity_main.xml b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..16a7ea9
--- /dev/null
+++ b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/res/layout/activity_main.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 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.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_gravity="center_horizontal"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <Button
+                android:id="@+id/loadSdkButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/load_sdk"
+                android:textAllCaps="false"/>
+            <Button
+                android:id="@+id/unloadSdkButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/unload_sdk"
+                android:textAllCaps="false"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <Button
+                android:id="@+id/registerAppSdkButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/register_app_sdk"
+                android:textAllCaps="false"/>
+            <Button
+                android:id="@+id/unregisterAppSdkButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/unregister_app_sdk"
+                android:textAllCaps="false"/>
+        </LinearLayout>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <Button
+                android:id="@+id/getSandboxedSdksButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/get_sandboxed_sdks"
+                android:textAllCaps="false"/>
+            <Button
+                android:id="@+id/getAppSdksButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/get_app_sdks"
+                android:textAllCaps="false"/>
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <TextView
+        android:layout_gravity="center_horizontal"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:text="@string/log"/>
+    <TextView
+        android:id="@+id/logView"
+        android:gravity="bottom"
+        android:scrollbars="vertical"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:background="#FFFFFF"
+        android:textColor="#000000"/>
+</LinearLayout>
diff --git a/privacysandbox/sdkruntime/integration-tests/testapp/src/main/res/values/strings.xml b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/res/values/strings.xml
new file mode 100644
index 0000000..e809f95
--- /dev/null
+++ b/privacysandbox/sdkruntime/integration-tests/testapp/src/main/res/values/strings.xml
@@ -0,0 +1,25 @@
+<!--
+  Copyright 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.
+  -->
+
+<resources>
+    <string name="load_sdk">Load SDK</string>
+    <string name="unload_sdk">Unload SDK</string>
+    <string name="register_app_sdk">Register AppOwned SDK</string>
+    <string name="unregister_app_sdk">Unregister AppOwned SDK</string>
+    <string name="get_sandboxed_sdks">Get Sandboxed Sdks</string>
+    <string name="get_app_sdks">Get AppOwned Sdks</string>
+    <string name="log">Log: </string>
+</resources>
diff --git a/privacysandbox/sdkruntime/integration-tests/testsdk/src/main/java/androidx/privacysandbox/sdkruntime/integration/testsdk/TestSdk.kt b/privacysandbox/sdkruntime/integration-tests/testsdk/src/main/java/androidx/privacysandbox/sdkruntime/integration/testsdk/TestSdk.kt
index a574fff..9e411c3 100644
--- a/privacysandbox/sdkruntime/integration-tests/testsdk/src/main/java/androidx/privacysandbox/sdkruntime/integration/testsdk/TestSdk.kt
+++ b/privacysandbox/sdkruntime/integration-tests/testsdk/src/main/java/androidx/privacysandbox/sdkruntime/integration/testsdk/TestSdk.kt
@@ -20,6 +20,12 @@
 import androidx.privacysandbox.sdkruntime.integration.testaidl.ISdkApi
 
 class TestSdk : ISdkApi.Stub() {
+
+    override fun getMessage(): String {
+        Log.i(TAG, "TestSdk#getMessage()")
+        return MESSAGE
+    }
+
     override fun invert(value: Boolean): Boolean {
         Log.i(TAG, "TestSdk#invert($value)")
         return !value
@@ -27,5 +33,6 @@
 
     companion object {
         private const val TAG = "TestSdk"
+        private const val MESSAGE = "Message from TestSDK"
     }
 }
diff --git a/privacysandbox/tools/integration-tests/README.md b/privacysandbox/tools/integration-tests/README.md
new file mode 100644
index 0000000..9a99338
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/README.md
@@ -0,0 +1,14 @@
+# Privacy Sandbox tools integration tests
+
+Integration test (app and SDK) using the App-SDK bridge (the "shim library").
+
+testsdk/ contains an SDK (using the API Compiler), manually setting AidlInput and FrameworkAidlInput
+instead of using
+androidx.privacysandbox.library (https://developer.android.com/jetpack/androidx/releases/privacysandbox-plugins)
+since we want to use the HEAD versions of the shim tools.
+
+testsdk-asb/ wraps the test SDK in an Android SDK Bundle (ASB), using
+com.android.privacy-sandbox-sdk.
+
+testapp/ contains a simple Android app with a dependency on testsdk-asb, using the HEAD version of
+the API Generator. This is the project which will contain the integration tests themselves.
diff --git a/privacysandbox/tools/integration-tests/testapp/build.gradle b/privacysandbox/tools/integration-tests/testapp/build.gradle
new file mode 100644
index 0000000..d9a24ab
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testapp/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright 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.
+ */
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.application")
+    id("org.jetbrains.kotlin.android")
+}
+
+android {
+    namespace "androidx.privacysandbox.tools.integration.testapp"
+
+    defaultConfig {
+        applicationId "androidx.privacysandbox.tools.integration.testapp"
+        minSdk 33
+        compileSdk 35
+    }
+    experimentalProperties["android.privacySandboxSdk.apiGenerator"] =
+            project.dependencies.create(project(":privacysandbox:tools:tools-apigenerator"))
+    experimentalProperties["android.privacySandboxSdk.apiGenerator.generatedRuntimeDependencies"] =
+            [libs.kotlinStdlib.get(),
+             libs.kotlinCoroutinesAndroid.get(),
+             libs.kotlinCoroutinesCore.get()]
+    privacySandbox {
+            enable = true
+    }
+}
+
+dependencies {
+    implementation(project(":privacysandbox:tools:integration-tests:testsdk-asb"))
+
+    implementation(project(":privacysandbox:sdkruntime:sdkruntime-client"))
+    implementation(project(":privacysandbox:sdkruntime:sdkruntime-core"))
+
+    implementation("androidx.core:core-ktx:1.9.0")
+    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.5.1")
+    implementation("androidx.appcompat:appcompat:1.6.1")
+    implementation(project(":activity:activity"))
+    implementation(libs.material)
+    implementation(libs.constraintLayout)
+    implementation(libs.kotlinCoroutinesAndroid)
+}
diff --git a/privacysandbox/tools/integration-tests/testapp/src/main/AndroidManifest.xml b/privacysandbox/tools/integration-tests/testapp/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..8bf6137
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <application
+        android:label="Privacy Sandbox Tools Integration Test"
+        android:theme="@style/Theme.Androidx">
+        <activity
+            android:name=".MainActivity"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+</manifest>
diff --git a/privacysandbox/tools/integration-tests/testapp/src/main/java/androidx/privacysandbox/tools/integration/testapp/MainActivity.kt b/privacysandbox/tools/integration-tests/testapp/src/main/java/androidx/privacysandbox/tools/integration/testapp/MainActivity.kt
new file mode 100644
index 0000000..5a2e26e
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testapp/src/main/java/androidx/privacysandbox/tools/integration/testapp/MainActivity.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 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 androidx.privacysandbox.tools.integration.testapp
+
+import android.os.Bundle
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.lifecycleScope
+import androidx.privacysandbox.sdkruntime.client.SdkSandboxManagerCompat
+import androidx.privacysandbox.tools.integration.testsdk.MySdk
+import androidx.privacysandbox.tools.integration.testsdk.MySdkFactory.wrapToMySdk
+import kotlinx.coroutines.launch
+
+class MainActivity : AppCompatActivity() {
+    private lateinit var sdk: MySdk
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_main)
+
+        lifecycleScope.launch {
+            sdk = loadSdk()
+            Log.e("Test App MainActivity", "Sum = ${sdk.doSum(42, 2)}")
+        }
+    }
+
+    suspend fun loadSdk(): MySdk {
+        val sandboxManagerCompat = SdkSandboxManagerCompat.from(this)
+        val sandboxedSdk =
+            sandboxManagerCompat.loadSdk(
+                "androidx.privacysandbox.tools.integration.sdk",
+                Bundle.EMPTY
+            )
+        return wrapToMySdk(sandboxedSdk.getInterface()!!)
+    }
+}
diff --git a/privacysandbox/tools/integration-tests/testapp/src/main/res/layout/activity_main.xml b/privacysandbox/tools/integration-tests/testapp/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..90db0fa
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testapp/src/main/res/layout/activity_main.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright 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.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".MainActivity">
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/privacysandbox/tools/integration-tests/testapp/src/main/res/values-night/themes.xml b/privacysandbox/tools/integration-tests/testapp/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..874d812
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testapp/src/main/res/values-night/themes.xml
@@ -0,0 +1,31 @@
+<!--
+  Copyright 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.
+  -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.Androidx" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_200</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/black</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_200</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+    </style>
+</resources>
diff --git a/pdf/integration-tests/testapp/src/main/res/values-night/themes.xml b/privacysandbox/tools/integration-tests/testapp/src/main/res/values/colors.xml
similarity index 61%
rename from pdf/integration-tests/testapp/src/main/res/values-night/themes.xml
rename to privacysandbox/tools/integration-tests/testapp/src/main/res/values/colors.xml
index df40dfd..cb4c15d 100644
--- a/pdf/integration-tests/testapp/src/main/res/values-night/themes.xml
+++ b/privacysandbox/tools/integration-tests/testapp/src/main/res/values/colors.xml
@@ -1,4 +1,4 @@
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
   Copyright 2024 The Android Open Source Project
 
   Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,10 +14,12 @@
   limitations under the License.
   -->
 
-<resources xmlns:tools="http://schemas.android.com/tools">
-    <!-- Base application theme. -->
-    <style name="Base.Theme.Androidx" parent="Theme.Material3.DayNight.NoActionBar">
-        <!-- Customize your dark theme here. -->
-        <!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
-    </style>
+<resources>
+    <color name="purple_200">#FFBB86FC</color>
+    <color name="purple_500">#FF6200EE</color>
+    <color name="purple_700">#FF3700B3</color>
+    <color name="teal_200">#FF03DAC5</color>
+    <color name="teal_700">#FF018786</color>
+    <color name="black">#FF000000</color>
+    <color name="white">#FFFFFFFF</color>
 </resources>
\ No newline at end of file
diff --git a/privacysandbox/tools/integration-tests/testapp/src/main/res/values/themes.xml b/privacysandbox/tools/integration-tests/testapp/src/main/res/values/themes.xml
new file mode 100644
index 0000000..9a57bc00
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testapp/src/main/res/values/themes.xml
@@ -0,0 +1,31 @@
+<!--
+  Copyright 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.
+  -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <!-- Base application theme. -->
+    <style name="Theme.Androidx" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
+        <!-- Primary brand color. -->
+        <item name="colorPrimary">@color/purple_500</item>
+        <item name="colorPrimaryVariant">@color/purple_700</item>
+        <item name="colorOnPrimary">@color/white</item>
+        <!-- Secondary brand color. -->
+        <item name="colorSecondary">@color/teal_200</item>
+        <item name="colorSecondaryVariant">@color/teal_700</item>
+        <item name="colorOnSecondary">@color/black</item>
+        <!-- Status bar color. -->
+        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/privacysandbox/tools/integration-tests/testsdk-asb/build.gradle b/privacysandbox/tools/integration-tests/testsdk-asb/build.gradle
new file mode 100644
index 0000000..bf1e003
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testsdk-asb/build.gradle
@@ -0,0 +1,46 @@
+/*
+ * Copyright 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.
+ */
+
+import androidx.build.AndroidXConfig
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.privacy-sandbox-sdk")
+}
+
+android {
+    namespace = "androidx.privacysandbox.tools.integration.testsdk-asb"
+
+    // TODO(b/357865033): AndroidXPlugin should eventually set compileSdkVersion and
+    //  buildToolsVersion for this plugin. We shouldn't be setting these manually.
+    compileSdk = 35
+    buildToolsVersion AndroidXConfig.getDefaultAndroidConfig(project).getBuildToolsVersion()
+
+    bundle {
+        packageName = "androidx.privacysandbox.tools.integration.sdk"
+        compatSdkProviderClassName = "androidx.privacysandbox.tools.integration.testsdk.MySdkSandboxProvider"
+        // This is the entry point class to our SDK in the SDK Runtime. It enables backward
+        // compatibility support.
+        sdkProviderClassName = "androidx.privacysandbox.sdkruntime.provider.SandboxedSdkProviderAdapter"
+        setVersion(1, 0, 0)
+
+    }
+
+}
+
+dependencies {
+    include project(":privacysandbox:tools:integration-tests:testsdk")
+}
diff --git a/privacysandbox/tools/integration-tests/testsdk/build.gradle b/privacysandbox/tools/integration-tests/testsdk/build.gradle
new file mode 100644
index 0000000..a4eef17
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testsdk/build.gradle
@@ -0,0 +1,98 @@
+/*
+ * Copyright 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.
+ */
+
+import androidx.build.KotlinTarget
+
+plugins {
+    id("AndroidXPlugin")
+    id("com.android.library")
+    id("org.jetbrains.kotlin.android")
+    id("com.google.devtools.ksp")
+}
+
+abstract class AidlInput implements CommandLineArgumentProvider {
+    // Use the version of the build tools as the input to allow caching across platforms
+    @Internal
+    abstract RegularFileProperty getAidl()
+
+    @Input
+    abstract Property<String> getBuildToolsVersion()
+
+    @Override
+    Iterable<String> asArguments() {
+        return Collections.singleton("aidl_compiler_path=" + getAidl().get().asFile.absolutePath)
+    }
+}
+
+abstract class FrameworkAidlInput implements CommandLineArgumentProvider {
+
+    @Internal
+    abstract RegularFileProperty getFrameworkAidl()
+
+    @Input
+    abstract Property<String> getPlatformSdk()
+
+    @Override
+    Iterable<String> asArguments() {
+        return Collections.singleton(
+                "framework_aidl_path=" + getFrameworkAidl().get().asFile.absolutePath
+        )
+    }
+}
+
+AidlInput aidlInput = project.objects.newInstance(AidlInput)
+FrameworkAidlInput frameworkAidlInput = project.objects.newInstance(FrameworkAidlInput)
+
+androidComponents {
+    finalizeDsl { dsl ->
+        aidlInput.aidl.set(androidComponents.sdkComponents.aidl.flatMap { it.executable })
+        aidlInput.buildToolsVersion.set(androidComponents.sdkComponents.aidl.flatMap { it.version })
+
+        frameworkAidlInput.frameworkAidl.set(
+                androidComponents.sdkComponents.aidl.flatMap { it.framework })
+        frameworkAidlInput.platformSdk.set(
+                frameworkAidlInput.frameworkAidl.map {
+                    it.asFile.parentFile.absolutePath.toString()
+                }
+        )
+    }
+}
+
+android {
+    namespace = "androidx.privacysandbox.tools.integration.testsdk"
+
+    defaultConfig {
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+        ksp {
+            arg(aidlInput)
+            arg(frameworkAidlInput)
+        }
+    }
+}
+
+dependencies {
+    ksp(project(":privacysandbox:tools:tools-apicompiler"))
+    implementation(project(":privacysandbox:tools:tools"))
+    implementation(project(":privacysandbox:sdkruntime:sdkruntime-provider"))
+
+    implementation(libs.kotlinCoroutinesAndroid)
+    implementation(libs.kotlinCoroutinesCore)
+}
+
+androidx {
+    kotlinTarget = KotlinTarget.KOTLIN_1_9
+}
diff --git a/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdk.kt b/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdk.kt
new file mode 100644
index 0000000..4f2410e
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdk.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 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 androidx.privacysandbox.tools.integration.testsdk
+
+import android.content.Context
+import androidx.privacysandbox.tools.PrivacySandboxService
+
+@PrivacySandboxService
+interface MySdk {
+    suspend fun doSum(x: Int, y: Int): Int
+}
+
+class MySdkImpl(private val context: Context) : MySdk {
+    override suspend fun doSum(x: Int, y: Int): Int {
+        return x + y
+    }
+}
diff --git a/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdkSandboxProvider.kt b/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdkSandboxProvider.kt
new file mode 100644
index 0000000..f50e433
--- /dev/null
+++ b/privacysandbox/tools/integration-tests/testsdk/src/main/java/androidx/privacysandbox/tools/integration/testsdk/MySdkSandboxProvider.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright 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 androidx.privacysandbox.tools.integration.testsdk
+
+import android.content.Context
+
+class MySdkSandboxProvider : AbstractSandboxedSdkProviderCompat() {
+    override fun createMySdk(context: Context): MySdk = MySdkImpl(context)
+}
diff --git a/room/room-common/api/current.ignore b/room/room-common/api/current.ignore
index 2aeb40f..e328ae6 100644
--- a/room/room-common/api/current.ignore
+++ b/room/room-common/api/current.ignore
@@ -1,27 +1,17 @@
 // Baseline format: 1.0
-ChangedType: androidx.room.AutoMigration#spec():
-    Method androidx.room.AutoMigration.spec has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Database#entities():
-    Method androidx.room.Database.entities has changed return type from java.lang.Class<?>[] to kotlin.reflect.KClass<?>[]
-ChangedType: androidx.room.Database#views():
-    Method androidx.room.Database.views has changed return type from java.lang.Class<?>[] to kotlin.reflect.KClass<?>[]
-ChangedType: androidx.room.Delete#entity():
-    Method androidx.room.Delete.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.ForeignKey#entity():
-    Method androidx.room.ForeignKey.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Fts4#contentEntity():
-    Method androidx.room.Fts4.contentEntity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Insert#entity():
-    Method androidx.room.Insert.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Junction#value():
-    Method androidx.room.Junction.value has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.RawQuery#observedEntities():
-    Method androidx.room.RawQuery.observedEntities has changed return type from java.lang.Class<?>[] to kotlin.reflect.KClass<?>[]
-ChangedType: androidx.room.Relation#entity():
-    Method androidx.room.Relation.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.TypeConverters#value():
-    Method androidx.room.TypeConverters.value has changed return type from java.lang.Class<?>[] to kotlin.reflect.KClass<?>[]
-ChangedType: androidx.room.Update#entity():
-    Method androidx.room.Update.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Upsert#entity():
-    Method androidx.room.Upsert.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
+ChangedValue: androidx.room.AutoMigration#spec():
+    Method androidx.room.AutoMigration.spec has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Delete#entity():
+    Method androidx.room.Delete.entity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Fts4#contentEntity():
+    Method androidx.room.Fts4.contentEntity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Insert#entity():
+    Method androidx.room.Insert.entity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Relation#associateBy():
+    Method androidx.room.Relation.associateBy has changed value from androidx.room.Junction(java.lang.Object) to androidx.room.Junction(Any::class)
+ChangedValue: androidx.room.Relation#entity():
+    Method androidx.room.Relation.entity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Update#entity():
+    Method androidx.room.Update.entity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Upsert#entity():
+    Method androidx.room.Upsert.entity has changed value from java.lang.Object to Any::class
diff --git a/room/room-common/api/current.txt b/room/room-common/api/current.txt
index 22e0ef2..cb9d6da 100644
--- a/room/room-common/api/current.txt
+++ b/room/room-common/api/current.txt
@@ -3,7 +3,7 @@
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface AutoMigration {
     method public abstract int from();
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> spec() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> spec() default Any::class;
     method public abstract int to();
     property public abstract int from;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> spec;
@@ -103,7 +103,7 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface Delete {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity;
   }
 
@@ -189,7 +189,7 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface Fts4 {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> contentEntity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> contentEntity() default Any::class;
     method public abstract String languageId() default "";
     method public abstract androidx.room.FtsOptions.MatchInfo matchInfo() default androidx.room.FtsOptions.MatchInfo.FTS4;
     method public abstract String[] notIndexed();
@@ -245,7 +245,7 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface Insert {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     method @androidx.room.OnConflictStrategy public abstract int onConflict() default androidx.room.OnConflictStrategy.ABORT;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity;
     property @androidx.room.OnConflictStrategy public abstract int onConflict;
@@ -323,8 +323,8 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface Relation {
-    method public abstract androidx.room.Junction associateBy() default androidx.room.Junction(java.lang.Object);
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract androidx.room.Junction associateBy() default androidx.room.Junction(Any::class);
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     method public abstract String entityColumn();
     method public abstract String parentColumn();
     method public abstract String[] projection();
@@ -408,14 +408,14 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface Update {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     method @androidx.room.OnConflictStrategy public abstract int onConflict() default androidx.room.OnConflictStrategy.ABORT;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity;
     property @androidx.room.OnConflictStrategy public abstract int onConflict;
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface Upsert {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity;
   }
 
diff --git a/room/room-common/api/restricted_current.ignore b/room/room-common/api/restricted_current.ignore
index 2aeb40f..e328ae6 100644
--- a/room/room-common/api/restricted_current.ignore
+++ b/room/room-common/api/restricted_current.ignore
@@ -1,27 +1,17 @@
 // Baseline format: 1.0
-ChangedType: androidx.room.AutoMigration#spec():
-    Method androidx.room.AutoMigration.spec has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Database#entities():
-    Method androidx.room.Database.entities has changed return type from java.lang.Class<?>[] to kotlin.reflect.KClass<?>[]
-ChangedType: androidx.room.Database#views():
-    Method androidx.room.Database.views has changed return type from java.lang.Class<?>[] to kotlin.reflect.KClass<?>[]
-ChangedType: androidx.room.Delete#entity():
-    Method androidx.room.Delete.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.ForeignKey#entity():
-    Method androidx.room.ForeignKey.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Fts4#contentEntity():
-    Method androidx.room.Fts4.contentEntity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Insert#entity():
-    Method androidx.room.Insert.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Junction#value():
-    Method androidx.room.Junction.value has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.RawQuery#observedEntities():
-    Method androidx.room.RawQuery.observedEntities has changed return type from java.lang.Class<?>[] to kotlin.reflect.KClass<?>[]
-ChangedType: androidx.room.Relation#entity():
-    Method androidx.room.Relation.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.TypeConverters#value():
-    Method androidx.room.TypeConverters.value has changed return type from java.lang.Class<?>[] to kotlin.reflect.KClass<?>[]
-ChangedType: androidx.room.Update#entity():
-    Method androidx.room.Update.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
-ChangedType: androidx.room.Upsert#entity():
-    Method androidx.room.Upsert.entity has changed return type from java.lang.Class<?> to kotlin.reflect.KClass<?>
+ChangedValue: androidx.room.AutoMigration#spec():
+    Method androidx.room.AutoMigration.spec has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Delete#entity():
+    Method androidx.room.Delete.entity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Fts4#contentEntity():
+    Method androidx.room.Fts4.contentEntity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Insert#entity():
+    Method androidx.room.Insert.entity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Relation#associateBy():
+    Method androidx.room.Relation.associateBy has changed value from androidx.room.Junction(java.lang.Object) to androidx.room.Junction(Any::class)
+ChangedValue: androidx.room.Relation#entity():
+    Method androidx.room.Relation.entity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Update#entity():
+    Method androidx.room.Update.entity has changed value from java.lang.Object to Any::class
+ChangedValue: androidx.room.Upsert#entity():
+    Method androidx.room.Upsert.entity has changed value from java.lang.Object to Any::class
diff --git a/room/room-common/api/restricted_current.txt b/room/room-common/api/restricted_current.txt
index e824b58..4ce830e 100644
--- a/room/room-common/api/restricted_current.txt
+++ b/room/room-common/api/restricted_current.txt
@@ -9,7 +9,7 @@
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface AutoMigration {
     method public abstract int from();
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> spec() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> spec() default Any::class;
     method public abstract int to();
     property public abstract int from;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> spec;
@@ -109,7 +109,7 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface Delete {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity;
   }
 
@@ -195,7 +195,7 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.CLASS) public @interface Fts4 {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> contentEntity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> contentEntity() default Any::class;
     method public abstract String languageId() default "";
     method public abstract androidx.room.FtsOptions.MatchInfo matchInfo() default androidx.room.FtsOptions.MatchInfo.FTS4;
     method public abstract String[] notIndexed();
@@ -251,7 +251,7 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface Insert {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     method @androidx.room.OnConflictStrategy public abstract int onConflict() default androidx.room.OnConflictStrategy.ABORT;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity;
     property @androidx.room.OnConflictStrategy public abstract int onConflict;
@@ -329,8 +329,8 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets={kotlin.annotation.AnnotationTarget.FIELD, kotlin.annotation.AnnotationTarget.FUNCTION}) public @interface Relation {
-    method public abstract androidx.room.Junction associateBy() default androidx.room.Junction(java.lang.Object);
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract androidx.room.Junction associateBy() default androidx.room.Junction(Any::class);
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     method public abstract String entityColumn();
     method public abstract String parentColumn();
     method public abstract String[] projection();
@@ -424,14 +424,14 @@
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface Update {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     method @androidx.room.OnConflictStrategy public abstract int onConflict() default androidx.room.OnConflictStrategy.ABORT;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity;
     property @androidx.room.OnConflictStrategy public abstract int onConflict;
   }
 
   @kotlin.annotation.Retention(kotlin.annotation.AnnotationRetention.BINARY) @kotlin.annotation.Target(allowedTargets=kotlin.annotation.AnnotationTarget.FUNCTION) public @interface Upsert {
-    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default java.lang.Object;
+    method public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity() default Any::class;
     property public abstract kotlin.reflect.KClass<? extends java.lang.Object?> entity;
   }
 
diff --git a/settings.gradle b/settings.gradle
index 00a4f6b..c02141b 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -859,6 +859,9 @@
 includeProject(":privacysandbox:sdkruntime:test-sdks:v4", [BuildType.MAIN])
 includeProject(":privacysandbox:sdkruntime:test-sdks:v5", [BuildType.MAIN])
 includeProject(":privacysandbox:sdkruntime:test-sdks:v6", [BuildType.MAIN])
+includeProject(":privacysandbox:tools:integration-tests:testapp", [BuildType.MAIN])
+includeProject(":privacysandbox:tools:integration-tests:testsdk", [BuildType.MAIN])
+includeProject(":privacysandbox:tools:integration-tests:testsdk-asb", [BuildType.MAIN])
 includeProject(":privacysandbox:tools:tools", [BuildType.MAIN])
 includeProject(":privacysandbox:tools:tools-apicompiler", [BuildType.MAIN])
 includeProject(":privacysandbox:tools:tools-apigenerator", [BuildType.MAIN])
diff --git a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ImageWithScrimPainter.kt b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ImageWithScrimPainter.kt
index 3e96b65..fe3d7e6 100644
--- a/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ImageWithScrimPainter.kt
+++ b/wear/compose/compose-material-core/src/main/java/androidx/wear/compose/materialcore/ImageWithScrimPainter.kt
@@ -26,13 +26,20 @@
 /**
  * A painter which wraps another [Painter] for drawing a background image and a [Brush] which is
  * used to create an effect over the image to ensure that text drawn over it will be legible.
+ *
+ * This painter is intended for a background, and the size param if non-null will override the
+ * Painters intrinsicSize.
+ *
+ * For more control of the background image, an image loading library like Coil should be used which
+ * allows explicit contentScale handling.
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 class ImageWithScrimPainter(
     val imagePainter: Painter,
     val brush: Brush,
     private var scrimAlpha: Float = 1.0f,
-    private var alpha: Float = 1.0f
+    private var alpha: Float = 1.0f,
+    private val forcedSize: Size? = null,
 ) : Painter() {
 
     private var colorFilter: ColorFilter? = null
@@ -64,6 +71,7 @@
         if (brush != other.brush) return false
         if (scrimAlpha != other.scrimAlpha) return false
         if (alpha != other.alpha) return false
+        if (forcedSize != other.forcedSize) return false
 
         return true
     }
@@ -73,14 +81,22 @@
         result = 31 * result + brush.hashCode()
         result = 31 * result + scrimAlpha.hashCode()
         result = 31 * result + alpha.hashCode()
+        result = 31 * result + forcedSize.hashCode()
         return result
     }
 
     override fun toString(): String {
         return "ImageWithScrimPainter(imagePainter=$imagePainter, brush=$brush, " +
-            "scrimAlpha=$scrimAlpha, alpha=$alpha)"
+            "scrimAlpha=$scrimAlpha, alpha=$alpha, forcedSize=$forcedSize)"
     }
 
-    /** Size of the combined painter, return Unspecified to allow us to fill the available space */
-    override val intrinsicSize: Size = imagePainter.intrinsicSize
+    /**
+     * Size of the combined painter. Returns imagePainter.intrinsicSize unless size is non-null in
+     * constructor.
+     * - [Size.Unspecified] - the composable size should be used without considering the Painter
+     *   size. This likely involves scaling of the painter.
+     * - [Painter.intrinsicSize] - the Painter's size should be used, this likely increases the size
+     *   of the component.
+     */
+    override val intrinsicSize: Size = forcedSize ?: imagePainter.intrinsicSize
 }
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Card.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Card.kt
index 5bf20cf..ae42a46 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Card.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Card.kt
@@ -50,6 +50,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.wear.compose.materialcore.ImageWithScrimPainter
 import kotlin.math.min
 
 /**
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Chip.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Chip.kt
index 6048051..de80846 100644
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Chip.kt
+++ b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/Chip.kt
@@ -60,6 +60,7 @@
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
+import androidx.wear.compose.materialcore.ImageWithScrimPainter
 
 /**
  * Base level Wear Material [Chip] that offers a single slot to take any content.
diff --git a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ImageWithScrimPainter.kt b/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ImageWithScrimPainter.kt
deleted file mode 100644
index 4b626b8..0000000
--- a/wear/compose/compose-material/src/main/java/androidx/wear/compose/material/ImageWithScrimPainter.kt
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright 2021 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 androidx.wear.compose.material
-
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.Brush
-import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.drawscope.DrawScope
-import androidx.compose.ui.graphics.painter.Painter
-
-/**
- * A painter which takes wraps another [Painter] for drawing a background image and a [Brush] which
- * is used to create an effect over the image to ensure that text drawn over it will be legible.
- */
-internal class ImageWithScrimPainter(
-    val imagePainter: Painter,
-    val brush: Brush,
-    private var scrimAlpha: Float = 1.0f,
-    private var alpha: Float = 1.0f
-) : Painter() {
-
-    private var colorFilter: ColorFilter? = null
-
-    override fun DrawScope.onDraw() {
-        val size = this.size
-        with(imagePainter) { draw(size = size, alpha = alpha, colorFilter = colorFilter) }
-        drawRect(brush = brush, alpha = scrimAlpha * alpha, colorFilter = colorFilter)
-    }
-
-    override fun applyAlpha(alpha: Float): Boolean {
-        this.alpha = alpha
-        return true
-    }
-
-    override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
-        this.colorFilter = colorFilter
-        return true
-    }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other == null) return false
-        if (this::class != other::class) return false
-
-        other as ImageWithScrimPainter
-
-        if (imagePainter != other.imagePainter) return false
-        if (brush != other.brush) return false
-        if (scrimAlpha != other.scrimAlpha) return false
-
-        return true
-    }
-
-    override fun hashCode(): Int {
-        var result = imagePainter.hashCode()
-        result = 31 * result + brush.hashCode()
-        result = 31 * result + scrimAlpha.hashCode()
-        return result
-    }
-
-    override fun toString(): String {
-        return "ImageWithScrimPainter(imagePainter=$imagePainter, brush=$brush, " +
-            "scrimAlpha=$scrimAlpha)"
-    }
-
-    /** Size of the combined painter, return Unspecified to allow us to fill the available space */
-    override val intrinsicSize: Size = imagePainter.intrinsicSize
-}
diff --git a/wear/compose/compose-material3/api/current.txt b/wear/compose/compose-material3/api/current.txt
index 44a766f..265b837 100644
--- a/wear/compose/compose-material3/api/current.txt
+++ b/wear/compose/compose-material3/api/current.txt
@@ -67,7 +67,7 @@
     method public float getLargeIconSize();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
     method public float getSmallIconSize();
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors imageBackgroundButtonColors(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush, optional long contentColor, optional long secondaryContentColor, optional long iconColor, optional long disabledContentColor, optional long disabledSecondaryContentColor, optional long disabledIconColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors imageBackgroundButtonColors(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush, optional long contentColor, optional long secondaryContentColor, optional long iconColor, optional long disabledContentColor, optional long disabledSecondaryContentColor, optional long disabledIconColor, optional androidx.compose.ui.geometry.Size? forcedSize);
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedButtonBorder(boolean enabled, optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors outlinedButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors outlinedButtonColors(optional long contentColor, optional long secondaryContentColor, optional long iconColor, optional long disabledContentColor, optional long disabledSecondaryContentColor, optional long disabledIconColor);
@@ -143,13 +143,17 @@
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors cardColors(optional long containerColor, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor, optional long subtitleColor);
     method public float getAppImageSize();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method public float getImageBottomPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getImageContentPadding();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors imageCardColors(androidx.compose.ui.graphics.painter.Painter containerPainter, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor, optional long subtitleColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush, optional androidx.compose.ui.geometry.Size? forcedSize);
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedCardBorder(optional long outlineColor, optional float borderWidth);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors outlinedCardColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors outlinedCardColors(optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor, optional long subtitleColor);
     property public final float AppImageSize;
     property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float ImageBottomPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues ImageContentPadding;
     field public static final androidx.wear.compose.material3.CardDefaults INSTANCE;
   }
 
diff --git a/wear/compose/compose-material3/api/restricted_current.txt b/wear/compose/compose-material3/api/restricted_current.txt
index 44a766f..265b837 100644
--- a/wear/compose/compose-material3/api/restricted_current.txt
+++ b/wear/compose/compose-material3/api/restricted_current.txt
@@ -67,7 +67,7 @@
     method public float getLargeIconSize();
     method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.Shape getShape();
     method public float getSmallIconSize();
-    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors imageBackgroundButtonColors(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush, optional long contentColor, optional long secondaryContentColor, optional long iconColor, optional long disabledContentColor, optional long disabledSecondaryContentColor, optional long disabledIconColor);
+    method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors imageBackgroundButtonColors(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush, optional long contentColor, optional long secondaryContentColor, optional long iconColor, optional long disabledContentColor, optional long disabledSecondaryContentColor, optional long disabledIconColor, optional androidx.compose.ui.geometry.Size? forcedSize);
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedButtonBorder(boolean enabled, optional long borderColor, optional long disabledBorderColor, optional float borderWidth);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors outlinedButtonColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.ButtonColors outlinedButtonColors(optional long contentColor, optional long secondaryContentColor, optional long iconColor, optional long disabledContentColor, optional long disabledSecondaryContentColor, optional long disabledIconColor);
@@ -143,13 +143,17 @@
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors cardColors(optional long containerColor, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor, optional long subtitleColor);
     method public float getAppImageSize();
     method public androidx.compose.foundation.layout.PaddingValues getContentPadding();
+    method public float getImageBottomPadding();
+    method public androidx.compose.foundation.layout.PaddingValues getImageContentPadding();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors imageCardColors(androidx.compose.ui.graphics.painter.Painter containerPainter, optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor, optional long subtitleColor);
-    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush);
+    method @androidx.compose.runtime.Composable public androidx.compose.ui.graphics.painter.Painter imageWithScrimBackgroundPainter(androidx.compose.ui.graphics.painter.Painter backgroundImagePainter, optional androidx.compose.ui.graphics.Brush backgroundImageScrimBrush, optional androidx.compose.ui.geometry.Size? forcedSize);
     method @androidx.compose.runtime.Composable public androidx.compose.foundation.BorderStroke outlinedCardBorder(optional long outlineColor, optional float borderWidth);
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors outlinedCardColors();
     method @androidx.compose.runtime.Composable public androidx.wear.compose.material3.CardColors outlinedCardColors(optional long contentColor, optional long appNameColor, optional long timeColor, optional long titleColor, optional long subtitleColor);
     property public final float AppImageSize;
     property public final androidx.compose.foundation.layout.PaddingValues ContentPadding;
+    property public final float ImageBottomPadding;
+    property public final androidx.compose.foundation.layout.PaddingValues ImageContentPadding;
     field public static final androidx.wear.compose.material3.CardDefaults INSTANCE;
   }
 
diff --git a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/CardSample.kt b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/CardSample.kt
index a8388f9..f699ff0 100644
--- a/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/CardSample.kt
+++ b/wear/compose/compose-material3/samples/src/main/java/androidx/wear/compose/material3/samples/CardSample.kt
@@ -211,6 +211,7 @@
                 contentColor = MaterialTheme.colorScheme.onSurface,
                 titleColor = MaterialTheme.colorScheme.onSurface
             ),
+        contentPadding = CardDefaults.ImageContentPadding,
         modifier = Modifier.semantics { contentDescription = "Background image" }
     ) {
         Text("Card content")
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonScreenshotTest.kt
index 4f8b9b4..4b23eab 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/ButtonScreenshotTest.kt
@@ -25,6 +25,7 @@
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.painterResource
@@ -80,11 +81,19 @@
 
     @Test fun button_outlined_disabled() = verifyScreenshot() { OutlinedButton(enabled = false) }
 
-    @Test fun button_image_background_enabled() = verifyScreenshot { ImageBackgroundButton() }
+    @Test
+    fun button_image_background_enabled() = verifyScreenshot {
+        ImageBackgroundButton(size = Size.Unspecified)
+    }
 
     @Test
     fun button_image_background_disabled() = verifyScreenshot {
-        ImageBackgroundButton(enabled = false)
+        ImageBackgroundButton(enabled = false, size = Size.Unspecified)
+    }
+
+    @Test
+    fun button_image_background_with_intrinsic_size() = verifyScreenshot {
+        ImageBackgroundButton(size = null)
     }
 
     @Test
@@ -213,7 +222,7 @@
     }
 
     @Composable
-    private fun ImageBackgroundButton(enabled: Boolean = true) {
+    private fun ImageBackgroundButton(enabled: Boolean = true, size: Size?) {
         Button(
             enabled = enabled,
             onClick = {},
@@ -221,7 +230,8 @@
             secondaryLabel = { Text("Secondary Label") },
             colors =
                 ButtonDefaults.imageBackgroundButtonColors(
-                    backgroundImagePainter = painterResource(R.drawable.backgroundimage1)
+                    backgroundImagePainter = painterResource(R.drawable.backgroundimage1),
+                    forcedSize = size
                 ),
             icon = { TestIcon() },
             modifier = Modifier.testTag(TEST_TAG)
diff --git a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CardScreenshotTest.kt b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CardScreenshotTest.kt
index 4648d79..7c87784 100644
--- a/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CardScreenshotTest.kt
+++ b/wear/compose/compose-material3/src/androidTest/kotlin/androidx/wear/compose/material3/CardScreenshotTest.kt
@@ -18,11 +18,13 @@
 
 import android.os.Build
 import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.testutils.assertAgainstGolden
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.res.painterResource
@@ -70,9 +72,31 @@
                                     id =
                                         androidx.wear.compose.material3.test.R.drawable
                                             .backgroundimage1
-                                )
+                                ),
+                            forcedSize = Size.Unspecified
                         )
-                )
+                ),
+            contentPadding = CardDefaults.ImageContentPadding,
+        )
+    }
+
+    @Test
+    fun card_image_background_with_intrinsic_size() = verifyScreenshot {
+        TestCard(
+            colors =
+                CardDefaults.imageCardColors(
+                    containerPainter =
+                        CardDefaults.imageWithScrimBackgroundPainter(
+                            backgroundImagePainter =
+                                painterResource(
+                                    id =
+                                        androidx.wear.compose.material3.test.R.drawable
+                                            .backgroundimage1
+                                ),
+                            forcedSize = null
+                        ),
+                ),
+            contentPadding = CardDefaults.ImageContentPadding,
         )
     }
 
@@ -172,18 +196,24 @@
                                     id =
                                         androidx.wear.compose.material3.test.R.drawable
                                             .backgroundimage1
-                                )
+                                ),
                         )
-                )
+                ),
+            contentPadding = CardDefaults.ImageContentPadding,
         )
     }
 
     @Composable
-    private fun TestCard(enabled: Boolean = true, colors: CardColors = CardDefaults.cardColors()) {
+    private fun TestCard(
+        enabled: Boolean = true,
+        colors: CardColors = CardDefaults.cardColors(),
+        contentPadding: PaddingValues = CardDefaults.ContentPadding
+    ) {
         Card(
             enabled = enabled,
             onClick = {},
             colors = colors,
+            contentPadding = contentPadding,
             modifier = Modifier.testTag(TEST_TAG).width(IntrinsicSize.Max),
         ) {
             Text("Card: Some body content")
@@ -225,6 +255,7 @@
     @Composable
     private fun TestTitleCard(
         enabled: Boolean = true,
+        contentPadding: PaddingValues = CardDefaults.ContentPadding,
         colors: CardColors = CardDefaults.cardColors()
     ) {
         TitleCard(
@@ -233,6 +264,7 @@
             title = { Text("TitleCard") },
             time = { Text("now") },
             colors = colors,
+            contentPadding = contentPadding,
             modifier = Modifier.testTag(TEST_TAG).width(IntrinsicSize.Max),
         ) {
             Text("Some body content and some more body content")
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
index e800100..0cf422b 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Button.kt
@@ -46,6 +46,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.draw.paint
+import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.Shape
@@ -1228,6 +1229,9 @@
      * @param disabledSecondaryContentColor The secondary content color of this [Button] when
      *   disabled, used for secondary label content
      * @param disabledIconColor The icon color of this [Button] when disabled, used for icon content
+     * @param forcedSize The value for [Painter.intrinsicSize], a value of null will respect the
+     *   [backgroundImagePainter] size. Defaults to [Size.Unspecified] which does not affect
+     *   component size.
      */
     @Composable
     fun imageBackgroundButtonColors(
@@ -1258,13 +1262,15 @@
         disabledIconColor: Color =
             ImageButtonTokens.DisabledContentColor.value.toDisabledColor(
                 disabledAlpha = ImageButtonTokens.DisabledContentOpacity
-            )
+            ),
+        forcedSize: Size? = Size.Unspecified,
     ): ButtonColors {
         val backgroundPainter =
             remember(backgroundImagePainter, backgroundImageScrimBrush) {
                 androidx.wear.compose.materialcore.ImageWithScrimPainter(
                     imagePainter = backgroundImagePainter,
-                    brush = backgroundImageScrimBrush
+                    brush = backgroundImageScrimBrush,
+                    forcedSize = forcedSize,
                 )
             }
 
@@ -1275,6 +1281,7 @@
                     imagePainter = backgroundImagePainter,
                     brush = backgroundImageScrimBrush,
                     alpha = disabledContentAlpha,
+                    forcedSize = forcedSize,
                 )
             }
         return ButtonColors(
diff --git a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Card.kt b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Card.kt
index 116cef2..eddedb95 100644
--- a/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Card.kt
+++ b/wear/compose/compose-material3/src/main/java/androidx/wear/compose/material3/Card.kt
@@ -444,9 +444,6 @@
                     subtitle()
                 }
             }
-            if (colors.containerPainter.isImagePainter()) {
-                Spacer(modifier = Modifier.height(12.dp))
-            }
         }
     }
 }
@@ -599,14 +596,14 @@
 
     /**
      * Creates a [CardColors] that represents the default container and content colors used in a
-     * [Card], [AppCard] or [TitleCard] with Image set as a background.
+     * [TitleCard] with Image set as a background.
      *
      * @param containerPainter a Painter which is used for background drawing.
      * @param contentColor the content color of this [Card].
      * @param appNameColor the color used for appName, only applies to [AppCard].
-     * @param timeColor the color used for time, applies to [AppCard] and [TitleCard].
-     * @param titleColor the color used for title, applies to [AppCard] and [TitleCard].
-     * @param subtitleColor the color used for subtitle, applies to [TitleCard].
+     * @param timeColor the color used for time.
+     * @param titleColor the color used for title.
+     * @param subtitleColor the color used for subtitle.
      */
     @Composable
     fun imageCardColors(
@@ -644,15 +641,20 @@
      * @param backgroundImagePainter The [Painter] to use to draw the background of the [Card]
      * @param backgroundImageScrimBrush The [Brush] to use to paint a scrim over the background
      *   image to ensure that any text drawn over the image is legible
+     * @param forcedSize The value for [Painter.intrinsicSize], a value of null will respect the
+     *   [backgroundImagePainter] size. Defaults to [Size.Unspecified] which does not affect
+     *   component size.
      */
     @Composable
     fun imageWithScrimBackgroundPainter(
         backgroundImagePainter: Painter,
-        backgroundImageScrimBrush: Brush = SolidColor(overlayScrimColor)
+        backgroundImageScrimBrush: Brush = SolidColor(overlayScrimColor),
+        forcedSize: Size? = Size.Unspecified,
     ): Painter {
         return ImageWithScrimPainter(
             imagePainter = backgroundImagePainter,
-            brush = backgroundImageScrimBrush
+            brush = backgroundImageScrimBrush,
+            forcedSize = forcedSize,
         )
     }
 
@@ -688,6 +690,21 @@
             bottom = CardVerticalPadding
         )
 
+    /** Additional bottom padding added for TitleCard with an image background */
+    val ImageBottomPadding = 12.dp
+
+    /**
+     * ContentPadding for use with an image background in order to show more of the image. Expected
+     * to be used with TitleCard's with an image background
+     */
+    val ImageContentPadding: PaddingValues =
+        PaddingValues(
+            start = CardHorizontalPadding,
+            top = CardVerticalPadding,
+            end = CardHorizontalPadding,
+            bottom = CardVerticalPadding + ImageBottomPadding
+        )
+
     /** The default size of the app icon/image when used inside a [AppCard]. */
     val AppImageSize: Dp = CardTokens.AppImageSize
 
@@ -724,8 +741,6 @@
 private fun Modifier.cardSizeModifier(): Modifier =
     defaultMinSize(minHeight = CardTokens.ContainerMinHeight).height(IntrinsicSize.Min)
 
-private fun Painter.isImagePainter() = intrinsicSize != Size.Unspecified
-
 /**
  * Represents Colors used in [Card]. Unlike other Material 3 components, Cards do not change their
  * color appearance when they are disabled. All colors remain the same in enabled and disabled
diff --git a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/AnimatedTextBenchmark.kt b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/AnimatedTextBenchmark.kt
index 5ea556a..925f27e 100644
--- a/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/AnimatedTextBenchmark.kt
+++ b/wear/compose/integration-tests/macrobenchmark/src/main/java/androidx/wear/compose/integration/macrobenchmark/AnimatedTextBenchmark.kt
@@ -20,7 +20,7 @@
 import android.os.Build
 import androidx.benchmark.macro.CompilationMode
 import androidx.benchmark.macro.ExperimentalMetricApi
-import androidx.benchmark.macro.FrameTimingMetric
+import androidx.benchmark.macro.FrameTimingGfxInfoMetric
 import androidx.benchmark.macro.MemoryUsageMetric
 import androidx.benchmark.macro.junit4.MacrobenchmarkRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -61,7 +61,7 @@
             packageName = PACKAGE_NAME,
             metrics =
                 listOf(
-                    FrameTimingMetric(),
+                    FrameTimingGfxInfoMetric(),
                     MemoryUsageMetric(MemoryUsageMetric.Mode.Last),
                 ),
             compilationMode = CompilationMode.Full(),
diff --git a/webkit/webkit/api/1.12.0-beta01.txt b/webkit/webkit/api/1.12.0-beta01.txt
new file mode 100644
index 0000000..2b1f934
--- /dev/null
+++ b/webkit/webkit/api/1.12.0-beta01.txt
@@ -0,0 +1,445 @@
+// Signature format: 4.0
+package androidx.webkit {
+
+  @AnyThread public class CookieManagerCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_COOKIE_INFO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.List<java.lang.String!> getCookieInfo(android.webkit.CookieManager, String);
+  }
+
+  public final class DropDataContentProvider extends android.content.ContentProvider {
+    ctor public DropDataContentProvider();
+    method public int delete(android.net.Uri, String?, String![]?);
+    method public String? getType(android.net.Uri);
+    method public android.net.Uri? insert(android.net.Uri, android.content.ContentValues?);
+    method public boolean onCreate();
+    method public android.database.Cursor? query(android.net.Uri, String![]?, String?, String![]?, String?);
+    method public int update(android.net.Uri, android.content.ContentValues?, String?, String![]?);
+  }
+
+  @UiThread public abstract class JavaScriptReplyProxy {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(byte[]);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
+  }
+
+  public class ProcessGlobalConfig {
+    ctor public ProcessGlobalConfig();
+    method public static void apply(androidx.webkit.ProcessGlobalConfig);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDataDirectorySuffix(android.content.Context, String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDirectoryBasePaths(android.content.Context, java.io.File, java.io.File);
+  }
+
+  public interface Profile {
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.CookieManager getCookieManager();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.GeolocationPermissions getGeolocationPermissions();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public String getName();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.ServiceWorkerController getServiceWorkerController();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.WebStorage getWebStorage();
+    field public static final String DEFAULT_PROFILE_NAME = "Default";
+  }
+
+  @UiThread public interface ProfileStore {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public boolean deleteProfile(String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public java.util.List<java.lang.String!> getAllProfileNames();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProfileStore getInstance();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile getOrCreateProfile(String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile? getProfile(String);
+  }
+
+  public final class ProxyConfig {
+    method public java.util.List<java.lang.String!> getBypassRules();
+    method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
+    method public boolean isReverseBypassEnabled();
+    field public static final String MATCH_ALL_SCHEMES = "*";
+    field public static final String MATCH_HTTP = "http";
+    field public static final String MATCH_HTTPS = "https";
+  }
+
+  public static final class ProxyConfig.Builder {
+    ctor public ProxyConfig.Builder();
+    ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
+    method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addDirect();
+    method public androidx.webkit.ProxyConfig.Builder addDirect(String);
+    method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
+    method public androidx.webkit.ProxyConfig build();
+    method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
+    method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.ProxyConfig.Builder setReverseBypassEnabled(boolean);
+  }
+
+  public static final class ProxyConfig.ProxyRule {
+    method public String getSchemeFilter();
+    method public String getUrl();
+  }
+
+  @AnyThread public abstract class ProxyController {
+    method public abstract void clearProxyOverride(java.util.concurrent.Executor, Runnable);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProxyController getInstance();
+    method public abstract void setProxyOverride(androidx.webkit.ProxyConfig, java.util.concurrent.Executor, Runnable);
+  }
+
+  public abstract class SafeBrowsingResponseCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void backToSafety(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void proceed(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void showInterstitial(boolean);
+  }
+
+  public interface ScriptHandler {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DOCUMENT_START_SCRIPT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public void remove();
+  }
+
+  public abstract class ServiceWorkerClientCompat {
+    ctor public ServiceWorkerClientCompat();
+    method @WorkerThread public abstract android.webkit.WebResourceResponse? shouldInterceptRequest(android.webkit.WebResourceRequest);
+  }
+
+  @AnyThread public abstract class ServiceWorkerControllerCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
+    method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
+    method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat?);
+  }
+
+  @AnyThread public abstract class ServiceWorkerWebSettingsCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowContentAccess();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowFileAccess();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getBlockNetworkLoads();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getCacheMode();
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowContentAccess(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowFileAccess(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setBlockNetworkLoads(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setCacheMode(int);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setRequestedWithHeaderOriginAllowList(java.util.Set<java.lang.String!>);
+  }
+
+  public class TracingConfig {
+    method public java.util.List<java.lang.String!> getCustomIncludedCategories();
+    method public int getPredefinedCategories();
+    method public int getTracingMode();
+    field public static final int CATEGORIES_ALL = 1; // 0x1
+    field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
+    field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
+    field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
+    field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
+    field public static final int CATEGORIES_NONE = 0; // 0x0
+    field public static final int CATEGORIES_RENDERING = 16; // 0x10
+    field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
+    field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
+    field public static final int RECORD_UNTIL_FULL = 0; // 0x0
+  }
+
+  public static class TracingConfig.Builder {
+    ctor public TracingConfig.Builder();
+    method public androidx.webkit.TracingConfig.Builder addCategories(int...);
+    method public androidx.webkit.TracingConfig.Builder addCategories(java.lang.String!...);
+    method public androidx.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String!>);
+    method public androidx.webkit.TracingConfig build();
+    method public androidx.webkit.TracingConfig.Builder setTracingMode(int);
+  }
+
+  @AnyThread public abstract class TracingController {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.TracingController getInstance();
+    method public abstract boolean isTracing();
+    method public abstract void start(androidx.webkit.TracingConfig);
+    method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
+  }
+
+  public final class URLUtilCompat {
+    method public static String? getFilenameFromContentDisposition(String);
+    method public static String guessFileName(String, String?, String?);
+  }
+
+  public final class UserAgentMetadata {
+    method public String? getArchitecture();
+    method public int getBitness();
+    method public java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!> getBrandVersionList();
+    method public String? getFullVersion();
+    method public String? getModel();
+    method public String? getPlatform();
+    method public String? getPlatformVersion();
+    method public boolean isMobile();
+    method public boolean isWow64();
+    field public static final int BITNESS_DEFAULT = 0; // 0x0
+  }
+
+  public static final class UserAgentMetadata.BrandVersion {
+    method public String getBrand();
+    method public String getFullVersion();
+    method public String getMajorVersion();
+  }
+
+  public static final class UserAgentMetadata.BrandVersion.Builder {
+    ctor public UserAgentMetadata.BrandVersion.Builder();
+    ctor public UserAgentMetadata.BrandVersion.Builder(androidx.webkit.UserAgentMetadata.BrandVersion);
+    method public androidx.webkit.UserAgentMetadata.BrandVersion build();
+    method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setBrand(String);
+    method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setFullVersion(String);
+    method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setMajorVersion(String);
+  }
+
+  public static final class UserAgentMetadata.Builder {
+    ctor public UserAgentMetadata.Builder();
+    ctor public UserAgentMetadata.Builder(androidx.webkit.UserAgentMetadata);
+    method public androidx.webkit.UserAgentMetadata build();
+    method public androidx.webkit.UserAgentMetadata.Builder setArchitecture(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setBitness(int);
+    method public androidx.webkit.UserAgentMetadata.Builder setBrandVersionList(java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!>);
+    method public androidx.webkit.UserAgentMetadata.Builder setFullVersion(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setMobile(boolean);
+    method public androidx.webkit.UserAgentMetadata.Builder setModel(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setPlatform(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setPlatformVersion(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setWow64(boolean);
+  }
+
+  public class WebMessageCompat {
+    ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[]);
+    ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[], androidx.webkit.WebMessagePortCompat![]?);
+    ctor public WebMessageCompat(String?);
+    ctor public WebMessageCompat(String?, androidx.webkit.WebMessagePortCompat![]?);
+    method public byte[] getArrayBuffer();
+    method public String? getData();
+    method public androidx.webkit.WebMessagePortCompat![]? getPorts();
+    method public int getType();
+    field public static final int TYPE_ARRAY_BUFFER = 1; // 0x1
+    field public static final int TYPE_STRING = 0; // 0x0
+  }
+
+  @AnyThread public abstract class WebMessagePortCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_CLOSE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void close();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(androidx.webkit.WebMessageCompat);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(android.os.Handler?, androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+  }
+
+  public abstract static class WebMessagePortCompat.WebMessageCallbackCompat {
+    ctor public WebMessagePortCompat.WebMessageCallbackCompat();
+    method public void onMessage(androidx.webkit.WebMessagePortCompat, androidx.webkit.WebMessageCompat?);
+  }
+
+  public abstract class WebResourceErrorCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract CharSequence getDescription();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getErrorCode();
+  }
+
+  public class WebResourceRequestCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isRedirect(android.webkit.WebResourceRequest);
+  }
+
+  public class WebSettingsCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getAttributionRegistrationBehavior(android.webkit.WebSettings);
+    method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.BACK_FORWARD_CACHE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalBackForwardCache public static boolean getBackForwardCacheEnabled(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDarkStrategy(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+    method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static int getSpeculativeLoadingStatus(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.UserAgentMetadata getUserAgentMetadata(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_AUTHENTICATION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getWebAuthenticationSupport(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewMediaIntegrityApiStatusConfig getWebViewMediaIntegrityApiStatus(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAttributionRegistrationBehavior(android.webkit.WebSettings, int);
+    method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.BACK_FORWARD_CACHE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalBackForwardCache public static void setBackForwardCacheEnabled(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDarkStrategy(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+    method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static void setSpeculativeLoadingStatus(android.webkit.WebSettings, @SuppressCompatibility int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setUserAgentMetadata(android.webkit.WebSettings, androidx.webkit.UserAgentMetadata);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_AUTHENTICATION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebAuthenticationSupport(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewMediaIntegrityApiStatus(android.webkit.WebSettings, androidx.webkit.WebViewMediaIntegrityApiStatusConfig);
+    field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_APP_TRIGGER = 3; // 0x3
+    field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_WEB_TRIGGER = 1; // 0x1
+    field public static final int ATTRIBUTION_BEHAVIOR_DISABLED = 0; // 0x0
+    field public static final int ATTRIBUTION_BEHAVIOR_WEB_SOURCE_AND_WEB_TRIGGER = 2; // 0x2
+    field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
+    field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
+    field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
+    field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
+    field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
+    field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
+    field @SuppressCompatibility @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static final int SPECULATIVE_LOADING_DISABLED = 0; // 0x0
+    field @SuppressCompatibility @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static final int SPECULATIVE_LOADING_PRERENDER_ENABLED = 1; // 0x1
+    field public static final int WEB_AUTHENTICATION_SUPPORT_FOR_APP = 1; // 0x1
+    field public static final int WEB_AUTHENTICATION_SUPPORT_FOR_BROWSER = 2; // 0x2
+    field public static final int WEB_AUTHENTICATION_SUPPORT_NONE = 0; // 0x0
+  }
+
+  @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public static @interface WebSettingsCompat.ExperimentalBackForwardCache {
+  }
+
+  @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public static @interface WebSettingsCompat.ExperimentalSpeculativeLoading {
+  }
+
+  public final class WebViewAssetLoader {
+    method @WorkerThread public android.webkit.WebResourceResponse? shouldInterceptRequest(android.net.Uri);
+    field public static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
+  }
+
+  public static final class WebViewAssetLoader.AssetsPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.AssetsPathHandler(android.content.Context);
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public static final class WebViewAssetLoader.Builder {
+    ctor public WebViewAssetLoader.Builder();
+    method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+    method public androidx.webkit.WebViewAssetLoader build();
+    method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+    method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
+  }
+
+  public static final class WebViewAssetLoader.InternalStoragePathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.InternalStoragePathHandler(android.content.Context, java.io.File);
+    method @WorkerThread public android.webkit.WebResourceResponse handle(String);
+  }
+
+  public static interface WebViewAssetLoader.PathHandler {
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public static final class WebViewAssetLoader.ResourcesPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.ResourcesPathHandler(android.content.Context);
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public class WebViewClientCompat extends android.webkit.WebViewClient {
+    ctor public WebViewClientCompat();
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public final void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError);
+    method @UiThread public void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, androidx.webkit.WebResourceErrorCompat);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O_MR1) public final void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, android.webkit.SafeBrowsingResponse);
+    method @UiThread public void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, androidx.webkit.SafeBrowsingResponseCompat);
+  }
+
+  public class WebViewCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DOCUMENT_START_SCRIPT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.ScriptHandler addDocumentStartJavaScript(android.webkit.WebView, String, java.util.Set<java.lang.String!>);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
+    method @AnyThread public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.Profile getProfile(android.webkit.WebView);
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static android.webkit.WebViewClient getWebViewClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_RENDERER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebViewRenderProcess? getWebViewRenderProcess(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebViewRenderProcessClient? getWebViewRenderProcessClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static boolean isAudioMuted(android.webkit.WebView);
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isMultiProcessEnabled();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void removeWebMessageListener(android.webkit.WebView, String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setAudioMuted(android.webkit.WebView, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setProfile(android.webkit.WebView, String);
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+    method @Deprecated @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebViewRenderProcessClient(android.webkit.WebView, java.util.concurrent.Executor, androidx.webkit.WebViewRenderProcessClient);
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.START_SAFE_BROWSING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean!>?);
+  }
+
+  public static interface WebViewCompat.VisualStateCallback {
+    method @UiThread public void onComplete(long);
+  }
+
+  public static interface WebViewCompat.WebMessageListener {
+    method @UiThread public void onPostMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri, boolean, androidx.webkit.JavaScriptReplyProxy);
+  }
+
+  public class WebViewFeature {
+    method public static boolean isFeatureSupported(String);
+    method public static boolean isStartupFeatureSupported(android.content.Context, String);
+    field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
+    field public static final String ATTRIBUTION_REGISTRATION_BEHAVIOR = "ATTRIBUTION_REGISTRATION_BEHAVIOR";
+    field public static final String BACK_FORWARD_CACHE = "BACK_FORWARD_CACHE";
+    field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
+    field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
+    field public static final String DOCUMENT_START_SCRIPT = "DOCUMENT_START_SCRIPT";
+    field public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY = "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
+    field public static final String FORCE_DARK = "FORCE_DARK";
+    field public static final String FORCE_DARK_STRATEGY = "FORCE_DARK_STRATEGY";
+    field public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
+    field public static final String GET_VARIATIONS_HEADER = "GET_VARIATIONS_HEADER";
+    field public static final String GET_WEB_CHROME_CLIENT = "GET_WEB_CHROME_CLIENT";
+    field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
+    field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
+    field public static final String MULTI_PROCESS = "MULTI_PROCESS";
+    field public static final String MULTI_PROFILE = "MULTI_PROFILE";
+    field public static final String MUTE_AUDIO = "MUTE_AUDIO";
+    field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
+    field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
+    field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
+    field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
+    field public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
+    field public static final String RECEIVE_WEB_RESOURCE_ERROR = "RECEIVE_WEB_RESOURCE_ERROR";
+    field public static final String SAFE_BROWSING_ALLOWLIST = "SAFE_BROWSING_ALLOWLIST";
+    field public static final String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
+    field public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
+    field public static final String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
+    field public static final String SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY";
+    field public static final String SAFE_BROWSING_RESPONSE_PROCEED = "SAFE_BROWSING_RESPONSE_PROCEED";
+    field public static final String SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL";
+    field @Deprecated public static final String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
+    field public static final String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
+    field public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
+    field public static final String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
+    field public static final String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
+    field public static final String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
+    field public static final String SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST";
+    field public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
+    field public static final String SPECULATIVE_LOADING = "SPECULATIVE_LOADING_STATUS";
+    field public static final String STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX = "STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX";
+    field public static final String STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS = "STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS";
+    field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
+    field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
+    field public static final String USER_AGENT_METADATA = "USER_AGENT_METADATA";
+    field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
+    field public static final String WEBVIEW_MEDIA_INTEGRITY_API_STATUS = "WEBVIEW_MEDIA_INTEGRITY_API_STATUS";
+    field public static final String WEB_AUTHENTICATION = "WEB_AUTHENTICATION";
+    field public static final String WEB_MESSAGE_ARRAY_BUFFER = "WEB_MESSAGE_ARRAY_BUFFER";
+    field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
+    field public static final String WEB_MESSAGE_LISTENER = "WEB_MESSAGE_LISTENER";
+    field public static final String WEB_MESSAGE_PORT_CLOSE = "WEB_MESSAGE_PORT_CLOSE";
+    field public static final String WEB_MESSAGE_PORT_POST_MESSAGE = "WEB_MESSAGE_PORT_POST_MESSAGE";
+    field public static final String WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK";
+    field public static final String WEB_RESOURCE_ERROR_GET_CODE = "WEB_RESOURCE_ERROR_GET_CODE";
+    field public static final String WEB_RESOURCE_ERROR_GET_DESCRIPTION = "WEB_RESOURCE_ERROR_GET_DESCRIPTION";
+    field public static final String WEB_RESOURCE_REQUEST_IS_REDIRECT = "WEB_RESOURCE_REQUEST_IS_REDIRECT";
+    field public static final String WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE";
+    field public static final String WEB_VIEW_RENDERER_TERMINATE = "WEB_VIEW_RENDERER_TERMINATE";
+  }
+
+  @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public class WebViewMediaIntegrityApiStatusConfig {
+    ctor public WebViewMediaIntegrityApiStatusConfig(androidx.webkit.WebViewMediaIntegrityApiStatusConfig.Builder);
+    method public int getDefaultStatus();
+    method public java.util.Map<java.lang.String!,java.lang.Integer!> getOverrideRules();
+    field public static final int WEBVIEW_MEDIA_INTEGRITY_API_DISABLED = 0; // 0x0
+    field public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED = 2; // 0x2
+    field public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED_WITHOUT_APP_IDENTITY = 1; // 0x1
+  }
+
+  public static final class WebViewMediaIntegrityApiStatusConfig.Builder {
+    ctor public WebViewMediaIntegrityApiStatusConfig.Builder(int);
+    method public androidx.webkit.WebViewMediaIntegrityApiStatusConfig.Builder addOverrideRule(String, int);
+    method public androidx.webkit.WebViewMediaIntegrityApiStatusConfig build();
+  }
+
+  public abstract class WebViewRenderProcess {
+    ctor public WebViewRenderProcess();
+    method public abstract boolean terminate();
+  }
+
+  public abstract class WebViewRenderProcessClient {
+    ctor public WebViewRenderProcessClient();
+    method public abstract void onRenderProcessResponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+    method public abstract void onRenderProcessUnresponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+  }
+
+}
+
diff --git a/webkit/webkit/api/res-1.12.0-beta01.txt b/webkit/webkit/api/res-1.12.0-beta01.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/webkit/webkit/api/res-1.12.0-beta01.txt
diff --git a/webkit/webkit/api/restricted_1.12.0-beta01.txt b/webkit/webkit/api/restricted_1.12.0-beta01.txt
new file mode 100644
index 0000000..2b1f934
--- /dev/null
+++ b/webkit/webkit/api/restricted_1.12.0-beta01.txt
@@ -0,0 +1,445 @@
+// Signature format: 4.0
+package androidx.webkit {
+
+  @AnyThread public class CookieManagerCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_COOKIE_INFO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.List<java.lang.String!> getCookieInfo(android.webkit.CookieManager, String);
+  }
+
+  public final class DropDataContentProvider extends android.content.ContentProvider {
+    ctor public DropDataContentProvider();
+    method public int delete(android.net.Uri, String?, String![]?);
+    method public String? getType(android.net.Uri);
+    method public android.net.Uri? insert(android.net.Uri, android.content.ContentValues?);
+    method public boolean onCreate();
+    method public android.database.Cursor? query(android.net.Uri, String![]?, String?, String![]?, String?);
+    method public int update(android.net.Uri, android.content.ContentValues?, String?, String![]?);
+  }
+
+  @UiThread public abstract class JavaScriptReplyProxy {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(byte[]);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(String);
+  }
+
+  public class ProcessGlobalConfig {
+    ctor public ProcessGlobalConfig();
+    method public static void apply(androidx.webkit.ProcessGlobalConfig);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDataDirectorySuffix(android.content.Context, String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS, enforcement="androidx.webkit.WebViewFeature#isConfigFeatureSupported(String, Context)") public androidx.webkit.ProcessGlobalConfig setDirectoryBasePaths(android.content.Context, java.io.File, java.io.File);
+  }
+
+  public interface Profile {
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.CookieManager getCookieManager();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.GeolocationPermissions getGeolocationPermissions();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public String getName();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.ServiceWorkerController getServiceWorkerController();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public android.webkit.WebStorage getWebStorage();
+    field public static final String DEFAULT_PROFILE_NAME = "Default";
+  }
+
+  @UiThread public interface ProfileStore {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public boolean deleteProfile(String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public java.util.List<java.lang.String!> getAllProfileNames();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProfileStore getInstance();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile getOrCreateProfile(String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.Profile? getProfile(String);
+  }
+
+  public final class ProxyConfig {
+    method public java.util.List<java.lang.String!> getBypassRules();
+    method public java.util.List<androidx.webkit.ProxyConfig.ProxyRule!> getProxyRules();
+    method public boolean isReverseBypassEnabled();
+    field public static final String MATCH_ALL_SCHEMES = "*";
+    field public static final String MATCH_HTTP = "http";
+    field public static final String MATCH_HTTPS = "https";
+  }
+
+  public static final class ProxyConfig.Builder {
+    ctor public ProxyConfig.Builder();
+    ctor public ProxyConfig.Builder(androidx.webkit.ProxyConfig);
+    method public androidx.webkit.ProxyConfig.Builder addBypassRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addDirect();
+    method public androidx.webkit.ProxyConfig.Builder addDirect(String);
+    method public androidx.webkit.ProxyConfig.Builder addProxyRule(String);
+    method public androidx.webkit.ProxyConfig.Builder addProxyRule(String, String);
+    method public androidx.webkit.ProxyConfig build();
+    method public androidx.webkit.ProxyConfig.Builder bypassSimpleHostnames();
+    method public androidx.webkit.ProxyConfig.Builder removeImplicitRules();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE_REVERSE_BYPASS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public androidx.webkit.ProxyConfig.Builder setReverseBypassEnabled(boolean);
+  }
+
+  public static final class ProxyConfig.ProxyRule {
+    method public String getSchemeFilter();
+    method public String getUrl();
+  }
+
+  @AnyThread public abstract class ProxyController {
+    method public abstract void clearProxyOverride(java.util.concurrent.Executor, Runnable);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.PROXY_OVERRIDE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ProxyController getInstance();
+    method public abstract void setProxyOverride(androidx.webkit.ProxyConfig, java.util.concurrent.Executor, Runnable);
+  }
+
+  public abstract class SafeBrowsingResponseCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void backToSafety(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_PROCEED, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void proceed(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void showInterstitial(boolean);
+  }
+
+  public interface ScriptHandler {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DOCUMENT_START_SCRIPT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public void remove();
+  }
+
+  public abstract class ServiceWorkerClientCompat {
+    ctor public ServiceWorkerClientCompat();
+    method @WorkerThread public abstract android.webkit.WebResourceResponse? shouldInterceptRequest(android.webkit.WebResourceRequest);
+  }
+
+  @AnyThread public abstract class ServiceWorkerControllerCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.ServiceWorkerControllerCompat getInstance();
+    method public abstract androidx.webkit.ServiceWorkerWebSettingsCompat getServiceWorkerWebSettings();
+    method public abstract void setServiceWorkerClient(androidx.webkit.ServiceWorkerClientCompat?);
+  }
+
+  @AnyThread public abstract class ServiceWorkerWebSettingsCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowContentAccess();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getAllowFileAccess();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract boolean getBlockNetworkLoads();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getCacheMode();
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CONTENT_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowContentAccess(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_FILE_ACCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setAllowFileAccess(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_BLOCK_NETWORK_LOADS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setBlockNetworkLoads(boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SERVICE_WORKER_CACHE_MODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setCacheMode(int);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setRequestedWithHeaderOriginAllowList(java.util.Set<java.lang.String!>);
+  }
+
+  public class TracingConfig {
+    method public java.util.List<java.lang.String!> getCustomIncludedCategories();
+    method public int getPredefinedCategories();
+    method public int getTracingMode();
+    field public static final int CATEGORIES_ALL = 1; // 0x1
+    field public static final int CATEGORIES_ANDROID_WEBVIEW = 2; // 0x2
+    field public static final int CATEGORIES_FRAME_VIEWER = 64; // 0x40
+    field public static final int CATEGORIES_INPUT_LATENCY = 8; // 0x8
+    field public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 32; // 0x20
+    field public static final int CATEGORIES_NONE = 0; // 0x0
+    field public static final int CATEGORIES_RENDERING = 16; // 0x10
+    field public static final int CATEGORIES_WEB_DEVELOPER = 4; // 0x4
+    field public static final int RECORD_CONTINUOUSLY = 1; // 0x1
+    field public static final int RECORD_UNTIL_FULL = 0; // 0x0
+  }
+
+  public static class TracingConfig.Builder {
+    ctor public TracingConfig.Builder();
+    method public androidx.webkit.TracingConfig.Builder addCategories(int...);
+    method public androidx.webkit.TracingConfig.Builder addCategories(java.lang.String!...);
+    method public androidx.webkit.TracingConfig.Builder addCategories(java.util.Collection<java.lang.String!>);
+    method public androidx.webkit.TracingConfig build();
+    method public androidx.webkit.TracingConfig.Builder setTracingMode(int);
+  }
+
+  @AnyThread public abstract class TracingController {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.TRACING_CONTROLLER_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.TracingController getInstance();
+    method public abstract boolean isTracing();
+    method public abstract void start(androidx.webkit.TracingConfig);
+    method public abstract boolean stop(java.io.OutputStream?, java.util.concurrent.Executor);
+  }
+
+  public final class URLUtilCompat {
+    method public static String? getFilenameFromContentDisposition(String);
+    method public static String guessFileName(String, String?, String?);
+  }
+
+  public final class UserAgentMetadata {
+    method public String? getArchitecture();
+    method public int getBitness();
+    method public java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!> getBrandVersionList();
+    method public String? getFullVersion();
+    method public String? getModel();
+    method public String? getPlatform();
+    method public String? getPlatformVersion();
+    method public boolean isMobile();
+    method public boolean isWow64();
+    field public static final int BITNESS_DEFAULT = 0; // 0x0
+  }
+
+  public static final class UserAgentMetadata.BrandVersion {
+    method public String getBrand();
+    method public String getFullVersion();
+    method public String getMajorVersion();
+  }
+
+  public static final class UserAgentMetadata.BrandVersion.Builder {
+    ctor public UserAgentMetadata.BrandVersion.Builder();
+    ctor public UserAgentMetadata.BrandVersion.Builder(androidx.webkit.UserAgentMetadata.BrandVersion);
+    method public androidx.webkit.UserAgentMetadata.BrandVersion build();
+    method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setBrand(String);
+    method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setFullVersion(String);
+    method public androidx.webkit.UserAgentMetadata.BrandVersion.Builder setMajorVersion(String);
+  }
+
+  public static final class UserAgentMetadata.Builder {
+    ctor public UserAgentMetadata.Builder();
+    ctor public UserAgentMetadata.Builder(androidx.webkit.UserAgentMetadata);
+    method public androidx.webkit.UserAgentMetadata build();
+    method public androidx.webkit.UserAgentMetadata.Builder setArchitecture(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setBitness(int);
+    method public androidx.webkit.UserAgentMetadata.Builder setBrandVersionList(java.util.List<androidx.webkit.UserAgentMetadata.BrandVersion!>);
+    method public androidx.webkit.UserAgentMetadata.Builder setFullVersion(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setMobile(boolean);
+    method public androidx.webkit.UserAgentMetadata.Builder setModel(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setPlatform(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setPlatformVersion(String?);
+    method public androidx.webkit.UserAgentMetadata.Builder setWow64(boolean);
+  }
+
+  public class WebMessageCompat {
+    ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[]);
+    ctor @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public WebMessageCompat(byte[], androidx.webkit.WebMessagePortCompat![]?);
+    ctor public WebMessageCompat(String?);
+    ctor public WebMessageCompat(String?, androidx.webkit.WebMessagePortCompat![]?);
+    method public byte[] getArrayBuffer();
+    method public String? getData();
+    method public androidx.webkit.WebMessagePortCompat![]? getPorts();
+    method public int getType();
+    field public static final int TYPE_ARRAY_BUFFER = 1; // 0x1
+    field public static final int TYPE_STRING = 0; // 0x0
+  }
+
+  @AnyThread public abstract class WebMessagePortCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_CLOSE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void close();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_POST_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void postMessage(androidx.webkit.WebMessageCompat);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(android.os.Handler?, androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract void setWebMessageCallback(androidx.webkit.WebMessagePortCompat.WebMessageCallbackCompat);
+  }
+
+  public abstract static class WebMessagePortCompat.WebMessageCallbackCompat {
+    ctor public WebMessagePortCompat.WebMessageCallbackCompat();
+    method public void onMessage(androidx.webkit.WebMessagePortCompat, androidx.webkit.WebMessageCompat?);
+  }
+
+  public abstract class WebResourceErrorCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_DESCRIPTION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract CharSequence getDescription();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_ERROR_GET_CODE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public abstract int getErrorCode();
+  }
+
+  public class WebResourceRequestCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_RESOURCE_REQUEST_IS_REDIRECT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isRedirect(android.webkit.WebResourceRequest);
+  }
+
+  public class WebSettingsCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getAttributionRegistrationBehavior(android.webkit.WebSettings);
+    method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.BACK_FORWARD_CACHE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalBackForwardCache public static boolean getBackForwardCacheEnabled(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getDisabledActionModeMenuItems(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDark(android.webkit.WebSettings);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getForceDarkStrategy(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getOffscreenPreRaster(android.webkit.WebSettings);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static java.util.Set<java.lang.String!> getRequestedWithHeaderOriginAllowList(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean getSafeBrowsingEnabled(android.webkit.WebSettings);
+    method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static int getSpeculativeLoadingStatus(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.UserAgentMetadata getUserAgentMetadata(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_AUTHENTICATION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static int getWebAuthenticationSupport(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static androidx.webkit.WebViewMediaIntegrityApiStatusConfig getWebViewMediaIntegrityApiStatus(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isAlgorithmicDarkeningAllowed(android.webkit.WebSettings);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ALGORITHMIC_DARKENING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAlgorithmicDarkeningAllowed(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ATTRIBUTION_REGISTRATION_BEHAVIOR, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setAttributionRegistrationBehavior(android.webkit.WebSettings, int);
+    method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.BACK_FORWARD_CACHE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalBackForwardCache public static void setBackForwardCacheEnabled(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DISABLED_ACTION_MODE_MENU_ITEMS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setDisabledActionModeMenuItems(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setEnterpriseAuthenticationAppLinkPolicyEnabled(android.webkit.WebSettings, boolean);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDark(android.webkit.WebSettings, int);
+    method @Deprecated @RequiresFeature(name=androidx.webkit.WebViewFeature.FORCE_DARK_STRATEGY, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setForceDarkStrategy(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.OFF_SCREEN_PRERASTER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setOffscreenPreRaster(android.webkit.WebSettings, boolean);
+    method @RequiresFeature(name="REQUESTED_WITH_HEADER_ALLOW_LIST", enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setRequestedWithHeaderOriginAllowList(android.webkit.WebSettings, java.util.Set<java.lang.String!>);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ENABLE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingEnabled(android.webkit.WebSettings, boolean);
+    method @SuppressCompatibility @RequiresFeature(name=androidx.webkit.WebViewFeature.SPECULATIVE_LOADING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static void setSpeculativeLoadingStatus(android.webkit.WebSettings, @SuppressCompatibility int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.USER_AGENT_METADATA, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setUserAgentMetadata(android.webkit.WebSettings, androidx.webkit.UserAgentMetadata);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_AUTHENTICATION, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebAuthenticationSupport(android.webkit.WebSettings, int);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setWebViewMediaIntegrityApiStatus(android.webkit.WebSettings, androidx.webkit.WebViewMediaIntegrityApiStatusConfig);
+    field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_APP_TRIGGER = 3; // 0x3
+    field public static final int ATTRIBUTION_BEHAVIOR_APP_SOURCE_AND_WEB_TRIGGER = 1; // 0x1
+    field public static final int ATTRIBUTION_BEHAVIOR_DISABLED = 0; // 0x0
+    field public static final int ATTRIBUTION_BEHAVIOR_WEB_SOURCE_AND_WEB_TRIGGER = 2; // 0x2
+    field @Deprecated public static final int DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING = 2; // 0x2
+    field @Deprecated public static final int DARK_STRATEGY_USER_AGENT_DARKENING_ONLY = 0; // 0x0
+    field @Deprecated public static final int DARK_STRATEGY_WEB_THEME_DARKENING_ONLY = 1; // 0x1
+    field @Deprecated public static final int FORCE_DARK_AUTO = 1; // 0x1
+    field @Deprecated public static final int FORCE_DARK_OFF = 0; // 0x0
+    field @Deprecated public static final int FORCE_DARK_ON = 2; // 0x2
+    field @SuppressCompatibility @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static final int SPECULATIVE_LOADING_DISABLED = 0; // 0x0
+    field @SuppressCompatibility @androidx.webkit.WebSettingsCompat.ExperimentalSpeculativeLoading public static final int SPECULATIVE_LOADING_PRERENDER_ENABLED = 1; // 0x1
+    field public static final int WEB_AUTHENTICATION_SUPPORT_FOR_APP = 1; // 0x1
+    field public static final int WEB_AUTHENTICATION_SUPPORT_FOR_BROWSER = 2; // 0x2
+    field public static final int WEB_AUTHENTICATION_SUPPORT_NONE = 0; // 0x0
+  }
+
+  @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public static @interface WebSettingsCompat.ExperimentalBackForwardCache {
+  }
+
+  @SuppressCompatibility @RequiresOptIn(level=androidx.annotation.RequiresOptIn.Level.ERROR) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.TYPE}) public static @interface WebSettingsCompat.ExperimentalSpeculativeLoading {
+  }
+
+  public final class WebViewAssetLoader {
+    method @WorkerThread public android.webkit.WebResourceResponse? shouldInterceptRequest(android.net.Uri);
+    field public static final String DEFAULT_DOMAIN = "appassets.androidplatform.net";
+  }
+
+  public static final class WebViewAssetLoader.AssetsPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.AssetsPathHandler(android.content.Context);
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public static final class WebViewAssetLoader.Builder {
+    ctor public WebViewAssetLoader.Builder();
+    method public androidx.webkit.WebViewAssetLoader.Builder addPathHandler(String, androidx.webkit.WebViewAssetLoader.PathHandler);
+    method public androidx.webkit.WebViewAssetLoader build();
+    method public androidx.webkit.WebViewAssetLoader.Builder setDomain(String);
+    method public androidx.webkit.WebViewAssetLoader.Builder setHttpAllowed(boolean);
+  }
+
+  public static final class WebViewAssetLoader.InternalStoragePathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.InternalStoragePathHandler(android.content.Context, java.io.File);
+    method @WorkerThread public android.webkit.WebResourceResponse handle(String);
+  }
+
+  public static interface WebViewAssetLoader.PathHandler {
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public static final class WebViewAssetLoader.ResourcesPathHandler implements androidx.webkit.WebViewAssetLoader.PathHandler {
+    ctor public WebViewAssetLoader.ResourcesPathHandler(android.content.Context);
+    method @WorkerThread public android.webkit.WebResourceResponse? handle(String);
+  }
+
+  public class WebViewClientCompat extends android.webkit.WebViewClient {
+    ctor public WebViewClientCompat();
+    method @RequiresApi(android.os.Build.VERSION_CODES.M) public final void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, android.webkit.WebResourceError);
+    method @UiThread public void onReceivedError(android.webkit.WebView, android.webkit.WebResourceRequest, androidx.webkit.WebResourceErrorCompat);
+    method @RequiresApi(android.os.Build.VERSION_CODES.O_MR1) public final void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, android.webkit.SafeBrowsingResponse);
+    method @UiThread public void onSafeBrowsingHit(android.webkit.WebView, android.webkit.WebResourceRequest, int, androidx.webkit.SafeBrowsingResponseCompat);
+  }
+
+  public class WebViewCompat {
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.DOCUMENT_START_SCRIPT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.ScriptHandler addDocumentStartJavaScript(android.webkit.WebView, String, java.util.Set<java.lang.String!>);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void addWebMessageListener(android.webkit.WebView, String, java.util.Set<java.lang.String!>, androidx.webkit.WebViewCompat.WebMessageListener);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.CREATE_WEB_MESSAGE_CHANNEL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebMessagePortCompat![] createWebMessageChannel(android.webkit.WebView);
+    method @AnyThread public static android.content.pm.PackageInfo? getCurrentWebViewPackage(android.content.Context);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.Profile getProfile(android.webkit.WebView);
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_PRIVACY_POLICY_URL, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static android.net.Uri getSafeBrowsingPrivacyPolicyUrl();
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_VARIATIONS_HEADER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static String getVariationsHeader();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_CHROME_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static android.webkit.WebChromeClient? getWebChromeClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_CLIENT, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static android.webkit.WebViewClient getWebViewClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.GET_WEB_VIEW_RENDERER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebViewRenderProcess? getWebViewRenderProcess(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static androidx.webkit.WebViewRenderProcessClient? getWebViewRenderProcessClient(android.webkit.WebView);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static boolean isAudioMuted(android.webkit.WebView);
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROCESS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static boolean isMultiProcessEnabled();
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.VISUAL_STATE_CALLBACK, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void postVisualStateCallback(android.webkit.WebView, long, androidx.webkit.WebViewCompat.VisualStateCallback);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.POST_WEB_MESSAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void postWebMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_MESSAGE_LISTENER, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void removeWebMessageListener(android.webkit.WebView, String);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MUTE_AUDIO, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setAudioMuted(android.webkit.WebView, boolean);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.MULTI_PROFILE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setProfile(android.webkit.WebView, String);
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_ALLOWLIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingAllowlist(java.util.Set<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+    method @Deprecated @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.SAFE_BROWSING_WHITELIST, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void setSafeBrowsingWhitelist(java.util.List<java.lang.String!>, android.webkit.ValueCallback<java.lang.Boolean!>?);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebViewRenderProcessClient(android.webkit.WebView, androidx.webkit.WebViewRenderProcessClient?);
+    method @RequiresFeature(name=androidx.webkit.WebViewFeature.WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") @UiThread public static void setWebViewRenderProcessClient(android.webkit.WebView, java.util.concurrent.Executor, androidx.webkit.WebViewRenderProcessClient);
+    method @AnyThread @RequiresFeature(name=androidx.webkit.WebViewFeature.START_SAFE_BROWSING, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public static void startSafeBrowsing(android.content.Context, android.webkit.ValueCallback<java.lang.Boolean!>?);
+  }
+
+  public static interface WebViewCompat.VisualStateCallback {
+    method @UiThread public void onComplete(long);
+  }
+
+  public static interface WebViewCompat.WebMessageListener {
+    method @UiThread public void onPostMessage(android.webkit.WebView, androidx.webkit.WebMessageCompat, android.net.Uri, boolean, androidx.webkit.JavaScriptReplyProxy);
+  }
+
+  public class WebViewFeature {
+    method public static boolean isFeatureSupported(String);
+    method public static boolean isStartupFeatureSupported(android.content.Context, String);
+    field public static final String ALGORITHMIC_DARKENING = "ALGORITHMIC_DARKENING";
+    field public static final String ATTRIBUTION_REGISTRATION_BEHAVIOR = "ATTRIBUTION_REGISTRATION_BEHAVIOR";
+    field public static final String BACK_FORWARD_CACHE = "BACK_FORWARD_CACHE";
+    field public static final String CREATE_WEB_MESSAGE_CHANNEL = "CREATE_WEB_MESSAGE_CHANNEL";
+    field public static final String DISABLED_ACTION_MODE_MENU_ITEMS = "DISABLED_ACTION_MODE_MENU_ITEMS";
+    field public static final String DOCUMENT_START_SCRIPT = "DOCUMENT_START_SCRIPT";
+    field public static final String ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY = "ENTERPRISE_AUTHENTICATION_APP_LINK_POLICY";
+    field public static final String FORCE_DARK = "FORCE_DARK";
+    field public static final String FORCE_DARK_STRATEGY = "FORCE_DARK_STRATEGY";
+    field public static final String GET_COOKIE_INFO = "GET_COOKIE_INFO";
+    field public static final String GET_VARIATIONS_HEADER = "GET_VARIATIONS_HEADER";
+    field public static final String GET_WEB_CHROME_CLIENT = "GET_WEB_CHROME_CLIENT";
+    field public static final String GET_WEB_VIEW_CLIENT = "GET_WEB_VIEW_CLIENT";
+    field public static final String GET_WEB_VIEW_RENDERER = "GET_WEB_VIEW_RENDERER";
+    field public static final String MULTI_PROCESS = "MULTI_PROCESS";
+    field public static final String MULTI_PROFILE = "MULTI_PROFILE";
+    field public static final String MUTE_AUDIO = "MUTE_AUDIO";
+    field public static final String OFF_SCREEN_PRERASTER = "OFF_SCREEN_PRERASTER";
+    field public static final String POST_WEB_MESSAGE = "POST_WEB_MESSAGE";
+    field public static final String PROXY_OVERRIDE = "PROXY_OVERRIDE";
+    field public static final String PROXY_OVERRIDE_REVERSE_BYPASS = "PROXY_OVERRIDE_REVERSE_BYPASS";
+    field public static final String RECEIVE_HTTP_ERROR = "RECEIVE_HTTP_ERROR";
+    field public static final String RECEIVE_WEB_RESOURCE_ERROR = "RECEIVE_WEB_RESOURCE_ERROR";
+    field public static final String SAFE_BROWSING_ALLOWLIST = "SAFE_BROWSING_ALLOWLIST";
+    field public static final String SAFE_BROWSING_ENABLE = "SAFE_BROWSING_ENABLE";
+    field public static final String SAFE_BROWSING_HIT = "SAFE_BROWSING_HIT";
+    field public static final String SAFE_BROWSING_PRIVACY_POLICY_URL = "SAFE_BROWSING_PRIVACY_POLICY_URL";
+    field public static final String SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY = "SAFE_BROWSING_RESPONSE_BACK_TO_SAFETY";
+    field public static final String SAFE_BROWSING_RESPONSE_PROCEED = "SAFE_BROWSING_RESPONSE_PROCEED";
+    field public static final String SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL = "SAFE_BROWSING_RESPONSE_SHOW_INTERSTITIAL";
+    field @Deprecated public static final String SAFE_BROWSING_WHITELIST = "SAFE_BROWSING_WHITELIST";
+    field public static final String SERVICE_WORKER_BASIC_USAGE = "SERVICE_WORKER_BASIC_USAGE";
+    field public static final String SERVICE_WORKER_BLOCK_NETWORK_LOADS = "SERVICE_WORKER_BLOCK_NETWORK_LOADS";
+    field public static final String SERVICE_WORKER_CACHE_MODE = "SERVICE_WORKER_CACHE_MODE";
+    field public static final String SERVICE_WORKER_CONTENT_ACCESS = "SERVICE_WORKER_CONTENT_ACCESS";
+    field public static final String SERVICE_WORKER_FILE_ACCESS = "SERVICE_WORKER_FILE_ACCESS";
+    field public static final String SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST = "SERVICE_WORKER_SHOULD_INTERCEPT_REQUEST";
+    field public static final String SHOULD_OVERRIDE_WITH_REDIRECTS = "SHOULD_OVERRIDE_WITH_REDIRECTS";
+    field public static final String SPECULATIVE_LOADING = "SPECULATIVE_LOADING_STATUS";
+    field public static final String STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX = "STARTUP_FEATURE_SET_DATA_DIRECTORY_SUFFIX";
+    field public static final String STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS = "STARTUP_FEATURE_SET_DIRECTORY_BASE_PATHS";
+    field public static final String START_SAFE_BROWSING = "START_SAFE_BROWSING";
+    field public static final String TRACING_CONTROLLER_BASIC_USAGE = "TRACING_CONTROLLER_BASIC_USAGE";
+    field public static final String USER_AGENT_METADATA = "USER_AGENT_METADATA";
+    field public static final String VISUAL_STATE_CALLBACK = "VISUAL_STATE_CALLBACK";
+    field public static final String WEBVIEW_MEDIA_INTEGRITY_API_STATUS = "WEBVIEW_MEDIA_INTEGRITY_API_STATUS";
+    field public static final String WEB_AUTHENTICATION = "WEB_AUTHENTICATION";
+    field public static final String WEB_MESSAGE_ARRAY_BUFFER = "WEB_MESSAGE_ARRAY_BUFFER";
+    field public static final String WEB_MESSAGE_CALLBACK_ON_MESSAGE = "WEB_MESSAGE_CALLBACK_ON_MESSAGE";
+    field public static final String WEB_MESSAGE_LISTENER = "WEB_MESSAGE_LISTENER";
+    field public static final String WEB_MESSAGE_PORT_CLOSE = "WEB_MESSAGE_PORT_CLOSE";
+    field public static final String WEB_MESSAGE_PORT_POST_MESSAGE = "WEB_MESSAGE_PORT_POST_MESSAGE";
+    field public static final String WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK = "WEB_MESSAGE_PORT_SET_MESSAGE_CALLBACK";
+    field public static final String WEB_RESOURCE_ERROR_GET_CODE = "WEB_RESOURCE_ERROR_GET_CODE";
+    field public static final String WEB_RESOURCE_ERROR_GET_DESCRIPTION = "WEB_RESOURCE_ERROR_GET_DESCRIPTION";
+    field public static final String WEB_RESOURCE_REQUEST_IS_REDIRECT = "WEB_RESOURCE_REQUEST_IS_REDIRECT";
+    field public static final String WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE = "WEB_VIEW_RENDERER_CLIENT_BASIC_USAGE";
+    field public static final String WEB_VIEW_RENDERER_TERMINATE = "WEB_VIEW_RENDERER_TERMINATE";
+  }
+
+  @RequiresFeature(name=androidx.webkit.WebViewFeature.WEBVIEW_MEDIA_INTEGRITY_API_STATUS, enforcement="androidx.webkit.WebViewFeature#isFeatureSupported") public class WebViewMediaIntegrityApiStatusConfig {
+    ctor public WebViewMediaIntegrityApiStatusConfig(androidx.webkit.WebViewMediaIntegrityApiStatusConfig.Builder);
+    method public int getDefaultStatus();
+    method public java.util.Map<java.lang.String!,java.lang.Integer!> getOverrideRules();
+    field public static final int WEBVIEW_MEDIA_INTEGRITY_API_DISABLED = 0; // 0x0
+    field public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED = 2; // 0x2
+    field public static final int WEBVIEW_MEDIA_INTEGRITY_API_ENABLED_WITHOUT_APP_IDENTITY = 1; // 0x1
+  }
+
+  public static final class WebViewMediaIntegrityApiStatusConfig.Builder {
+    ctor public WebViewMediaIntegrityApiStatusConfig.Builder(int);
+    method public androidx.webkit.WebViewMediaIntegrityApiStatusConfig.Builder addOverrideRule(String, int);
+    method public androidx.webkit.WebViewMediaIntegrityApiStatusConfig build();
+  }
+
+  public abstract class WebViewRenderProcess {
+    ctor public WebViewRenderProcess();
+    method public abstract boolean terminate();
+  }
+
+  public abstract class WebViewRenderProcessClient {
+    ctor public WebViewRenderProcessClient();
+    method public abstract void onRenderProcessResponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+    method public abstract void onRenderProcessUnresponsive(android.webkit.WebView, androidx.webkit.WebViewRenderProcess?);
+  }
+
+}
+
diff --git a/work/work-runtime/src/androidTest/java/androidx/work/impl/foreground/SystemForegroundDispatcherTest.kt b/work/work-runtime/src/androidTest/java/androidx/work/impl/foreground/SystemForegroundDispatcherTest.kt
index 22a47e0..05f8602 100644
--- a/work/work-runtime/src/androidTest/java/androidx/work/impl/foreground/SystemForegroundDispatcherTest.kt
+++ b/work/work-runtime/src/androidTest/java/androidx/work/impl/foreground/SystemForegroundDispatcherTest.kt
@@ -20,6 +20,7 @@
 import android.content.Context
 import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
 import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION
+import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE
 import android.util.Log
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -451,4 +452,10 @@
         dispatcher.onStartCommand(intent)
         assertThat(fakeChargingTracker.isTracking, `is`(true))
     }
+
+    @Test
+    fun testTimeoutForeground() {
+        dispatcher.onTimeout(0, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE)
+        verify(dispatcherCallback, times(1)).stop()
+    }
 }
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java
index de9b1e8..5c3bca6 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundDispatcher.java
@@ -239,6 +239,14 @@
     }
 
     @MainThread
+    void onTimeout(int startId, int fgsType) {
+        Logger.get().info(TAG, "Foreground service timed out, FGS type: " + fgsType);
+        if (mCallback != null) {
+            mCallback.stop();
+        }
+    }
+
+    @MainThread
     private void handleStartForeground(@NonNull Intent intent) {
         Logger.get().info(TAG, "Started foreground service " + intent);
         final String workSpecId = intent.getStringExtra(KEY_WORKSPEC_ID);
diff --git a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java
index 5057015..b48701daf 100644
--- a/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java
+++ b/work/work-runtime/src/main/java/androidx/work/impl/foreground/SystemForegroundService.java
@@ -16,6 +16,8 @@
 
 package androidx.work.impl.foreground;
 
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
+
 import android.Manifest;
 import android.app.ForegroundServiceStartNotAllowedException;
 import android.app.Notification;
@@ -100,7 +102,7 @@
     @Override
     public void stop() {
         mIsShutdown = true;
-        Logger.get().debug(TAG, "All commands completed.");
+        Logger.get().debug(TAG, "Shutting down.");
         // No need to pass in startId; stopSelf() translates to stopSelf(-1) which is a hard stop
         // of all startCommands. This is the behavior we want.
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -110,6 +112,22 @@
         stopSelf();
     }
 
+    @Override
+    public void onTimeout(int startId) {
+        // On API devices 35 both overloads of onTimeout() are invoked so we do nothing on the
+        // version introduced in API 34 since the newer version will be invoked. However, on API 34
+        // devices only this version is invoked, and thus why both versions are overridden.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+            return;
+        }
+        mDispatcher.onTimeout(startId, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE);
+    }
+
+    @Override
+    public void onTimeout(int startId, int fgsType) {
+        mDispatcher.onTimeout(startId, fgsType);
+    }
+
     @MainThread
     @Override
     public void startForeground(