Merge "Removing upstream flag as it is now the default. Also moving to internal robolectric projects to no longer use the  _upstream suffix." into main
diff --git a/boot/Android.bp b/boot/Android.bp
index 6eead42..6159845 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -75,8 +75,8 @@
             module: "art-bootclasspath-fragment",
         },
         {
-            apex: "com.android.btservices",
-            module: "com.android.btservices-bootclasspath-fragment",
+            apex: "com.android.bt",
+            module: "com.android.bt-bootclasspath-fragment",
         },
         {
             apex: "com.android.configinfrastructure",
diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist
index e3e929c..a6a1d16 100644
--- a/config/preloaded-classes-denylist
+++ b/config/preloaded-classes-denylist
@@ -1,5 +1,4 @@
 android.content.AsyncTaskLoader$LoadTask
-android.media.MediaCodecInfo$CodecCapabilities$FeatureList
 android.net.ConnectivityThread$Singleton
 android.os.FileObserver
 android.os.NullVibrator
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 67f7bee..b5ac4e7 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -70,7 +70,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.Charset;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
@@ -1064,7 +1063,7 @@
             Log.e(TAG, "Save lock exception", e);
             success = false;
         } finally {
-            Arrays.fill(password, (byte) 0);
+            LockPatternUtils.zeroize(password);
         }
         return success;
     }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 1e45d6f..29578c9 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -1129,6 +1129,10 @@
 
     @UnsupportedAppUsage
     public ClassLoader getClassLoader() {
+        ClassLoader ret = mClassLoader;
+        if (ret != null) {
+            return ret;
+        }
         synchronized (mLock) {
             if (mClassLoader == null) {
                 createOrUpdateClassLoaderLocked(null /*addedPaths*/);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 14a802a..d4905f9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4614,6 +4614,7 @@
      * the Android Keystore backed by an isolated execution environment. The version indicates
      * which features are implemented in the isolated execution environment:
      * <ul>
+     * <li>400: Inclusion of module information (via tag MODULE_HASH) in the attestation record.
      * <li>300: Ability to include a second IMEI in the ID attestation record, see
      * {@link android.app.admin.DevicePolicyManager#ID_TYPE_IMEI}.
      * <li>200: Hardware support for Curve 25519 (including both Ed25519 signature generation and
@@ -4647,6 +4648,7 @@
      * StrongBox</a>. If this feature has a version, the version number indicates which features are
      * implemented in StrongBox:
      * <ul>
+     * <li>400: Inclusion of module information (via tag MODULE_HASH) in the attestation record.
      * <li>300: Ability to include a second IMEI in the ID attestation record, see
      * {@link android.app.admin.DevicePolicyManager#ID_TYPE_IMEI}.
      * <li>200: No new features for StrongBox (the Android Keystore environment backed by an
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index ef1e6c94..51aa06b 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -2741,4 +2741,12 @@
      */
     public static native boolean logAllocatorStats();
 
+    /**
+     * Return the amount of memory (in kB) allocated by kernel drivers through CMA.
+     * @return a non-negative value or -1 on error.
+     *
+     * @hide
+     */
+    public static native long getKernelCmaUsageKb();
+
 }
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 9085fe0..a58fea8 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -278,7 +278,7 @@
                 return service;
             } else {
                 return Binder.allowBlocking(
-                        getIServiceManager().checkService(name).getServiceWithMetadata().service);
+                        getIServiceManager().checkService2(name).getServiceWithMetadata().service);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "error in checkService", e);
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 7ea521e..a5aa1b3 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -62,16 +62,23 @@
     @UnsupportedAppUsage
     public IBinder getService(String name) throws RemoteException {
         // Same as checkService (old versions of servicemanager had both methods).
-        return checkService(name).getServiceWithMetadata().service;
+        return checkService2(name).getServiceWithMetadata().service;
     }
 
     public Service getService2(String name) throws RemoteException {
         // Same as checkService (old versions of servicemanager had both methods).
-        return checkService(name);
+        return checkService2(name);
     }
 
-    public Service checkService(String name) throws RemoteException {
-        return mServiceManager.checkService(name);
+    // TODO(b/355394904): This function has been deprecated, please use checkService2 instead.
+    @UnsupportedAppUsage
+    public IBinder checkService(String name) throws RemoteException {
+        // Same as checkService (old versions of servicemanager had both methods).
+        return checkService2(name).getServiceWithMetadata().service;
+    }
+
+    public Service checkService2(String name) throws RemoteException {
+        return mServiceManager.checkService2(name);
     }
 
     public void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 392b42d..ceee898b 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -42,6 +42,16 @@
 }
 
 flag {
+    name: "secure_array_zeroization"
+    namespace: "platform_security"
+    description: "Enable secure array zeroization"
+    bug: "320392352"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "deprecate_fsv_sig"
     namespace: "hardware_backed_security"
     description: "Feature flag for deprecating .fsv_sig"
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index b21e85a..da3a817f 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -514,10 +514,14 @@
             final TypedArray a = resources.obtainAttributes(
                     parser, com.android.internal.R.styleable.PointerIcon);
             bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0);
-            hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0)
-                    * pointerScale;
-            hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0)
-                    * pointerScale;
+            // Cast the hotspot dimensions to int before scaling to match the scaling logic of
+            // the bitmap, whose intrinsic size is also an int before it is scaled.
+            final int unscaledHotSpotX =
+                    (int) a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0);
+            final int unscaledHotSpotY =
+                    (int) a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0);
+            hotSpotX = unscaledHotSpotX * pointerScale;
+            hotSpotY = unscaledHotSpotY * pointerScale;
             a.recycle();
         } catch (Exception ex) {
             throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9bd5237..d129762 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -61,6 +61,7 @@
 import android.util.SparseLongArray;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
 
 import com.google.android.collect.Lists;
@@ -71,6 +72,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
@@ -288,6 +290,56 @@
 
     }
 
+    /**
+     * This exists temporarily due to trunk-stable policies.
+     * Please use ArrayUtils directly if you can.
+     */
+    public static byte[] newNonMovableByteArray(int length) {
+        if (!android.security.Flags.secureArrayZeroization()) {
+            return new byte[length];
+        }
+        return ArrayUtils.newNonMovableByteArray(length);
+    }
+
+    /**
+     * This exists temporarily due to trunk-stable policies.
+     * Please use ArrayUtils directly if you can.
+     */
+    public static char[] newNonMovableCharArray(int length) {
+        if (!android.security.Flags.secureArrayZeroization()) {
+            return new char[length];
+        }
+        return ArrayUtils.newNonMovableCharArray(length);
+    }
+
+    /**
+     * This exists temporarily due to trunk-stable policies.
+     * Please use ArrayUtils directly if you can.
+     */
+    public static void zeroize(byte[] array) {
+        if (!android.security.Flags.secureArrayZeroization()) {
+            if (array != null) {
+                Arrays.fill(array, (byte) 0);
+            }
+            return;
+        }
+        ArrayUtils.zeroize(array);
+    }
+
+    /**
+     * This exists temporarily due to trunk-stable policies.
+     * Please use ArrayUtils directly if you can.
+     */
+    public static void zeroize(char[] array) {
+        if (!android.security.Flags.secureArrayZeroization()) {
+            if (array != null) {
+                Arrays.fill(array, (char) 0);
+            }
+            return;
+        }
+        ArrayUtils.zeroize(array);
+    }
+
     @UnsupportedAppUsage
     public DevicePolicyManager getDevicePolicyManager() {
         if (mDevicePolicyManager == null) {
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
index 54b9a22..92ce990 100644
--- a/core/java/com/android/internal/widget/LockscreenCredential.java
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -246,7 +246,7 @@
      */
     public void zeroize() {
         if (mCredential != null) {
-            Arrays.fill(mCredential, (byte) 0);
+            LockPatternUtils.zeroize(mCredential);
             mCredential = null;
         }
     }
@@ -346,7 +346,7 @@
             byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword);
             byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword);
 
-            Arrays.fill(saltedPassword, (byte) 0);
+            LockPatternUtils.zeroize(saltedPassword);
             return HexEncoding.encodeToString(ArrayUtils.concat(sha1, md5));
         } catch (NoSuchAlgorithmException e) {
             throw new AssertionError("Missing digest algorithm: ", e);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 3c2dccd..9ef17e8 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -729,6 +729,17 @@
     return gpuPrivateMem / 1024;
 }
 
+static jlong android_os_Debug_getKernelCmaUsageKb(JNIEnv* env, jobject clazz) {
+    jlong totalKernelCmaUsageKb = -1;
+    uint64_t size;
+
+    if (meminfo::ReadKernelCmaUsageKb(&size)) {
+        totalKernelCmaUsageKb = size;
+    }
+
+    return totalKernelCmaUsageKb;
+}
+
 static jlong android_os_Debug_getDmabufMappedSizeKb(JNIEnv* env, jobject clazz) {
     jlong dmabufPss = 0;
     std::vector<dmabufinfo::DmaBuffer> dmabufs;
@@ -836,6 +847,7 @@
         {"getGpuTotalUsageKb", "()J", (void*)android_os_Debug_getGpuTotalUsageKb},
         {"isVmapStack", "()Z", (void*)android_os_Debug_isVmapStack},
         {"logAllocatorStats", "()Z", (void*)android_os_Debug_logAllocatorStats},
+        {"getKernelCmaUsageKb", "()J", (void*)android_os_Debug_getKernelCmaUsageKb},
 };
 
 int register_android_os_Debug(JNIEnv *env)
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 06cd44e..9b3a6cb 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -111,7 +111,7 @@
     <shortcode country="cz" premium="90\\d{5}|90\\d{3}" free="116\\d{3}" />
 
     <!-- Germany: 4-5 digits plus 1232xxx (premium codes from http://www.vodafone.de/infofaxe/537.pdf and http://premiumdienste.eplus.de/pdf/kodex.pdf), plus EU. To keep the premium regex from being too large, it only includes payment processors that have been used by SMS malware, with the regular pattern matching the other premium short codes. -->
-    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529|70296|83782|3011|73240|72438" />
+    <shortcode country="de" pattern="\\d{4,5}|1232\\d{3}" premium="11(?:111|833)|1232(?:013|021|060|075|286|358)|118(?:44|80|86)|20[25]00|220(?:21|22|88|99)|221(?:14|21)|223(?:44|53|77)|224[13]0|225(?:20|59|90)|226(?:06|10|20|26|30|40|56|70)|227(?:07|33|39|66|76|78|79|88|99)|228(?:08|11|66|77)|23300|30030|3[12347]000|330(?:33|55|66)|33(?:233|331|366|533)|34(?:34|567)|37000|40(?:040|123|444|[3568]00)|41(?:010|414)|44(?:000|044|344|44[24]|544)|50005|50100|50123|50555|51000|52(?:255|783)|54(?:100|2542)|55(?:077|[24]00|222|333|55|[12369]55)|56(?:789|886)|60800|6[13]000|66(?:[12348]66|566|766|777|88|999)|68888|70(?:07|123|777)|76766|77(?:007|070|222|444|[567]77)|80(?:008|123|888)|82(?:002|[378]00|323|444|472|474|488|727)|83(?:005|[169]00|333|830)|84(?:141|300|32[34]|343|488|499|777|888)|85888|86(?:188|566|640|644|650|677|868|888)|870[24]9|871(?:23|[49]9)|872(?:1[0-8]|49|99)|87499|875(?:49|55|99)|876(?:0[1367]|1[1245678]|54|99)|877(?:00|99)|878(?:15|25|3[567]|8[12])|87999|880(?:08|44|55|77|99)|88688|888(?:03|10|8|89)|8899|90(?:009|999)|99999" free="116\\d{3}|81214|81215|47529|70296|83782|3011|73240|72438|70997" />
 
     <!-- Denmark: see http://iprs.webspacecommerce.com/Denmark-Premium-Rate-Numbers -->
     <shortcode country="dk" pattern="\\d{4,5}" premium="1\\d{3}" free="116\\d{3}|4665" />
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index c45080f..5fd4ffc 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -10,6 +10,9 @@
 # PerformanceHintManager
 per-file PerformanceHintManagerTest.java = file:/ADPF_OWNERS
 
+# SystemHealthManager
+per-file SystemHealthManagerUnitTest.java = file:/ADPF_OWNERS
+
 # Caching
 per-file IpcDataCache* = file:/PERFORMANCE_OWNERS
 
diff --git a/keystore/java/android/security/keystore/KeyStoreManager.java b/keystore/java/android/security/keystore/KeyStoreManager.java
index 740ccb5..13f1a72 100644
--- a/keystore/java/android/security/keystore/KeyStoreManager.java
+++ b/keystore/java/android/security/keystore/KeyStoreManager.java
@@ -312,9 +312,11 @@
      * When passed into getSupplementaryAttestationInfo, getSupplementaryAttestationInfo returns the
      * DER-encoded structure corresponding to the `Modules` schema described in the KeyMint HAL's
      * KeyCreationResult.aidl. The SHA-256 hash of this encoded structure is what's included with
-     * the tag in attestations.
+     * the tag in attestations. To ensure the returned encoded structure is the one attested to,
+     * clients should verify its SHA-256 hash matches the one in the attestation. Note that the
+     * returned structure can vary between boots.
      */
-    // TODO(b/369375199): Replace with Tag.MODULE_HASH when flagging is removed.
+    // TODO(b/380020528): Replace with Tag.MODULE_HASH when KeyMint V4 is frozen.
     public static final int MODULE_HASH = TagType.BYTES | 724;
 
     /**
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index e302393..a0d1cbc 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -674,7 +674,7 @@
         VkSemaphore semaphore;
         VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore);
         ALOGE_IF(VK_SUCCESS != err,
-                 "VulkanManager::makeSwapSemaphore(): Failed to create semaphore");
+                 "VulkanManager::finishFrame(): Failed to create semaphore");
 
         if (err == VK_SUCCESS) {
             sharedSemaphore = sp<SharedSemaphoreInfo>::make(mDestroySemaphore, mDevice, semaphore);
@@ -722,7 +722,7 @@
 
         int fenceFd = -1;
         VkResult err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd);
-        ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd");
+        ALOGE_IF(VK_SUCCESS != err, "VulkanManager::finishFrame(): Failed to get semaphore Fd");
         drawResult.presentFence.reset(fenceFd);
     } else {
         ALOGE("VulkanManager::finishFrame(): Semaphore submission failed");
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index e52e0b1..6a21496 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -1,7 +1,10 @@
 {
   "presubmit": [
     {
-      "name": "CtsMediaBetterTogetherTestCases"
+      "name": "CtsMediaRouterTestCases"
+    },
+    {
+      "name": "CtsMediaSessionTestCases"
     },
     {
       "name": "mediaroutertest"
diff --git a/native/android/tests/system_health/OWNERS b/native/android/tests/system_health/OWNERS
new file mode 100644
index 0000000..e3bbee92
--- /dev/null
+++ b/native/android/tests/system_health/OWNERS
@@ -0,0 +1 @@
+include /ADPF_OWNERS
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 493afde..df3310f 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -451,6 +451,9 @@
         mTelephonyListenerManager.removeServiceStateListener(mPhoneStateListener);
         mGlobalSettings.unregisterContentObserverSync(mAirplaneModeObserver);
         mConfigurationController.removeCallback(this);
+        if (mShowSilentToggle) {
+            mRingerModeTracker.getRingerMode().removeObservers(this);
+        }
     }
 
     protected Context getContext() {
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 59043a83..8e99842 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -345,14 +345,41 @@
     ],
 }
 
+// We define our own version of platform_compat_config's here, because:
+// - The original version (e.g. "framework-platform-compat-config) is built from
+//   the output file of the device side jar, rather than the host jar, meaning
+//   they're slow to build because they depend on D8/R8 output.
+// - The original services one ("services-platform-compat-config") is built from services.jar,
+//   which includes service.permission, which is very slow to rebuild because of kotlin.
+//
+// Because we're re-defining the same compat-IDs that are defined elsewhere,
+// they should all have `include_in_merged_xml: false`. Otherwise, generating
+// merged_compat_config.xml would fail due to duplicate IDs.
+//
+// These module names must end with "compat-config" because these will be used as the filename,
+// and at runtime, we only loads files that match `*compat-config.xml`.
+platform_compat_config {
+    name: "ravenwood-framework-platform-compat-config",
+    src: ":framework-minus-apex-for-host",
+    include_in_merged_xml: false,
+    visibility: ["//visibility:private"],
+}
+
+platform_compat_config {
+    name: "ravenwood-services.core-platform-compat-config",
+    src: ":services.core-for-host",
+    include_in_merged_xml: false,
+    visibility: ["//visibility:private"],
+}
+
 filegroup {
     name: "ravenwood-data",
     device_common_srcs: [
         ":system-build.prop",
         ":framework-res",
         ":ravenwood-empty-res",
-        ":framework-platform-compat-config",
-        ":services-platform-compat-config",
+        ":ravenwood-framework-platform-compat-config",
+        ":ravenwood-services.core-platform-compat-config",
         "texts/ravenwood-build.prop",
     ],
     device_first_srcs: [
@@ -616,6 +643,10 @@
         "android.test.mock.ravenwood",
         "ravenwood-helper-runtime",
         "hoststubgen-helper-runtime.ravenwood",
+
+        // Note, when we include other services.* jars, we'll need to add
+        // platform_compat_config for that module too.
+        // See ravenwood-services.core-platform-compat-config above.
         "services.core.ravenwood-jarjar",
         "services.fakes.ravenwood-jarjar",
 
diff --git a/ravenwood/CleanSpec.mk b/ravenwood/CleanSpec.mk
new file mode 100644
index 0000000..50d2fab
--- /dev/null
+++ b/ravenwood/CleanSpec.mk
@@ -0,0 +1,45 @@
+# 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.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# *****************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
+# *****************************************************************
+
+$(call add-clean-step, rm -rf $(OUT_DIR)/host/linux-x86/testcases/ravenwood-runtime)
+
+# ******************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
+# ******************************************************************
diff --git a/ravenwood/tools/hoststubgen/scripts/dump-jar b/ravenwood/tools/hoststubgen/scripts/dump-jar
index 8765245..998357b 100755
--- a/ravenwood/tools/hoststubgen/scripts/dump-jar
+++ b/ravenwood/tools/hoststubgen/scripts/dump-jar
@@ -89,7 +89,7 @@
     # - Some other transient lines
     # - Sometimes the javap shows mysterious warnings, so remove them too.
     #
-    # `/PATTERN-1/,/PATTERN-1/{//!d}` is a trick to delete lines between two patterns, without
+    # `/PATTERN-1/,/PATTERN-2/{//!d}` is a trick to delete lines between two patterns, without
     # the start and the end lines.
     sed -e 's/#[0-9][0-9]*/#x/g' \
         -e 's/^\( *\)[0-9][0-9]*:/\1x:/' \
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 6d8d7b7..cc704b2 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -27,7 +27,7 @@
 import com.android.hoststubgen.filters.KeepNativeFilter
 import com.android.hoststubgen.filters.OutputFilter
 import com.android.hoststubgen.filters.SanitizationFilter
-import com.android.hoststubgen.filters.TextFileFilterPolicyParser
+import com.android.hoststubgen.filters.TextFileFilterPolicyBuilder
 import com.android.hoststubgen.filters.printAsTextPolicy
 import com.android.hoststubgen.utils.ClassFilter
 import com.android.hoststubgen.visitors.BaseAdapter
@@ -179,9 +179,9 @@
         // Next, "text based" filter, which allows to override polices without touching
         // the target code.
         if (options.policyOverrideFiles.isNotEmpty()) {
-            val parser = TextFileFilterPolicyParser(allClasses, filter)
-            options.policyOverrideFiles.forEach(parser::parse)
-            filter = parser.createOutputFilter()
+            val builder = TextFileFilterPolicyBuilder(allClasses, filter)
+            options.policyOverrideFiles.forEach(builder::parse)
+            filter = builder.createOutputFilter()
         }
 
         // Apply the implicit filter.
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 7462a8c..be1b6ca 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -23,10 +23,12 @@
 import com.android.hoststubgen.log
 import com.android.hoststubgen.normalizeTextLine
 import com.android.hoststubgen.whitespaceRegex
-import java.io.File
-import java.io.PrintWriter
-import java.util.regex.Pattern
 import org.objectweb.asm.tree.ClassNode
+import java.io.BufferedReader
+import java.io.FileReader
+import java.io.PrintWriter
+import java.io.Reader
+import java.util.regex.Pattern
 
 /**
  * Print a class node as a "keep" policy.
@@ -48,7 +50,7 @@
 
 private const val FILTER_REASON = "file-override"
 
-private enum class SpecialClass {
+enum class SpecialClass {
     NotSpecial,
     Aidl,
     FeatureFlags,
@@ -56,10 +58,58 @@
     RFile,
 }
 
-class TextFileFilterPolicyParser(
+/**
+ * This receives [TextFileFilterPolicyBuilder] parsing result.
+ */
+interface PolicyFileProcessor {
+    /** "package" directive. */
+    fun onPackage(name: String, policy: FilterPolicyWithReason)
+
+    /** "rename" directive. */
+    fun onRename(pattern: Pattern, prefix: String)
+
+    /** "class" directive. */
+    fun onSimpleClassStart(className: String)
+    fun onSimpleClassPolicy(className: String, policy: FilterPolicyWithReason)
+    fun onSimpleClassEnd(className: String)
+
+    fun onSubClassPolicy(superClassName: String, policy: FilterPolicyWithReason)
+    fun onRedirectionClass(fromClassName: String, toClassName: String)
+    fun onClassLoadHook(className: String, callback: String)
+    fun onSpecialClassPolicy(type: SpecialClass, policy: FilterPolicyWithReason)
+
+    /** "field" directive. */
+    fun onField(className: String, fieldName: String, policy: FilterPolicyWithReason)
+
+    /** "method" directive. */
+    fun onSimpleMethodPolicy(
+        className: String,
+        methodName: String,
+        methodDesc: String,
+        policy: FilterPolicyWithReason,
+    )
+    fun onMethodInClassReplace(
+        className: String,
+        methodName: String,
+        methodDesc: String,
+        targetName: String,
+        policy: FilterPolicyWithReason,
+    )
+    fun onMethodOutClassReplace(
+        className: String,
+        methodName: String,
+        methodDesc: String,
+        replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
+        policy: FilterPolicyWithReason,
+    )
+}
+
+class TextFileFilterPolicyBuilder(
     private val classes: ClassNodes,
     fallback: OutputFilter
 ) {
+    private val parser = TextFileFilterPolicyParser()
+
     private val subclassFilter = SubclassFilter(classes, fallback)
     private val packageFilter = PackageFilter(subclassFilter)
     private val imf = InMemoryOutputFilter(classes, packageFilter)
@@ -71,30 +121,19 @@
     private val methodReplaceSpec =
         mutableListOf<TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec>()
 
-    private lateinit var currentClassName: String
-
     /**
-     * Read a given "policy" file and return as an [OutputFilter]
+     * Parse a given policy file. This method can be called multiple times to read from
+     * multiple files. To get the resulting filter, use [createOutputFilter]
      */
     fun parse(file: String) {
-        log.i("Loading offloaded annotations from $file ...")
-        log.withIndent {
-            var lineNo = 0
-            try {
-                File(file).forEachLine {
-                    lineNo++
-                    val line = normalizeTextLine(it)
-                    if (line.isEmpty()) {
-                        return@forEachLine // skip empty lines.
-                    }
-                    parseLine(line)
-                }
-            } catch (e: ParseException) {
-                throw e.withSourceInfo(file, lineNo)
-            }
-        }
+        // We may parse multiple files, but we reuse the same parser, because the parser
+        // will make sure there'll be no dupplicating "special class" policies.
+        parser.parse(FileReader(file), file, Processor())
     }
 
+    /**
+     * Generate the resulting [OutputFilter].
+     */
     fun createOutputFilter(): OutputFilter {
         var ret: OutputFilter = imf
         if (typeRenameSpec.isNotEmpty()) {
@@ -112,14 +151,200 @@
         return ret
     }
 
+    private inner class Processor : PolicyFileProcessor {
+        override fun onPackage(name: String, policy: FilterPolicyWithReason) {
+            packageFilter.addPolicy(name, policy)
+        }
+
+        override fun onRename(pattern: Pattern, prefix: String) {
+            typeRenameSpec += TextFilePolicyRemapperFilter.TypeRenameSpec(
+                pattern, prefix
+            )
+        }
+
+        override fun onSimpleClassStart(className: String) {
+        }
+
+        override fun onSimpleClassEnd(className: String) {
+        }
+
+        override fun onSimpleClassPolicy(className: String, policy: FilterPolicyWithReason) {
+            imf.setPolicyForClass(className, policy)
+        }
+
+        override fun onSubClassPolicy(
+            superClassName: String,
+            policy: FilterPolicyWithReason,
+            ) {
+            log.i("class extends $superClassName")
+            subclassFilter.addPolicy( superClassName, policy)
+        }
+
+        override fun onRedirectionClass(fromClassName: String, toClassName: String) {
+            imf.setRedirectionClass(fromClassName, toClassName)
+        }
+
+        override fun onClassLoadHook(className: String, callback: String) {
+            imf.setClassLoadHook(className, callback)
+        }
+
+        override fun onSpecialClassPolicy(
+            type: SpecialClass,
+            policy: FilterPolicyWithReason,
+        ) {
+            log.i("class special $type $policy")
+            when (type) {
+                SpecialClass.NotSpecial -> {} // Shouldn't happen
+
+                SpecialClass.Aidl -> {
+                    aidlPolicy = policy
+                }
+
+                SpecialClass.FeatureFlags -> {
+                    featureFlagsPolicy = policy
+                }
+
+                SpecialClass.Sysprops -> {
+                    syspropsPolicy = policy
+                }
+
+                SpecialClass.RFile -> {
+                    rFilePolicy = policy
+                }
+            }
+        }
+
+        override fun onField(className: String, fieldName: String, policy: FilterPolicyWithReason) {
+            imf.setPolicyForField(className, fieldName, policy)
+        }
+
+        override fun onSimpleMethodPolicy(
+            className: String,
+            methodName: String,
+            methodDesc: String,
+            policy: FilterPolicyWithReason,
+        ) {
+            imf.setPolicyForMethod(className, methodName, methodDesc, policy)
+        }
+
+        override fun onMethodInClassReplace(
+            className: String,
+            methodName: String,
+            methodDesc: String,
+            targetName: String,
+            policy: FilterPolicyWithReason,
+        ) {
+            imf.setPolicyForMethod(className, methodName, methodDesc, policy)
+
+            // Make sure to keep the target method.
+            imf.setPolicyForMethod(
+                className,
+                targetName,
+                methodDesc,
+                FilterPolicy.Keep.withReason(FILTER_REASON)
+            )
+            // Set up the rename.
+            imf.setRenameTo(className, targetName, methodDesc, methodName)
+        }
+
+        override fun onMethodOutClassReplace(
+            className: String,
+            methodName: String,
+            methodDesc: String,
+            replaceSpec: TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec,
+            policy: FilterPolicyWithReason,
+        ) {
+            imf.setPolicyForMethod(className, methodName, methodDesc, policy)
+            methodReplaceSpec.add(replaceSpec)
+        }
+    }
+}
+
+/**
+ * Parses a filer policy text file.
+ */
+class TextFileFilterPolicyParser {
+    private lateinit var processor: PolicyFileProcessor
+    private var currentClassName: String? = null
+
+    private var aidlPolicy: FilterPolicyWithReason? = null
+    private var featureFlagsPolicy: FilterPolicyWithReason? = null
+    private var syspropsPolicy: FilterPolicyWithReason? = null
+    private var rFilePolicy: FilterPolicyWithReason? = null
+
+    /** Name of the file that's currently being processed.  */
+    var filename: String? = null
+        private set
+
+    /** 1-based line number in the current file */
+    var lineNumber = -1
+        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
+            try {
+                while (true) {
+                    var line = rd.readLine()
+                    if (line == null) {
+                        break
+                    }
+                    lineNumber++
+                    line = normalizeTextLine(line) // Remove comment and trim.
+                    if (line.isEmpty()) {
+                        continue
+                    }
+                    parseLine(line)
+                }
+                finishLastClass()
+            } catch (e: ParseException) {
+                throw e.withSourceInfo(inputName, lineNumber)
+            }
+        }
+    }
+
+    private fun finishLastClass() {
+        currentClassName?.let { className ->
+            processor.onSimpleClassEnd(className)
+            currentClassName = null
+        }
+    }
+
+    private fun ensureInClass(directive: String): String {
+        return currentClassName ?:
+            throw ParseException("Directive '$directive' must follow a 'class' directive")
+    }
+
     private fun parseLine(line: String) {
         val fields = line.split(whitespaceRegex).toTypedArray()
         when (fields[0].lowercase()) {
-            "p", "package" -> parsePackage(fields)
-            "c", "class" -> parseClass(fields)
-            "f", "field" -> parseField(fields)
-            "m", "method" -> parseMethod(fields)
-            "r", "rename" -> parseRename(fields)
+            "p", "package" -> {
+                finishLastClass()
+                parsePackage(fields)
+            }
+            "c", "class" -> {
+                finishLastClass()
+                parseClass(fields)
+            }
+            "f", "field" -> {
+                ensureInClass("field")
+                parseField(fields)
+            }
+            "m", "method" -> {
+                ensureInClass("method")
+                parseMethod(fields)
+            }
+            "r", "rename" -> {
+                finishLastClass()
+                parseRename(fields)
+            }
             else -> throw ParseException("Unknown directive \"${fields[0]}\"")
         }
     }
@@ -184,20 +409,20 @@
         if (!policy.isUsableWithClasses) {
             throw ParseException("Package can't have policy '$policy'")
         }
-        packageFilter.addPolicy(name, policy.withReason(FILTER_REASON))
+        processor.onPackage(name, policy.withReason(FILTER_REASON))
     }
 
     private fun parseClass(fields: Array<String>) {
         if (fields.size < 3) {
             throw ParseException("Class ('c') expects 2 fields.")
         }
-        currentClassName = fields[1]
+        val className = fields[1]
 
         // superClass is set when the class name starts with a "*".
-        val superClass = resolveExtendingClass(currentClassName)
+        val superClass = resolveExtendingClass(className)
 
         // :aidl, etc?
-        val classType = resolveSpecialClass(currentClassName)
+        val classType = resolveSpecialClass(className)
 
         if (fields[2].startsWith("!")) {
             if (classType != SpecialClass.NotSpecial) {
@@ -208,7 +433,8 @@
             }
             // It's a redirection class.
             val toClass = fields[2].substring(1)
-            imf.setRedirectionClass(currentClassName, toClass)
+
+            processor.onRedirectionClass(className, toClass)
         } else if (fields[2].startsWith("~")) {
             if (classType != SpecialClass.NotSpecial) {
                 // We could support it, but not needed at least for now.
@@ -218,7 +444,8 @@
             }
             // It's a class-load hook
             val callback = fields[2].substring(1)
-            imf.setClassLoadHook(currentClassName, callback)
+
+            processor.onClassLoadHook(className, callback)
         } else {
             val policy = parsePolicy(fields[2])
             if (!policy.isUsableWithClasses) {
@@ -229,26 +456,27 @@
                 SpecialClass.NotSpecial -> {
                     // TODO: Duplicate check, etc
                     if (superClass == null) {
-                        imf.setPolicyForClass(
-                            currentClassName, policy.withReason(FILTER_REASON)
-                        )
+                        currentClassName = className
+                        processor.onSimpleClassStart(className)
+                        processor.onSimpleClassPolicy(className, policy.withReason(FILTER_REASON))
                     } else {
-                        subclassFilter.addPolicy(
+                        processor.onSubClassPolicy(
                             superClass,
-                            policy.withReason("extends $superClass")
+                            policy.withReason("extends $superClass"),
                         )
                     }
                 }
-
                 SpecialClass.Aidl -> {
                     if (aidlPolicy != null) {
                         throw ParseException(
                             "Policy for AIDL classes already defined"
                         )
                     }
-                    aidlPolicy = policy.withReason(
+                    val p = policy.withReason(
                         "$FILTER_REASON (special-class AIDL)"
                     )
+                    processor.onSpecialClassPolicy(classType, p)
+                    aidlPolicy = p
                 }
 
                 SpecialClass.FeatureFlags -> {
@@ -257,9 +485,11 @@
                             "Policy for feature flags already defined"
                         )
                     }
-                    featureFlagsPolicy = policy.withReason(
+                    val p = policy.withReason(
                         "$FILTER_REASON (special-class feature flags)"
                     )
+                    processor.onSpecialClassPolicy(classType, p)
+                    featureFlagsPolicy = p
                 }
 
                 SpecialClass.Sysprops -> {
@@ -268,9 +498,11 @@
                             "Policy for sysprops already defined"
                         )
                     }
-                    syspropsPolicy = policy.withReason(
+                    val p = policy.withReason(
                         "$FILTER_REASON (special-class sysprops)"
                     )
+                    processor.onSpecialClassPolicy(classType, p)
+                    syspropsPolicy = p
                 }
 
                 SpecialClass.RFile -> {
@@ -279,9 +511,11 @@
                             "Policy for R file already defined"
                         )
                     }
-                    rFilePolicy = policy.withReason(
+                    val p = policy.withReason(
                         "$FILTER_REASON (special-class R file)"
                     )
+                    processor.onSpecialClassPolicy(classType, p)
+                    rFilePolicy = p
                 }
             }
         }
@@ -296,17 +530,16 @@
         if (!policy.isUsableWithFields) {
             throw ParseException("Field can't have policy '$policy'")
         }
-        require(this::currentClassName.isInitialized)
 
         // TODO: Duplicate check, etc
-        imf.setPolicyForField(currentClassName, name, policy.withReason(FILTER_REASON))
+        processor.onField(currentClassName!!, name, policy.withReason(FILTER_REASON))
     }
 
     private fun parseMethod(fields: Array<String>) {
         if (fields.size < 3 || fields.size > 4) {
             throw ParseException("Method ('m') expects 3 or 4 fields.")
         }
-        val name = fields[1]
+        val methodName = fields[1]
         val signature: String
         val policyStr: String
         if (fields.size <= 3) {
@@ -323,44 +556,48 @@
             throw ParseException("Method can't have policy '$policy'")
         }
 
-        require(this::currentClassName.isInitialized)
+        val className = currentClassName!!
 
-        imf.setPolicyForMethod(
-            currentClassName, name, signature,
-            policy.withReason(FILTER_REASON)
-        )
-        if (policy == FilterPolicy.Substitute) {
-            val fromName = policyStr.substring(1)
+        val policyWithReason = policy.withReason(FILTER_REASON)
+        if (policy != FilterPolicy.Substitute) {
+            processor.onSimpleMethodPolicy(className, methodName, signature, policyWithReason)
+        } else {
+            val targetName = policyStr.substring(1)
 
-            if (fromName == name) {
+            if (targetName == methodName) {
                 throw ParseException(
                     "Substitution must have a different name"
                 )
             }
 
-            // Set the policy for the "from" method.
-            imf.setPolicyForMethod(
-                currentClassName, fromName, signature,
-                FilterPolicy.Keep.withReason(FILTER_REASON)
-            )
-
-            val classAndMethod = splitWithLastPeriod(fromName)
+            val classAndMethod = splitWithLastPeriod(targetName)
             if (classAndMethod != null) {
                 // If the substitution target contains a ".", then
                 // it's a method call redirect.
-                methodReplaceSpec.add(
-                    TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec(
-                        currentClassName.toJvmClassName(),
-                        name,
+                val spec = TextFilePolicyMethodReplaceFilter.MethodCallReplaceSpec(
+                        currentClassName!!.toJvmClassName(),
+                        methodName,
                         signature,
                         classAndMethod.first.toJvmClassName(),
                         classAndMethod.second,
                     )
+                processor.onMethodOutClassReplace(
+                    className,
+                    methodName,
+                    signature,
+                    spec,
+                    policyWithReason,
                 )
             } else {
                 // It's an in-class replace.
                 // ("@RavenwoodReplace" equivalent)
-                imf.setRenameTo(currentClassName, fromName, signature, name)
+                processor.onMethodInClassReplace(
+                    className,
+                    methodName,
+                    signature,
+                    targetName,
+                    policyWithReason,
+                )
             }
         }
     }
@@ -378,7 +615,7 @@
         // applied. (Which is needed for services.jar)
         val prefix = fields[2].trimStart('/')
 
-        typeRenameSpec += TextFilePolicyRemapperFilter.TypeRenameSpec(
+        processor.onRename(
             pattern, prefix
         )
     }
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
index b389a67..8408a18 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/diff-and-update-golden.sh
@@ -34,10 +34,11 @@
 
 SCRIPT_NAME="${0##*/}"
 
-GOLDEN_DIR=golden-output
+GOLDEN_DIR=${GOLDEN_DIR:-golden-output}
 mkdir -p $GOLDEN_DIR
 
-DIFF_CMD=${DIFF:-diff -u --ignore-blank-lines --ignore-space-change}
+# TODO(b/388562869) We shouldn't need `--ignore-matching-lines`, but the golden files may not have the "Constant pool:" lines.
+DIFF_CMD=${DIFF_CMD:-diff -u --ignore-blank-lines --ignore-space-change --ignore-matching-lines='^\(Constant.pool:\|{\)$'}
 
 update=0
 three_way=0
@@ -62,7 +63,7 @@
 shift $(($OPTIND - 1))
 
 # Build the dump files, which are the input of this test.
-run m  dump-jar tiny-framework-dump-test
+run ${BUILD_CMD:=m} dump-jar tiny-framework-dump-test
 
 
 # Get the path to the generate text files. (not the golden files.)
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index 88fa492..c35d6d1 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -28,8 +28,11 @@
 
 # Run diff.
 def run_diff(file1, file2):
+    # TODO(b/388562869) We shouldn't need `--ignore-matching-lines`, but the golden files may not have the "Constant pool:" lines.
     command = ['diff', '-u', '--ignore-blank-lines',
-               '--ignore-space-change', file1, file2]
+               '--ignore-space-change',
+               '--ignore-matching-lines=^\(Constant.pool:\|{\)$',
+               file1, file2]
     print(' '.join(command))
     result = subprocess.run(command, stderr=sys.stdout)
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f5e1545..8de1295 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -12796,6 +12796,28 @@
                 }
             }
 
+            final long kernelCmaUsage = Debug.getKernelCmaUsageKb();
+            if (kernelCmaUsage >= 0) {
+                pw.print("      Kernel CMA: ");
+                pw.println(stringifyKBSize(kernelCmaUsage));
+                // CMA memory can be in one of the following four states:
+                //
+                // 1. Free, in which case it is accounted for as part of MemFree, which
+                //    is already considered in the lostRAM calculation below.
+                //
+                // 2. Allocated as part of a userspace allocated, in which case it is
+                //    already accounted for in the total PSS value that was computed.
+                //
+                // 3. Allocated for storing compressed memory (ZRAM) on Android kernels.
+                //    This is accounted for by calculating the amount of memory ZRAM
+                //    consumes and including it in the lostRAM calculuation.
+                //
+                // 4. Allocated by a kernel driver, in which case, it is currently not
+                //    attributed to any term that has been derived thus far. Since the
+                //    allocations come from a kernel driver, add it to kernelUsed.
+                kernelUsed += kernelCmaUsage;
+            }
+
              // Note: ION/DMA-BUF heap pools are reclaimable and hence, they are included as part of
              // memInfo.getCachedSizeKb().
             final long lostRAM = memInfo.getTotalSizeKb()
@@ -13313,12 +13335,32 @@
                 proto.write(MemInfoDumpProto.CACHED_KERNEL_KB, memInfo.getCachedSizeKb());
                 proto.write(MemInfoDumpProto.FREE_KB, memInfo.getFreeSizeKb());
             }
+            // CMA memory can be in one of the following four states:
+            //
+            // 1. Free, in which case it is accounted for as part of MemFree, which
+            //    is already considered in the lostRAM calculation below.
+            //
+            // 2. Allocated as part of a userspace allocated, in which case it is
+            //    already accounted for in the total PSS value that was computed.
+            //
+            // 3. Allocated for storing compressed memory (ZRAM) on Android Kernels.
+            //    This is accounted for by calculating hte amount of memory ZRAM
+            //    consumes and including it in the lostRAM calculation.
+            //
+            // 4. Allocated by a kernel driver, in which case, it is currently not
+            //    attributed to any term that has been derived thus far, so subtract
+            //    it from lostRAM.
+            long kernelCmaUsage = Debug.getKernelCmaUsageKb();
+            if (kernelCmaUsage < 0) {
+                kernelCmaUsage = 0;
+            }
             long lostRAM = memInfo.getTotalSizeKb()
                     - (ss[INDEX_TOTAL_PSS] - ss[INDEX_TOTAL_SWAP_PSS])
                     - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
                     // NR_SHMEM is subtracted twice (getCachedSizeKb() and getKernelUsedSizeKb())
                     + memInfo.getShmemSizeKb()
-                    - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
+                    - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb()
+                    - kernelCmaUsage;
             proto.write(MemInfoDumpProto.USED_PSS_KB, ss[INDEX_TOTAL_PSS] - cachedPss);
             proto.write(MemInfoDumpProto.USED_KERNEL_KB, memInfo.getKernelUsedSizeKb());
             proto.write(MemInfoDumpProto.LOST_RAM_KB, lostRAM);
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudger.java b/services/core/java/com/android/server/location/fudger/LocationFudger.java
index 88a2697..27ad555 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudger.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudger.java
@@ -239,6 +239,15 @@
 
     // requires latitude since longitudinal distances change with distance from equator.
     private static double metersToDegreesLongitude(double distance, double lat) {
-        return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(Math.toRadians(lat));
+        // Needed to convert from longitude distance to longitude degree.
+        // X meters near the poles is more degrees than at the equator.
+        double cosLat = Math.cos(Math.toRadians(lat));
+        // If we are right on top of the pole, the degree is always 0.
+        // We return a very small value instead to avoid divide by zero errors
+        // later on.
+        if (cosLat == 0.0) {
+            return 0.0001;
+        }
+        return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / cosLat;
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 286238e..0d0cdd8 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -438,9 +438,9 @@
         }
         LockscreenCredential credential =
                 LockscreenCredential.createUnifiedProfilePassword(newPassword);
-        Arrays.fill(newPasswordChars, '\u0000');
-        Arrays.fill(newPassword, (byte) 0);
-        Arrays.fill(randomLockSeed, (byte) 0);
+        LockPatternUtils.zeroize(newPasswordChars);
+        LockPatternUtils.zeroize(newPassword);
+        LockPatternUtils.zeroize(randomLockSeed);
         return credential;
     }
 
@@ -1537,7 +1537,7 @@
                         + userId);
             }
         } finally {
-            Arrays.fill(password, (byte) 0);
+            LockPatternUtils.zeroize(password);
         }
     }
 
@@ -1570,7 +1570,7 @@
         decryptionResult = cipher.doFinal(encryptedPassword);
         LockscreenCredential credential = LockscreenCredential.createUnifiedProfilePassword(
                 decryptionResult);
-        Arrays.fill(decryptionResult, (byte) 0);
+        LockPatternUtils.zeroize(decryptionResult);
         try {
             long parentSid = getGateKeeperService().getSecureUserId(
                     mUserManager.getProfileParent(userId).id);
@@ -2263,7 +2263,7 @@
         } catch (RemoteException e) {
             Slogf.wtf(TAG, e, "Failed to unlock CE storage for %s user %d", userType, userId);
         } finally {
-            Arrays.fill(secret, (byte) 0);
+            LockPatternUtils.zeroize(secret);
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java
index 21caf76..3d64f18 100644
--- a/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java
+++ b/services/core/java/com/android/server/locksettings/UnifiedProfilePasswordCache.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockscreenCredential;
 
 import java.security.GeneralSecurityException;
@@ -154,7 +155,7 @@
             }
             LockscreenCredential result =
                     LockscreenCredential.createUnifiedProfilePassword(credential);
-            Arrays.fill(credential, (byte) 0);
+            LockPatternUtils.zeroize(credential);
             return result;
         }
     }
@@ -175,7 +176,7 @@
                 Slog.d(TAG, "Cannot delete key", e);
             }
             if (mEncryptedPasswords.contains(userId)) {
-                Arrays.fill(mEncryptedPasswords.get(userId), (byte) 0);
+                LockPatternUtils.zeroize(mEncryptedPasswords.get(userId));
                 mEncryptedPasswords.remove(userId);
             }
         }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index bf1b3c3..85dc811 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -162,7 +162,7 @@
             Log.e(TAG, "Unexpected exception thrown during KeySyncTask", e);
         } finally {
             if (mCredential != null) {
-                Arrays.fill(mCredential, (byte) 0); // no longer needed.
+                LockPatternUtils.zeroize(mCredential); // no longer needed.
             }
         }
     }
@@ -506,7 +506,7 @@
 
         try {
             byte[] hash = MessageDigest.getInstance(LOCK_SCREEN_HASH_ALGORITHM).digest(bytes);
-            Arrays.fill(bytes, (byte) 0);
+            LockPatternUtils.zeroize(bytes);
             return hash;
         } catch (NoSuchAlgorithmException e) {
             // Impossible, SHA-256 must be supported on Android.
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 54303c0..7d8300a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -1082,7 +1082,7 @@
             int keyguardCredentialsType = lockPatternUtilsToKeyguardType(savedCredentialType);
             try (LockscreenCredential credential =
                     createLockscreenCredential(keyguardCredentialsType, decryptedCredentials)) {
-                Arrays.fill(decryptedCredentials, (byte) 0);
+                LockPatternUtils.zeroize(decryptedCredentials);
                 decryptedCredentials = null;
                 VerifyCredentialResponse verifyResponse =
                         lockSettingsService.verifyCredential(credential, userId, 0);
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java
index 0e66746..f1ef333 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySessionStorage.java
@@ -19,8 +19,9 @@
 import android.annotation.Nullable;
 import android.util.SparseArray;
 
+import com.android.internal.widget.LockPatternUtils;
+
 import java.util.ArrayList;
-import java.util.Arrays;
 
 import javax.security.auth.Destroyable;
 
@@ -187,8 +188,8 @@
          */
         @Override
         public void destroy() {
-            Arrays.fill(mLskfHash, (byte) 0);
-            Arrays.fill(mKeyClaimant, (byte) 0);
+            LockPatternUtils.zeroize(mLskfHash);
+            LockPatternUtils.zeroize(mKeyClaimant);
         }
     }
 }
diff --git a/services/core/java/com/android/server/media/TEST_MAPPING b/services/core/java/com/android/server/media/TEST_MAPPING
index 43e2afd..dbf9915 100644
--- a/services/core/java/com/android/server/media/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/TEST_MAPPING
@@ -1,7 +1,10 @@
 {
   "presubmit": [
     {
-      "name": "CtsMediaBetterTogetherTestCases"
+      "name": "CtsMediaRouterTestCases"
+    },
+    {
+      "name": "CtsMediaSessionTestCases"
     },
     {
       "name": "MediaRouterServiceTests"
diff --git a/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java b/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
index 2088e41..3831352 100644
--- a/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
+++ b/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
@@ -142,11 +142,8 @@
     private final RateLimiter mRateLimiter;
 
     AggregatedMobileDataStatsPuller(@NonNull NetworkStatsManager networkStatsManager) {
-        if (DEBUG) {
-            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
-                        TAG + "-AggregatedMobileDataStatsPullerInit");
-            }
+        if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-Init");
         }
 
         mRateLimiter = new RateLimiter(/* window= */ Duration.ofSeconds(1));
@@ -173,10 +170,16 @@
 
     public void noteUidProcessState(int uid, int state, long unusedElapsedRealtime,
                                     long unusedUptime) {
-        mMobileDataStatsHandler.post(
+        if (mRateLimiter.tryAcquire()) {
+            mMobileDataStatsHandler.post(
                 () -> {
                     noteUidProcessStateImpl(uid, state);
                 });
+        } else {
+            synchronized (mLock) {
+                mUidPreviousState.put(uid, state);
+            }
+        }
     }
 
     public int pullDataBytesTransfer(List<StatsEvent> data) {
@@ -209,29 +212,27 @@
     }
 
     private void noteUidProcessStateImpl(int uid, int state) {
-        if (mRateLimiter.tryAcquire()) {
-            // noteUidProcessStateImpl can be called back to back several times while
-            // the updateNetworkStats loops over several stats for multiple uids
-            // and during the first call in a batch of proc state change event it can
-            // contain info for uid with unknown previous state yet which can happen due to a few
-            // reasons:
-            // - app was just started
-            // - app was started before the ActivityManagerService
-            // as result stats would be created with state == ActivityManager.PROCESS_STATE_UNKNOWN
-            if (mNetworkStatsManager != null) {
-                updateNetworkStats(mNetworkStatsManager);
-            } else {
-                Slog.w(TAG, "noteUidProcessStateLocked() can not get mNetworkStatsManager");
-            }
+        // noteUidProcessStateImpl can be called back to back several times while
+        // the updateNetworkStats loops over several stats for multiple uids
+        // and during the first call in a batch of proc state change event it can
+        // contain info for uid with unknown previous state yet which can happen due to a few
+        // reasons:
+        // - app was just started
+        // - app was started before the ActivityManagerService
+        // as result stats would be created with state == ActivityManager.PROCESS_STATE_UNKNOWN
+        if (mNetworkStatsManager != null) {
+            updateNetworkStats(mNetworkStatsManager);
+        } else {
+            Slog.w(TAG, "noteUidProcessStateLocked() can not get mNetworkStatsManager");
         }
-        mUidPreviousState.put(uid, state);
+        synchronized (mLock) {
+            mUidPreviousState.put(uid, state);
+        }
     }
 
     private void updateNetworkStats(NetworkStatsManager networkStatsManager) {
-        if (DEBUG) {
-            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStats");
-            }
+        if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStats");
         }
 
         final NetworkStats latestStats = networkStatsManager.getMobileUidStats();
@@ -256,20 +257,25 @@
     }
 
     private void updateNetworkStatsDelta(NetworkStats delta) {
+        if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStatsDelta");
+        }
         synchronized (mLock) {
             for (NetworkStats.Entry entry : delta) {
-                if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
-                    continue;
-                }
-                MobileDataStats stats = getUidStatsForPreviousStateLocked(entry.getUid());
-                if (stats != null) {
-                    stats.addTxBytes(entry.getTxBytes());
-                    stats.addRxBytes(entry.getRxBytes());
-                    stats.addTxPackets(entry.getTxPackets());
-                    stats.addRxPackets(entry.getRxPackets());
+                if (entry.getRxPackets() != 0 || entry.getTxPackets() != 0) {
+                    MobileDataStats stats = getUidStatsForPreviousStateLocked(entry.getUid());
+                    if (stats != null) {
+                        stats.addTxBytes(entry.getTxBytes());
+                        stats.addRxBytes(entry.getRxBytes());
+                        stats.addTxPackets(entry.getTxPackets());
+                        stats.addRxPackets(entry.getRxPackets());
+                    }
                 }
             }
         }
+        if (DEBUG) {
+            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+        }
     }
 
     @GuardedBy("mLock")
@@ -298,18 +304,12 @@
     }
 
     private static boolean isEmpty(NetworkStats stats) {
-        long totalRxPackets = 0;
-        long totalTxPackets = 0;
         for (NetworkStats.Entry entry : stats) {
-            if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
-                continue;
+            if (entry.getRxPackets() != 0 || entry.getTxPackets() != 0) {
+                // at least one non empty entry located
+                return false;
             }
-            totalRxPackets += entry.getRxPackets();
-            totalTxPackets += entry.getTxPackets();
-            // at least one non empty entry located
-            break;
         }
-        final long totalPackets = totalRxPackets + totalTxPackets;
-        return totalPackets == 0;
+        return true;
     }
 }
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index 1c8c245..67f4e56 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -354,10 +354,8 @@
             bitmap.recycle();
 
             final File file = mPersistInfoProvider.getHighResolutionBitmapFile(mId, mUserId);
-            try {
-                FileOutputStream fos = new FileOutputStream(file);
+            try (FileOutputStream fos = new FileOutputStream(file)) {
                 swBitmap.compress(JPEG, COMPRESS_QUALITY, fos);
-                fos.close();
             } catch (IOException e) {
                 Slog.e(TAG, "Unable to open " + file + " for persisting.", e);
                 return false;
@@ -375,10 +373,8 @@
             swBitmap.recycle();
 
             final File lowResFile = mPersistInfoProvider.getLowResolutionBitmapFile(mId, mUserId);
-            try {
-                FileOutputStream lowResFos = new FileOutputStream(lowResFile);
+            try (FileOutputStream lowResFos = new FileOutputStream(lowResFile)) {
                 lowResBitmap.compress(JPEG, COMPRESS_QUALITY, lowResFos);
-                lowResFos.close();
             } catch (IOException e) {
                 Slog.e(TAG, "Unable to open " + lowResFile + " for persisting.", e);
                 return false;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c80d6db..7972d9c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -428,7 +428,7 @@
             "/apex/com.android.uwb/javalib/service-uwb.jar";
     private static final String UWB_SERVICE_CLASS = "com.android.server.uwb.UwbService";
     private static final String BLUETOOTH_APEX_SERVICE_JAR_PATH =
-            "/apex/com.android.btservices/javalib/service-bluetooth.jar";
+            "/apex/com.android.bt/javalib/service-bluetooth.jar";
     private static final String BLUETOOTH_SERVICE_CLASS =
             "com.android.server.bluetooth.BluetoothService";
     private static final String DEVICE_LOCK_SERVICE_CLASS =
@@ -3038,13 +3038,8 @@
                             && context.getPackageManager().hasSystemFeature(
                                     PackageManager.FEATURE_BLUETOOTH_LE))) {
                 t.traceBegin("RangingService");
-                // TODO: b/375264320 - Remove after RELEASE_RANGING_STACK is ramped to next.
-                try {
-                    mSystemServiceManager.startServiceFromJar(RANGING_SERVICE_CLASS,
-                            RANGING_APEX_SERVICE_JAR_PATH);
-                } catch (Throwable e) {
-                    Slog.d(TAG, "service-ranging.jar not found, not starting RangingService");
-                }
+                mSystemServiceManager.startServiceFromJar(RANGING_SERVICE_CLASS,
+                        RANGING_APEX_SERVICE_JAR_PATH);
                 t.traceEnd();
             }
         }
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index ea660b0..22d364e 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -263,7 +263,7 @@
                 .returns(Boolean::class.java)
                 .addParameter(CONTEXT_CLASS, "context")
                 .addParameter(String::class.java, "featureName")
-                .addStatement("return context.getPackageManager().hasSystemFeature(featureName, 0)")
+                .addStatement("return context.getPackageManager().hasSystemFeature(featureName)")
                 .build()
         )
     }
diff --git a/tools/systemfeatures/tests/golden/RoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
index ee97b26..730dacb 100644
--- a/tools/systemfeatures/tests/golden/RoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoFeatures.java.gen
@@ -70,7 +70,7 @@
     }
 
     private static boolean hasFeatureFallback(Context context, String featureName) {
-        return context.getPackageManager().hasSystemFeature(featureName, 0);
+        return context.getPackageManager().hasSystemFeature(featureName);
     }
 
     /**
diff --git a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
index 40c7db7..fe268c7 100644
--- a/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RoNoFeatures.java.gen
@@ -25,7 +25,7 @@
     }
 
     private static boolean hasFeatureFallback(Context context, String featureName) {
-        return context.getPackageManager().hasSystemFeature(featureName, 0);
+        return context.getPackageManager().hasSystemFeature(featureName);
     }
 
     /**
diff --git a/tools/systemfeatures/tests/golden/RwFeatures.java.gen b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
index 7bf8961..bcf978d 100644
--- a/tools/systemfeatures/tests/golden/RwFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwFeatures.java.gen
@@ -55,7 +55,7 @@
     }
 
     private static boolean hasFeatureFallback(Context context, String featureName) {
-        return context.getPackageManager().hasSystemFeature(featureName, 0);
+        return context.getPackageManager().hasSystemFeature(featureName);
     }
 
     /**
diff --git a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
index eb7ec63..7bad5a2 100644
--- a/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
+++ b/tools/systemfeatures/tests/golden/RwNoFeatures.java.gen
@@ -14,7 +14,7 @@
  */
 public final class RwNoFeatures {
     private static boolean hasFeatureFallback(Context context, String featureName) {
-        return context.getPackageManager().hasSystemFeature(featureName, 0);
+        return context.getPackageManager().hasSystemFeature(featureName);
     }
 
     /**
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
index ed3f5c9..491b55e 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorTest.java
@@ -76,28 +76,28 @@
 
         // Also ensure we fall back to the PackageManager for feature APIs without an accompanying
         // versioned feature definition.
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(true);
         assertThat(RwFeatures.hasFeatureWatch(mContext)).isTrue();
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(false);
         assertThat(RwFeatures.hasFeatureWatch(mContext)).isFalse();
     }
 
     @Test
     public void testReadonlyDisabledWithDefinedFeatures() {
         // Always fall back to the PackageManager for defined, explicit features queries.
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(true);
         assertThat(RwFeatures.hasFeatureWatch(mContext)).isTrue();
 
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH, 0)).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)).thenReturn(false);
         assertThat(RwFeatures.hasFeatureWatch(mContext)).isFalse();
 
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI, 0)).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true);
         assertThat(RwFeatures.hasFeatureWifi(mContext)).isTrue();
 
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN, 0)).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_VULKAN)).thenReturn(false);
         assertThat(RwFeatures.hasFeatureVulkan(mContext)).isFalse();
 
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO, 0)).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO)).thenReturn(false);
         assertThat(RwFeatures.hasFeatureAuto(mContext)).isFalse();
 
         // For defined and undefined features, conditional queries should report null (unknown).
@@ -139,9 +139,9 @@
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_VULKAN, 100)).isFalse();
 
         // VERSION=
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO, 0)).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO)).thenReturn(false);
         assertThat(RoFeatures.hasFeatureAuto(mContext)).isFalse();
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO, 0)).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTO)).thenReturn(true);
         assertThat(RoFeatures.hasFeatureAuto(mContext)).isTrue();
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, -1)).isNull();
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_AUTO, 0)).isNull();
@@ -149,9 +149,9 @@
 
         // For feature APIs without an associated feature definition, conditional queries should
         // report null, and explicit queries should report runtime-defined versions.
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC, 0)).thenReturn(true);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC)).thenReturn(true);
         assertThat(RoFeatures.hasFeaturePc(mContext)).isTrue();
-        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC, 0)).thenReturn(false);
+        when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_PC)).thenReturn(false);
         assertThat(RoFeatures.hasFeaturePc(mContext)).isFalse();
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_PC, -1)).isNull();
         assertThat(RoFeatures.maybeHasFeature(PackageManager.FEATURE_PC, 0)).isNull();