Merge "Use 10 jarjar shards unconditionally" into main
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index a949ff5..787fdee 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -1023,7 +1023,6 @@
     api_levels_annotations_enabled: true,
     api_levels_annotations_dirs: [
         "sdk-dir",
-        "api-versions-jars-dir",
     ],
 }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8a85406..a624994 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6950,7 +6950,7 @@
                         Slog.w(TAG, "Low overhead tracing feature is not enabled");
                         break;
                     }
-                    VMDebug.startLowOverheadTrace();
+                    VMDebug.startLowOverheadTraceForAllMethods();
                     break;
                 default:
                     try {
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index ceee898b..4249372 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -43,7 +43,7 @@
 
 flag {
     name: "secure_array_zeroization"
-    namespace: "platform_security"
+    namespace: "security"
     description: "Enable secure array zeroization"
     bug: "320392352"
     metadata {
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index ee5bd65..32eb080 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -684,14 +684,15 @@
     }
 
     private void setMiniresolverPadding() {
-        Insets systemWindowInsets =
-                getWindowManager().getCurrentWindowMetrics().getWindowInsets().getInsets(
-                        WindowInsets.Type.systemBars());
-
         View buttonContainer = findViewById(R.id.button_bar_container);
-        buttonContainer.setPadding(0, 0, 0,
-                systemWindowInsets.bottom + getResources().getDimensionPixelOffset(
-                        R.dimen.resolver_button_bar_spacing));
+        if (buttonContainer != null) {
+            Insets systemWindowInsets =
+                    getWindowManager().getCurrentWindowMetrics().getWindowInsets().getInsets(
+                            WindowInsets.Type.systemBars());
+            buttonContainer.setPadding(0, 0, 0,
+                    systemWindowInsets.bottom + getResources().getDimensionPixelOffset(
+                            R.dimen.resolver_button_bar_spacing));
+        }
     }
 
     @VisibleForTesting
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index a194535..db65d31 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -929,8 +929,11 @@
 
         if (shouldUseMiniResolver()) {
             View buttonContainer = findViewById(R.id.button_bar_container);
-            buttonContainer.setPadding(0, 0, 0, mSystemWindowInsets.bottom
-                    + getResources().getDimensionPixelOffset(R.dimen.resolver_button_bar_spacing));
+            if (buttonContainer != null) {
+                buttonContainer.setPadding(0, 0, 0, mSystemWindowInsets.bottom
+                        + getResources().getDimensionPixelOffset(
+                                R.dimen.resolver_button_bar_spacing));
+            }
         }
 
         // Need extra padding so the list can fully scroll up
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 201e0c8..45c4ea0 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -180,6 +180,7 @@
     <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL" />
     <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST" />
     <protected-broadcast android:name="android.bluetooth.device.action.KEY_MISSING" />
+    <protected-broadcast android:name="android.bluetooth.device.action.ENCRYPTION_CHANGE" />
     <protected-broadcast android:name="android.bluetooth.device.action.SDP_RECORD" />
     <protected-broadcast android:name="android.bluetooth.device.action.BATTERY_LEVEL_CHANGED" />
     <protected-broadcast android:name="android.bluetooth.device.action.REMOTE_ISSUE_OCCURRED" />
@@ -8909,6 +8910,11 @@
                 android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.ZramMaintenance"
+                 android:exported="false"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service android:name="com.android.server.ZramWriteback"
                  android:exported="false"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cccd6ba..c01a8a9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5126,10 +5126,6 @@
     <!-- Whether or not swipe up gesture's opt-in setting is available on this device -->
     <bool name="config_swipe_up_gesture_setting_available">true</bool>
 
-    <!-- Applications which are disabled unless matching a particular sku -->
-    <string-array name="config_disableApksUnlessMatchedSku_apk_list" translatable="false" />
-    <string-array name="config_disableApkUnlessMatchedSku_skus_list" translatable="false" />
-
     <!-- Whether or not we should show the option to show battery percentage -->
     <bool name="config_battery_percentage_setting_available">true</bool>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e827102..a3d5be7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4259,10 +4259,6 @@
   <java-symbol type="integer" name="config_unfoldTransitionHalfFoldedTimeout" />
   <java-symbol type="array" name="config_perDeviceStateRotationLockDefaults" />
 
-
-  <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" />
-  <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" />
-
   <java-symbol type="string" name="config_misprovisionedDeviceModel" />
   <java-symbol type="string" name="config_misprovisionedBrandValue" />
 
diff --git a/graphics/java/android/graphics/OWNERS b/graphics/java/android/graphics/OWNERS
index ef8d26c..1ea1976 100644
--- a/graphics/java/android/graphics/OWNERS
+++ b/graphics/java/android/graphics/OWNERS
@@ -2,10 +2,10 @@
 
 romainguy@google.com
 jreck@google.com
-njawad@google.com
 sumir@google.com
 djsollen@google.com
-scroggo@google.com
+alecmouri@google.com
+sallyqi@google.com
 
 per-file BLASTBufferQueue.java = file:/services/core/java/com/android/server/wm/OWNERS
 per-file FontFamily.java = file:fonts/OWNERS
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
index bcd40a9..c4696d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java
@@ -192,15 +192,22 @@
                 throw new IllegalStateException("Sync Transactions must be serialized. In Flight: "
                         + mInFlight.mId + " - " + mInFlight.mWCT);
             }
-            mInFlight = this;
             if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT);
-            if (mLegacyTransition != null) {
-                mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
-                        mLegacyTransition.getAdapter(), this, mWCT);
-            } else {
-                mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
+            try {
+                if (mLegacyTransition != null) {
+                    mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(),
+                            mLegacyTransition.getAdapter(), this, mWCT);
+                } else {
+                    mId = new WindowOrganizer().applySyncTransaction(mWCT, this);
+                }
+            } catch (RuntimeException e) {
+                Slog.e(TAG, "Send failed", e);
+                // Finish current sync callback immediately.
+                onTransactionReady(mId, new SurfaceControl.Transaction());
+                return;
             }
             if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId);
+            mInFlight = this;
             mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT);
         }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 8077aee..8cec017 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -1080,9 +1080,12 @@
                                 change, layer, info, t, mLeashMap);
                         appearedTargets[nextTargetIdx++] = target;
                         // reparent into the original `mInfo` since that's where we are animating.
-                        final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
+                        final TransitionInfo.Root root = TransitionUtil.getRootFor(change, mInfo);
                         final boolean wasClosing = closingIdx >= 0;
-                        t.reparent(target.leash, mInfo.getRoot(rootIdx).getLeash());
+                        t.reparent(target.leash, root.getLeash());
+                        t.setPosition(target.leash,
+                                change.getStartAbsBounds().left - root.getOffset().x,
+                                change.getStartAbsBounds().top - root.getOffset().y);
                         t.setLayer(target.leash, layer);
                         if (wasClosing) {
                             // App was previously visible and is closing
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 782db35..cf54f6c 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -907,13 +907,13 @@
         /** @hide */
         public String[] validFeatures() {
             Feature[] features = getValidFeatures();
-            String[] res = new String[features.length];
-            for (int i = 0; i < res.length; i++) {
+            ArrayList<String> res = new ArrayList();
+            for (int i = 0; i < features.length; i++) {
                 if (!features[i].mInternal) {
-                    res[i] = features[i].mName;
+                    res.add(features[i].mName);
                 }
             }
-            return res;
+            return res.toArray(new String[0]);
         }
 
         private Feature[] getValidFeatures() {
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
index e12d828..fdddf13 100644
--- a/media/jni/OWNERS
+++ b/media/jni/OWNERS
@@ -4,5 +4,5 @@
 # extra for TV related files
 per-file android_media_tv_*=hgchen@google.com,quxiangfang@google.com
 
-per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=set noparent
-per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.java=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.cpp=set noparent
+per-file android_media_JetPlayer.cpp,android_media_MediaDataSource.cpp,android_media_MediaDataSource.h,android_media_MediaPlayer.cpp=file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index e4bc7b4..04df308 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -3,6 +3,7 @@
 chiujason@google.com
 cipson@google.com
 dsandler@android.com
+dswliu@google.com
 edgarwang@google.com
 evanlaird@google.com
 jiannan@google.com
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index c3c23e3..38bd29a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -596,6 +596,7 @@
         "-Adagger.fastInit=enabled",
         "-Adagger.explicitBindingConflictsWithInject=ERROR",
         "-Adagger.strictMultibindingValidation=enabled",
+        "-Adagger.useBindingGraphFix=ENABLED",
         "-Aroom.schemaLocation=frameworks/base/packages/SystemUI/schemas",
     ],
     kotlincflags: ["-Xjvm-default=all"],
@@ -773,6 +774,9 @@
     use_resource_processor: true,
     manifest: "tests/AndroidManifest-base.xml",
     resource_dirs: [],
+
+    kotlin_lang_version: "1.9",
+
     additional_manifests: ["tests/AndroidManifest.xml"],
     srcs: [
         "tests/src/**/*.kt",
@@ -815,6 +819,10 @@
         // TODO(b/352363800): Why do we need this?
         "-J-Xmx8192M",
     ],
+    javacflags: [
+        "-Adagger.useBindingGraphFix=ENABLED",
+    ],
+
     aaptflags: [
         "--extra-packages",
         "com.android.systemui",
@@ -904,7 +912,6 @@
         "truth",
     ],
 
-
     instrumentation_for: "SystemUIRobo-stub",
     java_resource_dirs: ["tests/robolectric/config"],
     plugins: [
@@ -940,7 +947,6 @@
         "truth",
     ],
 
-
     instrumentation_for: "SystemUIRobo-stub",
     java_resource_dirs: ["tests/robolectric/config"],
     plugins: [
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 795b395..7fadcc4 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -39,6 +39,7 @@
 gallmann@google.com
 graciecheng@google.com
 gwasserman@google.com
+helencheuk@google.com
 hwwang@google.com
 hyunyoungs@google.com
 ikateryna@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
index 208a17c..ebe603b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/OWNERS
@@ -2,6 +2,8 @@
 
 # Bug component: 78010
 
+include /services/core/java/com/android/server/biometrics/OWNERS
+
 amiko@google.com
 beverlyt@google.com
 bhinegardner@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 5a616df..2d36f16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -138,10 +138,12 @@
         DumpUtilsKt.withIncreasedIndent(pw, () -> {
             pw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility()));
             pw.println("manageButton showHistory: " + mShowHistory);
-            pw.println("manageButton visibility: "
-                    + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
-            pw.println("dismissButton visibility: "
-                    + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
+            if (mManageOrHistoryButton != null)
+                pw.println("mManageOrHistoryButton visibility: "
+                        + DumpUtilsKt.visibilityString(mManageOrHistoryButton.getVisibility()));
+            if (mClearAllButton != null)
+                pw.println("mClearAllButton visibility: "
+                        + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
         });
     }
 
diff --git a/ravenwood/scripts/pta-framework.sh b/ravenwood/scripts/pta-framework.sh
new file mode 100755
index 0000000..224ab59
--- /dev/null
+++ b/ravenwood/scripts/pta-framework.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Use "ravehleper pta" to create a shell script which:
+# - Reads the text "policy" files
+# - Convert to java annotations (using sed)
+#
+
+set -e
+
+
+# Uncomment it to always build ravenhelper (slow)
+# ${BUILD_CMD:-m} ravenhelper
+
+# Get the target directory. Default to $ANDROID_BUILD_TOP.
+TARGET_DIR="${TARGET_DIR:-${ANDROID_BUILD_TOP?\$ANDROID_BUILD_TOP must be set}}"
+
+echo "Target dir=$TARGET_DIR"
+
+cd "$TARGET_DIR"
+
+# Add -v or -d as needed.
+extra_args="$@"
+
+OUT_SCRIPT="${OUT_SCRIPT:-/tmp/pta.sh}"
+
+rm -f "$OUT_SCRIPT"
+
+# If you want to run on other files, run this script with the following
+# env vars predefined.
+
+POLICIES="${POLICIES:-
+frameworks/base/ravenwood/texts/ravenwood-common-policies.txt
+frameworks/base/ravenwood/texts/ravenwood-framework-policies.txt
+}"
+
+SOURCES="${SOURCES:-
+frameworks/base/core/java/
+frameworks/base/graphics/java/
+}"
+
+AAC="${AAC:-frameworks/base/ravenwood/texts/ravenwood-annotation-allowed-classes.txt}"
+
+with_flag() {
+    local flag="$1"
+    shift
+
+    for arg in "$@"; do
+        echo "$flag $arg"
+    done
+}
+
+run() {
+    echo "Running: $*"
+    "$@"
+}
+
+run_pta() {
+    local extra_args="$@"
+
+    run ${RAVENHELPER_CMD:-ravenhelper pta} \
+        --output-script $OUT_SCRIPT \
+        --annotation-allowed-classes-file $AAC \
+        $(with_flag --policy-override-file $POLICIES) \
+        $(with_flag --src $SOURCES) \
+        $extra_args
+
+    if ! [[ -f $OUT_SCRIPT ]] ; then
+        # no operations generated.
+        exit 0
+    fi
+
+    echo
+    echo "Created script at $OUT_SCRIPT. Run it with: sh $OUT_SCRIPT"
+    return 0
+}
+
+run_pta "$extra_args"
\ No newline at end of file
diff --git a/ravenwood/texts/ravenwood-common-policies.txt b/ravenwood/texts/ravenwood-common-policies.txt
index 83c3151..fd4ea6c 100644
--- a/ravenwood/texts/ravenwood-common-policies.txt
+++ b/ravenwood/texts/ravenwood-common-policies.txt
@@ -1,5 +1,8 @@
 # Ravenwood "policy" that should apply to all code.
 
+# The "no-pta" marker is used to exclude the lines from "ravenhelper pta",
+# which tries to convert policies to annotations.
+
 # Keep all AIDL interfaces
 class :aidl keepclass
 
@@ -13,8 +16,8 @@
 class :r keepclass
 
 # Support APIs not available in standard JRE
-class java.io.FileDescriptor keep
+class java.io.FileDescriptor  # no-pta
     method getInt$ @com.android.ravenwood.RavenwoodJdkPatch.getInt$
     method setInt$ @com.android.ravenwood.RavenwoodJdkPatch.setInt$
-class java.util.LinkedHashMap keep
+class java.util.LinkedHashMap  # no-pta
     method eldest @com.android.ravenwood.RavenwoodJdkPatch.eldest
diff --git a/ravenwood/texts/ravenwood-framework-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
index 26b6fe3..4033782 100644
--- a/ravenwood/texts/ravenwood-framework-policies.txt
+++ b/ravenwood/texts/ravenwood-framework-policies.txt
@@ -1,62 +1,65 @@
 # Ravenwood "policy" file for framework-minus-apex.
 
+# The "no-pta" marker is used to exclude the lines from "ravenhelper pta",
+# which tries to convert policies to annotations.
+
 # To avoid VerifyError on nano proto files (b/324063814), we rename nano proto classes.
 # Note: The "rename" directive must use slashes (/) as a package name separator.
 rename com/.*/nano/   devicenano/
 rename android/.*/nano/   devicenano/
 
 # StatsD auto-generated
-class com.android.internal.util.FrameworkStatsLog keepclass
+class com.android.internal.util.FrameworkStatsLog keepclass  # no-pta
 
 # Exported to Mainline modules; cannot use annotations
-class com.android.internal.util.FastXmlSerializer keepclass
-class com.android.internal.util.FileRotator keepclass
-class com.android.internal.util.HexDump keepclass
-class com.android.internal.util.IndentingPrintWriter keepclass
-class com.android.internal.util.LocalLog keepclass
-class com.android.internal.util.MessageUtils keepclass
-class com.android.internal.util.TokenBucket keepclass
-class android.os.HandlerExecutor keepclass
-class android.util.BackupUtils keepclass
-class android.util.IndentingPrintWriter keepclass
-class android.util.LocalLog keepclass
-class android.util.Pair keepclass
-class android.util.Rational keepclass
+class com.android.internal.util.FastXmlSerializer keepclass  # no-pta
+class com.android.internal.util.FileRotator keepclass  # no-pta
+class com.android.internal.util.HexDump keepclass  # no-pta
+class com.android.internal.util.IndentingPrintWriter keepclass  # no-pta
+class com.android.internal.util.LocalLog keepclass  # no-pta
+class com.android.internal.util.MessageUtils keepclass  # no-pta
+class com.android.internal.util.TokenBucket keepclass  # no-pta
+class android.os.HandlerExecutor keepclass  # no-pta
+class android.util.BackupUtils keepclass  # no-pta
+class android.util.IndentingPrintWriter keepclass  # no-pta
+class android.util.LocalLog keepclass  # no-pta
+class android.util.Pair keepclass  # no-pta
+class android.util.Rational keepclass  # no-pta
 
 # From modules-utils; cannot use annotations
-class com.android.internal.util.Preconditions keepclass
-class com.android.internal.logging.InstanceId keepclass
-class com.android.internal.logging.InstanceIdSequence keepclass
-class com.android.internal.logging.UiEvent keepclass
-class com.android.internal.logging.UiEventLogger keepclass
+class com.android.internal.util.Preconditions keepclass  # no-pta
+class com.android.internal.logging.InstanceId keepclass  # no-pta
+class com.android.internal.logging.InstanceIdSequence keepclass  # no-pta
+class com.android.internal.logging.UiEvent keepclass  # no-pta
+class com.android.internal.logging.UiEventLogger keepclass  # no-pta
 
 # From modules-utils; cannot use annotations
-class com.android.modules.utils.BinaryXmlPullParser keepclass
-class com.android.modules.utils.BinaryXmlSerializer keepclass
-class com.android.modules.utils.FastDataInput keepclass
-class com.android.modules.utils.FastDataOutput keepclass
-class com.android.modules.utils.ModifiedUtf8 keepclass
-class com.android.modules.utils.TypedXmlPullParser keepclass
-class com.android.modules.utils.TypedXmlSerializer keepclass
+class com.android.modules.utils.BinaryXmlPullParser keepclass  # no-pta
+class com.android.modules.utils.BinaryXmlSerializer keepclass  # no-pta
+class com.android.modules.utils.FastDataInput keepclass  # no-pta
+class com.android.modules.utils.FastDataOutput keepclass  # no-pta
+class com.android.modules.utils.ModifiedUtf8 keepclass  # no-pta
+class com.android.modules.utils.TypedXmlPullParser keepclass  # no-pta
+class com.android.modules.utils.TypedXmlSerializer keepclass  # no-pta
 
 # Uri
-class android.net.Uri keepclass
-class android.net.UriCodec keepclass
+class android.net.Uri keepclass  # no-pta
+class android.net.UriCodec keepclass  # no-pta
 
 # Telephony
-class android.telephony.PinResult keepclass
+class android.telephony.PinResult keepclass  # no-pta
 
 # Just enough to support mocking, no further functionality
-class android.content.BroadcastReceiver keep
+class android.content.BroadcastReceiver keep  # no-pta
     method <init> ()V keep
-class android.content.Context keep
+class android.content.Context keep  # no-pta
     method <init> ()V keep
-    method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; keep
-class android.content.pm.PackageManager
+    method getSystemService (Ljava/lang/Class;)Ljava/lang/Object; keep  # no-pta
+class android.content.pm.PackageManager  # no-pta
     method <init> ()V keep
-class android.text.ClipboardManager keep
+class android.text.ClipboardManager keep  # no-pta
     method <init> ()V keep
 
 # Just enough to allow ResourcesManager to run
-class android.hardware.display.DisplayManagerGlobal keep
+class android.hardware.display.DisplayManagerGlobal keep # no-pta
     method getInstance ()Landroid/hardware/display/DisplayManagerGlobal; ignore
diff --git a/ravenwood/texts/ravenwood-services-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt
index 530e5c8..e3be9af 100644
--- a/ravenwood/texts/ravenwood-services-policies.txt
+++ b/ravenwood/texts/ravenwood-services-policies.txt
@@ -1,12 +1,15 @@
 # Ravenwood "policy" file for services.core.
 
+# The "no-pta" marker is used to exclude the lines from "ravenhelper pta",
+# which tries to convert policies to annotations.
+
 # Auto-generated from XSD
-class com.android.server.compat.config.Change keepclass
-class com.android.server.compat.config.Config keepclass
-class com.android.server.compat.config.XmlParser keepclass
-class com.android.server.compat.overrides.ChangeOverrides keepclass
-class com.android.server.compat.overrides.OverrideValue keepclass
-class com.android.server.compat.overrides.Overrides keepclass
-class com.android.server.compat.overrides.RawOverrideValue keepclass
-class com.android.server.compat.overrides.XmlParser keepclass
-class com.android.server.compat.overrides.XmlWriter keepclass
\ No newline at end of file
+class com.android.server.compat.config.Change keepclass  # no-pta
+class com.android.server.compat.config.Config keepclass  # no-pta
+class com.android.server.compat.config.XmlParser keepclass  # no-pta
+class com.android.server.compat.overrides.ChangeOverrides keepclass  # no-pta
+class com.android.server.compat.overrides.OverrideValue keepclass  # no-pta
+class com.android.server.compat.overrides.Overrides keepclass  # no-pta
+class com.android.server.compat.overrides.RawOverrideValue keepclass  # no-pta
+class com.android.server.compat.overrides.XmlParser keepclass  # no-pta
+class com.android.server.compat.overrides.XmlWriter keepclass  # no-pta
\ No newline at end of file
diff --git a/ravenwood/texts/ravenwood-standard-options.txt b/ravenwood/texts/ravenwood-standard-options.txt
index 3ec3e3c..27223d8b 100644
--- a/ravenwood/texts/ravenwood-standard-options.txt
+++ b/ravenwood/texts/ravenwood-standard-options.txt
@@ -5,6 +5,8 @@
 # Keep all classes / methods / fields, but make the methods throw.
 --default-throw
 
+--delete-finals
+
 # Uncomment below lines to enable each feature.
 
 #--default-method-call-hook
diff --git a/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
index 001943c..9c46a16 100644
--- a/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
+++ b/ravenwood/tools/hoststubgen/hoststubgen-standard-options.txt
@@ -2,6 +2,8 @@
 
 --debug
 
+--delete-finals
+
 # Uncomment below lines to enable each feature.
 
 #--default-method-call-hook
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index cc704b2..9859475 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -411,6 +411,8 @@
             stats = stats,
             enablePreTrace = options.enablePreTrace.get,
             enablePostTrace = options.enablePostTrace.get,
+            deleteClassFinals = options.deleteFinals.get,
+            deleteMethodFinals = options.deleteFinals.get,
         )
         outVisitor = BaseAdapter.getVisitor(
             classInternalName, classes, outVisitor, filter,
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
index 55e853e..ae9276f 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGenOptions.kt
@@ -106,6 +106,8 @@
 
         var cleanUpOnError: SetOnce<Boolean> = SetOnce(false),
 
+        var deleteFinals: SetOnce<Boolean> = SetOnce(false),
+
         var enableClassChecker: SetOnce<Boolean> = SetOnce(false),
         var enablePreTrace: SetOnce<Boolean> = SetOnce(false),
         var enablePostTrace: SetOnce<Boolean> = SetOnce(false),
@@ -218,6 +220,8 @@
                         "--gen-keep-all-file" ->
                             ret.inputJarAsKeepAllFile.set(nextArg())
 
+                        "--delete-finals" -> ret.deleteFinals.set(true)
+
                         // Following options are for debugging.
                         "--enable-class-checker" -> ret.enableClassChecker.set(true)
                         "--no-class-checker" -> ret.enableClassChecker.set(false)
@@ -293,6 +297,7 @@
               defaultMethodCallHook=$defaultMethodCallHook,
               policyOverrideFiles=${policyOverrideFiles.toTypedArray().contentToString()},
               defaultPolicy=$defaultPolicy,
+              deleteFinals=$deleteFinals,
               cleanUpOnError=$cleanUpOnError,
               enableClassChecker=$enableClassChecker,
               enablePreTrace=$enablePreTrace,
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index c550083..9782f3d 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -69,9 +69,9 @@
     fun onRename(pattern: Pattern, prefix: String)
 
     /** "class" directive. */
-    fun onSimpleClassStart(className: String)
+    fun onClassStart(className: String)
     fun onSimpleClassPolicy(className: String, policy: FilterPolicyWithReason)
-    fun onSimpleClassEnd(className: String)
+    fun onClassEnd(className: String)
 
     fun onSubClassPolicy(superClassName: String, policy: FilterPolicyWithReason)
     fun onRedirectionClass(fromClassName: String, toClassName: String)
@@ -162,10 +162,10 @@
             )
         }
 
-        override fun onSimpleClassStart(className: String) {
+        override fun onClassStart(className: String) {
         }
 
-        override fun onSimpleClassEnd(className: String) {
+        override fun onClassEnd(className: String) {
         }
 
         override fun onSimpleClassPolicy(className: String, policy: FilterPolicyWithReason) {
@@ -273,20 +273,23 @@
     private var rFilePolicy: FilterPolicyWithReason? = null
 
     /** Name of the file that's currently being processed.  */
-    var filename: String? = null
+    var filename: String = ""
         private set
 
     /** 1-based line number in the current file */
     var lineNumber = -1
         private set
 
+    /** Current line */
+    var currentLineText = ""
+        private set
+
     /**
      * Parse a given "policy" file.
      */
     fun parse(reader: Reader, inputName: String, processor: PolicyFileProcessor) {
         filename = inputName
 
-        log.i("Parsing text policy file $inputName ...")
         this.processor = processor
         BufferedReader(reader).use { rd ->
             lineNumber = 0
@@ -297,6 +300,7 @@
                         break
                     }
                     lineNumber++
+                    currentLineText = line
                     line = normalizeTextLine(line) // Remove comment and trim.
                     if (line.isEmpty()) {
                         continue
@@ -312,7 +316,7 @@
 
     private fun finishLastClass() {
         currentClassName?.let { className ->
-            processor.onSimpleClassEnd(className)
+            processor.onClassEnd(className)
             currentClassName = null
         }
     }
@@ -416,7 +420,7 @@
         if (fields.size <= 1) {
             throw ParseException("Class ('c') expects 1 or 2 fields.")
         }
-        val className = fields[1]
+        val className = fields[1].toHumanReadableClassName()
 
         // superClass is set when the class name starts with a "*".
         val superClass = resolveExtendingClass(className)
@@ -436,6 +440,8 @@
             // It's a redirection class.
             val toClass = policyStr.substring(1)
 
+            currentClassName = className
+            processor.onClassStart(className)
             processor.onRedirectionClass(className, toClass)
         } else if (policyStr.startsWith("~")) {
             if (classType != SpecialClass.NotSpecial) {
@@ -447,6 +453,8 @@
             // It's a class-load hook
             val callback = policyStr.substring(1)
 
+            currentClassName = className
+            processor.onClassStart(className)
             processor.onClassLoadHook(className, callback)
         } else {
             // Special case: if it's a class directive with no policy, then it encloses
@@ -455,7 +463,6 @@
             if (policyStr == "") {
                 if (classType == SpecialClass.NotSpecial && superClass == null) {
                     currentClassName = className
-                    processor.onSimpleClassStart(className)
                     return
                 }
                 throw ParseException("Special class or subclass directive must have a policy")
@@ -471,7 +478,7 @@
                     // TODO: Duplicate check, etc
                     if (superClass == null) {
                         currentClassName = className
-                        processor.onSimpleClassStart(className)
+                        processor.onClassStart(className)
                         processor.onSimpleClassPolicy(className, policy.withReason(FILTER_REASON))
                     } else {
                         processor.onSubClassPolicy(
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
index 261ef59c..a08d1d6 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/BaseAdapter.kt
@@ -50,7 +50,13 @@
         val errors: HostStubGenErrors,
         val stats: HostStubGenStats?,
         val enablePreTrace: Boolean,
-        val enablePostTrace: Boolean
+        val enablePostTrace: Boolean,
+        val deleteClassFinals: Boolean,
+        val deleteMethodFinals: Boolean,
+        // We don't remove finals from fields, because final fields have a stronger memory
+        // guarantee than non-final fields, see:
+        // https://docs.oracle.com/javase/specs/jls/se22/html/jls-17.html#jls-17.5
+        // i.e. changing a final field to non-final _could_ result in different behavior.
     )
 
     protected lateinit var currentPackageName: String
@@ -58,14 +64,33 @@
     protected var redirectionClass: String? = null
     protected lateinit var classPolicy: FilterPolicyWithReason
 
+    private fun isEnum(access: Int): Boolean {
+        return (access and Opcodes.ACC_ENUM) != 0
+    }
+
+    protected fun modifyClassAccess(access: Int): Int {
+        if (options.deleteClassFinals && !isEnum(access)) {
+            return access and Opcodes.ACC_FINAL.inv()
+        }
+        return access
+    }
+
+    protected fun modifyMethodAccess(access: Int): Int {
+        if (options.deleteMethodFinals) {
+            return access and Opcodes.ACC_FINAL.inv()
+        }
+        return access
+    }
+
     override fun visit(
         version: Int,
-        access: Int,
+        origAccess: Int,
         name: String,
         signature: String?,
         superName: String?,
         interfaces: Array<String>,
     ) {
+        val access = modifyClassAccess(origAccess)
         super.visit(version, access, name, signature, superName, interfaces)
         currentClassName = name
         currentPackageName = getPackageNameFromFullClassName(name)
@@ -130,13 +155,14 @@
         }
     }
 
-    override fun visitMethod(
-        access: Int,
+    final override fun visitMethod(
+        origAccess: Int,
         name: String,
         descriptor: String,
         signature: String?,
         exceptions: Array<String>?,
     ): MethodVisitor? {
+        val access = modifyMethodAccess(origAccess)
         if (skipMemberModificationNestCount > 0) {
             return super.visitMethod(access, name, descriptor, signature, exceptions)
         }
@@ -176,6 +202,7 @@
                 if (newAccess == NOT_COMPATIBLE) {
                     return null
                 }
+                newAccess = modifyMethodAccess(newAccess)
 
                 log.v(
                     "Emitting %s.%s%s as %s %s", currentClassName, name, descriptor,
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
index 567a69e..70e7d46 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/visitors/ImplGeneratingAdapter.kt
@@ -51,12 +51,13 @@
 
     override fun visit(
         version: Int,
-        access: Int,
+        origAccess: Int,
         name: String,
         signature: String?,
         superName: String?,
         interfaces: Array<String>
     ) {
+        val access = modifyClassAccess(origAccess)
         super.visit(version, access, name, signature, superName, interfaces)
 
         classLoadHooks = filter.getClassLoadHooks(currentClassName)
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 5e5ca62..b009b09 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output.RELEASE_TARGET_JAVA_21/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -7,6 +7,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestClassLoadHook
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
   public abstract java.lang.String value();
     descriptor: ()Ljava/lang/String;
     flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -30,6 +32,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestIgnore
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestIgnore.java"
 RuntimeVisibleAnnotations:
@@ -50,6 +54,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestKeep
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestKeep.java"
 RuntimeVisibleAnnotations:
@@ -70,6 +76,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestRedirect
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestRedirect.java"
 RuntimeVisibleAnnotations:
@@ -90,6 +98,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestRedirectionClass
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
   public abstract java.lang.String value();
     descriptor: ()Ljava/lang/String;
     flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -113,6 +123,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestRemove
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestRemove.java"
 RuntimeVisibleAnnotations:
@@ -133,6 +145,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestStaticInitializerKeep
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestStaticInitializerKeep.java"
 RuntimeVisibleAnnotations:
@@ -153,6 +167,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestSubstitute
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
   public abstract java.lang.String suffix();
     descriptor: ()Ljava/lang/String;
     flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -176,6 +192,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestThrow
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestThrow.java"
 RuntimeVisibleAnnotations:
@@ -196,6 +214,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestWholeClassKeep
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestWholeClassKeep.java"
 RuntimeVisibleAnnotations:
@@ -216,6 +236,8 @@
   this_class: #x                          // android/hosttest/annotation/tests/HostSideTestSuppress
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestSuppress.java"
 RuntimeVisibleAnnotations:
@@ -232,6 +254,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -273,6 +297,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -314,6 +340,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 0, attributes: 3
+Constant pool:
+{
 }
 SourceFile: "IPretendingAidl.java"
 NestMembers:
@@ -331,6 +359,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 2, attributes: 3
+Constant pool:
+{
   public static int[] ARRAY;
     descriptor: [I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -376,6 +406,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.R();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -396,13 +428,15 @@
   public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
   Compiled from "TinyFrameworkAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
   minor version: 0
   major version: 65
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 9, attributes: 2
+Constant pool:
+{
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -433,9 +467,9 @@
       x: #x()
         android.hosttest.annotation.HostSideTestKeep
 
-  public int addOne(int);
+  public final int addOne(int);
     descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
     Code:
       stack=2, locals=2, args_size=2
          x: iload_1
@@ -505,18 +539,18 @@
             0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
             0       4     1 value   I
 
-  public static native int nativeAddThree(int);
+  public static final native int nativeAddThree(int);
     descriptor: (I)I
-    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+    flags: (0x0119) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_NATIVE
     RuntimeInvisibleAnnotations:
       x: #x(#x=s#x)
         android.hosttest.annotation.HostSideTestSubstitute(
           suffix="_host"
         )
 
-  private static int nativeAddThree_host(int);
+  private static final int nativeAddThree_host(int);
     descriptor: (I)I
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
     Code:
       stack=2, locals=1, args_size=1
          x: iload_0
@@ -578,6 +612,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 3, attributes: 2
+Constant pool:
+{
   public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
     descriptor: Ljava/util/Set;
     flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
@@ -640,6 +676,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 6, attributes: 2
+Constant pool:
+{
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -764,6 +802,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 2, attributes: 2
+Constant pool:
+{
   public static boolean sInitialized;
     descriptor: Z
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -818,6 +858,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 2, attributes: 2
+Constant pool:
+{
   public static boolean sInitialized;
     descriptor: Z
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -878,6 +920,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
   super_class: #x                        // java/lang/Enum
   interfaces: 0, fields: 6, methods: 7, attributes: 3
+Constant pool:
+{
   public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
     descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
     flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
@@ -1081,6 +1125,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
   super_class: #x                        // java/lang/Enum
   interfaces: 0, fields: 3, methods: 5, attributes: 3
+Constant pool:
+{
   public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
     descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
     flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
@@ -1202,6 +1248,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -1256,6 +1304,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 17, attributes: 1
+Constant pool:
+{
   public int stub;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -1507,6 +1557,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 8, attributes: 5
+Constant pool:
+{
   public final java.util.function.Supplier<java.lang.Integer> mSupplier;
     descriptor: Ljava/util/function/Supplier;
     flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -1661,6 +1713,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 8, attributes: 5
+Constant pool:
+{
   public final java.util.function.Supplier<java.lang.Integer> mSupplier;
     descriptor: Ljava/util/function/Supplier;
     flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -1816,6 +1870,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 3, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -1873,6 +1929,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 5, attributes: 5
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -1984,6 +2042,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 14, attributes: 2
+Constant pool:
+{
   int value;
     descriptor: I
     flags: (0x0000)
@@ -2157,6 +2217,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 7, attributes: 2
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -2263,6 +2325,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
   com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$1(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
     descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
     flags: (0x0000)
@@ -2321,6 +2385,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
   com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
     descriptor: ()V
     flags: (0x0000)
@@ -2375,6 +2441,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
   com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$3(com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses);
     descriptor: (Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;)V
     flags: (0x0000)
@@ -2433,6 +2501,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
   com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
     descriptor: ()V
     flags: (0x0000)
@@ -2487,6 +2557,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2521,6 +2593,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2558,6 +2632,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
   com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
     descriptor: ()V
     flags: (0x0000)
@@ -2613,6 +2689,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2647,6 +2725,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 2, attributes: 3
+Constant pool:
+{
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2694,6 +2774,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
   interfaces: 0, fields: 0, methods: 1, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
     descriptor: (I)V
     flags: (0x0001) ACC_PUBLIC
@@ -2723,6 +2805,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 4, attributes: 4
+Constant pool:
+{
   public final java.util.function.Supplier<java.lang.Integer> mSupplier;
     descriptor: Ljava/util/function/Supplier;
     flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -2827,6 +2911,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -2869,6 +2955,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -2911,6 +2999,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 2, attributes: 2
+Constant pool:
+{
   private final int mValue;
     descriptor: I
     flags: (0x0012) ACC_PRIVATE, ACC_FINAL
@@ -2958,6 +3048,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.packagetest.A();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -2981,6 +3073,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/B
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.packagetest.B();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3004,6 +3098,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.packagetest.sub.A();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3027,6 +3123,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/B
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.packagetest.sub.B();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3050,6 +3148,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.C1();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3073,6 +3173,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.C2();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3096,6 +3198,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.C3();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3119,6 +3223,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.CA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3142,6 +3248,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.CB();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3165,6 +3273,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3188,6 +3298,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3211,6 +3323,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C3
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3234,6 +3348,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/CA
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3257,6 +3373,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/CB
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3280,6 +3398,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/CB
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3303,6 +3423,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3326,6 +3448,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3349,6 +3473,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3372,6 +3498,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3395,6 +3523,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3418,6 +3548,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3441,6 +3573,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3464,6 +3598,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3487,6 +3623,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3510,6 +3648,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3533,6 +3673,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_None
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_None();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3556,6 +3698,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "I1.java"
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
@@ -3567,6 +3711,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "I2.java"
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
@@ -3578,6 +3724,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "I3.java"
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
@@ -3589,6 +3737,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "IA.java"
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
@@ -3600,6 +3750,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "IB.java"
 ## Class: com/supported/UnsupportedClass.class
@@ -3611,6 +3763,8 @@
   this_class: #x                          // com/supported/UnsupportedClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 2, attributes: 2
+Constant pool:
+{
   private final int mValue;
     descriptor: I
     flags: (0x0012) ACC_PRIVATE, ACC_FINAL
@@ -3658,6 +3812,8 @@
   this_class: #x                         // com/unsupported/UnsupportedClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
   public com.unsupported.UnsupportedClass(int);
     descriptor: (I)V
     flags: (0x0001) ACC_PUBLIC
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 103e152..ad41342 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -7,6 +7,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestClassLoadHook
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
   public abstract java.lang.String value();
     descriptor: ()Ljava/lang/String;
     flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -30,6 +32,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestIgnore
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestIgnore.java"
 RuntimeVisibleAnnotations:
@@ -50,6 +54,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestKeep
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestKeep.java"
 RuntimeVisibleAnnotations:
@@ -70,6 +76,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestRedirect
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestRedirect.java"
 RuntimeVisibleAnnotations:
@@ -90,6 +98,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestRedirectionClass
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
   public abstract java.lang.String value();
     descriptor: ()Ljava/lang/String;
     flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -113,6 +123,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestRemove
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestRemove.java"
 RuntimeVisibleAnnotations:
@@ -133,6 +145,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestStaticInitializerKeep
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestStaticInitializerKeep.java"
 RuntimeVisibleAnnotations:
@@ -153,6 +167,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestSubstitute
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 2
+Constant pool:
+{
   public abstract java.lang.String suffix();
     descriptor: ()Ljava/lang/String;
     flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
@@ -176,6 +192,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestThrow
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestThrow.java"
 RuntimeVisibleAnnotations:
@@ -196,6 +214,8 @@
   this_class: #x                          // android/hosttest/annotation/HostSideTestWholeClassKeep
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestWholeClassKeep.java"
 RuntimeVisibleAnnotations:
@@ -216,6 +236,8 @@
   this_class: #x                          // android/hosttest/annotation/tests/HostSideTestSuppress
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 2
+Constant pool:
+{
 }
 SourceFile: "HostSideTestSuppress.java"
 RuntimeVisibleAnnotations:
@@ -232,6 +254,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub$Proxy
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub$Proxy();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -273,6 +297,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl$Stub
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.IPretendingAidl$Stub();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -314,6 +340,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/IPretendingAidl
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 0, attributes: 3
+Constant pool:
+{
 }
 SourceFile: "IPretendingAidl.java"
 NestMembers:
@@ -331,6 +359,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/R$Nested
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 2, attributes: 3
+Constant pool:
+{
   public static int[] ARRAY;
     descriptor: [I
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -376,6 +406,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/R
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.R();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -396,13 +428,15 @@
   public static #x= #x of #x;           // Nested=class com/android/hoststubgen/test/tinyframework/R$Nested of class com/android/hoststubgen/test/tinyframework/R
 ## Class: com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.class
   Compiled from "TinyFrameworkAnnotations.java"
-public class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
+public final class com.android.hoststubgen.test.tinyframework.TinyFrameworkAnnotations
   minor version: 0
   major version: 61
-  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+  flags: (0x0031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 9, attributes: 2
+Constant pool:
+{
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -433,9 +467,9 @@
       x: #x()
         android.hosttest.annotation.HostSideTestKeep
 
-  public int addOne(int);
+  public final int addOne(int);
     descriptor: (I)I
-    flags: (0x0001) ACC_PUBLIC
+    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
     Code:
       stack=2, locals=2, args_size=2
          x: iload_1
@@ -505,18 +539,18 @@
             0       4     0  this   Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations;
             0       4     1 value   I
 
-  public static native int nativeAddThree(int);
+  public static final native int nativeAddThree(int);
     descriptor: (I)I
-    flags: (0x0109) ACC_PUBLIC, ACC_STATIC, ACC_NATIVE
+    flags: (0x0119) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_NATIVE
     RuntimeInvisibleAnnotations:
       x: #x(#x=s#x)
         android.hosttest.annotation.HostSideTestSubstitute(
           suffix="_host"
         )
 
-  private static int nativeAddThree_host(int);
+  private static final int nativeAddThree_host(int);
     descriptor: (I)I
-    flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+    flags: (0x001a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL
     Code:
       stack=2, locals=1, args_size=1
          x: iload_0
@@ -578,6 +612,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassLoadHook
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 3, attributes: 2
+Constant pool:
+{
   public static final java.util.Set<java.lang.Class<?>> sLoadedClasses;
     descriptor: Ljava/util/Set;
     flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
@@ -640,6 +676,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWideAnnotations
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 6, attributes: 2
+Constant pool:
+{
   public int keep;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -764,6 +802,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerDefault
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 2, attributes: 2
+Constant pool:
+{
   public static boolean sInitialized;
     descriptor: Z
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -818,6 +858,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkClassWithInitializerStub
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 2, attributes: 2
+Constant pool:
+{
   public static boolean sInitialized;
     descriptor: Z
     flags: (0x0009) ACC_PUBLIC, ACC_STATIC
@@ -878,6 +920,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex
   super_class: #x                        // java/lang/Enum
   interfaces: 0, fields: 6, methods: 7, attributes: 3
+Constant pool:
+{
   public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumComplex RED;
     descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumComplex;
     flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
@@ -1081,6 +1125,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple
   super_class: #x                        // java/lang/Enum
   interfaces: 0, fields: 3, methods: 5, attributes: 3
+Constant pool:
+{
   public static final com.android.hoststubgen.test.tinyframework.TinyFrameworkEnumSimple CAT;
     descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkEnumSimple;
     flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
@@ -1202,6 +1248,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkExceptionTester
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkExceptionTester();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -1256,6 +1304,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 17, attributes: 1
+Constant pool:
+{
   public int stub;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -1507,6 +1557,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas$Nested
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 8, attributes: 5
+Constant pool:
+{
   public final java.util.function.Supplier<java.lang.Integer> mSupplier;
     descriptor: Ljava/util/function/Supplier;
     flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -1661,6 +1713,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkLambdas
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 8, attributes: 5
+Constant pool:
+{
   public final java.util.function.Supplier<java.lang.Integer> mSupplier;
     descriptor: Ljava/util/function/Supplier;
     flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -1816,6 +1870,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace$ReplaceTo
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 3, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace$ReplaceTo();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -1873,6 +1929,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkMethodCallReplace
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 5, attributes: 5
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkMethodCallReplace();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -1984,6 +2042,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 14, attributes: 2
+Constant pool:
+{
   int value;
     descriptor: I
     flags: (0x0000)
@@ -2157,6 +2217,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNative_host
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 7, attributes: 2
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkNative_host();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -2263,6 +2325,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$1
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 1, methods: 3, attributes: 5
+Constant pool:
+{
   final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
     descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
     flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
@@ -2328,6 +2392,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$2
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
   com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$2();
     descriptor: ()V
     flags: (0x0000)
@@ -2382,6 +2448,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$3
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 1, methods: 3, attributes: 5
+Constant pool:
+{
   final com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses this$0;
     descriptor: Lcom/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses;
     flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
@@ -2447,6 +2515,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$4
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
   com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$4();
     descriptor: ()V
     flags: (0x0000)
@@ -2501,6 +2571,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2535,6 +2607,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$InnerClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 1, attributes: 3
+Constant pool:
+{
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2579,6 +2653,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$1
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 3, attributes: 5
+Constant pool:
+{
   com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$StaticNestedClass$1();
     descriptor: ()V
     flags: (0x0000)
@@ -2634,6 +2710,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass$Double$NestedClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 1, attributes: 3
+Constant pool:
+{
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2668,6 +2746,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$StaticNestedClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 2, attributes: 3
+Constant pool:
+{
   public int value;
     descriptor: I
     flags: (0x0001) ACC_PUBLIC
@@ -2715,6 +2795,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$SubClass
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses$BaseClass
   interfaces: 0, fields: 0, methods: 1, attributes: 3
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkNestedClasses$SubClass(int);
     descriptor: (I)V
     flags: (0x0001) ACC_PUBLIC
@@ -2744,6 +2826,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkNestedClasses
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 2, methods: 4, attributes: 4
+Constant pool:
+{
   public final java.util.function.Supplier<java.lang.Integer> mSupplier;
     descriptor: Ljava/util/function/Supplier;
     flags: (0x0011) ACC_PUBLIC, ACC_FINAL
@@ -2848,6 +2932,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkPackageRedirect
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkPackageRedirect();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -2890,6 +2976,8 @@
   this_class: #x                         // com/android/hoststubgen/test/tinyframework/TinyFrameworkRenamedClassCaller
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.TinyFrameworkRenamedClassCaller();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -2932,6 +3020,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/TinyFrameworkToBeRenamed
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 2, attributes: 2
+Constant pool:
+{
   private final int mValue;
     descriptor: I
     flags: (0x0012) ACC_PRIVATE, ACC_FINAL
@@ -2979,6 +3069,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/A
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.packagetest.A();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3002,6 +3094,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/B
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.packagetest.B();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3025,6 +3119,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/A
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.packagetest.sub.A();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3048,6 +3144,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/packagetest/sub/B
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.packagetest.sub.B();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3071,6 +3169,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C1
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.C1();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3094,6 +3194,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C2
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.C2();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3117,6 +3219,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/C3
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.C3();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3140,6 +3244,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CA
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.CA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3163,6 +3269,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/CB
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.CB();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3186,6 +3294,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C1
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3209,6 +3319,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C2
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3232,6 +3344,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/C3
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3255,6 +3369,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/CA
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3278,6 +3394,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/CB
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3301,6 +3419,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA
   super_class: #x                         // com/android/hoststubgen/test/tinyframework/subclasstest/CB
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3324,6 +3444,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3347,6 +3469,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3370,6 +3494,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3393,6 +3519,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3416,6 +3544,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3439,6 +3569,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3462,6 +3594,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3485,6 +3619,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3508,6 +3644,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3531,6 +3669,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA
   super_class: #x                         // java/lang/Object
   interfaces: 2, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3554,6 +3694,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/Class_None
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 1, attributes: 1
+Constant pool:
+{
   public com.android.hoststubgen.test.tinyframework.subclasstest.Class_None();
     descriptor: ()V
     flags: (0x0001) ACC_PUBLIC
@@ -3577,6 +3719,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I1
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "I1.java"
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
@@ -3588,6 +3732,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I2
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "I2.java"
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
@@ -3599,6 +3745,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/I3
   super_class: #x                         // java/lang/Object
   interfaces: 1, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "I3.java"
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
@@ -3610,6 +3758,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IA
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "IA.java"
 ## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
@@ -3621,6 +3771,8 @@
   this_class: #x                          // com/android/hoststubgen/test/tinyframework/subclasstest/IB
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 0, attributes: 1
+Constant pool:
+{
 }
 SourceFile: "IB.java"
 ## Class: com/supported/UnsupportedClass.class
@@ -3632,6 +3784,8 @@
   this_class: #x                          // com/supported/UnsupportedClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 1, methods: 2, attributes: 2
+Constant pool:
+{
   private final int mValue;
     descriptor: I
     flags: (0x0012) ACC_PRIVATE, ACC_FINAL
@@ -3679,6 +3833,8 @@
   this_class: #x                         // com/unsupported/UnsupportedClass
   super_class: #x                         // java/lang/Object
   interfaces: 0, fields: 0, methods: 2, attributes: 2
+Constant pool:
+{
   public com.unsupported.UnsupportedClass(int);
     descriptor: (I)V
     flags: (0x0001) ACC_PUBLIC
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
index 3415deb..674937d 100644
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/TinyFrameworkAnnotations.java
@@ -28,7 +28,7 @@
 @HostSideTestKeep
 @HostSideTestClassLoadHook(
         "com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded")
-public class TinyFrameworkAnnotations {
+public final class TinyFrameworkAnnotations {
     @HostSideTestKeep
     public TinyFrameworkAnnotations() {
     }
@@ -42,7 +42,7 @@
     public int remove;
 
     @HostSideTestKeep
-    public int addOne(int value) {
+    public final int addOne(int value) {
         return value + 1;
     }
 
@@ -61,10 +61,10 @@
     }
 
     @HostSideTestSubstitute(suffix = "_host")
-    public static native int nativeAddThree(int value);
+    public final static native int nativeAddThree(int value);
 
     // This method is private, but at runtime, it'll inherit the visibility of the original method
-    private static int nativeAddThree_host(int value) {
+    private final static int nativeAddThree_host(int value) {
         return value + 3;
     }
 
diff --git a/ravenwood/tools/ravenhelper/Android.bp b/ravenwood/tools/ravenhelper/Android.bp
new file mode 100644
index 0000000..a7ee468
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/Android.bp
@@ -0,0 +1,26 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+java_binary_host {
+    name: "ravenhelper",
+    main_class: "com.android.platform.test.ravenwood.ravenhelper.RavenHelperMain",
+    srcs: ["src/**/*.kt"],
+    static_libs: [
+        "guava",
+        "hoststubgen-lib",
+        "junit",
+        "metalava-gradle-plugin-deps", // Get lint/PSI related classes from here.
+        "ow2-asm",
+        "ow2-asm-analysis",
+        "ow2-asm-commons",
+        "ow2-asm-tree",
+        "ow2-asm-util",
+    ],
+    visibility: ["//visibility:public"],
+}
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/RavenHelperMain.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/RavenHelperMain.kt
new file mode 100644
index 0000000..e6efbf6
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/RavenHelperMain.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@file:JvmName("RavenHelperMain")
+package com.android.platform.test.ravenwood.ravenhelper
+
+/*
+ * This file contains the main entry point for the "ravenhelper" command, which
+ * contains subcommands to help various tasks.
+ */
+
+import com.android.hoststubgen.GeneralUserErrorException
+import com.android.hoststubgen.LogLevel
+import com.android.hoststubgen.executableName
+import com.android.hoststubgen.log
+import com.android.hoststubgen.runMainWithBoilerplate
+import com.android.platform.test.ravenwood.ravenhelper.policytoannot.PtaProcessor
+
+interface SubcommandHandler {
+    fun handle(args: List<String>)
+}
+
+fun usage() {
+    System.out.println("""
+        Usage:
+          ravenhelper SUBCOMMAND options...
+
+        Subcommands:
+          pta:        "policy-to-annotations" Convert policy file to annotations.
+                      (See the pta-framework.sh script for usage.) 1
+
+        """.trimIndent())
+}
+
+fun main(args: Array<String>) {
+    executableName = "RavenHelper"
+    log.setConsoleLogLevel(LogLevel.Info)
+
+    runMainWithBoilerplate {
+        log.i("$executableName started")
+
+        if (args.size == 0) {
+            usage()
+            return
+        }
+
+        // Find the subcommand handler.
+        val subcommand = args[0]
+        val handler: SubcommandHandler = when (subcommand) {
+            "pta" -> PtaProcessor()
+            else -> {
+                usage()
+                throw GeneralUserErrorException("Unknown subcommand '$subcommand'")
+            }
+        }
+
+        // Run the subcommand.
+        handler.handle(args.copyOfRange(1, args.size).toList())
+    }
+}
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt
new file mode 100644
index 0000000..4a11259
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Annotations.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.platform.test.ravenwood.ravenhelper.policytoannot
+
+import com.android.hoststubgen.filters.FilterPolicy
+
+
+/**
+ * This class knows about the Ravenwood annotations.
+ */
+class Annotations {
+    enum class Target {
+        Class,
+        Field,
+        Method,
+    }
+
+    fun get(policy: FilterPolicy, target: Target): String? {
+        return when (policy) {
+            FilterPolicy.Keep ->
+                if (target == Target.Class) {
+                    "@android.ravenwood.annotation.RavenwoodKeepPartialClass"
+                } else {
+                    "@android.ravenwood.annotation.RavenwoodKeep"
+                }
+            FilterPolicy.KeepClass ->
+                "@android.ravenwood.annotation.RavenwoodKeepWholeClass"
+            FilterPolicy.Substitute ->
+                "@android.ravenwood.annotation.RavenwoodReplace"
+            FilterPolicy.Redirect ->
+                "@android.ravenwood.annotation.RavenwoodRedirect"
+            FilterPolicy.Throw ->
+                "@android.ravenwood.annotation.RavenwoodThrow"
+            FilterPolicy.Ignore -> null // Ignore has no annotation. (because it's not very safe.)
+            FilterPolicy.Remove ->
+                "@android.ravenwood.annotation.RavenwoodRemove"
+        }
+    }
+
+    private fun withArg(annot: String, arg: String): String {
+        return "@$annot(\"$arg\")"
+    }
+
+    fun getClassLoadHookAnnotation(arg: String): String {
+        return withArg("android.ravenwood.annotation.RavenwoodClassLoadHook", arg)
+    }
+
+    fun getRedirectionClassAnnotation(arg: String): String {
+        return withArg("android.ravenwood.annotation.RavenwoodRedirectionClass", arg)
+    }
+}
+
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Operations.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Operations.kt
new file mode 100644
index 0000000..3531ba95
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/Operations.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.platform.test.ravenwood.ravenhelper.policytoannot
+
+/*
+ * This file contains classes and functions about file edit operations, such as
+ * "insert a line", "delete a line".
+ */
+
+
+import com.android.hoststubgen.log
+import java.io.BufferedWriter
+import java.io.File
+
+enum class SourceOperationType {
+    /** Insert a line */
+    Insert,
+
+    /** delete a line */
+    Delete,
+
+    /** Insert a text at the beginning of a line */
+    Prepend,
+}
+
+data class SourceOperation(
+    /** Target file to edit. */
+    val sourceFile: String,
+
+    /** 1-based line number. Use -1 to add at the end of the file. */
+    val lineNumber: Int,
+
+    /** Operation type.*/
+    val type: SourceOperationType,
+
+    /** Operand -- text to insert or prepend. Ignored for delete. */
+    val text: String = "",
+
+    /** Human-readable description of why this operation was created */
+    val description: String,
+) {
+    override fun toString(): String {
+        return "SourceOperation(sourceFile='$sourceFile', " +
+                "lineNumber=$lineNumber, type=$type, text='$text' desc='$description')"
+    }
+}
+
+/**
+ * Stores list of [SourceOperation]s for each file.
+ */
+class SourceOperations {
+    var size: Int = 0
+        private set
+    private val fileOperations = mutableMapOf<String, MutableList<SourceOperation>>()
+
+    fun add(op: SourceOperation) {
+        log.forVerbose {
+            log.v("Adding operation: $op")
+        }
+        size++
+        fileOperations[op.sourceFile]?.let { ops ->
+            ops.add(op)
+            return
+        }
+        fileOperations[op.sourceFile] = mutableListOf(op)
+    }
+
+    /**
+     * Get the collected [SourceOperation]s for each file.
+     */
+    fun getOperations(): MutableMap<String, MutableList<SourceOperation>> {
+        return fileOperations
+    }
+}
+
+/**
+ * Create a shell script to apply all the operations (using sed).
+ */
+fun createShellScript(ops: SourceOperations, writer: BufferedWriter) {
+    // File header.
+    // Note ${'$'} is an ugly way to put a dollar sign ($) in a multi-line string.
+    writer.write(
+        """
+        #!/bin/bash
+
+        set -e # Finish when any command fails.
+
+        function apply() {
+            local file="${'$'}1"
+
+            # The script is given via stdin. Write it to file.
+            local sed="/tmp/pta-script.sed.tmp"
+            cat > "${'$'}sed"
+
+            echo "Running: sed -i -f \"${'$'}sed\" \"${'$'}file\""
+
+            if ! sed -i -f "${'$'}sed" "${'$'}file" ; then
+                echo 'Failed!' 1>&2
+                return 1
+            fi
+        }
+
+        """.trimIndent()
+    )
+
+    ops.getOperations().toSortedMap().forEach { (origFile, ops) ->
+        val file = File(origFile).absolutePath
+
+        writer.write("\n")
+
+        writer.write("#")
+        writer.write("=".repeat(78))
+        writer.write("\n")
+
+        writer.write("\n")
+
+        writer.write("apply \"$file\" <<'__END_OF_SCRIPT__'\n")
+        toSedScript(ops, writer)
+        writer.write("__END_OF_SCRIPT__\n")
+    }
+
+    writer.write("\n")
+
+    writer.write("echo \"All files updated successfully!\"\n")
+    writer.flush()
+}
+
+/**
+ * Create a sed script to apply a list of operations.
+ */
+private fun toSedScript(ops: List<SourceOperation>, writer: BufferedWriter) {
+    ops.sortedBy { it.lineNumber }.forEach { op ->
+        if (op.text.contains('\n')) {
+            throw RuntimeException("Operation $op may not contain newlines.")
+        }
+
+        // Convert each operation to a sed operation. Examples:
+        //
+        // - Insert "abc" to line 2
+        //   2i\
+        //   abc
+        //
+        // - Insert "abc" to the end of the file
+        //   $a\
+        //   abc
+        //
+        // - Delete line 2
+        //   2d
+        //
+        // - Prepend abc to line 2
+        //   2s/^/abc/
+        //
+        // The line numbers are all the line numbers in the original file. Even though
+        // the script itself will change them because of inserts and deletes, we don't need to
+        // change the line numbers in the script.
+
+        // Write the target line number.
+        writer.write("\n")
+        writer.write("# ${op.description}\n")
+        if (op.lineNumber >= 0) {
+            writer.write(op.lineNumber.toString())
+        } else {
+            writer.write("$")
+        }
+
+        when (op.type) {
+            SourceOperationType.Insert -> {
+                if (op.lineNumber >= 0) {
+                    writer.write("i\\\n") // "Insert"
+                } else {
+                    // If it's the end of the file, we need to use "a" (append)
+                    writer.write("a\\\n")
+                }
+                writer.write(op.text)
+                writer.write("\n")
+            }
+            SourceOperationType.Delete -> {
+                writer.write("d\n")
+            }
+            SourceOperationType.Prepend -> {
+                if (op.text.contains('/')) {
+                    TODO("Operation $op contains character(s) that needs to be escaped.")
+                }
+                writer.write("s/^/${op.text}/\n")
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaOptions.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaOptions.kt
new file mode 100644
index 0000000..08bd95f
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaOptions.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.platform.test.ravenwood.ravenhelper.policytoannot
+
+import com.android.hoststubgen.ArgIterator
+import com.android.hoststubgen.ArgumentsException
+import com.android.hoststubgen.SetOnce
+import com.android.hoststubgen.ensureFileExists
+import com.android.hoststubgen.log
+
+/**
+ * Options for the "ravenhelper pta" subcommand.
+ */
+class PtaOptions(
+    /** Text policy files */
+    var policyOverrideFiles: MutableList<String> = mutableListOf(),
+
+    /** Annotation allowed list file. */
+    var annotationAllowedClassesFile: SetOnce<String?> = SetOnce(null),
+
+    /** Source files or directories. */
+    var sourceFilesOrDirectories: MutableList<String> = mutableListOf(),
+
+    /** Output script file. */
+    var outputScriptFile: SetOnce<String?> = SetOnce(null),
+
+    /** Dump the operations (for debugging) */
+    var dumpOperations: SetOnce<Boolean> = SetOnce(false),
+) {
+    companion object {
+        fun parseArgs(args: List<String>): PtaOptions {
+            val ret = PtaOptions()
+            val ai = ArgIterator.withAtFiles(args.toTypedArray())
+
+            while (true) {
+                val arg = ai.nextArgOptional() ?: break
+
+                fun nextArg(): String = ai.nextArgRequired(arg)
+
+                if (log.maybeHandleCommandLineArg(arg) { nextArg() }) {
+                    continue
+                }
+                try {
+                    when (arg) {
+                        // TODO: Write help
+                        "-h", "--help" -> TODO("Help is not implemented yet")
+
+                        "-p", "--policy-override-file" ->
+                            ret.policyOverrideFiles.add(nextArg().ensureFileExists())
+
+                        "-a", "--annotation-allowed-classes-file" ->
+                            ret.annotationAllowedClassesFile.set(nextArg().ensureFileExists())
+
+                        "-s", "--src" ->
+                            ret.sourceFilesOrDirectories.add(nextArg().ensureFileExists())
+
+                        "--dump" ->
+                            ret.dumpOperations.set(true)
+
+                        "-o", "--output-script" ->
+                            ret.outputScriptFile.set(nextArg())
+
+                        else -> throw ArgumentsException("Unknown option: $arg")
+                    }
+                } catch (e: SetOnce.SetMoreThanOnceException) {
+                    throw ArgumentsException("Duplicate or conflicting argument found: $arg")
+                }
+            }
+
+            if (ret.policyOverrideFiles.size == 0) {
+                throw ArgumentsException("Must specify at least one policy file")
+            }
+
+            if (ret.sourceFilesOrDirectories.size == 0) {
+                throw ArgumentsException("Must specify at least one source path")
+            }
+
+            return ret
+        }
+    }
+
+    override fun toString(): String {
+        return """
+            PtaOptions{
+              policyOverrideFiles=$policyOverrideFiles
+              annotationAllowedClassesFile=$annotationAllowedClassesFile
+              sourceFilesOrDirectories=$sourceFilesOrDirectories
+              outputScriptFile=$outputScriptFile
+              dumpOperations=$dumpOperations
+            }
+            """.trimIndent()
+    }
+}
\ No newline at end of file
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
new file mode 100644
index 0000000..5984e4f
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/policytoannot/PtaProcessor.kt
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.platform.test.ravenwood.ravenhelper.policytoannot
+
+import com.android.hoststubgen.LogLevel
+import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME
+import com.android.hoststubgen.asm.toJvmClassName
+import com.android.hoststubgen.filters.FilterPolicyWithReason
+import com.android.hoststubgen.filters.PolicyFileProcessor
+import com.android.hoststubgen.filters.SpecialClass
+import com.android.hoststubgen.filters.TextFileFilterPolicyParser
+import com.android.hoststubgen.filters.TextFilePolicyMethodReplaceFilter
+import com.android.hoststubgen.log
+import com.android.hoststubgen.utils.ClassFilter
+import com.android.platform.test.ravenwood.ravenhelper.SubcommandHandler
+import com.android.platform.test.ravenwood.ravenhelper.psi.createUastEnvironment
+import com.android.platform.test.ravenwood.ravenhelper.sourcemap.AllClassInfo
+import com.android.platform.test.ravenwood.ravenhelper.sourcemap.ClassInfo
+import com.android.platform.test.ravenwood.ravenhelper.sourcemap.MethodInfo
+import com.android.platform.test.ravenwood.ravenhelper.sourcemap.SourceLoader
+import java.io.BufferedWriter
+import java.io.FileOutputStream
+import java.io.FileReader
+import java.io.OutputStreamWriter
+import java.util.regex.Pattern
+
+/**
+ * This is the main routine of the "pta" -- policy-to-annotation -- subcommands.
+ */
+class PtaProcessor : SubcommandHandler {
+    override fun handle(args: List<String>) {
+        val options = PtaOptions.parseArgs(args)
+
+        log.v("Options: $options")
+
+        val converter = TextPolicyToAnnotationConverter(
+            options.policyOverrideFiles,
+            options.sourceFilesOrDirectories,
+            options.annotationAllowedClassesFile.get,
+            Annotations(),
+            options.dumpOperations.get || log.isEnabled(LogLevel.Debug),
+        )
+        converter.process()
+
+        val ops = converter.resultOperations
+
+        if (ops.size == 0) {
+            log.i("No files need to be updated.")
+            return
+        }
+
+        val scriptWriter = BufferedWriter(OutputStreamWriter(
+            options.outputScriptFile.get?.let { file ->
+                FileOutputStream(file)
+            } ?: System.out
+        ))
+
+        scriptWriter.use { writer ->
+            options.outputScriptFile.get?.let {
+                log.i("Creating script file at $it ...")
+            }
+            createShellScript(ops, writer)
+        }
+    }
+}
+
+/**
+ * This class implements the actual logic.
+ */
+private class TextPolicyToAnnotationConverter(
+    val policyFiles: List<String>,
+    val sourceFilesOrDirectories: List<String>,
+    val annotationAllowedClassesFile: String?,
+    val annotations: Annotations,
+    val dumpOperations: Boolean,
+) {
+    private val annotationAllowedClasses: ClassFilter = annotationAllowedClassesFile.let { file ->
+        if (file == null) {
+            ClassFilter.newNullFilter(true) // Allow all classes
+        } else {
+            ClassFilter.loadFromFile(file, false)
+        }
+    }
+
+    val resultOperations = SourceOperations()
+    private val classes = AllClassInfo()
+    private val policyParser = TextFileFilterPolicyParser()
+    private val annotationNeedingClasses = mutableSetOf<String>()
+
+    /**
+     * Entry point.
+     */
+    fun process() {
+        // First, load
+        val env = createUastEnvironment()
+        try {
+            loadSources()
+
+            processPolicies()
+
+            addToAnnotationsAllowedListFile()
+
+            if (dumpOperations) {
+                log.withIndent {
+                    resultOperations.getOperations().toSortedMap().forEach { (file, ops) ->
+                        log.i("ops: $file")
+                        ops.forEach { op ->
+                            log.i("  line: ${op.lineNumber}: ${op.type}: \"${op.text}\" " +
+                                    "(${op.description})")
+                        }
+                    }
+                }
+            }
+        } finally {
+            env.dispose()
+        }
+    }
+
+    /**
+     * Load all the java source files into [classes].
+     */
+    private fun loadSources() {
+        val env = createUastEnvironment()
+        try {
+            val loader = SourceLoader(env)
+            loader.load(sourceFilesOrDirectories, classes)
+        } finally {
+            env.dispose()
+        }
+    }
+
+    private fun addToAnnotationsAllowedListFile() {
+        log.i("Generating operations to update annotation allowlist file...")
+        log.withIndent {
+            annotationNeedingClasses.sorted().forEach { className ->
+                if (!annotationAllowedClasses.matches(className.toJvmClassName())) {
+                    resultOperations.add(
+                        SourceOperation(
+                            annotationAllowedClassesFile!!,
+                            -1, // add to the end
+                            SourceOperationType.Insert,
+                            className,
+                            "add to annotation allowlist"
+                        ))
+                }
+            }
+        }
+    }
+
+    /**
+     * Process the policy files with [Processor].
+     */
+    private fun processPolicies() {
+        log.i("Loading the policy files and generating operations...")
+        log.withIndent {
+            policyFiles.forEach { policyFile ->
+                log.i("Parsing $policyFile ...")
+                log.withIndent {
+                    policyParser.parse(FileReader(policyFile), policyFile, Processor())
+                }
+            }
+        }
+    }
+
+    private inner class Processor : PolicyFileProcessor {
+
+        var classPolicyText = ""
+        var classPolicyLine = -1
+
+        // Whether the current class has a skip marker, in which case we ignore all members.
+        // Applicable only within a "simple class"
+        var classSkipping = false
+
+        var classLineConverted = false
+        var classHasMember = false
+
+        private fun currentLineHasSkipMarker(): Boolean {
+            val ret = policyParser.currentLineText.contains("no-pta")
+
+            if (ret) {
+                log.forVerbose {
+                    log.v("Current line has a skip marker: ${policyParser.currentLineText}")
+                }
+            }
+
+            return ret
+        }
+
+        private fun shouldSkipCurrentLine(): Boolean {
+            // If a line contains a special marker "no-pta", we'll skip it.
+            return classSkipping || currentLineHasSkipMarker()
+        }
+
+        /** Print a warning about an unsupported policy directive. */
+        private fun warnOnPolicy(message: String, policyLine: String, lineNumber: Int) {
+            log.w("Warning: $message")
+            log.w("  policy: \"$policyLine\"")
+            log.w("  at ${policyParser.filename}:$lineNumber")
+        }
+
+        /** Print a warning about an unsupported policy directive. */
+        private fun warnOnCurrentPolicy(message: String) {
+            warnOnPolicy(message, policyParser.currentLineText, policyParser.lineNumber)
+        }
+
+        /** Print a warning about an unsupported policy directive on the class line. */
+        private fun warnOnClassPolicy(message: String) {
+            warnOnPolicy(message, classPolicyText, classPolicyLine)
+        }
+
+        override fun onPackage(name: String, policy: FilterPolicyWithReason) {
+            warnOnCurrentPolicy("'package' directive isn't supported (yet).")
+        }
+
+        override fun onRename(pattern: Pattern, prefix: String) {
+            // Rename will never be supported, so don't show a warning.
+        }
+
+        private fun addOperation(op: SourceOperation) {
+            resultOperations.add(op)
+        }
+
+        private fun commentOutPolicy(lineNumber: Int, description: String) {
+            addOperation(
+                SourceOperation(
+                    policyParser.filename,
+                    lineNumber,
+                    SourceOperationType.Prepend,
+                    "#[PTA]: ", // comment out.
+                    description,
+                )
+            )
+        }
+
+        override fun onClassStart(className: String) {
+            classSkipping = currentLineHasSkipMarker()
+            classLineConverted = false
+            classHasMember = false
+            classPolicyLine = policyParser.lineNumber
+            classPolicyText = policyParser.currentLineText
+        }
+
+        override fun onClassEnd(className: String) {
+            if (classSkipping) {
+                classSkipping = false
+                return
+            }
+            if (!classLineConverted) {
+                // Class line is still needed in the policy file.
+                // (Because the source file wasn't found.)
+                return
+            }
+            if (!classHasMember) {
+                commentOutPolicy(classPolicyLine, "remove class policy on $className")
+            } else {
+                warnOnClassPolicy(
+                    "Class policy on $className can't be removed because it still has members.")
+            }
+        }
+
+        private fun findClass(className: String): ClassInfo? {
+            val ci = classes.findClass(className)
+            if (ci == null) {
+                warnOnCurrentPolicy("Class not found: $className")
+            }
+            return ci
+        }
+
+        private fun addClassAnnotation(
+            className: String,
+            annotation: String,
+        ): Boolean {
+            val ci = findClass(className) ?: return false
+
+            // Add the annotation to the source file.
+            addOperation(
+                SourceOperation(
+                    ci.location.file,
+                    ci.location.line,
+                    SourceOperationType.Insert,
+                    ci.location.getIndent() + annotation,
+                    "add class annotation to $className"
+                )
+            )
+            annotationNeedingClasses.add(className)
+            return true
+        }
+
+        override fun onSimpleClassPolicy(className: String, policy: FilterPolicyWithReason) {
+            if (shouldSkipCurrentLine()) {
+                return
+            }
+            log.v("Found simple class policy: $className - ${policy.policy}")
+
+            val annot = annotations.get(policy.policy, Annotations.Target.Class)!!
+            if (addClassAnnotation(className, annot)) {
+                classLineConverted = true
+            }
+        }
+
+        override fun onSubClassPolicy(superClassName: String, policy: FilterPolicyWithReason) {
+            warnOnCurrentPolicy("Subclass policies isn't supported (yet).")
+        }
+
+        override fun onRedirectionClass(fromClassName: String, toClassName: String) {
+            if (shouldSkipCurrentLine()) {
+                return
+            }
+
+            log.v("Found class redirection: $fromClassName - $toClassName")
+
+            if (addClassAnnotation(
+                    fromClassName,
+                    annotations.getRedirectionClassAnnotation(toClassName),
+                )) {
+                commentOutPolicy(policyParser.lineNumber,
+                    "remove class redirection policy on $fromClassName")
+            }
+        }
+
+        override fun onClassLoadHook(className: String, callback: String) {
+            if (shouldSkipCurrentLine()) {
+                return
+            }
+
+            log.v("Found class load hook: $className - $callback")
+
+            if (addClassAnnotation(
+                    className,
+                    annotations.getClassLoadHookAnnotation(callback),
+                )) {
+                commentOutPolicy(policyParser.lineNumber,
+                    "remove class load hook policy on $className")
+            }
+        }
+
+        override fun onSpecialClassPolicy(type: SpecialClass, policy: FilterPolicyWithReason) {
+            // This can't be converted to an annotation, so don't show a warning.
+        }
+
+        override fun onField(className: String, fieldName: String, policy: FilterPolicyWithReason) {
+            if (shouldSkipCurrentLine()) {
+                return
+            }
+
+            log.v("Found field policy: $className.$fieldName - ${policy.policy}")
+
+            val ci = findClass(className) ?: return
+
+            ci.findField(fieldName)?.let { fi ->
+                val annot = annotations.get(policy.policy, Annotations.Target.Field)!!
+
+                addOperation(
+                    SourceOperation(
+                        fi.location.file,
+                        fi.location.line,
+                        SourceOperationType.Insert,
+                        fi.location.getIndent() + annot,
+                        "add annotation to field $className.$fieldName",
+                    )
+                )
+                commentOutPolicy(policyParser.lineNumber,
+                    "remove field policy $className.$fieldName")
+
+                annotationNeedingClasses.add(className)
+            } ?: {
+                warnOnCurrentPolicy("Field not found: $className.$fieldName")
+            }
+        }
+
+        override fun onSimpleMethodPolicy(
+            className: String,
+            methodName: String,
+            methodDesc: String,
+            policy: FilterPolicyWithReason
+        ) {
+            if (shouldSkipCurrentLine()) {
+                return
+            }
+            val readableName = "$className.$methodName$methodDesc"
+            log.v("Found simple method policy: $readableName - ${policy.policy}")
+
+
+            // Inner method to get the matching methods for this policy.
+            //
+            // If this policy can't be converted for any reason, it'll return null.
+            // Otherwise, it'll return a pair of method list and the annotation string.
+            fun getMethods(): Pair<List<MethodInfo>, String>? {
+                if (methodName == CLASS_INITIALIZER_NAME) {
+                    warnOnClassPolicy("Policy for class initializers not supported.")
+                    return null
+                }
+                val ci = findClass(className) ?: return null
+                val methods = ci.findMethods(methodName, methodDesc)
+                if (methods == null) {
+                    warnOnCurrentPolicy("Method not found: $readableName")
+                    return null
+                }
+
+                // If the policy is "ignore", we can't convert it to an annotation, in which case
+                // annotations.get() will return null.
+                val annot = annotations.get(policy.policy, Annotations.Target.Method)
+                if (annot == null) {
+                    warnOnCurrentPolicy("Annotation for policy '${policy.policy}' isn't available")
+                    return null
+                }
+                return Pair(methods, annot)
+            }
+
+            val methodsAndAnnot = getMethods()
+
+            if (methodsAndAnnot == null) {
+                classHasMember = true
+                return // This policy can't converted.
+            }
+            val methods = methodsAndAnnot.first
+            val annot = methodsAndAnnot.second
+
+            var found = false
+            methods.forEach { mi ->
+                found = true
+                addOperation(
+                    SourceOperation(
+                        mi.location.file,
+                        mi.location.line,
+                        SourceOperationType.Insert,
+                        mi.location.getIndent() + annot,
+                        "add annotation to method $readableName",
+                    )
+                )
+            }
+            if (found) {
+                commentOutPolicy(
+                    policyParser.lineNumber,
+                    "remove method policy $readableName"
+                )
+
+                annotationNeedingClasses.add(className)
+            } else {
+                warnOnCurrentPolicy("Method not found: $readableName")
+            }
+        }
+
+        override fun onMethodInClassReplace(
+            className: String,
+            methodName: String,
+            methodDesc: String,
+            targetName: String,
+            policy: FilterPolicyWithReason
+        ) {
+            warnOnCurrentPolicy("Found method replace but it's not supported yet: "
+                + "$className.$methodName$methodDesc - $targetName")
+        }
+
+        override fun onMethodOutClassReplace(
+            className: String,
+            methodName: String,
+            methodDesc: String,
+            replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
+            policy: FilterPolicyWithReason
+        ) {
+            // This can't be converted to an annotation.
+            classHasMember = true
+        }
+    }
+}
\ No newline at end of file
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/psi/PsiUtil.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/psi/PsiUtil.kt
new file mode 100644
index 0000000..6775135
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/psi/PsiUtil.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.platform.test.ravenwood.ravenhelper.psi
+
+import com.android.tools.lint.UastEnvironment
+
+// PSI is a library to parse Java/Kotlin source files, which is part of JetBrains' IntelliJ/
+// Android Studio, and other IDEs.
+//
+// PSI is normally used by IntelliJ's plugins, and as such, there isn't really a good documentation
+// on how to use it from a standalone program. However, fortunately, Android Studio's Lint
+// and Metalava both use PSI. Metalava reuses some of the APIs exposed by Lint. We also use the
+// same Lint APIs used by Metalava here.
+//
+// Some code pointers around the relevant projects:
+//
+// - We stole code from Metalava, but the recent version of Metalava is too complicated,
+//   and hard to understand. Older Metalava, such as this one:
+//   https://android.git.corp.google.com/platform/tools/metalava/+/refs/heads/android13-dev
+//   is easier to understand.
+//
+// - PSI is source code is available in IntelliJ's code base:
+//   https://github.com/JetBrains/intellij-community.git
+//
+// - Lint is in Android studio.
+//  https://android.googlesource.com/platform/tools/base/+/studio-master-dev/source.md
+
+
+/**
+ * Create [UastEnvironment] enough to parse Java source files.
+ */
+fun createUastEnvironment(): UastEnvironment {
+    val config = UastEnvironment.Configuration.create(
+        enableKotlinScripting = false,
+        useFirUast = false,
+    )
+
+    config.javaLanguageLevel = com.intellij.pom.java.LanguageLevel.JDK_21
+
+    // The following code exists in Metalava, but we don't seem to need it.
+    // We may need to when we need to support kotlin.
+//    config.kotlinLanguageLevel = kotlinLanguageLevel
+//    config.addSourceRoots(listOf(File(root)))
+//    config.addClasspathRoots(classpath.map { it.absoluteFile })
+//    options.jdkHome?.let {
+//        if (options.isJdkModular(it)) {
+//            config.kotlinCompilerConfig.put(JVMConfigurationKeys.JDK_HOME, it)
+//            config.kotlinCompilerConfig.put(JVMConfigurationKeys.NO_JDK, false)
+//        }
+//    }
+
+    return UastEnvironment.create(config)
+}
diff --git a/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/SourceMapGenerator.kt b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/SourceMapGenerator.kt
new file mode 100644
index 0000000..58e4497
--- /dev/null
+++ b/ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/sourcemap/SourceMapGenerator.kt
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.platform.test.ravenwood.ravenhelper.sourcemap
+
+/*
+ * This file contains classes used to parse Java source files to build "source map" which
+ * basically tells you what classes/methods/fields are declared in what line of what file.
+ */
+
+import com.android.hoststubgen.GeneralUserErrorException
+import com.android.hoststubgen.log
+import com.android.tools.lint.UastEnvironment
+import com.intellij.openapi.editor.Document
+import com.intellij.openapi.vfs.StandardFileSystems
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiClassOwner
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiFile
+import com.intellij.psi.PsiManager
+import com.intellij.psi.PsiMethod
+import com.intellij.psi.PsiNameIdentifierOwner
+import com.intellij.psi.SyntheticElement
+import java.io.File
+
+
+/**
+ * Represents the location of an item. (class, field or method)
+ */
+data class Location (
+    /** Full path filename. */
+    val file: String,
+
+    /** 1-based line number */
+    val line: Int,
+
+    /** Indent of the line */
+    val indent: Int,
+) {
+
+    fun getIndent(): String {
+        return " ".repeat(indent)
+    }
+
+    fun dump() {
+        log.i("Location: $file:$line (indent: $indent)")
+    }
+}
+
+/**
+ * Represents the type of item.
+ */
+enum class ItemType {
+    Class,
+    Field,
+    Method,
+}
+
+/** Holds a field's location. */
+data class FieldInfo (
+    val name: String,
+    val location: Location,
+) {
+    fun dump() {
+        log.i("Field: $name")
+        log.withIndent {
+            location.dump()
+        }
+    }
+}
+
+/** Holds a method's location. */
+data class MethodInfo (
+    val name: String,
+    /** "Simplified" description. */
+    val simpleDesc: String,
+    val location: Location,
+) {
+    fun dump() {
+        log.i("Method: $name$simpleDesc")
+        log.withIndent {
+            location.dump()
+        }
+    }
+}
+
+/** Holds a class's location and members. */
+data class ClassInfo (
+    val fullName: String,
+    val location: Location,
+    val fields: MutableMap<String, FieldInfo> = mutableMapOf(),
+    val methods: MutableMap<String, MutableList<MethodInfo>> = mutableMapOf(),
+) {
+    fun add(fi: FieldInfo) {
+        fields.put(fi.name, fi)
+    }
+
+    fun add(mi: MethodInfo) {
+        val list = methods.get(mi.name)
+        if (list != null) {
+            list.add(mi)
+        } else {
+            methods.put(mi.name, mutableListOf(mi))
+        }
+    }
+
+    fun dump() {
+        log.i("Class: $fullName")
+        log.withIndent {
+            location.dump()
+
+            // Sort and print fields and methods.
+            methods.toSortedMap().forEach { entry ->
+                entry.value.sortedBy { method -> method.simpleDesc }.forEach {
+                    it.dump()
+                }
+            }
+        }
+    }
+
+    /** Find a field by name */
+    fun findField(fieldName: String): FieldInfo? {
+        return fields[fieldName]
+    }
+
+    /**
+     * Find a field by name and descriptor.
+     *
+     * If [descriptor] is "*", then all methods with the name will be returned.
+     */
+    fun findMethods(methodName: String, methodDesc: String): List<MethodInfo>? {
+        val list = methods[methodName] ?: return null
+
+        // Wildcard method policy.
+        if (methodDesc == "*") {
+            return list
+        }
+
+        val simpleDesc = simplifyMethodDesc(methodDesc)
+        list.forEach { mi ->
+            if (simpleDesc == mi.simpleDesc) {
+                return listOf(mi)
+            }
+        }
+        log.w("Method $fullName.$methodName found, but none match description '$methodDesc'")
+        return null
+    }
+}
+
+/**
+ * Stores all classes
+ */
+data class AllClassInfo (
+    val classes: MutableMap<String, ClassInfo> = mutableMapOf(),
+) {
+    fun add(ci: ClassInfo) {
+        classes.put(ci.fullName, ci)
+    }
+
+    fun dump() {
+        classes.toSortedMap { a, b -> a.compareTo(b) }.forEach {
+            it.value.dump()
+        }
+    }
+
+    fun findClass(name: String): ClassInfo? {
+        return classes.get(name)
+    }
+}
+
+fun typeToSimpleDesc(origType: String): String {
+    var type = origType
+
+    // Detect arrays.
+    var arrayPrefix = ""
+    while (type.endsWith("[]")) {
+        arrayPrefix += "["
+        type = type.substring(0, type.length - 2)
+    }
+
+    // Delete generic parameters. (delete everything after '<')
+    type.indexOf('<').let { pos ->
+        if (pos >= 0) {
+            type = type.substring(0, pos)
+        }
+    }
+
+    // Handle builtins.
+    val builtinType = when (type) {
+        "byte" -> "B"
+        "short" -> "S"
+        "int" -> "I"
+        "long" -> "J"
+        "float" -> "F"
+        "double" -> "D"
+        "boolean" -> "Z"
+        "char" -> "C"
+        "void" -> "V"
+        else -> null
+    }
+
+    builtinType?.let {
+        return arrayPrefix + builtinType
+    }
+
+    return arrayPrefix + "L" + type + ";"
+}
+
+/**
+ * Get a "simple" description of a method.
+ *
+ * "Simple" descriptions are similar to "real" ones, except:
+ * - No return type.
+ * - No package names in type names.
+ */
+fun getSimpleDesc(method: PsiMethod): String {
+    val sb = StringBuilder()
+
+    sb.append("(")
+
+    val params = method.parameterList
+    for (i in 0..<params.parametersCount) {
+        val param = params.getParameter(i)
+
+        val type = param?.type?.presentableText
+
+        if (type == null) {
+            throw RuntimeException(
+                "Unable to decode parameter list from method from ${params.parent}")
+        }
+
+        sb.append(typeToSimpleDesc(type))
+    }
+
+    sb.append(")")
+
+    return sb.toString()
+}
+
+private val reTypeFinder = "L.*/".toRegex()
+
+private fun simplifyMethodDesc(origMethodDesc: String): String {
+    // We don't need the return type, so remove everything after the ')'.
+    val pos = origMethodDesc.indexOf(')')
+    var desc = if (pos < 0) { origMethodDesc } else { origMethodDesc.substring(0, pos + 1) }
+
+    // Then we remove the package names from all the class names.
+    // i.e. convert "Ljava/lang/String" to "LString".
+
+    return desc.replace(reTypeFinder, "L")
+}
+
+/**
+ * Class that reads and parses java source files using PSI and populate [AllClassInfo].
+ */
+class SourceLoader(
+    val environment: UastEnvironment,
+) {
+    private val fileSystem = StandardFileSystems.local()
+    private val manager = PsiManager.getInstance(environment.ideaProject)
+
+    /** Classes that were parsed */
+    private var numParsedClasses = 0
+
+    /**
+     * Main entry point.
+     */
+    fun load(filesOrDirectories: List<String>, classes: AllClassInfo) {
+        val psiFiles = mutableListOf<PsiFile>()
+        log.i("Loading source files...")
+        log.iTime("Discovering source files") {
+            load(filesOrDirectories.map { File(it) }, psiFiles)
+        }
+
+        log.i("${psiFiles.size} file(s) found.")
+
+        if (psiFiles.size == 0) {
+            throw GeneralUserErrorException("No source files found.")
+        }
+
+        log.iTime("Parsing source files") {
+            log.withIndent {
+                for (file in psiFiles.asSequence().distinct()) {
+                    val classesInFile = (file as? PsiClassOwner)?.classes?.toList()
+                    classesInFile?.forEach { clazz ->
+                        loadClass(clazz)?.let { classes.add(it) }
+
+                        clazz.innerClasses.forEach { inner ->
+                            loadClass(inner)?.let { classes.add(it) }
+                        }
+                    }
+                }
+            }
+        }
+        log.i("$numParsedClasses class(es) found.")
+    }
+
+    private fun load(filesOrDirectories: List<File>, result: MutableList<PsiFile>) {
+        filesOrDirectories.forEach {
+            load(it, result)
+        }
+    }
+
+    private fun load(file: File, result: MutableList<PsiFile>) {
+        if (file.isDirectory) {
+            file.listFiles()?.forEach { child ->
+                load(child, result)
+            }
+            return
+        }
+
+        // It's a file
+        when (file.extension) {
+            "java" -> {
+                // Load it.
+            }
+            "kt" -> {
+                log.w("Kotlin not supported, not loading ${file.path}")
+                return
+            }
+            else -> return // Silently skip
+        }
+        fileSystem.findFileByPath(file.path)?.let { virtualFile ->
+            manager.findFile(virtualFile)?.let { psiFile ->
+                result.add(psiFile)
+            }
+        }
+    }
+
+    private fun loadClass(clazz: PsiClass): ClassInfo? {
+        if (clazz is SyntheticElement) {
+            return null
+        }
+        log.forVerbose {
+            log.v("Class found: ${clazz.qualifiedName}")
+        }
+        numParsedClasses++
+
+        log.withIndent {
+            val ci = ClassInfo(
+                clazz.qualifiedName!!,
+                getLocation(clazz) ?: return null,
+            )
+
+            // Load fields.
+            clazz.fields.filter { it !is SyntheticElement }.forEach {
+                val name = it.name
+                log.forDebug { log.d("Field found: $name") }
+                val loc = getLocation(it) ?: return@forEach
+                ci.add(FieldInfo(name, loc))
+            }
+
+            // Load methods.
+            clazz.methods.filter { it !is SyntheticElement }.forEach {
+                val name = resolveMethodName(it)
+                val simpleDesc = getSimpleDesc(it)
+                log.forDebug { log.d("Method found: $name$simpleDesc") }
+                val loc = getLocation(it) ?: return@forEach
+                ci.add(MethodInfo(name, simpleDesc, loc))
+            }
+            return ci
+        }
+    }
+
+    private fun resolveMethodName(method: PsiMethod): String {
+        val clazz = method.containingClass!!
+        if (clazz.name == method.name) {
+            return "<init>" // It's a constructor.
+        }
+        return method.name
+    }
+
+    private fun getLocation(elem: PsiElement): Location? {
+        val lineAndIndent = getLineNumberAndIndent(elem)
+        if (lineAndIndent == null) {
+            log.w("Unable to determine location of $elem")
+            return null
+        }
+        return Location(
+            elem.containingFile.originalFile.virtualFile.path,
+            lineAndIndent.first,
+            lineAndIndent.second,
+        )
+    }
+
+    private fun getLineNumberAndIndent(element: PsiElement): Pair<Int, Int>? {
+        val psiFile: PsiFile = element.containingFile ?: return null
+        val document: Document = psiFile.viewProvider.document ?: return null
+
+        // Actual elements such as PsiClass, PsiMethod and PsiField contains the leading
+        // javadoc, etc, so use the "identifier"'s element, if available.
+        // For synthesized elements, this may return null.
+        val targetRange = (
+                (element as PsiNameIdentifierOwner).nameIdentifier?.textRange ?: element.textRange
+                ) ?: return null
+        val lineNumber = document.getLineNumber(targetRange.startOffset)
+        val lineStartOffset = document.getLineStartOffset(lineNumber)
+
+        val lineLeadingText = document.getText(
+            com.intellij.openapi.util.TextRange(lineStartOffset, targetRange.startOffset))
+
+        val indent = lineLeadingText.takeWhile { it.isWhitespace() }.length
+
+        // Line numbers are 0-based, add 1 for human-readable format
+        return Pair(lineNumber + 1, indent)
+    }
+}
\ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index f2c42f7..97ed214b 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -838,13 +838,12 @@
 
         @Override
         public void onAuthenticationPrompt(int uid) {
-            synchronized (mVirtualDeviceManagerLock) {
-                for (int i = 0; i < mVirtualDevices.size(); i++) {
-                    VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
-                    device.showToastWhereUidIsRunning(uid,
-                            R.string.app_streaming_blocked_message_for_fingerprint_dialog,
-                            Toast.LENGTH_LONG, Looper.getMainLooper());
-                }
+            ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+            for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+                VirtualDeviceImpl device = virtualDevicesSnapshot.get(i);
+                device.showToastWhereUidIsRunning(uid,
+                        R.string.app_streaming_blocked_message_for_fingerprint_dialog,
+                        Toast.LENGTH_LONG, Looper.getMainLooper());
             }
         }
 
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 4478232..717a2d4 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -37,6 +37,7 @@
         ":framework_native_aidl",
         ":gsiservice_aidl",
         ":installd_aidl",
+        ":mmd_aidl",
         ":storaged_aidl",
         ":vold_aidl",
     ],
@@ -242,6 +243,7 @@
         "aconfig_new_storage_flags_lib",
         "powerstats_flags_lib",
         "locksettings_flags_lib",
+        "mmd_flags_lib",
     ],
     javac_shard_size: 50,
     javacflags: [
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 6858e29..ef769cf 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -9,6 +9,7 @@
 
 # Zram writeback
 per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com
+per-file ZramMaintenance.java = kawasin@google.com
 
 # ServiceWatcher
 per-file ServiceWatcher.java = sooniln@google.com
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b4cdfb1..19676eb 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -41,6 +41,7 @@
 import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED;
 import static android.os.storage.OnObbStateChangeListener.MOUNTED;
 import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
+import static android.mmd.flags.Flags.mmdEnabled;
 
 import static com.android.internal.util.XmlUtils.readStringAttribute;
 import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -944,12 +945,17 @@
             });
         refreshZramSettings();
 
-        // Schedule zram writeback unless zram is disabled by persist.sys.zram_enabled
-        String zramPropValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY);
-        if (!zramPropValue.equals("0")
-                && mContext.getResources().getBoolean(
+        if (mmdEnabled()) {
+            // TODO: b/375432472 - Start zram maintenance only when zram is enabled.
+            ZramMaintenance.startZramMaintenance(mContext);
+        } else {
+            // Schedule zram writeback unless zram is disabled by persist.sys.zram_enabled
+            String zramPropValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY);
+            if (!zramPropValue.equals("0")
+                    && mContext.getResources().getBoolean(
                     com.android.internal.R.bool.config_zramWriteback)) {
-            ZramWriteback.scheduleZramWriteback(mContext);
+                ZramWriteback.scheduleZramWriteback(mContext);
+            }
         }
 
         configureTranscoding();
@@ -976,7 +982,7 @@
             // sole writer.
             SystemProperties.set(ZRAM_ENABLED_PROPERTY, desiredPropertyValue);
             // Schedule writeback only if zram is being enabled.
-            if (desiredPropertyValue.equals("1")
+            if (!mmdEnabled() && desiredPropertyValue.equals("1")
                     && mContext.getResources().getBoolean(
                         com.android.internal.R.bool.config_zramWriteback)) {
                 ZramWriteback.scheduleZramWriteback(mContext);
diff --git a/services/core/java/com/android/server/ZramMaintenance.java b/services/core/java/com/android/server/ZramMaintenance.java
new file mode 100644
index 0000000..cdb4812
--- /dev/null
+++ b/services/core/java/com/android/server/ZramMaintenance.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.IMmd;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import java.time.Duration;
+
+/**
+ * Schedules zram maintenance (e.g. zram writeback, zram recompression).
+ *
+ * <p>ZramMaintenance notifies mmd the good timing to execute zram maintenance based on:
+ *
+ * <ul>
+ * <li>Enough interval has passed.
+ * <li>The system is idle.
+ * <li>The battery is not low.
+ * </ul>
+ */
+public class ZramMaintenance extends JobService {
+    private static final String TAG = ZramMaintenance.class.getName();
+    // Job id must be unique across all clients of the same uid. ZramMaintenance uses the bug number
+    // as the job id.
+    private static final int JOB_ID = 375432472;
+    private static final ComponentName sZramMaintenance =
+            new ComponentName("android", ZramMaintenance.class.getName());
+
+    private static final String FIRST_DELAY_SECONDS_PROP =
+            "mm.zram.maintenance.first_delay_seconds";
+    // The default is 1 hour.
+    private static final long DEFAULT_FIRST_DELAY_SECONDS = 3600;
+    private static final String PERIODIC_DELAY_SECONDS_PROP =
+            "mm.zram.maintenance.periodic_delay_seconds";
+    // The default is 1 hour.
+    private static final long DEFAULT_PERIODIC_DELAY_SECONDS = 3600;
+    private static final String REQUIRE_DEVICE_IDLE_PROP =
+            "mm.zram.maintenance.require_device_idle";
+    private static final boolean DEFAULT_REQUIRE_DEVICE_IDLE =
+            true;
+    private static final String REQUIRE_BATTERY_NOT_LOW_PROP =
+            "mm.zram.maintenance.require_battry_not_low";
+    private static final boolean DEFAULT_REQUIRE_BATTERY_NOT_LOW =
+            true;
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        IBinder binder = ServiceManager.getService("mmd");
+        if (binder != null) {
+            IMmd mmd = IMmd.Stub.asInterface(binder);
+            try {
+                mmd.doZramMaintenance();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to doZramMaintenance", e);
+            }
+        } else {
+            Slog.w(TAG, "binder not found");
+        }
+        Duration delay = Duration.ofSeconds(SystemProperties.getLong(PERIODIC_DELAY_SECONDS_PROP,
+                DEFAULT_PERIODIC_DELAY_SECONDS));
+        scheduleZramMaintenance(this, delay);
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        return false;
+    }
+
+    /**
+     * Starts periodical zram maintenance.
+     */
+    public static void startZramMaintenance(Context context) {
+        Duration delay = Duration.ofSeconds(
+                SystemProperties.getLong(FIRST_DELAY_SECONDS_PROP, DEFAULT_FIRST_DELAY_SECONDS));
+        scheduleZramMaintenance(context, delay);
+    }
+
+    private static void scheduleZramMaintenance(Context context, Duration delay) {
+        JobScheduler js = context.getSystemService(JobScheduler.class);
+
+        if (js != null) {
+            js.schedule(new JobInfo.Builder(JOB_ID, sZramMaintenance)
+                    .setMinimumLatency(delay.toMillis())
+                    .setRequiresDeviceIdle(
+                            SystemProperties.getBoolean(REQUIRE_DEVICE_IDLE_PROP,
+                                    DEFAULT_REQUIRE_DEVICE_IDLE))
+                    .setRequiresBatteryNotLow(
+                            SystemProperties.getBoolean(REQUIRE_BATTERY_NOT_LOW_PROP,
+                                    DEFAULT_REQUIRE_BATTERY_NOT_LOW))
+                    .build());
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index 7eeec32..f13bfac 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -77,6 +77,7 @@
      * This is for verifying the UID report flow.
      */
     private static final boolean VALIDATE_UID_STATES = true;
+    @GuardedBy("mLock")
     private final ActiveUids mValidateUids;
 
     UidObserverController(@NonNull Handler handler) {
@@ -282,31 +283,30 @@
         }
         mUidObservers.finishBroadcast();
 
-        if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
-            for (int j = 0; j < numUidChanges; ++j) {
-                final ChangeRecord item = mActiveUidChanges[j];
-                if ((item.change & UidRecord.CHANGE_GONE) != 0) {
-                    mValidateUids.remove(item.uid);
-                } else {
-                    UidRecord validateUid = mValidateUids.get(item.uid);
-                    if (validateUid == null) {
-                        validateUid = new UidRecord(item.uid, null);
-                        mValidateUids.put(item.uid, validateUid);
+        synchronized (mLock) {
+            if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) {
+                for (int j = 0; j < numUidChanges; ++j) {
+                    final ChangeRecord item = mActiveUidChanges[j];
+                    if ((item.change & UidRecord.CHANGE_GONE) != 0) {
+                        mValidateUids.remove(item.uid);
+                    } else {
+                        UidRecord validateUid = mValidateUids.get(item.uid);
+                        if (validateUid == null) {
+                            validateUid = new UidRecord(item.uid, null);
+                            mValidateUids.put(item.uid, validateUid);
+                        }
+                        if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
+                            validateUid.setIdle(true);
+                        } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
+                            validateUid.setIdle(false);
+                        }
+                        validateUid.setSetProcState(item.procState);
+                        validateUid.setCurProcState(item.procState);
+                        validateUid.setSetCapability(item.capability);
+                        validateUid.setCurCapability(item.capability);
                     }
-                    if ((item.change & UidRecord.CHANGE_IDLE) != 0) {
-                        validateUid.setIdle(true);
-                    } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) {
-                        validateUid.setIdle(false);
-                    }
-                    validateUid.setSetProcState(item.procState);
-                    validateUid.setCurProcState(item.procState);
-                    validateUid.setSetCapability(item.capability);
-                    validateUid.setCurCapability(item.capability);
                 }
             }
-        }
-
-        synchronized (mLock) {
             for (int j = 0; j < numUidChanges; j++) {
                 final ChangeRecord changeRecord = mActiveUidChanges[j];
                 changeRecord.isPending = false;
@@ -433,7 +433,9 @@
     }
 
     UidRecord getValidateUidRecord(int uid) {
-        return mValidateUids.get(uid);
+        synchronized (mLock) {
+            return mValidateUids.get(uid);
+        }
     }
 
     void dump(@NonNull PrintWriter pw, @Nullable String dumpPackage) {
@@ -488,12 +490,16 @@
 
     boolean dumpValidateUids(@NonNull PrintWriter pw, @Nullable String dumpPackage, int dumpAppId,
             @NonNull String header, boolean needSep) {
-        return mValidateUids.dump(pw, dumpPackage, dumpAppId, header, needSep);
+        synchronized (mLock) {
+            return mValidateUids.dump(pw, dumpPackage, dumpAppId, header, needSep);
+        }
     }
 
     void dumpValidateUidsProto(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage,
             int dumpAppId, long fieldId) {
-        mValidateUids.dumpProto(proto, dumpPackage, dumpAppId, fieldId);
+        synchronized (mLock) {
+            mValidateUids.dumpProto(proto, dumpPackage, dumpAppId, fieldId);
+        }
     }
 
     static final class ChangeRecord {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index d82d77d..2fb58f0 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -2566,7 +2566,6 @@
 
                 Map<String, Ops> packages = uidState.pkgOps;
                 Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
-                boolean uidChanged = false;
                 while (it.hasNext()) {
                     Map.Entry<String, Ops> ent = it.next();
                     String packageName = ent.getKey();
@@ -2599,7 +2598,6 @@
                                     newMode,
                                     UserHandle.getUserId(curOp.uid));
                             changed = true;
-                            uidChanged = true;
                             final int uid = curOp.uidState.uid;
                             callbacks = addCallbacks(callbacks, curOp.op, uid, packageName,
                                     previousMode, mOpModeWatchers.get(curOp.op));
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index aa59258..2a03dcb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4240,8 +4240,6 @@
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(
                 mContext.getOpPackageName(), UserHandle.USER_SYSTEM, mContext);
 
-        disableSkuSpecificApps();
-
         // Read the compatibilty setting when the system is ready.
         boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
                 mContext.getContentResolver(),
@@ -4374,29 +4372,6 @@
         }
     }
 
-    //TODO: b/111402650
-    private void disableSkuSpecificApps() {
-        String[] apkList = mContext.getResources().getStringArray(
-                R.array.config_disableApksUnlessMatchedSku_apk_list);
-        String[] skuArray = mContext.getResources().getStringArray(
-                R.array.config_disableApkUnlessMatchedSku_skus_list);
-        if (ArrayUtils.isEmpty(apkList)) {
-           return;
-        }
-        String sku = SystemProperties.get("ro.boot.hardware.sku");
-        if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) {
-            return;
-        }
-        final Computer snapshot = snapshotComputer();
-        for (String packageName : apkList) {
-            setSystemAppHiddenUntilInstalled(snapshot, packageName, true);
-            final List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(false);
-            for (int i = 0; i < users.size(); i++) {
-                setSystemAppInstallState(snapshot, packageName, false, users.get(i).id);
-            }
-        }
-    }
-
     public PackageFreezer freezePackage(String packageName, int userId, String killReason,
             int exitInfoReason, InstallRequest request) {
         return freezePackage(packageName, userId, killReason, exitInfoReason, request,
diff --git a/services/core/java/com/android/server/power/OWNERS b/services/core/java/com/android/server/power/OWNERS
index c1fad33..a1531a8 100644
--- a/services/core/java/com/android/server/power/OWNERS
+++ b/services/core/java/com/android/server/power/OWNERS
@@ -2,6 +2,8 @@
 santoscordon@google.com
 petsjonkin@google.com
 brup@google.com
+flc@google.com
+wilczynskip@google.com
 
 per-file ThermalManagerService.java=file:/THERMAL_OWNERS
 per-file LowPowerStandbyController.java=qingxun@google.com
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index b35a0a7..4b55f27 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -4853,7 +4853,7 @@
             Slog.e(TAG, "Disconnected from keystore service. Cannot pull.", e);
             return StatsManager.PULL_SKIP;
         } catch (ServiceSpecificException e) {
-            Slog.e(TAG, "pulling keystore metrics failed", e);
+            Slog.e(TAG, "Pulling keystore atom with tag " + atomTag + " failed", e);
             return StatsManager.PULL_SKIP;
         } finally {
             Binder.restoreCallingIdentity(callingToken);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 908f51b..ccac969 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1511,10 +1511,13 @@
             getUiState(displayId).setImeWindowState(vis, backDisposition, showImeSwitcher);
 
             mHandler.post(() -> {
-                if (mBar == null) return;
-                try {
-                    mBar.setImeWindowStatus(displayId, vis, backDisposition, showImeSwitcher);
-                } catch (RemoteException ex) { }
+                IStatusBar bar = mBar;
+                if (bar != null) {
+                    try {
+                        bar.setImeWindowStatus(displayId, vis, backDisposition, showImeSwitcher);
+                    } catch (RemoteException ex) {
+                    }
+                }
             });
         }
     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 9a3ad2d..cebf1be 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -1655,6 +1655,12 @@
         activityIdleInternal(null /* idleActivity */, false /* fromTimeout */,
                 true /* processPausingActivities */, null /* configuration */);
 
+        if (rootTask.getParent() == null) {
+            // The activities in the task may already be finishing. Then the task could be removed
+            // when performing the idle check.
+            return;
+        }
+
         // Reparent all the tasks to the bottom of the display
         final DisplayContent toDisplay =
                 mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY);
diff --git a/tests/CompanionDeviceMultiDeviceTests/client/Android.bp b/tests/CompanionDeviceMultiDeviceTests/client/Android.bp
index ce63fe8..02b6391 100644
--- a/tests/CompanionDeviceMultiDeviceTests/client/Android.bp
+++ b/tests/CompanionDeviceMultiDeviceTests/client/Android.bp
@@ -45,7 +45,6 @@
     ],
 
     optimize: {
-        proguard_compatibility: true,
         proguard_flags_files: ["proguard.flags"],
     },
 }
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
index 7b1e4cc..c11b6bb 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
@@ -38,7 +38,6 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index af753e5..b62843c 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -19,6 +19,7 @@
 import com.android.tools.lint.client.api.IssueRegistry
 import com.android.tools.lint.client.api.Vendor
 import com.android.tools.lint.detector.api.CURRENT_API
+import com.google.android.lint.multiuser.PendingIntentGetActivityDetector
 import com.google.android.lint.parcel.SaferParcelChecker
 import com.google.auto.service.AutoService
 
@@ -40,6 +41,7 @@
         PermissionMethodDetector.ISSUE_PERMISSION_METHOD_USAGE,
         PermissionMethodDetector.ISSUE_CAN_BE_PERMISSION_METHOD,
         FeatureAutomotiveDetector.ISSUE,
+        PendingIntentGetActivityDetector.ISSUE_PENDING_INTENT_GET_ACTIVITY,
     )
 
     override val api: Int
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/multiuser/PendingIntentGetActivityDetector.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/multiuser/PendingIntentGetActivityDetector.kt
new file mode 100644
index 0000000..b9f22eb
--- /dev/null
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/multiuser/PendingIntentGetActivityDetector.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.lint.multiuser
+
+import com.android.tools.lint.detector.api.Category
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Issue
+import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.Scope
+import com.android.tools.lint.detector.api.Severity
+import com.android.tools.lint.detector.api.SourceCodeScanner
+import com.intellij.psi.PsiMethod
+import java.util.EnumSet
+import org.jetbrains.uast.UCallExpression
+
+/**
+ * Detector for flagging potential multiuser issues in `PendingIntent.getActivity()` calls.
+ *
+ * This detector checks for calls to `PendingIntent#getActivity()` and
+ * reports a warning if such a call is found, suggesting that the
+ * default user 0 context might not be the right one.
+ */
+class PendingIntentGetActivityDetector : Detector(), SourceCodeScanner {
+
+  companion object {
+
+    val description = """Flags potential multiuser issue in PendingIntent.getActivity() calls."""
+
+    val EXPLANATION =
+      """
+      **Problem:**
+
+      Calling `PendingIntent.getActivity()` in the `system_server` often accidentally uses the user 0 context.  Moreover, since there's no explicit user parameter in the `getActivity` method, it can be hard to tell which user the `PendingIntent` activity is associated with, making the code error-prone and less readable.
+
+      **Solution:**
+
+      Always use the user aware methods to refer the correct user context. You can achieve this by:
+
+      * **Using `PendingIntent.getActivityAsUser(...)`:** This API allows you to explicitly specify the user for the activity.
+
+         ```java
+         PendingIntent.getActivityAsUser(
+             mContext, /*requestCode=*/0, intent,
+             PendingIntent.FLAG_IMMUTABLE, /*options=*/null,
+             UserHandle.of(mUserId));
+         ```
+
+      **When to Ignore this Warning:**
+
+      You can safely ignore this warning if you are certain that:
+
+      * You've confirmed that the `PendingIntent` activity you're targeting is the correct one and is **rightly** associated with the context parameter passed into the `PendingIntent.getActivity` method.
+
+      **Note:** If you are unsure about the user context, it's best to err on the side of caution and explicitly specify the user using the method specified above.
+
+      **For any further questions, please reach out to go/multiuser-help.**
+      """.trimIndent()
+
+    val ISSUE_PENDING_INTENT_GET_ACTIVITY: Issue =
+      Issue.create(
+        id = "PendingIntent#getActivity",
+        briefDescription = description,
+        explanation = EXPLANATION,
+        category = Category.SECURITY,
+        priority = 8,
+        severity = Severity.WARNING,
+        implementation =
+          Implementation(
+            PendingIntentGetActivityDetector::class.java,
+            EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES),
+          ),
+      )
+  }
+
+  override fun getApplicableMethodNames() = listOf("getActivity")
+
+  override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+    // Check if the method call is PendingIntent.getActivity
+    if (
+      context.evaluator.isMemberInClass(method, "android.app.PendingIntent") &&
+        method.name == "getActivity"
+    ) {
+        context.report(
+          ISSUE_PENDING_INTENT_GET_ACTIVITY,
+          node,
+          context.getLocation(node),
+          "Using `PendingIntent.getActivity(...)` might not be multiuser-aware. " +
+            "Consider using the user aware method `PendingIntent.getActivityAsUser(...)`.",
+        )
+    }
+  }
+}
diff --git a/tools/lint/framework/checks/src/test/java/com/google/android/lint/multiuser/PendingIntentGetActivityDetectorTest.kt b/tools/lint/framework/checks/src/test/java/com/google/android/lint/multiuser/PendingIntentGetActivityDetectorTest.kt
new file mode 100644
index 0000000..4010550
--- /dev/null
+++ b/tools/lint/framework/checks/src/test/java/com/google/android/lint/multiuser/PendingIntentGetActivityDetectorTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.google.android.lint.multiuser
+
+import com.android.tools.lint.checks.infrastructure.LintDetectorTest
+import com.android.tools.lint.checks.infrastructure.TestFile
+import com.android.tools.lint.checks.infrastructure.TestLintTask
+import com.android.tools.lint.detector.api.Detector
+import com.android.tools.lint.detector.api.Issue
+
+@Suppress("UnstableApiUsage")
+class PendingIntentGetActivityDetectorTest : LintDetectorTest() {
+
+  override fun getDetector(): Detector = PendingIntentGetActivityDetector()
+
+  override fun getIssues(): List<Issue> =
+    listOf(PendingIntentGetActivityDetector.ISSUE_PENDING_INTENT_GET_ACTIVITY)
+
+  override fun lint(): TestLintTask = super.lint().allowMissingSdk(true)
+
+  fun testPendingIntentGetActivity() {
+    lint()
+      .files(
+        java(
+            """
+                package test.pkg;
+
+                import android.app.PendingIntent;
+                import android.content.Context;
+                import android.content.Intent;
+
+                public class TestClass {
+                    private Context mContext;
+
+                    public void testMethod(Intent intent) {
+                        PendingIntent.getActivity(
+                            mContext, /*requestCode=*/0, intent,
+                            PendingIntent.FLAG_IMMUTABLE, /*options=*/null
+                        );
+                    }
+                }
+                """
+          )
+          .indented(),
+        *stubs,
+      )
+      .issues(PendingIntentGetActivityDetector.ISSUE_PENDING_INTENT_GET_ACTIVITY)
+      .run()
+      .expect(
+        """
+        src/test/pkg/TestClass.java:11: Warning: Using PendingIntent.getActivity(...) might not be multiuser-aware. Consider using the user aware method PendingIntent.getActivityAsUser(...). [PendingIntent#getActivity]
+                PendingIntent.getActivity(
+                ^
+        0 errors, 1 warnings
+        """
+      )
+  }
+
+  fun testPendingIntentGetActivityAsUser() {
+    lint()
+      .files(
+        java(
+            """
+                package test.pkg;
+
+                import android.app.PendingIntent;
+                import android.content.Context;
+                import android.content.Intent;
+                import android.os.UserHandle;
+
+                public class TestClass {
+                    private Context mContext;
+
+                    public void testMethod(Intent intent) {
+                        PendingIntent.getActivityAsUser(
+                            mContext, /*requestCode=*/0, intent,
+                            0, /*options=*/null,
+                            UserHandle.CURRENT
+                        );
+                    }
+                }
+                """
+          )
+          .indented(),
+        *stubs,
+      )
+      .issues(PendingIntentGetActivityDetector.ISSUE_PENDING_INTENT_GET_ACTIVITY)
+      .run()
+      .expectClean()
+  }
+
+  private val pendingIntentStub: TestFile =
+    java(
+      """
+        package android.app;
+
+        import android.content.Context;
+        import android.content.Intent;
+        import android.os.UserHandle;
+
+        public class PendingIntent {
+            public static boolean getActivity(Context context, int requestCode, Intent intent, int flags) {
+                return true;
+            }
+
+            public static boolean getActivityAsUser(
+                Context context,
+                int requestCode,
+                Intent intent,
+                int flags,
+                UserHandle userHandle
+            ) {
+                return true;
+            }
+        }
+        """
+    )
+
+  private val contxtStub: TestFile =
+    java(
+      """
+        package android.content;
+
+        import android.os.UserHandle;
+
+        public class Context {
+
+           public Context createContextAsUser(UserHandle userHandle, int flags) {
+                return this;
+            }
+        }
+
+        """
+    )
+
+  private val userHandleStub: TestFile =
+    java(
+      """
+        package android.os;
+
+        public class UserHandle {
+
+        }
+
+        """
+    )
+
+ private val intentStub: TestFile =
+    java(
+        """
+                package android.content;
+
+                public class Intent {
+
+                }
+                """
+    )
+
+  private val stubs = arrayOf(pendingIntentStub, contxtStub, userHandleStub, intentStub)
+}