Merge "Add UpsideDownCake to aapt2 codenames and android.os.Build"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index c5c6012..3a6b347 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -17,6 +17,14 @@
   ],
   "presubmit": [
     {
+      "name": "ManagedProvisioningTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
       "name": "FrameworksUiServicesTests",
       "options": [
         {
diff --git a/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
new file mode 100644
index 0000000..e0c12dd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+
+/**
+ * Tries to measure important BigInteger operations across a variety of BigInteger sizes. Note that
+ * BigInteger implementations commonly need to use wildly different algorithms for different sizes,
+ * so relative performance may change substantially depending on the size of the integer. This is
+ * not structured as a proper benchmark; just run main(), e.g. with vogar
+ * libcore/benchmarks/src/benchmarks/BigIntegerBenchmark.java.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BigIntegerPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    // A simple sum of products computation, mostly so we can check timing in the
+    // absence of any division. Computes the sum from 1 to n of ((10^prec) << 30) + 1)^2,
+    // repeating the multiplication, but not addition of 1, each time through the loop.
+    // Check the last few bits of the result as we go. Assumes n < 2^30.
+    // Note that we're actually squaring values in computing the product.
+    // That affects the algorithm used by some implementations.
+    private static void inner(int n, int prec) {
+        BigInteger big = BigInteger.TEN.pow(prec).shiftLeft(30).add(BigInteger.ONE);
+        BigInteger sum = BigInteger.ZERO;
+        for (int i = 0; i < n; ++i) {
+            sum = sum.add(big.multiply(big));
+        }
+        if (sum.and(BigInteger.valueOf(0x3fffffff)).intValue() != n) {
+            throw new AssertionError(
+                    "inner() got " + sum.and(BigInteger.valueOf(0x3fffffff)) + " instead of " + n);
+        }
+    }
+
+    // Execute the above rep times, optionally timing it.
+    @Test
+    public void repeatInner() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 10; i <= 10_000; i *= 10) {
+                inner(100, i);
+            }
+        }
+    }
+
+    // Approximate the sum of the first 1000 terms of the harmonic series (sum of 1/m as m
+    // goes from 1 to n) to about prec digits. The result has an implicit decimal point
+    // prec digits from the right.
+    private static BigInteger harmonic1000(int prec) {
+        BigInteger scaledOne = BigInteger.TEN.pow(prec);
+        BigInteger sum = BigInteger.ZERO;
+        for (int i = 1; i <= 1000; ++i) {
+            sum = sum.add(scaledOne.divide(BigInteger.valueOf(i)));
+        }
+        return sum;
+    }
+
+    // Execute the above rep times, optionally timing it.
+    // Check results for equality, and print one, to compaare against reference.
+    @Test
+    public void repeatHarmonic1000() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 5; i <= 5_000; i *= 10) {
+                BigInteger refRes = harmonic1000(i);
+                BigInteger newRes = harmonic1000(i);
+                if (!newRes.equals(refRes)) {
+                    throw new AssertionError(newRes + " != " + refRes);
+                }
+                if (i >= 50
+                        && !refRes.toString()
+                                .startsWith("748547086055034491265651820433390017652167916970")) {
+                    throw new AssertionError("harmanic(" + i + ") incorrectly produced " + refRes);
+                }
+            }
+        }
+    }
+
+    // Repeatedly execute just the base conversion from the last test, allowing
+    // us to time and check it for consistency as well.
+    @Test
+    public void repeatToString() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 5; i <= 5_000; i *= 10) {
+                BigInteger refRes = harmonic1000(i);
+                String refString = refRes.toString();
+                // Disguise refRes to avoid compiler optimization issues.
+                BigInteger newRes = refRes.shiftLeft(30).add(BigInteger.valueOf(i)).shiftRight(30);
+                // The time-consuming part:
+                String newString = newRes.toString();
+            }
+        }
+    }
+
+    // Compute base^exp, where base and result are scaled/multiplied by scaleBy to make them
+    // integers. exp >= 0 .
+    private static BigInteger myPow(BigInteger base, int exp, BigInteger scaleBy) {
+        if (exp == 0) {
+            return scaleBy; // Return one.
+        } else if ((exp & 1) != 0) {
+            BigInteger tmp = myPow(base, exp - 1, scaleBy);
+            return tmp.multiply(base).divide(scaleBy);
+        } else {
+            BigInteger tmp = myPow(base, exp / 2, scaleBy);
+            return tmp.multiply(tmp).divide(scaleBy);
+        }
+    }
+
+    // Approximate e by computing (1 + 1/n)^n to prec decimal digits.
+    // This isn't necessarily a very good approximation to e.
+    // Return the result, scaled by 10^prec.
+    private static BigInteger eApprox(int n, int prec) {
+        BigInteger scaledOne = BigInteger.TEN.pow(prec);
+        BigInteger base = scaledOne.add(scaledOne.divide(BigInteger.valueOf(n)));
+        return myPow(base, n, scaledOne);
+    }
+
+    // Repeatedly execute and check the above, printing one of the results
+    // to compare to reference.
+    @Test
+    public void repeatEApprox() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 10; i <= 10_000; i *= 10) {
+                BigInteger refRes = eApprox(100_000, i);
+                BigInteger newRes = eApprox(100_000, i);
+                if (!newRes.equals(refRes)) {
+                    throw new AssertionError(newRes + " != " + refRes);
+                }
+                if (i >= 10 && !refRes.toString().startsWith("271826")) {
+                    throw new AssertionError(
+                            "eApprox(" + 100_000 + "," + i + ") incorrectly produced " + refRes);
+                }
+            }
+        }
+    }
+
+    // Test / time modPow()
+    @Test
+    public void repeatModPow() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 5; i <= 500; i *= 10) {
+                BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
+                BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17));
+                BigInteger product = odd1.multiply(odd2);
+                BigInteger exponent = BigInteger.TEN.pow(i / 2 - 1);
+                BigInteger base = BigInteger.TEN.pow(i / 4);
+                BigInteger newRes = base.modPow(exponent, product);
+                if (!newRes.mod(odd1).equals(base.modPow(exponent, odd1))) {
+                    throw new AssertionError(
+                            "ModPow() result incorrect mod odd1:"
+                                    + odd1
+                                    + "; lastRes.mod(odd1)="
+                                    + newRes.mod(odd1)
+                                    + " vs. "
+                                    + "base.modPow(exponent, odd1)="
+                                    + base.modPow(exponent, odd1)
+                                    + " base="
+                                    + base
+                                    + " exponent="
+                                    + exponent);
+                }
+                if (!newRes.mod(odd2).equals(base.modPow(exponent, odd2))) {
+                    throw new AssertionError("ModPow() result incorrect mod odd2");
+                }
+            }
+        }
+    }
+
+    // Test / time modInverse()
+    @Test
+    public void repeatModInverse() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 10; i <= 10_000; i *= 10) {
+                BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
+                BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17));
+                BigInteger product = odd1.multiply(odd2);
+                BigInteger arg = BigInteger.ONE.shiftLeft(i / 4);
+                BigInteger lastRes = null;
+                BigInteger newRes = arg.modInverse(product);
+                lastRes = newRes;
+                if (!lastRes.mod(odd1).equals(arg.modInverse(odd1))) {
+                    throw new AssertionError("ModInverse() result incorrect mod odd1");
+                }
+                if (!lastRes.mod(odd2).equals(arg.modInverse(odd2))) {
+                    throw new AssertionError("ModInverse() result incorrect mod odd2");
+                }
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
new file mode 100644
index 0000000..04ef09e4
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public final class BufferedZipFilePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    int[] mReadSize = new int[] {4, 32, 128};
+    int[] mCompressedSize = new int[] {128, 1024, 8192, 65536};
+    private File mFile;
+
+    @Before
+    public void setUp() throws Exception {
+        mFile = File.createTempFile("BufferedZipFilePerfTest", ".zip");
+        mFile.deleteOnExit();
+        Random random = new Random(0);
+        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(mFile));
+        for (int i = 0; i < mCompressedSize.length; i++) {
+            byte[] data = new byte[8192];
+            out.putNextEntry(new ZipEntry("entry.data" + mCompressedSize[i]));
+            int written = 0;
+            while (written < mCompressedSize[i]) {
+                random.nextBytes(data);
+                int toWrite = Math.min(mCompressedSize[i] - written, data.length);
+                out.write(data, 0, toWrite);
+                written += toWrite;
+            }
+        }
+        out.close();
+    }
+
+    @Test
+    public void timeUnbufferedRead() throws Exception {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 0; i < mCompressedSize.length; i++) {
+                for (int j = 0; j < mReadSize.length; j++) {
+                    ZipFile zipFile = new ZipFile(mFile);
+                    ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]);
+                    InputStream in = zipFile.getInputStream(entry);
+                    byte[] buffer = new byte[mReadSize[j]];
+                    while (in.read(buffer) != -1) {
+                        // Keep reading
+                    }
+                    in.close();
+                    zipFile.close();
+                }
+            }
+        }
+    }
+
+    @Test
+    public void timeBufferedRead() throws Exception {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 0; i < mCompressedSize.length; i++) {
+                for (int j = 0; j < mReadSize.length; j++) {
+                    ZipFile zipFile = new ZipFile(mFile);
+                    ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]);
+                    InputStream in = new BufferedInputStream(zipFile.getInputStream(entry));
+                    byte[] buffer = new byte[mReadSize[j]];
+                    while (in.read(buffer) != -1) {
+                        // Keep reading
+                    }
+                    in.close();
+                    zipFile.close();
+                }
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
new file mode 100644
index 0000000..4ae88b8
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ClassLoaderResourcePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private static final String EXISTENT_RESOURCE = "java/util/logging/logging.properties";
+    private static final String MISSING_RESOURCE = "missing_entry";
+
+    @Test
+    public void timeGetBootResource_hit() {
+        ClassLoader currentClassLoader = getClass().getClassLoader();
+        Assert.assertNotNull(currentClassLoader.getResource(EXISTENT_RESOURCE));
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            currentClassLoader.getResource(EXISTENT_RESOURCE);
+        }
+    }
+
+    @Test
+    public void timeGetBootResource_miss() {
+        ClassLoader currentClassLoader = getClass().getClassLoader();
+        Assert.assertNull(currentClassLoader.getResource(MISSING_RESOURCE));
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            currentClassLoader.getResource(MISSING_RESOURCE);
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
new file mode 100644
index 0000000..5e73916
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
@@ -0,0 +1,1197 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ClonePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    static class CloneableObject implements Cloneable {
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+    }
+
+    static class CloneableManyFieldObject implements Cloneable {
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+
+        Object mO1 = new Object();
+        Object mO2 = new Object();
+        Object mO3 = new Object();
+        Object mO4 = new Object();
+        Object mO5 = new Object();
+        Object mO6 = new Object();
+        Object mO7 = new Object();
+        Object mO8 = new Object();
+        Object mO9 = new Object();
+        Object mO10 = new Object();
+        Object mO11 = new Object();
+        Object mO12 = new Object();
+        Object mO13 = new Object();
+        Object mO14 = new Object();
+        Object mO15 = new Object();
+        Object mO16 = new Object();
+        Object mO17 = new Object();
+        Object mO18 = new Object();
+        Object mO19 = new Object();
+        Object mO20 = new Object();
+        Object mO21 = new Object();
+        Object mO22 = new Object();
+        Object mO23 = new Object();
+        Object mO24 = new Object();
+        Object mO25 = new Object();
+        Object mO26 = new Object();
+        Object mO27 = new Object();
+        Object mO28 = new Object();
+        Object mO29 = new Object();
+        Object mO30 = new Object();
+        Object mO31 = new Object();
+        Object mO32 = new Object();
+        Object mO33 = new Object();
+        Object mO34 = new Object();
+        Object mO35 = new Object();
+        Object mO36 = new Object();
+        Object mO37 = new Object();
+        Object mO38 = new Object();
+        Object mO39 = new Object();
+        Object mO40 = new Object();
+        Object mO41 = new Object();
+        Object mO42 = new Object();
+        Object mO43 = new Object();
+        Object mO44 = new Object();
+        Object mO45 = new Object();
+        Object mO46 = new Object();
+        Object mO47 = new Object();
+        Object mO48 = new Object();
+        Object mO49 = new Object();
+        Object mO50 = new Object();
+        Object mO51 = new Object();
+        Object mO52 = new Object();
+        Object mO53 = new Object();
+        Object mO54 = new Object();
+        Object mO55 = new Object();
+        Object mO56 = new Object();
+        Object mO57 = new Object();
+        Object mO58 = new Object();
+        Object mO59 = new Object();
+        Object mO60 = new Object();
+        Object mO61 = new Object();
+        Object mO62 = new Object();
+        Object mO63 = new Object();
+        Object mO64 = new Object();
+        Object mO65 = new Object();
+        Object mO66 = new Object();
+        Object mO67 = new Object();
+        Object mO68 = new Object();
+        Object mO69 = new Object();
+        Object mO70 = new Object();
+        Object mO71 = new Object();
+        Object mO72 = new Object();
+        Object mO73 = new Object();
+        Object mO74 = new Object();
+        Object mO75 = new Object();
+        Object mO76 = new Object();
+        Object mO77 = new Object();
+        Object mO78 = new Object();
+        Object mO79 = new Object();
+        Object mO80 = new Object();
+        Object mO81 = new Object();
+        Object mO82 = new Object();
+        Object mO83 = new Object();
+        Object mO84 = new Object();
+        Object mO85 = new Object();
+        Object mO86 = new Object();
+        Object mO87 = new Object();
+        Object mO88 = new Object();
+        Object mO89 = new Object();
+        Object mO90 = new Object();
+        Object mO91 = new Object();
+        Object mO92 = new Object();
+        Object mO93 = new Object();
+        Object mO94 = new Object();
+        Object mO95 = new Object();
+        Object mO96 = new Object();
+        Object mO97 = new Object();
+        Object mO98 = new Object();
+        Object mO99 = new Object();
+        Object mO100 = new Object();
+        Object mO101 = new Object();
+        Object mO102 = new Object();
+        Object mO103 = new Object();
+        Object mO104 = new Object();
+        Object mO105 = new Object();
+        Object mO106 = new Object();
+        Object mO107 = new Object();
+        Object mO108 = new Object();
+        Object mO109 = new Object();
+        Object mO110 = new Object();
+        Object mO111 = new Object();
+        Object mO112 = new Object();
+        Object mO113 = new Object();
+        Object mO114 = new Object();
+        Object mO115 = new Object();
+        Object mO116 = new Object();
+        Object mO117 = new Object();
+        Object mO118 = new Object();
+        Object mO119 = new Object();
+        Object mO120 = new Object();
+        Object mO121 = new Object();
+        Object mO122 = new Object();
+        Object mO123 = new Object();
+        Object mO124 = new Object();
+        Object mO125 = new Object();
+        Object mO126 = new Object();
+        Object mO127 = new Object();
+        Object mO128 = new Object();
+        Object mO129 = new Object();
+        Object mO130 = new Object();
+        Object mO131 = new Object();
+        Object mO132 = new Object();
+        Object mO133 = new Object();
+        Object mO134 = new Object();
+        Object mO135 = new Object();
+        Object mO136 = new Object();
+        Object mO137 = new Object();
+        Object mO138 = new Object();
+        Object mO139 = new Object();
+        Object mO140 = new Object();
+        Object mO141 = new Object();
+        Object mO142 = new Object();
+        Object mO143 = new Object();
+        Object mO144 = new Object();
+        Object mO145 = new Object();
+        Object mO146 = new Object();
+        Object mO147 = new Object();
+        Object mO148 = new Object();
+        Object mO149 = new Object();
+        Object mO150 = new Object();
+        Object mO151 = new Object();
+        Object mO152 = new Object();
+        Object mO153 = new Object();
+        Object mO154 = new Object();
+        Object mO155 = new Object();
+        Object mO156 = new Object();
+        Object mO157 = new Object();
+        Object mO158 = new Object();
+        Object mO159 = new Object();
+        Object mO160 = new Object();
+        Object mO161 = new Object();
+        Object mO162 = new Object();
+        Object mO163 = new Object();
+        Object mO164 = new Object();
+        Object mO165 = new Object();
+        Object mO166 = new Object();
+        Object mO167 = new Object();
+        Object mO168 = new Object();
+        Object mO169 = new Object();
+        Object mO170 = new Object();
+        Object mO171 = new Object();
+        Object mO172 = new Object();
+        Object mO173 = new Object();
+        Object mO174 = new Object();
+        Object mO175 = new Object();
+        Object mO176 = new Object();
+        Object mO177 = new Object();
+        Object mO178 = new Object();
+        Object mO179 = new Object();
+        Object mO180 = new Object();
+        Object mO181 = new Object();
+        Object mO182 = new Object();
+        Object mO183 = new Object();
+        Object mO184 = new Object();
+        Object mO185 = new Object();
+        Object mO186 = new Object();
+        Object mO187 = new Object();
+        Object mO188 = new Object();
+        Object mO189 = new Object();
+        Object mO190 = new Object();
+        Object mO191 = new Object();
+        Object mO192 = new Object();
+        Object mO193 = new Object();
+        Object mO194 = new Object();
+        Object mO195 = new Object();
+        Object mO196 = new Object();
+        Object mO197 = new Object();
+        Object mO198 = new Object();
+        Object mO199 = new Object();
+        Object mO200 = new Object();
+        Object mO201 = new Object();
+        Object mO202 = new Object();
+        Object mO203 = new Object();
+        Object mO204 = new Object();
+        Object mO205 = new Object();
+        Object mO206 = new Object();
+        Object mO207 = new Object();
+        Object mO208 = new Object();
+        Object mO209 = new Object();
+        Object mO210 = new Object();
+        Object mO211 = new Object();
+        Object mO212 = new Object();
+        Object mO213 = new Object();
+        Object mO214 = new Object();
+        Object mO215 = new Object();
+        Object mO216 = new Object();
+        Object mO217 = new Object();
+        Object mO218 = new Object();
+        Object mO219 = new Object();
+        Object mO220 = new Object();
+        Object mO221 = new Object();
+        Object mO222 = new Object();
+        Object mO223 = new Object();
+        Object mO224 = new Object();
+        Object mO225 = new Object();
+        Object mO226 = new Object();
+        Object mO227 = new Object();
+        Object mO228 = new Object();
+        Object mO229 = new Object();
+        Object mO230 = new Object();
+        Object mO231 = new Object();
+        Object mO232 = new Object();
+        Object mO233 = new Object();
+        Object mO234 = new Object();
+        Object mO235 = new Object();
+        Object mO236 = new Object();
+        Object mO237 = new Object();
+        Object mO238 = new Object();
+        Object mO239 = new Object();
+        Object mO240 = new Object();
+        Object mO241 = new Object();
+        Object mO242 = new Object();
+        Object mO243 = new Object();
+        Object mO244 = new Object();
+        Object mO245 = new Object();
+        Object mO246 = new Object();
+        Object mO247 = new Object();
+        Object mO248 = new Object();
+        Object mO249 = new Object();
+        Object mO250 = new Object();
+        Object mO251 = new Object();
+        Object mO252 = new Object();
+        Object mO253 = new Object();
+        Object mO254 = new Object();
+        Object mO255 = new Object();
+        Object mO256 = new Object();
+        Object mO257 = new Object();
+        Object mO258 = new Object();
+        Object mO259 = new Object();
+        Object mO260 = new Object();
+        Object mO261 = new Object();
+        Object mO262 = new Object();
+        Object mO263 = new Object();
+        Object mO264 = new Object();
+        Object mO265 = new Object();
+        Object mO266 = new Object();
+        Object mO267 = new Object();
+        Object mO268 = new Object();
+        Object mO269 = new Object();
+        Object mO270 = new Object();
+        Object mO271 = new Object();
+        Object mO272 = new Object();
+        Object mO273 = new Object();
+        Object mO274 = new Object();
+        Object mO275 = new Object();
+        Object mO276 = new Object();
+        Object mO277 = new Object();
+        Object mO278 = new Object();
+        Object mO279 = new Object();
+        Object mO280 = new Object();
+        Object mO281 = new Object();
+        Object mO282 = new Object();
+        Object mO283 = new Object();
+        Object mO284 = new Object();
+        Object mO285 = new Object();
+        Object mO286 = new Object();
+        Object mO287 = new Object();
+        Object mO288 = new Object();
+        Object mO289 = new Object();
+        Object mO290 = new Object();
+        Object mO291 = new Object();
+        Object mO292 = new Object();
+        Object mO293 = new Object();
+        Object mO294 = new Object();
+        Object mO295 = new Object();
+        Object mO296 = new Object();
+        Object mO297 = new Object();
+        Object mO298 = new Object();
+        Object mO299 = new Object();
+        Object mO300 = new Object();
+        Object mO301 = new Object();
+        Object mO302 = new Object();
+        Object mO303 = new Object();
+        Object mO304 = new Object();
+        Object mO305 = new Object();
+        Object mO306 = new Object();
+        Object mO307 = new Object();
+        Object mO308 = new Object();
+        Object mO309 = new Object();
+        Object mO310 = new Object();
+        Object mO311 = new Object();
+        Object mO312 = new Object();
+        Object mO313 = new Object();
+        Object mO314 = new Object();
+        Object mO315 = new Object();
+        Object mO316 = new Object();
+        Object mO317 = new Object();
+        Object mO318 = new Object();
+        Object mO319 = new Object();
+        Object mO320 = new Object();
+        Object mO321 = new Object();
+        Object mO322 = new Object();
+        Object mO323 = new Object();
+        Object mO324 = new Object();
+        Object mO325 = new Object();
+        Object mO326 = new Object();
+        Object mO327 = new Object();
+        Object mO328 = new Object();
+        Object mO329 = new Object();
+        Object mO330 = new Object();
+        Object mO331 = new Object();
+        Object mO332 = new Object();
+        Object mO333 = new Object();
+        Object mO334 = new Object();
+        Object mO335 = new Object();
+        Object mO336 = new Object();
+        Object mO337 = new Object();
+        Object mO338 = new Object();
+        Object mO339 = new Object();
+        Object mO340 = new Object();
+        Object mO341 = new Object();
+        Object mO342 = new Object();
+        Object mO343 = new Object();
+        Object mO344 = new Object();
+        Object mO345 = new Object();
+        Object mO346 = new Object();
+        Object mO347 = new Object();
+        Object mO348 = new Object();
+        Object mO349 = new Object();
+        Object mO350 = new Object();
+        Object mO351 = new Object();
+        Object mO352 = new Object();
+        Object mO353 = new Object();
+        Object mO354 = new Object();
+        Object mO355 = new Object();
+        Object mO356 = new Object();
+        Object mO357 = new Object();
+        Object mO358 = new Object();
+        Object mO359 = new Object();
+        Object mO360 = new Object();
+        Object mO361 = new Object();
+        Object mO362 = new Object();
+        Object mO363 = new Object();
+        Object mO364 = new Object();
+        Object mO365 = new Object();
+        Object mO366 = new Object();
+        Object mO367 = new Object();
+        Object mO368 = new Object();
+        Object mO369 = new Object();
+        Object mO370 = new Object();
+        Object mO371 = new Object();
+        Object mO372 = new Object();
+        Object mO373 = new Object();
+        Object mO374 = new Object();
+        Object mO375 = new Object();
+        Object mO376 = new Object();
+        Object mO377 = new Object();
+        Object mO378 = new Object();
+        Object mO379 = new Object();
+        Object mO380 = new Object();
+        Object mO381 = new Object();
+        Object mO382 = new Object();
+        Object mO383 = new Object();
+        Object mO384 = new Object();
+        Object mO385 = new Object();
+        Object mO386 = new Object();
+        Object mO387 = new Object();
+        Object mO388 = new Object();
+        Object mO389 = new Object();
+        Object mO390 = new Object();
+        Object mO391 = new Object();
+        Object mO392 = new Object();
+        Object mO393 = new Object();
+        Object mO394 = new Object();
+        Object mO395 = new Object();
+        Object mO396 = new Object();
+        Object mO397 = new Object();
+        Object mO398 = new Object();
+        Object mO399 = new Object();
+        Object mO400 = new Object();
+        Object mO401 = new Object();
+        Object mO402 = new Object();
+        Object mO403 = new Object();
+        Object mO404 = new Object();
+        Object mO405 = new Object();
+        Object mO406 = new Object();
+        Object mO407 = new Object();
+        Object mO408 = new Object();
+        Object mO409 = new Object();
+        Object mO410 = new Object();
+        Object mO411 = new Object();
+        Object mO412 = new Object();
+        Object mO413 = new Object();
+        Object mO414 = new Object();
+        Object mO415 = new Object();
+        Object mO416 = new Object();
+        Object mO417 = new Object();
+        Object mO418 = new Object();
+        Object mO419 = new Object();
+        Object mO420 = new Object();
+        Object mO421 = new Object();
+        Object mO422 = new Object();
+        Object mO423 = new Object();
+        Object mO424 = new Object();
+        Object mO425 = new Object();
+        Object mO426 = new Object();
+        Object mO427 = new Object();
+        Object mO428 = new Object();
+        Object mO429 = new Object();
+        Object mO430 = new Object();
+        Object mO431 = new Object();
+        Object mO432 = new Object();
+        Object mO433 = new Object();
+        Object mO434 = new Object();
+        Object mO435 = new Object();
+        Object mO436 = new Object();
+        Object mO437 = new Object();
+        Object mO438 = new Object();
+        Object mO439 = new Object();
+        Object mO440 = new Object();
+        Object mO441 = new Object();
+        Object mO442 = new Object();
+        Object mO460 = new Object();
+        Object mO461 = new Object();
+        Object mO462 = new Object();
+        Object mO463 = new Object();
+        Object mO464 = new Object();
+        Object mO465 = new Object();
+        Object mO466 = new Object();
+        Object mO467 = new Object();
+        Object mO468 = new Object();
+        Object mO469 = new Object();
+        Object mO470 = new Object();
+        Object mO471 = new Object();
+        Object mO472 = new Object();
+        Object mO473 = new Object();
+        Object mO474 = new Object();
+        Object mO475 = new Object();
+        Object mO476 = new Object();
+        Object mO477 = new Object();
+        Object mO478 = new Object();
+        Object mO479 = new Object();
+        Object mO480 = new Object();
+        Object mO481 = new Object();
+        Object mO482 = new Object();
+        Object mO483 = new Object();
+        Object mO484 = new Object();
+        Object mO485 = new Object();
+        Object mO486 = new Object();
+        Object mO487 = new Object();
+        Object mO488 = new Object();
+        Object mO489 = new Object();
+        Object mO490 = new Object();
+        Object mO491 = new Object();
+        Object mO492 = new Object();
+        Object mO493 = new Object();
+        Object mO494 = new Object();
+        Object mO495 = new Object();
+        Object mO496 = new Object();
+        Object mO497 = new Object();
+        Object mO498 = new Object();
+        Object mO499 = new Object();
+        Object mO500 = new Object();
+        Object mO501 = new Object();
+        Object mO502 = new Object();
+        Object mO503 = new Object();
+        Object mO504 = new Object();
+        Object mO505 = new Object();
+        Object mO506 = new Object();
+        Object mO507 = new Object();
+        Object mO508 = new Object();
+        Object mO509 = new Object();
+        Object mO510 = new Object();
+        Object mO511 = new Object();
+        Object mO512 = new Object();
+        Object mO513 = new Object();
+        Object mO514 = new Object();
+        Object mO515 = new Object();
+        Object mO516 = new Object();
+        Object mO517 = new Object();
+        Object mO518 = new Object();
+        Object mO519 = new Object();
+        Object mO520 = new Object();
+        Object mO521 = new Object();
+        Object mO522 = new Object();
+        Object mO523 = new Object();
+        Object mO556 = new Object();
+        Object mO557 = new Object();
+        Object mO558 = new Object();
+        Object mO559 = new Object();
+        Object mO560 = new Object();
+        Object mO561 = new Object();
+        Object mO562 = new Object();
+        Object mO563 = new Object();
+        Object mO564 = new Object();
+        Object mO565 = new Object();
+        Object mO566 = new Object();
+        Object mO567 = new Object();
+        Object mO568 = new Object();
+        Object mO569 = new Object();
+        Object mO570 = new Object();
+        Object mO571 = new Object();
+        Object mO572 = new Object();
+        Object mO573 = new Object();
+        Object mO574 = new Object();
+        Object mO575 = new Object();
+        Object mO576 = new Object();
+        Object mO577 = new Object();
+        Object mO578 = new Object();
+        Object mO579 = new Object();
+        Object mO580 = new Object();
+        Object mO581 = new Object();
+        Object mO582 = new Object();
+        Object mO583 = new Object();
+        Object mO584 = new Object();
+        Object mO585 = new Object();
+        Object mO586 = new Object();
+        Object mO587 = new Object();
+        Object mO588 = new Object();
+        Object mO589 = new Object();
+        Object mO590 = new Object();
+        Object mO591 = new Object();
+        Object mO592 = new Object();
+        Object mO593 = new Object();
+        Object mO594 = new Object();
+        Object mO595 = new Object();
+        Object mO596 = new Object();
+        Object mO597 = new Object();
+        Object mO598 = new Object();
+        Object mO599 = new Object();
+        Object mO600 = new Object();
+        Object mO601 = new Object();
+        Object mO602 = new Object();
+        Object mO603 = new Object();
+        Object mO604 = new Object();
+        Object mO605 = new Object();
+        Object mO606 = new Object();
+        Object mO607 = new Object();
+        Object mO608 = new Object();
+        Object mO609 = new Object();
+        Object mO610 = new Object();
+        Object mO611 = new Object();
+        Object mO612 = new Object();
+        Object mO613 = new Object();
+        Object mO614 = new Object();
+        Object mO615 = new Object();
+        Object mO616 = new Object();
+        Object mO617 = new Object();
+        Object mO618 = new Object();
+        Object mO619 = new Object();
+        Object mO620 = new Object();
+        Object mO621 = new Object();
+        Object mO622 = new Object();
+        Object mO623 = new Object();
+        Object mO624 = new Object();
+        Object mO625 = new Object();
+        Object mO626 = new Object();
+        Object mO627 = new Object();
+        Object mO628 = new Object();
+        Object mO629 = new Object();
+        Object mO630 = new Object();
+        Object mO631 = new Object();
+        Object mO632 = new Object();
+        Object mO633 = new Object();
+        Object mO634 = new Object();
+        Object mO635 = new Object();
+        Object mO636 = new Object();
+        Object mO637 = new Object();
+        Object mO638 = new Object();
+        Object mO639 = new Object();
+        Object mO640 = new Object();
+        Object mO641 = new Object();
+        Object mO642 = new Object();
+        Object mO643 = new Object();
+        Object mO644 = new Object();
+        Object mO645 = new Object();
+        Object mO646 = new Object();
+        Object mO647 = new Object();
+        Object mO648 = new Object();
+        Object mO649 = new Object();
+        Object mO650 = new Object();
+        Object mO651 = new Object();
+        Object mO652 = new Object();
+        Object mO653 = new Object();
+        Object mO654 = new Object();
+        Object mO655 = new Object();
+        Object mO656 = new Object();
+        Object mO657 = new Object();
+        Object mO658 = new Object();
+        Object mO659 = new Object();
+        Object mO660 = new Object();
+        Object mO661 = new Object();
+        Object mO662 = new Object();
+        Object mO663 = new Object();
+        Object mO664 = new Object();
+        Object mO665 = new Object();
+        Object mO666 = new Object();
+        Object mO667 = new Object();
+        Object mO668 = new Object();
+        Object mO669 = new Object();
+        Object mO670 = new Object();
+        Object mO671 = new Object();
+        Object mO672 = new Object();
+        Object mO673 = new Object();
+        Object mO674 = new Object();
+        Object mO675 = new Object();
+        Object mO676 = new Object();
+        Object mO677 = new Object();
+        Object mO678 = new Object();
+        Object mO679 = new Object();
+        Object mO680 = new Object();
+        Object mO681 = new Object();
+        Object mO682 = new Object();
+        Object mO683 = new Object();
+        Object mO684 = new Object();
+        Object mO685 = new Object();
+        Object mO686 = new Object();
+        Object mO687 = new Object();
+        Object mO688 = new Object();
+        Object mO734 = new Object();
+        Object mO735 = new Object();
+        Object mO736 = new Object();
+        Object mO737 = new Object();
+        Object mO738 = new Object();
+        Object mO739 = new Object();
+        Object mO740 = new Object();
+        Object mO741 = new Object();
+        Object mO742 = new Object();
+        Object mO743 = new Object();
+        Object mO744 = new Object();
+        Object mO745 = new Object();
+        Object mO746 = new Object();
+        Object mO747 = new Object();
+        Object mO748 = new Object();
+        Object mO749 = new Object();
+        Object mO750 = new Object();
+        Object mO751 = new Object();
+        Object mO752 = new Object();
+        Object mO753 = new Object();
+        Object mO754 = new Object();
+        Object mO755 = new Object();
+        Object mO756 = new Object();
+        Object mO757 = new Object();
+        Object mO758 = new Object();
+        Object mO759 = new Object();
+        Object mO760 = new Object();
+        Object mO761 = new Object();
+        Object mO762 = new Object();
+        Object mO763 = new Object();
+        Object mO764 = new Object();
+        Object mO765 = new Object();
+        Object mO766 = new Object();
+        Object mO767 = new Object();
+        Object mO768 = new Object();
+        Object mO769 = new Object();
+        Object mO770 = new Object();
+        Object mO771 = new Object();
+        Object mO772 = new Object();
+        Object mO773 = new Object();
+        Object mO774 = new Object();
+        Object mO775 = new Object();
+        Object mO776 = new Object();
+        Object mO777 = new Object();
+        Object mO778 = new Object();
+        Object mO779 = new Object();
+        Object mO780 = new Object();
+        Object mO781 = new Object();
+        Object mO782 = new Object();
+        Object mO783 = new Object();
+        Object mO784 = new Object();
+        Object mO785 = new Object();
+        Object mO786 = new Object();
+        Object mO787 = new Object();
+        Object mO788 = new Object();
+        Object mO789 = new Object();
+        Object mO790 = new Object();
+        Object mO791 = new Object();
+        Object mO792 = new Object();
+        Object mO793 = new Object();
+        Object mO794 = new Object();
+        Object mO795 = new Object();
+        Object mO796 = new Object();
+        Object mO797 = new Object();
+        Object mO798 = new Object();
+        Object mO799 = new Object();
+        Object mO800 = new Object();
+        Object mO801 = new Object();
+        Object mO802 = new Object();
+        Object mO803 = new Object();
+        Object mO804 = new Object();
+        Object mO805 = new Object();
+        Object mO806 = new Object();
+        Object mO807 = new Object();
+        Object mO808 = new Object();
+        Object mO809 = new Object();
+        Object mO810 = new Object();
+        Object mO811 = new Object();
+        Object mO812 = new Object();
+        Object mO813 = new Object();
+        Object mO848 = new Object();
+        Object mO849 = new Object();
+        Object mO850 = new Object();
+        Object mO851 = new Object();
+        Object mO852 = new Object();
+        Object mO853 = new Object();
+        Object mO854 = new Object();
+        Object mO855 = new Object();
+        Object mO856 = new Object();
+        Object mO857 = new Object();
+        Object mO858 = new Object();
+        Object mO859 = new Object();
+        Object mO860 = new Object();
+        Object mO861 = new Object();
+        Object mO862 = new Object();
+        Object mO863 = new Object();
+        Object mO864 = new Object();
+        Object mO865 = new Object();
+        Object mO866 = new Object();
+        Object mO867 = new Object();
+        Object mO868 = new Object();
+        Object mO869 = new Object();
+        Object mO870 = new Object();
+        Object mO871 = new Object();
+        Object mO872 = new Object();
+        Object mO873 = new Object();
+        Object mO874 = new Object();
+        Object mO875 = new Object();
+        Object mO876 = new Object();
+        Object mO877 = new Object();
+        Object mO878 = new Object();
+        Object mO879 = new Object();
+        Object mO880 = new Object();
+        Object mO881 = new Object();
+        Object mO882 = new Object();
+        Object mO883 = new Object();
+        Object mO884 = new Object();
+        Object mO885 = new Object();
+        Object mO886 = new Object();
+        Object mO887 = new Object();
+        Object mO888 = new Object();
+        Object mO889 = new Object();
+        Object mO890 = new Object();
+        Object mO891 = new Object();
+        Object mO892 = new Object();
+        Object mO893 = new Object();
+        Object mO894 = new Object();
+        Object mO895 = new Object();
+        Object mO896 = new Object();
+        Object mO897 = new Object();
+        Object mO898 = new Object();
+        Object mO899 = new Object();
+        Object mO900 = new Object();
+        Object mO901 = new Object();
+        Object mO902 = new Object();
+        Object mO903 = new Object();
+        Object mO904 = new Object();
+        Object mO905 = new Object();
+        Object mO906 = new Object();
+        Object mO907 = new Object();
+        Object mO908 = new Object();
+        Object mO909 = new Object();
+        Object mO910 = new Object();
+        Object mO911 = new Object();
+        Object mO912 = new Object();
+        Object mO913 = new Object();
+        Object mO914 = new Object();
+        Object mO915 = new Object();
+        Object mO916 = new Object();
+        Object mO917 = new Object();
+        Object mO918 = new Object();
+        Object mO919 = new Object();
+        Object mO920 = new Object();
+        Object mO921 = new Object();
+        Object mO922 = new Object();
+        Object mO923 = new Object();
+        Object mO924 = new Object();
+        Object mO925 = new Object();
+        Object mO926 = new Object();
+        Object mO927 = new Object();
+        Object mO928 = new Object();
+        Object mO929 = new Object();
+        Object mO930 = new Object();
+        Object mO931 = new Object();
+        Object mO932 = new Object();
+        Object mO933 = new Object();
+        Object mO934 = new Object();
+        Object mO935 = new Object();
+        Object mO936 = new Object();
+        Object mO937 = new Object();
+        Object mO938 = new Object();
+        Object mO939 = new Object();
+        Object mO940 = new Object();
+        Object mO941 = new Object();
+        Object mO942 = new Object();
+        Object mO943 = new Object();
+        Object mO944 = new Object();
+        Object mO945 = new Object();
+        Object mO946 = new Object();
+        Object mO947 = new Object();
+        Object mO948 = new Object();
+        Object mO949 = new Object();
+        Object mO950 = new Object();
+        Object mO951 = new Object();
+        Object mO952 = new Object();
+        Object mO953 = new Object();
+        Object mO954 = new Object();
+        Object mO955 = new Object();
+        Object mO956 = new Object();
+        Object mO957 = new Object();
+        Object mO958 = new Object();
+        Object mO959 = new Object();
+        Object mO960 = new Object();
+        Object mO961 = new Object();
+        Object mO962 = new Object();
+        Object mO963 = new Object();
+        Object mO964 = new Object();
+        Object mO965 = new Object();
+        Object mO966 = new Object();
+        Object mO967 = new Object();
+        Object mO968 = new Object();
+        Object mO969 = new Object();
+        Object mO970 = new Object();
+        Object mO971 = new Object();
+        Object mO972 = new Object();
+        Object mO973 = new Object();
+        Object mO974 = new Object();
+        Object mO975 = new Object();
+        Object mO976 = new Object();
+        Object mO977 = new Object();
+        Object mO978 = new Object();
+        Object mO979 = new Object();
+        Object mO980 = new Object();
+        Object mO981 = new Object();
+        Object mO982 = new Object();
+        Object mO983 = new Object();
+        Object mO984 = new Object();
+        Object mO985 = new Object();
+        Object mO986 = new Object();
+        Object mO987 = new Object();
+        Object mO988 = new Object();
+        Object mO989 = new Object();
+        Object mO990 = new Object();
+        Object mO991 = new Object();
+        Object mO992 = new Object();
+        Object mO993 = new Object();
+        Object mO994 = new Object();
+        Object mO995 = new Object();
+        Object mO996 = new Object();
+        Object mO997 = new Object();
+        Object mO998 = new Object();
+        Object mO999 = new Object();
+    }
+
+    static class Deep0 {}
+
+    static class Deep1 extends Deep0 {}
+
+    static class Deep2 extends Deep1 {}
+
+    static class Deep3 extends Deep2 {}
+
+    static class Deep4 extends Deep3 {}
+
+    static class Deep5 extends Deep4 {}
+
+    static class Deep6 extends Deep5 {}
+
+    static class Deep7 extends Deep6 {}
+
+    static class Deep8 extends Deep7 {}
+
+    static class Deep9 extends Deep8 {}
+
+    static class Deep10 extends Deep9 {}
+
+    static class Deep11 extends Deep10 {}
+
+    static class Deep12 extends Deep11 {}
+
+    static class Deep13 extends Deep12 {}
+
+    static class Deep14 extends Deep13 {}
+
+    static class Deep15 extends Deep14 {}
+
+    static class Deep16 extends Deep15 {}
+
+    static class Deep17 extends Deep16 {}
+
+    static class Deep18 extends Deep17 {}
+
+    static class Deep19 extends Deep18 {}
+
+    static class Deep20 extends Deep19 {}
+
+    static class Deep21 extends Deep20 {}
+
+    static class Deep22 extends Deep21 {}
+
+    static class Deep23 extends Deep22 {}
+
+    static class Deep24 extends Deep23 {}
+
+    static class Deep25 extends Deep24 {}
+
+    static class Deep26 extends Deep25 {}
+
+    static class Deep27 extends Deep26 {}
+
+    static class Deep28 extends Deep27 {}
+
+    static class Deep29 extends Deep28 {}
+
+    static class Deep30 extends Deep29 {}
+
+    static class Deep31 extends Deep30 {}
+
+    static class Deep32 extends Deep31 {}
+
+    static class Deep33 extends Deep32 {}
+
+    static class Deep34 extends Deep33 {}
+
+    static class Deep35 extends Deep34 {}
+
+    static class Deep36 extends Deep35 {}
+
+    static class Deep37 extends Deep36 {}
+
+    static class Deep38 extends Deep37 {}
+
+    static class Deep39 extends Deep38 {}
+
+    static class Deep40 extends Deep39 {}
+
+    static class Deep41 extends Deep40 {}
+
+    static class Deep42 extends Deep41 {}
+
+    static class Deep43 extends Deep42 {}
+
+    static class Deep44 extends Deep43 {}
+
+    static class Deep45 extends Deep44 {}
+
+    static class Deep46 extends Deep45 {}
+
+    static class Deep47 extends Deep46 {}
+
+    static class Deep48 extends Deep47 {}
+
+    static class Deep49 extends Deep48 {}
+
+    static class Deep50 extends Deep49 {}
+
+    static class Deep51 extends Deep50 {}
+
+    static class Deep52 extends Deep51 {}
+
+    static class Deep53 extends Deep52 {}
+
+    static class Deep54 extends Deep53 {}
+
+    static class Deep55 extends Deep54 {}
+
+    static class Deep56 extends Deep55 {}
+
+    static class Deep57 extends Deep56 {}
+
+    static class Deep58 extends Deep57 {}
+
+    static class Deep59 extends Deep58 {}
+
+    static class Deep60 extends Deep59 {}
+
+    static class Deep61 extends Deep60 {}
+
+    static class Deep62 extends Deep61 {}
+
+    static class Deep63 extends Deep62 {}
+
+    static class Deep64 extends Deep63 {}
+
+    static class Deep65 extends Deep64 {}
+
+    static class Deep66 extends Deep65 {}
+
+    static class Deep67 extends Deep66 {}
+
+    static class Deep68 extends Deep67 {}
+
+    static class Deep69 extends Deep68 {}
+
+    static class Deep70 extends Deep69 {}
+
+    static class Deep71 extends Deep70 {}
+
+    static class Deep72 extends Deep71 {}
+
+    static class Deep73 extends Deep72 {}
+
+    static class Deep74 extends Deep73 {}
+
+    static class Deep75 extends Deep74 {}
+
+    static class Deep76 extends Deep75 {}
+
+    static class Deep77 extends Deep76 {}
+
+    static class Deep78 extends Deep77 {}
+
+    static class Deep79 extends Deep78 {}
+
+    static class Deep80 extends Deep79 {}
+
+    static class Deep81 extends Deep80 {}
+
+    static class Deep82 extends Deep81 {}
+
+    static class Deep83 extends Deep82 {}
+
+    static class Deep84 extends Deep83 {}
+
+    static class Deep85 extends Deep84 {}
+
+    static class Deep86 extends Deep85 {}
+
+    static class Deep87 extends Deep86 {}
+
+    static class Deep88 extends Deep87 {}
+
+    static class Deep89 extends Deep88 {}
+
+    static class Deep90 extends Deep89 {}
+
+    static class Deep91 extends Deep90 {}
+
+    static class Deep92 extends Deep91 {}
+
+    static class Deep93 extends Deep92 {}
+
+    static class Deep94 extends Deep93 {}
+
+    static class Deep95 extends Deep94 {}
+
+    static class Deep96 extends Deep95 {}
+
+    static class Deep97 extends Deep96 {}
+
+    static class Deep98 extends Deep97 {}
+
+    static class Deep99 extends Deep98 {}
+
+    static class Deep100 extends Deep99 {}
+
+    static class DeepCloneable extends Deep100 implements Cloneable {
+        public Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+    }
+
+    @Test
+    public void time_Object_clone() {
+        try {
+            CloneableObject o = new CloneableObject();
+            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            while (state.keepRunning()) {
+                o.clone();
+            }
+        } catch (Exception e) {
+            throw new AssertionError(e.getMessage());
+        }
+    }
+
+    @Test
+    public void time_Object_manyFieldClone() {
+        try {
+            CloneableManyFieldObject o = new CloneableManyFieldObject();
+            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            while (state.keepRunning()) {
+                o.clone();
+            }
+        } catch (Exception e) {
+            throw new AssertionError(e.getMessage());
+        }
+    }
+
+    @Test
+    public void time_Object_deepClone() {
+        try {
+            DeepCloneable o = new DeepCloneable();
+            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            while (state.keepRunning()) {
+                o.clone();
+            }
+        } catch (Exception e) {
+            throw new AssertionError(e.getMessage());
+        }
+    }
+
+    @Test
+    public void time_Array_clone() {
+        int[] o = new int[32];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            o.clone();
+        }
+    }
+
+    @Test
+    public void time_ObjectArray_smallClone() {
+        Object[] o = new Object[32];
+        for (int i = 0; i < o.length / 2; ++i) {
+            o[i] = new Object();
+        }
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            o.clone();
+        }
+    }
+
+    @Test
+    public void time_ObjectArray_largeClone() {
+        Object[] o = new Object[2048];
+        for (int i = 0; i < o.length / 2; ++i) {
+            o[i] = new Object();
+        }
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            o.clone();
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
new file mode 100644
index 0000000..3f4f6af
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class DeepArrayOpsPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private Object[] mArray;
+    private Object[] mArray2;
+
+    @Parameterized.Parameter(0)
+    public int mArrayLength;
+
+    @Parameterized.Parameters(name = "mArrayLength({0})")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {{1}, {4}, {16}, {32}, {2048}});
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        mArray = new Object[mArrayLength * 14];
+        mArray2 = new Object[mArrayLength * 14];
+        for (int i = 0; i < mArrayLength; i += 14) {
+            mArray[i] = new IntWrapper(i);
+            mArray2[i] = new IntWrapper(i);
+
+            mArray[i + 1] = new16ElementObjectmArray();
+            mArray2[i + 1] = new16ElementObjectmArray();
+
+            mArray[i + 2] = new boolean[16];
+            mArray2[i + 2] = new boolean[16];
+
+            mArray[i + 3] = new byte[16];
+            mArray2[i + 3] = new byte[16];
+
+            mArray[i + 4] = new char[16];
+            mArray2[i + 4] = new char[16];
+
+            mArray[i + 5] = new short[16];
+            mArray2[i + 5] = new short[16];
+
+            mArray[i + 6] = new float[16];
+            mArray2[i + 6] = new float[16];
+
+            mArray[i + 7] = new long[16];
+            mArray2[i + 7] = new long[16];
+
+            mArray[i + 8] = new int[16];
+            mArray2[i + 8] = new int[16];
+
+            mArray[i + 9] = new double[16];
+            mArray2[i + 9] = new double[16];
+
+            // SubmArray types are concrete objects.
+            mArray[i + 10] = new16ElementArray(String.class, String.class);
+            mArray2[i + 10] = new16ElementArray(String.class, String.class);
+
+            mArray[i + 11] = new16ElementArray(Integer.class, Integer.class);
+            mArray2[i + 11] = new16ElementArray(Integer.class, Integer.class);
+
+            // SubmArray types is an interface.
+            mArray[i + 12] = new16ElementArray(CharSequence.class, String.class);
+            mArray2[i + 12] = new16ElementArray(CharSequence.class, String.class);
+
+            mArray[i + 13] = null;
+            mArray2[i + 13] = null;
+        }
+    }
+
+    @Test
+    public void deepHashCode() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Arrays.deepHashCode(mArray);
+        }
+    }
+
+    @Test
+    public void deepEquals() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Arrays.deepEquals(mArray, mArray2);
+        }
+    }
+
+    private static Object[] new16ElementObjectmArray() {
+        Object[] array = new Object[16];
+        for (int i = 0; i < 16; ++i) {
+            array[i] = new IntWrapper(i);
+        }
+
+        return array;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T, V> T[] new16ElementArray(Class<T> mArrayType, Class<V> type)
+            throws Exception {
+        T[] array = (T[]) Array.newInstance(type, 16);
+        if (!mArrayType.isAssignableFrom(type)) {
+            throw new IllegalArgumentException(mArrayType + " is not assignable from " + type);
+        }
+
+        Constructor<V> constructor = type.getDeclaredConstructor(String.class);
+        for (int i = 0; i < 16; ++i) {
+            array[i] = (T) constructor.newInstance(String.valueOf(i + 1000));
+        }
+
+        return array;
+    }
+
+    /**
+     * A class that provides very basic equals() and hashCode() operations and doesn't resort to
+     * memoization tricks like {@link java.lang.Integer}.
+     *
+     * <p>Useful for providing equal objects that aren't the same (a.equals(b) but a != b).
+     */
+    public static final class IntWrapper {
+        private final int mWrapped;
+
+        public IntWrapper(int wrap) {
+            mWrapped = wrap;
+        }
+
+        @Override
+        public int hashCode() {
+            return mWrapped;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof IntWrapper)) {
+                return false;
+            }
+
+            return ((IntWrapper) o).mWrapped == this.mWrapped;
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
new file mode 100644
index 0000000..da94ae1
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** What does field access cost? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class FieldAccessPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private static class Inner {
+        public int mPublicInnerIntVal;
+        protected int mProtectedInnerIntVal;
+        private int mPrivateInnerIntVal;
+        int mPackageInnerIntVal;
+    }
+
+    int mIntVal = 42;
+    final int mFinalIntVal = 42;
+    static int sStaticIntVal = 42;
+    static final int FINAL_INT_VAL = 42;
+
+    @Test
+    public void timeField() {
+        int result = 0;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = mIntVal;
+        }
+    }
+
+    @Test
+    public void timeFieldFinal() {
+        int result = 0;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = mFinalIntVal;
+        }
+    }
+
+    @Test
+    public void timeFieldStatic() {
+        int result = 0;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = sStaticIntVal;
+        }
+    }
+
+    @Test
+    public void timeFieldStaticFinal() {
+        int result = 0;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = FINAL_INT_VAL;
+        }
+    }
+
+    @Test
+    public void timeFieldCached() {
+        int result = 0;
+        int cachedIntVal = this.mIntVal;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = cachedIntVal;
+        }
+    }
+
+    @Test
+    public void timeFieldPrivateInnerClassPublicField() {
+        int result = 0;
+        Inner inner = new Inner();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = inner.mPublicInnerIntVal;
+        }
+    }
+
+    @Test
+    public void timeFieldPrivateInnerClassProtectedField() {
+        int result = 0;
+        Inner inner = new Inner();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = inner.mProtectedInnerIntVal;
+        }
+    }
+
+    @Test
+    public void timeFieldPrivateInnerClassPrivateField() {
+        int result = 0;
+        Inner inner = new Inner();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = inner.mPrivateInnerIntVal;
+        }
+    }
+
+    @Test
+    public void timeFieldPrivateInnerClassPackageField() {
+        int result = 0;
+        Inner inner = new Inner();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = inner.mPackageInnerIntVal;
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
new file mode 100644
index 0000000..9446d99c
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** How do the various hash maps compare? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class HashedCollectionsPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void timeHashMapGet() {
+        HashMap<String, String> map = new HashMap<String, String>();
+        map.put("hello", "world");
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            map.get("hello");
+        }
+    }
+
+    @Test
+    public void timeHashMapGet_Synchronized() {
+        HashMap<String, String> map = new HashMap<String, String>();
+        synchronized (map) {
+            map.put("hello", "world");
+        }
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            synchronized (map) {
+                map.get("hello");
+            }
+        }
+    }
+
+    @Test
+    public void timeHashtableGet() {
+        Hashtable<String, String> map = new Hashtable<String, String>();
+        map.put("hello", "world");
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            map.get("hello");
+        }
+    }
+
+    @Test
+    public void timeLinkedHashMapGet() {
+        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
+        map.put("hello", "world");
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            map.get("hello");
+        }
+    }
+
+    @Test
+    public void timeConcurrentHashMapGet() {
+        ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
+        map.put("hello", "world");
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            map.get("hello");
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
new file mode 100644
index 0000000..be2a7e9
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
@@ -0,0 +1,1818 @@
+/*
+ * Copyright 2016 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This file is script-generated by ImtConflictPerfTestGen.py. It measures the performance impact of
+ * conflicts in interface method tables. Run `python ImtConflictPerfTestGen.py >
+ * ImtConflictPerfTest.java` to regenerate.
+ *
+ * <p>Each interface has 64 methods, which is the current size of an IMT. C0 implements one
+ * interface, C1 implements two, C2 implements three, and so on. The intent is that C0 has no
+ * conflicts in its IMT, C1 has depth-2 conflicts in its IMT, C2 has depth-3 conflicts, etc. This is
+ * currently guaranteed by the fact that we hash interface methods by taking their method index
+ * modulo 64. (Note that a "conflict depth" of 1 means no conflict at all.)
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ImtConflictPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Before
+    public void setup() {
+        C0 c0 = new C0();
+        callF0(c0);
+        C1 c1 = new C1();
+        callF0(c1);
+        callF19(c1);
+        C2 c2 = new C2();
+        callF0(c2);
+        callF19(c2);
+        callF38(c2);
+        C3 c3 = new C3();
+        callF0(c3);
+        callF19(c3);
+        callF38(c3);
+        callF57(c3);
+        C4 c4 = new C4();
+        callF0(c4);
+        callF19(c4);
+        callF38(c4);
+        callF57(c4);
+        callF76(c4);
+        C5 c5 = new C5();
+        callF0(c5);
+        callF19(c5);
+        callF38(c5);
+        callF57(c5);
+        callF76(c5);
+        callF95(c5);
+        C6 c6 = new C6();
+        callF0(c6);
+        callF19(c6);
+        callF38(c6);
+        callF57(c6);
+        callF76(c6);
+        callF95(c6);
+        callF114(c6);
+        C7 c7 = new C7();
+        callF0(c7);
+        callF19(c7);
+        callF38(c7);
+        callF57(c7);
+        callF76(c7);
+        callF95(c7);
+        callF114(c7);
+        callF133(c7);
+        C8 c8 = new C8();
+        callF0(c8);
+        callF19(c8);
+        callF38(c8);
+        callF57(c8);
+        callF76(c8);
+        callF95(c8);
+        callF114(c8);
+        callF133(c8);
+        callF152(c8);
+        C9 c9 = new C9();
+        callF0(c9);
+        callF19(c9);
+        callF38(c9);
+        callF57(c9);
+        callF76(c9);
+        callF95(c9);
+        callF114(c9);
+        callF133(c9);
+        callF152(c9);
+        callF171(c9);
+        C10 c10 = new C10();
+        callF0(c10);
+        callF19(c10);
+        callF38(c10);
+        callF57(c10);
+        callF76(c10);
+        callF95(c10);
+        callF114(c10);
+        callF133(c10);
+        callF152(c10);
+        callF171(c10);
+        callF190(c10);
+        C11 c11 = new C11();
+        callF0(c11);
+        callF19(c11);
+        callF38(c11);
+        callF57(c11);
+        callF76(c11);
+        callF95(c11);
+        callF114(c11);
+        callF133(c11);
+        callF152(c11);
+        callF171(c11);
+        callF190(c11);
+        callF209(c11);
+        C12 c12 = new C12();
+        callF0(c12);
+        callF19(c12);
+        callF38(c12);
+        callF57(c12);
+        callF76(c12);
+        callF95(c12);
+        callF114(c12);
+        callF133(c12);
+        callF152(c12);
+        callF171(c12);
+        callF190(c12);
+        callF209(c12);
+        callF228(c12);
+        C13 c13 = new C13();
+        callF0(c13);
+        callF19(c13);
+        callF38(c13);
+        callF57(c13);
+        callF76(c13);
+        callF95(c13);
+        callF114(c13);
+        callF133(c13);
+        callF152(c13);
+        callF171(c13);
+        callF190(c13);
+        callF209(c13);
+        callF228(c13);
+        callF247(c13);
+        C14 c14 = new C14();
+        callF0(c14);
+        callF19(c14);
+        callF38(c14);
+        callF57(c14);
+        callF76(c14);
+        callF95(c14);
+        callF114(c14);
+        callF133(c14);
+        callF152(c14);
+        callF171(c14);
+        callF190(c14);
+        callF209(c14);
+        callF228(c14);
+        callF247(c14);
+        callF266(c14);
+        C15 c15 = new C15();
+        callF0(c15);
+        callF19(c15);
+        callF38(c15);
+        callF57(c15);
+        callF76(c15);
+        callF95(c15);
+        callF114(c15);
+        callF133(c15);
+        callF152(c15);
+        callF171(c15);
+        callF190(c15);
+        callF209(c15);
+        callF228(c15);
+        callF247(c15);
+        callF266(c15);
+        callF285(c15);
+        C16 c16 = new C16();
+        callF0(c16);
+        callF19(c16);
+        callF38(c16);
+        callF57(c16);
+        callF76(c16);
+        callF95(c16);
+        callF114(c16);
+        callF133(c16);
+        callF152(c16);
+        callF171(c16);
+        callF190(c16);
+        callF209(c16);
+        callF228(c16);
+        callF247(c16);
+        callF266(c16);
+        callF285(c16);
+        callF304(c16);
+        C17 c17 = new C17();
+        callF0(c17);
+        callF19(c17);
+        callF38(c17);
+        callF57(c17);
+        callF76(c17);
+        callF95(c17);
+        callF114(c17);
+        callF133(c17);
+        callF152(c17);
+        callF171(c17);
+        callF190(c17);
+        callF209(c17);
+        callF228(c17);
+        callF247(c17);
+        callF266(c17);
+        callF285(c17);
+        callF304(c17);
+        callF323(c17);
+        C18 c18 = new C18();
+        callF0(c18);
+        callF19(c18);
+        callF38(c18);
+        callF57(c18);
+        callF76(c18);
+        callF95(c18);
+        callF114(c18);
+        callF133(c18);
+        callF152(c18);
+        callF171(c18);
+        callF190(c18);
+        callF209(c18);
+        callF228(c18);
+        callF247(c18);
+        callF266(c18);
+        callF285(c18);
+        callF304(c18);
+        callF323(c18);
+        callF342(c18);
+        C19 c19 = new C19();
+        callF0(c19);
+        callF19(c19);
+        callF38(c19);
+        callF57(c19);
+        callF76(c19);
+        callF95(c19);
+        callF114(c19);
+        callF133(c19);
+        callF152(c19);
+        callF171(c19);
+        callF190(c19);
+        callF209(c19);
+        callF228(c19);
+        callF247(c19);
+        callF266(c19);
+        callF285(c19);
+        callF304(c19);
+        callF323(c19);
+        callF342(c19);
+        callF361(c19);
+    }
+
+    @Test
+    public void timeConflictDepth01() {
+        C0 c0 = new C0();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+            callF0(c0);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth02() {
+        C1 c1 = new C1();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c1);
+            callF19(c1);
+            callF0(c1);
+            callF19(c1);
+            callF0(c1);
+            callF19(c1);
+            callF0(c1);
+            callF19(c1);
+            callF0(c1);
+            callF19(c1);
+            callF0(c1);
+            callF19(c1);
+            callF0(c1);
+            callF19(c1);
+            callF0(c1);
+            callF19(c1);
+            callF0(c1);
+            callF19(c1);
+            callF0(c1);
+            callF19(c1);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth03() {
+        C2 c2 = new C2();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c2);
+            callF19(c2);
+            callF38(c2);
+            callF0(c2);
+            callF19(c2);
+            callF38(c2);
+            callF0(c2);
+            callF19(c2);
+            callF38(c2);
+            callF0(c2);
+            callF19(c2);
+            callF38(c2);
+            callF0(c2);
+            callF19(c2);
+            callF38(c2);
+            callF0(c2);
+            callF19(c2);
+            callF38(c2);
+            callF0(c2);
+            callF19(c2);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth04() {
+        C3 c3 = new C3();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c3);
+            callF19(c3);
+            callF38(c3);
+            callF57(c3);
+            callF0(c3);
+            callF19(c3);
+            callF38(c3);
+            callF57(c3);
+            callF0(c3);
+            callF19(c3);
+            callF38(c3);
+            callF57(c3);
+            callF0(c3);
+            callF19(c3);
+            callF38(c3);
+            callF57(c3);
+            callF0(c3);
+            callF19(c3);
+            callF38(c3);
+            callF57(c3);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth05() {
+        C4 c4 = new C4();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c4);
+            callF19(c4);
+            callF38(c4);
+            callF57(c4);
+            callF76(c4);
+            callF0(c4);
+            callF19(c4);
+            callF38(c4);
+            callF57(c4);
+            callF76(c4);
+            callF0(c4);
+            callF19(c4);
+            callF38(c4);
+            callF57(c4);
+            callF76(c4);
+            callF0(c4);
+            callF19(c4);
+            callF38(c4);
+            callF57(c4);
+            callF76(c4);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth06() {
+        C5 c5 = new C5();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c5);
+            callF19(c5);
+            callF38(c5);
+            callF57(c5);
+            callF76(c5);
+            callF95(c5);
+            callF0(c5);
+            callF19(c5);
+            callF38(c5);
+            callF57(c5);
+            callF76(c5);
+            callF95(c5);
+            callF0(c5);
+            callF19(c5);
+            callF38(c5);
+            callF57(c5);
+            callF76(c5);
+            callF95(c5);
+            callF0(c5);
+            callF19(c5);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth07() {
+        C6 c6 = new C6();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c6);
+            callF19(c6);
+            callF38(c6);
+            callF57(c6);
+            callF76(c6);
+            callF95(c6);
+            callF114(c6);
+            callF0(c6);
+            callF19(c6);
+            callF38(c6);
+            callF57(c6);
+            callF76(c6);
+            callF95(c6);
+            callF114(c6);
+            callF0(c6);
+            callF19(c6);
+            callF38(c6);
+            callF57(c6);
+            callF76(c6);
+            callF95(c6);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth08() {
+        C7 c7 = new C7();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c7);
+            callF19(c7);
+            callF38(c7);
+            callF57(c7);
+            callF76(c7);
+            callF95(c7);
+            callF114(c7);
+            callF133(c7);
+            callF0(c7);
+            callF19(c7);
+            callF38(c7);
+            callF57(c7);
+            callF76(c7);
+            callF95(c7);
+            callF114(c7);
+            callF133(c7);
+            callF0(c7);
+            callF19(c7);
+            callF38(c7);
+            callF57(c7);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth09() {
+        C8 c8 = new C8();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c8);
+            callF19(c8);
+            callF38(c8);
+            callF57(c8);
+            callF76(c8);
+            callF95(c8);
+            callF114(c8);
+            callF133(c8);
+            callF152(c8);
+            callF0(c8);
+            callF19(c8);
+            callF38(c8);
+            callF57(c8);
+            callF76(c8);
+            callF95(c8);
+            callF114(c8);
+            callF133(c8);
+            callF152(c8);
+            callF0(c8);
+            callF19(c8);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth10() {
+        C9 c9 = new C9();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c9);
+            callF19(c9);
+            callF38(c9);
+            callF57(c9);
+            callF76(c9);
+            callF95(c9);
+            callF114(c9);
+            callF133(c9);
+            callF152(c9);
+            callF171(c9);
+            callF0(c9);
+            callF19(c9);
+            callF38(c9);
+            callF57(c9);
+            callF76(c9);
+            callF95(c9);
+            callF114(c9);
+            callF133(c9);
+            callF152(c9);
+            callF171(c9);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth11() {
+        C10 c10 = new C10();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c10);
+            callF19(c10);
+            callF38(c10);
+            callF57(c10);
+            callF76(c10);
+            callF95(c10);
+            callF114(c10);
+            callF133(c10);
+            callF152(c10);
+            callF171(c10);
+            callF190(c10);
+            callF0(c10);
+            callF19(c10);
+            callF38(c10);
+            callF57(c10);
+            callF76(c10);
+            callF95(c10);
+            callF114(c10);
+            callF133(c10);
+            callF152(c10);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth12() {
+        C11 c11 = new C11();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c11);
+            callF19(c11);
+            callF38(c11);
+            callF57(c11);
+            callF76(c11);
+            callF95(c11);
+            callF114(c11);
+            callF133(c11);
+            callF152(c11);
+            callF171(c11);
+            callF190(c11);
+            callF209(c11);
+            callF0(c11);
+            callF19(c11);
+            callF38(c11);
+            callF57(c11);
+            callF76(c11);
+            callF95(c11);
+            callF114(c11);
+            callF133(c11);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth13() {
+        C12 c12 = new C12();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c12);
+            callF19(c12);
+            callF38(c12);
+            callF57(c12);
+            callF76(c12);
+            callF95(c12);
+            callF114(c12);
+            callF133(c12);
+            callF152(c12);
+            callF171(c12);
+            callF190(c12);
+            callF209(c12);
+            callF228(c12);
+            callF0(c12);
+            callF19(c12);
+            callF38(c12);
+            callF57(c12);
+            callF76(c12);
+            callF95(c12);
+            callF114(c12);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth14() {
+        C13 c13 = new C13();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c13);
+            callF19(c13);
+            callF38(c13);
+            callF57(c13);
+            callF76(c13);
+            callF95(c13);
+            callF114(c13);
+            callF133(c13);
+            callF152(c13);
+            callF171(c13);
+            callF190(c13);
+            callF209(c13);
+            callF228(c13);
+            callF247(c13);
+            callF0(c13);
+            callF19(c13);
+            callF38(c13);
+            callF57(c13);
+            callF76(c13);
+            callF95(c13);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth15() {
+        C14 c14 = new C14();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c14);
+            callF19(c14);
+            callF38(c14);
+            callF57(c14);
+            callF76(c14);
+            callF95(c14);
+            callF114(c14);
+            callF133(c14);
+            callF152(c14);
+            callF171(c14);
+            callF190(c14);
+            callF209(c14);
+            callF228(c14);
+            callF247(c14);
+            callF266(c14);
+            callF0(c14);
+            callF19(c14);
+            callF38(c14);
+            callF57(c14);
+            callF76(c14);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth16() {
+        C15 c15 = new C15();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c15);
+            callF19(c15);
+            callF38(c15);
+            callF57(c15);
+            callF76(c15);
+            callF95(c15);
+            callF114(c15);
+            callF133(c15);
+            callF152(c15);
+            callF171(c15);
+            callF190(c15);
+            callF209(c15);
+            callF228(c15);
+            callF247(c15);
+            callF266(c15);
+            callF285(c15);
+            callF0(c15);
+            callF19(c15);
+            callF38(c15);
+            callF57(c15);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth17() {
+        C16 c16 = new C16();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c16);
+            callF19(c16);
+            callF38(c16);
+            callF57(c16);
+            callF76(c16);
+            callF95(c16);
+            callF114(c16);
+            callF133(c16);
+            callF152(c16);
+            callF171(c16);
+            callF190(c16);
+            callF209(c16);
+            callF228(c16);
+            callF247(c16);
+            callF266(c16);
+            callF285(c16);
+            callF304(c16);
+            callF0(c16);
+            callF19(c16);
+            callF38(c16);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth18() {
+        C17 c17 = new C17();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c17);
+            callF19(c17);
+            callF38(c17);
+            callF57(c17);
+            callF76(c17);
+            callF95(c17);
+            callF114(c17);
+            callF133(c17);
+            callF152(c17);
+            callF171(c17);
+            callF190(c17);
+            callF209(c17);
+            callF228(c17);
+            callF247(c17);
+            callF266(c17);
+            callF285(c17);
+            callF304(c17);
+            callF323(c17);
+            callF0(c17);
+            callF19(c17);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth19() {
+        C18 c18 = new C18();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c18);
+            callF19(c18);
+            callF38(c18);
+            callF57(c18);
+            callF76(c18);
+            callF95(c18);
+            callF114(c18);
+            callF133(c18);
+            callF152(c18);
+            callF171(c18);
+            callF190(c18);
+            callF209(c18);
+            callF228(c18);
+            callF247(c18);
+            callF266(c18);
+            callF285(c18);
+            callF304(c18);
+            callF323(c18);
+            callF342(c18);
+            callF0(c18);
+        }
+    }
+
+    @Test
+    public void timeConflictDepth20() {
+        C19 c19 = new C19();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            callF0(c19);
+            callF19(c19);
+            callF38(c19);
+            callF57(c19);
+            callF76(c19);
+            callF95(c19);
+            callF114(c19);
+            callF133(c19);
+            callF152(c19);
+            callF171(c19);
+            callF190(c19);
+            callF209(c19);
+            callF228(c19);
+            callF247(c19);
+            callF266(c19);
+            callF285(c19);
+            callF304(c19);
+            callF323(c19);
+            callF342(c19);
+            callF361(c19);
+        }
+    }
+
+    public void callF0(I0 i) {
+        i.f0();
+    }
+
+    public void callF19(I1 i) {
+        i.f19();
+    }
+
+    public void callF38(I2 i) {
+        i.f38();
+    }
+
+    public void callF57(I3 i) {
+        i.f57();
+    }
+
+    public void callF76(I4 i) {
+        i.f76();
+    }
+
+    public void callF95(I5 i) {
+        i.f95();
+    }
+
+    public void callF114(I6 i) {
+        i.f114();
+    }
+
+    public void callF133(I7 i) {
+        i.f133();
+    }
+
+    public void callF152(I8 i) {
+        i.f152();
+    }
+
+    public void callF171(I9 i) {
+        i.f171();
+    }
+
+    public void callF190(I10 i) {
+        i.f190();
+    }
+
+    public void callF209(I11 i) {
+        i.f209();
+    }
+
+    public void callF228(I12 i) {
+        i.f228();
+    }
+
+    public void callF247(I13 i) {
+        i.f247();
+    }
+
+    public void callF266(I14 i) {
+        i.f266();
+    }
+
+    public void callF285(I15 i) {
+        i.f285();
+    }
+
+    public void callF304(I16 i) {
+        i.f304();
+    }
+
+    public void callF323(I17 i) {
+        i.f323();
+    }
+
+    public void callF342(I18 i) {
+        i.f342();
+    }
+
+    public void callF361(I19 i) {
+        i.f361();
+    }
+
+    static class C0 implements I0 {}
+
+    static class C1 implements I0, I1 {}
+
+    static class C2 implements I0, I1, I2 {}
+
+    static class C3 implements I0, I1, I2, I3 {}
+
+    static class C4 implements I0, I1, I2, I3, I4 {}
+
+    static class C5 implements I0, I1, I2, I3, I4, I5 {}
+
+    static class C6 implements I0, I1, I2, I3, I4, I5, I6 {}
+
+    static class C7 implements I0, I1, I2, I3, I4, I5, I6, I7 {}
+
+    static class C8 implements I0, I1, I2, I3, I4, I5, I6, I7, I8 {}
+
+    static class C9 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9 {}
+
+    static class C10 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10 {}
+
+    static class C11 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11 {}
+
+    static class C12 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12 {}
+
+    static class C13 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13 {}
+
+    static class C14 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14 {}
+
+    static class C15
+            implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15 {}
+
+    static class C16
+            implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16 {}
+
+    static class C17
+            implements I0,
+                    I1,
+                    I2,
+                    I3,
+                    I4,
+                    I5,
+                    I6,
+                    I7,
+                    I8,
+                    I9,
+                    I10,
+                    I11,
+                    I12,
+                    I13,
+                    I14,
+                    I15,
+                    I16,
+                    I17 {}
+
+    static class C18
+            implements I0,
+                    I1,
+                    I2,
+                    I3,
+                    I4,
+                    I5,
+                    I6,
+                    I7,
+                    I8,
+                    I9,
+                    I10,
+                    I11,
+                    I12,
+                    I13,
+                    I14,
+                    I15,
+                    I16,
+                    I17,
+                    I18 {}
+
+    static class C19
+            implements I0,
+                    I1,
+                    I2,
+                    I3,
+                    I4,
+                    I5,
+                    I6,
+                    I7,
+                    I8,
+                    I9,
+                    I10,
+                    I11,
+                    I12,
+                    I13,
+                    I14,
+                    I15,
+                    I16,
+                    I17,
+                    I18,
+                    I19 {}
+
+    interface I0 {
+        default void f0() {}
+
+        default void f1() {}
+
+        default void f2() {}
+
+        default void f3() {}
+
+        default void f4() {}
+
+        default void f5() {}
+
+        default void f6() {}
+
+        default void f7() {}
+
+        default void f8() {}
+
+        default void f9() {}
+
+        default void f10() {}
+
+        default void f11() {}
+
+        default void f12() {}
+
+        default void f13() {}
+
+        default void f14() {}
+
+        default void f15() {}
+
+        default void f16() {}
+
+        default void f17() {}
+
+        default void f18() {}
+    }
+
+    interface I1 {
+        default void f19() {}
+
+        default void f20() {}
+
+        default void f21() {}
+
+        default void f22() {}
+
+        default void f23() {}
+
+        default void f24() {}
+
+        default void f25() {}
+
+        default void f26() {}
+
+        default void f27() {}
+
+        default void f28() {}
+
+        default void f29() {}
+
+        default void f30() {}
+
+        default void f31() {}
+
+        default void f32() {}
+
+        default void f33() {}
+
+        default void f34() {}
+
+        default void f35() {}
+
+        default void f36() {}
+
+        default void f37() {}
+    }
+
+    interface I2 {
+        default void f38() {}
+
+        default void f39() {}
+
+        default void f40() {}
+
+        default void f41() {}
+
+        default void f42() {}
+
+        default void f43() {}
+
+        default void f44() {}
+
+        default void f45() {}
+
+        default void f46() {}
+
+        default void f47() {}
+
+        default void f48() {}
+
+        default void f49() {}
+
+        default void f50() {}
+
+        default void f51() {}
+
+        default void f52() {}
+
+        default void f53() {}
+
+        default void f54() {}
+
+        default void f55() {}
+
+        default void f56() {}
+    }
+
+    interface I3 {
+        default void f57() {}
+
+        default void f58() {}
+
+        default void f59() {}
+
+        default void f60() {}
+
+        default void f61() {}
+
+        default void f62() {}
+
+        default void f63() {}
+
+        default void f64() {}
+
+        default void f65() {}
+
+        default void f66() {}
+
+        default void f67() {}
+
+        default void f68() {}
+
+        default void f69() {}
+
+        default void f70() {}
+
+        default void f71() {}
+
+        default void f72() {}
+
+        default void f73() {}
+
+        default void f74() {}
+
+        default void f75() {}
+    }
+
+    interface I4 {
+        default void f76() {}
+
+        default void f77() {}
+
+        default void f78() {}
+
+        default void f79() {}
+
+        default void f80() {}
+
+        default void f81() {}
+
+        default void f82() {}
+
+        default void f83() {}
+
+        default void f84() {}
+
+        default void f85() {}
+
+        default void f86() {}
+
+        default void f87() {}
+
+        default void f88() {}
+
+        default void f89() {}
+
+        default void f90() {}
+
+        default void f91() {}
+
+        default void f92() {}
+
+        default void f93() {}
+
+        default void f94() {}
+    }
+
+    interface I5 {
+        default void f95() {}
+
+        default void f96() {}
+
+        default void f97() {}
+
+        default void f98() {}
+
+        default void f99() {}
+
+        default void f100() {}
+
+        default void f101() {}
+
+        default void f102() {}
+
+        default void f103() {}
+
+        default void f104() {}
+
+        default void f105() {}
+
+        default void f106() {}
+
+        default void f107() {}
+
+        default void f108() {}
+
+        default void f109() {}
+
+        default void f110() {}
+
+        default void f111() {}
+
+        default void f112() {}
+
+        default void f113() {}
+    }
+
+    interface I6 {
+        default void f114() {}
+
+        default void f115() {}
+
+        default void f116() {}
+
+        default void f117() {}
+
+        default void f118() {}
+
+        default void f119() {}
+
+        default void f120() {}
+
+        default void f121() {}
+
+        default void f122() {}
+
+        default void f123() {}
+
+        default void f124() {}
+
+        default void f125() {}
+
+        default void f126() {}
+
+        default void f127() {}
+
+        default void f128() {}
+
+        default void f129() {}
+
+        default void f130() {}
+
+        default void f131() {}
+
+        default void f132() {}
+    }
+
+    interface I7 {
+        default void f133() {}
+
+        default void f134() {}
+
+        default void f135() {}
+
+        default void f136() {}
+
+        default void f137() {}
+
+        default void f138() {}
+
+        default void f139() {}
+
+        default void f140() {}
+
+        default void f141() {}
+
+        default void f142() {}
+
+        default void f143() {}
+
+        default void f144() {}
+
+        default void f145() {}
+
+        default void f146() {}
+
+        default void f147() {}
+
+        default void f148() {}
+
+        default void f149() {}
+
+        default void f150() {}
+
+        default void f151() {}
+    }
+
+    interface I8 {
+        default void f152() {}
+
+        default void f153() {}
+
+        default void f154() {}
+
+        default void f155() {}
+
+        default void f156() {}
+
+        default void f157() {}
+
+        default void f158() {}
+
+        default void f159() {}
+
+        default void f160() {}
+
+        default void f161() {}
+
+        default void f162() {}
+
+        default void f163() {}
+
+        default void f164() {}
+
+        default void f165() {}
+
+        default void f166() {}
+
+        default void f167() {}
+
+        default void f168() {}
+
+        default void f169() {}
+
+        default void f170() {}
+    }
+
+    interface I9 {
+        default void f171() {}
+
+        default void f172() {}
+
+        default void f173() {}
+
+        default void f174() {}
+
+        default void f175() {}
+
+        default void f176() {}
+
+        default void f177() {}
+
+        default void f178() {}
+
+        default void f179() {}
+
+        default void f180() {}
+
+        default void f181() {}
+
+        default void f182() {}
+
+        default void f183() {}
+
+        default void f184() {}
+
+        default void f185() {}
+
+        default void f186() {}
+
+        default void f187() {}
+
+        default void f188() {}
+
+        default void f189() {}
+    }
+
+    interface I10 {
+        default void f190() {}
+
+        default void f191() {}
+
+        default void f192() {}
+
+        default void f193() {}
+
+        default void f194() {}
+
+        default void f195() {}
+
+        default void f196() {}
+
+        default void f197() {}
+
+        default void f198() {}
+
+        default void f199() {}
+
+        default void f200() {}
+
+        default void f201() {}
+
+        default void f202() {}
+
+        default void f203() {}
+
+        default void f204() {}
+
+        default void f205() {}
+
+        default void f206() {}
+
+        default void f207() {}
+
+        default void f208() {}
+    }
+
+    interface I11 {
+        default void f209() {}
+
+        default void f210() {}
+
+        default void f211() {}
+
+        default void f212() {}
+
+        default void f213() {}
+
+        default void f214() {}
+
+        default void f215() {}
+
+        default void f216() {}
+
+        default void f217() {}
+
+        default void f218() {}
+
+        default void f219() {}
+
+        default void f220() {}
+
+        default void f221() {}
+
+        default void f222() {}
+
+        default void f223() {}
+
+        default void f224() {}
+
+        default void f225() {}
+
+        default void f226() {}
+
+        default void f227() {}
+    }
+
+    interface I12 {
+        default void f228() {}
+
+        default void f229() {}
+
+        default void f230() {}
+
+        default void f231() {}
+
+        default void f232() {}
+
+        default void f233() {}
+
+        default void f234() {}
+
+        default void f235() {}
+
+        default void f236() {}
+
+        default void f237() {}
+
+        default void f238() {}
+
+        default void f239() {}
+
+        default void f240() {}
+
+        default void f241() {}
+
+        default void f242() {}
+
+        default void f243() {}
+
+        default void f244() {}
+
+        default void f245() {}
+
+        default void f246() {}
+    }
+
+    interface I13 {
+        default void f247() {}
+
+        default void f248() {}
+
+        default void f249() {}
+
+        default void f250() {}
+
+        default void f251() {}
+
+        default void f252() {}
+
+        default void f253() {}
+
+        default void f254() {}
+
+        default void f255() {}
+
+        default void f256() {}
+
+        default void f257() {}
+
+        default void f258() {}
+
+        default void f259() {}
+
+        default void f260() {}
+
+        default void f261() {}
+
+        default void f262() {}
+
+        default void f263() {}
+
+        default void f264() {}
+
+        default void f265() {}
+    }
+
+    interface I14 {
+        default void f266() {}
+
+        default void f267() {}
+
+        default void f268() {}
+
+        default void f269() {}
+
+        default void f270() {}
+
+        default void f271() {}
+
+        default void f272() {}
+
+        default void f273() {}
+
+        default void f274() {}
+
+        default void f275() {}
+
+        default void f276() {}
+
+        default void f277() {}
+
+        default void f278() {}
+
+        default void f279() {}
+
+        default void f280() {}
+
+        default void f281() {}
+
+        default void f282() {}
+
+        default void f283() {}
+
+        default void f284() {}
+    }
+
+    interface I15 {
+        default void f285() {}
+
+        default void f286() {}
+
+        default void f287() {}
+
+        default void f288() {}
+
+        default void f289() {}
+
+        default void f290() {}
+
+        default void f291() {}
+
+        default void f292() {}
+
+        default void f293() {}
+
+        default void f294() {}
+
+        default void f295() {}
+
+        default void f296() {}
+
+        default void f297() {}
+
+        default void f298() {}
+
+        default void f299() {}
+
+        default void f300() {}
+
+        default void f301() {}
+
+        default void f302() {}
+
+        default void f303() {}
+    }
+
+    interface I16 {
+        default void f304() {}
+
+        default void f305() {}
+
+        default void f306() {}
+
+        default void f307() {}
+
+        default void f308() {}
+
+        default void f309() {}
+
+        default void f310() {}
+
+        default void f311() {}
+
+        default void f312() {}
+
+        default void f313() {}
+
+        default void f314() {}
+
+        default void f315() {}
+
+        default void f316() {}
+
+        default void f317() {}
+
+        default void f318() {}
+
+        default void f319() {}
+
+        default void f320() {}
+
+        default void f321() {}
+
+        default void f322() {}
+    }
+
+    interface I17 {
+        default void f323() {}
+
+        default void f324() {}
+
+        default void f325() {}
+
+        default void f326() {}
+
+        default void f327() {}
+
+        default void f328() {}
+
+        default void f329() {}
+
+        default void f330() {}
+
+        default void f331() {}
+
+        default void f332() {}
+
+        default void f333() {}
+
+        default void f334() {}
+
+        default void f335() {}
+
+        default void f336() {}
+
+        default void f337() {}
+
+        default void f338() {}
+
+        default void f339() {}
+
+        default void f340() {}
+
+        default void f341() {}
+    }
+
+    interface I18 {
+        default void f342() {}
+
+        default void f343() {}
+
+        default void f344() {}
+
+        default void f345() {}
+
+        default void f346() {}
+
+        default void f347() {}
+
+        default void f348() {}
+
+        default void f349() {}
+
+        default void f350() {}
+
+        default void f351() {}
+
+        default void f352() {}
+
+        default void f353() {}
+
+        default void f354() {}
+
+        default void f355() {}
+
+        default void f356() {}
+
+        default void f357() {}
+
+        default void f358() {}
+
+        default void f359() {}
+
+        default void f360() {}
+    }
+
+    interface I19 {
+        default void f361() {}
+
+        default void f362() {}
+
+        default void f363() {}
+
+        default void f364() {}
+
+        default void f365() {}
+
+        default void f366() {}
+
+        default void f367() {}
+
+        default void f368() {}
+
+        default void f369() {}
+
+        default void f370() {}
+
+        default void f371() {}
+
+        default void f372() {}
+
+        default void f373() {}
+
+        default void f374() {}
+
+        default void f375() {}
+
+        default void f376() {}
+
+        default void f377() {}
+
+        default void f378() {}
+
+        default void f379() {}
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py
new file mode 100755
index 0000000..eea3b84
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import sys
+
+max_conflict_depth = 20 # In practice does not go above 20 for reasonable IMT sizes
+try:
+    imt_size = int(sys.argv[1])
+except (IndexError, ValueError):
+    print("Usage: python ImtConflictPerfTestGen.py <IMT_SIZE>")
+    sys.exit(1)
+
+license = """\
+/*
+ * Copyright 2016 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.
+ */
+"""
+description = """
+/**
+ * This file is script-generated by ImtConflictPerfTestGen.py.
+ * It measures the performance impact of conflicts in interface method tables.
+ * Run `python ImtConflictPerfTestGen.py > ImtConflictPerfTest.java` to regenerate.
+ *
+ * Each interface has 64 methods, which is the current size of an IMT. C0 implements
+ * one interface, C1 implements two, C2 implements three, and so on. The intent
+ * is that C0 has no conflicts in its IMT, C1 has depth-2 conflicts in
+ * its IMT, C2 has depth-3 conflicts, etc. This is currently guaranteed by
+ * the fact that we hash interface methods by taking their method index modulo 64.
+ * (Note that a "conflict depth" of 1 means no conflict at all.)
+ */\
+"""
+
+print(license)
+print("package android.libcore;")
+imports = """
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+"""
+print(imports)
+print(description)
+
+print("@RunWith(AndroidJUnit4.class)")
+print("@LargeTest")
+print("public class ImtConflictPerfTest {")
+print("    @Rule")
+print("    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();")
+print("")
+# Warm up interface method tables
+print("    @Before")
+print("    public void setup() {")
+for i in range(max_conflict_depth):
+    print("        C{0} c{0} = new C{0}();".format(i))
+    for j in range(i+1):
+        print("        callF{}(c{});".format(imt_size * j, i))
+print("    }")
+
+# Print test cases--one for each conflict depth
+for i in range(max_conflict_depth):
+    print("    @Test")
+    print("    public void timeConflictDepth{:02d}() {{".format(i+1))
+    print("        C{0} c{0} = new C{0}();".format(i))
+    print("        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();")
+    print("        while (state.keepRunning()) {")
+    # Cycle through each interface method in an IMT entry in order
+    # to test all conflict resolution possibilities
+    for j in range(max_conflict_depth):
+        print("            callF{}(c{});".format(imt_size * (j % (i + 1)), i))
+    print("        }")
+    print("    }")
+
+# Make calls through the IMTs
+for i in range(max_conflict_depth):
+    print("    public void callF{0}(I{1} i) {{ i.f{0}(); }}".format(imt_size*i, i))
+
+# Class definitions, implementing varying amounts of interfaces
+for i in range(max_conflict_depth):
+    interfaces = ", ".join(["I{}".format(j) for j in range(i+1)])
+    print("    static class C{} implements {} {{}}".format(i, interfaces))
+
+# Interface definitions, each with enough methods to fill an entire IMT
+for i in range(max_conflict_depth):
+    print("    interface I{} {{".format(i))
+    for j in range(imt_size):
+        print("        default void f{}() {{}}".format(i*imt_size + j))
+    print("    }")
+
+print("}")
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
new file mode 100644
index 0000000..ca99779
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Compares various kinds of method invocation. */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MethodInvocationPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    interface I {
+        void emptyInterface();
+    }
+
+    static class C implements I {
+        private int mField;
+
+        private int getField() {
+            return mField;
+        }
+
+        public void timeInternalGetter(BenchmarkState state) {
+            int result = 0;
+            while (state.keepRunning()) {
+                result = getField();
+            }
+        }
+
+        public void timeInternalFieldAccess(BenchmarkState state) {
+            int result = 0;
+            while (state.keepRunning()) {
+                result = mField;
+            }
+        }
+
+        public static void emptyStatic() {}
+
+        public void emptyVirtual() {}
+
+        public void emptyInterface() {}
+    }
+
+    public void timeInternalGetter() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        new C().timeInternalGetter(state);
+    }
+
+    public void timeInternalFieldAccess() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        new C().timeInternalFieldAccess(state);
+    }
+
+    // Test an intrinsic.
+    @Test
+    public void timeStringLength() {
+        int result = 0;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result = "hello, world!".length();
+        }
+    }
+
+    @Test
+    public void timeEmptyStatic() {
+        C c = new C();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            c.emptyStatic();
+        }
+    }
+
+    @Test
+    public void timeEmptyVirtual() {
+        C c = new C();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            c.emptyVirtual();
+        }
+    }
+
+    @Test
+    public void timeEmptyInterface() {
+        I c = new C();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            c.emptyInterface();
+        }
+    }
+
+    public static class Inner {
+        private int mI;
+
+        private void privateMethod() {
+            ++mI;
+        }
+
+        protected void protectedMethod() {
+            ++mI;
+        }
+
+        public void publicMethod() {
+            ++mI;
+        }
+
+        void packageMethod() {
+            ++mI;
+        }
+
+        final void finalPackageMethod() {
+            ++mI;
+        }
+    }
+
+    @Test
+    public void timePrivateInnerPublicMethod() {
+        Inner inner = new Inner();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            inner.publicMethod();
+        }
+    }
+
+    @Test
+    public void timePrivateInnerProtectedMethod() {
+        Inner inner = new Inner();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            inner.protectedMethod();
+        }
+    }
+
+    @Test
+    public void timePrivateInnerPrivateMethod() {
+        Inner inner = new Inner();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            inner.privateMethod();
+        }
+    }
+
+    @Test
+    public void timePrivateInnerPackageMethod() {
+        Inner inner = new Inner();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            inner.packageMethod();
+        }
+    }
+
+    @Test
+    public void timePrivateInnerFinalPackageMethod() {
+        Inner inner = new Inner();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            inner.finalPackageMethod();
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
new file mode 100644
index 0000000..8496fbe
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** How much do various kinds of multiplication cost? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MultiplicationPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void timeMultiplyIntByConstant10() {
+        int result = 1;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result *= 10;
+        }
+    }
+
+    @Test
+    public void timeMultiplyIntByConstant8() {
+        int result = 1;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result *= 8;
+        }
+    }
+
+    @Test
+    public void timeMultiplyIntByVariable10() {
+        int result = 1;
+        int factor = 10;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result *= factor;
+        }
+    }
+
+    @Test
+    public void timeMultiplyIntByVariable8() {
+        int result = 1;
+        int factor = 8;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            result *= factor;
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
new file mode 100644
index 0000000..bb79424
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ReferenceGetPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    boolean mIntrinsicDisabled;
+
+    private Object mObj = "str";
+
+    @Before
+    public void setUp() throws Exception {
+        Field intrinsicDisabledField = Reference.class.getDeclaredField("disableIntrinsic");
+        intrinsicDisabledField.setAccessible(true);
+        intrinsicDisabledField.setBoolean(null, mIntrinsicDisabled);
+    }
+
+    @Test
+    public void timeSoftReferenceGet() throws Exception {
+        Reference soft = new SoftReference(mObj);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Object o = soft.get();
+        }
+    }
+
+    @Test
+    public void timeWeakReferenceGet() throws Exception {
+        Reference weak = new WeakReference(mObj);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Object o = weak.get();
+        }
+    }
+
+    @Test
+    public void timeNonPreservedWeakReferenceGet() throws Exception {
+        Reference weak = new WeakReference(mObj);
+        mObj = null;
+        Runtime.getRuntime().gc();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            Object o = weak.get();
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
new file mode 100644
index 0000000..2ef68ca
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** Benchmark to evaluate the performance of References. */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ReferencePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private Object mObject;
+
+    // How fast can references can be allocated?
+    @Test
+    public void timeAlloc() {
+        ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            new PhantomReference(mObject, queue);
+        }
+    }
+
+    // How fast can references can be allocated and manually enqueued?
+    @Test
+    public void timeAllocAndEnqueue() {
+        ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            (new PhantomReference<Object>(mObject, queue)).enqueue();
+        }
+    }
+
+    // How fast can references can be allocated, enqueued, and polled?
+    @Test
+    public void timeAllocEnqueueAndPoll() {
+        ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            (new PhantomReference<Object>(mObject, queue)).enqueue();
+            queue.poll();
+        }
+    }
+
+    // How fast can references can be allocated, enqueued, and removed?
+    @Test
+    public void timeAllocEnqueueAndRemove() {
+        ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            (new PhantomReference<Object>(mObject, queue)).enqueue();
+            try {
+                queue.remove();
+            } catch (InterruptedException ie) {
+            }
+        }
+    }
+
+    private static class FinalizableObject {
+        AtomicInteger mCount;
+
+        FinalizableObject(AtomicInteger count) {
+            this.mCount = count;
+        }
+
+        @Override
+        protected void finalize() {
+            mCount.incrementAndGet();
+        }
+    }
+
+    // How fast does finalization run?
+    @Test
+    public void timeFinalization() {
+        // Allocate a bunch of finalizable objects.
+        int n = 0;
+        AtomicInteger count = new AtomicInteger(0);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            n++;
+            new FinalizableObject(count);
+        }
+
+        // Run GC so the objects will be collected for finalization.
+        Runtime.getRuntime().gc();
+
+        // Wait for finalization.
+        Runtime.getRuntime().runFinalization();
+
+        // Double check all the objects were finalized.
+        int got = count.get();
+        if (n != got) {
+            throw new IllegalStateException(
+                    String.format("Only %i of %i objects finalized?", got, n));
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
new file mode 100644
index 0000000..65a2fdb
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.util.Random;
+
+/**
+ * This measures performance of operations on small BigIntegers. We manually determine the number of
+ * iterations so that it should cause total memory allocation on the order of a few hundred
+ * megabytes. Due to BigInteger's reliance on finalization, these may unfortunately all be kept
+ * around at once.
+ *
+ * <p>This is not structured as a proper benchmark; just run main(), e.g. with vogar
+ * libcore/benchmarks/src/benchmarks/SmallBigIntegerBenchmark.java
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SmallBigIntegerPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+    // We allocate about 2 1/3 BigIntegers per iteration.
+    // Assuming 100 bytes/BigInteger, this gives us around 500MB total.
+    static final BigInteger BIG_THREE = BigInteger.valueOf(3);
+    static final BigInteger BIG_FOUR = BigInteger.valueOf(4);
+
+    @Test
+    public void testSmallBigInteger() {
+        final Random r = new Random();
+        BigInteger x = new BigInteger(20, r);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            // We know this converges, but the compiler doesn't.
+            if (x.and(BigInteger.ONE).equals(BigInteger.ONE)) {
+                x = x.multiply(BIG_THREE).add(BigInteger.ONE);
+            } else {
+                x = x.shiftRight(1);
+            }
+        }
+        if (x.signum() < 0 || x.compareTo(BIG_FOUR) > 0) {
+            throw new AssertionError("Something went horribly wrong.");
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
new file mode 100644
index 0000000..4f5c54d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** How long does it take to access a string in the dex cache? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class StringDexCachePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void timeStringDexCacheAccess() {
+        int v = 0;
+        int count = 0;
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            // Deliberately obscured to make optimizations less likely.
+            String s = (count >= 0) ? "hello, world!" : null;
+            v += s.length();
+            ++count;
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
new file mode 100644
index 0000000..08ad926
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** How do the various schemes for iterating through a string compare? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class StringIterationPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void timeStringIteration0() {
+        String s = "hello, world!";
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            char ch;
+            for (int i = 0; i < s.length(); ++i) {
+                ch = s.charAt(i);
+            }
+        }
+    }
+
+    @Test
+    public void timeStringIteration1() {
+        String s = "hello, world!";
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            char ch;
+            for (int i = 0, length = s.length(); i < length; ++i) {
+                ch = s.charAt(i);
+            }
+        }
+    }
+
+    @Test
+    public void timeStringIteration2() {
+        String s = "hello, world!";
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            char ch;
+            char[] chars = s.toCharArray();
+            for (int i = 0, length = chars.length; i < length; ++i) {
+                ch = chars[i];
+            }
+        }
+    }
+
+    @Test
+    public void timeStringToCharArray() {
+        String s = "hello, world!";
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            char[] chars = s.toCharArray();
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
new file mode 100644
index 0000000..5aacfc2
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class SystemArrayCopyPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Parameters(name = "arrayLength={0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(
+                new Object[][] {
+                    {2}, {4}, {8}, {16}, {32}, {64}, {128}, {256}, {512}, {1024}, {2048}, {4096},
+                    {8192}, {16384}, {32768}, {65536}, {131072}, {262144}
+                });
+    }
+
+    @Parameterized.Parameter(0)
+    public int arrayLength;
+
+    // Provides benchmarking for different types of arrays using the arraycopy function.
+    @Test
+    public void timeSystemCharArrayCopy() {
+        final int len = arrayLength;
+        char[] src = new char[len];
+        char[] dst = new char[len];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            System.arraycopy(src, 0, dst, 0, len);
+        }
+    }
+
+    @Test
+    public void timeSystemByteArrayCopy() {
+        final int len = arrayLength;
+        byte[] src = new byte[len];
+        byte[] dst = new byte[len];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            System.arraycopy(src, 0, dst, 0, len);
+        }
+    }
+
+    @Test
+    public void timeSystemShortArrayCopy() {
+        final int len = arrayLength;
+        short[] src = new short[len];
+        short[] dst = new short[len];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            System.arraycopy(src, 0, dst, 0, len);
+        }
+    }
+
+    @Test
+    public void timeSystemIntArrayCopy() {
+        final int len = arrayLength;
+        int[] src = new int[len];
+        int[] dst = new int[len];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            System.arraycopy(src, 0, dst, 0, len);
+        }
+    }
+
+    @Test
+    public void timeSystemLongArrayCopy() {
+        final int len = arrayLength;
+        long[] src = new long[len];
+        long[] dst = new long[len];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            System.arraycopy(src, 0, dst, 0, len);
+        }
+    }
+
+    @Test
+    public void timeSystemFloatArrayCopy() {
+        final int len = arrayLength;
+        float[] src = new float[len];
+        float[] dst = new float[len];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            System.arraycopy(src, 0, dst, 0, len);
+        }
+    }
+
+    @Test
+    public void timeSystemDoubleArrayCopy() {
+        final int len = arrayLength;
+        double[] src = new double[len];
+        double[] dst = new double[len];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            System.arraycopy(src, 0, dst, 0, len);
+        }
+    }
+
+    @Test
+    public void timeSystemBooleanArrayCopy() {
+        final int len = arrayLength;
+        boolean[] src = new boolean[len];
+        boolean[] dst = new boolean[len];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            System.arraycopy(src, 0, dst, 0, len);
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
new file mode 100644
index 0000000..7e71976
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Is there a performance reason to "Prefer virtual over interface", as the Android documentation
+ * once claimed?
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class VirtualVersusInterfacePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void timeMapPut() {
+        Map<String, String> map = new HashMap<String, String>();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            map.put("hello", "world");
+        }
+    }
+
+    @Test
+    public void timeHashMapPut() {
+        HashMap<String, String> map = new HashMap<String, String>();
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            map.put("hello", "world");
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
new file mode 100644
index 0000000..eec0734
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.CharArrayWriter;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Random;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class XmlSerializePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Parameters(name = "mDatasetAsString({0}), mSeed({1})")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(
+                new Object[][] {
+                    {"0.99 0.7 0.7 0.7 0.7 0.7", 854328},
+                    {"0.999 0.3 0.3 0.95 0.9 0.9", 854328},
+                    {"0.99 0.7 0.7 0.7 0.7 0.7", 312547},
+                    {"0.999 0.3 0.3 0.95 0.9 0.9", 312547}
+                });
+    }
+
+    @Parameterized.Parameter(0)
+    public String mDatasetAsString;
+
+    @Parameterized.Parameter(1)
+    public int mSeed;
+
+    double[] mDataset;
+    private Constructor<? extends XmlSerializer> mKxmlConstructor;
+    private Constructor<? extends XmlSerializer> mFastConstructor;
+
+    private void serializeRandomXml(Constructor<? extends XmlSerializer> ctor, long mSeed)
+            throws Exception {
+        double contChance = mDataset[0];
+        double levelUpChance = mDataset[1];
+        double levelDownChance = mDataset[2];
+        double attributeChance = mDataset[3];
+        double writeChance1 = mDataset[4];
+        double writeChance2 = mDataset[5];
+
+        XmlSerializer serializer = (XmlSerializer) ctor.newInstance();
+
+        CharArrayWriter w = new CharArrayWriter();
+        serializer.setOutput(w);
+        int level = 0;
+        Random r = new Random(mSeed);
+        char[] toWrite = {'a', 'b', 'c', 'd', 's', 'z'};
+        serializer.startDocument("UTF-8", true);
+        while (r.nextDouble() < contChance) {
+            while (level > 0 && r.nextDouble() < levelUpChance) {
+                serializer.endTag("aaaaaa", "bbbbbb");
+                level--;
+            }
+            while (r.nextDouble() < levelDownChance) {
+                serializer.startTag("aaaaaa", "bbbbbb");
+                level++;
+            }
+            serializer.startTag("aaaaaa", "bbbbbb");
+            level++;
+            while (r.nextDouble() < attributeChance) {
+                serializer.attribute("aaaaaa", "cccccc", "dddddd");
+            }
+            serializer.endTag("aaaaaa", "bbbbbb");
+            level--;
+            while (r.nextDouble() < writeChance1) serializer.text(toWrite, 0, 5);
+            while (r.nextDouble() < writeChance2) serializer.text("Textxtsxtxtxt ");
+        }
+        serializer.endDocument();
+    }
+
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() throws Exception {
+        mKxmlConstructor =
+                (Constructor)
+                        Class.forName("com.android.org.kxml2.io.KXmlSerializer").getConstructor();
+        mFastConstructor =
+                (Constructor)
+                        Class.forName("com.android.internal.util.FastXmlSerializer")
+                                .getConstructor();
+        String[] splitStrings = mDatasetAsString.split(" ");
+        mDataset = new double[splitStrings.length];
+        for (int i = 0; i < splitStrings.length; i++) {
+            mDataset[i] = Double.parseDouble(splitStrings[i]);
+        }
+    }
+
+    private void internalTimeSerializer(Constructor<? extends XmlSerializer> ctor)
+            throws Exception {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            serializeRandomXml(ctor, mSeed);
+        }
+    }
+
+    @Test
+    public void timeKxml() throws Exception {
+        internalTimeSerializer(mKxmlConstructor);
+    }
+
+    @Test
+    public void timeFast() throws Exception {
+        internalTimeSerializer(mFastConstructor);
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
new file mode 100644
index 0000000..517e3ce
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class ZipFilePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private File mFile;
+
+    @Parameters(name = "numEntries={0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {{128}, {1024}, {8192}});
+    }
+
+    @Parameterized.Parameter(0)
+    public int numEntries;
+
+    @Before
+    public void setUp() throws Exception {
+        mFile = File.createTempFile(getClass().getName(), ".zip");
+        mFile.deleteOnExit();
+        writeEntries(new ZipOutputStream(new FileOutputStream(mFile)), numEntries, 0);
+        ZipFile zipFile = new ZipFile(mFile);
+        for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+            ZipEntry zipEntry = e.nextElement();
+        }
+        zipFile.close();
+    }
+
+    @Test
+    public void timeZipFileOpen() throws Exception {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            ZipFile zf = new ZipFile(mFile);
+        }
+    }
+
+    /** Compresses the given number of files, each of the given size, into a .zip archive. */
+    protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize)
+            throws IOException {
+        byte[] writeBuffer = new byte[8192];
+        Random random = new Random();
+        try {
+            for (int entry = 0; entry < entryCount; ++entry) {
+                ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+                ze.setSize(entrySize);
+                out.putNextEntry(ze);
+
+                for (long i = 0; i < entrySize; i += writeBuffer.length) {
+                    random.nextBytes(writeBuffer);
+                    int byteCount = (int) Math.min(writeBuffer.length, entrySize - i);
+                    out.write(writeBuffer, 0, byteCount);
+                }
+
+                out.closeEntry();
+            }
+        } finally {
+            out.close();
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
new file mode 100644
index 0000000..faa9628
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2017 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 android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class ZipFileReadPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Parameters(name = "readBufferSize={0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {{1024}, {16384}, {65536}});
+    }
+
+    private File mFile;
+
+    @Parameterized.Parameter(0)
+    public int readBufferSize;
+
+    @Before
+    public void setUp() throws Exception {
+        mFile = File.createTempFile(getClass().getName(), ".zip");
+        writeEntries(new ZipOutputStream(new FileOutputStream(mFile)), 2, 1024 * 1024);
+        ZipFile zipFile = new ZipFile(mFile);
+        for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+            ZipEntry zipEntry = e.nextElement();
+        }
+        zipFile.close();
+    }
+
+    /** Compresses the given number of files, each of the given size, into a .zip archive. */
+    protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize)
+            throws IOException {
+        byte[] writeBuffer = new byte[8192];
+        Random random = new Random();
+        try {
+            for (int entry = 0; entry < entryCount; ++entry) {
+                ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+                ze.setSize(entrySize);
+                out.putNextEntry(ze);
+
+                for (long i = 0; i < entrySize; i += writeBuffer.length) {
+                    random.nextBytes(writeBuffer);
+                    int byteCount = (int) Math.min(writeBuffer.length, entrySize - i);
+                    out.write(writeBuffer, 0, byteCount);
+                }
+
+                out.closeEntry();
+            }
+        } finally {
+            out.close();
+        }
+    }
+
+    @Test
+    public void timeZipFileRead() throws Exception {
+        byte[] readBuffer = new byte[readBufferSize];
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            ZipFile zipFile = new ZipFile(mFile);
+            for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+                ZipEntry zipEntry = e.nextElement();
+                InputStream is = zipFile.getInputStream(zipEntry);
+                while (true) {
+                    if (is.read(readBuffer, 0, readBuffer.length) < 0) {
+                        break;
+                    }
+                }
+            }
+            zipFile.close();
+        }
+    }
+}
diff --git a/apex/jobscheduler/OWNERS b/apex/jobscheduler/OWNERS
index c77ea33..58434f1 100644
--- a/apex/jobscheduler/OWNERS
+++ b/apex/jobscheduler/OWNERS
@@ -1,7 +1,9 @@
 ctate@android.com
 ctate@google.com
 dplotnikov@google.com
+jji@google.com
 kwekua@google.com
 omakoto@google.com
 suprabh@google.com
+varunshah@google.com
 yamasani@google.com
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index db45466..6a877f9 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4658,6 +4658,11 @@
     }
 
     /** @hide */
+    public static boolean isProcStateConsideredInteraction(@ProcessState int procState) {
+        return (procState <= PROCESS_STATE_TOP || procState == PROCESS_STATE_BOUND_TOP);
+    }
+
+    /** @hide */
     public static String procStateToString(int procState) {
         final String procStateStr;
         switch (procState) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4c30f56..7ec3619 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -209,6 +209,12 @@
     public abstract void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq);
 
     /**
+     * Inform ActivityManagerService about the latest {@code blockedReasons} for an uid, which
+     * can be used to understand whether the {@code uid} is allowed to access network or not.
+     */
+    public abstract void onUidBlockedReasonsChanged(int uid, int blockedReasons);
+
+    /**
      * @return true if runtime was restarted, false if it's normal boot
      */
     public abstract boolean isRuntimeRestarted();
@@ -581,7 +587,7 @@
      * @param uid uid
      * @param pid pid of the ProcessRecord that is pending top.
      */
-    public abstract void addPendingTopUid(int uid, int pid);
+    public abstract void addPendingTopUid(int uid, int pid, @Nullable IApplicationThread thread);
 
     /**
      * Delete uid from the ActivityManagerService PendingStartActivityUids list.
@@ -680,4 +686,15 @@
          */
         void notifyActivityEventChanged();
     }
+
+    /**
+     * Register the UidObserver for NetworkPolicyManager service.
+     *
+     * This is equivalent to calling
+     * {@link IActivityManager#registerUidObserver(IUidObserver, int, int, String)} but having a
+     * separate method for NetworkPolicyManager service so that it's UidObserver can be called
+     * separately outside the usual UidObserver flow.
+     */
+    public abstract void registerNetworkPolicyUidObserver(@NonNull IUidObserver observer,
+            int which, int cutpoint, @NonNull String callingPackage);
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 48edb2e..8bbfd8d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7026,7 +7026,13 @@
                 // local, we'll need to wait for the publishing of the provider.
                 if (holder != null && holder.provider == null && !holder.mLocal) {
                     synchronized (key.mLock) {
-                        key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
+                        if (key.mHolder != null) {
+                            if (DEBUG_PROVIDER) {
+                                Slog.i(TAG, "already received provider: " + auth);
+                            }
+                        } else {
+                            key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
+                        }
                         holder = key.mHolder;
                     }
                     if (holder != null && holder.provider == null) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bde612e..402007d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -463,7 +463,7 @@
 
     /**
      * @hide Flag for {@link #bindService}: For only the case where the binding
-     * is coming from the system, set the process state to FOREGROUND_SERVICE
+     * is coming from the system, set the process state to BOUND_FOREGROUND_SERVICE
      * instead of the normal maximum of IMPORTANT_FOREGROUND.  That is, this is
      * saying that the process shouldn't participate in the normal power reduction
      * modes (removing network access etc).
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 660368a..1c9713d 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -1,7 +1,8 @@
 # Remain no owner because multiple modules may touch this file.
 per-file Context.java = *
 per-file ContextWrapper.java = *
-per-file Content* = file:/services/core/java/com/android/server/am/OWNERS
+per-file *Content* = file:/services/core/java/com/android/server/am/OWNERS
+per-file *Sync* = file:/services/core/java/com/android/server/am/OWNERS
 per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS
 per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS
 per-file Intent.java = file:/PACKAGE_MANAGER_OWNERS
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 4b35b0d..16ad0b8 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -396,7 +396,8 @@
                 return true;
             case MATCH_CARRIER:
             case MATCH_MOBILE:
-                return !template.getSubscriberIds().isEmpty();
+                return !template.getSubscriberIds().isEmpty()
+                        && template.getMeteredness() == METERED_YES;
             case MATCH_WIFI:
                 if (template.getWifiNetworkKeys().isEmpty()
                         && template.getSubscriberIds().isEmpty()) {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2c2a703..9341105 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -817,11 +817,13 @@
     public static final class UidState {
         public int uid;
         public int procState;
+        public long procStateSeq;
         public int capability;
 
-        public UidState(int uid, int procState, int capability) {
+        public UidState(int uid, int procState, long procStateSeq, int capability) {
             this.uid = uid;
             this.procState = procState;
+            this.procStateSeq = procStateSeq;
             this.capability = capability;
         }
 
@@ -830,6 +832,8 @@
             final StringBuilder sb = new StringBuilder();
             sb.append("{procState=");
             sb.append(procStateToString(procState));
+            sb.append(",seq=");
+            sb.append(procStateSeq);
             sb.append(",cap=");
             ActivityManager.printCapabilitiesSummary(sb, capability);
             sb.append("}");
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index ae7d91f..37eb74a 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -187,14 +187,24 @@
     /**
      * The network that was underlying the VPN when the event occurred, as a {@link Network}.
      *
-     * This extra will be null if there was no underlying network at the time of the event.
+     * <p>This extra will be null if there was no underlying network at the time of the event, or
+     *    the underlying network has no bearing on the event, as in the case of:
+     * <ul>
+     *   <li>CATEGORY_EVENT_DEACTIVATED_BY_USER
+     *   <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED
+     * </ul>
      */
     public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK";
 
     /**
      * The {@link NetworkCapabilities} of the underlying network when the event occurred.
      *
-     * This extra will be null if there was no underlying network at the time of the event.
+     * <p>This extra will be null if there was no underlying network at the time of the event, or
+     *    the underlying network has no bearing on the event, as in the case of:
+     * <ul>
+     *   <li>CATEGORY_EVENT_DEACTIVATED_BY_USER
+     *   <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED
+     * </ul>
      */
     public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES =
             "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES";
@@ -202,7 +212,12 @@
     /**
      * The {@link LinkProperties} of the underlying network when the event occurred.
      *
-     * This extra will be null if there was no underlying network at the time of the event.
+     * <p>This extra will be null if there was no underlying network at the time of the event, or
+     *    the underlying network has no bearing on the event, as in the case of:
+     * <ul>
+     *   <li>CATEGORY_EVENT_DEACTIVATED_BY_USER
+     *   <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED
+     * </ul>
      */
     public static final String EXTRA_UNDERLYING_LINK_PROPERTIES =
             "android.net.extra.UNDERLYING_LINK_PROPERTIES";
diff --git a/core/java/android/net/VpnProfileState.java b/core/java/android/net/VpnProfileState.java
index c69ea1a..0f21a9d 100644
--- a/core/java/android/net/VpnProfileState.java
+++ b/core/java/android/net/VpnProfileState.java
@@ -24,6 +24,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.StringJoiner;
 
 /**
  * Describe the state of VPN.
@@ -150,4 +151,29 @@
         mAlwaysOn = in.readBoolean();
         mLockdown = in.readBoolean();
     }
+
+    private String convertStateToString(@State int state) {
+        switch (state) {
+            case STATE_CONNECTED:
+                return "CONNECTED";
+            case STATE_CONNECTING:
+                return "CONNECTING";
+            case STATE_DISCONNECTED:
+                return "DISCONNECTED";
+            case STATE_FAILED:
+                return "FAILED";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
+    @Override
+    public String toString() {
+        final StringJoiner resultJoiner = new StringJoiner(", ", "{", "}");
+        resultJoiner.add("State: " + convertStateToString(getState()));
+        resultJoiner.add("SessionId: " + getSessionId());
+        resultJoiner.add("Always-on: " + isAlwaysOn());
+        resultJoiner.add("Lockdown: " + isLockdownEnabled());
+        return resultJoiner.toString();
+    }
 }
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index f1b110a..40e4083 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -104,6 +104,14 @@
 
     // TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz
 
+    /** List of Carrier Config options to extract from Carrier Config bundles. @hide */
+    @NonNull
+    public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS =
+            new String[] {
+                VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+                VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY
+            };
+
     private static final Map<
                     VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
             REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 80ffd40..c33c2ab 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -2263,8 +2263,11 @@
     }
 
     /**
-     * {@link #getX(int)} for the first pointer index (may be an
-     * arbitrary pointer identifier).
+     * Equivalent to {@link #getX(int)} for pointer index 0 (regardless of the
+     * pointer identifier).
+     *
+     * @return The X coordinate of the first pointer index in the coordinate
+     *      space of the view that received this motion event.
      *
      * @see #AXIS_X
      */
@@ -2273,8 +2276,11 @@
     }
 
     /**
-     * {@link #getY(int)} for the first pointer index (may be an
-     * arbitrary pointer identifier).
+     * Equivalent to {@link #getY(int)} for pointer index 0 (regardless of the
+     * pointer identifier).
+     *
+     * @return The Y coordinate of the first pointer index in the coordinate
+     *      space of the view that received this motion event.
      *
      * @see #AXIS_Y
      */
@@ -2416,13 +2422,20 @@
     }
 
     /**
-     * Returns the X coordinate of this event for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
-     * Whole numbers are pixels; the
-     * value may have a fraction for input devices that are sub-pixel precise.
-     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
-     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * Returns the X coordinate of the pointer referenced by
+     * {@code pointerIndex} for this motion event. The coordinate is in the
+     * coordinate space of the view that received this motion event.
+     *
+     * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+     * pointer referenced by {@code pointerIndex}.
+     *
+     * @param pointerIndex Index of the pointer for which the X coordinate is
+     *      returned. May be a value in the range of 0 (the first pointer that
+     *      is down) to {@link #getPointerCount()} - 1.
+     * @return The X coordinate of the pointer referenced by
+     *      {@code pointerIndex} for this motion event. The unit is pixels. The
+     *      value may contain a fractional portion for devices that are subpixel
+     *      precise.
      *
      * @see #AXIS_X
      */
@@ -2431,13 +2444,20 @@
     }
 
     /**
-     * Returns the Y coordinate of this event for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
-     * Whole numbers are pixels; the
-     * value may have a fraction for input devices that are sub-pixel precise.
-     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
-     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * Returns the Y coordinate of the pointer referenced by
+     * {@code pointerIndex} for this motion event. The coordinate is in the
+     * coordinate space of the view that received this motion event.
+     *
+     * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+     * pointer referenced by {@code pointerIndex}.
+     *
+     * @param pointerIndex Index of the pointer for which the Y coordinate is
+     *      returned. May be a value in the range of 0 (the first pointer that
+     *      is down) to {@link #getPointerCount()} - 1.
+     * @return The Y coordinate of the pointer referenced by
+     *      {@code pointerIndex} for this motion event. The unit is pixels. The
+     *      value may contain a fractional portion for devices that are subpixel
+     *      precise.
      *
      * @see #AXIS_Y
      */
@@ -2683,12 +2703,13 @@
     }
 
     /**
-     * Returns the original raw X coordinate of this event.  For touch
-     * events on the screen, this is the original location of the event
-     * on the screen, before it had been adjusted for the containing window
-     * and views.
+     * Equivalent to {@link #getRawX(int)} for pointer index 0 (regardless of
+     * the pointer identifier).
      *
-     * @see #getX(int)
+     * @return The X coordinate of the first pointer index in the coordinate
+     *      space of the device display.
+     *
+     * @see #getX()
      * @see #AXIS_X
      */
     public final float getRawX() {
@@ -2696,12 +2717,13 @@
     }
 
     /**
-     * Returns the original raw Y coordinate of this event.  For touch
-     * events on the screen, this is the original location of the event
-     * on the screen, before it had been adjusted for the containing window
-     * and views.
+     * Equivalent to {@link #getRawY(int)} for pointer index 0 (regardless of
+     * the pointer identifier).
      *
-     * @see #getY(int)
+     * @return The Y coordinate of the first pointer index in the coordinate
+     *      space of the device display.
+     *
+     * @see #getY()
      * @see #AXIS_Y
      */
     public final float getRawY() {
@@ -2709,13 +2731,38 @@
     }
 
     /**
-     * Returns the original raw X coordinate of this event.  For touch
-     * events on the screen, this is the original location of the event
-     * on the screen, before it had been adjusted for the containing window
-     * and views.
+     * Returns the X coordinate of the pointer referenced by
+     * {@code pointerIndex} for this motion event. The coordinate is in the
+     * coordinate space of the device display, irrespective of system
+     * decorations and whether or not the system is in multi-window mode. If the
+     * app spans multiple screens in a multiple-screen environment, the
+     * coordinate space includes all of the spanned screens.
      *
-     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
-     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * <p>In multi-window mode, the coordinate space extends beyond the bounds
+     * of the app window to encompass the entire display area. For example, if
+     * the motion event occurs in the right-hand window of split-screen mode in
+     * landscape orientation, the left edge of the screen&mdash;not the left
+     * edge of the window&mdash;is the origin from which the X coordinate is
+     * calculated.
+     *
+     * <p>In multiple-screen scenarios, the coordinate space can span screens.
+     * For example, if the app is spanning both screens of a dual-screen device,
+     * and the motion event occurs on the right-hand screen, the X coordinate is
+     * calculated from the left edge of the left-hand screen to the point of the
+     * motion event on the right-hand screen. When the app is restricted to a
+     * single screen in a multiple-screen environment, the coordinate space
+     * includes only the screen on which the app is running.
+     *
+     * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+     * pointer referenced by {@code pointerIndex}.
+     *
+     * @param pointerIndex Index of the pointer for which the X coordinate is
+     *      returned. May be a value in the range of 0 (the first pointer that
+     *      is down) to {@link #getPointerCount()} - 1.
+     * @return The X coordinate of the pointer referenced by
+     *      {@code pointerIndex} for this motion event. The unit is pixels. The
+     *      value may contain a fractional portion for devices that are subpixel
+     *      precise.
      *
      * @see #getX(int)
      * @see #AXIS_X
@@ -2725,13 +2772,38 @@
     }
 
     /**
-     * Returns the original raw Y coordinate of this event.  For touch
-     * events on the screen, this is the original location of the event
-     * on the screen, before it had been adjusted for the containing window
-     * and views.
+     * Returns the Y coordinate of the pointer referenced by
+     * {@code pointerIndex} for this motion event. The coordinate is in the
+     * coordinate space of the device display, irrespective of system
+     * decorations and whether or not the system is in multi-window mode. If the
+     * app spans multiple screens in a multiple-screen environment, the
+     * coordinate space includes all of the spanned screens.
      *
-     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
-     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     * <p>In multi-window mode, the coordinate space extends beyond the bounds
+     * of the app window to encompass the entire device screen. For example, if
+     * the motion event occurs in the lower window of split-screen mode in
+     * portrait orientation, the top edge of the screen&mdash;not the top edge
+     * of the window&mdash;is the origin from which the Y coordinate is
+     * determined.
+     *
+     * <p>In multiple-screen scenarios, the coordinate space can span screens.
+     * For example, if the app is spanning both screens of a dual-screen device
+     * that's rotated 90 degrees, and the motion event occurs on the lower
+     * screen, the Y coordinate is calculated from the top edge of the upper
+     * screen to the point of the motion event on the lower screen. When the app
+     * is restricted to a single screen in a multiple-screen environment, the
+     * coordinate space includes only the screen on which the app is running.
+     *
+     * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+     * pointer referenced by {@code pointerIndex}.
+     *
+     * @param pointerIndex Index of the pointer for which the Y coordinate is
+     *      returned. May be a value in the range of 0 (the first pointer that
+     *      is down) to {@link #getPointerCount()} - 1.
+     * @return The Y coordinate of the pointer referenced by
+     *      {@code pointerIndex} for this motion event. The unit is pixels. The
+     *      value may contain a fractional portion for devices that are subpixel
+     *      precise.
      *
      * @see #getY(int)
      * @see #AXIS_Y
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f2ddf52..ffa577a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28283,7 +28283,7 @@
      *     {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be
      *     available through {@link MotionEvent#getX} and {@link MotionEvent#getY}.</li>
      *
-     *     <li>Events from a touchpad will be delivered with the source
+     *     <li>Events from a touchpad or trackpad will be delivered with the source
      *     {@link InputDevice#SOURCE_TOUCHPAD}, where the absolute position of each of the pointers
      *     on the touchpad will be available through {@link MotionEvent#getX(int)} and
      *     {@link MotionEvent#getY(int)}, and their relative movements are stored in
@@ -28292,6 +28292,12 @@
      *     <li>Events from other types of devices, such as touchscreens, will not be affected.</li>
      * </ul>
      * <p>
+     * When pointer capture changes, connected mouse and trackpad devices may be reconfigured,
+     * and their properties (such as their sources or motion ranges) may change. Use an
+     * {@link android.hardware.input.InputManager.InputDeviceListener} to be notified when a device
+     * changes (which may happen after enabling or disabling pointer capture), and use
+     * {@link InputDevice#getDevice(int)} to get the updated {@link InputDevice}.
+     * <p>
      * Events captured through pointer capture will be dispatched to
      * {@link OnCapturedPointerListener#onCapturedPointer(View, MotionEvent)} if an
      * {@link OnCapturedPointerListener} is set, and otherwise to
diff --git a/core/java/com/android/internal/usb/OWNERS b/core/java/com/android/internal/usb/OWNERS
new file mode 100644
index 0000000..f7b2a37
--- /dev/null
+++ b/core/java/com/android/internal/usb/OWNERS
@@ -0,0 +1 @@
+include /services/usb/OWNERS
diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h
index 9098d46..d73db62 100644
--- a/core/jni/android_util_Binder.h
+++ b/core/jni/android_util_Binder.h
@@ -24,8 +24,18 @@
 
 namespace android {
 
-// Converstion to/from Java IBinder Object and C++ IBinder instance.
+/**
+ * Conversion to Java IBinder Object from C++ IBinder instance.
+ *
+ * WARNING: this function returns global and local references. This can be
+ * figured out using GetObjectRefType. Though, when this function is called
+ * from within a Java context, the local ref will automatically be cleaned
+ * up. If this is called outside of a Java frame,
+ * PushObjectFrame/PopObjectFrame can simulate this automatic cleanup. The
+ * platform provides ScopedLocalFrame as an RAII object for this.
+ */
 extern jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val);
+/** Conversion from Java IBinder Object to C++ IBinder instance. */
 extern sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj);
 
 extern jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5023927..86e97d5 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -110,6 +110,8 @@
 
 using android::zygote::ZygoteFailure;
 
+using Action = android_mallopt_gwp_asan_options_t::Action;
+
 // This type is duplicated in fd_utils.h
 typedef const std::function<void(std::string)>& fail_fn_t;
 
@@ -1597,10 +1599,11 @@
     // since the directory is owned by root.
     if (!is_system_server && getuid() == 0) {
         const int rc = createProcessGroup(uid, getpid());
-        if (rc == -EROFS) {
-            ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
-        } else if (rc != 0) {
-            ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
+        if (rc != 0) {
+            fail_fn(rc == -EROFS ? CREATE_ERROR("createProcessGroup failed, kernel missing "
+                                                "CONFIG_CGROUP_CPUACCT?")
+                                 : CREATE_ERROR("createProcessGroup(%d, %d) failed: %s", uid,
+                                                /* pid= */ 0, strerror(-rc)));
         }
     }
 
@@ -1717,16 +1720,24 @@
     // runtime.
     runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT;
 
-    bool forceEnableGwpAsan = false;
+    const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
+    android_mallopt_gwp_asan_options_t gwp_asan_options;
+    // The system server doesn't have its nice name set by the time SpecializeCommon is called.
+    gwp_asan_options.program_name = nice_name_ptr ?: process_name;
     switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
         default:
         case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
+            gwp_asan_options.desire = Action::DONT_TURN_ON_UNLESS_OVERRIDDEN;
+            android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
             break;
         case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS:
-            forceEnableGwpAsan = true;
-            [[fallthrough]];
+            gwp_asan_options.desire = Action::TURN_ON_FOR_APP;
+            android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
+            break;
         case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
-            android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
+            gwp_asan_options.desire = Action::TURN_ON_WITH_SAMPLING;
+            android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
+            break;
     }
     // Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
     // runtime.
@@ -1739,7 +1750,6 @@
     AStatsSocket_close();
 
     const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
-    const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
 
     if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
         fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 679a4f0..add645de 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -296,7 +296,7 @@
   ++buffersAllocd;
   // MMap explicitly to get it page aligned.
   void *bufferMem = mmap(NULL, sizeof(NativeCommandBuffer), PROT_READ | PROT_WRITE,
-                         MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0);
+                         MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
   // Currently we mmap and unmap one for every request handled by the Java code.
   // That could be improved, but unclear it matters.
   if (bufferMem == MAP_FAILED) {
diff --git a/graphics/TEST_MAPPING b/graphics/TEST_MAPPING
index 10bd0ee..abeaf19 100644
--- a/graphics/TEST_MAPPING
+++ b/graphics/TEST_MAPPING
@@ -1,7 +1,12 @@
 {
   "presubmit": [
     {
-      "name": "CtsGraphicsTestCases"
+      "name": "CtsGraphicsTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
     }
   ]
 }
diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java
index 2e54e63..6981332 100644
--- a/keystore/java/android/security/GenerateRkpKey.java
+++ b/keystore/java/android/security/GenerateRkpKey.java
@@ -16,6 +16,8 @@
 
 package android.security;
 
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -24,6 +26,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -57,6 +61,21 @@
     private Context mContext;
     private CountDownLatch mCountDownLatch;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, value = {
+            IGenerateRkpKeyService.Status.OK,
+            IGenerateRkpKeyService.Status.NO_NETWORK_CONNECTIVITY,
+            IGenerateRkpKeyService.Status.NETWORK_COMMUNICATION_ERROR,
+            IGenerateRkpKeyService.Status.DEVICE_NOT_REGISTERED,
+            IGenerateRkpKeyService.Status.HTTP_CLIENT_ERROR,
+            IGenerateRkpKeyService.Status.HTTP_SERVER_ERROR,
+            IGenerateRkpKeyService.Status.HTTP_UNKNOWN_ERROR,
+            IGenerateRkpKeyService.Status.INTERNAL_ERROR,
+    })
+    public @interface Status {
+    }
+
     private ServiceConnection mConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName className, IBinder service) {
@@ -81,12 +100,14 @@
         mContext = context;
     }
 
-    private void bindAndSendCommand(int command, int securityLevel) throws RemoteException {
+    @Status
+    private int bindAndSendCommand(int command, int securityLevel) throws RemoteException {
         Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
         ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        int returnCode = IGenerateRkpKeyService.Status.OK;
         if (comp == null) {
             // On a system that does not use RKP, the RemoteProvisioner app won't be installed.
-            return;
+            return returnCode;
         }
         intent.setComponent(comp);
         mCountDownLatch = new CountDownLatch(1);
@@ -102,7 +123,7 @@
         if (mBinder != null) {
             switch (command) {
                 case NOTIFY_EMPTY:
-                    mBinder.generateKey(securityLevel);
+                    returnCode = mBinder.generateKey(securityLevel);
                     break;
                 case NOTIFY_KEY_GENERATED:
                     mBinder.notifyKeyGenerated(securityLevel);
@@ -112,16 +133,21 @@
             }
         } else {
             Log.e(TAG, "Binder object is null; failed to bind to GenerateRkpKeyService.");
+            returnCode = IGenerateRkpKeyService.Status.INTERNAL_ERROR;
         }
         mContext.unbindService(mConnection);
+        return returnCode;
     }
 
     /**
      * Fulfills the use case of (2) described in the class documentation. Blocks until the
      * RemoteProvisioner application can get new attestation keys signed by the server.
+     * @return the status of the key generation
      */
-    public void notifyEmpty(int securityLevel) throws RemoteException {
-        bindAndSendCommand(NOTIFY_EMPTY, securityLevel);
+    @CheckResult
+    @Status
+    public int notifyEmpty(int securityLevel) throws RemoteException {
+        return bindAndSendCommand(NOTIFY_EMPTY, securityLevel);
     }
 
     /**
diff --git a/keystore/java/android/security/IGenerateRkpKeyService.aidl b/keystore/java/android/security/IGenerateRkpKeyService.aidl
index 5f1d669..eeaeb27 100644
--- a/keystore/java/android/security/IGenerateRkpKeyService.aidl
+++ b/keystore/java/android/security/IGenerateRkpKeyService.aidl
@@ -26,11 +26,35 @@
  * @hide
  */
 interface IGenerateRkpKeyService {
+    @JavaDerive(toString=true)
+    @Backing(type="int")
+    enum Status {
+        /** No error(s) occurred */
+        OK = 0,
+        /** Unable to provision keys due to a lack of internet connectivity. */
+        NO_NETWORK_CONNECTIVITY = 1,
+        /** An error occurred while communicating with the RKP server. */
+        NETWORK_COMMUNICATION_ERROR = 2,
+        /** The given device was not registered with the RKP backend. */
+        DEVICE_NOT_REGISTERED = 4,
+        /** The RKP server returned an HTTP client error, indicating a misbehaving client. */
+        HTTP_CLIENT_ERROR = 5,
+        /** The RKP server returned an HTTP server error, indicating something went wrong on the server. */
+        HTTP_SERVER_ERROR = 6,
+        /** The RKP server returned an HTTP status that is unknown. This should never happen. */
+        HTTP_UNKNOWN_ERROR = 7,
+        /** An unexpected internal error occurred. This should never happen. */
+        INTERNAL_ERROR = 8,
+    }
+
     /**
      * Ping the provisioner service to let it know an app generated a key. This may or may not have
      * consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check.
      */
     oneway void notifyKeyGenerated(in int securityLevel);
-    /** Ping the provisioner service to indicate there are no remaining attestation keys left. */
-    void generateKey(in int securityLevel);
+
+    /**
+     * Ping the provisioner service to indicate there are no remaining attestation keys left.
+     */
+    Status generateKey(in int securityLevel);
 }
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 3d53cfb..c2cd6ff 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -345,6 +345,12 @@
                 case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
                     return new KeyStoreException(errorCode, "Key permanently invalidated",
                             serviceErrorMessage);
+                case ResponseCode.OUT_OF_KEYS:
+                    // Getting a more specific RKP status requires the security level, which we
+                    // don't have here. Higher layers of the stack can interpret this exception
+                    // and add more flavor.
+                    return new KeyStoreException(errorCode, serviceErrorMessage,
+                            KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE);
                 default:
                     return new KeyStoreException(errorCode, String.valueOf(errorCode),
                             serviceErrorMessage);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index 5950b5b..40659f5 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -28,6 +28,7 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.security.GenerateRkpKey;
+import android.security.IGenerateRkpKeyService;
 import android.security.KeyPairGeneratorSpec;
 import android.security.KeyStore2;
 import android.security.KeyStoreException;
@@ -624,7 +625,7 @@
              * GenerateRkpKey.notifyEmpty() will delay for a while before returning.
              */
             result = generateKeyPairHelper();
-            if (result.rkpStatus == KeyStoreException.RKP_SUCCESS) {
+            if (result.rkpStatus == KeyStoreException.RKP_SUCCESS && result.keyPair != null) {
                 return result.keyPair;
             }
         }
@@ -706,27 +707,12 @@
             success = true;
             KeyPair kp = new KeyPair(publicKey, publicKey.getPrivateKey());
             return new GenerateKeyPairHelperResult(0, kp);
-        } catch (android.security.KeyStoreException e) {
+        } catch (KeyStoreException e) {
             switch (e.getErrorCode()) {
                 case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
                     throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
                 case ResponseCode.OUT_OF_KEYS:
-                    GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread
-                            .currentApplication());
-                    try {
-                        //TODO: When detailed error information is available from the remote
-                        //provisioner, propagate it up.
-                        keyGen.notifyEmpty(securityLevel);
-                    } catch (RemoteException f) {
-                        KeyStoreException ksException = new KeyStoreException(
-                                ResponseCode.OUT_OF_KEYS,
-                                "Remote exception: " + f.getMessage(),
-                                KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE);
-                        throw new ProviderException("Failed to talk to RemoteProvisioner",
-                                ksException);
-                    }
-                    return new GenerateKeyPairHelperResult(
-                            KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE, null);
+                    throw makeOutOfKeysException(e, securityLevel);
                 default:
                     ProviderException p = new ProviderException("Failed to generate key pair.", e);
                     if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
@@ -752,6 +738,52 @@
         }
     }
 
+    // In case keystore reports OUT_OF_KEYS, call this handler in an attempt to remotely provision
+    // some keys.
+    private ProviderException makeOutOfKeysException(KeyStoreException e, int securityLevel) {
+        GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread
+                .currentApplication());
+        KeyStoreException ksException;
+        try {
+            final int keyGenStatus = keyGen.notifyEmpty(securityLevel);
+            // Default stance: temporary error. This is a hint to the caller to try again with
+            // exponential back-off.
+            int rkpStatus;
+            switch (keyGenStatus) {
+                case IGenerateRkpKeyService.Status.NO_NETWORK_CONNECTIVITY:
+                    rkpStatus = KeyStoreException.RKP_FETCHING_PENDING_CONNECTIVITY;
+                    break;
+                case IGenerateRkpKeyService.Status.DEVICE_NOT_REGISTERED:
+                    rkpStatus = KeyStoreException.RKP_SERVER_REFUSED_ISSUANCE;
+                    break;
+                case IGenerateRkpKeyService.Status.OK:
+                    // This will actually retry once immediately, so on "OK" go ahead and return
+                    // "temporarily unavailable". @see generateKeyPair
+                case IGenerateRkpKeyService.Status.NETWORK_COMMUNICATION_ERROR:
+                case IGenerateRkpKeyService.Status.HTTP_CLIENT_ERROR:
+                case IGenerateRkpKeyService.Status.HTTP_SERVER_ERROR:
+                case IGenerateRkpKeyService.Status.HTTP_UNKNOWN_ERROR:
+                case IGenerateRkpKeyService.Status.INTERNAL_ERROR:
+                default:
+                    // These errors really should never happen. The best we can do is assume they
+                    // are transient and hint to the caller to retry with back-off.
+                    rkpStatus = KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE;
+                    break;
+            }
+            ksException = new KeyStoreException(
+                    ResponseCode.OUT_OF_KEYS,
+                    "Out of RKP keys due to IGenerateRkpKeyService status: " + keyGenStatus,
+                    rkpStatus);
+        } catch (RemoteException f) {
+            ksException = new KeyStoreException(
+                    ResponseCode.OUT_OF_KEYS,
+                    "Remote exception: " + f.getMessage(),
+                    KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE);
+        }
+        ksException.initCause(e);
+        return new ProviderException("Failed to talk to RemoteProvisioner", ksException);
+    }
+
     private void addAttestationParameters(@NonNull List<KeyParameter> params)
             throws ProviderException, IllegalArgumentException, DeviceIdAttestationException {
         byte[] challenge = mSpec.getAttestationChallenge();
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 77709d7..5c75e47 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -3634,6 +3634,7 @@
                         .parseIntRange(info.getString("quality-range"), mQualityRange);
             }
             if (info.containsKey("feature-bitrate-modes")) {
+                mBitControl = 0;
                 for (String mode: info.getString("feature-bitrate-modes").split(",")) {
                     mBitControl |= (1 << parseBitrateMode(mode));
                 }
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 522b021..050ae72 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -1147,7 +1147,7 @@
      * A key describing the per-frame average block QP (Quantization Parameter).
      * This is a part of a video 'Encoding Statistics' export feature.
      * This value is emitted from video encoder for a video frame.
-     * The average value is rounded down (using floor()) to integer value.
+     * The average value is rounded to the nearest integer value.
      *
      * The associated value is an integer.
      */
diff --git a/omapi/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java
index f42ca36..306c09a 100644
--- a/omapi/java/android/se/omapi/SEService.java
+++ b/omapi/java/android/se/omapi/SEService.java
@@ -118,6 +118,16 @@
                 });
             }
         }
+
+        @Override
+        public String getInterfaceHash() {
+            return ISecureElementListener.HASH;
+        }
+
+        @Override
+        public int getInterfaceVersion() {
+            return ISecureElementListener.VERSION;
+        }
     }
     private SEListener mSEListener = new SEListener();
 
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 6c8a92d..4b07eaf 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -73,6 +73,7 @@
 zakcohen@google.com
 jernej@google.com
 jglazier@google.com
+peskal@google.com
 
 #Android Auto
 hseog@google.com
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 99e12a8..ce0e69c 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -91,6 +91,10 @@
                 if (!volume.isMountedWritable()) {
                     continue;
                 }
+                // gsid only supports vfat external storage.
+                if (!"vfat".equalsIgnoreCase(volume.fsType)) {
+                    continue;
+                }
                 DiskInfo disk = volume.getDisk();
                 long mega = disk.size >> 20;
                 Slog.i(TAG, volume.getPath() + ": " + mega + " MB");
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 81627a0..2ab477f 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1803,7 +1803,10 @@
             mComputedNightMode = false;
             return;
         }
-        resetNightModeOverrideLocked();
+        if (mNightMode != MODE_NIGHT_AUTO || (mTwilightManager != null
+                && mTwilightManager.getLastTwilightState() != null)) {
+            resetNightModeOverrideLocked();
+        }
     }
 
     private boolean resetNightModeOverrideLocked() {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index eefeee3..f26d9f9 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -52,6 +52,7 @@
 import android.net.vcn.VcnUnderlyingNetworkPolicy;
 import android.net.wifi.WifiInfo;
 import android.os.Binder;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -82,6 +83,7 @@
 import com.android.server.vcn.VcnNetworkProvider;
 import com.android.server.vcn.util.PersistableBundleUtils;
 
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -162,7 +164,8 @@
     public static final boolean VDBG = false; // STOPSHIP: if true
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
-    static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
+    static final String VCN_CONFIG_FILE =
+            new File(Environment.getDataSystemDirectory(), "vcn/configs.xml").getPath();
 
     /* Binder context for this service */
     @NonNull private final Context mContext;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0f450e1..ec9f1fe 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -28,6 +28,7 @@
 import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
 import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
 import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
 import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
 import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -45,6 +46,13 @@
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
@@ -1415,6 +1423,10 @@
     final ActivityThread mSystemThread;
 
     final UidObserverController mUidObserverController;
+    private volatile IUidObserver mNetworkPolicyUidObserver;
+
+    @GuardedBy("mUidNetworkBlockedReasons")
+    private final SparseIntArray mUidNetworkBlockedReasons = new SparseIntArray();
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
         final ProcessRecord mApp;
@@ -6784,6 +6796,9 @@
         }
 
         synchronized (mProcLock) {
+            if (mPendingStartActivityUids.isPendingTopUid(uid)) {
+                return PROCESS_STATE_TOP;
+            }
             return mProcessList.getUidProcStateLOSP(uid);
         }
     }
@@ -10375,7 +10390,7 @@
             "Native",
             "System", "Persistent", "Persistent Service", "Foreground",
             "Visible", "Perceptible", "Perceptible Low", "Perceptible Medium",
-            "Heavy Weight", "Backup",
+            "Backup", "Heavy Weight",
             "A Services", "Home",
             "Previous", "B Services", "Cached"
     };
@@ -10383,7 +10398,7 @@
             "native",
             "sys", "pers", "persvc", "fore",
             "vis", "percept", "perceptl", "perceptm",
-            "heavy", "backup",
+            "backup", "heavy",
             "servicea", "home",
             "prev", "serviceb", "cached"
     };
@@ -14447,7 +14462,6 @@
                 uid, change, procState, procStateSeq, capability, ephemeral);
         if (uidRec != null) {
             uidRec.setLastReportedChange(enqueuedChange);
-            uidRec.updateLastDispatchedProcStateSeq(enqueuedChange);
         }
 
         // Directly update the power manager, since we sit on top of it and it is critical
@@ -15585,18 +15599,13 @@
                     return;
                 }
                 record.lastNetworkUpdatedProcStateSeq = procStateSeq;
-                if (record.curProcStateSeq > procStateSeq) {
-                    if (DEBUG_NETWORK) {
-                        Slog.d(TAG_NETWORK, "No need to handle older seq no., Uid: " + uid
-                                + ", curProcstateSeq: " + record.curProcStateSeq
-                                + ", procStateSeq: " + procStateSeq);
-                    }
-                    return;
-                }
-                if (record.waitingForNetwork) {
+                if (record.procStateSeqWaitingForNetwork != 0
+                        && procStateSeq >= record.procStateSeqWaitingForNetwork) {
                     if (DEBUG_NETWORK) {
                         Slog.d(TAG_NETWORK, "Notifying all blocking threads for uid: " + uid
-                                + ", procStateSeq: " + procStateSeq);
+                                + ", procStateSeq: " + procStateSeq
+                                + ", procStateSeqWaitingForNetwork: "
+                                + record.procStateSeqWaitingForNetwork);
                     }
                     record.networkStateLock.notifyAll();
                 }
@@ -15604,6 +15613,17 @@
         }
 
         @Override
+        public void onUidBlockedReasonsChanged(int uid, int blockedReasons) {
+            synchronized (mUidNetworkBlockedReasons) {
+                if (blockedReasons == BLOCKED_REASON_NONE) {
+                    mUidNetworkBlockedReasons.delete(uid);
+                } else {
+                    mUidNetworkBlockedReasons.put(uid, blockedReasons);
+                }
+            }
+        }
+
+        @Override
         public boolean isRuntimeRestarted() {
             return mSystemServiceManager.isRuntimeRestarted();
         }
@@ -16301,8 +16321,51 @@
         }
 
         @Override
-        public void addPendingTopUid(int uid, int pid) {
-            mPendingStartActivityUids.add(uid, pid);
+        public void addPendingTopUid(int uid, int pid, @Nullable IApplicationThread thread) {
+            final boolean isNewPending = mPendingStartActivityUids.add(uid, pid);
+            // We need to update the network rules for the app coming to the top state so that
+            // it can access network when the device or the app is in a restricted state
+            // (e.g. battery/data saver) but since waiting for updateOomAdj to complete and then
+            // informing NetworkPolicyManager might get delayed, informing the state change as soon
+            // as we know app is going to come to the top state.
+            if (isNewPending && mNetworkPolicyUidObserver != null) {
+                try {
+                    final long procStateSeq = mProcessList.getNextProcStateSeq();
+                    mNetworkPolicyUidObserver.onUidStateChanged(uid, PROCESS_STATE_TOP,
+                            procStateSeq, PROCESS_CAPABILITY_ALL);
+                    if (thread != null && isNetworkingBlockedForUid(uid)) {
+                        thread.setNetworkBlockSeq(procStateSeq);
+                    }
+                } catch (RemoteException e) {
+                    Slog.d(TAG, "Error calling setNetworkBlockSeq", e);
+                }
+            }
+        }
+
+        private boolean isNetworkingBlockedForUid(int uid) {
+            synchronized (mUidNetworkBlockedReasons) {
+                // TODO: We can consider only those blocked reasons that will be overridden
+                // by the TOP state. For other ones, there is no point in waiting.
+                // TODO: We can reuse this data in
+                // ProcessList#incrementProcStateSeqAndNotifyAppsLOSP instead of calling into
+                // NetworkManagementService.
+                final int uidBlockedReasons = mUidNetworkBlockedReasons.get(
+                        uid, BLOCKED_REASON_NONE);
+                if (uidBlockedReasons == BLOCKED_REASON_NONE) {
+                    return false;
+                }
+                final int topExemptedBlockedReasons = BLOCKED_REASON_BATTERY_SAVER
+                        | BLOCKED_REASON_DOZE
+                        | BLOCKED_REASON_APP_STANDBY
+                        | BLOCKED_REASON_LOW_POWER_STANDBY
+                        | BLOCKED_METERED_REASON_DATA_SAVER
+                        | BLOCKED_METERED_REASON_USER_RESTRICTED;
+                final int effectiveBlockedReasons =
+                        uidBlockedReasons & ~topExemptedBlockedReasons;
+                // Only consider it as blocked if it is not blocked by a reason
+                // that is not exempted by app being in the top state.
+                return effectiveBlockedReasons == BLOCKED_REASON_NONE;
+            }
         }
 
         @Override
@@ -16449,6 +16512,14 @@
         public void setStopUserOnSwitch(int value) {
             ActivityManagerService.this.setStopUserOnSwitch(value);
         }
+
+        @Override
+        public void registerNetworkPolicyUidObserver(@NonNull IUidObserver observer,
+                int which, int cutpoint, @NonNull String callingPackage) {
+            mNetworkPolicyUidObserver = observer;
+            mUidObserverController.register(observer, which, cutpoint, callingPackage,
+                    Binder.getCallingUid());
+        }
     }
 
     long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -16528,23 +16599,6 @@
             }
         }
         synchronized (record.networkStateLock) {
-            if (record.lastDispatchedProcStateSeq < procStateSeq) {
-                if (DEBUG_NETWORK) {
-                    Slog.d(TAG_NETWORK, "Uid state change for seq no. " + procStateSeq + " is not "
-                            + "dispatched to NPMS yet, so don't wait. Uid: " + callingUid
-                            + " lastProcStateSeqDispatchedToObservers: "
-                            + record.lastDispatchedProcStateSeq);
-                }
-                return;
-            }
-            if (record.curProcStateSeq > procStateSeq) {
-                if (DEBUG_NETWORK) {
-                    Slog.d(TAG_NETWORK, "Ignore the wait requests for older seq numbers. Uid: "
-                            + callingUid + ", curProcStateSeq: " + record.curProcStateSeq
-                            + ", procStateSeq: " + procStateSeq);
-                }
-                return;
-            }
             if (record.lastNetworkUpdatedProcStateSeq >= procStateSeq) {
                 if (DEBUG_NETWORK) {
                     Slog.d(TAG_NETWORK, "Network rules have been already updated for seq no. "
@@ -16560,9 +16614,9 @@
                         + " Uid: " + callingUid + " procStateSeq: " + procStateSeq);
                 }
                 final long startTime = SystemClock.uptimeMillis();
-                record.waitingForNetwork = true;
+                record.procStateSeqWaitingForNetwork = procStateSeq;
                 record.networkStateLock.wait(mWaitForNetworkTimeoutMs);
-                record.waitingForNetwork = false;
+                record.procStateSeqWaitingForNetwork = 0;
                 final long totalTime = SystemClock.uptimeMillis() - startTime;
                 if (totalTime >= mWaitForNetworkTimeoutMs || DEBUG_NETWORK) {
                     Slog.w(TAG_NETWORK, "Total time waited for network rules to get updated: "
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 0bf0fe2..98412bd 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -577,12 +577,14 @@
             mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(),
                     PackageWatchdog.FAILURE_REASON_APP_CRASH);
 
-            mService.mProcessList.noteAppKill(r, (crashInfo != null
-                      && "Native crash".equals(crashInfo.exceptionClassName))
-                      ? ApplicationExitInfo.REASON_CRASH_NATIVE
-                      : ApplicationExitInfo.REASON_CRASH,
-                      ApplicationExitInfo.SUBREASON_UNKNOWN,
-                    "crash");
+            synchronized (mService) {
+                mService.mProcessList.noteAppKill(r, (crashInfo != null
+                          && "Native crash".equals(crashInfo.exceptionClassName))
+                          ? ApplicationExitInfo.REASON_CRASH_NATIVE
+                          : ApplicationExitInfo.REASON_CRASH,
+                          ApplicationExitInfo.SUBREASON_UNKNOWN,
+                        "crash");
+            }
         }
 
         final int relaunchReason = r != null
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2030b19..dedd898 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1978,7 +1978,7 @@
             mHandler.post(() -> {
                 synchronized (mStats) {
                     mStats.noteBluetoothScanStoppedFromSourceLocked(localWs, isUnoptimized,
-                            uptime, elapsedRealtime);
+                            elapsedRealtime, uptime);
                 }
             });
         }
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 9e04410..ce40f85 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2911,8 +2911,7 @@
         // To avoid some abuse patterns, we are going to be careful about what we consider
         // to be an app interaction.  Being the top activity doesn't count while the display
         // is sleeping, nor do short foreground services.
-        if (state.getCurProcState() <= PROCESS_STATE_TOP
-                || state.getCurProcState() == PROCESS_STATE_BOUND_TOP) {
+        if (ActivityManager.isProcStateConsideredInteraction(state.getCurProcState())) {
             isInteraction = true;
             state.setFgInteractionTime(0);
         } else if (state.getCurProcState() <= PROCESS_STATE_FOREGROUND_SERVICE) {
diff --git a/services/core/java/com/android/server/am/PendingStartActivityUids.java b/services/core/java/com/android/server/am/PendingStartActivityUids.java
index 20f6bb2..5beda31 100644
--- a/services/core/java/com/android/server/am/PendingStartActivityUids.java
+++ b/services/core/java/com/android/server/am/PendingStartActivityUids.java
@@ -44,10 +44,12 @@
         mContext = context;
     }
 
-    synchronized void add(int uid, int pid) {
+    synchronized boolean add(int uid, int pid) {
         if (mPendingUids.get(uid) == null) {
             mPendingUids.put(uid, new Pair<>(pid, SystemClock.elapsedRealtime()));
+            return true;
         }
+        return false;
     }
 
     synchronized void delete(int uid, long nowElapsed) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1e66ed4..ca16f57 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -482,9 +482,8 @@
      * Having a global counter ensures that seq numbers are monotonically increasing for a
      * particular uid even when the uidRecord is re-created.
      */
-    @GuardedBy("mService")
     @VisibleForTesting
-    long mProcStateSeqCounter = 0;
+    volatile long mProcStateSeqCounter = 0;
 
     /**
      * A global counter for generating sequence numbers to uniquely identify pending process starts.
@@ -2426,8 +2425,8 @@
             if (!regularZygote) {
                 // webview and app zygote don't have the permission to create the nodes
                 if (Process.createProcessGroup(uid, startResult.pid) < 0) {
-                    Slog.e(ActivityManagerService.TAG, "Unable to create process group for "
-                            + app.processName + " (" + startResult.pid + ")");
+                    throw new AssertionError("Unable to create process group for " + app.processName
+                            + " (" + startResult.pid + ")");
                 }
             }
 
@@ -4878,13 +4877,17 @@
     }
 
     /**
-     * Checks if any uid is coming from background to foreground or vice versa and if so, increments
-     * the {@link UidRecord#curProcStateSeq} corresponding to that uid using global seq counter
-     * {@link ProcessList#mProcStateSeqCounter} and notifies the app if it needs to block.
+     * Increments the {@link UidRecord#curProcStateSeq} for all uids using global seq counter
+     * {@link ProcessList#mProcStateSeqCounter} and checks if any uid is coming
+     * from background to foreground or vice versa and if so, notifies the app if it needs to block.
      */
     @VisibleForTesting
     @GuardedBy(anyOf = {"mService", "mProcLock"})
     void incrementProcStateSeqAndNotifyAppsLOSP(ActiveUids activeUids) {
+        for (int i = activeUids.size() - 1; i >= 0; --i) {
+            final UidRecord uidRec = activeUids.valueAt(i);
+            uidRec.curProcStateSeq = getNextProcStateSeq();
+        }
         if (mService.mWaitForNetworkTimeoutMs <= 0) {
             return;
         }
@@ -4911,7 +4914,6 @@
                 continue;
             }
             synchronized (uidRec.networkStateLock) {
-                uidRec.curProcStateSeq = ++mProcStateSeqCounter; // TODO: use method
                 if (blockState == NETWORK_STATE_BLOCK) {
                     if (blockingUids == null) {
                         blockingUids = new ArrayList<>();
@@ -4922,7 +4924,7 @@
                         Slog.d(TAG_NETWORK, "uid going to background, notifying all blocking"
                                 + " threads for uid: " + uidRec);
                     }
-                    if (uidRec.waitingForNetwork) {
+                    if (uidRec.procStateSeqWaitingForNetwork != 0) {
                         uidRec.networkStateLock.notifyAll();
                     }
                 }
@@ -4956,6 +4958,10 @@
         }
     }
 
+    long getNextProcStateSeq() {
+        return ++mProcStateSeqCounter;
+    }
+
     /**
      * Create a server socket in system_server, zygote will connect to it
      * in order to send unsolicited messages to system_server.
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index c1bfe25..67b9ae6 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -219,7 +219,6 @@
                     validateUid.setCurProcState(item.procState);
                     validateUid.setSetCapability(item.capability);
                     validateUid.setCurCapability(item.capability);
-                    validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 4ba59fa..2ec08a4 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -95,16 +95,9 @@
     long lastNetworkUpdatedProcStateSeq;
 
     /**
-     * Last seq number for which AcitivityManagerService dispatched uid state change to
-     * NetworkPolicyManagerService.
+     * Indicates if any thread is waiting for network rules to get updated for {@link #mUid}.
      */
-    @GuardedBy("networkStateUpdate")
-    long lastDispatchedProcStateSeq;
-
-    /**
-     * Indicates if any thread is waiting for network rules to get updated for {@link #uid}.
-     */
-    volatile boolean waitingForNetwork;
+    volatile long procStateSeqWaitingForNetwork;
 
     /**
      * Indicates whether this uid has internet permission or not.
@@ -309,18 +302,6 @@
                 mUid) == PackageManager.PERMISSION_GRANTED;
     }
 
-    /**
-     * If the change being dispatched is not CHANGE_GONE (not interested in
-     * these changes), then update the {@link #lastDispatchedProcStateSeq} with
-     * {@link #curProcStateSeq}.
-     */
-    public void updateLastDispatchedProcStateSeq(int changeToDispatch) {
-        if ((changeToDispatch & CHANGE_GONE) == 0) {
-            lastDispatchedProcStateSeq = curProcStateSeq;
-        }
-    }
-
-
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
         proto.write(UidRecordProto.UID, mUid);
@@ -341,7 +322,6 @@
         proto.write(UidRecordProto.ProcStateSequence.CURURENT, curProcStateSeq);
         proto.write(UidRecordProto.ProcStateSequence.LAST_NETWORK_UPDATED,
                 lastNetworkUpdatedProcStateSeq);
-        proto.write(UidRecordProto.ProcStateSequence.LAST_DISPATCHED, lastDispatchedProcStateSeq);
         proto.end(seqToken);
 
         proto.end(token);
@@ -412,8 +392,6 @@
         sb.append(curProcStateSeq);
         sb.append(",");
         sb.append(lastNetworkUpdatedProcStateSeq);
-        sb.append(",");
-        sb.append(lastDispatchedProcStateSeq);
         sb.append(")}");
         return sb.toString();
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 33440a3..d9db28a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -85,6 +85,7 @@
 import android.net.ipsec.ike.IkeSession;
 import android.net.ipsec.ike.IkeSessionCallback;
 import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
@@ -208,7 +209,6 @@
     private final NetworkInfo mNetworkInfo;
     private int mLegacyState;
     @VisibleForTesting protected String mPackage;
-    private String mSessionKey;
     private int mOwnerUID;
     private boolean mIsPackageTargetingAtLeastQ;
     @VisibleForTesting
@@ -1990,9 +1990,7 @@
     public synchronized int getActiveVpnType() {
         if (!mNetworkInfo.isConnectedOrConnecting()) return VpnManager.TYPE_VPN_NONE;
         if (mVpnRunner == null) return VpnManager.TYPE_VPN_SERVICE;
-        return mVpnRunner instanceof IkeV2VpnRunner
-                ? VpnManager.TYPE_VPN_PLATFORM
-                : VpnManager.TYPE_VPN_LEGACY;
+        return isIkev2VpnRunner() ? VpnManager.TYPE_VPN_PLATFORM : VpnManager.TYPE_VPN_LEGACY;
     }
 
     private void updateAlwaysOnNotification(DetailedState networkState) {
@@ -2267,6 +2265,11 @@
                 profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS);
                 startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN);
                 return;
+            case VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS:
+                // All the necessary IKE options should come from IkeTunnelConnectionParams in the
+                // profile.
+                startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN);
+                return;
             case VpnProfile.TYPE_L2TP_IPSEC_PSK:
                 racoon = new String[] {
                     iface, profile.server, "udppsk", profile.ipsecIdentifier,
@@ -2518,6 +2521,7 @@
         @Nullable private IpSecTunnelInterface mTunnelIface;
         @Nullable private IkeSession mSession;
         @Nullable private Network mActiveNetwork;
+        private final String mSessionKey;
 
         IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
             super(TAG);
@@ -2702,10 +2706,23 @@
                     resetIkeState();
                     mActiveNetwork = network;
 
-                    final IkeSessionParams ikeSessionParams =
-                            VpnIkev2Utils.buildIkeSessionParams(mContext, mProfile, network);
-                    final ChildSessionParams childSessionParams =
-                            VpnIkev2Utils.buildChildSessionParams(mProfile.getAllowedAlgorithms());
+                    // Get Ike options from IkeTunnelConnectionParams if it's available in the
+                    // profile.
+                    final IkeTunnelConnectionParams ikeTunConnParams =
+                            mProfile.getIkeTunnelConnectionParams();
+                    final IkeSessionParams ikeSessionParams;
+                    final ChildSessionParams childSessionParams;
+                    if (ikeTunConnParams != null) {
+                        final IkeSessionParams.Builder builder = new IkeSessionParams.Builder(
+                                ikeTunConnParams.getIkeSessionParams()).setNetwork(network);
+                        ikeSessionParams = builder.build();
+                        childSessionParams = ikeTunConnParams.getTunnelModeChildSessionParams();
+                    } else {
+                        ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(
+                                mContext, mProfile, network);
+                        childSessionParams = VpnIkev2Utils.buildChildSessionParams(
+                                mProfile.getAllowedAlgorithms());
+                    }
 
                     // TODO: Remove the need for adding two unused addresses with
                     // IPsec tunnels.
@@ -2850,7 +2867,6 @@
          */
         private void disconnectVpnRunner() {
             mActiveNetwork = null;
-            mSessionKey = null;
             mIsRunning = false;
 
             resetIkeState();
@@ -3226,6 +3242,7 @@
             case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
             case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
             case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+            case VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS:
                 if (!mContext.getPackageManager().hasSystemFeature(
                         PackageManager.FEATURE_IPSEC_TUNNELS)) {
                     throw new UnsupportedOperationException(
@@ -3279,7 +3296,7 @@
     }
 
     private boolean isCurrentIkev2VpnLocked(@NonNull String packageName) {
-        return isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner;
+        return isCurrentPreparedPackage(packageName) && isIkev2VpnRunner();
     }
 
     /**
@@ -3333,6 +3350,16 @@
         return VpnProfile.decode("" /* Key unused */, encoded);
     }
 
+    private boolean isIkev2VpnRunner() {
+        return (mVpnRunner instanceof IkeV2VpnRunner);
+    }
+
+    @GuardedBy("this")
+    @Nullable
+    private String getSessionKeyLocked() {
+        return isIkev2VpnRunner() ? ((IkeV2VpnRunner) mVpnRunner).mSessionKey : null;
+    }
+
     /**
      * Starts an already provisioned VPN Profile, keyed by package name.
      *
@@ -3360,7 +3387,11 @@
             }
 
             startVpnProfilePrivileged(profile, packageName);
-            return mSessionKey;
+            if (!isIkev2VpnRunner()) {
+                throw new IllegalStateException("mVpnRunner shouldn't be null and should also be "
+                        + "an instance of Ikev2VpnRunner");
+            }
+            return getSessionKeyLocked();
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -3399,6 +3430,7 @@
                 case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
                 case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
                 case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+                case VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS:
                     mVpnRunner =
                             new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile));
                     mVpnRunner.start();
@@ -3462,11 +3494,8 @@
     }
 
     private VpnProfileState makeVpnProfileState() {
-        // TODO: mSessionKey will be moved to Ikev2VpnRunner once aosp/2007077 is merged, so after
-        //  merging aosp/2007077, here should check Ikev2VpnRunner is null or not. Session key will
-        //  be null if Ikev2VpnRunner is null.
-        return new VpnProfileState(getStateFromLegacyState(mLegacyState), mSessionKey, mAlwaysOn,
-                mLockdown);
+        return new VpnProfileState(getStateFromLegacyState(mLegacyState),
+                isIkev2VpnRunner() ? getSessionKeyLocked() : null, mAlwaysOn, mLockdown);
     }
 
     /**
diff --git a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
index 629011a..90a2f48 100644
--- a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
+++ b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
@@ -115,5 +115,13 @@
         public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException {
             mServiceInfoCallback.update(healthInfo);
         }
+        @Override
+        public String getInterfaceHash() {
+            return IHealthInfoCallback.HASH;
+        }
+        @Override
+        public int getInterfaceVersion() {
+            return IHealthInfoCallback.VERSION;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 33ac6cd..c963154 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -77,6 +77,8 @@
     private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
     private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13;
     private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
+    private static final int EVENT_METERED_ALLOWLIST_CHANGED = 15;
+    private static final int EVENT_METERED_DENYLIST_CHANGED = 16;
 
     private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
     private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
@@ -89,7 +91,7 @@
     void networkBlocked(int uid, @Nullable UidBlockedState uidBlockedState) {
         synchronized (mLock) {
             if (LOGD || uid == mDebugUid) {
-                Slog.d(TAG, "Blocked state of uid: " + uidBlockedState.toString());
+                Slog.d(TAG, "Blocked state of " + uid + ": " + uidBlockedState.toString());
             }
             if (uidBlockedState == null) {
                 mNetworkBlockedBuffer.networkBlocked(uid, BLOCKED_REASON_NONE, ALLOWED_REASON_NONE,
@@ -245,6 +247,24 @@
         }
     }
 
+    void meteredAllowlistChanged(int uid, boolean added) {
+        synchronized (mLock) {
+            if (LOGD || mDebugUid == uid) {
+                Slog.d(TAG, getMeteredAllowlistChangedLog(uid, added));
+            }
+            mEventsBuffer.meteredAllowlistChanged(uid, added);
+        }
+    }
+
+    void meteredDenylistChanged(int uid, boolean added) {
+        synchronized (mLock) {
+            if (LOGD || mDebugUid == uid) {
+                Slog.d(TAG, getMeteredDenylistChangedLog(uid, added));
+            }
+            mEventsBuffer.meteredDenylistChanged(uid, added);
+        }
+    }
+
     void setDebugUid(int uid) {
         mDebugUid = uid;
     }
@@ -320,6 +340,14 @@
         return "Firewall chain " + getFirewallChainName(chain) + " state: " + enabled;
     }
 
+    private static String getMeteredAllowlistChangedLog(int uid, boolean added) {
+        return "metered-allowlist for " + uid + " changed to " + added;
+    }
+
+    private static String getMeteredDenylistChangedLog(int uid, boolean added) {
+        return "metered-denylist for " + uid + " changed to " + added;
+    }
+
     private static String getFirewallChainName(int chain) {
         switch (chain) {
             case FIREWALL_CHAIN_DOZABLE:
@@ -520,6 +548,28 @@
             data.timeStamp = System.currentTimeMillis();
         }
 
+        public void meteredAllowlistChanged(int uid, boolean added) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_METERED_ALLOWLIST_CHANGED;
+            data.ifield1 = uid;
+            data.bfield1 = added;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
+        public void meteredDenylistChanged(int uid, boolean added) {
+            final Data data = getNextSlot();
+            if (data == null) return;
+
+            data.reset();
+            data.type = EVENT_METERED_DENYLIST_CHANGED;
+            data.ifield1 = uid;
+            data.bfield1 = added;
+            data.timeStamp = System.currentTimeMillis();
+        }
+
         public void reverseDump(IndentingPrintWriter pw) {
             final Data[] allData = toArray();
             for (int i = allData.length - 1; i >= 0; --i) {
@@ -567,6 +617,10 @@
                     return getUidFirewallRuleChangedLog(data.ifield1, data.ifield2, data.ifield3);
                 case EVENT_FIREWALL_CHAIN_ENABLED:
                     return getFirewallChainEnabledLog(data.ifield1, data.bfield1);
+                case EVENT_METERED_ALLOWLIST_CHANGED:
+                    return getMeteredAllowlistChangedLog(data.ifield1, data.bfield1);
+                case EVENT_METERED_DENYLIST_CHANGED:
+                    return getMeteredDenylistChangedLog(data.ifield1, data.bfield1);
                 default:
                     return String.valueOf(data.type);
             }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index ea851ba..3858d7a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -26,6 +26,9 @@
 import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
+import static android.app.ActivityManager.isProcStateConsideredInteraction;
+import static android.app.ActivityManager.procStateToString;
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
@@ -90,6 +93,8 @@
 import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE;
 import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
+import static android.net.NetworkPolicyManager.allowedReasonsToString;
+import static android.net.NetworkPolicyManager.blockedReasonsToString;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileInLowPowerStandby;
 import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
@@ -160,6 +165,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
@@ -168,6 +174,7 @@
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
+import android.net.LinkProperties;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
@@ -230,6 +237,7 @@
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.SparseLongArray;
+import android.util.SparseSetArray;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
@@ -418,6 +426,7 @@
     private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
     private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
     private static final int MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED = 20;
+
     // TODO: Add similar docs for other messages.
     /**
      * Message to indicate that reasons for why an uid is blocked changed.
@@ -425,7 +434,8 @@
      * arg2 = newBlockedReasons
      * obj = oldBlockedReasons
      */
-    private static final int MSG_BLOCKED_REASON_CHANGED = 21;
+    private static final int MSG_UID_BLOCKED_REASON_CHANGED = 21;
+
     /**
      * Message to indicate that subscription plans expired and should be cleared.
      * arg1 = subId
@@ -434,6 +444,15 @@
      */
     private static final int MSG_CLEAR_SUBSCRIPTION_PLANS = 22;
 
+    /**
+     * Message to indicate that reasons for why some uids are blocked changed.
+     * obj = SparseArray<SomeArgs> where key = uid and value = SomeArgs object with
+     *       value.argi1 = oldEffectiveBlockedReasons,
+     *       value.argi2 = newEffectiveBlockedReasons,
+     *       value.argi3 = uidRules
+     */
+    private static final int MSG_UIDS_BLOCKED_REASONS_CHANGED = 23;
+
     private static final int UID_MSG_STATE_CHANGED = 100;
     private static final int UID_MSG_GONE = 101;
 
@@ -471,7 +490,7 @@
     final Object mUidRulesFirstLock = new Object();
     final Object mNetworkPoliciesSecondLock = new Object();
 
-    @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
+    @GuardedBy(anyOf = {"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
     volatile boolean mSystemReady;
 
     @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
@@ -594,7 +613,7 @@
     @GuardedBy("mUidRulesFirstLock")
     private final SparseArray<UidState> mUidState = new SparseArray<>();
 
-    @GuardedBy("mUidRulesFirstLock")
+    @GuardedBy("mUidBlockedState")
     private final SparseArray<UidBlockedState> mUidBlockedState = new SparseArray<>();
 
     /** Objects used temporarily while computing the new blocked state for each uid. */
@@ -607,6 +626,9 @@
     /** Map from network ID to last observed roaming state */
     @GuardedBy("mNetworkPoliciesSecondLock")
     private final SparseBooleanArray mNetworkRoaming = new SparseBooleanArray();
+    /** Map from network ID to the last ifaces on it */
+    @GuardedBy("mNetworkPoliciesSecondLock")
+    private SparseSetArray<String> mNetworkToIfaces = new SparseSetArray<>();
 
     /** Map from netId to subId as of last update */
     @GuardedBy("mNetworkPoliciesSecondLock")
@@ -909,6 +931,7 @@
 
             mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
             mAppStandby = LocalServices.getService(AppStandbyInternal.class);
+            mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
 
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
@@ -986,12 +1009,11 @@
                 }
             }
 
-            mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
             try {
                 final int changes = ActivityManager.UID_OBSERVER_PROCSTATE
                         | ActivityManager.UID_OBSERVER_GONE
                         | ActivityManager.UID_OBSERVER_CAPABILITY;
-                mActivityManager.registerUidObserver(mUidObserver, changes,
+                mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes,
                         NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android");
                 mNetworkManager.registerObserver(mAlertObserver);
             } catch (RemoteException e) {
@@ -1107,7 +1129,9 @@
                     callbackInfo = new UidStateCallbackInfo();
                     mUidStateCallbackInfos.put(uid, callbackInfo);
                 }
-                callbackInfo.update(uid, procState, procStateSeq, capability);
+                if (callbackInfo.procStateSeq == -1 || procStateSeq > callbackInfo.procStateSeq) {
+                    callbackInfo.update(uid, procState, procStateSeq, capability);
+                }
                 if (!callbackInfo.isPending) {
                     mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo)
                             .sendToTarget();
@@ -1131,8 +1155,8 @@
 
     private static final class UidStateCallbackInfo {
         public int uid;
-        public int procState;
-        public long procStateSeq;
+        public int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+        public long procStateSeq = -1;
         @ProcessCapability
         public int capability;
         public boolean isPending;
@@ -1324,11 +1348,28 @@
         return changed;
     }
 
+    @GuardedBy("mNetworkPoliciesSecondLock")
+    private boolean updateNetworkToIfacesNL(int netId, @NonNull ArraySet<String> newIfaces) {
+        // TODO: Add a facility SparseSetArray.contains(key) to return whether the key exists.
+        final ArraySet<String> lastIfaces = mNetworkToIfaces.get(netId);
+        final boolean changed = lastIfaces == null ? true : !lastIfaces.equals(newIfaces);
+
+        if (changed) {
+            // Changed on the same network should remove last ifaces and add new ifaces.
+            // TODO: Add a facility SparseSetArray.put(key, value) for replacing the
+            //       value for a given key.
+            mNetworkToIfaces.remove(netId);
+            for (String iface : newIfaces) {
+                mNetworkToIfaces.add(netId, iface);
+            }
+        }
+        return changed;
+    }
+
     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
         @Override
-        public void onCapabilitiesChanged(Network network,
-                NetworkCapabilities networkCapabilities) {
-            if (network == null || networkCapabilities == null) return;
+        public void onCapabilitiesChanged(@NonNull Network network,
+                @NonNull NetworkCapabilities networkCapabilities) {
 
             synchronized (mNetworkPoliciesSecondLock) {
                 final boolean newMetered = !networkCapabilities
@@ -1347,6 +1388,25 @@
                 }
             }
         }
+
+        @Override
+        public void onLinkPropertiesChanged(@NonNull Network network, @NonNull LinkProperties lp) {
+            synchronized (mNetworkPoliciesSecondLock) {
+                final ArraySet<String> newIfaces = new ArraySet<>(lp.getAllInterfaceNames());
+                final boolean ifacesChanged = updateNetworkToIfacesNL(network.getNetId(),
+                        newIfaces);
+                if (ifacesChanged) {
+                    updateNetworkRulesNL();
+                }
+            }
+        }
+
+        @Override
+        public void onLost(@NonNull Network network) {
+            synchronized (mNetworkPoliciesSecondLock) {
+                mNetworkToIfaces.remove(network.getNetId());
+            }
+        }
     };
 
     /**
@@ -3298,7 +3358,7 @@
                 if (mSystemReady) {
                     // Device idle change means we need to rebuild rules for all
                     // known apps, so do a global refresh.
-                    updateRulesForRestrictPowerUL();
+                    handleDeviceIdleModeChangedUL(enabled);
                 }
             }
             if (enabled) {
@@ -3947,7 +4007,9 @@
 
                 final SparseBooleanArray knownUids = new SparseBooleanArray();
                 collectKeys(mUidState, knownUids);
-                collectKeys(mUidBlockedState, knownUids);
+                synchronized (mUidBlockedState) {
+                    collectKeys(mUidBlockedState, knownUids);
+                }
 
                 fout.println("Status for all known UIDs:");
                 fout.increaseIndent();
@@ -3965,12 +4027,14 @@
                         fout.print(uidState.toString());
                     }
 
-                    final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
-                    if (uidBlockedState == null) {
-                        fout.print(" blocked_state={null}");
-                    } else {
-                        fout.print(" blocked_state=");
-                        fout.print(uidBlockedState.toString());
+                    synchronized (mUidBlockedState) {
+                        final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+                        if (uidBlockedState == null) {
+                            fout.print(" blocked_state={null}");
+                        } else {
+                            fout.print(" blocked_state=");
+                            fout.print(uidBlockedState);
+                        }
                     }
                     fout.println();
                 }
@@ -4038,13 +4102,22 @@
      * {@link #updateRulesForPowerRestrictionsUL(int)}. Returns true if the state was updated.
      */
     @GuardedBy("mUidRulesFirstLock")
-    private boolean updateUidStateUL(int uid, int procState, @ProcessCapability int capability) {
+    private boolean updateUidStateUL(int uid, int procState, long procStateSeq,
+            @ProcessCapability int capability) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
         try {
             final UidState oldUidState = mUidState.get(uid);
+            if (oldUidState != null && procStateSeq < oldUidState.procStateSeq) {
+                if (LOGV) {
+                    Slog.v(TAG, "Ignoring older uid state updates; uid=" + uid
+                            + ",procState=" + procStateToString(procState) + ",seq=" + procStateSeq
+                            + ",cap=" + capability + ",oldUidState=" + oldUidState);
+                }
+                return false;
+            }
             if (oldUidState == null || oldUidState.procState != procState
                     || oldUidState.capability != capability) {
-                final UidState newUidState = new UidState(uid, procState, capability);
+                final UidState newUidState = new UidState(uid, procState, procStateSeq, capability);
                 // state changed, push updated rules
                 mUidState.put(uid, newUidState);
                 updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState);
@@ -4052,14 +4125,14 @@
                         isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
                                 != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState);
                 if (allowedWhileIdleOrPowerSaveModeChanged) {
-                    updateRuleForAppIdleUL(uid);
+                    updateRuleForAppIdleUL(uid, procState);
                     if (mDeviceIdleMode) {
                         updateRuleForDeviceIdleUL(uid);
                     }
                     if (mRestrictPower) {
                         updateRuleForRestrictPowerUL(uid);
                     }
-                    updateRulesForPowerRestrictionsUL(uid);
+                    updateRulesForPowerRestrictionsUL(uid, procState);
                 }
                 if (mLowPowerStandbyActive) {
                     boolean allowedInLpsChanged =
@@ -4067,7 +4140,7 @@
                                     != isProcStateAllowedWhileInLowPowerStandby(newUidState);
                     if (allowedInLpsChanged) {
                         if (!allowedWhileIdleOrPowerSaveModeChanged) {
-                            updateRulesForPowerRestrictionsUL(uid);
+                            updateRulesForPowerRestrictionsUL(uid, procState);
                         }
                         updateRuleForLowPowerStandbyUL(uid);
                     }
@@ -4145,9 +4218,9 @@
         mUidFirewallRestrictedModeRules.clear();
         forEachUid("updateRestrictedModeAllowlist", uid -> {
             synchronized (mUidRulesFirstLock) {
-                final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL(
+                final int effectiveBlockedReasons = updateBlockedReasonsForRestrictedModeUL(
                         uid);
-                final int newFirewallRule = getRestrictedModeFirewallRule(uidBlockedState);
+                final int newFirewallRule = getRestrictedModeFirewallRule(effectiveBlockedReasons);
 
                 // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add
                 // non-default rules.
@@ -4167,7 +4240,7 @@
     @VisibleForTesting
     @GuardedBy("mUidRulesFirstLock")
     void updateRestrictedModeForUidUL(int uid) {
-        final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL(uid);
+        final int effectiveBlockedReasons = updateBlockedReasonsForRestrictedModeUL(uid);
 
         // if restricted networking mode is on, and the app has an access exemption, the uid rule
         // will not change, but the firewall rule will have to be updated.
@@ -4175,37 +4248,48 @@
             // Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules.
             // In this case, default firewall rules can also be added.
             setUidFirewallRuleUL(FIREWALL_CHAIN_RESTRICTED, uid,
-                    getRestrictedModeFirewallRule(uidBlockedState));
+                    getRestrictedModeFirewallRule(effectiveBlockedReasons));
         }
     }
 
     @GuardedBy("mUidRulesFirstLock")
-    private UidBlockedState updateBlockedReasonsForRestrictedModeUL(int uid) {
-        final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
-                mUidBlockedState, uid);
-        final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
-        if (mRestrictedNetworkingMode) {
-            uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE;
-        } else {
-            uidBlockedState.blockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE;
-        }
-        if (hasRestrictedModeAccess(uid)) {
-            uidBlockedState.allowedReasons |= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
-        } else {
-            uidBlockedState.allowedReasons &= ~ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
-        }
-        uidBlockedState.updateEffectiveBlockedReasons();
-        if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
-            postBlockedReasonsChangedMsg(uid,
-                    uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons);
+    private int updateBlockedReasonsForRestrictedModeUL(int uid) {
+        final boolean hasRestrictedModeAccess = hasRestrictedModeAccess(uid);
+        final int oldEffectiveBlockedReasons;
+        final int newEffectiveBlockedReasons;
+        final int uidRules;
+        synchronized (mUidBlockedState) {
+            final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+                    mUidBlockedState, uid);
+            oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+            if (mRestrictedNetworkingMode) {
+                uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE;
+            } else {
+                uidBlockedState.blockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE;
+            }
+            if (hasRestrictedModeAccess) {
+                uidBlockedState.allowedReasons |= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+            } else {
+                uidBlockedState.allowedReasons &= ~ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+            }
+            uidBlockedState.updateEffectiveBlockedReasons();
 
-            postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules());
+            newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+            uidRules = oldEffectiveBlockedReasons == newEffectiveBlockedReasons
+                    ? RULE_NONE
+                    : uidBlockedState.deriveUidRules();
         }
-        return uidBlockedState;
+        if (oldEffectiveBlockedReasons != newEffectiveBlockedReasons) {
+            handleBlockedReasonsChanged(uid,
+                    newEffectiveBlockedReasons, oldEffectiveBlockedReasons);
+
+            postUidRulesChangedMsg(uid, uidRules);
+        }
+        return newEffectiveBlockedReasons;
     }
 
-    private static int getRestrictedModeFirewallRule(UidBlockedState uidBlockedState) {
-        if ((uidBlockedState.effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) {
+    private static int getRestrictedModeFirewallRule(int effectiveBlockedReasons) {
+        if ((effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) {
             // rejected in restricted mode, this is the default behavior.
             return FIREWALL_RULE_DEFAULT;
         } else {
@@ -4309,12 +4393,10 @@
                 mUidFirewallLowPowerStandbyModeRules.clear();
                 for (int i = mUidState.size() - 1; i >= 0; i--) {
                     final int uid = mUidState.keyAt(i);
-                    UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
-                    if (hasInternetPermissionUL(uid) && uidBlockedState != null
-                            && (uidBlockedState.effectiveBlockedReasons
+                    final int effectiveBlockedReasons = getEffectiveBlockedReasons(uid);
+                    if (hasInternetPermissionUL(uid) && (effectiveBlockedReasons
                                     & BLOCKED_REASON_LOW_POWER_STANDBY) == 0) {
-                        mUidFirewallLowPowerStandbyModeRules.put(mUidBlockedState.keyAt(i),
-                                FIREWALL_RULE_ALLOW);
+                        mUidFirewallLowPowerStandbyModeRules.put(uid, FIREWALL_RULE_ALLOW);
                     }
                 }
                 setUidFirewallRulesUL(FIREWALL_CHAIN_LOW_POWER_STANDBY,
@@ -4333,10 +4415,9 @@
             return;
         }
 
-        final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
-        if (mUidState.contains(uid) && uidBlockedState != null
-                && (uidBlockedState.effectiveBlockedReasons & BLOCKED_REASON_LOW_POWER_STANDBY)
-                == 0) {
+        final int effectiveBlockedReasons = getEffectiveBlockedReasons(uid);
+        if (mUidState.contains(uid)
+                && (effectiveBlockedReasons & BLOCKED_REASON_LOW_POWER_STANDBY) == 0) {
             mUidFirewallLowPowerStandbyModeRules.put(uid, FIREWALL_RULE_ALLOW);
             setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_ALLOW);
         } else {
@@ -4428,7 +4509,7 @@
     }
 
     @GuardedBy("mUidRulesFirstLock")
-    void updateRuleForAppIdleUL(int uid) {
+    void updateRuleForAppIdleUL(int uid, int uidProcessState) {
         if (!isUidValidForDenylistRulesUL(uid)) return;
 
         if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
@@ -4436,7 +4517,7 @@
         }
         try {
             int appId = UserHandle.getAppId(uid);
-            if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
+            if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid, uidProcessState)
                     && !isUidForegroundOnRestrictPowerUL(uid)) {
                 setUidFirewallRuleUL(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
                 if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
@@ -4465,9 +4546,8 @@
             if (!isUidValidForDenylistRulesUL(uid)) {
                 continue;
             }
-            final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
-                    mUidBlockedState, uid);
-            if (!enableChain && (uidBlockedState.blockedReasons & ~BLOCKED_METERED_REASON_MASK)
+            final int blockedReasons = getBlockedReasons(uid);
+            if (!enableChain && (blockedReasons & ~BLOCKED_METERED_REASON_MASK)
                     == BLOCKED_REASON_NONE) {
                 // Chain isn't enabled and the uid had no restrictions to begin with.
                 continue;
@@ -4512,6 +4592,71 @@
         }
     }
 
+    @GuardedBy("mUidRulesFirstLock")
+    private void handleDeviceIdleModeChangedUL(boolean enabled) {
+        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL");
+        try {
+            updateRulesForDeviceIdleUL();
+            if (enabled) {
+                forEachUid("updateRulesForRestrictPower", uid -> {
+                    synchronized (mUidRulesFirstLock) {
+                        updateRulesForPowerRestrictionsUL(uid);
+                    }
+                });
+            } else {
+                // TODO: Note that we could handle the case of enabling-doze state similar
+                // to this but first, we need to update how we listen to uid state changes
+                // so that we always get a callback when a process moves from a NONEXISTENT state
+                // to a "background" state.
+                handleDeviceIdleModeDisabledUL();
+            }
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
+        }
+    }
+
+    @GuardedBy("mUidRulesFirstLock")
+    private void handleDeviceIdleModeDisabledUL() {
+        Trace.traceBegin(TRACE_TAG_NETWORK, "handleDeviceIdleModeDisabledUL");
+        try {
+            final SparseArray<SomeArgs> uidStateUpdates = new SparseArray<>();
+            synchronized (mUidBlockedState) {
+                final int size = mUidBlockedState.size();
+                for (int i = 0; i < size; ++i) {
+                    final int uid = mUidBlockedState.keyAt(i);
+                    final UidBlockedState uidBlockedState = mUidBlockedState.valueAt(i);
+                    if ((uidBlockedState.blockedReasons & BLOCKED_REASON_DOZE) == 0) {
+                        continue;
+                    }
+                    uidBlockedState.blockedReasons &= ~BLOCKED_REASON_DOZE;
+                    final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+                    uidBlockedState.updateEffectiveBlockedReasons();
+                    if (LOGV) {
+                        Log.v(TAG, "handleDeviceIdleModeDisabled(" + uid + "); "
+                                + "newUidBlockedState=" + uidBlockedState
+                                + ", oldEffectiveBlockedReasons=" + oldEffectiveBlockedReasons);
+                    }
+                    if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
+                        final SomeArgs someArgs = SomeArgs.obtain();
+                        someArgs.argi1 = oldEffectiveBlockedReasons;
+                        someArgs.argi2 = uidBlockedState.effectiveBlockedReasons;
+                        someArgs.argi3 = uidBlockedState.deriveUidRules();
+                        uidStateUpdates.append(uid, someArgs);
+                        // TODO: Update the state for all changed uids together.
+                        mActivityManagerInternal.onUidBlockedReasonsChanged(uid,
+                                uidBlockedState.effectiveBlockedReasons);
+                    }
+                }
+            }
+            if (uidStateUpdates.size() != 0) {
+                mHandler.obtainMessage(MSG_UIDS_BLOCKED_REASONS_CHANGED, uidStateUpdates)
+                        .sendToTarget();
+            }
+        } finally {
+            Trace.traceEnd(TRACE_TAG_NETWORK);
+        }
+    }
+
     // TODO: rename / document to make it clear these are global (not app-specific) rules
     @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForRestrictPowerUL() {
@@ -4543,9 +4688,7 @@
         }
         try {
             // update rules for all installed applications
-            final PackageManager pm = mContext.getPackageManager();
             final List<UserInfo> users;
-            final List<ApplicationInfo> apps;
 
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "list-users");
             try {
@@ -4553,26 +4696,30 @@
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
             }
-            Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "list-uids");
+            Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "iterate-uids");
             try {
-                apps = pm.getInstalledApplications(
-                        PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DISABLED_COMPONENTS
-                                | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+                final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                        PackageManagerInternal.class);
+                final int usersSize = users.size();
+                for (int i = 0; i < usersSize; ++i) {
+                    final int userId = users.get(i).id;
+                    final SparseBooleanArray sharedAppIdsHandled = new SparseBooleanArray();
+                    packageManagerInternal.forEachInstalledPackage(androidPackage -> {
+                        final int appId = androidPackage.getUid();
+                        if (androidPackage.getSharedUserId() != null) {
+                            if (sharedAppIdsHandled.indexOfKey(appId) < 0) {
+                                sharedAppIdsHandled.put(appId, true);
+                            } else {
+                                return;
+                            }
+                        }
+                        final int uid = UserHandle.getUid(userId, appId);
+                        consumer.accept(uid);
+                    }, userId);
+                }
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
             }
-
-            final int usersSize = users.size();
-            final int appsSize = apps.size();
-            for (int i = 0; i < usersSize; i++) {
-                final UserInfo user = users.get(i);
-                for (int j = 0; j < appsSize; j++) {
-                    final ApplicationInfo app = apps.get(j);
-                    final int uid = UserHandle.getUid(user.id, app.uid);
-                    consumer.accept(uid);
-                }
-            }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
@@ -4586,7 +4733,7 @@
             final UserInfo user = users.get(i);
             int uid = UserHandle.getUid(user.id, appId);
             // Update external firewall rules.
-            updateRuleForAppIdleUL(uid);
+            updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
             updateRuleForDeviceIdleUL(uid);
             updateRuleForRestrictPowerUL(uid);
             // Update internal rules.
@@ -4634,7 +4781,7 @@
                 } else {
                     mAppIdleTempWhitelistAppIds.delete(uid);
                 }
-                updateRuleForAppIdleUL(uid);
+                updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
                 updateRulesForPowerRestrictionsUL(uid);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -4660,7 +4807,15 @@
     /** Returns if the UID is currently considered idle. */
     @VisibleForTesting
     boolean isUidIdle(int uid) {
+        return isUidIdle(uid, PROCESS_STATE_UNKNOWN);
+    }
+
+    private boolean isUidIdle(int uid, int uidProcessState) {
         synchronized (mUidRulesFirstLock) {
+            if (uidProcessState != PROCESS_STATE_UNKNOWN && isProcStateConsideredInteraction(
+                    uidProcessState)) {
+                return false;
+            }
             if (mAppIdleTempWhitelistAppIds.get(uid)) {
                 // UID is temporarily allowlisted.
                 return false;
@@ -4709,7 +4864,11 @@
     @GuardedBy("mUidRulesFirstLock")
     private void onUidDeletedUL(int uid) {
         // First cleanup in-memory state synchronously...
-        mUidBlockedState.delete(uid);
+        synchronized (mUidBlockedState) {
+            mUidBlockedState.delete(uid);
+        }
+        mUidState.delete(uid);
+        mActivityManagerInternal.onUidBlockedReasonsChanged(uid, BLOCKED_REASON_NONE);
         mUidPolicy.delete(uid);
         mUidFirewallStandbyRules.delete(uid);
         mUidFirewallDozableRules.delete(uid);
@@ -4745,7 +4904,7 @@
     private void updateRestrictionRulesForUidUL(int uid) {
         // Methods below only changes the firewall rules for the power-related modes.
         updateRuleForDeviceIdleUL(uid);
-        updateRuleForAppIdleUL(uid);
+        updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
         updateRuleForRestrictPowerUL(uid);
 
         // If the uid has the necessary permissions, then it should be added to the restricted mode
@@ -4821,11 +4980,6 @@
         final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
         final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid);
         final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid);
-        final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
-                mUidBlockedState, uid);
-        final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
-                mTmpUidBlockedState, uid);
-        previousUidBlockedState.copyFrom(uidBlockedState);
 
         final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
         final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
@@ -4840,18 +4994,47 @@
         newAllowedReasons |= (isForeground ? ALLOWED_METERED_REASON_FOREGROUND : 0);
         newAllowedReasons |= (isAllowed ? ALLOWED_METERED_REASON_USER_EXEMPTED : 0);
 
-        uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
-                & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
-        uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
-                & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
-        uidBlockedState.updateEffectiveBlockedReasons();
-        final int oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons;
-        final int newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+        final int oldEffectiveBlockedReasons;
+        final int newEffectiveBlockedReasons;
+        final int oldAllowedReasons;
+        final int uidRules;
+        synchronized (mUidBlockedState) {
+            final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+                    mUidBlockedState, uid);
+            final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
+                    mTmpUidBlockedState, uid);
+            previousUidBlockedState.copyFrom(uidBlockedState);
+
+            uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+                    & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+            uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+                    & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+            uidBlockedState.updateEffectiveBlockedReasons();
+
+            oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons;
+            newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+            oldAllowedReasons = previousUidBlockedState.allowedReasons;
+            uidRules = (oldEffectiveBlockedReasons == newEffectiveBlockedReasons)
+                    ? RULE_NONE : uidBlockedState.deriveUidRules();
+
+            if (LOGV) {
+                Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
+                        + ": isForeground=" + isForeground
+                        + ", isDenied=" + isDenied
+                        + ", isAllowed=" + isAllowed
+                        + ", isRestrictedByAdmin=" + isRestrictedByAdmin
+                        + ", oldBlockedState=" + previousUidBlockedState
+                        + ", newBlockedState=" + uidBlockedState
+                        + ", newBlockedMeteredReasons=" + blockedReasonsToString(newBlockedReasons)
+                        + ", newAllowedMeteredReasons=" + allowedReasonsToString(
+                                newAllowedReasons));
+            }
+        }
         if (oldEffectiveBlockedReasons != newEffectiveBlockedReasons) {
-            postBlockedReasonsChangedMsg(uid,
+            handleBlockedReasonsChanged(uid,
                     newEffectiveBlockedReasons, oldEffectiveBlockedReasons);
 
-            postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules());
+            postUidRulesChangedMsg(uid, uidRules);
         }
 
         // Note that the conditionals below are for avoiding unnecessary calls to netd.
@@ -4867,29 +5050,11 @@
         }
         final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND
                 | ALLOWED_METERED_REASON_USER_EXEMPTED;
-        final int oldAllowedReasons = previousUidBlockedState.allowedReasons;
         if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE
                 || (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) {
             setMeteredNetworkAllowlist(uid,
                     (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE);
         }
-
-        if (LOGV) {
-            Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
-                    + ": isForeground=" +isForeground
-                    + ", isDenied=" + isDenied
-                    + ", isAllowed=" + isAllowed
-                    + ", isRestrictedByAdmin=" + isRestrictedByAdmin
-                    + ", oldBlockedState=" + previousUidBlockedState.toString()
-                    + ", newBlockedState="
-                    + ", oldBlockedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString(
-                    uidBlockedState.blockedReasons & BLOCKED_METERED_REASON_MASK)
-                    + ", oldBlockedMeteredEffectiveReasons="
-                    + NetworkPolicyManager.blockedReasonsToString(
-                    uidBlockedState.effectiveBlockedReasons & BLOCKED_METERED_REASON_MASK)
-                    + ", oldAllowedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString(
-                    uidBlockedState.allowedReasons & BLOCKED_METERED_REASON_MASK));
-        }
     }
 
     /**
@@ -4913,7 +5078,12 @@
      */
     @GuardedBy("mUidRulesFirstLock")
     private void updateRulesForPowerRestrictionsUL(int uid) {
-        updateRulesForPowerRestrictionsUL(uid, isUidIdle(uid));
+        updateRulesForPowerRestrictionsUL(uid, PROCESS_STATE_UNKNOWN);
+    }
+
+    @GuardedBy("mUidRulesFirstLock")
+    private void updateRulesForPowerRestrictionsUL(int uid, int uidProcState) {
+        updateRulesForPowerRestrictionsUL(uid, isUidIdle(uid, uidProcState));
     }
 
     /**
@@ -4949,56 +5119,66 @@
 
         final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
 
-        final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
-                mUidBlockedState, uid);
-        final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
-                mTmpUidBlockedState, uid);
-        previousUidBlockedState.copyFrom(uidBlockedState);
+        final int oldEffectiveBlockedReasons;
+        final int newEffectiveBlockedReasons;
+        final int uidRules;
+        synchronized (mUidBlockedState) {
+            final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+                    mUidBlockedState, uid);
+            final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
+                    mTmpUidBlockedState, uid);
+            previousUidBlockedState.copyFrom(uidBlockedState);
 
-        int newBlockedReasons = BLOCKED_REASON_NONE;
-        int newAllowedReasons = ALLOWED_REASON_NONE;
-        newBlockedReasons |= (mRestrictPower ? BLOCKED_REASON_BATTERY_SAVER : 0);
-        newBlockedReasons |= (mDeviceIdleMode ? BLOCKED_REASON_DOZE : 0);
-        newBlockedReasons |= (mLowPowerStandbyActive ? BLOCKED_REASON_LOW_POWER_STANDBY : 0);
-        newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0);
-        newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE);
+            int newBlockedReasons = BLOCKED_REASON_NONE;
+            int newAllowedReasons = ALLOWED_REASON_NONE;
+            newBlockedReasons |= (mRestrictPower ? BLOCKED_REASON_BATTERY_SAVER : 0);
+            newBlockedReasons |= (mDeviceIdleMode ? BLOCKED_REASON_DOZE : 0);
+            newBlockedReasons |= (mLowPowerStandbyActive ? BLOCKED_REASON_LOW_POWER_STANDBY : 0);
+            newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0);
+            newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE);
 
-        newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
-        newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
-        newAllowedReasons |= (isTop ? ALLOWED_REASON_TOP : 0);
-        newAllowedReasons |= (isWhitelistedFromPowerSaveUL(uid, true)
-                ? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
-        newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
-                ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
-        newAllowedReasons |= (uidBlockedState.allowedReasons
-                & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
-        newAllowedReasons |= (isAllowlistedFromLowPowerStandbyUL(uid))
-                ? ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST : 0;
+            newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
+            newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
+            newAllowedReasons |= (isTop ? ALLOWED_REASON_TOP : 0);
+            newAllowedReasons |= (isWhitelistedFromPowerSaveUL(uid, true)
+                    ? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
+            newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
+                    ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
+            newAllowedReasons |= (uidBlockedState.allowedReasons
+                    & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
+            newAllowedReasons |= (isAllowlistedFromLowPowerStandbyUL(uid))
+                    ? ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST : 0;
 
-        uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
-                & BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
-        uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
-                & ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
-        uidBlockedState.updateEffectiveBlockedReasons();
-        if (previousUidBlockedState.effectiveBlockedReasons
-                != uidBlockedState.effectiveBlockedReasons) {
-            postBlockedReasonsChangedMsg(uid,
-                    uidBlockedState.effectiveBlockedReasons,
-                    previousUidBlockedState.effectiveBlockedReasons);
+            uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+                    & BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+            uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+                    & ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+            uidBlockedState.updateEffectiveBlockedReasons();
 
-            postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules());
+            if (LOGV) {
+                Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
+                        + ", isIdle: " + isUidIdle
+                        + ", mRestrictPower: " + mRestrictPower
+                        + ", mDeviceIdleMode: " + mDeviceIdleMode
+                        + ", isForeground=" + isForeground
+                        + ", isTop=" + isTop
+                        + ", isWhitelisted=" + isWhitelisted
+                        + ", oldUidBlockedState=" + previousUidBlockedState
+                        + ", newUidBlockedState=" + uidBlockedState);
+            }
+
+            oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons;
+            newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+            uidRules = oldEffectiveBlockedReasons == newEffectiveBlockedReasons
+                    ? RULE_NONE
+                    : uidBlockedState.deriveUidRules();
         }
+        if (oldEffectiveBlockedReasons != newEffectiveBlockedReasons) {
+            handleBlockedReasonsChanged(uid,
+                    newEffectiveBlockedReasons,
+                    oldEffectiveBlockedReasons);
 
-        if (LOGV) {
-            Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
-                    + ", isIdle: " + isUidIdle
-                    + ", mRestrictPower: " + mRestrictPower
-                    + ", mDeviceIdleMode: " + mDeviceIdleMode
-                    + ", isForeground=" + isForeground
-                    + ", isTop=" + isTop
-                    + ", isWhitelisted=" + isWhitelisted
-                    + ", oldUidBlockedState=" + previousUidBlockedState.toString()
-                    + ", newUidBlockedState=" + uidBlockedState.toString());
+            postUidRulesChangedMsg(uid, uidRules);
         }
     }
 
@@ -5011,7 +5191,7 @@
                         PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
                 synchronized (mUidRulesFirstLock) {
                     mLogger.appIdleStateChanged(uid, idle);
-                    updateRuleForAppIdleUL(uid);
+                    updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
                     updateRulesForPowerRestrictionsUL(uid);
                 }
             } catch (NameNotFoundException nnfe) {
@@ -5027,9 +5207,15 @@
         }
     }
 
+    private void handleBlockedReasonsChanged(int uid, int newEffectiveBlockedReasons,
+            int oldEffectiveBlockedReasons) {
+        mActivityManagerInternal.onUidBlockedReasonsChanged(uid, newEffectiveBlockedReasons);
+        postBlockedReasonsChangedMsg(uid, newEffectiveBlockedReasons, oldEffectiveBlockedReasons);
+    }
+
     private void postBlockedReasonsChangedMsg(int uid, int newEffectiveBlockedReasons,
             int oldEffectiveBlockedReasons) {
-        mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+        mHandler.obtainMessage(MSG_UID_BLOCKED_REASON_CHANGED, uid,
                 newEffectiveBlockedReasons, oldEffectiveBlockedReasons)
                 .sendToTarget();
     }
@@ -5276,7 +5462,7 @@
                     }
                     return true;
                 }
-                case MSG_BLOCKED_REASON_CHANGED: {
+                case MSG_UID_BLOCKED_REASON_CHANGED: {
                     final int uid = msg.arg1;
                     final int newBlockedReasons = msg.arg2;
                     final int oldBlockedReasons = (int) msg.obj;
@@ -5289,6 +5475,35 @@
                     mListeners.finishBroadcast();
                     return true;
                 }
+                case MSG_UIDS_BLOCKED_REASONS_CHANGED: {
+                    final SparseArray<SomeArgs> uidStateUpdates = (SparseArray<SomeArgs>) msg.obj;
+                    final int uidsSize = uidStateUpdates.size();
+                    final int listenersSize = mListeners.beginBroadcast();
+                    for (int i = 0; i < listenersSize; ++i) {
+                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+                        for (int uidIndex = 0; uidIndex < uidsSize; ++uidIndex) {
+                            final int uid = uidStateUpdates.keyAt(uidIndex);
+                            final SomeArgs someArgs = uidStateUpdates.valueAt(uidIndex);
+                            final int oldBlockedReasons = someArgs.argi1;
+                            final int newBlockedReasons = someArgs.argi2;
+                            final int uidRules = someArgs.argi3;
+
+                            dispatchBlockedReasonChanged(listener, uid,
+                                    oldBlockedReasons, newBlockedReasons);
+                            if (LOGV) {
+                                Slog.v(TAG, "Dispatching rules=" + uidRulesToString(uidRules)
+                                        + " for uid=" + uid);
+                            }
+                            dispatchUidRulesChanged(listener, uid, uidRules);
+                        }
+                    }
+                    mListeners.finishBroadcast();
+
+                    for (int uidIndex = 0; uidIndex < uidsSize; ++uidIndex) {
+                        uidStateUpdates.valueAt(uidIndex).recycle();
+                    }
+                    return true;
+                }
                 default: {
                     return false;
                 }
@@ -5301,21 +5516,7 @@
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
                 case UID_MSG_STATE_CHANGED: {
-                    final UidStateCallbackInfo uidStateCallbackInfo =
-                            (UidStateCallbackInfo) msg.obj;
-                    final int uid;
-                    final int procState;
-                    final long procStateSeq;
-                    final int capability;
-                    synchronized (mUidStateCallbackInfos) {
-                        uid = uidStateCallbackInfo.uid;
-                        procState = uidStateCallbackInfo.procState;
-                        procStateSeq = uidStateCallbackInfo.procStateSeq;
-                        capability = uidStateCallbackInfo.capability;
-                        uidStateCallbackInfo.isPending = false;
-                    }
-
-                    handleUidChanged(uid, procState, procStateSeq, capability);
+                    handleUidChanged((UidStateCallbackInfo) msg.obj);
                     return true;
                 }
                 case UID_MSG_GONE: {
@@ -5330,17 +5531,28 @@
         }
     };
 
-    void handleUidChanged(int uid, int procState, long procStateSeq,
-            @ProcessCapability int capability) {
+    void handleUidChanged(@NonNull UidStateCallbackInfo uidStateCallbackInfo) {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged");
         try {
             boolean updated;
+            final int uid;
+            final int procState;
+            final long procStateSeq;
+            final int capability;
             synchronized (mUidRulesFirstLock) {
+                synchronized (mUidStateCallbackInfos) {
+                    uid = uidStateCallbackInfo.uid;
+                    procState = uidStateCallbackInfo.procState;
+                    procStateSeq = uidStateCallbackInfo.procStateSeq;
+                    capability = uidStateCallbackInfo.capability;
+                    uidStateCallbackInfo.isPending = false;
+                }
+
                 // We received a uid state change callback, add it to the history so that it
                 // will be useful for debugging.
                 mLogger.uidStateChanged(uid, procState, procStateSeq, capability);
                 // Now update the network policy rules as per the updated uid state.
-                updated = updateUidStateUL(uid, procState, capability);
+                updated = updateUidStateUL(uid, procState, procStateSeq, capability);
                 // Updating the network rules is done, so notify AMS about this.
                 mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq);
             }
@@ -5437,6 +5649,12 @@
         if (LOGV) Slog.v(TAG, "setMeteredNetworkDenylist " + uid + ": " + enable);
         try {
             mNetworkManager.setUidOnMeteredNetworkDenylist(uid, enable);
+            mLogger.meteredDenylistChanged(uid, enable);
+            if (Process.isApplicationUid(uid)) {
+                final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
+                mNetworkManager.setUidOnMeteredNetworkDenylist(sdkSandboxUid, enable);
+                mLogger.meteredDenylistChanged(sdkSandboxUid, enable);
+            }
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem setting denylist (" + enable + ") rules for " + uid, e);
         } catch (RemoteException e) {
@@ -5448,6 +5666,12 @@
         if (LOGV) Slog.v(TAG, "setMeteredNetworkAllowlist " + uid + ": " + enable);
         try {
             mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, enable);
+            mLogger.meteredAllowlistChanged(uid, enable);
+            if (Process.isApplicationUid(uid)) {
+                final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
+                mNetworkManager.setUidOnMeteredNetworkAllowlist(sdkSandboxUid, enable);
+                mLogger.meteredAllowlistChanged(sdkSandboxUid, enable);
+            }
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem setting allowlist (" + enable + ") rules for " + uid, e);
         } catch (RemoteException e) {
@@ -5486,12 +5710,31 @@
         }
     }
 
+    private void addSdkSandboxUidsIfNeeded(SparseIntArray uidRules) {
+        final int size = uidRules.size();
+        final SparseIntArray sdkSandboxUids = new SparseIntArray();
+        for (int index = 0; index < size; index++) {
+            final int uid = uidRules.keyAt(index);
+            final int rule = uidRules.valueAt(index);
+            if (Process.isApplicationUid(uid)) {
+                sdkSandboxUids.put(Process.toSdkSandboxUid(uid), rule);
+            }
+        }
+
+        for (int index = 0; index < sdkSandboxUids.size(); index++) {
+            final int uid = sdkSandboxUids.keyAt(index);
+            final int rule = sdkSandboxUids.valueAt(index);
+            uidRules.put(uid, rule);
+        }
+    }
+
     /**
      * Set uid rules on a particular firewall chain. This is going to synchronize the rules given
      * here to netd.  It will clean up dead rules and make sure the target chain only contains rules
      * specified here.
      */
     private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
+        addSdkSandboxUidsIfNeeded(uidRules);
         try {
             int size = uidRules.size();
             int[] uids = new int[size];
@@ -5534,6 +5777,11 @@
             try {
                 mNetworkManager.setFirewallUidRule(chain, uid, rule);
                 mLogger.uidFirewallRuleChanged(chain, uid, rule);
+                if (Process.isApplicationUid(uid)) {
+                    final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
+                    mNetworkManager.setFirewallUidRule(chain, sdkSandboxUid, rule);
+                    mLogger.uidFirewallRuleChanged(chain, sdkSandboxUid, rule);
+                }
             } catch (IllegalStateException e) {
                 Log.wtf(TAG, "problem setting firewall uid rules", e);
             } catch (RemoteException e) {
@@ -5570,22 +5818,28 @@
      */
     private void resetUidFirewallRules(int uid) {
         try {
-            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT);
-            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
-            mNetworkManager
-                    .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT);
-            mNetworkManager
-                    .setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid, FIREWALL_RULE_DEFAULT);
-            mNetworkManager
-                    .setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid,
-                            FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid,
+                    FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid,
+                    FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid,
+                    FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid,
+                    FIREWALL_RULE_DEFAULT);
+            mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid,
+                    FIREWALL_RULE_DEFAULT);
             mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
+            mLogger.meteredAllowlistChanged(uid, false);
             mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
+            mLogger.meteredDenylistChanged(uid, false);
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem resetting firewall uid rules for " + uid, e);
         } catch (RemoteException e) {
             // ignored; service lives in system_server
         }
+        if (Process.isApplicationUid(uid)) {
+            resetUidFirewallRules(Process.toSdkSandboxUid(uid));
+        }
     }
 
     @Deprecated
@@ -5745,7 +5999,7 @@
 
         mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         int blockedReasons;
-        synchronized (mUidRulesFirstLock) {
+        synchronized (mUidBlockedState) {
             final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
             blockedReasons = uidBlockedState == null
                     ? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons;
@@ -5763,7 +6017,7 @@
     @Override
     public boolean isUidRestrictedOnMeteredNetworks(int uid) {
         mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
-        synchronized (mUidRulesFirstLock) {
+        synchronized (mUidBlockedState) {
             final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
             int blockedReasons = uidBlockedState == null
                     ? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons;
@@ -6011,10 +6265,6 @@
         return restrictedUids != null && restrictedUids.contains(uid);
     }
 
-    private static boolean hasRule(int uidRules, int rule) {
-        return (uidRules & rule) != 0;
-    }
-
     private static boolean getBooleanDefeatingNullable(@Nullable PersistableBundle bundle,
             String key, boolean defaultValue) {
         return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
@@ -6030,16 +6280,39 @@
         return uidBlockedState;
     }
 
+    private int getEffectiveBlockedReasons(int uid) {
+        synchronized (mUidBlockedState) {
+            final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+            return uidBlockedState == null
+                    ? BLOCKED_REASON_NONE
+                    : uidBlockedState.effectiveBlockedReasons;
+        }
+    }
+
+    private int getBlockedReasons(int uid) {
+        synchronized (mUidBlockedState) {
+            final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+            return uidBlockedState == null
+                    ? BLOCKED_REASON_NONE
+                    : uidBlockedState.blockedReasons;
+        }
+    }
+
     @VisibleForTesting
     static final class UidBlockedState {
         public int blockedReasons;
         public int allowedReasons;
         public int effectiveBlockedReasons;
 
+        private UidBlockedState(int blockedReasons, int allowedReasons,
+                int effectiveBlockedReasons) {
+            this.blockedReasons = blockedReasons;
+            this.allowedReasons = allowedReasons;
+            this.effectiveBlockedReasons = effectiveBlockedReasons;
+        }
+
         UidBlockedState() {
-            blockedReasons = BLOCKED_REASON_NONE;
-            allowedReasons = ALLOWED_REASON_NONE;
-            effectiveBlockedReasons = BLOCKED_REASON_NONE;
+            this(BLOCKED_REASON_NONE, ALLOWED_REASON_NONE, BLOCKED_REASON_NONE);
         }
 
         void updateEffectiveBlockedReasons() {
@@ -6276,7 +6549,7 @@
                 }
             }
             if (LOGV) {
-                Slog.v(TAG, "uidBlockedState=" + this.toString()
+                Slog.v(TAG, "uidBlockedState=" + this
                         + " -> uidRule=" + uidRulesToString(uidRule));
             }
             return uidRule;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fad18d2..7d1dbc5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13257,7 +13257,8 @@
             // Don't use profiles since that may cause compilation to be skipped.
             final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
                     new DexoptOptions(packageName,
-                            getDefaultCompilerFilter(),
+                            REASON_CMDLINE,
+                            getDefaultCompilerFilter(), null /* splitName */,
                             DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
 
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 89470ec..5c305c6 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.vcn.VcnManager;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.ParcelUuid;
@@ -47,6 +48,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.util.PersistableBundleUtils;
+import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -95,6 +98,10 @@
 
     // TODO (Android T+): Add ability to handle multiple subIds per slot.
     @NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>();
+
+    @NonNull
+    private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap = new HashMap<>();
+
     @NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener;
 
     @NonNull
@@ -250,7 +257,10 @@
 
         final TelephonySubscriptionSnapshot newSnapshot =
                 new TelephonySubscriptionSnapshot(
-                        mDeps.getActiveDataSubscriptionId(), newSubIdToInfoMap, privilegedPackages);
+                        mDeps.getActiveDataSubscriptionId(),
+                        newSubIdToInfoMap,
+                        mSubIdToCarrierConfigMap,
+                        privilegedPackages);
 
         // If snapshot was meaningfully updated, fire the callback
         if (!newSnapshot.equals(mCurrentSnapshot)) {
@@ -311,47 +321,77 @@
         }
 
         if (SubscriptionManager.isValidSubscriptionId(subId)) {
-            final PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
-            if (mDeps.isConfigForIdentifiedCarrier(carrierConfigs)) {
+            final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
+            if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
                 mReadySubIdsBySlotId.put(slotId, subId);
+
+                final PersistableBundle minimized =
+                        PersistableBundleUtils.minimizeBundle(
+                                carrierConfig, VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+                if (minimized != null) {
+                    mSubIdToCarrierConfigMap.put(subId, new PersistableBundleWrapper(minimized));
+                }
                 handleSubscriptionsChanged();
             }
         } else {
-            mReadySubIdsBySlotId.remove(slotId);
+            final Integer oldSubid = mReadySubIdsBySlotId.remove(slotId);
+            if (oldSubid != null) {
+                mSubIdToCarrierConfigMap.remove(oldSubid);
+            }
             handleSubscriptionsChanged();
         }
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     void setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId) {
+        mReadySubIdsBySlotId.clear();
         mReadySubIdsBySlotId.putAll(readySubIdsBySlotId);
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
+    void setSubIdToCarrierConfigMap(
+            Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap) {
+        mSubIdToCarrierConfigMap.clear();
+        mSubIdToCarrierConfigMap.putAll(subIdToCarrierConfigMap);
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
     Map<Integer, Integer> getReadySubIdsBySlotId() {
         return Collections.unmodifiableMap(mReadySubIdsBySlotId);
     }
 
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    Map<Integer, PersistableBundleWrapper> getSubIdToCarrierConfigMap() {
+        return Collections.unmodifiableMap(mSubIdToCarrierConfigMap);
+    }
+
     /** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
     public static class TelephonySubscriptionSnapshot {
         private final int mActiveDataSubId;
         private final Map<Integer, SubscriptionInfo> mSubIdToInfoMap;
+        private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap;
         private final Map<ParcelUuid, Set<String>> mPrivilegedPackages;
 
         public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT =
                 new TelephonySubscriptionSnapshot(
-                        INVALID_SUBSCRIPTION_ID, Collections.emptyMap(), Collections.emptyMap());
+                        INVALID_SUBSCRIPTION_ID,
+                        Collections.emptyMap(),
+                        Collections.emptyMap(),
+                        Collections.emptyMap());
 
         @VisibleForTesting(visibility = Visibility.PRIVATE)
         TelephonySubscriptionSnapshot(
                 int activeDataSubId,
                 @NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap,
+                @NonNull Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap,
                 @NonNull Map<ParcelUuid, Set<String>> privilegedPackages) {
             mActiveDataSubId = activeDataSubId;
             Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null");
             Objects.requireNonNull(privilegedPackages, "privilegedPackages was null");
+            Objects.requireNonNull(subIdToCarrierConfigMap, "subIdToCarrierConfigMap was null");
 
             mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap);
+            mSubIdToCarrierConfigMap = Collections.unmodifiableMap(subIdToCarrierConfigMap);
 
             final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
             for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
@@ -423,9 +463,40 @@
                     : false;
         }
 
+        /**
+         * Retrieves a carrier config for a subscription in the provided group.
+         *
+         * <p>This method will prioritize non-opportunistic subscriptions, but will use the a
+         * carrier config for an opportunistic subscription if no other subscriptions are found.
+         */
+        @Nullable
+        public PersistableBundleWrapper getCarrierConfigForSubGrp(@NonNull ParcelUuid subGrp) {
+            PersistableBundleWrapper result = null;
+
+            for (int subId : getAllSubIdsInGroup(subGrp)) {
+                final PersistableBundleWrapper config = mSubIdToCarrierConfigMap.get(subId);
+                if (config != null) {
+                    result = config;
+
+                    // Attempt to use (any) non-opportunistic subscription. If this subscription is
+                    // opportunistic, continue and try to find a non-opportunistic subscription,
+                    // using the opportunistic ones as a last resort.
+                    if (!isOpportunistic(subId)) {
+                        return config;
+                    }
+                }
+            }
+
+            return result;
+        }
+
         @Override
         public int hashCode() {
-            return Objects.hash(mActiveDataSubId, mSubIdToInfoMap, mPrivilegedPackages);
+            return Objects.hash(
+                    mActiveDataSubId,
+                    mSubIdToInfoMap,
+                    mSubIdToCarrierConfigMap,
+                    mPrivilegedPackages);
         }
 
         @Override
@@ -438,6 +509,7 @@
 
             return mActiveDataSubId == other.mActiveDataSubId
                     && mSubIdToInfoMap.equals(other.mSubIdToInfoMap)
+                    && mSubIdToCarrierConfigMap.equals(other.mSubIdToCarrierConfigMap)
                     && mPrivilegedPackages.equals(other.mPrivilegedPackages);
         }
 
@@ -448,6 +520,7 @@
 
             pw.println("mActiveDataSubId: " + mActiveDataSubId);
             pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap);
+            pw.println("mSubIdToCarrierConfigMap: " + mSubIdToCarrierConfigMap);
             pw.println("mPrivilegedPackages: " + mPrivilegedPackages);
 
             pw.decreaseIndent();
@@ -458,6 +531,7 @@
             return "TelephonySubscriptionSnapshot{ "
                     + "mActiveDataSubId=" + mActiveDataSubId
                     + ", mSubIdToInfoMap=" + mSubIdToInfoMap
+                    + ", mSubIdToCarrierConfigMap=" + mSubIdToCarrierConfigMap
                     + ", mPrivilegedPackages=" + mPrivilegedPackages
                     + " }";
         }
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index cefd8ef..05df22f 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -61,6 +61,7 @@
 import android.net.ipsec.ike.IkeSession;
 import android.net.ipsec.ike.IkeSessionCallback;
 import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
 import android.net.ipsec.ike.IkeSessionParams;
 import android.net.ipsec.ike.IkeTunnelConnectionParams;
 import android.net.ipsec.ike.exceptions.IkeException;
@@ -509,6 +510,42 @@
         }
     }
 
+    /**
+     * Sent when an IKE session connection information has changed.
+     *
+     * <p>This signal is always fired before EVENT_SETUP_COMPLETED and EVENT_MIGRATION_COMPLETED.
+     *
+     * <p>Only relevant in the Connecting and Connected state.
+     *
+     * @param arg1 The session token for the IKE Session whose connection information has changed,
+     *     used to prevent out-of-date signals from propagating.
+     * @param obj @NonNull An EventIkeConnectionInfoChangedInfo instance with relevant data.
+     */
+    private static final int EVENT_IKE_CONNECTION_INFO_CHANGED = 12;
+
+    private static class EventIkeConnectionInfoChangedInfo implements EventInfo {
+        @NonNull public final IkeSessionConnectionInfo ikeConnectionInfo;
+
+        EventIkeConnectionInfoChangedInfo(@NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
+            this.ikeConnectionInfo = ikeConnectionInfo;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(ikeConnectionInfo);
+        }
+
+        @Override
+        public boolean equals(@Nullable Object other) {
+            if (!(other instanceof EventIkeConnectionInfoChangedInfo)) {
+                return false;
+            }
+
+            final EventIkeConnectionInfoChangedInfo rhs = (EventIkeConnectionInfoChangedInfo) other;
+            return Objects.equals(ikeConnectionInfo, rhs.ikeConnectionInfo);
+        }
+    }
+
     @VisibleForTesting(visibility = Visibility.PRIVATE)
     @NonNull
     final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -624,6 +661,14 @@
     private UnderlyingNetworkRecord mUnderlying;
 
     /**
+     * The current IKE Session connection information
+     *
+     * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating
+     * states, @Nullable otherwise.
+     */
+    private IkeSessionConnectionInfo mIkeConnectionInfo;
+
+    /**
      * The active IKE session.
      *
      * <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
@@ -1197,6 +1242,14 @@
                 exceptionMessage);
     }
 
+    private void ikeConnectionInfoChanged(
+            int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
+        sendMessageAndAcquireWakeLock(
+                EVENT_IKE_CONNECTION_INFO_CHANGED,
+                token,
+                new EventIkeConnectionInfoChangedInfo(ikeConnectionInfo));
+    }
+
     private void sessionClosed(int token, @Nullable Exception exception) {
         if (exception != null) {
             notifyStatusCallbackForSessionClosed(exception);
@@ -1313,7 +1366,8 @@
                 case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
                 case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough
                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough
-                case EVENT_MIGRATION_COMPLETED:
+                case EVENT_MIGRATION_COMPLETED: // Fallthrough
+                case EVENT_IKE_CONNECTION_INFO_CHANGED:
                     logUnexpectedEvent(msg.what);
                     break;
                 default:
@@ -1592,6 +1646,7 @@
                     transitionTo(mDisconnectingState);
                     break;
                 case EVENT_SETUP_COMPLETED: // fallthrough
+                case EVENT_IKE_CONNECTION_INFO_CHANGED: // fallthrough
                 case EVENT_TRANSFORM_CREATED:
                     // Child setup complete; move to ConnectedState for NetworkAgent registration
                     deferMessage(msg);
@@ -1614,12 +1669,17 @@
         protected void updateNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
                 @NonNull VcnNetworkAgent agent,
-                @NonNull VcnChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig,
+                @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
             final LinkProperties lp =
                     buildConnectedLinkProperties(
-                            mConnectionConfig, tunnelIface, childConfig, mUnderlying);
+                            mConnectionConfig,
+                            tunnelIface,
+                            childConfig,
+                            mUnderlying,
+                            ikeConnectionInfo);
 
             agent.sendNetworkCapabilities(caps);
             agent.sendLinkProperties(lp);
@@ -1630,12 +1690,17 @@
 
         protected VcnNetworkAgent buildNetworkAgent(
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull VcnChildSessionConfiguration childConfig) {
+                @NonNull VcnChildSessionConfiguration childConfig,
+                @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
             final NetworkCapabilities caps =
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
             final LinkProperties lp =
                     buildConnectedLinkProperties(
-                            mConnectionConfig, tunnelIface, childConfig, mUnderlying);
+                            mConnectionConfig,
+                            tunnelIface,
+                            childConfig,
+                            mUnderlying,
+                            ikeConnectionInfo);
             final NetworkAgentConfig nac =
                     new NetworkAgentConfig.Builder()
                             .setLegacyType(ConnectivityManager.TYPE_MOBILE)
@@ -1838,7 +1903,11 @@
                     mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
 
                     setupInterfaceAndNetworkAgent(
-                            mCurrentToken, mTunnelIface, mChildConfig, oldChildConfig);
+                            mCurrentToken,
+                            mTunnelIface,
+                            mChildConfig,
+                            oldChildConfig,
+                            mIkeConnectionInfo);
                     break;
                 case EVENT_DISCONNECT_REQUESTED:
                     handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
@@ -1852,6 +1921,10 @@
 
                     handleMigrationCompleted(migrationCompletedInfo);
                     break;
+                case EVENT_IKE_CONNECTION_INFO_CHANGED:
+                    mIkeConnectionInfo =
+                            ((EventIkeConnectionInfoChangedInfo) msg.obj).ikeConnectionInfo;
+                    break;
                 default:
                     logUnhandledMessage(msg);
                     break;
@@ -1875,7 +1948,7 @@
                     migrationCompletedInfo.outTransform,
                     IpSecManager.DIRECTION_OUT);
 
-            updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+            updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo);
 
             // Trigger re-validation after migration events.
             mConnectivityManager.reportNetworkConnectivity(
@@ -1906,7 +1979,8 @@
                 // Network not yet set up, or child not yet connected.
                 if (mNetworkAgent != null && mChildConfig != null) {
                     // If only network properties changed and agent is active, update properties
-                    updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+                    updateNetworkAgent(
+                            mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo);
                 }
             }
         }
@@ -1915,13 +1989,14 @@
                 int token,
                 @NonNull IpSecTunnelInterface tunnelIface,
                 @NonNull VcnChildSessionConfiguration childConfig,
-                @NonNull VcnChildSessionConfiguration oldChildConfig) {
+                @NonNull VcnChildSessionConfiguration oldChildConfig,
+                @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
             setupInterface(token, tunnelIface, childConfig, oldChildConfig);
 
             if (mNetworkAgent == null) {
-                mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
+                mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig, ikeConnectionInfo);
             } else {
-                updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+                updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig, ikeConnectionInfo);
 
                 // mNetworkAgent not null, so the VCN Network has already been established. Clear
                 // the failed attempt counter and safe mode alarm since this transition is complete.
@@ -2098,7 +2173,8 @@
             @NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
             @NonNull IpSecTunnelInterface tunnelIface,
             @NonNull VcnChildSessionConfiguration childConfig,
-            @Nullable UnderlyingNetworkRecord underlying) {
+            @Nullable UnderlyingNetworkRecord underlying,
+            @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
         final IkeTunnelConnectionParams ikeTunnelParams =
                 gatewayConnectionConfig.getTunnelConnectionParams();
         final LinkProperties lp = new LinkProperties();
@@ -2139,7 +2215,8 @@
                 MtuUtils.getMtu(
                         ikeTunnelParams.getTunnelModeChildSessionParams().getSaProposals(),
                         gatewayConnectionConfig.getMaxMtu(),
-                        underlyingMtu));
+                        underlyingMtu,
+                        ikeConnectionInfo.getLocalAddress() instanceof Inet4Address));
 
         return lp;
     }
@@ -2154,7 +2231,7 @@
         @Override
         public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
             logDbg("IkeOpened for token " + mToken);
-            // Nothing to do here.
+            ikeConnectionInfoChanged(mToken, ikeSessionConfig.getIkeSessionConnectionInfo());
         }
 
         @Override
@@ -2174,6 +2251,13 @@
             logInfo("IkeError for token " + mToken, exception);
             // Non-fatal, log and continue.
         }
+
+        @Override
+        public void onIkeSessionConnectionInfoChanged(
+                @NonNull IkeSessionConnectionInfo connectionInfo) {
+            logDbg("onIkeSessionConnectionInfoChanged for token " + mToken);
+            ikeConnectionInfoChanged(mToken, connectionInfo);
+        }
     }
 
     /** Implementation of ChildSessionCallback, exposed for testing. */
@@ -2350,6 +2434,11 @@
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
+    IkeSessionConnectionInfo getIkeConnectionInfo() {
+        return mIkeConnectionInfo;
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
     boolean isQuitting() {
         return mIsQuitting.getValue();
     }
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index c96c1ee..2f84fdd 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -24,6 +24,7 @@
 import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
 
 import static com.android.server.VcnManagementService.LOCAL_LOG;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,7 +35,6 @@
 import android.net.vcn.VcnUnderlyingNetworkTemplate;
 import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
 import android.os.ParcelUuid;
-import android.os.PersistableBundle;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
@@ -81,7 +81,7 @@
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
-            PersistableBundle carrierConfig) {
+            PersistableBundleWrapper carrierConfig) {
         // mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
 
         if (networkRecord.isBlocked) {
@@ -119,7 +119,7 @@
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
-            PersistableBundle carrierConfig) {
+            PersistableBundleWrapper carrierConfig) {
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
         final boolean isSelectedUnderlyingNetwork =
                 currentlySelected != null
@@ -181,7 +181,7 @@
             VcnWifiUnderlyingNetworkTemplate networkPriority,
             UnderlyingNetworkRecord networkRecord,
             UnderlyingNetworkRecord currentlySelected,
-            PersistableBundle carrierConfig) {
+            PersistableBundleWrapper carrierConfig) {
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
 
         if (!caps.hasTransport(TRANSPORT_WIFI)) {
@@ -204,7 +204,7 @@
     private static boolean isWifiRssiAcceptable(
             UnderlyingNetworkRecord networkRecord,
             UnderlyingNetworkRecord currentlySelected,
-            PersistableBundle carrierConfig) {
+            PersistableBundleWrapper carrierConfig) {
         final NetworkCapabilities caps = networkRecord.networkCapabilities;
         final boolean isSelectedNetwork =
                 currentlySelected != null
@@ -314,7 +314,7 @@
         return false;
     }
 
-    static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+    static int getWifiEntryRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) {
         if (carrierConfig != null) {
             return carrierConfig.getInt(
                     VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
@@ -323,7 +323,7 @@
         return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
     }
 
-    static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+    static int getWifiExitRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) {
         if (carrierConfig != null) {
             return carrierConfig.getInt(
                     VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index a3babf7..d474c5d 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -21,7 +21,7 @@
 import static com.android.server.VcnManagementService.LOCAL_LOG;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold;
-import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.isOpportunistic;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -37,8 +37,6 @@
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.ParcelUuid;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
@@ -51,7 +49,6 @@
 import com.android.server.vcn.util.LogUtils;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
@@ -87,7 +84,7 @@
     @Nullable private UnderlyingNetworkListener mRouteSelectionCallback;
 
     @NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
-    @Nullable private PersistableBundle mCarrierConfig;
+    @Nullable private PersistableBundleWrapper mCarrierConfig;
     private boolean mIsQuitting = false;
 
     @Nullable private UnderlyingNetworkRecord mCurrentRecord;
@@ -124,25 +121,7 @@
                 .getSystemService(TelephonyManager.class)
                 .registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener);
 
-        // TODO: Listen for changes in carrier config that affect this.
-        for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
-            PersistableBundle config =
-                    mVcnContext
-                            .getContext()
-                            .getSystemService(CarrierConfigManager.class)
-                            .getConfigForSubId(subId);
-
-            if (config != null) {
-                mCarrierConfig = config;
-
-                // Attempt to use (any) non-opportunistic subscription. If this subscription is
-                // opportunistic, continue and try to find a non-opportunistic subscription, using
-                // the opportunistic ones as a last resort.
-                if (!isOpportunistic(mLastSnapshot, Collections.singleton(subId))) {
-                    break;
-                }
-            }
-        }
+        mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
 
         registerOrUpdateNetworkRequests();
     }
@@ -334,6 +313,9 @@
         final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
         mLastSnapshot = newSnapshot;
 
+        // Update carrier config
+        mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
+
         // Only trigger re-registration if subIds in this group have changed
         if (oldSnapshot
                 .getAllSubIdsInGroup(mSubscriptionGroup)
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index 06f9280..319680e 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -16,6 +16,8 @@
 
 package com.android.server.vcn.routeselection;
 
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.net.LinkProperties;
@@ -23,7 +25,6 @@
 import android.net.NetworkCapabilities;
 import android.net.vcn.VcnUnderlyingNetworkTemplate;
 import android.os.ParcelUuid;
-import android.os.PersistableBundle;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -68,7 +69,7 @@
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
-            PersistableBundle carrierConfig) {
+            PersistableBundleWrapper carrierConfig) {
         // Never changes after the underlying network record is created.
         if (mPriorityClass == PRIORITY_CLASS_INVALID) {
             mPriorityClass =
@@ -113,7 +114,7 @@
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
-            PersistableBundle carrierConfig) {
+            PersistableBundleWrapper carrierConfig) {
         return (left, right) -> {
             final int leftIndex =
                     left.getOrCalculatePriorityClass(
@@ -167,7 +168,7 @@
             ParcelUuid subscriptionGroup,
             TelephonySubscriptionSnapshot snapshot,
             UnderlyingNetworkRecord currentlySelected,
-            PersistableBundle carrierConfig) {
+            PersistableBundleWrapper carrierConfig) {
         pw.println("UnderlyingNetworkRecord:");
         pw.increaseIndent();
 
diff --git a/services/core/java/com/android/server/vcn/util/MtuUtils.java b/services/core/java/com/android/server/vcn/util/MtuUtils.java
index 5d40cca..356c71f 100644
--- a/services/core/java/com/android/server/vcn/util/MtuUtils.java
+++ b/services/core/java/com/android/server/vcn/util/MtuUtils.java
@@ -51,9 +51,20 @@
     /**
      * Max ESP overhead possible
      *
-     * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad + NextHeader)
+     * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad Length + Next
+     * Header). Note: Payload data, Pad Length and Next Header will need to be padded to be multiple
+     * of the block size of a cipher, and at the same time be aligned on a 4-byte boundary.
      */
-    private static final int GENERIC_ESP_OVERHEAD_MAX = 78;
+    private static final int GENERIC_ESP_OVERHEAD_MAX_V4 = 78;
+
+    /**
+     * Max ESP overhead possible
+     *
+     * <p>40 (Outer IPv6) + 4 (SPI) + 4 (Seq) + 2 (Pad Length + Next Header). Note: Payload data,
+     * Pad Length and Next Header will need to be padded to be multiple of the block size of a
+     * cipher, and at the same time be aligned on a 4-byte boundary.
+     */
+    private static final int GENERIC_ESP_OVERHEAD_MAX_V6 = 50;
 
     /** Maximum overheads of authentication algorithms, keyed on IANA-defined constants */
     private static final Map<Integer, Integer> AUTH_ALGORITHM_OVERHEAD;
@@ -108,7 +119,10 @@
      * </ul>
      */
     public static int getMtu(
-            @NonNull List<ChildSaProposal> childProposals, int maxMtu, int underlyingMtu) {
+            @NonNull List<ChildSaProposal> childProposals,
+            int maxMtu,
+            int underlyingMtu,
+            boolean isIpv4) {
         if (underlyingMtu <= 0) {
             return IPV6_MIN_MTU;
         }
@@ -145,10 +159,13 @@
             }
         }
 
+        final int genericEspOverheadMax =
+                isIpv4 ? GENERIC_ESP_OVERHEAD_MAX_V4 : GENERIC_ESP_OVERHEAD_MAX_V6;
+
         // Return minimum of maxMtu, and the adjusted MTUs based on algorithms.
-        final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - GENERIC_ESP_OVERHEAD_MAX;
+        final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - genericEspOverheadMax;
         final int normalModeMtu =
-                underlyingMtu - maxCryptOverhead - maxAuthOverhead - GENERIC_ESP_OVERHEAD_MAX;
+                underlyingMtu - maxCryptOverhead - maxAuthOverhead - genericEspOverheadMax;
         return Math.min(Math.min(maxMtu, combinedModeMtu), normalModeMtu);
     }
 }
diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
index 1c675c2..08e8eebb 100644
--- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
+++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
@@ -28,11 +28,13 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.TreeSet;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -354,4 +356,182 @@
             }
         }
     }
+
+    /**
+     * Returns a copy of the persistable bundle with only the specified keys
+     *
+     * <p>This allows for holding minimized copies for memory-saving purposes.
+     */
+    @NonNull
+    public static PersistableBundle minimizeBundle(
+            @NonNull PersistableBundle bundle, String... keys) {
+        final PersistableBundle minimized = new PersistableBundle();
+
+        if (bundle == null) {
+            return minimized;
+        }
+
+        for (String key : keys) {
+            if (bundle.containsKey(key)) {
+                final Object value = bundle.get(key);
+                if (value == null) {
+                    continue;
+                }
+
+                if (value instanceof Boolean) {
+                    minimized.putBoolean(key, (Boolean) value);
+                } else if (value instanceof boolean[]) {
+                    minimized.putBooleanArray(key, (boolean[]) value);
+                } else if (value instanceof Double) {
+                    minimized.putDouble(key, (Double) value);
+                } else if (value instanceof double[]) {
+                    minimized.putDoubleArray(key, (double[]) value);
+                } else if (value instanceof Integer) {
+                    minimized.putInt(key, (Integer) value);
+                } else if (value instanceof int[]) {
+                    minimized.putIntArray(key, (int[]) value);
+                } else if (value instanceof Long) {
+                    minimized.putLong(key, (Long) value);
+                } else if (value instanceof long[]) {
+                    minimized.putLongArray(key, (long[]) value);
+                } else if (value instanceof String) {
+                    minimized.putString(key, (String) value);
+                } else if (value instanceof String[]) {
+                    minimized.putStringArray(key, (String[]) value);
+                } else if (value instanceof PersistableBundle) {
+                    minimized.putPersistableBundle(key, (PersistableBundle) value);
+                } else {
+                    continue;
+                }
+            }
+        }
+
+        return minimized;
+    }
+
+    /** Builds a stable hashcode */
+    public static int getHashCode(@Nullable PersistableBundle bundle) {
+        if (bundle == null) {
+            return -1;
+        }
+
+        int iterativeHashcode = 0;
+        TreeSet<String> treeSet = new TreeSet<>(bundle.keySet());
+        for (String key : treeSet) {
+            Object val = bundle.get(key);
+            if (val instanceof PersistableBundle) {
+                iterativeHashcode =
+                        Objects.hash(iterativeHashcode, key, getHashCode((PersistableBundle) val));
+            } else {
+                iterativeHashcode = Objects.hash(iterativeHashcode, key, val);
+            }
+        }
+
+        return iterativeHashcode;
+    }
+
+    /** Checks for persistable bundle equality */
+    public static boolean isEqual(
+            @Nullable PersistableBundle left, @Nullable PersistableBundle right) {
+        // Check for pointer equality & null equality
+        if (Objects.equals(left, right)) {
+            return true;
+        }
+
+        // If only one of the two is null, but not the other, not equal by definition.
+        if (Objects.isNull(left) != Objects.isNull(right)) {
+            return false;
+        }
+
+        if (!left.keySet().equals(right.keySet())) {
+            return false;
+        }
+
+        for (String key : left.keySet()) {
+            Object leftVal = left.get(key);
+            Object rightVal = right.get(key);
+
+            // Check for equality
+            if (Objects.equals(leftVal, rightVal)) {
+                continue;
+            } else if (Objects.isNull(leftVal) != Objects.isNull(rightVal)) {
+                // If only one of the two is null, but not the other, not equal by definition.
+                return false;
+            } else if (!Objects.equals(leftVal.getClass(), rightVal.getClass())) {
+                // If classes are different, not equal by definition.
+                return false;
+            }
+            if (leftVal instanceof PersistableBundle) {
+                if (!isEqual((PersistableBundle) leftVal, (PersistableBundle) rightVal)) {
+                    return false;
+                }
+            } else if (leftVal.getClass().isArray()) {
+                if (leftVal instanceof boolean[]) {
+                    if (!Arrays.equals((boolean[]) leftVal, (boolean[]) rightVal)) {
+                        return false;
+                    }
+                } else if (leftVal instanceof double[]) {
+                    if (!Arrays.equals((double[]) leftVal, (double[]) rightVal)) {
+                        return false;
+                    }
+                } else if (leftVal instanceof int[]) {
+                    if (!Arrays.equals((int[]) leftVal, (int[]) rightVal)) {
+                        return false;
+                    }
+                } else if (leftVal instanceof long[]) {
+                    if (!Arrays.equals((long[]) leftVal, (long[]) rightVal)) {
+                        return false;
+                    }
+                } else if (!Arrays.equals((Object[]) leftVal, (Object[]) rightVal)) {
+                    return false;
+                }
+            } else {
+                if (!Objects.equals(leftVal, rightVal)) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Wrapper class around PersistableBundles to allow equality comparisons
+     *
+     * <p>This class exposes the minimal getters to retrieve values.
+     */
+    public static class PersistableBundleWrapper {
+        @NonNull private final PersistableBundle mBundle;
+
+        public PersistableBundleWrapper(@NonNull PersistableBundle bundle) {
+            mBundle = Objects.requireNonNull(bundle, "Bundle was null");
+        }
+
+        /**
+         * Retrieves the integer associated with the provided key.
+         *
+         * @param key the string key to query
+         * @param defaultValue the value to return if key does not exist
+         * @return the int value, or the default
+         */
+        public int getInt(String key, int defaultValue) {
+            return mBundle.getInt(key, defaultValue);
+        }
+
+        @Override
+        public int hashCode() {
+            return getHashCode(mBundle);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof PersistableBundleWrapper)) {
+                return false;
+            }
+
+            final PersistableBundleWrapper other = (PersistableBundleWrapper) obj;
+
+            return isEqual(mBundle, other.mBundle);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 3ccb06c..ab8527e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1123,7 +1123,7 @@
 
     /** Makes the process have top state before oom-adj is computed from a posted message. */
     void addToPendingTop() {
-        mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
+        mAtm.mAmInternal.addPendingTopUid(mUid, mPid, mThread);
     }
 
     void updateServiceConnectionActivities() {
@@ -1176,7 +1176,7 @@
         }
         // update ActivityManagerService.PendingStartActivityUids list.
         if (topProcessState == ActivityManager.PROCESS_STATE_TOP) {
-            mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
+            mAtm.mAmInternal.addPendingTopUid(mUid, mPid, mThread);
         }
         prepareOomAdjustment();
         // Posting the message at the front of queue so WM lock isn't held when we call into AM,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8e4afe6..dc729f2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1382,8 +1382,9 @@
                     Slog.i(TAG, SECONDARY_ZYGOTE_PRELOAD);
                     TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
                     traceLog.traceBegin(SECONDARY_ZYGOTE_PRELOAD);
-                    if (!Process.ZYGOTE_PROCESS.preloadDefault(Build.SUPPORTED_32_BIT_ABIS[0])) {
-                        Slog.e(TAG, "Unable to preload default resources");
+                    String[] abis32 = Build.SUPPORTED_32_BIT_ABIS;
+                    if (abis32.length > 0 && !Process.ZYGOTE_PROCESS.preloadDefault(abis32[0])) {
+                        Slog.e(TAG, "Unable to preload default resources for secondary");
                     }
                     traceLog.traceEnd();
                 } catch (Exception ex) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 8c21a39..16406bc 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -129,7 +129,7 @@
         thread2.assertWaiting("Unexpected state for " + record2);
         thread2.interrupt();
 
-        mAms.mProcessList.mActiveUids.clear();
+        clearActiveUids();
     }
 
     private UidRecord addActiveUidRecord(int uid, long curProcStateSeq,
@@ -137,11 +137,21 @@
         final UidRecord record = new UidRecord(uid, mAms);
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
         record.curProcStateSeq = curProcStateSeq;
-        record.waitingForNetwork = true;
-        mAms.mProcessList.mActiveUids.put(uid, record);
+        record.procStateSeqWaitingForNetwork = 1;
+        addActiveUidRecord(uid, record);
         return record;
     }
 
+    @SuppressWarnings("GuardedBy")
+    private void addActiveUidRecord(int uid, UidRecord record) {
+        mAms.mProcessList.mActiveUids.put(uid, record);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    private void clearActiveUids() {
+        mAms.mProcessList.mActiveUids.clear();
+    }
+
     static class CustomThread extends Thread {
         private static final long WAIT_TIMEOUT_MS = 1000;
         private static final long WAIT_INTERVAL_MS = 100;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 12e0d8b..968a3e2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -191,8 +191,6 @@
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_TOP, // prevState
                 PROCESS_STATE_TOP, // curState
-                0, // expectedGlobalCounter
-                0, // exptectedCurProcStateSeq
                 NETWORK_STATE_NO_CHANGE, // expectedBlockState
                 false); // expectNotify
 
@@ -200,8 +198,6 @@
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_FOREGROUND_SERVICE, // prevState
                 PROCESS_STATE_SERVICE, // curState
-                1, // expectedGlobalCounter
-                1, // exptectedCurProcStateSeq
                 NETWORK_STATE_UNBLOCK, // expectedBlockState
                 true); // expectNotify
 
@@ -213,8 +209,6 @@
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_TRANSIENT_BACKGROUND, // prevState
                 PROCESS_STATE_IMPORTANT_BACKGROUND, // curState
-                42, // expectedGlobalCounter
-                1, // exptectedCurProcStateSeq
                 NETWORK_STATE_NO_CHANGE, // expectedBlockState
                 false); // expectNotify
 
@@ -222,73 +216,22 @@
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_LAST_ACTIVITY, // prevState
                 PROCESS_STATE_TOP, // curState
-                43, // expectedGlobalCounter
-                43, // exptectedCurProcStateSeq
                 NETWORK_STATE_BLOCK, // expectedBlockState
                 false); // expectNotify
 
         // verify waiting threads are not notified.
-        uidRec.waitingForNetwork = false;
+        uidRec.procStateSeqWaitingForNetwork = 0;
         // Uid state is moving from foreground to background.
         verifySeqCounterAndInteractions(uidRec,
                 PROCESS_STATE_FOREGROUND_SERVICE, // prevState
                 PROCESS_STATE_SERVICE, // curState
-                44, // expectedGlobalCounter
-                44, // exptectedCurProcStateSeq
                 NETWORK_STATE_UNBLOCK, // expectedBlockState
                 false); // expectNotify
-
-        // Verify when uid is not restricted, procStateSeq is not incremented.
-        uidRec.waitingForNetwork = true;
-        mInjector.setNetworkRestrictedForUid(false);
-        verifySeqCounterAndInteractions(uidRec,
-                PROCESS_STATE_IMPORTANT_BACKGROUND, // prevState
-                PROCESS_STATE_TOP, // curState
-                44, // expectedGlobalCounter
-                44, // exptectedCurProcStateSeq
-                -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
-                false); // expectNotify
-
-        // Verify when waitForNetworkTimeout is 0, then procStateSeq is not incremented.
-        mAms.mWaitForNetworkTimeoutMs = 0;
-        mInjector.setNetworkRestrictedForUid(true);
-        verifySeqCounterAndInteractions(uidRec,
-                PROCESS_STATE_TOP, // prevState
-                PROCESS_STATE_IMPORTANT_BACKGROUND, // curState
-                44, // expectedGlobalCounter
-                44, // exptectedCurProcStateSeq
-                -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
-                false); // expectNotify
-
-        // Verify when the uid doesn't have internet permission, then procStateSeq is not
-        // incremented.
-        uidRec.hasInternetPermission = false;
-        mAms.mWaitForNetworkTimeoutMs = 111;
-        mInjector.setNetworkRestrictedForUid(true);
-        verifySeqCounterAndInteractions(uidRec,
-                PROCESS_STATE_CACHED_ACTIVITY, // prevState
-                PROCESS_STATE_FOREGROUND_SERVICE, // curState
-                44, // expectedGlobalCounter
-                44, // exptectedCurProcStateSeq
-                -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
-                false); // expectNotify
-
-        // Verify procStateSeq is not incremented when the uid is not an application, regardless
-        // of the process state.
-        final int notAppUid = 111;
-        final UidRecord uidRec2 = addUidRecord(notAppUid);
-        verifySeqCounterAndInteractions(uidRec2,
-                PROCESS_STATE_CACHED_EMPTY, // prevState
-                PROCESS_STATE_TOP, // curState
-                44, // expectedGlobalCounter
-                0, // exptectedCurProcStateSeq
-                -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
-                false); // expectNotify
     }
 
     private UidRecord addUidRecord(int uid) {
         final UidRecord uidRec = new UidRecord(uid, mAms);
-        uidRec.waitingForNetwork = true;
+        uidRec.procStateSeqWaitingForNetwork = 1;
         uidRec.hasInternetPermission = true;
         mAms.mProcessList.mActiveUids.put(uid, uidRec);
 
@@ -305,18 +248,26 @@
 
     @SuppressWarnings("GuardedBy")
     private void verifySeqCounterAndInteractions(UidRecord uidRec, int prevState, int curState,
-            int expectedGlobalCounter, int expectedCurProcStateSeq, int expectedBlockState,
-            boolean expectNotify) throws Exception {
+            int expectedBlockState, boolean expectNotify) throws Exception {
         CustomThread thread = new CustomThread(uidRec.networkStateLock);
         thread.startAndWait("Unexpected state for " + uidRec);
 
         uidRec.setSetProcState(prevState);
         uidRec.setCurProcState(curState);
+        final long beforeProcStateSeq = mAms.mProcessList.mProcStateSeqCounter;
+
         mAms.mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(mAms.mProcessList.mActiveUids);
 
-        // @SuppressWarnings("GuardedBy")
-        assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
-        assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
+        final long afterProcStateSeq = beforeProcStateSeq
+                + mAms.mProcessList.mActiveUids.size();
+        assertEquals("beforeProcStateSeq=" + beforeProcStateSeq
+                        + ",activeUids.size=" + mAms.mProcessList.mActiveUids.size(),
+                afterProcStateSeq, mAms.mProcessList.mProcStateSeqCounter);
+        assertTrue("beforeProcStateSeq=" + beforeProcStateSeq
+                        + ",afterProcStateSeq=" + afterProcStateSeq
+                        + ",uidCurProcStateSeq=" + uidRec.curProcStateSeq,
+                uidRec.curProcStateSeq > beforeProcStateSeq
+                        && uidRec.curProcStateSeq <= afterProcStateSeq);
 
         for (int i = mAms.mProcessList.getLruSizeLOSP() - 1; i >= 0; --i) {
             final ProcessRecord app = mAms.mProcessList.getLruProcessesLOSP().get(i);
@@ -815,48 +766,11 @@
     }
 
     @Test
-    public void testEnqueueUidChangeLocked_procStateSeqUpdated() {
-        final UidRecord uidRecord = new UidRecord(TEST_UID, mAms);
-        uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
-
-        // Verify with no pending changes for TEST_UID.
-        verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ1);
-
-        // Add a pending change for TEST_UID and verify enqueueUidChangeLocked still works as
-        // expected.
-        uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ2;
-        verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ2);
-    }
-
-    @Test
     public void testEnqueueUidChangeLocked_nullUidRecord() {
         // Use "null" uidRecord to make sure there is no crash.
         mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
     }
 
-    private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) {
-        // Test enqueueUidChangeLocked with every UidRecord.CHANGE_*
-        for (int i = 0; i < UID_RECORD_CHANGES.length; ++i) {
-            final int changeToDispatch = UID_RECORD_CHANGES[i];
-            // Reset lastProcStateSeqDispatchToObservers after every test.
-            uidRecord.lastDispatchedProcStateSeq = 0;
-            mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
-            // Verify there is no effect on curProcStateSeq.
-            assertEquals(curProcstateSeq, uidRecord.curProcStateSeq);
-            if ((changeToDispatch & UidRecord.CHANGE_GONE) != 0) {
-                // Since the change is CHANGE_GONE or CHANGE_GONE_IDLE, verify that
-                // lastProcStateSeqDispatchedToObservers is not updated.
-                assertNotEquals(uidRecord.curProcStateSeq,
-                        uidRecord.lastDispatchedProcStateSeq);
-            } else {
-                // Since the change is neither CHANGE_GONE nor CHANGE_GONE_IDLE, verify that
-                // lastProcStateSeqDispatchedToObservers has been updated to curProcStateSeq.
-                assertEquals(uidRecord.curProcStateSeq,
-                        uidRecord.lastDispatchedProcStateSeq);
-            }
-        }
-    }
-
     @MediumTest
     @Test
     public void testEnqueueUidChangeLocked_dispatchUidsChanged() {
@@ -893,29 +807,10 @@
         // Check there is no crash when there is no UidRecord for myUid
         mAms.waitForNetworkStateUpdate(TEST_PROC_STATE_SEQ1);
 
-        // Verify there is no waiting when UidRecord.curProcStateSeq is greater than
-        // the procStateSeq in the request to wait.
-        verifyWaitingForNetworkStateUpdate(
-                TEST_PROC_STATE_SEQ1, // curProcStateSeq
-                TEST_PROC_STATE_SEQ1, // lastDsipatchedProcStateSeq
-                TEST_PROC_STATE_SEQ1 - 4, // lastNetworkUpdatedProcStateSeq
-                TEST_PROC_STATE_SEQ1 - 2, // procStateSeqToWait
-                false); // expectWait
-
-        // Verify there is no waiting when the procStateSeq in the request to wait is
-        // not dispatched to NPMS.
-        verifyWaitingForNetworkStateUpdate(
-                TEST_PROC_STATE_SEQ1, // curProcStateSeq
-                TEST_PROC_STATE_SEQ1 - 1, // lastDsipatchedProcStateSeq
-                TEST_PROC_STATE_SEQ1 - 1, // lastNetworkUpdatedProcStateSeq
-                TEST_PROC_STATE_SEQ1, // procStateSeqToWait
-                false); // expectWait
-
         // Verify there is not waiting when the procStateSeq in the request already has
         // an updated network state.
         verifyWaitingForNetworkStateUpdate(
                 TEST_PROC_STATE_SEQ1, // curProcStateSeq
-                TEST_PROC_STATE_SEQ1, // lastDsipatchedProcStateSeq
                 TEST_PROC_STATE_SEQ1, // lastNetworkUpdatedProcStateSeq
                 TEST_PROC_STATE_SEQ1, // procStateSeqToWait
                 false); // expectWait
@@ -923,18 +818,16 @@
         // Verify waiting for network works
         verifyWaitingForNetworkStateUpdate(
                 TEST_PROC_STATE_SEQ1, // curProcStateSeq
-                TEST_PROC_STATE_SEQ1, // lastDsipatchedProcStateSeq
                 TEST_PROC_STATE_SEQ1 - 1, // lastNetworkUpdatedProcStateSeq
                 TEST_PROC_STATE_SEQ1, // procStateSeqToWait
                 true); // expectWait
     }
 
     private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
-            long lastDispatchedProcStateSeq, long lastNetworkUpdatedProcStateSeq,
+            long lastNetworkUpdatedProcStateSeq,
             final long procStateSeqToWait, boolean expectWait) throws Exception {
         final UidRecord record = new UidRecord(Process.myUid(), mAms);
         record.curProcStateSeq = curProcStateSeq;
-        record.lastDispatchedProcStateSeq = lastDispatchedProcStateSeq;
         record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
         mAms.mProcessList.mActiveUids.put(Process.myUid(), record);
 
@@ -953,7 +846,7 @@
             }
             thread.assertTerminated(errMsg);
             assertTrue(thread.mNotified);
-            assertFalse(record.waitingForNetwork);
+            assertEquals(0, record.procStateSeqWaitingForNetwork);
         } else {
             thread.start();
             thread.assertTerminated(errMsg);
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index af8ac6f..0f2fe44 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -118,6 +118,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
 import android.content.pm.UserInfo;
 import android.net.ConnectivityManager;
@@ -166,6 +167,7 @@
 import com.android.internal.util.test.FsUtil;
 import com.android.server.DeviceIdleInternal;
 import com.android.server.LocalServices;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.usage.AppStandbyInternal;
 
 import com.google.common.util.concurrent.AbstractFuture;
@@ -216,6 +218,7 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 /**
@@ -274,6 +277,7 @@
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
 
     private ActivityManagerInternal mActivityManagerInternal;
+    private PackageManagerInternal mPackageManagerInternal;
 
     private IUidObserver mUidObserver;
     private INetworkManagementEventObserver mNetworkObserver;
@@ -335,6 +339,7 @@
         when(usageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{});
 
         mActivityManagerInternal = addLocalServiceMock(ActivityManagerInternal.class);
+        mPackageManagerInternal = addLocalServiceMock(PackageManagerInternal.class);
 
         final PowerSaveState state = new PowerSaveState.Builder()
                 .setBatterySaverEnabled(false).build();
@@ -436,14 +441,14 @@
         setNetpolicyXml(context);
 
         doAnswer(new Answer<Void>() {
-
             @Override
             public Void answer(InvocationOnMock invocation) throws Throwable {
                 mUidObserver = (IUidObserver) invocation.getArguments()[0];
                 Log.d(TAG, "set mUidObserver to " + mUidObserver);
                 return null;
             }
-        }).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class));
+        }).when(mActivityManagerInternal).registerNetworkPolicyUidObserver(any(),
+                anyInt(), anyInt(), any(String.class));
 
         mFutureIntent = newRestrictBackgroundChangedFuture();
         mDeps = new TestDependencies(mServiceContext);
@@ -483,8 +488,15 @@
                 .thenReturn(buildApplicationInfo(PKG_NAME_B, UID_B));
         when(mPackageManager.getApplicationInfo(eq(PKG_NAME_C), anyInt()))
                 .thenReturn(buildApplicationInfo(PKG_NAME_C, UID_C));
-        when(mPackageManager.getInstalledApplications(anyInt())).thenReturn(
-                buildInstalledApplicationInfoList());
+        doAnswer(arg -> {
+            final Consumer<AndroidPackage> consumer =
+                    (Consumer<AndroidPackage>) arg.getArguments()[0];
+            for (AndroidPackage androidPackage : buildInstalledPackageList()) {
+                consumer.accept(androidPackage);
+            }
+            return null;
+        }).when(mPackageManagerInternal).forEachInstalledPackage(
+                any(Consumer.class), anyInt());
         when(mUserManager.getUsers()).thenReturn(buildUserInfoList());
         when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
         when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true);
@@ -536,6 +548,7 @@
         LocalServices.removeServiceForTest(DeviceIdleInternal.class);
         LocalServices.removeServiceForTest(AppStandbyInternal.class);
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
     }
 
     @After
@@ -978,19 +991,20 @@
     @Test
     public void testUidForeground() throws Exception {
         // push all uids into background
-        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, 0);
-        callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_SERVICE, 0);
+        long procStateSeq = 0;
+        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq++);
+        callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq++);
         assertFalse(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // push one of the uids into foreground
-        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP, 0);
+        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP, procStateSeq++);
         assertTrue(mService.isUidForeground(UID_A));
         assertFalse(mService.isUidForeground(UID_B));
 
         // and swap another uid into foreground
-        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, 0);
-        callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_TOP, 0);
+        callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq++);
+        callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_TOP, procStateSeq++);
         assertFalse(mService.isUidForeground(UID_A));
         assertTrue(mService.isUidForeground(UID_B));
     }
@@ -2037,14 +2051,20 @@
         return ai;
     }
 
-    private List<ApplicationInfo> buildInstalledApplicationInfoList() {
-        final List<ApplicationInfo> installedApps = new ArrayList<>();
-        installedApps.add(buildApplicationInfo(PKG_NAME_A, UID_A));
-        installedApps.add(buildApplicationInfo(PKG_NAME_B, UID_B));
-        installedApps.add(buildApplicationInfo(PKG_NAME_C, UID_C));
+    private List<AndroidPackage> buildInstalledPackageList() {
+        final List<AndroidPackage> installedApps = new ArrayList<>();
+        installedApps.add(createPackageMock(UID_A));
+        installedApps.add(createPackageMock(UID_B));
+        installedApps.add(createPackageMock(UID_C));
         return installedApps;
     }
 
+    private AndroidPackage createPackageMock(int uid) {
+        final AndroidPackage androidPackage = mock(AndroidPackage.class);
+        when(androidPackage.getUid()).thenReturn(uid);
+        return androidPackage;
+    }
+
     private List<UserInfo> buildUserInfoList() {
         final List<UserInfo> users = new ArrayList<>();
         users.add(new UserInfo(USER_ID, "user1", 0));
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 83c78cd..0d0142c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2853,11 +2853,11 @@
      * <p>
      * 4 threshold integers must be within the boundaries [-140 dB, -44 dB], and the levels are:
      * <UL>
-     *     <LI>"NONE: [-140, threshold1]"</LI>
-     *     <LI>"POOR: (threshold1, threshold2]"</LI>
-     *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
-     *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
-     *     <LI>"EXCELLENT:  (threshold4, -44]"</LI>
+     *     <LI>"NONE: [-140, threshold1)"</LI>
+     *     <LI>"POOR: [threshold1, threshold2)"</LI>
+     *     <LI>"MODERATE: [threshold2, threshold3)"</LI>
+     *     <LI>"GOOD:  [threshold3, threshold4)"</LI>
+     *     <LI>"EXCELLENT:  [threshold4, -44]"</LI>
      * </UL>
      * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
@@ -2873,11 +2873,11 @@
      * <p>
      * 4 threshold integers must be within the boundaries [-43 dB, 20 dB], and the levels are:
      * <UL>
-     *     <LI>"NONE: [-43, threshold1]"</LI>
-     *     <LI>"POOR: (threshold1, threshold2]"</LI>
-     *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
-     *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
-     *     <LI>"EXCELLENT:  (threshold4, 20]"</LI>
+     *     <LI>"NONE: [-43, threshold1)"</LI>
+     *     <LI>"POOR: [threshold1, threshold2)"</LI>
+     *     <LI>"MODERATE: [threshold2, threshold3)"</LI>
+     *     <LI>"GOOD:  [threshold3, threshold4)"</LI>
+     *     <LI>"EXCELLENT:  [threshold4, 20]"</LI>
      * </UL>
      * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
@@ -2894,11 +2894,11 @@
      * <p>
      * 4 threshold integers must be within the boundaries [-23 dB, 40 dB], and the levels are:
      * <UL>
-     *     <LI>"NONE: [-23, threshold1]"</LI>
-     *     <LI>"POOR: (threshold1, threshold2]"</LI>
-     *     <LI>"MODERATE: (threshold2, threshold3]"</LI>
-     *     <LI>"GOOD:  (threshold3, threshold4]"</LI>
-     *     <LI>"EXCELLENT:  (threshold4, 40]"</LI>
+     *     <LI>"NONE: [-23, threshold1)"</LI>
+     *     <LI>"POOR: [threshold1, threshold2)"</LI>
+     *     <LI>"MODERATE: [threshold2, threshold3)"</LI>
+     *     <LI>"GOOD:  [threshold3, threshold4)"</LI>
+     *     <LI>"EXCELLENT:  [threshold4, 40]"</LI>
      * </UL>
      * <p>
      * This key is considered invalid if the format is violated. If the key is invalid or
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index cd22abd..417fd49 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -438,13 +438,13 @@
         int level;
         if (measure == CellInfo.UNAVAILABLE) {
             level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        } else if (measure > thresholds[3]) {
+        } else if (measure >= thresholds[3]) {
             level = SIGNAL_STRENGTH_GREAT;
-        } else if (measure > thresholds[2]) {
+        } else if (measure >= thresholds[2]) {
             level = SIGNAL_STRENGTH_GOOD;
-        } else if (measure > thresholds[1]) {
+        } else if (measure >= thresholds[1]) {
             level = SIGNAL_STRENGTH_MODERATE;
-        }  else if (measure > thresholds[0]) {
+        }  else if (measure >= thresholds[0]) {
             level = SIGNAL_STRENGTH_POOR;
         } else {
             level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 5f606e1..09080be 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -26,6 +26,7 @@
 
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
 import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -50,9 +51,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.vcn.VcnManager;
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.ParcelUuid;
+import android.os.PersistableBundle;
 import android.os.test.TestLooper;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
@@ -104,6 +107,26 @@
         TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap);
     }
 
+    private static final String TEST_CARRIER_CONFIG_KEY_1 = "TEST_CARRIER_CONFIG_KEY_1";
+    private static final String TEST_CARRIER_CONFIG_KEY_2 = "TEST_CARRIER_CONFIG_KEY_2";
+    private static final PersistableBundle TEST_CARRIER_CONFIG = new PersistableBundle();
+    private static final PersistableBundleWrapper TEST_CARRIER_CONFIG_WRAPPER;
+    private static final Map<Integer, PersistableBundleWrapper> TEST_SUBID_TO_CARRIER_CONFIG_MAP;
+
+    static {
+        TEST_CARRIER_CONFIG.putString(
+                VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+                VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY);
+        TEST_CARRIER_CONFIG.putString(
+                VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
+                VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY);
+        TEST_CARRIER_CONFIG_WRAPPER = new PersistableBundleWrapper(TEST_CARRIER_CONFIG);
+
+        final Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>();
+        subIdToCarrierConfigMap.put(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER);
+        TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
+    }
+
     @NonNull private final Context mContext;
     @NonNull private final TestLooper mTestLooper;
     @NonNull private final Handler mHandler;
@@ -144,6 +167,9 @@
         doReturn(mCarrierConfigManager)
                 .when(mContext)
                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        doReturn(TEST_CARRIER_CONFIG)
+                .when(mCarrierConfigManager)
+                .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1));
 
         // subId 1, 2 are in same subGrp, only subId 1 is active
         doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid();
@@ -227,14 +253,24 @@
     private TelephonySubscriptionSnapshot buildExpectedSnapshot(
             Map<Integer, SubscriptionInfo> subIdToInfoMap,
             Map<ParcelUuid, Set<String>> privilegedPackages) {
-        return new TelephonySubscriptionSnapshot(0, subIdToInfoMap, privilegedPackages);
+        return buildExpectedSnapshot(0, subIdToInfoMap, privilegedPackages);
     }
 
     private TelephonySubscriptionSnapshot buildExpectedSnapshot(
             int activeSubId,
             Map<Integer, SubscriptionInfo> subIdToInfoMap,
             Map<ParcelUuid, Set<String>> privilegedPackages) {
-        return new TelephonySubscriptionSnapshot(activeSubId, subIdToInfoMap, privilegedPackages);
+        return buildExpectedSnapshot(
+                activeSubId, subIdToInfoMap, TEST_SUBID_TO_CARRIER_CONFIG_MAP, privilegedPackages);
+    }
+
+    private TelephonySubscriptionSnapshot buildExpectedSnapshot(
+            int activeSubId,
+            Map<Integer, SubscriptionInfo> subIdToInfoMap,
+            Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap,
+            Map<ParcelUuid, Set<String>> privilegedPackages) {
+        return new TelephonySubscriptionSnapshot(
+                activeSubId, subIdToInfoMap, subIdToCarrierConfigMap, privilegedPackages);
     }
 
     private void verifyNoActiveSubscriptions() {
@@ -245,6 +281,8 @@
     private void setupReadySubIds() {
         mTelephonySubscriptionTracker.setReadySubIdsBySlotId(
                 Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1));
+        mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(
+                Collections.singletonMap(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER));
     }
 
     private void setPrivilegedPackagesForMock(@NonNull List<String> privilegedPackages) {
@@ -300,6 +338,7 @@
         readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX + 1, TEST_SUBSCRIPTION_ID_1);
 
         mTelephonySubscriptionTracker.setReadySubIdsBySlotId(readySubIdsBySlotId);
+        mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(TEST_SUBID_TO_CARRIER_CONFIG_MAP);
         doReturn(1).when(mTelephonyManager).getActiveModemCount();
 
         List<CarrierPrivilegesCallback> carrierPrivilegesCallbacks =
@@ -464,8 +503,16 @@
 
         mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
         mTestLooper.dispatchAll();
-        verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap())));
+        verify(mCallback)
+                .onNewSnapshot(
+                        eq(
+                                buildExpectedSnapshot(
+                                        0, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap())));
         assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+        assertNull(
+                mTelephonySubscriptionTracker
+                        .getSubIdToCarrierConfigMap()
+                        .get(TEST_SUBSCRIPTION_ID_1));
     }
 
     @Test
@@ -493,7 +540,7 @@
     public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
         final TelephonySubscriptionSnapshot snapshot =
                 new TelephonySubscriptionSnapshot(
-                        TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap());
+                        TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap());
 
         assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
         assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
@@ -503,7 +550,7 @@
     public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
         final TelephonySubscriptionSnapshot snapshot =
                 new TelephonySubscriptionSnapshot(
-                        TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap());
+                        TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap());
 
         assertEquals(
                 new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 841b81c..15d4f10 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -56,6 +56,7 @@
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
 import android.net.ipsec.ike.exceptions.IkeException;
 import android.net.ipsec.ike.exceptions.IkeInternalException;
 import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -216,14 +217,23 @@
     @Test
     public void testMigration() throws Exception {
         triggerChildOpened();
+        mTestLooper.dispatchAll();
+        assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
 
         mGatewayConnection
                 .getUnderlyingNetworkControllerCallback()
                 .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+
+        final IkeSessionConnectionInfo newIkeConnectionInfo =
+                new IkeSessionConnectionInfo(
+                        TEST_ADDR_V4, TEST_ADDR_V4_2, TEST_UNDERLYING_NETWORK_RECORD_2.network);
+        getIkeSessionCallback().onIkeSessionConnectionInfoChanged(newIkeConnectionInfo);
         getChildSessionCallback()
                 .onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
         mTestLooper.dispatchAll();
 
+        assertEquals(newIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
+
         verify(mIpSecSvc, times(2))
                 .setNetworkForTunnelInterface(
                         eq(TEST_IPSEC_TUNNEL_RESOURCE_ID),
@@ -246,7 +256,8 @@
                 MtuUtils.getMtu(
                         saProposals,
                         mConfig.getMaxMtu(),
-                        TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.getMtu());
+                        TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.getMtu(),
+                        true /* isIpv4 */);
         verify(mNetworkAgent).sendLinkProperties(
                 argThat(lp -> expectedMtu == lp.getMtu()
                         && TEST_TCP_BUFFER_SIZES_2.equals(lp.getTcpBufferSizes())));
@@ -269,6 +280,7 @@
                 .when(mMockChildSessionConfig)
                 .getInternalDnsServers();
 
+        getIkeSessionCallback().onOpened(mIkeSessionConfiguration);
         getChildSessionCallback().onOpened(mMockChildSessionConfig);
     }
 
@@ -298,6 +310,7 @@
         mTestLooper.dispatchAll();
 
         assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+        assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
 
         final ArgumentCaptor<LinkProperties> lpCaptor =
                 ArgumentCaptor.forClass(LinkProperties.class);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index b9dfda3..6a9a1e2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -213,7 +213,8 @@
                         VcnGatewayConnectionConfigTest.buildTestConfig(),
                         tunnelIface,
                         childSessionConfig,
-                        record);
+                        record,
+                        mIkeConnectionInfo);
 
         verify(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
 
@@ -226,7 +227,8 @@
                         VcnGatewayConnectionConfigTest.buildTestConfig(),
                         tunnelIface,
                         childSessionConfig,
-                        record);
+                        record,
+                        mIkeConnectionInfo);
 
         verify(mDeps, times(2)).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
 
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 5628321..785bff1 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -47,6 +47,8 @@
 import android.net.NetworkCapabilities;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
 import android.net.vcn.VcnGatewayConnectionConfig;
 import android.net.vcn.VcnGatewayConnectionConfigTest;
 import android.os.ParcelUuid;
@@ -80,6 +82,13 @@
         doReturn(TEST_SUB_GRP).when(TEST_SUB_INFO).getGroupUuid();
     }
 
+    protected static final InetAddress TEST_ADDR = InetAddresses.parseNumericAddress("2001:db8::1");
+    protected static final InetAddress TEST_ADDR_2 =
+            InetAddresses.parseNumericAddress("2001:db8::2");
+    protected static final InetAddress TEST_ADDR_V4 =
+            InetAddresses.parseNumericAddress("192.0.2.1");
+    protected static final InetAddress TEST_ADDR_V4_2 =
+            InetAddresses.parseNumericAddress("192.0.2.2");
     protected static final InetAddress TEST_DNS_ADDR =
             InetAddresses.parseNumericAddress("2001:DB8:0:1::");
     protected static final InetAddress TEST_DNS_ADDR_2 =
@@ -129,6 +138,7 @@
             new TelephonySubscriptionSnapshot(
                     TEST_SUB_ID,
                     Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO),
+                    Collections.EMPTY_MAP,
                     Collections.EMPTY_MAP);
 
     @NonNull protected final Context mContext;
@@ -148,6 +158,9 @@
     @NonNull protected final IpSecService mIpSecSvc;
     @NonNull protected final ConnectivityManager mConnMgr;
 
+    @NonNull protected final IkeSessionConnectionInfo mIkeConnectionInfo;
+    @NonNull protected final IkeSessionConfiguration mIkeSessionConfiguration;
+
     protected VcnIkeSession mMockIkeSession;
     protected VcnGatewayConnection mGatewayConnection;
 
@@ -173,6 +186,10 @@
         VcnTestUtils.setupSystemService(
                 mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
 
+        mIkeConnectionInfo =
+                new IkeSessionConnectionInfo(TEST_ADDR, TEST_ADDR_2, mock(Network.class));
+        mIkeSessionConfiguration = new IkeSessionConfiguration.Builder(mIkeConnectionInfo).build();
+
         doReturn(mContext).when(mVcnContext).getContext();
         doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
         doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 6c849b5..b0d6895 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -30,6 +30,7 @@
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
 import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
 import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -309,7 +310,9 @@
                         wifiNetworkPriority,
                         mWifiNetworkRecord,
                         selectedNetworkRecord,
-                        carrierConfig));
+                        carrierConfig == null
+                                ? null
+                                : new PersistableBundleWrapper(carrierConfig)));
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
index 29511f7..e9e7078 100644
--- a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
+++ b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
@@ -46,34 +46,85 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MtuUtilsTest {
-    @Test
-    public void testUnderlyingMtuZero() {
+    private void verifyUnderlyingMtuZero(boolean isIpv4) {
         assertEquals(
-                IPV6_MIN_MTU, getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */));
+                IPV6_MIN_MTU,
+                getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */, isIpv4));
     }
 
     @Test
-    public void testClampsToMaxMtu() {
-        assertEquals(0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */));
+    public void testUnderlyingMtuZeroV4() {
+        verifyUnderlyingMtuZero(true /* isIpv4 */);
     }
 
     @Test
-    public void testNormalModeAlgorithmLessThanUnderlyingMtu() {
-        final List<ChildSaProposal> saProposals =
-                Arrays.asList(
-                        new ChildSaProposal.Builder()
-                                .addEncryptionAlgorithm(
-                                        ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
-                                .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
-                                .build());
+    public void testUnderlyingMtuZeroV6() {
+        verifyUnderlyingMtuZero(false /* isIpv4 */);
+    }
 
+    private void verifyClampsToMaxMtu(boolean isIpv4) {
+        assertEquals(
+                0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */, isIpv4));
+    }
+
+    @Test
+    public void testClampsToMaxMtuV4() {
+        verifyClampsToMaxMtu(true /* isIpv4 */);
+    }
+
+    @Test
+    public void testClampsToMaxMtuV6() {
+        verifyClampsToMaxMtu(false /* isIpv4 */);
+    }
+
+    private List<ChildSaProposal> buildChildSaProposalsWithNormalModeAlgo() {
+        return Arrays.asList(
+                new ChildSaProposal.Builder()
+                        .addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
+                        .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+                        .build());
+    }
+
+    private void verifyNormalModeAlgorithmLessThanUnderlyingMtu(boolean isIpv4) {
         final int actualMtu =
-                getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+                getMtu(
+                        buildChildSaProposalsWithNormalModeAlgo(),
+                        ETHER_MTU /* maxMtu */,
+                        ETHER_MTU /* underlyingMtu */,
+                        isIpv4);
         assertTrue(ETHER_MTU > actualMtu);
     }
 
     @Test
-    public void testCombinedModeAlgorithmLessThanUnderlyingMtu() {
+    public void testNormalModeAlgorithmLessThanUnderlyingMtuV4() {
+        verifyNormalModeAlgorithmLessThanUnderlyingMtu(true /* isIpv4 */);
+    }
+
+    @Test
+    public void testNormalModeAlgorithmLessThanUnderlyingMtuV6() {
+        verifyNormalModeAlgorithmLessThanUnderlyingMtu(false /* isIpv4 */);
+    }
+
+    @Test
+    public void testMtuIpv4LessThanMtuIpv6() {
+        final int actualMtuV4 =
+                getMtu(
+                        buildChildSaProposalsWithNormalModeAlgo(),
+                        ETHER_MTU /* maxMtu */,
+                        ETHER_MTU /* underlyingMtu */,
+                        true /* isIpv4 */);
+
+        final int actualMtuV6 =
+                getMtu(
+                        buildChildSaProposalsWithNormalModeAlgo(),
+                        ETHER_MTU /* maxMtu */,
+                        ETHER_MTU /* underlyingMtu */,
+                        false /* isIpv4 */);
+
+        assertTrue(actualMtuV4 < actualMtuV6);
+    }
+
+    private void verifyCombinedModeAlgorithmLessThanUnderlyingMtu(boolean isIpv4) {
         final List<ChildSaProposal> saProposals =
                 Arrays.asList(
                         new ChildSaProposal.Builder()
@@ -86,7 +137,17 @@
                                 .build());
 
         final int actualMtu =
-                getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+                getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */, isIpv4);
         assertTrue(ETHER_MTU > actualMtu);
     }
+
+    @Test
+    public void testCombinedModeAlgorithmLessThanUnderlyingMtuV4() {
+        verifyCombinedModeAlgorithmLessThanUnderlyingMtu(true /* isIpv4 */);
+    }
+
+    @Test
+    public void testCombinedModeAlgorithmLessThanUnderlyingMtuV6() {
+        verifyCombinedModeAlgorithmLessThanUnderlyingMtu(false /* isIpv4 */);
+    }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
index a44a734..294f5c1 100644
--- a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
+++ b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
@@ -18,6 +18,8 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 import android.os.PersistableBundle;
 
@@ -211,4 +213,84 @@
 
         assertEquals(testInt, result);
     }
+
+    private PersistableBundle getTestBundle() {
+        final PersistableBundle bundle = new PersistableBundle();
+
+        bundle.putBoolean(TEST_KEY + "Boolean", true);
+        bundle.putBooleanArray(TEST_KEY + "BooleanArray", new boolean[] {true, false});
+        bundle.putDouble(TEST_KEY + "Double", 0.1);
+        bundle.putDoubleArray(TEST_KEY + "DoubleArray", new double[] {0.1, 0.2, 0.3});
+        bundle.putInt(TEST_KEY + "Int", 1);
+        bundle.putIntArray(TEST_KEY + "IntArray", new int[] {1, 2});
+        bundle.putLong(TEST_KEY + "Long", 5L);
+        bundle.putLongArray(TEST_KEY + "LongArray", new long[] {0L, -1L, -2L});
+        bundle.putString(TEST_KEY + "String", "TEST");
+        bundle.putStringArray(TEST_KEY + "StringArray", new String[] {"foo", "bar", "bas"});
+        bundle.putPersistableBundle(
+                TEST_KEY + "PersistableBundle",
+                new TestClass(1, TEST_INT_ARRAY, TEST_STRING_PREFIX, new PersistableBundle())
+                        .toPersistableBundle());
+
+        return bundle;
+    }
+
+    @Test
+    public void testMinimizeBundle() throws Exception {
+        final String[] minimizedKeys =
+                new String[] {
+                    TEST_KEY + "Boolean",
+                    TEST_KEY + "BooleanArray",
+                    TEST_KEY + "Double",
+                    TEST_KEY + "DoubleArray",
+                    TEST_KEY + "Int",
+                    TEST_KEY + "IntArray",
+                    TEST_KEY + "Long",
+                    TEST_KEY + "LongArray",
+                    TEST_KEY + "String",
+                    TEST_KEY + "StringArray",
+                    TEST_KEY + "PersistableBundle"
+                };
+
+        final PersistableBundle testBundle = getTestBundle();
+        testBundle.putBoolean(TEST_KEY + "Boolean2", true);
+
+        final PersistableBundle minimized =
+                PersistableBundleUtils.minimizeBundle(testBundle, minimizedKeys);
+
+        // Verify that the minimized bundle is NOT the same in size OR values due to the extra
+        // Boolean2 key
+        assertFalse(PersistableBundleUtils.isEqual(testBundle, minimized));
+
+        // Verify that removing the extra key from the source bundle results in equality.
+        testBundle.remove(TEST_KEY + "Boolean2");
+        assertTrue(PersistableBundleUtils.isEqual(testBundle, minimized));
+    }
+
+    @Test
+    public void testEquality_identical() throws Exception {
+        final PersistableBundle left = getTestBundle();
+        final PersistableBundle right = getTestBundle();
+
+        assertTrue(PersistableBundleUtils.isEqual(left, right));
+    }
+
+    @Test
+    public void testEquality_different() throws Exception {
+        final PersistableBundle left = getTestBundle();
+        final PersistableBundle right = getTestBundle();
+
+        left.putBoolean(TEST_KEY + "Boolean2", true);
+        assertFalse(PersistableBundleUtils.isEqual(left, right));
+
+        left.remove(TEST_KEY + "Boolean2");
+        assertTrue(PersistableBundleUtils.isEqual(left, right));
+    }
+
+    @Test
+    public void testEquality_null() throws Exception {
+        assertFalse(PersistableBundleUtils.isEqual(getTestBundle(), null));
+        assertFalse(PersistableBundleUtils.isEqual(null, getTestBundle()));
+        assertTrue(PersistableBundleUtils.isEqual(null, null));
+    }
 }