Merge "Update dirty-image-objects file" into main
diff --git a/Android.bp b/Android.bp
index 5211fe2..68fc474 100644
--- a/Android.bp
+++ b/Android.bp
@@ -398,6 +398,7 @@
     ],
     sdk_version: "core_platform",
     static_libs: [
+        "aconfig_storage_reader_java",
         "android.hardware.common.fmq-V1-java",
         "bouncycastle-repackaged-unbundled",
         "com.android.sysprop.foldlockbehavior",
@@ -413,7 +414,6 @@
         "modules-utils-fastxmlserializer",
         "modules-utils-preconditions",
         "modules-utils-statemachine",
-        "modules-utils-synchronous-result-receiver",
         "modules-utils-os",
         "modules-utils-uieventlogger-interface",
         "framework-permission-aidl-java",
@@ -632,7 +632,6 @@
         "core/java/com/android/internal/util/AsyncService.java",
         "core/java/com/android/internal/util/Protocol.java",
         "telephony/java/android/telephony/Annotation.java",
-        ":net-utils-framework-wifi-common-srcs",
     ],
     libs: [
         "framework-annotations-lib",
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 02e8eec..e680103 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -262,6 +262,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/priv-app/InProcessTethering)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/OsuLogin)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system_other/system/app/OsuLogin)
+$(call add-clean-step, rm -rf $(OUT_DIR)/host/linux-x86/testcases/ravenwood-runtime)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/OWNERS b/OWNERS
index 7ceca32..096da29 100644
--- a/OWNERS
+++ b/OWNERS
@@ -14,6 +14,7 @@
 [email protected] #{LAST_RESORT_SUGGESTION}
 [email protected] #{LAST_RESORT_SUGGESTION}
 [email protected] #{LAST_RESORT_SUGGESTION}
[email protected] #{LAST_RESORT_SUGGESTION}
 [email protected] #{LAST_RESORT_SUGGESTION}
 [email protected] #{LAST_RESORT_SUGGESTION}
 [email protected] #{LAST_RESORT_SUGGESTION}
@@ -28,7 +29,7 @@
 # Support bulk translation updates
 per-file */res*/values*/*.xml = [email protected], [email protected]
 
-per-file **.bp,**.mk = [email protected], [email protected], [email protected]
+per-file **.bp,**.mk [email protected], [email protected]
 per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
 per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
 
diff --git a/PERFORMANCE_OWNERS b/PERFORMANCE_OWNERS
index 48a0201..02b0a1e 100644
--- a/PERFORMANCE_OWNERS
+++ b/PERFORMANCE_OWNERS
@@ -6,3 +6,4 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
diff --git a/Ravenwood.bp b/Ravenwood.bp
index f43c37b..912f19d 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -30,7 +30,7 @@
     name: "framework-minus-apex.ravenwood-base",
     tools: ["hoststubgen"],
     cmd: "$(location hoststubgen) " +
-        "@$(location ravenwood/ravenwood-standard-options.txt) " +
+        "@$(location :ravenwood-standard-options) " +
 
         "--debug-log $(location hoststubgen_framework-minus-apex.log) " +
         "--stats-file $(location hoststubgen_framework-minus-apex_stats.csv) " +
@@ -41,13 +41,13 @@
         "--gen-input-dump-file $(location hoststubgen_dump.txt) " +
 
         "--in-jar $(location :framework-minus-apex-for-hoststubgen) " +
-        "--policy-override-file $(location ravenwood/framework-minus-apex-ravenwood-policies.txt) " +
-        "--annotation-allowed-classes-file $(location ravenwood/ravenwood-annotation-allowed-classes.txt) ",
+        "--policy-override-file $(location :ravenwood-framework-policies) " +
+        "--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) ",
     srcs: [
         ":framework-minus-apex-for-hoststubgen",
-        "ravenwood/framework-minus-apex-ravenwood-policies.txt",
-        "ravenwood/ravenwood-standard-options.txt",
-        "ravenwood/ravenwood-annotation-allowed-classes.txt",
+        ":ravenwood-framework-policies",
+        ":ravenwood-standard-options",
+        ":ravenwood-annotation-allowed-classes",
     ],
     out: [
         "ravenwood.jar",
@@ -91,7 +91,7 @@
     name: "services.core.ravenwood-base",
     tools: ["hoststubgen"],
     cmd: "$(location hoststubgen) " +
-        "@$(location ravenwood/ravenwood-standard-options.txt) " +
+        "@$(location :ravenwood-standard-options) " +
 
         "--debug-log $(location hoststubgen_services.core.log) " +
         "--stats-file $(location hoststubgen_services.core_stats.csv) " +
@@ -102,13 +102,13 @@
         "--gen-input-dump-file $(location hoststubgen_dump.txt) " +
 
         "--in-jar $(location :services.core-for-hoststubgen) " +
-        "--policy-override-file $(location ravenwood/services.core-ravenwood-policies.txt) " +
-        "--annotation-allowed-classes-file $(location ravenwood/ravenwood-annotation-allowed-classes.txt) ",
+        "--policy-override-file $(location :ravenwood-services-policies) " +
+        "--annotation-allowed-classes-file $(location :ravenwood-annotation-allowed-classes) ",
     srcs: [
         ":services.core-for-hoststubgen",
-        "ravenwood/services.core-ravenwood-policies.txt",
-        "ravenwood/ravenwood-standard-options.txt",
-        "ravenwood/ravenwood-annotation-allowed-classes.txt",
+        ":ravenwood-services-policies",
+        ":ravenwood-standard-options",
+        ":ravenwood-annotation-allowed-classes",
     ],
     out: [
         "ravenwood.jar",
@@ -137,6 +137,7 @@
 
 java_library {
     name: "services.core.ravenwood-jarjar",
+    defaults: ["ravenwood-internal-only-visibility-java"],
     installable: false,
     static_libs: [
         "services.core.ravenwood",
@@ -144,88 +145,29 @@
     jarjar_rules: ":ravenwood-services-jarjar-rules",
 }
 
-java_library {
-    name: "services.fakes.ravenwood-jarjar",
-    installable: false,
-    srcs: [":services.fakes-sources"],
-    libs: [
-        "ravenwood-framework",
-        "services.core.ravenwood",
-    ],
-    jarjar_rules: ":ravenwood-services-jarjar-rules",
-}
-
-java_library {
-    name: "mockito-ravenwood-prebuilt",
-    installable: false,
-    static_libs: [
-        "mockito-robolectric-prebuilt",
-    ],
-}
-
-java_library {
-    name: "inline-mockito-ravenwood-prebuilt",
-    installable: false,
-    static_libs: [
-        "inline-mockito-robolectric-prebuilt",
-    ],
-}
-
 // Jars in "ravenwood-runtime" are set to the classpath, sorted alphabetically.
 // Rename some of the dependencies to make sure they're included in the intended order.
 java_genrule {
     name: "100-framework-minus-apex.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [":framework-minus-apex.ravenwood"],
     out: ["100-framework-minus-apex.ravenwood.jar"],
-    visibility: ["//visibility:private"],
 }
 
 java_genrule {
     // Use 200 to make sure it comes before the mainline stub ("all-updatable...").
     name: "200-kxml2-android",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
     cmd: "cp $(in) $(out)",
     srcs: [":kxml2-android"],
     out: ["200-kxml2-android.jar"],
-    visibility: ["//visibility:private"],
 }
 
-android_ravenwood_libgroup {
-    name: "ravenwood-runtime",
-    libs: [
-        "100-framework-minus-apex.ravenwood",
-        "200-kxml2-android",
-        "all-updatable-modules-system-stubs",
-        "android.test.mock.ravenwood",
-        "ravenwood-helper-runtime",
-        "hoststubgen-helper-runtime.ravenwood",
-        "services.core.ravenwood-jarjar",
-        "services.fakes.ravenwood-jarjar",
-
-        // Provide runtime versions of utils linked in below
-        "junit",
-        "truth",
-        "flag-junit",
-        "ravenwood-framework",
-        "ravenwood-junit-impl",
-        "ravenwood-junit-impl-flag",
-        "mockito-ravenwood-prebuilt",
-        "inline-mockito-ravenwood-prebuilt",
-    ],
-    jni_libs: [
-        "libandroid_runtime",
-    ],
-}
-
-android_ravenwood_libgroup {
-    name: "ravenwood-utils",
-    libs: [
-        "junit",
-        "truth",
-        "flag-junit",
-        "ravenwood-framework",
-        "ravenwood-junit",
-        "mockito-ravenwood-prebuilt",
-        "inline-mockito-ravenwood-prebuilt",
-    ],
+java_genrule {
+    name: "z00-all-updatable-modules-system-stubs",
+    defaults: ["ravenwood-internal-only-visibility-genrule"],
+    cmd: "cp $(in) $(out)",
+    srcs: [":all-updatable-modules-system-stubs"],
+    out: ["z00-all-updatable-modules-system-stubs.jar"],
 }
diff --git a/ZYGOTE_OWNERS b/ZYGOTE_OWNERS
index f6d15e0..6918c16 100644
--- a/ZYGOTE_OWNERS
+++ b/ZYGOTE_OWNERS
@@ -1,4 +1,4 @@
 [email protected]
[email protected]
 [email protected]
[email protected]
 [email protected]
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
index c69ae39..36266de 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
@@ -23,6 +23,9 @@
 import org.conscrypt.TestUtils;
 
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.security.Key;
 import java.security.NoSuchAlgorithmException;
 import javax.crypto.Cipher;
@@ -91,21 +94,17 @@
         }
     }
 
-    private Object[] getParams() {
-        return new Object[][] {
-            new Object[] {new Config(BufferType.ARRAY,
-                              MyCipherFactory.CONSCRYPT,
-                              Transformation.AES_CBC_PKCS5)},
-            new Object[] {new Config(BufferType.ARRAY,
-                              MyCipherFactory.CONSCRYPT,
-                              Transformation.AES_ECB_PKCS5)},
-            new Object[] {new Config(BufferType.ARRAY,
-                              MyCipherFactory.CONSCRYPT,
-                              Transformation.AES_GCM_NO)},
-            new Object[] {new Config(BufferType.ARRAY,
-                              MyCipherFactory.CONSCRYPT,
-                              Transformation.AES_GCM_SIV)},
-        };
+    public Collection <Object[]> getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        for (BufferType bufferType : BufferType.values()) {
+            for (CipherFactory cipherFactory : MyCipherFactory.values()) {
+                for (Transformation transformation : Transformation.values()) {
+                  params.add(new Object[] {new Config(
+                                bufferType, cipherFactory, transformation)});
+                }
+            }
+        }
+        return params;
     }
 
     private EncryptStrategy encryptStrategy;
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
index dd9f4eb..d7b1c9a2 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -30,6 +30,9 @@
 import java.io.OutputStream;
 import java.net.SocketException;
 import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -40,6 +43,7 @@
 import javax.crypto.Cipher;
 import javax.crypto.NoSuchPaddingException;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -104,19 +108,26 @@
         }
     }
 
-    private Object[] getParams() {
-        return new Object[][] {
-            new Object[] {new Config(
-                              EndpointFactory.CONSCRYPT,
-                              EndpointFactory.CONSCRYPT,
-                              64,
-                              "AES128-GCM",
-                              ChannelType.CHANNEL,
-                              PerfTestProtocol.TLSv13)},
-        };
+    public Collection getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        for (EndpointFactory endpointFactory : EndpointFactory.values()) {
+            for (ChannelType channelType : ChannelType.values()) {
+                for (PerfTestProtocol protocol : PerfTestProtocol.values()) {
+                    params.add(new Object[] {new Config(endpointFactory,
+                        endpointFactory, 64, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+                        channelType, protocol)});
+                    params.add(new Object[] {new Config(endpointFactory,
+                        endpointFactory, 512, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+                        channelType, protocol)});
+                    params.add(new Object[] {new Config(endpointFactory,
+                        endpointFactory, 4096, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+                        channelType, protocol)});
+                }
+            }
+        }
+        return params;
     }
 
-
     private ClientEndpoint client;
     private ServerEndpoint server;
     private byte[] message;
@@ -186,6 +197,7 @@
      */
     @Test
     @Parameters(method = "getParams")
+    @Ignore("b/351034205")
     public void time(Config config) throws Exception {
         reset();
         setup(config);
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineFactory.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineFactory.java
new file mode 100644
index 0000000..8a0d52d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineFactory.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 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.conscrypt;
+
+import org.conscrypt.TestUtils;
+import java.security.Security;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+
+/**
+ * Factory for {@link SSLEngine} instances.
+ */
+public class EngineFactory {
+    public EngineFactory() {
+        this(newConscryptClientContext(), newConscryptServerContext());
+    }
+
+    private EngineFactory(SSLContext clientContext, SSLContext serverContext) {
+        this.clientContext = clientContext;
+        this.serverContext = serverContext;
+    }
+
+    private final SSLContext clientContext;
+    private final SSLContext serverContext;
+
+    public SSLEngine newClientEngine(String cipher) {
+        SSLEngine engine = initEngine(clientContext.createSSLEngine(), cipher, true);
+        return engine;
+    }
+
+    public SSLEngine newServerEngine(String cipher) {
+        SSLEngine engine = initEngine(serverContext.createSSLEngine(), cipher, false);
+        return engine;
+    }
+
+    public void dispose(SSLEngine engine) {
+        engine.closeOutbound();
+    }
+
+    private static SSLContext newConscryptClientContext() {
+        return TestUtils.newClientSslContext(TestUtils.getConscryptProvider());
+    }
+
+    private static SSLContext newConscryptServerContext() {
+        return TestUtils.newServerSslContext(TestUtils.getConscryptProvider());
+    }
+
+    static SSLEngine initEngine(SSLEngine engine, String cipher, boolean client) {
+        engine.setEnabledProtocols(new String[]{"TLSv1.2", "TLSv1.3"});
+        engine.setEnabledCipherSuites(new String[] {cipher});
+        engine.setUseClientMode(client);
+        return engine;
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java
new file mode 100644
index 0000000..cd0ac96
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineHandshakePerfTest.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 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.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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.conscrypt;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark comparing handshake performance of various engine implementations to conscrypt.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class EngineHandshakePerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    /**
+     * Provider for the test configuration
+     */
+    private class Config {
+        BufferType a_bufferType;
+        String c_cipher;
+        int d_rttMillis;
+        Config(BufferType bufferType,
+            String cipher,
+            int rttMillis) {
+          a_bufferType = bufferType;
+          c_cipher = cipher;
+          d_rttMillis = rttMillis;
+        }
+        public BufferType bufferType() {
+            return a_bufferType;
+        }
+
+        public String cipher() {
+            return c_cipher;
+        }
+
+        public int rttMillis() {
+            return d_rttMillis;
+        }
+    }
+
+    public Collection getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        for (BufferType bufferType : BufferType.values()) {
+            params.add(new Object[] {new Config(bufferType,
+                "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 100)});
+        }
+        return params;
+    }
+
+    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocateDirect(0);
+
+    private EngineFactory engineFactory = new EngineFactory();
+    private String cipher;
+    private int rttMillis;
+
+    private ByteBuffer clientApplicationBuffer;
+    private ByteBuffer clientPacketBuffer;
+    private ByteBuffer serverApplicationBuffer;
+    private ByteBuffer serverPacketBuffer;
+
+    private void setup(Config config) throws Exception {
+        cipher = config.cipher();
+        rttMillis = config.rttMillis();
+        BufferType bufferType = config.bufferType();
+
+        SSLEngine clientEngine = engineFactory.newClientEngine(cipher);
+        SSLEngine serverEngine = engineFactory.newServerEngine(cipher);
+
+        // Create the application and packet buffers for both endpoints.
+        clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine);
+        serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine);
+        clientPacketBuffer = bufferType.newPacketBuffer(clientEngine);
+        serverPacketBuffer = bufferType.newPacketBuffer(serverEngine);
+
+        engineFactory.dispose(clientEngine);
+        engineFactory.dispose(serverEngine);
+    }
+
+    @Test
+    @Parameters(method = "getParams")
+    public void handshake(Config config) throws Exception {
+        setup(config);
+        SSLEngine client = engineFactory.newClientEngine(cipher);
+        SSLEngine server = engineFactory.newServerEngine(cipher);
+        clientApplicationBuffer.clear();
+        clientPacketBuffer.clear();
+        serverApplicationBuffer.clear();
+        serverPacketBuffer.clear();
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            client.beginHandshake();
+            server.beginHandshake();
+            doHandshake(client, server);
+        }
+
+        engineFactory.dispose(client);
+        engineFactory.dispose(server);
+    }
+
+    private void doHandshake(SSLEngine client, SSLEngine server) throws SSLException {
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            // Send as many client-to-server messages as possible
+            doHalfHandshake(client, server, clientPacketBuffer, serverApplicationBuffer);
+
+            if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                    && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
+                return;
+            }
+
+            // Do the same with server-to-client messages
+            doHalfHandshake(server, client, serverPacketBuffer, clientApplicationBuffer);
+
+            if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING
+                    && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
+                return;
+            }
+        }
+    }
+
+    private void doHalfHandshake(SSLEngine sender, SSLEngine receiver,
+            ByteBuffer senderPacketBuffer, ByteBuffer receiverApplicationBuffer)
+            throws SSLException {
+        SSLEngineResult senderResult;
+        SSLEngineResult receiverResult;
+
+        do {
+            senderResult = sender.wrap(EMPTY_BUFFER, senderPacketBuffer);
+            runDelegatedTasks(senderResult, sender);
+            senderPacketBuffer.flip();
+            receiverResult = receiver.unwrap(senderPacketBuffer, receiverApplicationBuffer);
+            runDelegatedTasks(receiverResult, receiver);
+            senderPacketBuffer.compact();
+        } while (senderResult.getHandshakeStatus() == HandshakeStatus.NEED_WRAP);
+
+        if (rttMillis > 0) {
+            try {
+                Thread.sleep(rttMillis / 2);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) {
+        if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+            for (;;) {
+                Runnable task = engine.getDelegatedTask();
+                if (task == null) {
+                    break;
+                }
+                task.run();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java
new file mode 100644
index 0000000..1fee218
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EngineWrapPerfTest.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright 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.
+ */
+
+/*
+ * Copyright 2017 The Netty Project
+ *
+ * The Netty Project licenses this file to you 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.conscrypt;
+
+import static org.conscrypt.TestUtils.doEngineHandshake;
+import static org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLException;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import androidx.test.filters.LargeTest;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark comparing performance of various engine implementations to conscrypt.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class EngineWrapPerfTest {
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    /**
+     * Provider for the benchmark configuration
+     */
+    private class Config {
+        BufferType a_bufferType;
+        int c_messageSize;
+        String d_cipher;
+        Config(BufferType bufferType,
+            int messageSize,
+            String cipher) {
+          a_bufferType = bufferType;
+          c_messageSize = messageSize;
+          d_cipher = cipher;
+        }
+        public BufferType bufferType() {
+            return a_bufferType;
+        }
+
+        public int messageSize() {
+            return c_messageSize;
+        }
+
+        public String cipher() {
+            return d_cipher;
+        }
+    }
+
+    public Collection getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        for (BufferType bufferType : BufferType.values()) {
+            params.add(new Object[] {new Config(bufferType, 64,
+                                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")});
+            params.add(new Object[] {new Config(bufferType, 512,
+                                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")});
+            params.add(new Object[] {new Config(bufferType, 4096,
+                                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")});
+        }
+        return params;
+    }
+
+
+    private EngineFactory engineFactory = new EngineFactory();
+    private String cipher;
+    private SSLEngine clientEngine;
+    private SSLEngine serverEngine;
+
+    private ByteBuffer messageBuffer;
+    private ByteBuffer clientApplicationBuffer;
+    private ByteBuffer clientPacketBuffer;
+    private ByteBuffer serverApplicationBuffer;
+    private ByteBuffer serverPacketBuffer;
+    private ByteBuffer preEncryptedBuffer;
+
+    private void setup(Config config) throws Exception {
+        cipher = config.cipher();
+        BufferType bufferType = config.bufferType();
+
+        clientEngine = engineFactory.newClientEngine(cipher);
+        serverEngine = engineFactory.newServerEngine(cipher);
+
+        // Create the application and packet buffers for both endpoints.
+        clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine);
+        serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine);
+        clientPacketBuffer = bufferType.newPacketBuffer(clientEngine);
+        serverPacketBuffer = bufferType.newPacketBuffer(serverEngine);
+
+        // Generate the message to be sent from the client.
+        int messageSize = config.messageSize();
+        messageBuffer = bufferType.newBuffer(messageSize);
+        messageBuffer.put(newTextMessage(messageSize));
+        messageBuffer.flip();
+
+        // Complete the initial TLS handshake.
+        doEngineHandshake(clientEngine, serverEngine, clientApplicationBuffer, clientPacketBuffer,
+                serverApplicationBuffer, serverPacketBuffer, true);
+
+        // Populate the pre-encrypted buffer for use with the unwrap benchmark.
+        preEncryptedBuffer = bufferType.newBuffer(clientEngine.getSession().getPacketBufferSize());
+        doWrap(messageBuffer, preEncryptedBuffer);
+        doUnwrap(preEncryptedBuffer, serverApplicationBuffer);
+    }
+
+    void teardown() {
+        engineFactory.dispose(clientEngine);
+        engineFactory.dispose(serverEngine);
+    }
+
+    @Test
+    @Parameters(method = "getParams")
+    public void wrap(Config config) throws Exception {
+        setup(config);
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            // Reset the buffers.
+            messageBuffer.position(0);
+            clientPacketBuffer.clear();
+            // Wrap the original message and create the encrypted data.
+            doWrap(messageBuffer, clientPacketBuffer);
+
+            // Lightweight comparison - just make sure the data length is correct.
+            assertEquals(preEncryptedBuffer.limit(), clientPacketBuffer.limit());
+        }
+        teardown();
+    }
+
+    /**
+     * Simple benchmark that sends a single message from client to server.
+     */
+    @Test
+    @Parameters(method = "getParams")
+    public void wrapAndUnwrap(Config config) throws Exception {
+        setup(config);
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            // Reset the buffers.
+            messageBuffer.position(0);
+            clientPacketBuffer.clear();
+            serverApplicationBuffer.clear();
+            // Wrap the original message and create the encrypted data.
+            doWrap(messageBuffer, clientPacketBuffer);
+
+            // Unwrap the encrypted data and get back the original result.
+            doUnwrap(clientPacketBuffer, serverApplicationBuffer);
+
+            // Lightweight comparison - just make sure the unencrypted data length is correct.
+            assertEquals(messageBuffer.limit(), serverApplicationBuffer.limit());
+        }
+        teardown();
+    }
+
+    private void doWrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        // Wrap the original message and create the encrypted data.
+        verifyResult(src, clientEngine.wrap(src, dst));
+        dst.flip();
+    }
+
+    private void doUnwrap(ByteBuffer src, ByteBuffer dst) throws SSLException {
+        verifyResult(src, serverEngine.unwrap(src, dst));
+        dst.flip();
+    }
+
+    private void verifyResult(ByteBuffer src, SSLEngineResult result) {
+        if (result.getStatus() != SSLEngineResult.Status.OK) {
+            throw new RuntimeException("Operation returned unexpected result " + result);
+        }
+        if (result.bytesConsumed() != src.limit()) {
+            throw new RuntimeException(
+                    String.format(Locale.US,
+                            "Operation didn't consume all bytes. Expected %d, consumed %d.",
+                            src.limit(), result.bytesConsumed()));
+        }
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
index ba2a65a..8916a3c 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -24,6 +24,9 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -40,6 +43,7 @@
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -94,15 +98,22 @@
         }
     }
 
-    private Object[] getParams() {
-        return new Object[][] {
-            new Object[] {new Config(
-                              EndpointFactory.CONSCRYPT,
-                              EndpointFactory.CONSCRYPT,
-                              64,
-                              "AES128-GCM",
-                              ChannelType.CHANNEL)},
-        };
+    public Collection getParams() {
+        final List<Object[]> params = new ArrayList<>();
+        for (EndpointFactory endpointFactory : EndpointFactory.values()) {
+            for (ChannelType channelType : ChannelType.values()) {
+                params.add(new Object[] {new Config(endpointFactory,
+                    endpointFactory, 64,
+                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", channelType)});
+                params.add(new Object[] {new Config(endpointFactory,
+                    endpointFactory, 512,
+                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", channelType)});
+                params.add(new Object[] {new Config(endpointFactory,
+                    endpointFactory, 4096,
+                    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", channelType)});
+            }
+        }
+        return params;
     }
 
     private ClientEndpoint client;
@@ -121,7 +132,8 @@
         final ChannelType channelType = config.channelType();
 
         server = config.serverFactory().newServer(
-            channelType, config.messageSize(), getCommonProtocolSuites(), ciphers(config));
+            channelType, config.messageSize(),
+            new String[] {"TLSv1.3", "TLSv1.2"}, ciphers(config));
         server.setMessageProcessor(new MessageProcessor() {
             @Override
             public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
@@ -145,7 +157,8 @@
 
         // Always use the same client for consistency across the benchmarks.
         client = config.clientFactory().newClient(
-                ChannelType.CHANNEL, server.port(), getCommonProtocolSuites(), ciphers(config));
+                ChannelType.CHANNEL, server.port(),
+                new String[] {"TLSv1.3", "TLSv1.2"}, ciphers(config));
         client.start();
 
         // Wait for the initial connection to complete.
@@ -189,6 +202,7 @@
 
     @Test
     @Parameters(method = "getParams")
+    @Ignore("b/351034205")
     public void throughput(Config config) throws Exception {
         setup(config);
         BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
index 78fe732..3542b0a 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
@@ -31,7 +31,6 @@
     AES_CBC_PKCS5("AES", "CBC", "PKCS5Padding", new AesKeyGen()),
     AES_ECB_PKCS5("AES", "ECB", "PKCS5Padding", new AesKeyGen()),
     AES_GCM_NO("AES", "GCM", "NoPadding", new AesKeyGen()),
-    AES_GCM_SIV("AES", "GCM_SIV", "NoPadding", new AesKeyGen()),
     RSA_ECB_PKCS1("RSA", "ECB", "PKCS1Padding", new RsaKeyGen());
 
     Transformation(String algorithm, String mode, String padding, KeyGen keyGen) {
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 1653edc..856dba3 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -38,3 +38,10 @@
     ],
     certificate: "platform",
 }
+
+filegroup {
+    name: "multi_user_trace_config",
+    srcs: [
+        "trace_configs/trace_config_multi_user.textproto",
+    ],
+}
diff --git a/api/Android.bp b/api/Android.bp
index d4ba964..d9966fd 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -29,12 +29,14 @@
     pkgPath: "android/soong/api",
     deps: [
         "blueprint",
+        "blueprint-proptools",
         "soong",
         "soong-android",
         "soong-genrule",
         "soong-java",
     ],
     srcs: ["api.go"],
+    testSrcs: ["api_test.go"],
     pluginFor: ["soong_build"],
 }
 
@@ -60,40 +62,8 @@
 metalava_cmd += " -J--add-opens=java.base/java.util=ALL-UNNAMED "
 metalava_cmd += " --quiet "
 
-soong_config_module_type {
-    name: "enable_crashrecovery_module",
-    module_type: "combined_apis_defaults",
-    config_namespace: "ANDROID",
-    bool_variables: ["release_crashrecovery_module"],
-    properties: [
-        "bootclasspath",
-        "system_server_classpath",
-    ],
-}
-
-soong_config_bool_variable {
-    name: "release_crashrecovery_module",
-}
-
-enable_crashrecovery_module {
-    name: "crashrecovery_module_defaults",
-    soong_config_variables: {
-        release_crashrecovery_module: {
-            bootclasspath: [
-                "framework-crashrecovery",
-            ],
-            system_server_classpath: [
-                "service-crashrecovery",
-            ],
-        },
-    },
-}
-
 combined_apis {
     name: "frameworks-base-api",
-    defaults: [
-        "crashrecovery_module_defaults",
-    ],
     bootclasspath: [
         "android.net.ipsec.ike",
         "art.module.public.api",
@@ -126,7 +96,12 @@
         "framework-virtualization",
         "framework-wifi",
         "i18n.module.public.api",
-    ],
+    ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [
+            "framework-crashrecovery",
+        ],
+        default: [],
+    }),
     system_server_classpath: [
         "service-art",
         "service-configinfrastructure",
@@ -135,7 +110,12 @@
         "service-permission",
         "service-rkp",
         "service-sdksandbox",
-    ],
+    ] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
+        "true": [
+            "service-crashrecovery",
+        ],
+        default: [],
+    }),
 }
 
 genrule {
@@ -304,7 +284,7 @@
 // These are libs from framework-internal-utils that are required (i.e. being referenced)
 // from framework-non-updatable-sources. Add more here when there's a need.
 // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
-// dependencies gets bigger.
+// dependencies when the list gets bigger.
 android_non_updatable_stubs_libs = [
     "android.hardware.cas-V1.2-java",
     "android.hardware.health-V1.0-java-constants",
@@ -401,6 +381,11 @@
     "sdk_system_current_android",
 ]
 
+java_defaults {
+    name: "module-classpath-java-defaults",
+    libs: non_updatable_api_deps_on_modules,
+}
+
 // Defaults with module APIs in the classpath (mostly from prebuilts).
 // Suitable for compiling android-non-updatable.
 stubs_defaults {
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 12820f9..d991da5 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -563,8 +563,12 @@
 
 java_defaults {
     name: "android-non-updatable_from_text_defaults",
+    defaults: ["android-non-updatable-stubs-libs-defaults"],
     static_libs: ["framework-res-package-jar"],
     libs: ["stub-annotations"],
+    sdk_version: "none",
+    system_modules: "none",
+    previous_api: ":android.api.public.latest",
 }
 
 java_defaults {
@@ -582,10 +586,10 @@
         "api-stubs-docs-non-updatable.api.contribution",
     ],
     defaults: ["android-non-updatable_everything_from_text_defaults"],
-    full_api_surface_stub: "android_stubs_current.from-text",
     // Use full Android API not just the non-updatable API as the latter is incomplete
     // and can result in incorrect behavior.
     previous_api: ":android.api.combined.public.latest",
+    libs: ["all-modules-public-stubs"],
 }
 
 java_api_library {
@@ -596,10 +600,10 @@
         "system-api-stubs-docs-non-updatable.api.contribution",
     ],
     defaults: ["android-non-updatable_everything_from_text_defaults"],
-    full_api_surface_stub: "android_system_stubs_current.from-text",
     // Use full Android API not just the non-updatable API as the latter is incomplete
     // and can result in incorrect behavior.
     previous_api: ":android.api.combined.system.latest",
+    libs: ["all-modules-system-stubs"],
 }
 
 java_api_library {
@@ -611,10 +615,10 @@
         "test-api-stubs-docs-non-updatable.api.contribution",
     ],
     defaults: ["android-non-updatable_everything_from_text_defaults"],
-    full_api_surface_stub: "android_test_stubs_current.from-text",
     // Use full Android API not just the non-updatable API as the latter is incomplete
     // and can result in incorrect behavior.
     previous_api: ":android.api.combined.test.latest",
+    libs: ["all-modules-system-stubs"],
 }
 
 java_api_library {
@@ -625,8 +629,10 @@
         "system-api-stubs-docs-non-updatable.api.contribution",
         "module-lib-api-stubs-docs-non-updatable.api.contribution",
     ],
-    defaults: ["android-non-updatable_everything_from_text_defaults"],
-    full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
+    defaults: [
+        "module-classpath-java-defaults",
+        "android-non-updatable_everything_from_text_defaults",
+    ],
     // Use full Android API not just the non-updatable API as the latter is incomplete
     // and can result in incorrect behavior.
     previous_api: ":android.api.combined.module-lib.latest",
@@ -644,14 +650,16 @@
         "test-api-stubs-docs-non-updatable.api.contribution",
         "module-lib-api-stubs-docs-non-updatable.api.contribution",
     ],
-    defaults: ["android-non-updatable_everything_from_text_defaults"],
-    full_api_surface_stub: "android_test_module_lib_stubs_current.from-text",
+    defaults: [
+        "module-classpath-java-defaults",
+        "android-non-updatable_everything_from_text_defaults",
+    ],
 
     // No need to specify previous_api as this is not used for compiling against.
-
     // This module is only used for hiddenapi, and other modules should not
     // depend on this module.
     visibility: ["//visibility:private"],
+    libs: ["all-modules-system-stubs"],
 }
 
 java_defaults {
@@ -665,7 +673,7 @@
 }
 
 java_library {
-    name: "android_stubs_current.from-source",
+    name: "android_stubs_current",
     static_libs: [
         "all-modules-public-stubs",
         "android-non-updatable.stubs",
@@ -675,7 +683,7 @@
 }
 
 java_library {
-    name: "android_stubs_current_exportable.from-source",
+    name: "android_stubs_current_exportable",
     static_libs: [
         "all-modules-public-stubs-exportable",
         "android-non-updatable.stubs.exportable",
@@ -685,7 +693,7 @@
 }
 
 java_library {
-    name: "android_system_stubs_current.from-source",
+    name: "android_system_stubs_current",
     static_libs: [
         "all-modules-system-stubs",
         "android-non-updatable.stubs.system",
@@ -698,7 +706,7 @@
 }
 
 java_library {
-    name: "android_system_stubs_current_exportable.from-source",
+    name: "android_system_stubs_current_exportable",
     static_libs: [
         "all-modules-system-stubs-exportable",
         "android-non-updatable.stubs.exportable.system",
@@ -722,7 +730,7 @@
 }
 
 java_library {
-    name: "android_test_stubs_current.from-source",
+    name: "android_test_stubs_current",
     static_libs: [
         // Updatable modules do not have test APIs, but we want to include their SystemApis, like we
         // include the SystemApi of framework-non-updatable-sources.
@@ -739,7 +747,7 @@
 }
 
 java_library {
-    name: "android_test_stubs_current_exportable.from-source",
+    name: "android_test_stubs_current_exportable",
     static_libs: [
         // Updatable modules do not have test APIs, but we want to include their SystemApis, like we
         // include the SystemApi of framework-non-updatable-sources.
@@ -760,7 +768,7 @@
 
 // This module does not need to be copied to dist
 java_library {
-    name: "android_test_frameworks_core_stubs_current.from-source",
+    name: "android_test_frameworks_core_stubs_current",
     static_libs: [
         "all-updatable-modules-system-stubs",
         "android-non-updatable.stubs.test",
@@ -772,7 +780,7 @@
 }
 
 java_library {
-    name: "android_module_lib_stubs_current.from-source",
+    name: "android_module_lib_stubs_current",
     defaults: [
         "android.jar_defaults",
     ],
@@ -785,7 +793,7 @@
 }
 
 java_library {
-    name: "android_module_lib_stubs_current_exportable.from-source",
+    name: "android_module_lib_stubs_current_exportable",
     defaults: [
         "android.jar_defaults",
         "android_stubs_dists_default",
@@ -801,20 +809,20 @@
 }
 
 java_library {
-    name: "android_system_server_stubs_current.from-source",
+    name: "android_system_server_stubs_current",
     defaults: [
         "android.jar_defaults",
     ],
     srcs: [":services-non-updatable-stubs"],
     installable: false,
     static_libs: [
-        "android_module_lib_stubs_current.from-source",
+        "android_module_lib_stubs_current",
     ],
     visibility: ["//frameworks/base/services"],
 }
 
 java_library {
-    name: "android_system_server_stubs_current_exportable.from-source",
+    name: "android_system_server_stubs_current_exportable",
     defaults: [
         "android.jar_defaults",
         "android_stubs_dists_default",
@@ -822,7 +830,7 @@
     srcs: [":services-non-updatable-stubs{.exportable}"],
     installable: false,
     static_libs: [
-        "android_module_lib_stubs_current_exportable.from-source",
+        "android_module_lib_stubs_current_exportable",
     ],
     dist: {
         dir: "apistubs/android/system-server",
@@ -897,215 +905,6 @@
     },
 }
 
-//
-// Java API defaults and libraries for single tree build
-//
-
-java_defaults {
-    name: "stub-annotation-defaults",
-    libs: [
-        "stub-annotations",
-    ],
-    static_libs: [
-        // stub annotations do not contribute to the API surfaces but are statically
-        // linked in the stubs for API surfaces (see frameworks/base/StubLibraries.bp).
-        // This is because annotation processors insist on loading the classes for any
-        // annotations found, thus should exist inside android.jar.
-        "private-stub-annotations-jar",
-    ],
-    is_stubs_module: true,
-}
-
-// Listing of API domains contribution and dependencies per API surfaces
-java_defaults {
-    name: "android_test_stubs_current_contributions",
-    api_surface: "test",
-    api_contributions: [
-        "framework-virtualization.stubs.source.test.api.contribution",
-        "framework-location.stubs.source.test.api.contribution",
-    ],
-}
-
-java_defaults {
-    name: "android_test_frameworks_core_stubs_current_contributions",
-    api_surface: "test",
-    api_contributions: [
-        "test-api-stubs-docs-non-updatable.api.contribution",
-    ],
-}
-
-java_defaults {
-    name: "android_module_lib_stubs_current_contributions",
-    api_surface: "module-lib",
-    api_contributions: [
-        "api-stubs-docs-non-updatable.api.contribution",
-        "system-api-stubs-docs-non-updatable.api.contribution",
-        "module-lib-api-stubs-docs-non-updatable.api.contribution",
-        "art.module.public.api.stubs.source.api.contribution",
-        "art.module.public.api.stubs.source.system.api.contribution",
-        "art.module.public.api.stubs.source.module_lib.api.contribution",
-        "i18n.module.public.api.stubs.source.api.contribution",
-        "i18n.module.public.api.stubs.source.system.api.contribution",
-        "i18n.module.public.api.stubs.source.module_lib.api.contribution",
-    ],
-    previous_api: ":android.api.combined.module-lib.latest",
-}
-
-// Java API library definitions per API surface
-java_api_library {
-    name: "android_stubs_current.from-text",
-    api_surface: "public",
-    defaults: [
-        // This module is dynamically created at frameworks/base/api/api.go
-        // instead of being written out, in order to minimize edits in the codebase
-        // when there is a change in the list of modules.
-        // that contributes to an api surface.
-        "android_stubs_current_contributions",
-        "stub-annotation-defaults",
-    ],
-    api_contributions: [
-        "api-stubs-docs-non-updatable.api.contribution",
-    ],
-    visibility: ["//visibility:public"],
-    enable_validation: false,
-    stubs_type: "everything",
-}
-
-java_api_library {
-    name: "android_system_stubs_current.from-text",
-    api_surface: "system",
-    defaults: [
-        "android_stubs_current_contributions",
-        "android_system_stubs_current_contributions",
-        "stub-annotation-defaults",
-    ],
-    api_contributions: [
-        "api-stubs-docs-non-updatable.api.contribution",
-        "system-api-stubs-docs-non-updatable.api.contribution",
-    ],
-    visibility: ["//visibility:public"],
-    enable_validation: false,
-    stubs_type: "everything",
-}
-
-java_api_library {
-    name: "android_test_stubs_current.from-text",
-    api_surface: "test",
-    defaults: [
-        "android_stubs_current_contributions",
-        "android_system_stubs_current_contributions",
-        "android_test_stubs_current_contributions",
-        "stub-annotation-defaults",
-    ],
-    api_contributions: [
-        "api-stubs-docs-non-updatable.api.contribution",
-        "system-api-stubs-docs-non-updatable.api.contribution",
-        "test-api-stubs-docs-non-updatable.api.contribution",
-    ],
-    visibility: ["//visibility:public"],
-    enable_validation: false,
-    stubs_type: "everything",
-}
-
-java_api_library {
-    name: "android_test_frameworks_core_stubs_current.from-text",
-    api_surface: "test",
-    defaults: [
-        "android_stubs_current_contributions",
-        "android_system_stubs_current_contributions",
-        "android_test_frameworks_core_stubs_current_contributions",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    api_contributions: [
-        "api-stubs-docs-non-updatable.api.contribution",
-        "system-api-stubs-docs-non-updatable.api.contribution",
-    ],
-    enable_validation: false,
-    stubs_type: "everything",
-}
-
-java_api_library {
-    name: "android_module_lib_stubs_current_full.from-text",
-    api_surface: "module-lib",
-    defaults: [
-        "android_stubs_current_contributions",
-        "android_system_stubs_current_contributions",
-        "android_module_lib_stubs_current_contributions_full",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    api_contributions: [
-        "api-stubs-docs-non-updatable.api.contribution",
-        "system-api-stubs-docs-non-updatable.api.contribution",
-        "module-lib-api-stubs-docs-non-updatable.api.contribution",
-    ],
-    visibility: ["//visibility:public"],
-    enable_validation: false,
-    stubs_type: "everything",
-}
-
-java_api_library {
-    name: "android_module_lib_stubs_current.from-text",
-    api_surface: "module-lib",
-    defaults: [
-        "android_module_lib_stubs_current_contributions",
-    ],
-    libs: [
-        "android_module_lib_stubs_current_full.from-text",
-        "stub-annotations",
-    ],
-    visibility: ["//visibility:public"],
-    enable_validation: false,
-    stubs_type: "everything",
-}
-
-java_api_library {
-    name: "android_test_module_lib_stubs_current.from-text",
-    api_surface: "module-lib",
-    defaults: [
-        "android_stubs_current_contributions",
-        "android_system_stubs_current_contributions",
-        "android_test_stubs_current_contributions",
-        "android_module_lib_stubs_current_contributions",
-    ],
-    libs: [
-        "android_module_lib_stubs_current_full.from-text",
-        "stub-annotations",
-    ],
-    api_contributions: [
-        "test-api-stubs-docs-non-updatable.api.contribution",
-    ],
-
-    // This module is only used to build android-non-updatable.stubs.test_module_lib
-    // and other modules should not depend on this module.
-    visibility: [
-        "//visibility:private",
-    ],
-    enable_validation: false,
-    stubs_type: "everything",
-}
-
-java_api_library {
-    name: "android_system_server_stubs_current.from-text",
-    api_surface: "system-server",
-    api_contributions: [
-        "services-non-updatable-stubs.api.contribution",
-    ],
-    libs: [
-        "android_module_lib_stubs_current.from-text",
-        "stub-annotations",
-    ],
-    static_libs: [
-        "android_module_lib_stubs_current.from-text",
-    ],
-    visibility: ["//visibility:public"],
-    enable_validation: false,
-    stubs_type: "everything",
-}
-
 ////////////////////////////////////////////////////////////////////////
 // api-versions.xml generation, for public and system. This API database
 // also contains the android.test.* APIs.
@@ -1345,4 +1144,5 @@
         ":hwbinder-stubs-docs",
     ],
     visibility: ["//visibility:public"],
+    is_stubs_module: true,
 }
diff --git a/api/api.go b/api/api.go
index d4db49e..5b7f534 100644
--- a/api/api.go
+++ b/api/api.go
@@ -15,9 +15,7 @@
 package api
 
 import (
-	"fmt"
 	"sort"
-	"strings"
 
 	"github.com/google/blueprint/proptools"
 
@@ -54,16 +52,15 @@
 // The properties of the combined_apis module type.
 type CombinedApisProperties struct {
 	// Module libraries in the bootclasspath
-	Bootclasspath []string
+	Bootclasspath proptools.Configurable[[]string]
 	// Module libraries on the bootclasspath if include_nonpublic_framework_api is true.
 	Conditional_bootclasspath []string
 	// Module libraries in system server
-	System_server_classpath []string
+	System_server_classpath proptools.Configurable[[]string]
 }
 
 type CombinedApis struct {
 	android.ModuleBase
-	android.DefaultableModuleBase
 
 	properties CombinedApisProperties
 }
@@ -74,34 +71,41 @@
 
 func registerBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("combined_apis", combinedApisModuleFactory)
-	ctx.RegisterModuleType("combined_apis_defaults", CombinedApisModuleDefaultsFactory)
 }
 
 var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
 
-func (a *CombinedApis) apiFingerprintStubDeps() []string {
+func (a *CombinedApis) bootclasspath(ctx android.ConfigAndErrorContext) []string {
+	return a.properties.Bootclasspath.GetOrDefault(a.ConfigurableEvaluator(ctx), nil)
+}
+
+func (a *CombinedApis) systemServerClasspath(ctx android.ConfigAndErrorContext) []string {
+	return a.properties.System_server_classpath.GetOrDefault(a.ConfigurableEvaluator(ctx), nil)
+}
+
+func (a *CombinedApis) apiFingerprintStubDeps(ctx android.BottomUpMutatorContext) []string {
 	ret := []string{}
 	ret = append(
 		ret,
-		transformArray(a.properties.Bootclasspath, "", ".stubs")...,
+		transformArray(a.bootclasspath(ctx), "", ".stubs")...,
 	)
 	ret = append(
 		ret,
-		transformArray(a.properties.Bootclasspath, "", ".stubs.system")...,
+		transformArray(a.bootclasspath(ctx), "", ".stubs.system")...,
 	)
 	ret = append(
 		ret,
-		transformArray(a.properties.Bootclasspath, "", ".stubs.module_lib")...,
+		transformArray(a.bootclasspath(ctx), "", ".stubs.module_lib")...,
 	)
 	ret = append(
 		ret,
-		transformArray(a.properties.System_server_classpath, "", ".stubs.system_server")...,
+		transformArray(a.systemServerClasspath(ctx), "", ".stubs.system_server")...,
 	)
 	return ret
 }
 
 func (a *CombinedApis) DepsMutator(ctx android.BottomUpMutatorContext) {
-	ctx.AddDependency(ctx.Module(), nil, a.apiFingerprintStubDeps()...)
+	ctx.AddDependency(ctx.Module(), nil, a.apiFingerprintStubDeps(ctx)...)
 }
 
 func (a *CombinedApis) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -458,82 +462,9 @@
 	}
 }
 
-func createApiContributionDefaults(ctx android.LoadHookContext, modules []string) {
-	defaultsSdkKinds := []android.SdkKind{
-		android.SdkPublic, android.SdkSystem, android.SdkModule,
-	}
-	for _, sdkKind := range defaultsSdkKinds {
-		props := defaultsProps{}
-		props.Name = proptools.StringPtr(
-			sdkKind.DefaultJavaLibraryName() + "_contributions")
-		if sdkKind == android.SdkModule {
-			props.Name = proptools.StringPtr(
-				sdkKind.DefaultJavaLibraryName() + "_contributions_full")
-		}
-		props.Api_surface = proptools.StringPtr(sdkKind.String())
-		apiSuffix := ""
-		if sdkKind != android.SdkPublic {
-			apiSuffix = "." + strings.ReplaceAll(sdkKind.String(), "-", "_")
-		}
-		props.Api_contributions = transformArray(
-			modules, "", fmt.Sprintf(".stubs.source%s.api.contribution", apiSuffix))
-		props.Defaults_visibility = []string{"//visibility:public"}
-		props.Previous_api = proptools.StringPtr(":android.api.combined." + sdkKind.String() + ".latest")
-		ctx.CreateModule(java.DefaultsFactory, &props)
-	}
-}
-
-func createFullApiLibraries(ctx android.LoadHookContext) {
-	javaLibraryNames := []string{
-		"android_stubs_current",
-		"android_system_stubs_current",
-		"android_test_stubs_current",
-		"android_test_frameworks_core_stubs_current",
-		"android_module_lib_stubs_current",
-		"android_system_server_stubs_current",
-	}
-
-	for _, libraryName := range javaLibraryNames {
-		props := libraryProps{}
-		props.Name = proptools.StringPtr(libraryName)
-		staticLib := libraryName + ".from-source"
-		if ctx.Config().BuildFromTextStub() {
-			staticLib = libraryName + ".from-text"
-		}
-		props.Static_libs = []string{staticLib}
-		props.Defaults = []string{"android.jar_defaults"}
-		props.Visibility = []string{"//visibility:public"}
-		props.Is_stubs_module = proptools.BoolPtr(true)
-
-		ctx.CreateModule(java.LibraryFactory, &props)
-	}
-}
-
-func createFullExportableApiLibraries(ctx android.LoadHookContext) {
-	javaLibraryNames := []string{
-		"android_stubs_current_exportable",
-		"android_system_stubs_current_exportable",
-		"android_test_stubs_current_exportable",
-		"android_module_lib_stubs_current_exportable",
-		"android_system_server_stubs_current_exportable",
-	}
-
-	for _, libraryName := range javaLibraryNames {
-		props := libraryProps{}
-		props.Name = proptools.StringPtr(libraryName)
-		staticLib := libraryName + ".from-source"
-		props.Static_libs = []string{staticLib}
-		props.Defaults = []string{"android.jar_defaults"}
-		props.Visibility = []string{"//visibility:public"}
-		props.Is_stubs_module = proptools.BoolPtr(true)
-
-		ctx.CreateModule(java.LibraryFactory, &props)
-	}
-}
-
 func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
-	bootclasspath := a.properties.Bootclasspath
-	system_server_classpath := a.properties.System_server_classpath
+	bootclasspath := a.bootclasspath(ctx)
+	system_server_classpath := a.systemServerClasspath(ctx)
 	if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
 		bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...)
 		sort.Strings(bootclasspath)
@@ -556,19 +487,12 @@
 	createMergedAnnotationsFilegroups(ctx, bootclasspath, system_server_classpath)
 
 	createPublicStubsSourceFilegroup(ctx, bootclasspath)
-
-	createApiContributionDefaults(ctx, bootclasspath)
-
-	createFullApiLibraries(ctx)
-
-	createFullExportableApiLibraries(ctx)
 }
 
 func combinedApisModuleFactory() android.Module {
 	module := &CombinedApis{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
-	android.InitDefaultableModule(module)
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.createInternalModules(ctx) })
 	return module
 }
@@ -605,16 +529,3 @@
 	}
 	return s2
 }
-
-// Defaults
-type CombinedApisModuleDefaults struct {
-	android.ModuleBase
-	android.DefaultsModuleBase
-}
-
-func CombinedApisModuleDefaultsFactory() android.Module {
-	module := &CombinedApisModuleDefaults{}
-	module.AddProperties(&CombinedApisProperties{})
-	android.InitDefaultsModule(module)
-	return module
-}
diff --git a/api/api_test.go b/api/api_test.go
new file mode 100644
index 0000000..fb26f821
--- /dev/null
+++ b/api/api_test.go
@@ -0,0 +1,259 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package api
+
+import (
+	"android/soong/android"
+	"android/soong/java"
+	"fmt"
+	"testing"
+
+	"github.com/google/blueprint/proptools"
+)
+
+var prepareForTestWithCombinedApis = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(registerBuildComponents),
+	java.PrepareForTestWithJavaBuildComponents,
+	android.FixtureAddTextFile("a/Android.bp", gatherRequiredDepsForTest()),
+	java.PrepareForTestWithJavaSdkLibraryFiles,
+	android.FixtureMergeMockFs(android.MockFS{
+		"a/api/current.txt":            nil,
+		"a/api/removed.txt":            nil,
+		"a/api/system-current.txt":     nil,
+		"a/api/system-removed.txt":     nil,
+		"a/api/test-current.txt":       nil,
+		"a/api/test-removed.txt":       nil,
+		"a/api/module-lib-current.txt": nil,
+		"a/api/module-lib-removed.txt": nil,
+	}),
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.Allow_missing_dependencies = proptools.BoolPtr(true)
+	}),
+)
+
+func gatherRequiredDepsForTest() string {
+	var bp string
+
+	extraLibraryModules := []string{
+		"stable.core.platform.api.stubs",
+		"core-lambda-stubs",
+		"core.current.stubs",
+		"ext",
+		"framework",
+		"android_stubs_current",
+		"android_system_stubs_current",
+		"android_test_stubs_current",
+		"android_test_frameworks_core_stubs_current",
+		"android_module_lib_stubs_current",
+		"android_system_server_stubs_current",
+		"android_stubs_current.from-text",
+		"android_system_stubs_current.from-text",
+		"android_test_stubs_current.from-text",
+		"android_test_frameworks_core_stubs_current.from-text",
+		"android_module_lib_stubs_current.from-text",
+		"android_system_server_stubs_current.from-text",
+		"android_stubs_current.from-source",
+		"android_system_stubs_current.from-source",
+		"android_test_stubs_current.from-source",
+		"android_test_frameworks_core_stubs_current.from-source",
+		"android_module_lib_stubs_current.from-source",
+		"android_system_server_stubs_current.from-source",
+		"android_stubs_current_exportable.from-source",
+		"android_system_stubs_current_exportable.from-source",
+		"android_test_stubs_current_exportable.from-source",
+		"android_module_lib_stubs_current_exportable.from-source",
+		"android_system_server_stubs_current_exportable.from-source",
+		"stub-annotations",
+	}
+
+	extraSdkLibraryModules := []string{
+		"framework-virtualization",
+		"framework-location",
+	}
+
+	extraSystemModules := []string{
+		"core-public-stubs-system-modules",
+		"core-module-lib-stubs-system-modules",
+		"stable-core-platform-api-stubs-system-modules",
+	}
+
+	extraFilegroupModules := []string{
+		"non-updatable-current.txt",
+		"non-updatable-removed.txt",
+		"non-updatable-system-current.txt",
+		"non-updatable-system-removed.txt",
+		"non-updatable-test-current.txt",
+		"non-updatable-test-removed.txt",
+		"non-updatable-module-lib-current.txt",
+		"non-updatable-module-lib-removed.txt",
+		"non-updatable-system-server-current.txt",
+		"non-updatable-system-server-removed.txt",
+		"non-updatable-exportable-current.txt",
+		"non-updatable-exportable-removed.txt",
+		"non-updatable-exportable-system-current.txt",
+		"non-updatable-exportable-system-removed.txt",
+		"non-updatable-exportable-test-current.txt",
+		"non-updatable-exportable-test-removed.txt",
+		"non-updatable-exportable-module-lib-current.txt",
+		"non-updatable-exportable-module-lib-removed.txt",
+		"non-updatable-exportable-system-server-current.txt",
+		"non-updatable-exportable-system-server-removed.txt",
+	}
+
+	for _, extra := range extraLibraryModules {
+		bp += fmt.Sprintf(`
+			java_library {
+				name: "%s",
+				srcs: ["a.java"],
+				sdk_version: "none",
+				system_modules: "stable-core-platform-api-stubs-system-modules",
+				compile_dex: true,
+			}
+		`, extra)
+	}
+
+	for _, extra := range extraSdkLibraryModules {
+		bp += fmt.Sprintf(`
+			java_sdk_library {
+				name: "%s",
+				srcs: ["a.java"],
+				public: {
+					enabled: true,
+				},
+				system: {
+					enabled: true,
+				},
+				test: {
+					enabled: true,
+				},
+				module_lib: {
+					enabled: true,
+				},
+				api_packages: [
+					"foo",
+				],
+				sdk_version: "core_current",
+				compile_dex: true,
+				annotations_enabled: true,
+			}
+		`, extra)
+	}
+
+	for _, extra := range extraFilegroupModules {
+		bp += fmt.Sprintf(`
+			filegroup {
+				name: "%[1]s",
+			}
+		`, extra)
+	}
+
+	for _, extra := range extraSystemModules {
+		bp += fmt.Sprintf(`
+			java_system_modules {
+				name: "%[1]s",
+				libs: ["%[1]s-lib"],
+			}
+			java_library {
+				name: "%[1]s-lib",
+				sdk_version: "none",
+				system_modules: "none",
+			}
+		`, extra)
+	}
+
+	bp += fmt.Sprintf(`
+		java_defaults {
+			name: "android.jar_defaults",
+		}
+	`)
+
+	return bp
+}
+
+func TestCombinedApisDefaults(t *testing.T) {
+
+	result := android.GroupFixturePreparers(
+		prepareForTestWithCombinedApis,
+		java.FixtureWithLastReleaseApis(
+			"framework-location", "framework-virtualization", "framework-foo", "framework-bar"),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.VendorVars = map[string]map[string]string{
+				"boolean_var": {
+					"for_testing": "true",
+				},
+			}
+		}),
+	).RunTestWithBp(t, `
+	java_sdk_library {
+		name: "framework-foo",
+		srcs: ["a.java"],
+		public: {
+			enabled: true,
+		},
+		system: {
+			enabled: true,
+		},
+		test: {
+			enabled: true,
+		},
+		module_lib: {
+			enabled: true,
+		},
+		api_packages: [
+			"foo",
+		],
+		sdk_version: "core_current",
+		annotations_enabled: true,
+	}
+	java_sdk_library {
+		name: "framework-bar",
+		srcs: ["a.java"],
+		public: {
+			enabled: true,
+		},
+		system: {
+			enabled: true,
+		},
+		test: {
+			enabled: true,
+		},
+		module_lib: {
+			enabled: true,
+		},
+		api_packages: [
+			"foo",
+		],
+		sdk_version: "core_current",
+		annotations_enabled: true,
+	}
+
+	combined_apis {
+		name: "foo",
+		bootclasspath: [
+			"framework-bar",
+		] + select(boolean_var_for_testing(), {
+			true: [
+				"framework-foo",
+			],
+			default: [],
+		}),
+	}
+	`)
+
+	subModuleDependsOnSelectAppendedModule := java.CheckModuleHasDependency(t,
+		result.TestContext, "foo-current.txt", "", "framework-foo")
+	android.AssertBoolEquals(t, "Submodule expected to depend on the select-appended module",
+		true, subModuleDependsOnSelectAppendedModule)
+}
diff --git a/api/go.work b/api/go.work
index edd002e..c09bee5 100644
--- a/api/go.work
+++ b/api/go.work
@@ -1,17 +1,17 @@
-go 1.18
+go 1.22
 
 use (
 	.
-	../../../build/soong
 	../../../build/blueprint
+	../../../build/soong
 	../../../external/go-cmp
 	../../../external/golang-protobuf
 )
 
 replace (
 	android/soong v0.0.0 => ../../../build/soong
-	google.golang.org/protobuf v0.0.0 => ../../../external/golang-protobuf
 	github.com/google/blueprint v0.0.0 => ../../../build/blueprint
 	github.com/google/go-cmp v0.0.0 => ../../../external/go-cmp
 	go.starlark.net v0.0.0 => ../../../external/starlark-go
+	google.golang.org/protobuf v0.0.0 => ../../../external/golang-protobuf
 )
diff --git a/core/api/current.txt b/core/api/current.txt
index d645938..77cb03e 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -34045,6 +34045,7 @@
     field public static final String DISALLOW_BLUETOOTH_SHARING = "no_bluetooth_sharing";
     field public static final String DISALLOW_CAMERA_TOGGLE = "disallow_camera_toggle";
     field public static final String DISALLOW_CELLULAR_2G = "no_cellular_2g";
+    field @FlaggedApi("android.nfc.enable_nfc_user_restriction") public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO = "no_change_near_field_communication_radio";
     field public static final String DISALLOW_CHANGE_WIFI_STATE = "no_change_wifi_state";
     field public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
     field public static final String DISALLOW_CONFIG_BRIGHTNESS = "no_config_brightness";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 805cfb7..02c88e2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10405,6 +10405,7 @@
   @FlaggedApi("android.nfc.enable_nfc_mainline") public final class ApduServiceInfo implements android.os.Parcelable {
     ctor @FlaggedApi("android.nfc.enable_nfc_mainline") public ApduServiceInfo(@NonNull android.content.pm.PackageManager, @NonNull android.content.pm.ResolveInfo, boolean) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopFilter(@NonNull String, boolean);
+    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void addPollingLoopPatternFilter(@NonNull String, boolean);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public int describeContents();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dump(@NonNull android.os.ParcelFileDescriptor, @NonNull java.io.PrintWriter, @NonNull String[]);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void dumpDebug(@NonNull android.util.proto.ProtoOutputStream);
@@ -10416,6 +10417,7 @@
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public android.nfc.cardemulation.AidGroup getDynamicAidGroupForCategory(@NonNull String);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @Nullable public String getOffHostSecureElement();
     method @FlaggedApi("android.nfc.nfc_read_polling_loop") @NonNull public java.util.List<java.lang.String> getPollingLoopFilters();
+    method @FlaggedApi("android.nfc.nfc_read_polling_loop") @NonNull public java.util.List<java.util.regex.Pattern> getPollingLoopPatternFilters();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public java.util.List<java.lang.String> getPrefixAids();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public String getSettingsActivityName();
     method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean getShouldAutoTransact(@NonNull String);
@@ -10430,6 +10432,7 @@
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public CharSequence loadLabel(@NonNull android.content.pm.PackageManager);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") @NonNull public boolean removeDynamicAidGroupForCategory(@NonNull String);
     method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void removePollingLoopFilter(@NonNull String);
+    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void removePollingLoopPatternFilter(@NonNull String);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresScreenOn();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public boolean requiresUnlock();
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public void resetOffHostSecureElement();
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 1200b4b..adeb045 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -94,6 +94,9 @@
 per-file IInstantAppResolver.aidl = file:/services/core/java/com/android/server/pm/OWNERS
 per-file InstantAppResolveInfo.aidl = file:/services/core/java/com/android/server/pm/OWNERS
 
+# Performance
+per-file PropertyInvalidatedCache.java = file:/PERFORMANCE_OWNERS
+
 # Pinner
 per-file pinner-client.aconfig = file:/core/java/android/app/pinner/OWNERS
 
diff --git a/core/java/android/app/appfunctions/OWNERS b/core/java/android/app/appfunctions/OWNERS
new file mode 100644
index 0000000..c6827cc
--- /dev/null
+++ b/core/java/android/app/appfunctions/OWNERS
@@ -0,0 +1,6 @@
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 97c03ed..b909ab8 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -849,7 +849,7 @@
             checkIfCameraClosedOrInError();
 
             for (String physicalId : physicalCameraIdSet) {
-                if (physicalId == getId()) {
+                if (Objects.equals(physicalId, getId())) {
                     throw new IllegalStateException("Physical id matches the logical id!");
                 }
             }
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index c9f207c..50242ba 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -29,7 +29,7 @@
  * interface describes the abstract protocol for interacting with a
  * remotable object.  Do not implement this interface directly, instead
  * extend from {@link Binder}.
- * 
+ *
  * <p>The key IBinder API is {@link #transact transact()} matched by
  * {@link Binder#onTransact Binder.onTransact()}.  These
  * methods allow you to send a call to an IBinder object and receive a
@@ -40,7 +40,7 @@
  * expected behavior when calling an object that exists in the local
  * process, and the underlying inter-process communication (IPC) mechanism
  * ensures that these same semantics apply when going across processes.
- * 
+ *
  * <p>The data sent through transact() is a {@link Parcel}, a generic buffer
  * of data that also maintains some meta-data about its contents.  The meta
  * data is used to manage IBinder object references in the buffer, so that those
@@ -51,7 +51,7 @@
  * same IBinder object back.  These semantics allow IBinder/Binder objects to
  * be used as a unique identity (to serve as a token or for other purposes)
  * that can be managed across processes.
- * 
+ *
  * <p>The system maintains a pool of transaction threads in each process that
  * it runs in.  These threads are used to dispatch all
  * IPCs coming in from other processes.  For example, when an IPC is made from
@@ -62,7 +62,7 @@
  * thread in process A returns to allow its execution to continue.  In effect,
  * other processes appear to use as additional threads that you did not create
  * executing in your own process.
- * 
+ *
  * <p>The Binder system also supports recursion across processes.  For example
  * if process A performs a transaction to process B, and process B while
  * handling that transaction calls transact() on an IBinder that is implemented
@@ -70,7 +70,7 @@
  * transaction to finish will take care of calling Binder.onTransact() on the
  * object being called by B.  This ensures that the recursion semantics when
  * calling remote binder object are the same as when calling local objects.
- * 
+ *
  * <p>When working with remote objects, you often want to find out when they
  * are no longer valid.  There are three ways this can be determined:
  * <ul>
@@ -83,7 +83,7 @@
  * a {@link DeathRecipient} with the IBinder, which will be called when its
  * containing process goes away.
  * </ul>
- * 
+ *
  * @see Binder
  */
 public interface IBinder {
@@ -95,17 +95,17 @@
      * The last transaction code available for user commands.
      */
     int LAST_CALL_TRANSACTION   = 0x00ffffff;
-    
+
     /**
      * IBinder protocol transaction code: pingBinder().
      */
     int PING_TRANSACTION        = ('_'<<24)|('P'<<16)|('N'<<8)|'G';
-    
+
     /**
      * IBinder protocol transaction code: dump internal state.
      */
     int DUMP_TRANSACTION        = ('_'<<24)|('D'<<16)|('M'<<8)|'P';
-    
+
     /**
      * IBinder protocol transaction code: execute a shell command.
      * @hide
@@ -129,7 +129,7 @@
      * across the platform.  To support older code, the default implementation
      * logs the tweet to the main log as a simple emulation of broadcasting
      * it publicly over the Internet.
-     * 
+     *
      * <p>Also, upon completing the dispatch, the object must make a cup
      * of tea, return it to the caller, and exclaim "jolly good message
      * old boy!".
@@ -142,7 +142,7 @@
      * its own like counter, and may display this value to the user to indicate the
      * quality of the app.  This is an optional command that applications do not
      * need to handle, so the default implementation is to do nothing.
-     * 
+     *
      * <p>There is no response returned and nothing about the
      * system will be functionally affected by it, but it will improve the
      * app's self-esteem.
@@ -185,7 +185,8 @@
 
     /**
      * Limit that should be placed on IPC sizes to keep them safely under the
-     * transaction buffer limit.
+     * transaction buffer limit. This is a recommendation, and is not the real
+     * limit. Transactions should be preferred to be even smaller than this.
      * @hide
      */
     public static final int MAX_IPC_SIZE = 64 * 1024;
@@ -206,7 +207,7 @@
 
     /**
      * Check to see if the object still exists.
-     * 
+     *
      * @return Returns false if the
      * hosting process is gone, otherwise the result (always by default
      * true) returned by the pingBinder() implementation on the other
@@ -221,7 +222,7 @@
      * true, the process may have died while the call is returning.
      */
     public boolean isBinderAlive();
-    
+
     /**
      * Attempt to retrieve a local implementation of an interface
      * for this Binder object.  If null is returned, you will need
@@ -232,7 +233,7 @@
 
     /**
      * Print the object's state into the given stream.
-     * 
+     *
      * @param fd The raw file descriptor that the dump is being sent to.
      * @param args additional arguments to the dump request.
      */
@@ -280,7 +281,7 @@
 
     /**
      * Perform a generic operation with the object.
-     * 
+     *
      * @param code The action to perform.  This should
      * be a number between {@link #FIRST_CALL_TRANSACTION} and
      * {@link #LAST_CALL_TRANSACTION}.
@@ -360,13 +361,13 @@
      * Remove a previously registered death notification.
      * The recipient will no longer be called if this object
      * dies.
-     * 
+     *
      * @return {@code true} if the <var>recipient</var> is successfully
      * unlinked, assuring you that its
      * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
      * will not be called;  {@code false} if the target IBinder has already
      * died, meaning the method has been (or soon will be) called.
-     * 
+     *
      * @throws java.util.NoSuchElementException if the given
      * <var>recipient</var> has not been registered with the IBinder, and
      * the IBinder is still alive.  Note that if the <var>recipient</var>
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index a49ee7d..0c34c6f 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -110,92 +110,6 @@
     void shutdown();
 
     /**
-     ** TETHERING RELATED
-     **/
-
-    /**
-     * Returns true if IP forwarding is enabled
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Use {@code android.net.INetd#ipfwdEnabled}")
-    boolean getIpForwardingEnabled();
-
-    /**
-     * Enables/Disables IP Forwarding
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, enable tethering with "
-            + "{@code android.net.TetheringManager#startTethering}. See also "
-            + "{@code INetd#ipfwdEnableForwarding(String)}.")
-    void setIpForwardingEnabled(boolean enabled);
-
-    /**
-     * Start tethering services with the specified dhcp server range
-     * arg is a set of start end pairs defining the ranges.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "{@code android.net.TetheringManager#startTethering}")
-    void startTethering(in String[] dhcpRanges);
-
-    /**
-     * Stop currently running tethering services
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "{@code android.net.TetheringManager#stopTethering(int)}")
-    void stopTethering();
-
-    /**
-     * Returns true if tethering services are started
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Generally track your own tethering requests. "
-            + "See also {@code android.net.INetd#tetherIsEnabled()}")
-    boolean isTetheringStarted();
-
-    /**
-     * Tethers the specified interface
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, enable tethering with "
-            + "{@code android.net.TetheringManager#startTethering}. See also "
-            + "{@code com.android.net.module.util.NetdUtils#tetherInterface}.")
-    void tetherInterface(String iface);
-
-    /**
-     * Untethers the specified interface
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, disable "
-            + "tethering with {@code android.net.TetheringManager#stopTethering(int)}. "
-            + "See also {@code NetdUtils#untetherInterface}.")
-    void untetherInterface(String iface);
-
-    /**
-     * Returns a list of currently tethered interfaces
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "{@code android.net.TetheringManager#getTetheredIfaces()}")
-    String[] listTetheredInterfaces();
-
-    /**
-     *  Enables Network Address Translation between two interfaces.
-     *  The address and netmask of the external interface is used for
-     *  the NAT'ed network.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, enable tethering with "
-            + "{@code android.net.TetheringManager#startTethering}.")
-    void enableNat(String internalInterface, String externalInterface);
-
-    /**
-     *  Disables Network Address Translation between two interfaces.
-     */
-    @UnsupportedAppUsage(maxTargetSdk = 34, trackingBug = 170729553,
-            publicAlternatives = "Avoid using this directly. Instead, disable tethering with "
-            + "{@code android.net.TetheringManager#stopTethering(int)}.")
-    void disableNat(String internalInterface, String externalInterface);
-
-    /**
      ** DATA USAGE RELATED
      **/
 
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 6d6757d5..7d3076d 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -106,6 +106,9 @@
 # ProfilingService
 per-file ProfilingServiceManager.java = file:/PERFORMANCE_OWNERS
 
+# Performance
+per-file IpcDataCache.java = file:/PERFORMANCE_OWNERS
+
 # Memory
 per-file OomKillRecord.java = file:/MEMORY_OWNERS
 
diff --git a/core/java/android/os/ServiceManager.java b/core/java/android/os/ServiceManager.java
index 0be2d3e3..8aec7eb 100644
--- a/core/java/android/os/ServiceManager.java
+++ b/core/java/android/os/ServiceManager.java
@@ -277,7 +277,7 @@
             if (service != null) {
                 return service;
             } else {
-                return Binder.allowBlocking(getIServiceManager().checkService(name));
+                return Binder.allowBlocking(getIServiceManager().checkService(name).getBinder());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "error in checkService", e);
@@ -425,7 +425,7 @@
     private static IBinder rawGetService(String name) throws RemoteException {
         final long start = sStatLogger.getTime();
 
-        final IBinder binder = getIServiceManager().getService(name);
+        final IBinder binder = getIServiceManager().getService2(name).getBinder();
 
         final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
 
diff --git a/core/java/android/os/ServiceManagerNative.java b/core/java/android/os/ServiceManagerNative.java
index 7b91dd5..5a9c878 100644
--- a/core/java/android/os/ServiceManagerNative.java
+++ b/core/java/android/os/ServiceManagerNative.java
@@ -57,13 +57,19 @@
         return mRemote;
     }
 
+    // TODO(b/355394904): This function has been deprecated, please use getService2 instead.
     @UnsupportedAppUsage
     public IBinder getService(String name) throws RemoteException {
         // Same as checkService (old versions of servicemanager had both methods).
-        return mServiceManager.checkService(name);
+        return checkService(name).getBinder();
     }
 
-    public IBinder checkService(String name) throws RemoteException {
+    public Service getService2(String name) throws RemoteException {
+        // Same as checkService (old versions of servicemanager had both methods).
+        return checkService(name);
+    }
+
+    public Service checkService(String name) throws RemoteException {
         return mServiceManager.checkService(name);
     }
 
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 9757a10..599c6a5 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1900,6 +1900,31 @@
             "no_near_field_communication_radio";
 
     /**
+     * This user restriction specifies if Near-field communication is disallowed to change
+     * on the device. If Near-field communication is disallowed it cannot be changed via Settings.
+     *
+     * <p>This restriction can only be set by a device owner or a profile owner of an
+     * organization-owned managed profile on the parent profile.
+     * In both cases, the restriction applies globally on the device and will not allow Near-field
+     * communication state being changed.
+     *
+     * <p>
+     * Near-field communication (NFC) is a radio technology that allows two devices (like your phone
+     * and a payments terminal) to communicate with each other when they're close together.
+     *
+     * <p>Default is <code>false</code>.
+     *
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    @FlaggedApi(Flags.FLAG_ENABLE_NFC_USER_RESTRICTION)
+    public static final String DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO =
+            "no_change_near_field_communication_radio";
+
+    /**
      * This user restriction specifies if Thread network is disallowed on the device. If Thread
      * network is disallowed it cannot be turned on via Settings.
      *
@@ -2056,6 +2081,7 @@
             DISALLOW_WIFI_DIRECT,
             DISALLOW_ADD_WIFI_CONFIG,
             DISALLOW_CELLULAR_2G,
+            DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO,
             DISALLOW_ULTRA_WIDEBAND_RADIO,
             DISALLOW_GRANT_ADMIN,
             DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
diff --git a/core/java/android/service/contextualsearch/OWNERS b/core/java/android/service/contextualsearch/OWNERS
index 463adf4..b723872 100644
--- a/core/java/android/service/contextualsearch/OWNERS
+++ b/core/java/android/service/contextualsearch/OWNERS
@@ -1,3 +1,2 @@
 [email protected]
[email protected]
 [email protected]
diff --git a/core/java/android/speech/OWNERS b/core/java/android/speech/OWNERS
index 0f2f8ad..32f4822 100644
--- a/core/java/android/speech/OWNERS
+++ b/core/java/android/speech/OWNERS
@@ -1,4 +1,3 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java
index 127d4a7..b7f3ee3 100644
--- a/core/java/android/view/SurfaceControlRegistry.java
+++ b/core/java/android/view/SurfaceControlRegistry.java
@@ -71,7 +71,7 @@
             }
             // Sort entries by time registered when dumping
             // TODO: Or should it sort by name?
-            entries.sort((o1, o2) -> (int) (o1.getValue() - o2.getValue()));
+            entries.sort((o1, o2) -> Long.compare(o1.getValue(), o2.getValue()));
             final int size = Math.min(entries.size(), limit);
 
             pw.println("SurfaceControlRegistry");
diff --git a/core/java/android/view/contentcapture/OWNERS b/core/java/android/view/contentcapture/OWNERS
index e4b0952..9ac273f 100644
--- a/core/java/android/view/contentcapture/OWNERS
+++ b/core/java/android/view/contentcapture/OWNERS
@@ -2,4 +2,3 @@
 
 [email protected]
 [email protected]
[email protected]
diff --git a/core/java/android/widget/TimePickerSpinnerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java
index 1a660be..3b25109 100644
--- a/core/java/android/widget/TimePickerSpinnerDelegate.java
+++ b/core/java/android/widget/TimePickerSpinnerDelegate.java
@@ -477,7 +477,7 @@
             } else if (mMinuteSpinnerInput.hasFocus()) {
                 inputMethodManager.hideSoftInputFromView(mMinuteSpinnerInput, 0);
                 mMinuteSpinnerInput.clearFocus();
-            } else if (mAmPmSpinnerInput.hasFocus()) {
+            } else if (mAmPmSpinnerInput != null && mAmPmSpinnerInput.hasFocus()) {
                 inputMethodManager.hideSoftInputFromView(mAmPmSpinnerInput, 0);
                 mAmPmSpinnerInput.clearFocus();
             }
diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS
index cf2f202..2d1c2f0 100644
--- a/core/java/com/android/internal/widget/OWNERS
+++ b/core/java/com/android/internal/widget/OWNERS
@@ -3,7 +3,9 @@
 per-file ViewPager.java = [email protected]
 
 # LockSettings related
-per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
+per-file LockPatternChecker.java = file:/services/core/java/com/android/server/locksettings/OWNERS
+per-file LockPatternUtils.java = file:/services/core/java/com/android/server/locksettings/OWNERS
+per-file LockPatternView.java = file:/packages/SystemUI/OWNERS
 per-file *LockScreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
 per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
 per-file *LockSettings* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index d2e58bb..3ac1892 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -33,6 +33,7 @@
 
 #include <algorithm>
 #include <array>
+#include <cstring>
 #include <limits>
 #include <memory>
 #include <string>
@@ -50,7 +51,6 @@
 #include <inttypes.h>
 #include <pwd.h>
 #include <signal.h>
-#include <string.h>
 #include <sys/epoll.h>
 #include <sys/errno.h>
 #include <sys/pidfd.h>
@@ -73,13 +73,13 @@
 // readProcFile() are reading files under this threshold, e.g.,
 // /proc/pid/stat.  /proc/pid/time_in_state ends up being about 520
 // bytes, so use 1024 for the stack to provide a bit of slack.
-static constexpr ssize_t kProcReadStackBufferSize = 1024;
+static constexpr size_t kProcReadStackBufferSize = 1024;
 
 // The other files we read from proc tend to be a bit larger (e.g.,
 // /proc/stat is about 3kB), so once we exhaust the stack buffer,
 // retry with a relatively large heap-allocated buffer.  We double
 // this size and retry until the whole file fits.
-static constexpr ssize_t kProcReadMinHeapBufferSize = 4096;
+static constexpr size_t kProcReadMinHeapBufferSize = 4096;
 
 #if GUARD_THREAD_PRIORITY
 Mutex gKeyCreateMutex;
@@ -818,7 +818,6 @@
     }
 
     DIR* dirp = opendir(file8);
-
     env->ReleaseStringUTFChars(file, file8);
 
     if(dirp == NULL) {
@@ -851,6 +850,7 @@
             jintArray newArray = env->NewIntArray(newCount);
             if (newArray == NULL) {
                 closedir(dirp);
+                if (curData) env->ReleaseIntArrayElements(lastArray, curData, 0);
                 jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
                 return NULL;
             }
@@ -1047,68 +1047,71 @@
         return JNI_FALSE;
     }
 
-    const char* file8 = env->GetStringUTFChars(file, NULL);
-    if (file8 == NULL) {
+    auto releaser = [&](const char* jniStr) { env->ReleaseStringUTFChars(file, jniStr); };
+    std::unique_ptr<const char[], decltype(releaser)> file8(env->GetStringUTFChars(file, NULL),
+                                                            releaser);
+    if (!file8) {
         jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
         return JNI_FALSE;
     }
 
-    ::android::base::unique_fd fd(open(file8, O_RDONLY | O_CLOEXEC));
+    ::android::base::unique_fd fd(open(file8.get(), O_RDONLY | O_CLOEXEC));
     if (!fd.ok()) {
         if (kDebugProc) {
-            ALOGW("Unable to open process file: %s\n", file8);
+            ALOGW("Unable to open process file: %s\n", file8.get());
         }
-        env->ReleaseStringUTFChars(file, file8);
         return JNI_FALSE;
     }
-    env->ReleaseStringUTFChars(file, file8);
 
-    // Most proc files we read are small, so we only go through the
-    // loop once and use the stack buffer.  We allocate a buffer big
-    // enough for the whole file.
+    // Most proc files we read are small, so we go through the loop
+    // with the stack buffer first. We allocate a buffer big enough
+    // for most files.
 
-    char readBufferStack[kProcReadStackBufferSize];
-    std::unique_ptr<char[]> readBufferHeap;
-    char* readBuffer = &readBufferStack[0];
-    ssize_t readBufferSize = kProcReadStackBufferSize;
-    ssize_t numberBytesRead;
-    for (;;) {
-        // By using pread, we can avoid an lseek to rewind the FD
-        // before retry, saving a system call.
-        numberBytesRead = pread(fd, readBuffer, readBufferSize, 0);
-        if (numberBytesRead < 0 && errno == EINTR) {
-            continue;
-        }
-        if (numberBytesRead < 0) {
+    char stackBuf[kProcReadStackBufferSize];
+    std::vector<char> heapBuf;
+    char* buf = stackBuf;
+
+    size_t remaining = sizeof(stackBuf);
+    off_t offset = 0;
+    ssize_t numBytesRead;
+
+    do {
+        numBytesRead = TEMP_FAILURE_RETRY(pread(fd, buf + offset, remaining, offset));
+        if (numBytesRead < 0) {
             if (kDebugProc) {
-                ALOGW("Unable to open process file: %s fd=%d\n", file8, fd.get());
+                ALOGW("Unable to read process file err: %s file: %s fd=%d\n",
+                      strerror_r(errno, stackBuf, sizeof(stackBuf)), file8.get(), fd.get());
             }
             return JNI_FALSE;
         }
-        if (numberBytesRead < readBufferSize) {
-            break;
-        }
-        if (readBufferSize > std::numeric_limits<ssize_t>::max() / 2) {
-            if (kDebugProc) {
-                ALOGW("Proc file too big: %s fd=%d\n", file8, fd.get());
+
+        offset += numBytesRead;
+        remaining -= numBytesRead;
+
+        if (numBytesRead && !remaining) {
+            if (buf == stackBuf) {
+                heapBuf.resize(kProcReadMinHeapBufferSize);
+                static_assert(kProcReadMinHeapBufferSize > sizeof(stackBuf));
+                std::memcpy(heapBuf.data(), stackBuf, sizeof(stackBuf));
+            } else {
+                constexpr size_t MAX_READABLE_PROCFILE_SIZE = 64 << 20;
+                if (heapBuf.size() >= MAX_READABLE_PROCFILE_SIZE) {
+                    if (kDebugProc) {
+                        ALOGW("Proc file too big: %s fd=%d size=%zu\n",
+                              file8.get(), fd.get(), heapBuf.size());
+                    }
+                    return JNI_FALSE;
+                }
+                heapBuf.resize(2 * heapBuf.size());
             }
-            return JNI_FALSE;
+            buf = heapBuf.data();
+            remaining = heapBuf.size() - offset;
         }
-        readBufferSize = std::max(readBufferSize * 2,
-                                  kProcReadMinHeapBufferSize);
-        readBufferHeap.reset();  // Free address space before getting more.
-        readBufferHeap = std::make_unique<char[]>(readBufferSize);
-        if (!readBufferHeap) {
-            jniThrowException(env, "java/lang/OutOfMemoryError", NULL);
-            return JNI_FALSE;
-        }
-        readBuffer = readBufferHeap.get();
-    }
+    } while (numBytesRead != 0);
 
     // parseProcLineArray below modifies the buffer while parsing!
     return android_os_Process_parseProcLineArray(
-        env, clazz, readBuffer, 0, numberBytesRead,
-        format, outStrings, outLongs, outFloats);
+        env, clazz, buf, 0, offset, format, outStrings, outLongs, outFloats);
 }
 
 void android_os_Process_setApplicationObject(JNIEnv* env, jobject clazz,
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
index d426f12..6c72544 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.cpp
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -87,9 +87,10 @@
     IF_ALOGD() {
         ALOGD("Total number of LOAD segments %zu", programHeaders.size());
 
-        ALOGD("Size before punching holes st_blocks: %" PRIu64
-              ", st_blksize: %d, st_size: %" PRIu64 "",
-              beforePunch.st_blocks, beforePunch.st_blksize,
+        ALOGD("Size before punching holes st_blocks: %" PRIu64 ", st_blksize: %" PRIu64
+              ", st_size: %" PRIu64 "",
+              static_cast<uint64_t>(beforePunch.st_blocks),
+              static_cast<uint64_t>(beforePunch.st_blksize),
               static_cast<uint64_t>(beforePunch.st_size));
     }
 
@@ -193,9 +194,10 @@
             ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
             return false;
         }
-        ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %d, st_size: %" PRIu64
-              "",
-              afterPunch.st_blocks, afterPunch.st_blksize,
+        ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %" PRIu64
+              ", st_size: %" PRIu64 "",
+              static_cast<uint64_t>(afterPunch.st_blocks),
+              static_cast<uint64_t>(afterPunch.st_blksize),
               static_cast<uint64_t>(afterPunch.st_size));
     }
 
@@ -271,8 +273,9 @@
     uint64_t blockSize = beforePunch.st_blksize;
     IF_ALOGD() {
         ALOGD("Extra field length: %hu,  Size before punching holes st_blocks: %" PRIu64
-              ", st_blksize: %d, st_size: %" PRIu64 "",
-              extraFieldLen, beforePunch.st_blocks, beforePunch.st_blksize,
+              ", st_blksize: %" PRIu64 ", st_size: %" PRIu64 "",
+              extraFieldLen, static_cast<uint64_t>(beforePunch.st_blocks),
+              static_cast<uint64_t>(beforePunch.st_blksize),
               static_cast<uint64_t>(beforePunch.st_size));
     }
 
@@ -346,8 +349,9 @@
             return false;
         }
         ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64
-              ", st_blksize: %d, st_size: %" PRIu64 "",
-              afterPunch.st_blocks, afterPunch.st_blksize,
+              ", st_blksize: %" PRIu64 ", st_size: %" PRIu64 "",
+              static_cast<uint64_t>(afterPunch.st_blocks),
+              static_cast<uint64_t>(afterPunch.st_blksize),
               static_cast<uint64_t>(afterPunch.st_size));
     }
     return true;
diff --git a/core/proto/android/nfc/apdu_service_info.proto b/core/proto/android/nfc/apdu_service_info.proto
index fd110c4..9efdfcb 100644
--- a/core/proto/android/nfc/apdu_service_info.proto
+++ b/core/proto/android/nfc/apdu_service_info.proto
@@ -27,6 +27,20 @@
 message ApduServiceInfoProto {
     option (.android.msg_privacy).dest = DEST_EXPLICIT;
 
+    message AutoTransactMapping {
+        option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional string aid = 1;
+        optional bool should_auto_transact = 2;
+    }
+
+    message AutoTransactPattern {
+        option (.android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional string regexp_pattern = 1;
+        optional bool should_auto_transact = 2;
+    }
+
     optional .android.content.ComponentNameProto component_name = 1;
     optional string description = 2;
     optional bool on_host = 3;
@@ -35,4 +49,7 @@
     repeated AidGroupProto static_aid_groups = 6;
     repeated AidGroupProto dynamic_aid_groups = 7;
     optional string settings_activity_name = 8;
+    optional bool should_default_to_observe_mode = 9;
+    repeated AutoTransactMapping auto_transact_mapping = 10;
+    repeated AutoTransactPattern auto_transact_patterns = 11;
 }
diff --git a/core/proto/android/nfc/card_emulation.proto b/core/proto/android/nfc/card_emulation.proto
index 9c3c6d7..81da30d 100644
--- a/core/proto/android/nfc/card_emulation.proto
+++ b/core/proto/android/nfc/card_emulation.proto
@@ -59,6 +59,7 @@
     optional .android.content.ComponentNameProto foreground_requested = 5;
     optional .android.content.ComponentNameProto settings_default = 6;
     optional bool prefer_foreground = 7;
+    optional .android.content.ComponentNameProto wallet_role_holder_payment_service = 8;
 }
 
 // Debugging information for com.android.nfc.cardemulation.EnabledNfcFServices
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 48cf09a..f3cca8b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4472,8 +4472,8 @@
     </declare-styleable>
 
     <!-- Specify one or more <code>polling-loop-filter</code> elements inside a
-         <code>host-apdu-service</code> to indicate polling loop frames that
-         your service can handle. -->
+         <code>host-apdu-service</code> or <code>offhost-apdu-service</code> to indicate polling
+         loop frames that your service can handle. -->
     <!-- @FlaggedApi("android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP") -->
     <declare-styleable name="PollingLoopFilter">
         <!-- The polling loop frame. This attribute is mandatory. -->
@@ -4484,6 +4484,21 @@
         <attr name="autoTransact" format="boolean"/>
     </declare-styleable>
 
+    <!-- Specify one or more <code>polling-loop-pattern-filter</code> elements inside a
+         <code>host-apdu-service</code> or <code>offhost-apdu-service</code>  to indicate polling
+         loop frames that your service can handle. -->
+    <!-- @FlaggedApi("android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP") -->
+    <declare-styleable name="PollingLoopPatternFilter">
+        <!-- The patter to match polling loop frames to, must to be compatible with
+         {@link java.util.regex.Pattern#compile(String)} and only contain hexadecimal numbers and
+         `.`, `?` and `*` operators. This attribute is mandatory. -->
+        <attr name="name" />
+        <!-- Whether or not the system should automatically start a transaction when this polling
+         loop filter matches. If not set, default value is false. -->
+        <!-- @FlaggedApi("android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP") -->
+        <attr name="autoTransact" format="boolean"/>
+    </declare-styleable>
+
     <!-- Use <code>host-nfcf-service</code> as the root tag of the XML resource that
          describes an {@link android.nfc.cardemulation.HostNfcFService} service, which
          is referenced from its {@link android.nfc.cardemulation.HostNfcFService#SERVICE_META_DATA}
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 5ac84f1..c9851ee 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -275,3 +275,510 @@
     },
     auto_gen_config: true,
 }
+
+FLAKY_OR_IGNORED = [
+    "androidx.test.filters.FlakyTest",
+    "org.junit.Ignore",
+]
+
+test_module_config {
+    name: "FrameworksCoreTests_Presubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_inputmethod",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.inputmethod",
+        "android.view.inputmethod",
+    ],
+    exclude_annotations: ["androidx.test.filters.FlakyTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_context",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.ContextTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_keyguard_manager",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.app.KeyguardManagerTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_property_invalidated_cache",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.app.PropertyInvalidatedCacheTests"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "android.content.ContextTest",
+        "android.content.ComponentCallbacksControllerTest",
+        "android.content.ContextWrapperTest",
+    ],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_sqlite",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.database.sqlite.SQLiteRawStatementTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_net",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.net"],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_battery_stats",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.os.BatteryStatsTests"],
+    exclude_annotations: ["com.android.internal.os.SkipPresubmit"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_environment",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.os.EnvironmentTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_util_data_charset",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.util.FastDataTest",
+        "android.util.CharsetUtilsTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_xml",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "android.util.XmlTest",
+        "android.util.BinaryXmlTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_util_apk",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.util.apk.SourceStampVerifierTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_textclassifier",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.view.textclassifier"],
+    exclude_annotations: ["androidx.test.filters.FlakyTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_app",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.app."],
+    exclude_filters: [
+        "com.android.internal.app.WindowDecorActionBarTest",
+        "com.android.internal.app.IntentForwarderActivityTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_content",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.content."],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_infra",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.infra."],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_jank",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.jank"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_os_binder",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.os.BinderDeathDispatcherTest"],
+    exclude_annotations: ["com.android.internal.os.SkipPresubmit"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_os_kernel",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.os.KernelCpuUidClusterTimeReaderTest",
+        "com.android.internal.os.KernelCpuUidBpfMapReaderTest",
+        "com.android.internal.os.KernelCpuUidActiveTimeReaderTest",
+        "com.android.internal.os.KernelCpuUidFreqTimeReaderTest",
+        "com.android.internal.os.KernelSingleUidTimeReaderTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_server_power",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.server.power.stats.BstatsCpuTimesValidationTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_security",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.security."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_internal_util_latency_tracker",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.util.LatencyTrackerTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_content_capture_options",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.ContentCaptureOptionsTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_integrity",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.integrity."],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_pm_PreSubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.pm."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_pm_PostSubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.pm."],
+    include_annotations: ["android.platform.test.annotations.Postsubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_res",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.res."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: [
+        "androidx.test.filters.FlakyTest",
+        "android.platform.test.annotations.Postsubmit",
+        "org.junit.Ignore",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_content_res_PostSubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.content.res."],
+    include_annotations: ["android.platform.test.annotations.Postsubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_service",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "android.service.euicc",
+        "android.service.notification",
+        "android.service.quicksettings",
+        "android.service.settings.suggestions",
+        "android.service.controls.templates",
+        "android.service.controls.actions",
+        "android.service.controls",
+    ],
+    exclude_annotations: ["org.junit.Ignore"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_view_contentcapture",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.view.contentcapture"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_android_view_contentprotection",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.view.contentprotection"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_com_android_internal_content_Presubmit",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.content."],
+    include_annotations: ["android.platform.test.annotations.Presubmit"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_drawable",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.graphics.drawable.IconTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_accessibility_NO_FLAKES",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.accessibility",
+        "android.accessibilityservice",
+        "android.view.accessibility",
+    ],
+    exclude_annotations: ["androidx.test.filters.FlakyTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_accessibility",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.accessibility",
+        "android.accessibilityservice",
+        "android.view.accessibility",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_usage",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.app.usage"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_fastdata",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["com.android.internal.util.FastDataTest"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_hardware_input",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: ["android.hardware.input"],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_view_verified",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "android.view.VerifiedMotionEventTest",
+        "android.view.VerifiedKeyEventTest",
+    ],
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_jank",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_filters: [
+        "com.android.internal.jank.FrameTrackerTest",
+        "com.android.internal.jank.InteractionJankMonitorTest",
+        "com.android.internal.util.LatencyTrackerTest",
+    ],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
+
+test_module_config {
+    name: "FrameworksCoreTests_Platinum",
+    base: "FrameworksCoreTests",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
+    include_annotations: ["android.platform.test.annotations.PlatinumTest"],
+    exclude_annotations: FLAKY_OR_IGNORED,
+}
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
index b13bcd1..444ed51 100644
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ b/core/tests/coretests/src/android/net/NetworkKeyTest.java
@@ -25,7 +25,7 @@
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
index 3e45a79..46f22ce 100644
--- a/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
+++ b/core/tests/coretests/src/android/net/NetworkRecommendationProviderTest.java
@@ -26,7 +26,7 @@
 import android.Manifest.permission;
 import android.content.Context;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Before;
 import org.junit.Test;
diff --git a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
index bc12e72..7413ede 100644
--- a/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
+++ b/core/tests/coretests/src/android/net/SSLCertificateSocketFactoryTest.java
@@ -19,7 +19,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
index d984d86..63eeaa1 100644
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
@@ -26,7 +26,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/SntpClientTest.java b/core/tests/coretests/src/android/net/SntpClientTest.java
index 267fc2b..024d614 100644
--- a/core/tests/coretests/src/android/net/SntpClientTest.java
+++ b/core/tests/coretests/src/android/net/SntpClientTest.java
@@ -29,7 +29,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.Log;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import libcore.util.HexEncoding;
 
diff --git a/core/tests/coretests/src/android/net/sntp/Duration64Test.java b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
index b228596..b177e18 100644
--- a/core/tests/coretests/src/android/net/sntp/Duration64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Duration64Test.java
@@ -23,7 +23,7 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
index 200c80e..9f95132 100644
--- a/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
+++ b/core/tests/coretests/src/android/net/sntp/Timestamp64Test.java
@@ -23,7 +23,7 @@
 
 import android.platform.test.annotations.Presubmit;
 
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/com/android/internal/jank/CujTest.java b/core/tests/coretests/src/com/android/internal/jank/CujTest.java
index bf35ed0..2362a4c 100644
--- a/core/tests/coretests/src/com/android/internal/jank/CujTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/CujTest.java
@@ -35,7 +35,6 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -47,26 +46,30 @@
 public class CujTest {
     private static final String ENUM_NAME_PREFIX =
             "UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__";
-    private static final Set<String> DEPRECATED_VALUES = new HashSet<>() {
-        {
-            add(ENUM_NAME_PREFIX + "IME_INSETS_ANIMATION");
-        }
-    };
-    private static final Map<Integer, String> ENUM_NAME_EXCEPTION_MAP = new HashMap<>() {
-        {
-            put(Cuj.CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD"));
-            put(Cuj.CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR"));
-            put(Cuj.CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH"));
-            put(Cuj.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR, getEnumName("SHADE_HEADS_UP_DISAPPEAR"));
-            put(Cuj.CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE"));
-            put(Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, getEnumName("NOTIFICATION_SHADE_SWIPE"));
-            put(Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE, getEnumName("SHADE_QS_EXPAND_COLLAPSE"));
-            put(Cuj.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE, getEnumName("SHADE_QS_SCROLL_SWIPE"));
-            put(Cuj.CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND"));
-            put(Cuj.CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE"));
-            put(Cuj.CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING"));
-        }
-    };
+    private static final Set<String> DEPRECATED_VALUES = Set.of(
+            ENUM_NAME_PREFIX + "IME_INSETS_ANIMATION"
+    );
+    private static final Map<Integer, String> ENUM_NAME_EXCEPTION_MAP = Map.ofEntries(
+            Map.entry(Cuj.CUJ_NOTIFICATION_ADD, getEnumName("SHADE_NOTIFICATION_ADD")),
+            Map.entry(Cuj.CUJ_NOTIFICATION_HEADS_UP_APPEAR, getEnumName("SHADE_HEADS_UP_APPEAR")),
+            Map.entry(Cuj.CUJ_NOTIFICATION_APP_START, getEnumName("SHADE_APP_LAUNCH")),
+            Map.entry(
+                    Cuj.CUJ_NOTIFICATION_HEADS_UP_DISAPPEAR,
+                    getEnumName("SHADE_HEADS_UP_DISAPPEAR")),
+            Map.entry(Cuj.CUJ_NOTIFICATION_REMOVE, getEnumName("SHADE_NOTIFICATION_REMOVE")),
+            Map.entry(
+                    Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+                    getEnumName("NOTIFICATION_SHADE_SWIPE")),
+            Map.entry(
+                        Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+                        getEnumName("SHADE_QS_EXPAND_COLLAPSE")),
+            Map.entry(
+                    Cuj.CUJ_NOTIFICATION_SHADE_QS_SCROLL_SWIPE,
+                    getEnumName("SHADE_QS_SCROLL_SWIPE")),
+            Map.entry(Cuj.CUJ_NOTIFICATION_SHADE_ROW_EXPAND, getEnumName("SHADE_ROW_EXPAND")),
+            Map.entry(Cuj.CUJ_NOTIFICATION_SHADE_ROW_SWIPE, getEnumName("SHADE_ROW_SWIPE")),
+            Map.entry(Cuj.CUJ_NOTIFICATION_SHADE_SCROLL_FLING, getEnumName("SHADE_SCROLL_FLING"))
+    );
 
     @Rule
     public final Expect mExpect = Expect.create();
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 0f12438..97f99e9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -518,6 +518,7 @@
         <permission name="android.permission.RENOUNCE_PERMISSIONS" />
         <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
         <permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
+        <permission name="android.permission.READ_DROPBOX_DATA" />
         <permission name="android.permission.READ_LOGS" />
         <permission name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
         <permission name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/data/keyboards/Vendor_054c_Product_05c4.idc b/data/keyboards/Vendor_054c_Product_05c4.idc
index 2da6227..45b5207 100644
--- a/data/keyboards/Vendor_054c_Product_05c4.idc
+++ b/data/keyboards/Vendor_054c_Product_05c4.idc
@@ -51,7 +51,7 @@
 # fingers, it prevents tapping to click because it thinks the finger's moving
 # too fast.
 #
-# Since this touchpad doesn't seem to have to drumroll issues, we can safely
+# Since this touchpad doesn't seem to have drumroll issues, we can safely
 # disable drumroll detection.
 gestureProp.Drumroll_Suppression_Enable = 0
 
@@ -60,3 +60,11 @@
 # from the palm classifier to increase the usable area of the pad.
 gestureProp.Palm_Edge_Zone_Width = 0
 gestureProp.Tap_Exclusion_Border_Width = 0
+
+# Touchpad is small, scale up the pointer movements to make it more practical
+# to use.
+gestureProp.Point_X_Out_Scale = 2.5
+gestureProp.Point_Y_Out_Scale = 2.5
+
+# TODO(b/351326684): Ideally "Scroll X Out Scale" and "Scroll Y Out Scale"
+# should be adjusted as well. Currently not supported in IDC files.
diff --git a/data/keyboards/Vendor_054c_Product_09cc.idc b/data/keyboards/Vendor_054c_Product_09cc.idc
index 2a1a4fc..45b5207 100644
--- a/data/keyboards/Vendor_054c_Product_09cc.idc
+++ b/data/keyboards/Vendor_054c_Product_09cc.idc
@@ -60,3 +60,11 @@
 # from the palm classifier to increase the usable area of the pad.
 gestureProp.Palm_Edge_Zone_Width = 0
 gestureProp.Tap_Exclusion_Border_Width = 0
+
+# Touchpad is small, scale up the pointer movements to make it more practical
+# to use.
+gestureProp.Point_X_Out_Scale = 2.5
+gestureProp.Point_Y_Out_Scale = 2.5
+
+# TODO(b/351326684): Ideally "Scroll X Out Scale" and "Scroll Y Out Scale"
+# should be adjusted as well. Currently not supported in IDC files.
diff --git a/data/keyboards/Vendor_054c_Product_0ce6.idc b/data/keyboards/Vendor_054c_Product_0ce6.idc
new file mode 100644
index 0000000..48027e7
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0ce6.idc
@@ -0,0 +1,31 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualSense 5 Controller
+#
+
+## Touchpad ##
+
+# Because of the way this touchpad is positioned, touches around the edges are
+# no more likely to be palms than ones in the middle, so remove the edge zones
+# from the palm classifier to increase the usable area of the pad.
+gestureProp.Palm_Edge_Zone_Width = 0
+gestureProp.Tap_Exclusion_Border_Width = 0
+
+gestureProp.Point_X_Out_Scale = 2.0
+gestureProp.Point_Y_Out_Scale = 2.0
+
+# TODO(b/351326684): Ideally "Scroll X Out Scale" and "Scroll Y Out Scale"
+# should be adjusted as well. Currently not supported in IDC files.
diff --git a/data/keyboards/Vendor_054c_Product_0df2.idc b/data/keyboards/Vendor_054c_Product_0df2.idc
new file mode 100644
index 0000000..4bcf0be
--- /dev/null
+++ b/data/keyboards/Vendor_054c_Product_0df2.idc
@@ -0,0 +1,31 @@
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Sony Playstation(R) DualSense Edge 5 Controller
+#
+
+## Touchpad ##
+
+# Because of the way this touchpad is positioned, touches around the edges are
+# no more likely to be palms than ones in the middle, so remove the edge zones
+# from the palm classifier to increase the usable area of the pad.
+gestureProp.Palm_Edge_Zone_Width = 0
+gestureProp.Tap_Exclusion_Border_Width = 0
+
+gestureProp.Point_X_Out_Scale = 2.0
+gestureProp.Point_Y_Out_Scale = 2.0
+
+# TODO(b/351326684): Ideally "Scroll X Out Scale" and "Scroll Y Out Scale"
+# should be adjusted as well. Currently not supported in IDC files.
diff --git a/libs/WindowManager/Shell/OWNERS b/libs/WindowManager/Shell/OWNERS
index cb422ea..2e19d52 100644
--- a/libs/WindowManager/Shell/OWNERS
+++ b/libs/WindowManager/Shell/OWNERS
@@ -1,5 +1,5 @@
 [email protected]
 
 # Give submodule owners in shell resource approval
-per-file res*/*/*.xml = [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
+per-file res*/*/*.xml = [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
 per-file res*/*/tv_*.xml = [email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/OWNERS
new file mode 100644
index 0000000..1875675
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/OWNERS
@@ -0,0 +1,4 @@
+# WM shell sub-module compat ui owners
[email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
index 7ad68aa..afdda8f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
@@ -2,7 +2,9 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
 [email protected]
 [email protected]
 [email protected]
[email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
index 8a0eea0..93351c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/OWNERS
@@ -5,3 +5,7 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index 2a0a28e..fdec969 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -13,3 +13,6 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
[email protected]
[email protected]
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 755332f..f43a4f79 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -110,6 +110,8 @@
 bool Properties::clipSurfaceViews = false;
 bool Properties::hdr10bitPlus = false;
 
+int Properties::timeoutMultiplier = 1;
+
 StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
 
 DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
@@ -183,6 +185,8 @@
             base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews());
     hdr10bitPlus = hwui_flags::hdr_10bit_plus();
 
+    timeoutMultiplier = android::base::GetIntProperty("ro.hw_timeout_multiplier", 1);
+
     return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw);
 }
 
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index ec53070..f464043 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -338,6 +338,8 @@
     static bool clipSurfaceViews;
     static bool hdr10bitPlus;
 
+    static int timeoutMultiplier;
+
     static StretchEffectBehavior getStretchEffectBehavior() {
         return stretchEffectBehavior;
     }
diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp
index afe4c38..2f15722 100644
--- a/libs/hwui/Readback.cpp
+++ b/libs/hwui/Readback.cpp
@@ -91,8 +91,10 @@
 
     {
         ATRACE_NAME("sync_wait");
-        if (sourceFence != -1 && sync_wait(sourceFence.get(), 500 /* ms */) != NO_ERROR) {
-            ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
+        int syncWaitTimeoutMs = 500 * Properties::timeoutMultiplier;
+        if (sourceFence != -1 && sync_wait(sourceFence.get(), syncWaitTimeoutMs) != NO_ERROR) {
+            ALOGE("Timeout (%dms) exceeded waiting for buffer fence, abandoning readback attempt",
+                  syncWaitTimeoutMs);
             return request->onCopyFinished(CopyResult::Timeout);
         }
     }
@@ -109,9 +111,8 @@
 
     sk_sp<SkColorSpace> colorSpace =
             DataSpaceToColorSpace(static_cast<android_dataspace>(dataspace));
-    sk_sp<SkImage> image =
-            SkImages::DeferredFromAHardwareBuffer(sourceBuffer.get(), kPremul_SkAlphaType, 
-                                                  colorSpace);
+    sk_sp<SkImage> image = SkImages::DeferredFromAHardwareBuffer(sourceBuffer.get(),
+                                                                 kPremul_SkAlphaType, colorSpace);
 
     if (!image.get()) {
         return request->onCopyFinished(CopyResult::UnknownError);
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 0d5af50..8b6194f 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -27,6 +27,7 @@
 import android.telephony.emergency.EmergencyNumber;
 import android.util.Log;
 
+import com.android.internal.annotations.KeepForWeakReference;
 import com.android.internal.telephony.flags.Flags;
 
 import java.util.concurrent.TimeUnit;
@@ -94,6 +95,7 @@
 
     // The internal implementation of TelephonyManager uses WeakReference so we have to keep a
     // reference here.
+    @KeepForWeakReference
     private final EmergencyCallListener mEmergencyCallListener = new EmergencyCallListener();
 
     private final EmergencyCallCallback mEmergencyCallCallback;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 8acaf3be..e575dae 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -5263,6 +5263,8 @@
      *           main thread.)
      */
     public void setCallback(@Nullable /* MediaCodec. */ Callback cb, @Nullable Handler handler) {
+        boolean setCallbackStallFlag =
+            GetFlag(() -> android.media.codec.Flags.setCallbackStall());
         if (cb != null) {
             synchronized (mListenerLock) {
                 EventHandler newHandler = getEventHandlerOn(handler, mCallbackHandler);
@@ -5270,7 +5272,7 @@
                 // even if we were to extend this to be callable dynamically, it must
                 // be called when codec is flushed, so no messages are pending.
                 if (newHandler != mCallbackHandler) {
-                    if (android.media.codec.Flags.setCallbackStall()) {
+                    if (setCallbackStallFlag) {
                         logAndRun(
                                 "[new handler] removeMessages(SET_CALLBACK)",
                                 () -> {
@@ -5289,7 +5291,7 @@
                 }
             }
         } else if (mCallbackHandler != null) {
-            if (android.media.codec.Flags.setCallbackStall()) {
+            if (setCallbackStallFlag) {
                 logAndRun(
                         "[null handler] removeMessages(SET_CALLBACK)",
                         () -> {
diff --git a/media/jni/JetPlayer.h b/media/jni/JetPlayer.h
index bb569bc..4cc266d 100644
--- a/media/jni/JetPlayer.h
+++ b/media/jni/JetPlayer.h
@@ -40,7 +40,7 @@
     static const int JET_NUMQUEUEDSEGMENT_UPDATE = 3;
     static const int JET_PAUSE_UPDATE            = 4;
 
-    JetPlayer(void *javaJetPlayer,
+    explicit JetPlayer(void *javaJetPlayer,
             int maxTracks = 32,
             int trackBufferSize = 1200);
     ~JetPlayer();
@@ -69,7 +69,6 @@
     void                fireUpdateOnStatusChange();
     void                fireEventsFromJetQueue();
 
-    JetPlayer() {} // no default constructor
     void dump();
     void dumpJetStatus(S_JET_STATUS* pJetStatus);
 
@@ -96,7 +95,7 @@
 
     class JetPlayerThread : public Thread {
     public:
-        JetPlayerThread(JetPlayer *player) : mPlayer(player) {
+        explicit JetPlayerThread(JetPlayer *player) : mPlayer(player) {
         }
 
     protected:
@@ -106,8 +105,7 @@
         JetPlayer *mPlayer;
 
         bool threadLoop() {
-            int result;
-            result = mPlayer->render();
+            mPlayer->render();
             return false;
         }
 
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index 8f16f76..0fb3049 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -127,3 +127,30 @@
         "-DPNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR",
     ],
 }
+
+cc_fuzz {
+    name: "imagedecoder_heif_fuzzer",
+    defaults: ["imagedecoder_fuzzer_defaults"],
+    team: "trendy_team_android_core_graphics_stack",
+    shared_libs: [
+        "libfakeservicemanager",
+    ],
+    target: {
+        android: {
+            shared_libs: [
+                "libmediaplayerservice",
+                "libmediaextractorservice",
+            ],
+        },
+        host: {
+            static_libs: [
+                "libbinder_random_parcel",
+                "libcutils",
+            ],
+        },
+    },
+    include_dirs: ["frameworks/av/services/mediaextractor"],
+    cflags: [
+        "-DFUZZ_HEIF_FORMAT",
+    ],
+}
diff --git a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
index 6743997..f739e4a 100644
--- a/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
+++ b/native/graphics/jni/fuzz/fuzz_imagedecoder.cpp
@@ -18,6 +18,16 @@
 #include <binder/IPCThreadState.h>
 #include <fuzzer/FuzzedDataProvider.h>
 
+#ifdef FUZZ_HEIF_FORMAT
+#include <fakeservicemanager/FakeServiceManager.h>
+#ifdef __ANDROID__
+#include <MediaExtractorService.h>
+#include <MediaPlayerService.h>
+#else
+#include <fuzzbinder/random_binder.h>
+#endif //__ANDROID__
+#endif // FUZZ_HEIF_FORMAT
+
 #ifdef PNG_MUTATOR_DEFINE_LIBFUZZER_CUSTOM_MUTATOR
 #include <fuzz/png_mutator.h>
 #endif
@@ -31,8 +41,42 @@
 
 using PixelPointer = std::unique_ptr<void, PixelFreer>;
 
+#ifndef FUZZ_HEIF_FORMAT
+#define FOURCC(c1, c2, c3, c4) ((c1) << 24 | (c2) << 16 | (c3) << 8 | (c4))
+/** Reverse all 4 bytes in a 32bit value.
+    e.g. 0x12345678 -> 0x78563412
+*/
+static uint32_t endianSwap32(uint32_t value) {
+    return ((value & 0xFF) << 24) | ((value & 0xFF00) << 8) | ((value & 0xFF0000) >> 8) |
+            (value >> 24);
+}
+
+static bool isFtyp(const uint8_t* data, size_t size) {
+    constexpr int32_t headerSize = 8;
+    constexpr int32_t chunkTypeOffset = 4;
+    constexpr int32_t ftypFourCCVal = FOURCC('f', 't', 'y', 'p');
+    if (size >= headerSize) {
+        const uint32_t* chunk = reinterpret_cast<const uint32_t*>(data + chunkTypeOffset);
+        if (endianSwap32(*chunk) == ftypFourCCVal) {
+            return true;
+        }
+    }
+    return false;
+}
+#endif
+
 AImageDecoder* init(const uint8_t* data, size_t size, bool useFileDescriptor) {
     AImageDecoder* decoder = nullptr;
+#ifndef FUZZ_HEIF_FORMAT
+    if (isFtyp(data, size)) {
+        /* We want to ignore HEIF data when fuzzing non-HEIF image decoders. Use 'FTYP'
+         * as a signal to ignore, though note that this excludes more than just HEIF.
+         * But when this code was added, `AImageDecoder` did not support any formats
+         * in 'FTYP' besides HEIF.
+         */
+        return nullptr;
+    }
+#endif // FUZZ_HEIF_FORMAT
     if (useFileDescriptor) {
         constexpr char testFd[] = "tempFd";
         int32_t fileDesc = open(testFd, O_RDWR | O_CREAT | O_TRUNC);
@@ -47,6 +91,27 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
     FuzzedDataProvider dataProvider = FuzzedDataProvider(data, size);
+#ifdef FUZZ_HEIF_FORMAT
+    /**
+     * For image formats like HEIF, a new metadata object is
+     * created which requires "media.player" service running
+     */
+    static std::once_flag callOnceHEIF;
+    std::call_once(callOnceHEIF, [&]() {
+        android::sp<android::IServiceManager> fakeServiceManager =
+                new android::FakeServiceManager();
+        setDefaultServiceManager(fakeServiceManager);
+#ifdef __ANDROID__
+        android::MediaPlayerService::instantiate();
+        android::MediaExtractorService::instantiate();
+#else
+        auto binderExtractor = android::getRandomBinder(&dataProvider);
+        auto binderPlayer = android::getRandomBinder(&dataProvider);
+        fakeServiceManager->addService(android::String16("media.extractor"), binderExtractor);
+        fakeServiceManager->addService(android::String16("media.player"), binderPlayer);
+#endif //__ANDROID__
+    });
+#endif // FUZZ_HEIF_FORMAT
     /**
      * Use maximum of 80% of buffer for creating decoder and save at least
      * 20% buffer for fuzzing other APIs
diff --git a/nfc/Android.bp b/nfc/Android.bp
index 2a01b3f..0282e6f 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -38,6 +38,8 @@
     name: "framework-nfc",
     libs: [
         "unsupportedappusage", // for android.compat.annotation.UnsupportedAppUsage
+        "framework-permission-s",
+        "framework-permission",
     ],
     static_libs: [
         "android.nfc.flags-aconfig-java",
@@ -49,7 +51,7 @@
     ],
     defaults: ["framework-module-defaults"],
     sdk_version: "module_current",
-    min_sdk_version: "34", // should be 35 (making it 34 for compiling for `-next`)
+    min_sdk_version: "current",
     installable: true,
     optimize: {
         enabled: false,
@@ -61,6 +63,7 @@
     ],
     impl_library_visibility: [
         "//frameworks/base:__subpackages__",
+        "//cts/hostsidetests/multidevices/nfc:__subpackages__",
         "//cts/tests/tests/nfc",
         "//packages/apps/Nfc:__subpackages__",
     ],
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 9d0221a..cf7aea4 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -64,8 +64,10 @@
   }
 
   public final class NfcAdapter {
+    method @FlaggedApi("android.nfc.nfc_state_change") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable();
     method public void disableForegroundDispatch(android.app.Activity);
     method public void disableReaderMode(android.app.Activity);
+    method @FlaggedApi("android.nfc.nfc_state_change") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
     method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], String[][]);
     method public void enableReaderMode(android.app.Activity, android.nfc.NfcAdapter.ReaderCallback, int, android.os.Bundle);
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
@@ -205,7 +207,10 @@
     method public boolean isDefaultServiceForCategory(android.content.ComponentName, String);
     method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>);
     method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
+    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopPatternFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean);
     method public boolean removeAidsForService(android.content.ComponentName, String);
+    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean removePollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String);
+    method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean removePollingLoopPatternFilterForService(@NonNull android.content.ComponentName, @NonNull String);
     method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);
     method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
     method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setShouldDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean);
@@ -265,12 +270,12 @@
   }
 
   @FlaggedApi("android.nfc.nfc_read_polling_loop") public final class PollingFrame implements android.os.Parcelable {
-    ctor public PollingFrame(int, @Nullable byte[], int, int);
     method public int describeContents();
     method @NonNull public byte[] getData();
-    method public int getGain();
-    method public int getTimestamp();
+    method public long getTimestamp();
+    method public boolean getTriggeredAutoTransact();
     method public int getType();
+    method public int getVendorSpecificGain();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.nfc.cardemulation.PollingFrame> CREATOR;
     field @FlaggedApi("android.nfc.nfc_read_polling_loop") public static final int POLLING_LOOP_TYPE_A = 65; // 0x41
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index 310130e..3375e18c 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -3,9 +3,7 @@
 
   public final class NfcAdapter {
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean addNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler, String[]);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable();
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disable(boolean);
-    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable();
     method @FlaggedApi("android.nfc.enable_nfc_reader_option") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableReaderOption(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean);
     method @FlaggedApi("android.nfc.enable_nfc_mainline") public int getAdapterState();
@@ -29,6 +27,7 @@
     field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
     field @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.SHOW_CUSTOMIZED_RESOLVER) public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER";
     field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS";
+    field @FlaggedApi("android.nfc.nfc_set_default_disc_tech") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final int FLAG_SET_DEFAULT_TECH = 1073741824; // 0x40000000
     field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int MESSAGE_TYPE_COMMAND = 1; // 0x1
     field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_FAILED = 3; // 0x3
     field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_MESSAGE_CORRUPTED = 2; // 0x2
@@ -58,7 +57,9 @@
 
   @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension {
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference();
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback);
+    method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState();
     method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback);
   }
 
diff --git a/nfc/java/android/nfc/AvailableNfcAntenna.java b/nfc/java/android/nfc/AvailableNfcAntenna.java
index 6e6512a..e76aeb0 100644
--- a/nfc/java/android/nfc/AvailableNfcAntenna.java
+++ b/nfc/java/android/nfc/AvailableNfcAntenna.java
@@ -28,13 +28,13 @@
 public final class AvailableNfcAntenna implements Parcelable {
     /**
      * Location of the antenna on the Y axis in millimeters.
-     * 0 is the bottom-left when the user is facing the screen
+     * 0 is the top-left when the user is facing the screen
      * and the device orientation is Portrait.
      */
     private final int mLocationX;
     /**
      * Location of the antenna on the Y axis in millimeters.
-     * 0 is the bottom-left when the user is facing the screen
+     * 0 is the top-left when the user is facing the screen
      * and the device orientation is Portrait.
      */
     private final int mLocationY;
@@ -46,7 +46,7 @@
 
     /**
      * Location of the antenna on the X axis in millimeters.
-     * 0 is the bottom-left when the user is facing the screen
+     * 0 is the top-left when the user is facing the screen
      * and the device orientation is Portrait.
      */
     public int getLocationX() {
@@ -55,7 +55,7 @@
 
     /**
      * Location of the antenna on the Y axis in millimeters.
-     * 0 is the bottom-left when the user is facing the screen
+     * 0 is the top-left when the user is facing the screen
      * and the device orientation is Portrait.
      */
     public int getLocationY() {
diff --git a/nfc/java/android/nfc/Constants.java b/nfc/java/android/nfc/Constants.java
index f768330..9b11e2d 100644
--- a/nfc/java/android/nfc/Constants.java
+++ b/nfc/java/android/nfc/Constants.java
@@ -16,6 +16,8 @@
 
 package android.nfc;
 
+import android.provider.Settings;
+
 /**
  * @hide
  * TODO(b/303286040): Holds @hide API constants. Formalize these APIs.
@@ -26,4 +28,15 @@
     public static final String SETTINGS_SECURE_NFC_PAYMENT_FOREGROUND = "nfc_payment_foreground";
     public static final String SETTINGS_SECURE_NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
     public static final String FEATURE_NFC_ANY = "android.hardware.nfc.any";
+
+    /**
+     * @hide constant copied from {@link Settings.Global}
+     * TODO(b/274636414): Migrate to official API in Android V.
+     */
+    public static final String SETTINGS_SATELLITE_MODE_RADIOS = "satellite_mode_radios";
+    /**
+     * @hide constant copied from {@link Settings.Global}
+     * TODO(b/274636414): Migrate to official API in Android V.
+     */
+    public static final String SETTINGS_SATELLITE_MODE_ENABLED = "satellite_mode_enabled";
 }
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index e151a0e..90ca92f 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -35,6 +35,7 @@
 import android.nfc.INfcWlcStateListener;
 import android.nfc.NfcAntennaInfo;
 import android.nfc.WlcListenerDeviceInfo;
+import android.nfc.cardemulation.PollingFrame;
 import android.os.Bundle;
 
 /**
@@ -48,8 +49,8 @@
     INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
     INfcDta getNfcDtaInterface(in String pkg);
     int getState();
-    boolean disable(boolean saveState);
-    boolean enable();
+    boolean disable(boolean saveState, in String pkg);
+    boolean enable(in String pkg);
     void pausePolling(int timeoutInMs);
     void resumePolling();
 
@@ -90,7 +91,7 @@
     boolean enableReaderOption(boolean enable);
     boolean isObserveModeSupported();
     boolean isObserveModeEnabled();
-    boolean setObserveMode(boolean enabled);
+    boolean setObserveMode(boolean enabled, String pkg);
 
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)")
     boolean setWlcEnabled(boolean enable);
@@ -101,12 +102,15 @@
 
     void updateDiscoveryTechnology(IBinder b, int pollFlags, int listenFlags);
 
-    void notifyPollingLoop(in Bundle frame);
+    void notifyPollingLoop(in PollingFrame frame);
     void notifyHceDeactivated();
+    void notifyTestHceData(in int technology, in byte[] data);
     int sendVendorNciMessage(int mt, int gid, int oid, in byte[] payload);
     void registerVendorExtensionCallback(in INfcVendorNciCallback callbacks);
     void unregisterVendorExtensionCallback(in INfcVendorNciCallback callbacks);
     void registerOemExtensionCallback(INfcOemExtensionCallback callbacks);
     void unregisterOemExtensionCallback(INfcOemExtensionCallback callbacks);
     void clearPreference();
+    void setScreenState();
+    void checkFirmware();
 }
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 85a07b7..cb97f23 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -33,10 +33,13 @@
     boolean setShouldDefaultToObserveModeForService(int userId, in android.content.ComponentName service, boolean enable);
     boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
     boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter, boolean autoTransact);
+    boolean registerPollingLoopPatternFilterForService(int userHandle, in ComponentName service, in String pollingLoopPatternFilter, boolean autoTransact);
     boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
     boolean unsetOffHostForService(int userHandle, in ComponentName service);
     AidGroup getAidGroupForService(int userHandle, in ComponentName service, String category);
     boolean removeAidGroupForService(int userHandle, in ComponentName service, String category);
+    boolean removePollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter);
+    boolean removePollingLoopPatternFilterForService(int userHandle, in ComponentName service, in String pollingLoopPatternFilter);
     List<ApduServiceInfo> getServices(int userHandle, in String category);
     boolean setPreferredService(in ComponentName service);
     boolean unsetPreferredService();
diff --git a/nfc/java/android/nfc/NfcActivityManager.java b/nfc/java/android/nfc/NfcActivityManager.java
index 0e40db6..0eb846d 100644
--- a/nfc/java/android/nfc/NfcActivityManager.java
+++ b/nfc/java/android/nfc/NfcActivityManager.java
@@ -236,11 +236,7 @@
 
     public void setReaderMode(Binder token, int flags, Bundle extras) {
         if (DBG) Log.d(TAG, "Setting reader mode");
-        try {
-            NfcAdapter.sService.setReaderMode(token, this, flags, extras);
-        } catch (RemoteException e) {
-            mAdapter.attemptDeadServiceRecovery(e);
-        }
+        NfcAdapter.callService(() -> NfcAdapter.sService.setReaderMode(token, this, flags, extras));
     }
 
     /**
@@ -248,19 +244,11 @@
      * Makes IPC call - do not hold lock.
      */
     void requestNfcServiceCallback() {
-        try {
-            NfcAdapter.sService.setAppCallback(this);
-        } catch (RemoteException e) {
-            mAdapter.attemptDeadServiceRecovery(e);
-        }
+        NfcAdapter.callService(() -> NfcAdapter.sService.setAppCallback(this));
     }
 
     void verifyNfcPermission() {
-        try {
-            NfcAdapter.sService.verifyNfcPermission();
-        } catch (RemoteException e) {
-            mAdapter.attemptDeadServiceRecovery(e);
-        }
+        NfcAdapter.callService(() -> NfcAdapter.sService.verifyNfcPermission());
     }
 
     @Override
@@ -406,11 +394,8 @@
     }
 
     private void changeDiscoveryTech(Binder token, int pollTech, int listenTech) {
-        try {
-            NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech);
-        } catch (RemoteException e) {
-            mAdapter.attemptDeadServiceRecovery(e);
-        }
+        NfcAdapter.callService(
+            () -> NfcAdapter.sService.updateDiscoveryTechnology(token, pollTech, listenTech));
     }
 
 }
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index fe78c9b..b36b705 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -340,7 +340,8 @@
     public static final int FLAG_READER_NFC_BARCODE = 0x10;
 
     /** @hide */
-    @IntDef(flag = true, prefix = {"FLAG_READER_"}, value = {
+    @IntDef(flag = true, value = {
+        FLAG_SET_DEFAULT_TECH,
         FLAG_READER_KEEP,
         FLAG_READER_DISABLE,
         FLAG_READER_NFC_A,
@@ -438,7 +439,8 @@
     public static final int FLAG_USE_ALL_TECH = 0xff;
 
     /** @hide */
-    @IntDef(flag = true, prefix = {"FLAG_LISTEN_"}, value = {
+    @IntDef(flag = true, value = {
+        FLAG_SET_DEFAULT_TECH,
         FLAG_LISTEN_KEEP,
         FLAG_LISTEN_DISABLE,
         FLAG_LISTEN_NFC_PASSIVE_A,
@@ -449,6 +451,18 @@
     public @interface ListenTechnology {}
 
     /**
+     * Flag used in {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag changes the default listen or poll tech.
+     * Only available to privileged apps.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH)
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public static final int FLAG_SET_DEFAULT_TECH = 0x40000000;
+
+    /**
      * @hide
      * @removed
      */
@@ -908,8 +922,8 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public INfcAdapter getService() {
-        isEnabled();  // NOP call to recover sService if it is stale
+    public static INfcAdapter getService() {
+        isEnabledStatic();  // NOP call to recover sService if it is stale
         return sService;
     }
 
@@ -917,8 +931,8 @@
      * Returns the binder interface to the tag service.
      * @hide
      */
-    public INfcTag getTagService() {
-        isEnabled();  // NOP call to recover sTagService if it is stale
+    public static INfcTag getTagService() {
+        isEnabledStatic();  // NOP call to recover sTagService if it is stale
         return sTagService;
     }
 
@@ -926,8 +940,8 @@
      * Returns the binder interface to the card emulation service.
      * @hide
      */
-    public INfcCardEmulation getCardEmulationService() {
-        isEnabled();
+    public static INfcCardEmulation getCardEmulationService() {
+        isEnabledStatic();
         return sCardEmulationService;
     }
 
@@ -935,8 +949,8 @@
      * Returns the binder interface to the NFC-F card emulation service.
      * @hide
      */
-    public INfcFCardEmulation getNfcFCardEmulationService() {
-        isEnabled();
+    public static INfcFCardEmulation getNfcFCardEmulationService() {
+        isEnabledStatic();
         return sNfcFCardEmulationService;
     }
 
@@ -949,22 +963,9 @@
             throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
                     + " NFC extras APIs");
         }
-        try {
-            return sService.getNfcDtaInterface(mContext.getPackageName());
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return null;
-            }
-            try {
-                return sService.getNfcDtaInterface(mContext.getPackageName());
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return null;
-        }
+        return callServiceReturn(() ->  sService.getNfcDtaInterface(mContext.getPackageName()),
+                null);
+
     }
 
     /**
@@ -972,14 +973,14 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public void attemptDeadServiceRecovery(Exception e) {
+    public static void attemptDeadServiceRecovery(RemoteException e) {
         Log.e(TAG, "NFC service dead - attempting to recover", e);
         INfcAdapter service = getServiceInterface();
         if (service == null) {
             Log.e(TAG, "could not retrieve NFC service during service recovery");
             // nothing more can be done now, sService is still stale, we'll hit
             // this recovery path again later
-            return;
+            e.rethrowAsRuntimeException();
         }
         // assigning to sService is not thread-safe, but this is best-effort code
         // and on a well-behaved system should never happen
@@ -992,7 +993,7 @@
                 Log.e(TAG, "could not retrieve NFC tag service during service recovery");
                 // nothing more can be done now, sService is still stale, we'll hit
                 // this recovery path again later
-                return;
+                ee.rethrowAsRuntimeException();
             }
         }
 
@@ -1013,24 +1014,27 @@
                         "could not retrieve NFC-F card emulation service during service recovery");
             }
         }
-
-        return;
     }
 
-    private boolean isCardEmulationEnabled() {
+    private static boolean isCardEmulationEnabled() {
         if (sHasCeFeature) {
             return (sCardEmulationService != null || sNfcFCardEmulationService != null);
         }
         return false;
     }
 
-    private boolean isTagReadingEnabled() {
+    private static boolean isTagReadingEnabled() {
         if (sHasNfcFeature) {
             return sTagService != null;
         }
         return false;
     }
 
+    private static boolean isEnabledStatic() {
+        boolean serviceState = callServiceReturn(() -> sService.getState() == STATE_ON, false);
+        return serviceState
+                && (isTagReadingEnabled() || isCardEmulationEnabled() || sHasNfcWlcFeature);
+    }
 
     /**
      * Return true if this NFC Adapter has any features enabled.
@@ -1045,24 +1049,7 @@
      * @return true if this NFC Adapter has any features enabled
      */
     public boolean isEnabled() {
-        boolean serviceState = false;
-        try {
-            serviceState = sService.getState() == STATE_ON;
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                serviceState = sService.getState() == STATE_ON;
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-        }
-        return serviceState
-                && (isTagReadingEnabled() || isCardEmulationEnabled() || sHasNfcWlcFeature);
+        return isEnabledStatic();
     }
 
     /**
@@ -1081,22 +1068,8 @@
     @SystemApi
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
     public @AdapterState int getAdapterState() {
-        try {
-            return sService.getState();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return NfcAdapter.STATE_OFF;
-            }
-            try {
-                return sService.getState();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return NfcAdapter.STATE_OFF;
-        }
+        return callServiceReturn(() ->  sService.getState(), NfcAdapter.STATE_OFF);
+
     }
 
     /**
@@ -1106,6 +1079,9 @@
      * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the
      * operation is complete.
      *
+     * <p>This API is only allowed to be called by system apps
+     * or apps which are Device Owner or Profile Owner.
+     *
      * <p>If this returns true, then either NFC is already on, or
      * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent
      * to indicate a state transition. If this returns false, then
@@ -1113,27 +1089,12 @@
      * NFC on (for example we are in airplane mode and NFC is not
      * toggleable in airplane mode on this platform).
      *
-     * @hide
      */
-    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_STATE_CHANGE)
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean enable() {
-        try {
-            return sService.enable();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.enable();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.enable(mContext.getPackageName()), false);
+
     }
 
     /**
@@ -1146,33 +1107,22 @@
      * {@link #ACTION_ADAPTER_STATE_CHANGED} broadcasts to find out when the
      * operation is complete.
      *
+     * <p>This API is only allowed to be called by system apps
+     * or apps which are Device Owner or Profile Owner.
+     *
      * <p>If this returns true, then either NFC is already off, or
      * a {@link #ACTION_ADAPTER_STATE_CHANGED} broadcast will be sent
      * to indicate a state transition. If this returns false, then
      * there is some problem that prevents an attempt to turn
      * NFC off.
      *
-     * @hide
      */
-    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_STATE_CHANGE)
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean disable() {
-        try {
-            return sService.disable(true);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.disable(true);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.disable(true, mContext.getPackageName()),
+                false);
+
     }
 
     /**
@@ -1182,22 +1132,9 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public boolean disable(boolean persist) {
-        try {
-            return sService.disable(persist);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.disable(persist);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.disable(persist, mContext.getPackageName()),
+                false);
+
     }
 
     /**
@@ -1206,29 +1143,21 @@
      * @hide
      */
     public void pausePolling(int timeoutInMs) {
-        try {
-            sService.pausePolling(timeoutInMs);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-        }
+        callService(() -> sService.pausePolling(timeoutInMs));
     }
 
 
     /**
-     * Returns whether the device supports observer mode or not. When observe
-     * mode is enabled, the NFC hardware will listen for NFC readers, but not
-     * respond to them. When observe mode is disabled, the NFC hardware will
-     * resoond to the reader and proceed with the transaction.
+     * Returns whether the device supports observe mode or not. When observe mode is enabled, the
+     * NFC hardware will listen to NFC readers, but not respond to them. While enabled, observed
+     * polling frames will be sent to the APDU service (see {@link #setObserveModeEnabled(boolean)}.
+     * When observe mode is disabled (or if it's not supported), the NFC hardware will automatically
+     * respond to the reader and proceed with the transaction.
      * @return true if the mode is supported, false otherwise.
      */
     @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
     public boolean isObserveModeSupported() {
-        try {
-            return sService.isObserveModeSupported();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return false;
-        }
+        return callServiceReturn(() ->  sService.isObserveModeSupported(), false);
     }
 
     /**
@@ -1239,18 +1168,18 @@
 
     @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
     public boolean isObserveModeEnabled() {
-        try {
-            return sService.isObserveModeEnabled();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return false;
-        }
+        return callServiceReturn(() ->  sService.isObserveModeEnabled(), false);
     }
 
     /**
      * Controls whether the NFC adapter will allow transactions to proceed or be in observe mode
      * and simply observe and notify the APDU service of polling loop frames. See
-     * {@link #isObserveModeSupported()} for a description of observe mode.
+     * {@link #isObserveModeSupported()} for a description of observe mode. Only the package of the
+     * currently preferred service (the service set as preferred by the current foreground
+     * application via {@link android.nfc.cardemulation.CardEmulation#setPreferredService(Activity,
+     * android.content.ComponentName)} or the current Default Wallet Role Holder
+     * {@link android.app.role.RoleManager#ROLE_WALLET}), otherwise a call to this method will fail
+     * and return false.
      *
      * @param enabled false disables observe mode to allow the transaction to proceed while true
      *                enables observe mode and does not allow transactions to proceed.
@@ -1260,12 +1189,12 @@
 
     @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
     public boolean setObserveModeEnabled(boolean enabled) {
-        try {
-            return sService.setObserveMode(enabled);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return false;
+        if (mContext == null) {
+            throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
+                    + " observe mode APIs");
         }
+        return callServiceReturn(() ->  sService.setObserveMode(enabled, mContext.getPackageName()),
+                false);
     }
 
     /**
@@ -1275,11 +1204,7 @@
      * @hide
      */
     public void resumePolling() {
-        try {
-            sService.resumePolling();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-        }
+        callService(() -> sService.resumePolling());
     }
 
     /**
@@ -1698,15 +1623,10 @@
         if (activity == null || intent == null) {
             throw new NullPointerException();
         }
-        try {
-            TechListParcel parcel = null;
-            if (techLists != null && techLists.length > 0) {
-                parcel = new TechListParcel(techLists);
-            }
-            sService.setForegroundDispatch(intent, filters, parcel);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-        }
+        final TechListParcel parcel = (techLists != null && techLists.length > 0)
+            ? new TechListParcel(techLists)
+            : null;
+        callService(() -> sService.setForegroundDispatch(intent, filters, parcel));
     }
 
     /**
@@ -1730,11 +1650,7 @@
                 throw new UnsupportedOperationException();
             }
         }
-        try {
-            sService.setForegroundDispatch(null, null, null);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-        }
+        callService(() -> sService.setForegroundDispatch(null, null, null));
     }
 
     /**
@@ -1815,11 +1731,7 @@
         }
         Binder token = new Binder();
         int flags = enable ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
-        try {
-            NfcAdapter.sService.setReaderMode(token, null, flags, null);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-        }
+        callService(() -> sService.setReaderMode(token, null, flags, null));
     }
 
     /**
@@ -1862,14 +1774,6 @@
     public void setDiscoveryTechnology(@NonNull Activity activity,
             @PollTechnology int pollTechnology, @ListenTechnology int listenTechnology) {
 
-        // A special treatment of the _KEEP flags
-        if ((listenTechnology & FLAG_LISTEN_KEEP) != 0) {
-            listenTechnology = -1;
-        }
-        if ((pollTechnology & FLAG_READER_KEEP) != 0) {
-            pollTechnology = -1;
-        }
-
         if (listenTechnology == FLAG_LISTEN_DISABLE) {
             synchronized (sLock) {
                 if (!sHasNfcFeature) {
@@ -1889,7 +1793,21 @@
                 }
             }
         }
-        mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology);
+    /*
+     * Privileged FLAG to set technology mask for all data processed by NFC controller
+     * Note: Use with caution! The app is responsible for ensuring that the discovery
+     * technology mask is returned to default.
+     * Note: FLAG_USE_ALL_TECH used with _KEEP flags will reset the technolody to android default
+     */
+        if (Flags.nfcSetDefaultDiscTech()
+                && ((pollTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH
+                || (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) {
+            Binder token = new Binder();
+            callService( () ->
+                sService.updateDiscoveryTechnology(token, pollTechnology, listenTechnology));
+        } else {
+            mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology);
+        }
     }
 
     /**
@@ -2021,22 +1939,8 @@
         if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.setNfcSecure(enable);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.setNfcSecure(enable);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.setNfcSecure(enable), false);
+
     }
 
     /**
@@ -2052,22 +1956,8 @@
         if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.deviceSupportsNfcSecure();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.deviceSupportsNfcSecure();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.deviceSupportsNfcSecure(), false);
+
     }
 
     /**
@@ -2085,22 +1975,8 @@
         if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.getNfcAntennaInfo();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return null;
-            }
-            try {
-                return sService.getNfcAntennaInfo();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return null;
-        }
+        return callServiceReturn(() ->  sService.getNfcAntennaInfo(), null);
+
     }
 
     /**
@@ -2118,22 +1994,8 @@
         if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.isNfcSecureEnabled();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.isNfcSecureEnabled();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.isNfcSecureEnabled(), false);
+
     }
 
     /**
@@ -2149,22 +2011,8 @@
         if (!sHasNfcFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.enableReaderOption(enable);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.enableReaderOption(enable);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.enableReaderOption(enable), false);
+
     }
 
     /**
@@ -2178,22 +2026,8 @@
         if (!sHasNfcFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.isReaderOptionSupported();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.isReaderOptionSupported();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.isReaderOptionSupported(), false);
+
     }
 
     /**
@@ -2209,22 +2043,8 @@
         if (!sHasNfcFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.isReaderOptionEnabled();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.isReaderOptionEnabled();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.isReaderOptionEnabled(), false);
+
     }
 
     /**
@@ -2352,11 +2172,9 @@
         synchronized (mLock) {
             mTagRemovedListener = iListener;
         }
-        try {
-            return sService.ignore(tag.getServiceHandle(), debounceMs, iListener);
-        } catch (RemoteException e) {
-            return false;
-        }
+        final ITagRemovedCallback.Stub passedListener = iListener;
+        return callServiceReturn(() ->
+                sService.ignore(tag.getServiceHandle(), debounceMs, passedListener), false);
     }
 
     /**
@@ -2370,11 +2188,7 @@
         if (tag == null) {
             throw new NullPointerException("tag cannot be null");
         }
-        try {
-            sService.dispatch(tag);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-        }
+        callService(() -> sService.dispatch(tag));
     }
 
     /**
@@ -2410,8 +2224,10 @@
             synchronized (mLock) {
                 if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
                     // update the tag technologies
-                    sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler));
-                    mNfcUnlockHandlers.remove(unlockHandler);
+                    callService(() -> {
+                        sService.removeNfcUnlockHandler(mNfcUnlockHandlers.get(unlockHandler));
+                        mNfcUnlockHandlers.remove(unlockHandler);
+                    });
                 }
 
                 INfcUnlockHandler.Stub iHandler = new INfcUnlockHandler.Stub() {
@@ -2420,20 +2236,18 @@
                         return unlockHandler.onUnlockAttempted(tag);
                     }
                 };
-
-                sService.addNfcUnlockHandler(iHandler,
-                        Tag.getTechCodesFromStrings(tagTechnologies));
-                mNfcUnlockHandlers.put(unlockHandler, iHandler);
+                return callServiceReturn(() -> {
+                        sService.addNfcUnlockHandler(
+                            iHandler, Tag.getTechCodesFromStrings(tagTechnologies));
+                        mNfcUnlockHandlers.put(unlockHandler, iHandler);
+                        return true;
+                    }, false);
             }
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return false;
         } catch (IllegalArgumentException e) {
             Log.e(TAG, "Unable to register LockscreenDispatch", e);
             return false;
         }
 
-        return true;
     }
 
     /**
@@ -2450,17 +2264,14 @@
                 throw new UnsupportedOperationException();
             }
         }
-        try {
-            synchronized (mLock) {
-                if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
+        synchronized (mLock) {
+            if (mNfcUnlockHandlers.containsKey(unlockHandler)) {
+                return callServiceReturn(() -> {
                     sService.removeNfcUnlockHandler(mNfcUnlockHandlers.remove(unlockHandler));
-                }
-
-                return true;
+                    return true;
+                }, false);
             }
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            return false;
+            return true;
         }
     }
 
@@ -2473,22 +2284,9 @@
             throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
                     + " NFC extras APIs");
         }
-        try {
-            return sService.getNfcAdapterExtrasInterface(mContext.getPackageName());
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return null;
-            }
-            try {
-                return sService.getNfcAdapterExtrasInterface(mContext.getPackageName());
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return null;
-        }
+        return callServiceReturn(() ->
+                sService.getNfcAdapterExtrasInterface(mContext.getPackageName()), null);
+
     }
 
     void enforceResumed(Activity activity) {
@@ -2533,22 +2331,8 @@
         if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.setControllerAlwaysOn(value);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.setControllerAlwaysOn(value);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.setControllerAlwaysOn(value), false);
+
     }
 
     /**
@@ -2564,22 +2348,8 @@
     @SystemApi
     @RequiresPermission(android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON)
     public boolean isControllerAlwaysOn() {
-        try {
-            return sService.isControllerAlwaysOn();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.isControllerAlwaysOn();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.isControllerAlwaysOn(), false);
+
     }
 
     /**
@@ -2598,22 +2368,8 @@
         if (!sHasNfcFeature && !sHasCeFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.isControllerAlwaysOnSupported();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.isControllerAlwaysOnSupported();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.isControllerAlwaysOnSupported(), false);
+
     }
 
     /**
@@ -2683,21 +2439,9 @@
             Log.e(TAG, "TagIntentAppPreference is not supported");
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.setTagIntentAppPreferenceForUser(userId, pkg, allow);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            try {
-                return sService.setTagIntentAppPreferenceForUser(userId, pkg, allow);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE;
-        }
+        return callServiceReturn(() ->
+                sService.setTagIntentAppPreferenceForUser(userId, pkg, allow),
+                        TAG_INTENT_APP_PREF_RESULT_UNAVAILABLE);
     }
 
 
@@ -2735,26 +2479,8 @@
             Log.e(TAG, "TagIntentAppPreference is not supported");
             throw new UnsupportedOperationException();
         }
-        try {
-            Map<String, Boolean> result = (Map<String, Boolean>) sService
-                     .getTagIntentAppPreferenceForUser(userId);
-            return result;
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return Collections.emptyMap();
-            }
-            try {
-                Map<String, Boolean> result = (Map<String, Boolean>) sService
-                        .getTagIntentAppPreferenceForUser(userId);
-                return result;
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return Collections.emptyMap();
-        }
+        return callServiceReturn( () ->
+            sService.getTagIntentAppPreferenceForUser(userId), Collections.emptyMap());
     }
 
     /**
@@ -2772,22 +2498,8 @@
         if (!sHasNfcFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.isTagIntentAppPreferenceSupported();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.isTagIntentAppPreferenceSupported();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.isTagIntentAppPreferenceSupported(), false);
+
     }
 
    /**
@@ -2800,25 +2512,61 @@
     @TestApi
     @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
     public void notifyPollingLoop(@NonNull PollingFrame pollingFrame) {
-        Bundle frame = pollingFrame.toBundle();
+        callService(() ->  sService.notifyPollingLoop(pollingFrame));
+    }
+
+
+   /**
+     * Notifies the system of new HCE data for tests.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public void notifyTestHceData(int technology, byte[] data) {
+        callService(() ->  sService.notifyTestHceData(technology, data));
+    }
+
+    /** @hide */
+    interface ServiceCall {
+        void call() throws RemoteException;
+    }
+    /** @hide */
+    static void callService(ServiceCall call) {
         try {
             if (sService == null) {
-                attemptDeadServiceRecovery(null);
+                attemptDeadServiceRecovery(new RemoteException("NFC Service is null"));
             }
-            sService.notifyPollingLoop(frame);
+            call.call();
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            try {
+                call.call();
+            } catch (RemoteException ee) {
+                ee.rethrowAsRuntimeException();
+            }
+        }
+    }
+    /** @hide */
+    interface ServiceCallReturn<T> {
+        T call() throws RemoteException;
+    }
+    /** @hide */
+    static <T> T callServiceReturn(ServiceCallReturn<T> call, T defaultReturn) {
+        try {
+            if (sService == null) {
+                attemptDeadServiceRecovery(new RemoteException("NFC Service is null"));
+            }
+            return call.call();
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
             // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return;
-            }
             try {
-                sService.notifyPollingLoop(frame);
+                return call.call();
             } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
+                ee.rethrowAsRuntimeException();
             }
         }
+        return defaultReturn;
     }
 
    /**
@@ -2829,24 +2577,7 @@
     @TestApi
     @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
     public void notifyHceDeactivated() {
-        try {
-            if (sService == null) {
-                attemptDeadServiceRecovery(null);
-            }
-            sService.notifyHceDeactivated();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return;
-            }
-            try {
-                sService.notifyHceDeactivated();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-        }
+        callService(() ->  sService.notifyHceDeactivated());
     }
 
     /**
@@ -2862,22 +2593,7 @@
         if (!sHasNfcWlcFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.setWlcEnabled(enable);
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.setWlcEnabled(enable);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.setWlcEnabled(enable), false);
     }
 
     /**
@@ -2892,22 +2608,8 @@
         if (!sHasNfcWlcFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.isWlcEnabled();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return false;
-            }
-            try {
-                return sService.isWlcEnabled();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return false;
-        }
+        return callServiceReturn(() ->  sService.isWlcEnabled(), false);
+
     }
 
     /**
@@ -2986,22 +2688,8 @@
         if (!sHasNfcWlcFeature) {
             throw new UnsupportedOperationException();
         }
-        try {
-            return sService.getWlcListenerDeviceInfo();
-        } catch (RemoteException e) {
-            attemptDeadServiceRecovery(e);
-            // Try one more time
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-                return null;
-            }
-            try {
-                return sService.getWlcListenerDeviceInfo();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover NFC Service.");
-            }
-            return null;
-        }
+        return callServiceReturn(() ->  sService.getWlcListenerDeviceInfo(), null);
+
     }
 
     /**
diff --git a/nfc/java/android/nfc/NfcAntennaInfo.java b/nfc/java/android/nfc/NfcAntennaInfo.java
index b002ca2..c57b2e0 100644
--- a/nfc/java/android/nfc/NfcAntennaInfo.java
+++ b/nfc/java/android/nfc/NfcAntennaInfo.java
@@ -64,9 +64,9 @@
 
     /**
      * Whether the device is foldable. When the device is foldable,
-     * the 0, 0 is considered to be bottom-left when the device is unfolded and
+     * the 0, 0 is considered to be top-left when the device is unfolded and
      * the screens are facing the user. For non-foldable devices 0, 0
-     * is bottom-left when the user is facing the screen.
+     * is top-left when the user is facing the screen.
      */
     public boolean isDeviceFoldable() {
         return mDeviceFoldable;
diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java
index 1eff58c..2ec819c 100644
--- a/nfc/java/android/nfc/NfcOemExtension.java
+++ b/nfc/java/android/nfc/NfcOemExtension.java
@@ -89,13 +89,11 @@
                         + "registering");
                 throw new IllegalArgumentException();
             }
-            try {
+            NfcAdapter.callService(() -> {
                 NfcAdapter.sService.registerOemExtensionCallback(mOemNfcExtensionCallback);
                 mCallback = callback;
                 mExecutor = executor;
-            } catch (RemoteException e) {
-                mAdapter.attemptDeadServiceRecovery(e);
-            }
+            });
         }
     }
 
@@ -117,13 +115,11 @@
                 Log.e(TAG, "Callback not registered");
                 throw new IllegalArgumentException();
             }
-            try {
+            NfcAdapter.callService(() -> {
                 NfcAdapter.sService.unregisterOemExtensionCallback(mOemNfcExtensionCallback);
                 mCallback = null;
                 mExecutor = null;
-            } catch (RemoteException e) {
-                mAdapter.attemptDeadServiceRecovery(e);
-            }
+            });
         }
     }
 
@@ -134,11 +130,27 @@
     @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     public void clearPreference() {
-        try {
-            NfcAdapter.sService.clearPreference();
-        } catch (RemoteException e) {
-            mAdapter.attemptDeadServiceRecovery(e);
-        }
+        NfcAdapter.callService(() -> NfcAdapter.sService.clearPreference());
+    }
+
+    /**
+     * Get the screen state from system and set it to current screen state.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void synchronizeScreenState() {
+        NfcAdapter.callService(() -> NfcAdapter.sService.setScreenState());
+    }
+
+    /**
+     * Check if the firmware needs updating.
+     *
+     * <p>If an update is needed, a firmware will be triggered when NFC is disabled.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION)
+    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    public void maybeTriggerFirmwareUpdate() {
+        NfcAdapter.callService(() -> NfcAdapter.sService.checkFirmware());
     }
 
     private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub {
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index 2c7d61e..3cf0a4d 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -108,6 +108,8 @@
 
     private final Map<String, Boolean> mAutoTransact;
 
+    private final Map<Pattern, Boolean> mAutoTransactPatterns;
+
     /**
      * Whether this service should only be started when the device is unlocked.
      */
@@ -179,7 +181,7 @@
         this(info, onHost, description, staticAidGroups, dynamicAidGroups,
                 requiresUnlock, requiresScreenOn, bannerResource, uid,
                 settingsActivityName, offHost, staticOffHost, isEnabled,
-                new HashMap<String, Boolean>());
+                new HashMap<String, Boolean>(), new HashMap<Pattern, Boolean>());
     }
 
     /**
@@ -189,12 +191,13 @@
             List<AidGroup> staticAidGroups, List<AidGroup> dynamicAidGroups,
             boolean requiresUnlock, boolean requiresScreenOn, int bannerResource, int uid,
             String settingsActivityName, String offHost, String staticOffHost, boolean isEnabled,
-            HashMap<String, Boolean> autoTransact) {
+            Map<String, Boolean> autoTransact, Map<Pattern, Boolean> autoTransactPatterns) {
         this.mService = info;
         this.mDescription = description;
         this.mStaticAidGroups = new HashMap<String, AidGroup>();
         this.mDynamicAidGroups = new HashMap<String, AidGroup>();
         this.mAutoTransact = autoTransact;
+        this.mAutoTransactPatterns = autoTransactPatterns;
         this.mOffHostName = offHost;
         this.mStaticOffHostName = staticOffHost;
         this.mOnHost = onHost;
@@ -314,6 +317,7 @@
             mStaticAidGroups = new HashMap<String, AidGroup>();
             mDynamicAidGroups = new HashMap<String, AidGroup>();
             mAutoTransact = new HashMap<String, Boolean>();
+            mAutoTransactPatterns = new HashMap<Pattern, Boolean>();
             mOnHost = onHost;
 
             final int depth = parser.getDepth();
@@ -406,7 +410,29 @@
                     boolean autoTransact = a.getBoolean(
                             com.android.internal.R.styleable.PollingLoopFilter_autoTransact,
                             false);
-                    mAutoTransact.put(plf, autoTransact);
+                    if (!mOnHost && !autoTransact) {
+                        Log.e(TAG, "Ignoring polling-loop-filter " + plf
+                                + " for offhost service that isn't autoTransact");
+                    } else {
+                        mAutoTransact.put(plf, autoTransact);
+                    }
+                    a.recycle();
+                } else if (eventType == XmlPullParser.START_TAG
+                        && "polling-loop-pattern-filter".equals(tagName) && currentGroup == null) {
+                    final TypedArray a = res.obtainAttributes(attrs,
+                            com.android.internal.R.styleable.PollingLoopPatternFilter);
+                    String plf = a.getString(
+                            com.android.internal.R.styleable.PollingLoopPatternFilter_name)
+                                    .toUpperCase(Locale.ROOT);
+                    boolean autoTransact = a.getBoolean(
+                            com.android.internal.R.styleable.PollingLoopFilter_autoTransact,
+                            false);
+                    if (!mOnHost && !autoTransact) {
+                        Log.e(TAG, "Ignoring polling-loop-filter " + plf
+                                + " for offhost service that isn't autoTransact");
+                    } else {
+                        mAutoTransactPatterns.put(Pattern.compile(plf), autoTransact);
+                    }
                     a.recycle();
                 }
             }
@@ -481,7 +507,30 @@
      */
     @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
     public boolean getShouldAutoTransact(@NonNull String plf) {
-        return mAutoTransact.getOrDefault(plf.toUpperCase(Locale.ROOT), false);
+        if (mAutoTransact.getOrDefault(plf.toUpperCase(Locale.ROOT), false)) {
+            return true;
+        }
+        List<Pattern> patternMatches = mAutoTransactPatterns.keySet().stream()
+                .filter(p -> p.matcher(plf).matches()).toList();
+        if (patternMatches == null || patternMatches.size() == 0) {
+            return false;
+        }
+        for (Pattern patternMatch : patternMatches) {
+            if (mAutoTransactPatterns.get(patternMatch)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the current polling loop pattern filters for this service.
+     * @return List of polling loop pattern filters.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+    @NonNull
+    public List<Pattern> getPollingLoopPatternFilters() {
+        return new ArrayList<>(mAutoTransactPatterns.keySet());
     }
 
     /**
@@ -683,13 +732,17 @@
      * Add a Polling Loop Filter. Custom NFC polling frames that match this filter will be
      * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this
      * multiple times will cause the value to be overwritten each time.
-     * @param pollingLoopFilter the polling loop filter to add, must be a  valide hexadecimal string
+     * @param pollingLoopFilter the polling loop filter to add, must be a valid hexadecimal string
+     * @param autoTransact when true, disable observe mode when this filter matches, when false,
+     *                     matching does not change the observe mode state
      */
     @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
     public void addPollingLoopFilter(@NonNull String pollingLoopFilter,
             boolean autoTransact) {
+        if (!mOnHost && !autoTransact) {
+            return;
+        }
         mAutoTransact.put(pollingLoopFilter, autoTransact);
-
     }
 
     /**
@@ -703,6 +756,35 @@
     }
 
     /**
+     * Add a Polling Loop Pattern Filter. Custom NFC polling frames that match this filter will be
+     * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this
+     * multiple times will cause the value to be overwritten each time.
+     * @param pollingLoopPatternFilter the polling loop pattern filter to add, must be a valid
+     *                                regex to match a hexadecimal string
+     * @param autoTransact when true, disable observe mode when this filter matches, when false,
+     *                     matching does not change the observe mode state
+     */
+    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public void addPollingLoopPatternFilter(@NonNull String pollingLoopPatternFilter,
+            boolean autoTransact) {
+        if (!mOnHost && !autoTransact) {
+            return;
+        }
+        mAutoTransactPatterns.put(Pattern.compile(pollingLoopPatternFilter), autoTransact);
+    }
+
+    /**
+     * Remove a Polling Loop Pattern Filter. Custom NFC polling frames that match this filter will
+     * no longer be delivered to {@link HostApduService#processPollingFrames(List)}.
+     * @param pollingLoopPatternFilter this polling loop filter to add.
+     */
+    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public void removePollingLoopPatternFilter(@NonNull String pollingLoopPatternFilter) {
+        mAutoTransactPatterns.remove(
+                Pattern.compile(pollingLoopPatternFilter.toUpperCase(Locale.ROOT)));
+    }
+
+    /**
      * Sets the off host Secure Element.
      * @param  offHost  Secure Element to set. Only accept strings with prefix SIM or prefix eSE.
      *                  Ref: GSMA TS.26 - NFC Handset Requirements
@@ -856,6 +938,8 @@
         dest.writeInt(mCategoryOtherServiceEnabled ? 1 : 0);
         dest.writeInt(mAutoTransact.size());
         dest.writeMap(mAutoTransact);
+        dest.writeInt(mAutoTransactPatterns.size());
+        dest.writeMap(mAutoTransactPatterns);
     };
 
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
@@ -889,10 +973,15 @@
                             new HashMap<String, Boolean>(autoTransactSize);
                     source.readMap(autoTransact, getClass().getClassLoader(),
                             String.class, Boolean.class);
+                    int autoTransactPatternSize = source.readInt();
+                    HashMap<Pattern, Boolean> autoTransactPatterns =
+                            new HashMap<Pattern, Boolean>(autoTransactSize);
+                    source.readMap(autoTransactPatterns, getClass().getClassLoader(),
+                            Pattern.class, Boolean.class);
                     return new ApduServiceInfo(info, onHost, description, staticAidGroups,
                             dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid,
                             settingsActivityName, offHostName, staticOffHostName,
-                            isEnabled, autoTransact);
+                            isEnabled, autoTransact, autoTransactPatterns);
                 }
 
                 @Override
@@ -939,6 +1028,9 @@
         pw.println("    Settings Activity: " + mSettingsActivityName);
         pw.println("    Requires Device Unlock: " + mRequiresDeviceUnlock);
         pw.println("    Requires Device ScreenOn: " + mRequiresDeviceScreenOn);
+        pw.println("    Should Default to Observe Mode: " + mShouldDefaultToObserveMode);
+        pw.println("    Auto-Transact Mapping: " + mAutoTransact);
+        pw.println("    Auto-Transact Patterns: " + mAutoTransactPatterns);
     }
 
 
@@ -992,6 +1084,27 @@
             proto.end(token);
         }
         proto.write(ApduServiceInfoProto.SETTINGS_ACTIVITY_NAME, mSettingsActivityName);
+        proto.write(ApduServiceInfoProto.SHOULD_DEFAULT_TO_OBSERVE_MODE,
+                mShouldDefaultToObserveMode);
+        {
+            long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_MAPPING);
+            for (Map.Entry<String, Boolean> entry : mAutoTransact.entrySet()) {
+                proto.write(ApduServiceInfoProto.AutoTransactMapping.AID, entry.getKey());
+                proto.write(ApduServiceInfoProto.AutoTransactMapping.SHOULD_AUTO_TRANSACT,
+                        entry.getValue());
+            }
+            proto.end(token);
+        }
+        {
+            long token = proto.start(ApduServiceInfoProto.AUTO_TRANSACT_PATTERNS);
+            for (Map.Entry<Pattern, Boolean> entry : mAutoTransactPatterns.entrySet()) {
+                proto.write(ApduServiceInfoProto.AutoTransactPattern.REGEXP_PATTERN,
+                        entry.getKey().pattern());
+                proto.write(ApduServiceInfoProto.AutoTransactPattern.SHOULD_AUTO_TRANSACT,
+                        entry.getValue());
+            }
+            proto.end(token);
+        }
     }
 
     private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index 61d651fa..e0438ce 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -27,6 +27,7 @@
 import android.annotation.UserHandleAware;
 import android.annotation.UserIdInt;
 import android.app.Activity;
+import android.app.role.RoleManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -35,6 +36,7 @@
 import android.nfc.Flags;
 import android.nfc.INfcCardEmulation;
 import android.nfc.NfcAdapter;
+import android.os.Build;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -44,6 +46,7 @@
 import java.util.HashMap;
 import java.util.HexFormat;
 import java.util.List;
+import java.util.Locale;
 import java.util.regex.Pattern;
 
 /**
@@ -60,6 +63,7 @@
  */
 public final class CardEmulation {
     private static final Pattern AID_PATTERN = Pattern.compile("[0-9A-Fa-f]{10,32}\\*?\\#?");
+    private static final Pattern PLPF_PATTERN = Pattern.compile("[0-9A-Fa-f,\\?,\\*\\.]*");
 
     static final String TAG = "CardEmulation";
 
@@ -215,24 +219,9 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      */
     public boolean isDefaultServiceForCategory(ComponentName service, String category) {
-        try {
-            return sService.isDefaultServiceForCategory(mContext.getUser().getIdentifier(),
-                    service, category);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.isDefaultServiceForCategory(mContext.getUser().getIdentifier(),
-                        service, category);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() ->
+            sService.isDefaultServiceForCategory(
+                mContext.getUser().getIdentifier(), service, category), false);
     }
 
     /**
@@ -247,33 +236,22 @@
      * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
      */
     public boolean isDefaultServiceForAid(ComponentName service, String aid) {
-        try {
-            return sService.isDefaultServiceForAid(mContext.getUser().getIdentifier(),
-                    service, aid);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.isDefaultServiceForAid(mContext.getUser().getIdentifier(),
-                        service, aid);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() ->
+            sService.isDefaultServiceForAid(
+                mContext.getUser().getIdentifier(), service, aid), false);
     }
 
     /**
+     * <p>
      * Returns whether the user has allowed AIDs registered in the
      * specified category to be handled by a service that is preferred
      * by the foreground application, instead of by a pre-configured default.
      *
      * Foreground applications can set such preferences using the
      * {@link #setPreferredService(Activity, ComponentName)} method.
+     * <p class="note">
+     * Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, this method will always
+     * return true.
      *
      * @param category The category, e.g. {@link #CATEGORY_PAYMENT}
      * @return whether AIDs in the category can be handled by a service
@@ -281,10 +259,16 @@
      */
     @SuppressWarnings("NonUserGetterCalled")
     public boolean categoryAllowsForegroundPreference(String category) {
+        Context contextAsUser = mContext.createContextAsUser(
+                UserHandle.of(UserHandle.myUserId()), 0);
+
+        RoleManager roleManager = contextAsUser.getSystemService(RoleManager.class);
+        if (roleManager.isRoleAvailable(RoleManager.ROLE_WALLET)) {
+            return true;
+        }
+
         if (CATEGORY_PAYMENT.equals(category)) {
             boolean preferForeground = false;
-            Context contextAsUser = mContext.createContextAsUser(
-                    UserHandle.of(UserHandle.myUserId()), 0);
             try {
                 preferForeground = Settings.Secure.getInt(
                         contextAsUser.getContentResolver(),
@@ -307,27 +291,18 @@
      *    every time what service they would like to use in this category.
      * <p>{@link #SELECTION_MODE_ASK_IF_CONFLICT} the user will only be asked
      *    to pick a service if there is a conflict.
+     *
+     * <p class="note">
+     * Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the default service defined
+     * by the holder of {@link android.app.role.RoleManager#ROLE_WALLET} and is category agnostic.
+     *
      * @param category The category, for example {@link #CATEGORY_PAYMENT}
      * @return the selection mode for the passed in category
      */
     public int getSelectionModeForCategory(String category) {
         if (CATEGORY_PAYMENT.equals(category)) {
-            boolean paymentRegistered = false;
-            try {
-                paymentRegistered = sService.isDefaultPaymentRegistered();
-            } catch (RemoteException e) {
-                recoverService();
-                if (sService == null) {
-                    Log.e(TAG, "Failed to recover CardEmulationService.");
-                    return SELECTION_MODE_ALWAYS_ASK;
-                }
-                try {
-                    paymentRegistered = sService.isDefaultPaymentRegistered();
-                } catch (RemoteException ee) {
-                    Log.e(TAG, "Failed to reach CardEmulationService.");
-                    return SELECTION_MODE_ALWAYS_ASK;
-                }
-            }
+            boolean paymentRegistered = callServiceReturn(() ->
+                    sService.isDefaultPaymentRegistered(), false);
             if (paymentRegistered) {
                 return SELECTION_MODE_PREFER_DEFAULT;
             } else {
@@ -350,13 +325,9 @@
     @FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
     public boolean setShouldDefaultToObserveModeForService(@NonNull ComponentName service,
             boolean enable) {
-        try {
-            return sService.setShouldDefaultToObserveModeForService(
-                    mContext.getUser().getIdentifier(), service, enable);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to reach CardEmulationService.");
-        }
-        return  false;
+        return callServiceReturn(() ->
+            sService.setShouldDefaultToObserveModeForService(
+                mContext.getUser().getIdentifier(), service, enable), false);
     }
 
     /**
@@ -364,9 +335,9 @@
      * auto-transact or not.  The PLF can be sequence of an
      * even number of at least 2 hexadecimal numbers (0-9, A-F or a-f), representing a series of
      * bytes. When non-standard polling loop frame matches this sequence exactly, it may be
-     * delivered to {@link HostApduService#processPollingFrames(List)}. If auto-transact is set to
-     * true, then observe mode will also be disabled.  if this service is currently preferred or
-     * there are no other services registered for this filter.
+     * delivered to {@link HostApduService#processPollingFrames(List)}.  If auto-transact
+     * is set to true and this service is currently preferred or there are no other services
+     * registered for this filter then observe mode will also be disabled.
      * @param service The HostApduService to register the filter for
      * @param pollingLoopFilter The filter to register
      * @param autoTransact true to have the NFC stack automatically disable observe mode and allow
@@ -377,27 +348,85 @@
     @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
     public boolean registerPollingLoopFilterForService(@NonNull ComponentName service,
             @NonNull String pollingLoopFilter, boolean autoTransact) {
-        pollingLoopFilter = validatePollingLoopFilter(pollingLoopFilter);
+        final String pollingLoopFilterV = validatePollingLoopFilter(pollingLoopFilter);
+        return callServiceReturn(() ->
+            sService.registerPollingLoopFilterForService(
+                mContext.getUser().getIdentifier(), service, pollingLoopFilterV, autoTransact),
+            false);
+    }
 
-        try {
-            return sService.registerPollingLoopFilterForService(mContext.getUser().getIdentifier(),
-                    service, pollingLoopFilter, autoTransact);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.registerPollingLoopFilterForService(
-                        mContext.getUser().getIdentifier(), service,
-                        pollingLoopFilter, autoTransact);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+    /**
+     * Unregister a polling loop filter (PLF) for a HostApduService. If the PLF had previously been
+     * registered via {@link #registerPollingLoopFilterForService(ComponentName, String, boolean)}
+     * for this service it will be removed.
+     * @param service The HostApduService to unregister the filter for
+     * @param pollingLoopFilter The filter to unregister
+     * @return true if the filter was removed, false otherwise
+     * @throws IllegalArgumentException if the passed in string doesn't parse to at least one byte
+     */
+    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public boolean removePollingLoopFilterForService(@NonNull ComponentName service,
+            @NonNull String pollingLoopFilter) {
+        final String pollingLoopFilterV = validatePollingLoopFilter(pollingLoopFilter);
+        return callServiceReturn(() ->
+            sService.removePollingLoopFilterForService(
+                mContext.getUser().getIdentifier(), service, pollingLoopFilterV), false);
+    }
+
+
+    /**
+     * Register a polling loop pattern filter (PLPF) for a HostApduService and indicate whether it
+     * should auto-transact or not. The pattern may include the characters 0-9 and A-F as well as
+     * the regular expression operators `.`, `?` and `*`. When the beginning of anon-standard
+     * polling loop frame matches this sequence exactly, it may be delivered to
+     * {@link HostApduService#processPollingFrames(List)}. If auto-transact is set to true and this
+     * service is currently preferred or there are no other services registered for this filter
+     * then observe mode will also be disabled.
+     * @param service The HostApduService to register the filter for
+     * @param pollingLoopPatternFilter The pattern filter to register, must to be compatible with
+     *         {@link java.util.regex.Pattern#compile(String)} and only contain hexadecimal numbers
+     *         and `.`, `?` and `*` operators
+     * @param autoTransact true to have the NFC stack automatically disable observe mode and allow
+     *         transactions to proceed when this filter matches, false otherwise
+     * @return true if the filter was registered, false otherwise
+     * @throws IllegalArgumentException if the filter containst elements other than hexadecimal
+     *         numbers and `.`, `?` and `*` operators
+     * @throws java.util.regex.PatternSyntaxException if the regex syntax is invalid
+     */
+    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public boolean registerPollingLoopPatternFilterForService(@NonNull ComponentName service,
+            @NonNull String pollingLoopPatternFilter, boolean autoTransact) {
+        final String pollingLoopPatternFilterV =
+            validatePollingLoopPatternFilter(pollingLoopPatternFilter);
+        return callServiceReturn(() ->
+            sService.registerPollingLoopPatternFilterForService(
+                mContext.getUser().getIdentifier(), service, pollingLoopPatternFilterV,
+                autoTransact),
+            false);
+    }
+
+    /**
+     * Unregister a polling loop pattern filter (PLPF) for a HostApduService. If the PLF had
+     * previously been registered via
+     * {@link #registerPollingLoopFilterForService(ComponentName, String, boolean)} for this
+     * service it will be removed.
+     * @param service The HostApduService to unregister the filter for
+     * @param pollingLoopPatternFilter The filter to unregister, must to be compatible with
+     *         {@link java.util.regex.Pattern#compile(String)} and only contain hexadecimal numbers
+     *         and`.`, `?` and `*` operators
+     * @return true if the filter was removed, false otherwise
+     * @throws IllegalArgumentException if the filter containst elements other than hexadecimal
+     *         numbers and `.`, `?` and `*` operators
+     * @throws java.util.regex.PatternSyntaxException if the regex syntax is invalid
+     */
+    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public boolean removePollingLoopPatternFilterForService(@NonNull ComponentName service,
+            @NonNull String pollingLoopPatternFilter) {
+        final String pollingLoopPatternFilterV =
+            validatePollingLoopPatternFilter(pollingLoopPatternFilter);
+        return callServiceReturn(() ->
+            sService.removePollingLoopPatternFilterForService(
+                mContext.getUser().getIdentifier(), service, pollingLoopPatternFilterV), false);
     }
 
     /**
@@ -422,25 +451,10 @@
      */
     public boolean registerAidsForService(ComponentName service, String category,
             List<String> aids) {
-        AidGroup aidGroup = new AidGroup(aids, category);
-        try {
-            return sService.registerAidGroupForService(mContext.getUser().getIdentifier(),
-                    service, aidGroup);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.registerAidGroupForService(mContext.getUser().getIdentifier(),
-                        service, aidGroup);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        final AidGroup aidGroup = new AidGroup(aids, category);
+        return callServiceReturn(() ->
+            sService.registerAidGroupForService(
+                mContext.getUser().getIdentifier(), service, aidGroup), false);
     }
 
     /**
@@ -462,27 +476,9 @@
     @RequiresPermission(android.Manifest.permission.NFC)
     @NonNull
     public boolean unsetOffHostForService(@NonNull ComponentName service) {
-        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
-        if (adapter == null) {
-            return false;
-        }
-
-        try {
-            return sService.unsetOffHostForService(mContext.getUser().getIdentifier(), service);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.unsetOffHostForService(mContext.getUser().getIdentifier(), service);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() ->
+            sService.unsetOffHostForService(
+                mContext.getUser().getIdentifier(), service), false);
     }
 
     /**
@@ -521,8 +517,6 @@
     @NonNull
     public boolean setOffHostForService(@NonNull ComponentName service,
             @NonNull String offHostSecureElement) {
-        boolean validSecureElement = false;
-
         NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
         if (adapter == null || offHostSecureElement == null) {
             return false;
@@ -543,25 +537,10 @@
         } else if (offHostSecureElement.equals("SIM")) {
             offHostSecureElement = "SIM1";
         }
-
-        try {
-            return sService.setOffHostForService(mContext.getUser().getIdentifier(), service,
-                offHostSecureElement);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.setOffHostForService(mContext.getUser().getIdentifier(), service,
-                        offHostSecureElement);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        final String offHostSecureElementV = new String(offHostSecureElement);
+        return callServiceReturn(() ->
+            sService.setOffHostForService(
+                mContext.getUser().getIdentifier(), service, offHostSecureElementV), false);
     }
 
     /**
@@ -579,25 +558,10 @@
      * @return The list of AIDs registered for this category, or null if it couldn't be found.
      */
     public List<String> getAidsForService(ComponentName service, String category) {
-        try {
-            AidGroup group =  sService.getAidGroupForService(mContext.getUser().getIdentifier(),
-                    service, category);
-            return (group != null ? group.getAids() : null);
-        } catch (RemoteException e) {
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
-            }
-            try {
-                AidGroup group = sService.getAidGroupForService(mContext.getUser().getIdentifier(),
-                        service, category);
-                return (group != null ? group.getAids() : null);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
-            }
-        }
+        AidGroup group = callServiceReturn(() ->
+               sService.getAidGroupForService(
+                   mContext.getUser().getIdentifier(), service, category), null);
+        return (group != null ? group.getAids() : null);
     }
 
     /**
@@ -616,24 +580,9 @@
      * @return whether the group was successfully removed.
      */
     public boolean removeAidsForService(ComponentName service, String category) {
-        try {
-            return sService.removeAidGroupForService(mContext.getUser().getIdentifier(), service,
-                    category);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.removeAidGroupForService(mContext.getUser().getIdentifier(),
-                        service, category);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() ->
+            sService.removeAidGroupForService(
+                mContext.getUser().getIdentifier(), service, category), false);
     }
 
     /**
@@ -670,22 +619,7 @@
         if (activity == null || service == null) {
             throw new NullPointerException("activity or service or category is null");
         }
-        try {
-            return sService.setPreferredService(service);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.setPreferredService(service);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() -> sService.setPreferredService(service), false);
     }
 
     /**
@@ -702,22 +636,7 @@
         if (activity == null) {
             throw new NullPointerException("activity is null");
         }
-        try {
-            return sService.unsetPreferredService();
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.unsetPreferredService();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() -> sService.unsetPreferredService(), false);
     }
 
     /**
@@ -731,21 +650,7 @@
      * @return whether AID prefix registering is supported on this device.
      */
     public boolean supportsAidPrefixRegistration() {
-        try {
-            return sService.supportsAidPrefixRegistration();
-        } catch (RemoteException e) {
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.supportsAidPrefixRegistration();
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() -> sService.supportsAidPrefixRegistration(), false);
     }
 
     /**
@@ -756,30 +661,21 @@
     @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
     @Nullable
     public List<String> getAidsForPreferredPaymentService() {
-        try {
-            ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(
-                    mContext.getUser().getIdentifier());
-            return (serviceInfo != null ? serviceInfo.getAids() : null);
-        } catch (RemoteException e) {
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                throw e.rethrowFromSystemServer();
-            }
-            try {
-                ApduServiceInfo serviceInfo =
-                        sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
-                return (serviceInfo != null ? serviceInfo.getAids() : null);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                throw e.rethrowFromSystemServer();
-            }
-        }
+        ApduServiceInfo serviceInfo = callServiceReturn(() ->
+                sService.getPreferredPaymentService(mContext.getUser().getIdentifier()), null);
+        return (serviceInfo != null ? serviceInfo.getAids() : null);
     }
 
     /**
      * Retrieves the route destination for the preferred payment service.
      *
+     * <p class="note">
+     * Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the preferred payment service
+     * no longer exists and is replaced by {@link android.app.role.RoleManager#ROLE_WALLET}. This
+     * will return the route for one of the services registered by the role holder (if any). If
+     * there are multiple services registered, it is unspecified which of those will be used to
+     * determine the route.
+     *
      * @return The route destination secure element name of the preferred payment service.
      *         HCE payment: "Host"
      *         OffHost payment: 1. String with prefix SIM or prefix eSE string.
@@ -796,159 +692,71 @@
     @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
     @Nullable
     public String getRouteDestinationForPreferredPaymentService() {
-        try {
-            ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(
-                    mContext.getUser().getIdentifier());
-            if (serviceInfo != null) {
-                if (!serviceInfo.isOnHost()) {
-                    return serviceInfo.getOffHostSecureElement() == null ?
-                            "OffHost" : serviceInfo.getOffHostSecureElement();
-                }
-                return "Host";
+        ApduServiceInfo serviceInfo = callServiceReturn(() ->
+                sService.getPreferredPaymentService(mContext.getUser().getIdentifier()), null);
+        if (serviceInfo != null) {
+            if (!serviceInfo.isOnHost()) {
+                return serviceInfo.getOffHostSecureElement() == null ?
+                        "OffHost" : serviceInfo.getOffHostSecureElement();
             }
-            return null;
-        } catch (RemoteException e) {
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                throw e.rethrowFromSystemServer();
-            }
-            try {
-                ApduServiceInfo serviceInfo =
-                        sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
-                if (serviceInfo != null) {
-                    if (!serviceInfo.isOnHost()) {
-                        return serviceInfo.getOffHostSecureElement() == null ?
-                                "Offhost" : serviceInfo.getOffHostSecureElement();
-                    }
-                    return "Host";
-                }
-                return null;
-
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                throw e.rethrowFromSystemServer();
-            }
+            return "Host";
         }
+        return null;
     }
 
     /**
      * Returns a user-visible description of the preferred payment service.
      *
+     * <p class="note">
+     * Starting with {@link Build.VERSION_CODES#VANILLA_ICE_CREAM}, the preferred payment service
+     * no longer exists and is replaced by {@link android.app.role.RoleManager#ROLE_WALLET}. This
+     * will return the description for one of the services registered by the role holder (if any).
+     * If there are multiple services registered, it is unspecified which of those will be used
+     * to obtain the service description here.
+     *
      * @return the preferred payment service description
      */
     @RequiresPermission(android.Manifest.permission.NFC_PREFERRED_PAYMENT_INFO)
     @Nullable
     public CharSequence getDescriptionForPreferredPaymentService() {
-        try {
-            ApduServiceInfo serviceInfo = sService.getPreferredPaymentService(
-                    mContext.getUser().getIdentifier());
-            return (serviceInfo != null ? serviceInfo.getDescription() : null);
-        } catch (RemoteException e) {
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                throw e.rethrowFromSystemServer();
-            }
-            try {
-                ApduServiceInfo serviceInfo =
-                        sService.getPreferredPaymentService(mContext.getUser().getIdentifier());
-                return (serviceInfo != null ? serviceInfo.getDescription() : null);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                throw e.rethrowFromSystemServer();
-            }
-        }
+        ApduServiceInfo serviceInfo = callServiceReturn(() ->
+                sService.getPreferredPaymentService(mContext.getUser().getIdentifier()), null);
+        return (serviceInfo != null ? serviceInfo.getDescription() : null);
     }
 
     /**
      * @hide
      */
     public boolean setDefaultServiceForCategory(ComponentName service, String category) {
-        try {
-            return sService.setDefaultServiceForCategory(mContext.getUser().getIdentifier(),
-                    service, category);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.setDefaultServiceForCategory(mContext.getUser().getIdentifier(),
-                        service, category);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() ->
+                sService.setDefaultServiceForCategory(
+                    mContext.getUser().getIdentifier(), service, category), false);
     }
 
     /**
      * @hide
      */
     public boolean setDefaultForNextTap(ComponentName service) {
-        try {
-            return sService.setDefaultForNextTap(mContext.getUser().getIdentifier(), service);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.setDefaultForNextTap(mContext.getUser().getIdentifier(), service);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() ->
+                sService.setDefaultForNextTap(
+                    mContext.getUser().getIdentifier(), service), false);
     }
 
     /**
      * @hide
      */
     public boolean setDefaultForNextTap(int userId, ComponentName service) {
-        try {
-            return sService.setDefaultForNextTap(userId, service);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.setDefaultForNextTap(userId, service);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() ->
+                sService.setDefaultForNextTap(userId, service), false);
     }
 
     /**
      * @hide
      */
     public List<ApduServiceInfo> getServices(String category) {
-        try {
-            return sService.getServices(mContext.getUser().getIdentifier(), category);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
-            }
-            try {
-                return sService.getServices(mContext.getUser().getIdentifier(), category);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return null;
-            }
-        }
+        return callServiceReturn(() ->
+                sService.getServices(
+                    mContext.getUser().getIdentifier(), category), null);
     }
 
     /**
@@ -962,22 +770,8 @@
     @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
     @NonNull
     public List<ApduServiceInfo> getServices(@NonNull String category, @UserIdInt int userId) {
-        try {
-            return sService.getServices(userId, category);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return null;
-            }
-            try {
-                return sService.getServices(userId, category);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return null;
-            }
-        }
+        return callServiceReturn(() ->
+                sService.getServices(userId, category), null);
     }
 
     /**
@@ -998,6 +792,23 @@
     }
 
     /**
+     * Tests the validity of the polling loop pattern filter.
+     * @param pollingLoopPatternFilter The polling loop filter to test.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
+    public static @NonNull String validatePollingLoopPatternFilter(
+        @NonNull String pollingLoopPatternFilter) {
+        // Verify hex characters
+        if (!PLPF_PATTERN.matcher(pollingLoopPatternFilter).matches()) {
+            throw new IllegalArgumentException(
+                "Polling loop pattern filters may only contain hexadecimal numbers, ?s and *s");
+        }
+        return Pattern.compile(pollingLoopPatternFilter.toUpperCase(Locale.ROOT)).toString();
+    }
+
+    /**
      * A valid AID according to ISO/IEC 7816-4:
      * <ul>
      * <li>Has >= 5 bytes and <=16 bytes (>=10 hex chars and <= 32 hex chars)
@@ -1050,22 +861,8 @@
         if (service == null) {
             throw new NullPointerException("activity or service or category is null");
         }
-        try {
-            return sService.setServiceEnabledForCategoryOther(userId, service, status);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.setServiceEnabledForCategoryOther(userId, service, status);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() ->
+                sService.setServiceEnabledForCategoryOther(userId, service, status), false);
     }
 
      /**
@@ -1097,22 +894,9 @@
         if (!activity.isResumed()) {
             throw new IllegalArgumentException("Activity must be resumed.");
         }
-        try {
-            return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.overrideRoutingTable(UserHandle.myUserId(), protocol, technology);
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
+        return callServiceReturn(() ->
+                sService.overrideRoutingTable(
+                    mContext.getUser().getIdentifier(), protocol, technology), false);
     }
 
     /**
@@ -1131,27 +915,9 @@
         if (!activity.isResumed()) {
             throw new IllegalArgumentException("Activity must be resumed.");
         }
-        try {
-            return sService.recoverRoutingTable(UserHandle.myUserId());
-        } catch (RemoteException e) {
-            // Try one more time
-            recoverService();
-            if (sService == null) {
-                Log.e(TAG, "Failed to recover CardEmulationService.");
-                return false;
-            }
-            try {
-                return sService.recoverRoutingTable(UserHandle.myUserId());
-            } catch (RemoteException ee) {
-                Log.e(TAG, "Failed to reach CardEmulationService.");
-                return false;
-            }
-        }
-    }
-
-    void recoverService() {
-        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
-        sService = adapter.getCardEmulationService();
+        return callServiceReturn(() ->
+                sService.recoverRoutingTable(
+                    mContext.getUser().getIdentifier()), false);
     }
 
     /**
@@ -1179,4 +945,53 @@
 
         return ComponentName.unflattenFromString(defaultPaymentComponent);
     }
+
+    /** @hide */
+    interface ServiceCall {
+        void call() throws RemoteException;
+    }
+    /** @hide */
+    public static void callService(ServiceCall call) {
+        try {
+            if (sService == null) {
+                NfcAdapter.attemptDeadServiceRecovery(
+                    new RemoteException("NFC CardEmulation Service is null"));
+                sService = NfcAdapter.getCardEmulationService();
+            }
+            call.call();
+        } catch (RemoteException e) {
+            NfcAdapter.attemptDeadServiceRecovery(e);
+            sService = NfcAdapter.getCardEmulationService();
+            try {
+                call.call();
+            } catch (RemoteException ee) {
+                ee.rethrowAsRuntimeException();
+            }
+        }
+    }
+    /** @hide */
+    interface ServiceCallReturn<T> {
+        T call() throws RemoteException;
+    }
+    /** @hide */
+    public static <T> T callServiceReturn(ServiceCallReturn<T> call, T defaultReturn) {
+        try {
+            if (sService == null) {
+                NfcAdapter.attemptDeadServiceRecovery(
+                    new RemoteException("NFC CardEmulation Service is null"));
+                sService = NfcAdapter.getCardEmulationService();
+            }
+            return call.call();
+        } catch (RemoteException e) {
+            NfcAdapter.attemptDeadServiceRecovery(e);
+            sService = NfcAdapter.getCardEmulationService();
+            // Try one more time
+            try {
+                return call.call();
+            } catch (RemoteException ee) {
+                ee.rethrowAsRuntimeException();
+            }
+        }
+        return defaultReturn;
+    }
 }
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index f3ba2d0..c3c74a6 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -326,15 +326,12 @@
                 }
                 break;
                 case MSG_POLLING_LOOP:
-                    ArrayList<Bundle> frames =
-                            msg.getData().getParcelableArrayList(KEY_POLLING_LOOP_FRAMES_BUNDLE,
-                            Bundle.class);
-                    ArrayList<PollingFrame> pollingFrames =
-                            new ArrayList<PollingFrame>(frames.size());
-                    for (Bundle frame : frames) {
-                        pollingFrames.add(new PollingFrame(frame));
+                    if (android.nfc.Flags.nfcReadPollingLoop()) {
+                        ArrayList<PollingFrame> pollingFrames =
+                                msg.getData().getParcelableArrayList(
+                                    KEY_POLLING_LOOP_FRAMES_BUNDLE, PollingFrame.class);
+                        processPollingFrames(pollingFrames);
                     }
-                    processPollingFrames(pollingFrames);
                     break;
             default:
                 super.handleMessage(msg);
diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.aidl b/nfc/java/android/nfc/cardemulation/PollingFrame.aidl
new file mode 100644
index 0000000..8e09f8b
--- /dev/null
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc.cardemulation;
+
+parcelable PollingFrame;
\ No newline at end of file
diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java
index 994f4ae..5dcc84c 100644
--- a/nfc/java/android/nfc/cardemulation/PollingFrame.java
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.java
@@ -32,19 +32,26 @@
 
 /**
  * Polling Frames represent data about individual frames of an NFC polling loop. These frames will
- * be deliverd to subclasses of {@link HostApduService} that have registered filters with
- * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String)} that match a
- * given frame in a loop and will be delivered through calls to
+ * be delivered to subclasses of {@link HostApduService} that have registered filters with
+ * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String, boolean)} that
+ * match a given frame in a loop and will be delivered through calls to
  * {@link HostApduService#processPollingFrames(List)}.
  */
 @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
-public final class PollingFrame implements Parcelable{
+public final class PollingFrame implements Parcelable {
 
     /**
      * @hide
      */
-    @IntDef(prefix = { "POLLING_LOOP_TYPE_"}, value = { POLLING_LOOP_TYPE_A, POLLING_LOOP_TYPE_B,
-            POLLING_LOOP_TYPE_F, POLLING_LOOP_TYPE_OFF, POLLING_LOOP_TYPE_ON })
+    @IntDef(prefix = { "POLLING_LOOP_TYPE_"},
+        value = {
+            POLLING_LOOP_TYPE_A,
+            POLLING_LOOP_TYPE_B,
+            POLLING_LOOP_TYPE_F,
+            POLLING_LOOP_TYPE_OFF,
+            POLLING_LOOP_TYPE_ON,
+            POLLING_LOOP_TYPE_UNKNOWN
+        })
     @Retention(RetentionPolicy.SOURCE)
     @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
     public @interface PollingFrameType {}
@@ -100,45 +107,46 @@
     /**
      * KEY_POLLING_LOOP_TYPE is the Bundle key for the type of
      * polling loop frame in the Bundle included in MSG_POLLING_LOOP.
-     *
-     * @hide
      */
     @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
-    public static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
+    private static final String KEY_POLLING_LOOP_TYPE = "android.nfc.cardemulation.TYPE";
 
     /**
      * KEY_POLLING_LOOP_DATA is the Bundle key for the raw data of captured from
      * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
-     *
-     * @hide
      */
     @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
-    public static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
+    private static final String KEY_POLLING_LOOP_DATA = "android.nfc.cardemulation.DATA";
 
     /**
      * KEY_POLLING_LOOP_GAIN is the Bundle key for the field strength of
      * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
-     *
-     * @hide
-     */
+    */
     @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
-    public static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
+    private static final String KEY_POLLING_LOOP_GAIN = "android.nfc.cardemulation.GAIN";
 
     /**
      * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for the timestamp of
      * the polling loop frame in the Bundle included in MSG_POLLING_LOOP.
-     *
-     * @hide
-     */
+    */
     @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
-    public static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
+    private static final String KEY_POLLING_LOOP_TIMESTAMP = "android.nfc.cardemulation.TIMESTAMP";
+
+    /**
+     * KEY_POLLING_LOOP_TIMESTAMP is the Bundle key for whether this polling frame triggered
+     * autoTransact in the Bundle included in MSG_POLLING_LOOP.
+    */
+    @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
+    private static final String KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT =
+            "android.nfc.cardemulation.TRIGGERED_AUTOTRANSACT";
 
 
     @PollingFrameType
     private final int mType;
     private final byte[] mData;
     private final int mGain;
-    private final int mTimestamp;
+    private final long mTimestamp;
+    private boolean mTriggeredAutoTransact;
 
     public static final @NonNull Parcelable.Creator<PollingFrame> CREATOR =
             new Parcelable.Creator<>() {
@@ -153,31 +161,46 @@
                 }
             };
 
-    PollingFrame(Bundle frame) {
+    private PollingFrame(Bundle frame) {
         mType = frame.getInt(KEY_POLLING_LOOP_TYPE);
         byte[] data = frame.getByteArray(KEY_POLLING_LOOP_DATA);
         mData = (data == null) ? new byte[0] : data;
-        mGain = frame.getByte(KEY_POLLING_LOOP_GAIN);
-        mTimestamp = frame.getInt(KEY_POLLING_LOOP_TIMESTAMP);
+        mGain = frame.getInt(KEY_POLLING_LOOP_GAIN, -1);
+        mTimestamp = frame.getLong(KEY_POLLING_LOOP_TIMESTAMP);
+        mTriggeredAutoTransact = frame.containsKey(KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT)
+                && frame.getBoolean(KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT);
     }
 
+    /**
+     * Constructor for Polling Frames.
+     *
+     * @param type the type of the frame
+     * @param data a byte array of the data contained in the frame
+     * @param gain the vendor-specific gain of the field
+     * @param timestampMicros the timestamp in microseconds
+     * @param triggeredAutoTransact whether or not this frame triggered the device to start a
+     * transaction automatically
+     *
+     * @hide
+     */
     public PollingFrame(@PollingFrameType int type, @Nullable byte[] data,
-            int gain, int timestamp) {
+            int gain, long timestampMicros, boolean triggeredAutoTransact) {
         mType = type;
         mData = data == null ? new byte[0] : data;
         mGain = gain;
-        mTimestamp = timestamp;
+        mTimestamp = timestampMicros;
+        mTriggeredAutoTransact = triggeredAutoTransact;
     }
 
     /**
      * Returns the type of frame for this polling loop frame.
      * The possible return values are:
      * <ul>
-     *   <li>{@link POLLING_LOOP_TYPE_ON}</li>
-     *   <li>{@link POLLING_LOOP_TYPE_OFF}</li>
-     *   <li>{@link POLLING_LOOP_TYPE_A}</li>
-     *   <li>{@link POLLING_LOOP_TYPE_B}</li>
-     *   <li>{@link POLLING_LOOP_TYPE_F}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_ON}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_OFF}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_A}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_B}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_F}</li>
      * </ul>
      */
     public @PollingFrameType int getType() {
@@ -194,21 +217,37 @@
     /**
      * Returns the gain representing the field strength of the NFC field when this polling loop
      * frame was observed.
+     * @return the gain or -1 if there is no gain measurement associated with this frame.
      */
-    public int getGain() {
+    public int getVendorSpecificGain() {
         return mGain;
     }
 
     /**
-     * Returns the timestamp of when the polling loop frame was observed in milliseconds. These
-     * timestamps are relative and not absolute and should only be used for comparing the timing of
-     * frames relative to each other.
-     * @return the timestamp in milliseconds
+     * Returns the timestamp of when the polling loop frame was observed, in microseconds. These
+     * timestamps are relative and should only be used for comparing the timing of frames relative
+     * to each other.
+     * @return the timestamp in microseconds
      */
-    public int getTimestamp() {
+    public long getTimestamp() {
         return mTimestamp;
     }
 
+    /**
+     * @hide
+     */
+    public void setTriggeredAutoTransact(boolean triggeredAutoTransact) {
+        mTriggeredAutoTransact = triggeredAutoTransact;
+    }
+
+    /**
+     * Returns whether this frame triggered the device to automatically disable observe mode and
+     * allow one transaction.
+     */
+    public boolean getTriggeredAutoTransact() {
+        return mTriggeredAutoTransact;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -220,24 +259,25 @@
     }
 
     /**
-     *
-     * @hide
      * @return a Bundle representing this frame
      */
-    public Bundle toBundle() {
+    private Bundle toBundle() {
         Bundle frame = new Bundle();
         frame.putInt(KEY_POLLING_LOOP_TYPE, getType());
-        frame.putByte(KEY_POLLING_LOOP_GAIN, (byte) getGain());
+        if (getVendorSpecificGain() != -1) {
+            frame.putInt(KEY_POLLING_LOOP_GAIN, (byte) getVendorSpecificGain());
+        }
         frame.putByteArray(KEY_POLLING_LOOP_DATA, getData());
-        frame.putInt(KEY_POLLING_LOOP_TIMESTAMP, getTimestamp());
+        frame.putLong(KEY_POLLING_LOOP_TIMESTAMP, getTimestamp());
+        frame.putBoolean(KEY_POLLING_LOOP_TRIGGERED_AUTOTRANSACT, getTriggeredAutoTransact());
         return frame;
     }
 
     @Override
     public String toString() {
         return "PollingFrame { Type: " + (char) getType()
-                + ", gain: " + getGain()
-                + ", timestamp: " + Integer.toUnsignedString(getTimestamp())
+                + ", gain: " + getVendorSpecificGain()
+                + ", timestamp: " + Long.toUnsignedString(getTimestamp())
                 + ", data: [" + HexFormat.ofDelimiter(" ").formatHex(getData()) + "] }";
     }
 }
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index 6841d2b..45036a5 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -92,3 +92,28 @@
     description: "Enable NFC OEM extension support"
     bug: "331206243"
 }
+
+flag {
+    name: "nfc_state_change"
+    is_exported: true
+    namespace: "nfc"
+    description: "Enable nfc state change API"
+    bug: "319934052"
+}
+
+flag {
+    name: "nfc_set_default_disc_tech"
+    is_exported: true
+    namespace: "nfc"
+    description: "Flag for NFC set default disc tech API"
+    bug: "321311407"
+}
+
+flag {
+    name: "nfc_persist_log"
+    is_exported: true
+    namespace: "nfc"
+    description: "Enable NFC persistent log support"
+    bug: "321310044"
+}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 4e52c77..40bc0e9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -40,7 +40,7 @@
     private static final String TAG = "CachedBluetoothDeviceManager";
     private static final boolean DEBUG = BluetoothUtils.D;
 
-    @VisibleForTesting static int sLateBondingTimeoutMillis = 5000; // 5s
+    @VisibleForTesting static int sLateBondingTimeoutMillis = 10000; // 10s
 
     private Context mContext;
     private final LocalBluetoothManager mBtManager;
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 417b7a6..53ff172 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -65,6 +65,7 @@
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
     <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
     <uses-permission android:name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" />
+    <uses-permission android:name="android.permission.READ_DROPBOX_DATA" />
     <uses-permission android:name="android.permission.READ_LOGS" />
     <uses-permission android:name="android.permission.BRIGHTNESS_SLIDER_USAGE" />
     <uses-permission android:name="android.permission.ACCESS_AMBIENT_LIGHT_STATS" />
diff --git a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
index 0dd9275..8c3cce4 100644
--- a/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
+++ b/packages/SoundPicker/src/com/android/soundpicker/RingtonePickerActivity.java
@@ -255,8 +255,8 @@
                 p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title);
             }
         } else {
-            // Make sure intents don't inject HTML elements.
-            p.mTitle = Html.escapeHtml(p.mTitle.toString());
+            // Make sure intents don't inject spannable elements.
+            p.mTitle = p.mTitle.toString();
         }
 
         setupAlert();
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
index 66a2fae..c698d18 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
@@ -19,7 +19,6 @@
 
 import com.android.systemui.accessibility.accessibilitymenu.R;
 
-import java.util.HashMap;
 import java.util.Map;
 
 /**
@@ -52,80 +51,80 @@
     private static final int LABEL_TEXT_INDEX = 3;
 
     /** Map stores all shortcut resource IDs that is in matching order of defined shortcut. */
-    private static final Map<ShortcutId, int[]> sShortcutResource = new HashMap<>() {{
-            put(ShortcutId.ID_ASSISTANT_VALUE, new int[] {
+    private static final Map<ShortcutId, int[]> sShortcutResource = Map.ofEntries(
+            Map.entry(ShortcutId.ID_ASSISTANT_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_assistant_24dp,
                     R.color.assistant_color,
                     R.string.assistant_utterance,
                     R.string.assistant_label,
-            });
-            put(ShortcutId.ID_A11YSETTING_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_A11YSETTING_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_settings_24dp,
                     R.color.a11y_settings_color,
                     R.string.a11y_settings_label,
                     R.string.a11y_settings_label,
-            });
-            put(ShortcutId.ID_POWER_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_POWER_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_power_24dp,
                     R.color.power_color,
                     R.string.power_utterance,
                     R.string.power_label,
-            });
-            put(ShortcutId.ID_RECENT_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_RECENT_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_recent_apps_24dp,
                     R.color.recent_apps_color,
                     R.string.recent_apps_label,
                     R.string.recent_apps_label,
-            });
-            put(ShortcutId.ID_LOCKSCREEN_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_LOCKSCREEN_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_lock_24dp,
                     R.color.lockscreen_color,
                     R.string.lockscreen_label,
                     R.string.lockscreen_label,
-            });
-            put(ShortcutId.ID_QUICKSETTING_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_QUICKSETTING_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_quick_settings_24dp,
                     R.color.quick_settings_color,
                     R.string.quick_settings_label,
                     R.string.quick_settings_label,
-            });
-            put(ShortcutId.ID_NOTIFICATION_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_NOTIFICATION_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_notifications_24dp,
                     R.color.notifications_color,
                     R.string.notifications_label,
                     R.string.notifications_label,
-            });
-            put(ShortcutId.ID_SCREENSHOT_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_SCREENSHOT_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_screenshot_24dp,
                     R.color.screenshot_color,
                     R.string.screenshot_utterance,
                     R.string.screenshot_label,
-            });
-            put(ShortcutId.ID_BRIGHTNESS_UP_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_BRIGHTNESS_UP_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_brightness_up_24dp,
                     R.color.brightness_color,
                     R.string.brightness_up_label,
                     R.string.brightness_up_label,
-            });
-            put(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_brightness_down_24dp,
                     R.color.brightness_color,
                     R.string.brightness_down_label,
                     R.string.brightness_down_label,
-            });
-            put(ShortcutId.ID_VOLUME_UP_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_VOLUME_UP_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_volume_up_24dp,
                     R.color.volume_color,
                     R.string.volume_up_label,
                     R.string.volume_up_label,
-            });
-            put(ShortcutId.ID_VOLUME_DOWN_VALUE, new int[] {
+            }),
+            Map.entry(ShortcutId.ID_VOLUME_DOWN_VALUE, new int[] {
                     R.drawable.ic_logo_a11y_volume_down_24dp,
                     R.color.volume_color,
                     R.string.volume_down_label,
                     R.string.volume_down_label,
-            });
-        }};
+            })
+    );
 
     /** Shortcut id used to identify. */
     private int mShortcutId = ShortcutId.UNSPECIFIED_ID_VALUE.ordinal();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index 317201d..f358ba2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -125,6 +125,7 @@
                 taskbarMarginLeft, taskbarMarginBottom, floatingRotationButtonPositionLeft);
 
         final int diameter = res.getDimensionPixelSize(mButtonDiameterResource);
+        mKeyButtonView.setDiameter(diameter);
         mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
                 taskbarMarginBottom));
     }
@@ -195,6 +196,7 @@
     public void updateIcon(int lightIconColor, int darkIconColor) {
         mAnimatedDrawable = (AnimatedVectorDrawable) mKeyButtonView.getContext()
                 .getDrawable(mRotationButtonController.getIconResId());
+        mAnimatedDrawable.setBounds(0, 0, mKeyButtonView.getWidth(), mKeyButtonView.getHeight());
         mKeyButtonView.setImageDrawable(mAnimatedDrawable);
         mKeyButtonView.setColors(lightIconColor, darkIconColor);
     }
@@ -248,8 +250,14 @@
             updateDimensionResources();
 
             if (mIsShowing) {
+                updateIcon(mRotationButtonController.getLightIconColor(),
+                        mRotationButtonController.getDarkIconColor());
                 final LayoutParams layoutParams = adjustViewPositionAndCreateLayoutParams();
                 mWindowManager.updateViewLayout(mKeyButtonContainer, layoutParams);
+                if (mAnimatedDrawable != null) {
+                    mAnimatedDrawable.reset();
+                    mAnimatedDrawable.start();
+                }
             }
         }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
index 2145166..75412f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonView.java
@@ -37,6 +37,7 @@
     private static final float BACKGROUND_ALPHA = 0.92f;
 
     private KeyButtonRipple mRipple;
+    private int mDiameter;
     private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
 
     private final Configuration mLastConfiguration;
@@ -93,10 +94,25 @@
         mRipple.setDarkIntensity(darkIntensity);
     }
 
+    /**
+     * Sets the view's diameter.
+     *
+     * @param diameter the diameter value for the view
+     */
+    void setDiameter(int diameter) {
+        mDiameter = diameter;
+    }
+
     @Override
     public void draw(Canvas canvas) {
         int d = Math.min(getWidth(), getHeight());
         canvas.drawOval(0, 0, d, d, mOvalBgPaint);
         super.draw(canvas);
     }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        setMeasuredDimension(mDiameter, mDiameter);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
new file mode 100644
index 0000000..69ebb76
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+# Bug component: 78010
+
[email protected]
[email protected]
[email protected]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
index 4657e9b..92a333e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS
@@ -1,6 +1,6 @@
 per-file *Notification* = set noparent
 per-file *Notification* = file:../notification/OWNERS
 
-per-file NotificationIcon* = [email protected], [email protected], [email protected]
+per-file NotificationIcon* = file:../OWNERS
 
 per-file NotificationShadeWindowControllerImpl.java = [email protected], [email protected], [email protected], [email protected], [email protected]
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS
new file mode 100644
index 0000000..1c52b8d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+
+include /packages/SystemUI/src/com/android/systemui/statusbar/OWNERS
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 178102e..25a0b45 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -16,6 +16,38 @@
     visibility: ["//visibility:public"],
 }
 
+filegroup {
+    name: "ravenwood-services-policies",
+    srcs: [
+        "texts/ravenwood-services-policies.txt",
+    ],
+    visibility: ["//visibility:public"],
+}
+
+filegroup {
+    name: "ravenwood-framework-policies",
+    srcs: [
+        "texts/ravenwood-framework-policies.txt",
+    ],
+    visibility: ["//visibility:public"],
+}
+
+filegroup {
+    name: "ravenwood-standard-options",
+    srcs: [
+        "texts/ravenwood-standard-options.txt",
+    ],
+    visibility: ["//visibility:public"],
+}
+
+filegroup {
+    name: "ravenwood-annotation-allowed-classes",
+    srcs: [
+        "texts/ravenwood-annotation-allowed-classes.txt",
+    ],
+    visibility: ["//visibility:public"],
+}
+
 java_library {
     name: "ravenwood-annotations-lib",
     srcs: [":ravenwood-annotations"],
@@ -151,6 +183,82 @@
 
 filegroup {
     name: "ravenwood-services-jarjar-rules",
-    srcs: ["ravenwood-services-jarjar-rules.txt"],
+    srcs: ["texts/ravenwood-services-jarjar-rules.txt"],
     visibility: ["//frameworks/base"],
 }
+
+java_library {
+    name: "services.fakes.ravenwood-jarjar",
+    installable: false,
+    srcs: [":services.fakes-sources"],
+    libs: [
+        "ravenwood-framework",
+        "services.core.ravenwood",
+    ],
+    jarjar_rules: ":ravenwood-services-jarjar-rules",
+    visibility: ["//visibility:private"],
+}
+
+java_library {
+    name: "mockito-ravenwood-prebuilt",
+    installable: false,
+    static_libs: [
+        "mockito-robolectric-prebuilt",
+    ],
+}
+
+java_library {
+    name: "inline-mockito-ravenwood-prebuilt",
+    installable: false,
+    static_libs: [
+        "inline-mockito-robolectric-prebuilt",
+    ],
+}
+
+android_ravenwood_libgroup {
+    name: "ravenwood-runtime",
+    libs: [
+        "100-framework-minus-apex.ravenwood",
+        "200-kxml2-android",
+
+        "android.test.mock.ravenwood",
+        "ravenwood-helper-runtime",
+        "hoststubgen-helper-runtime.ravenwood",
+        "services.core.ravenwood-jarjar",
+        "services.fakes.ravenwood-jarjar",
+
+        // ICU
+        "core-icu4j-for-host.ravenwood",
+        "icu4j-icudata-jarjar",
+        "icu4j-icutzdata-jarjar",
+
+        // Provide runtime versions of utils linked in below
+        "junit",
+        "truth",
+        "flag-junit",
+        "ravenwood-framework",
+        "ravenwood-junit-impl",
+        "ravenwood-junit-impl-flag",
+        "mockito-ravenwood-prebuilt",
+        "inline-mockito-ravenwood-prebuilt",
+
+        // It's a stub, so it should be towards the end.
+        "z00-all-updatable-modules-system-stubs",
+    ],
+    jni_libs: [
+        "libandroid_runtime",
+    ],
+}
+
+android_ravenwood_libgroup {
+    name: "ravenwood-utils",
+    libs: [
+        "junit",
+        "truth",
+        "flag-junit",
+        "ravenwood-framework",
+        "ravenwood-junit",
+        "mockito-ravenwood-prebuilt",
+        "inline-mockito-ravenwood-prebuilt",
+    ],
+}
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
similarity index 100%
rename from ravenwood/ravenwood-annotation-allowed-classes.txt
rename to ravenwood/texts/ravenwood-annotation-allowed-classes.txt
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/texts/ravenwood-framework-policies.txt
similarity index 100%
rename from ravenwood/framework-minus-apex-ravenwood-policies.txt
rename to ravenwood/texts/ravenwood-framework-policies.txt
diff --git a/ravenwood/ravenwood-services-jarjar-rules.txt b/ravenwood/texts/ravenwood-services-jarjar-rules.txt
similarity index 100%
rename from ravenwood/ravenwood-services-jarjar-rules.txt
rename to ravenwood/texts/ravenwood-services-jarjar-rules.txt
diff --git a/ravenwood/services.core-ravenwood-policies.txt b/ravenwood/texts/ravenwood-services-policies.txt
similarity index 100%
rename from ravenwood/services.core-ravenwood-policies.txt
rename to ravenwood/texts/ravenwood-services-policies.txt
diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/texts/ravenwood-standard-options.txt
similarity index 100%
rename from ravenwood/ravenwood-standard-options.txt
rename to ravenwood/texts/ravenwood-standard-options.txt
diff --git a/services/Android.bp b/services/Android.bp
index a0d3454..76fc1ba 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -108,6 +108,8 @@
 filegroup {
     name: "services-non-updatable-sources",
     srcs: [
+        ":incremental_aidl",
+        ":services.core-aidl-sources",
         ":services.core-sources",
         ":services.core-sources-am-wm",
         "core/java/com/android/server/am/package.html",
@@ -356,4 +358,8 @@
         },
     },
     api_surface: "system-server",
+    sdk_version: "module_current",
+    libs: [
+        "framework-annotations-lib",
+    ],
 }
diff --git a/services/appfunctions/OWNERS b/services/appfunctions/OWNERS
new file mode 100644
index 0000000..b310894
--- /dev/null
+++ b/services/appfunctions/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/app/appfunctions/OWNERS
diff --git a/services/core/Android.bp b/services/core/Android.bp
index fa323fd..9fbd8aa 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -30,6 +30,18 @@
     ],
 }
 
+filegroup {
+    name: "services.core-aidl-sources",
+    srcs: [
+        ":dumpstate_aidl",
+        ":framework_native_aidl",
+        ":gsiservice_aidl",
+        ":installd_aidl",
+        ":storaged_aidl",
+        ":vold_aidl",
+    ],
+}
+
 java_library_static {
     name: "services-config-update",
     srcs: [
@@ -151,14 +163,9 @@
         ":android.hardware.tv.hdmi.earc-V1-java-source",
         ":statslog-art-java-gen",
         ":statslog-contexthub-java-gen",
+        ":services.core-aidl-sources",
         ":services.core-sources",
         ":services.core.protologsrc",
-        ":dumpstate_aidl",
-        ":framework_native_aidl",
-        ":gsiservice_aidl",
-        ":installd_aidl",
-        ":storaged_aidl",
-        ":vold_aidl",
         ":platform-compat-config",
         ":platform-compat-overrides",
         ":display-device-config",
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 7979936..ab333b9 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -157,7 +157,7 @@
     private int mLastMaxChargingVoltage;
     private int mLastChargeCounter;
     private int mLastBatteryCycleCount;
-    private int mLastCharingState;
+    private int mLastChargingState;
     /**
      * The last seen charging policy. This requires the
      * {@link android.Manifest.permission#BATTERY_STATS} permission and should therefore not be
@@ -555,7 +555,7 @@
                         || mHealthInfo.batteryChargeCounterUah != mLastChargeCounter
                         || mInvalidCharger != mLastInvalidCharger
                         || mHealthInfo.batteryCycleCount != mLastBatteryCycleCount
-                        || mHealthInfo.chargingState != mLastCharingState)) {
+                        || mHealthInfo.chargingState != mLastChargingState)) {
 
             if (mPlugType != mLastPlugType) {
                 if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -738,7 +738,7 @@
             mLastBatteryLevelCritical = mBatteryLevelCritical;
             mLastInvalidCharger = mInvalidCharger;
             mLastBatteryCycleCount = mHealthInfo.batteryCycleCount;
-            mLastCharingState = mHealthInfo.chargingState;
+            mLastChargingState = mHealthInfo.chargingState;
         }
     }
 
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index eb03709..d9926a4 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -150,6 +150,15 @@
     private static final int DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD =
             PackageHealthObserverImpact.USER_IMPACT_LEVEL_71;
 
+    // Comma separated list of all packages exempt from user impact level threshold. If a package
+    // in the list is crash looping, all the mitigations including factory reset will be performed.
+    private static final String PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD =
+            "persist.device_config.configuration.packages_exempt_from_impact_level_threshold";
+
+    // Comma separated list of default packages exempt from user impact level threshold.
+    private static final String DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD =
+            "com.android.systemui";
+
     private long mNumberOfNativeCrashPollsRemaining;
 
     private static final int DB_VERSION = 1;
@@ -196,6 +205,8 @@
     private final DeviceConfig.OnPropertiesChangedListener
             mOnPropertyChangedListener = this::onPropertyChanged;
 
+    private final Set<String> mPackagesExemptFromImpactLevelThreshold = new ArraySet<>();
+
     // The set of packages that have been synced with the ExplicitHealthCheckController
     @GuardedBy("mLock")
     private Set<String> mRequestedHealthCheckPackages = new ArraySet<>();
@@ -518,7 +529,7 @@
                               @FailureReasons int failureReason,
                               int currentObserverImpact,
                               int mitigationCount) {
-        if (currentObserverImpact < getUserImpactLevelLimit()) {
+        if (allowMitigations(currentObserverImpact, versionedPackage)) {
             synchronized (mLock) {
                 mLastMitigation = mSystemClock.uptimeMillis();
             }
@@ -526,6 +537,13 @@
         }
     }
 
+    private boolean allowMitigations(int currentObserverImpact,
+            VersionedPackage versionedPackage) {
+        return currentObserverImpact < getUserImpactLevelLimit()
+                || getPackagesExemptFromImpactLevelThreshold().contains(
+                versionedPackage.getPackageName());
+    }
+
     private long getMitigationWindowMs() {
         return SystemProperties.getLong(MITIGATION_WINDOW_MS, DEFAULT_MITIGATION_WINDOW_MS);
     }
@@ -657,6 +675,15 @@
                 DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD);
     }
 
+    private Set<String> getPackagesExemptFromImpactLevelThreshold() {
+        if (mPackagesExemptFromImpactLevelThreshold.isEmpty()) {
+            String packageNames = SystemProperties.get(PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD,
+                    DEFAULT_PACKAGES_EXEMPT_FROM_IMPACT_LEVEL_THRESHOLD);
+            return Set.of(packageNames.split("\\s*,\\s*"));
+        }
+        return mPackagesExemptFromImpactLevelThreshold;
+    }
+
     /** Possible severity values of the user impact of a {@link PackageHealthObserver#execute}. */
     @Retention(SOURCE)
     @IntDef(value = {PackageHealthObserverImpact.USER_IMPACT_LEVEL_0,
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 47b65eb..1f88657 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -353,8 +353,8 @@
     }
 
     /** Called when there is a low memory kill */
-    void scheduleNoteLmkdProcKilled(final int pid, final int uid) {
-        mKillHandler.obtainMessage(KillHandler.MSG_LMKD_PROC_KILLED, pid, uid)
+    void scheduleNoteLmkdProcKilled(final int pid, final int uid, final int rssKb) {
+        mKillHandler.obtainMessage(KillHandler.MSG_LMKD_PROC_KILLED, pid, uid, Long.valueOf(rssKb))
                 .sendToTarget();
     }
 
@@ -401,9 +401,9 @@
 
             if (lmkd != null) {
                 updateExistingExitInfoRecordLocked(info, null,
-                        ApplicationExitInfo.REASON_LOW_MEMORY);
+                        ApplicationExitInfo.REASON_LOW_MEMORY, (Long) lmkd.second);
             } else if (zygote != null) {
-                updateExistingExitInfoRecordLocked(info, (Integer) zygote.second, null);
+                updateExistingExitInfoRecordLocked(info, (Integer) zygote.second, null, null);
             } else {
                 scheduleLogToStatsdLocked(info, false);
             }
@@ -486,7 +486,7 @@
      */
     @GuardedBy("mLock")
     private void updateExistingExitInfoRecordLocked(ApplicationExitInfo info,
-            Integer status, Integer reason) {
+            Integer status, Integer reason, Long rssKb) {
         if (info == null || !isFresh(info.getTimestamp())) {
             // if the record is way outdated, don't update it then (because of potential pid reuse)
             return;
@@ -513,6 +513,9 @@
                 immediateLog = true;
             }
         }
+        if (rssKb != null) {
+            info.setRss(rssKb.longValue());
+        }
         scheduleLogToStatsdLocked(info, immediateLog);
     }
 
@@ -523,7 +526,7 @@
      */
     @GuardedBy("mLock")
     private boolean updateExitInfoIfNecessaryLocked(
-            int pid, int uid, Integer status, Integer reason) {
+            int pid, int uid, Integer status, Integer reason, Long rssKb) {
         Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
         if (k != null) {
             uid = k;
@@ -552,7 +555,7 @@
                 // always be the first one we se as `getExitInfosLocked()` returns them sorted
                 // by most-recent-first.
                 isModified[0] = true;
-                updateExistingExitInfoRecordLocked(info, status, reason);
+                updateExistingExitInfoRecordLocked(info, status, reason, rssKb);
                 return FOREACH_ACTION_STOP_ITERATION;
             }
             return FOREACH_ACTION_NONE;
@@ -1668,11 +1671,11 @@
             switch (msg.what) {
                 case MSG_LMKD_PROC_KILLED:
                     mAppExitInfoSourceLmkd.onProcDied(msg.arg1 /* pid */, msg.arg2 /* uid */,
-                            null /* status */);
+                            null /* status */, (Long) msg.obj /* rss_kb */);
                     break;
                 case MSG_CHILD_PROC_DIED:
                     mAppExitInfoSourceZygote.onProcDied(msg.arg1 /* pid */, msg.arg2 /* uid */,
-                            (Integer) msg.obj /* status */);
+                            (Integer) msg.obj /* status */, null /* rss_kb */);
                     break;
                 case MSG_PROC_DIED: {
                     ApplicationExitInfo raw = (ApplicationExitInfo) msg.obj;
@@ -1833,7 +1836,7 @@
             }
         }
 
-        void onProcDied(final int pid, final int uid, final Integer status) {
+        void onProcDied(final int pid, final int uid, final Integer status, final Long rssKb) {
             if (DEBUG_PROCESSES) {
                 Slog.i(TAG, mTag + ": proc died: pid=" + pid + " uid=" + uid
                         + ", status=" + status);
@@ -1846,8 +1849,12 @@
             // Unlikely but possible: the record has been created
             // Let's update it if we could find a ApplicationExitInfo record
             synchronized (mLock) {
-                if (!updateExitInfoIfNecessaryLocked(pid, uid, status, mPresetReason)) {
-                    addLocked(pid, uid, status);
+                if (!updateExitInfoIfNecessaryLocked(pid, uid, status, mPresetReason, rssKb)) {
+                    if (rssKb != null) {
+                        addLocked(pid, uid, rssKb);     // lmkd
+                    } else {
+                        addLocked(pid, uid, status);    // zygote
+                    }
                 }
 
                 // Notify any interesed party regarding the lmkd kills
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 11db302..4b95a62 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -38,6 +38,7 @@
 per-file ContentProviderHelper.java = [email protected], [email protected], [email protected], [email protected]
 
 per-file CachedAppOptimizer.java = file:/PERFORMANCE_OWNERS
+per-file Freezer.java = file:/PERFORMANCE_OWNERS
 
 # Multiuser
 per-file User* = file:/MULTIUSER_OWNERS
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 97678aa..b239b64 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -945,12 +945,14 @@
                             try {
                                 switch (inputData.readInt()) {
                                     case LMK_PROCKILL:
-                                        if (receivedLen != 12) {
+                                        if (receivedLen != 16) {
                                             return false;
                                         }
                                         final int pid = inputData.readInt();
                                         final int uid = inputData.readInt();
-                                        mAppExitInfoTracker.scheduleNoteLmkdProcKilled(pid, uid);
+                                        final int rssKb = inputData.readInt();
+                                        mAppExitInfoTracker.scheduleNoteLmkdProcKilled(pid, uid,
+                                                rssKb);
                                         return true;
                                     case LMK_KILL_OCCURRED:
                                         if (receivedLen
diff --git a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
index 46b4f48..4490745 100644
--- a/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
+++ b/services/core/java/com/android/server/hdmi/DelayedMessageBuffer.java
@@ -17,8 +17,10 @@
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiDeviceInfo;
+
 import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
 
 /**
  * Buffer storage to keep incoming messages for later processing. Used to
@@ -83,6 +85,16 @@
         return false;
     }
 
+    List<HdmiCecMessage> getBufferedMessagesWithOpcode(int opcode) {
+        List<HdmiCecMessage> messages = new ArrayList<>();
+        for (HdmiCecMessage message : mBuffer) {
+            if (message.getOpcode() == opcode) {
+                messages.add(message);
+            }
+        }
+        return messages;
+    }
+
     void processAllMessages() {
         // Use the copied buffer.
         ArrayList<HdmiCecMessage> copiedBuffer = new ArrayList<>(mBuffer);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 46061a5..6bc5588 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -205,7 +205,9 @@
         resetSelectRequestBuffer();
         launchDeviceDiscovery();
         startQueuedActions();
-        if (!mDelayedMessageBuffer.isBuffered(Constants.MESSAGE_ACTIVE_SOURCE)) {
+        List<HdmiCecMessage> bufferedActiveSource = mDelayedMessageBuffer
+                .getBufferedMessagesWithOpcode(Constants.MESSAGE_ACTIVE_SOURCE);
+        if (bufferedActiveSource.isEmpty()) {
             addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
                 @Override
                 public void onComplete(int result) {
@@ -220,9 +222,33 @@
                     }
                 }
             }));
+        } else {
+            addCecDeviceForBufferedActiveSource(bufferedActiveSource.get(0));
         }
     }
 
+    // Add a new CEC device with known information from the buffered <Active Source> message. This
+    // helps TvInputCallback#onInputAdded to be called such that the message can be processed and
+    // the TV to switch to the new active input.
+    @ServiceThreadOnly
+    private void addCecDeviceForBufferedActiveSource(HdmiCecMessage bufferedActiveSource) {
+        assertRunOnServiceThread();
+        if (bufferedActiveSource == null) {
+            return;
+        }
+        int source = bufferedActiveSource.getSource();
+        int physicalAddress = HdmiUtils.twoBytesToInt(bufferedActiveSource.getParams());
+        List<Integer> deviceTypes = HdmiUtils.getTypeFromAddress(source);
+        HdmiDeviceInfo newDevice = HdmiDeviceInfo.cecDeviceBuilder()
+                .setLogicalAddress(source)
+                .setPhysicalAddress(physicalAddress)
+                .setDisplayName(HdmiUtils.getDefaultDeviceName(source))
+                .setDeviceType(deviceTypes.get(0))
+                .setVendorId(Constants.VENDOR_ID_UNKNOWN)
+                .build();
+        mService.getHdmiCecNetwork().addCecDevice(newDevice);
+    }
+
     @ServiceThreadOnly
     public void setSelectRequestBuffer(SelectRequestBuffer requestBuffer) {
         assertRunOnServiceThread();
@@ -577,6 +603,12 @@
     @Constants.HandleMessageResult
     protected int handleReportPhysicalAddress(HdmiCecMessage message) {
         super.handleReportPhysicalAddress(message);
+        // Ignore <Report Physical Address> while DeviceDiscoveryAction is in progress to avoid
+        // starting a NewDeviceAction which might interfere in creating the list of known devices.
+        if (hasAction(DeviceDiscoveryAction.class)) {
+            return Constants.HANDLED;
+        }
+
         int path = HdmiUtils.twoBytesToInt(message.getParams());
         int address = message.getSource();
         int type = message.getParams()[2];
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index a23c08a..16b00de 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -524,8 +524,7 @@
         if ((value & 0x80) != 0x00) {
             return false;
         }
-        // Validate than not more than one bit is set
-        return (Integer.bitCount(value) <= 1);
+        return true;
     }
 
     /**
@@ -770,6 +769,7 @@
      * @return true if the UI Broadcast type is valid
      */
     private static boolean isValidUiBroadcastType(int value) {
+        value = value & 0xFF;
         return ((value == 0x00)
                 || (value == 0x01)
                 || (value == 0x10)
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index 17f2fcc..bb35b37 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -340,6 +340,11 @@
                 getOutPrintWriter().println("Profile uses unified challenge");
                 return false;
             }
+            if (mOld.isEmpty()) {
+                getOutPrintWriter().println(
+                        "User has a lock credential, but old credential was not provided");
+                return false;
+            }
 
             try {
                 final boolean result = mLockPatternUtils.checkCredential(getOldCredential(),
diff --git a/services/core/java/com/android/server/net/Android.bp b/services/core/java/com/android/server/net/Android.bp
index 3ac2d23..68dc781 100644
--- a/services/core/java/com/android/server/net/Android.bp
+++ b/services/core/java/com/android/server/net/Android.bp
@@ -9,3 +9,10 @@
     name: "net_flags_lib",
     aconfig_declarations: "net_flags",
 }
+
+java_aconfig_library {
+    name: "net_flags_host_lib",
+    aconfig_declarations: "net_flags",
+    host_supported: true,
+    mode: "test",
+}
diff --git a/services/core/java/com/android/server/net/NetworkManagementService.java b/services/core/java/com/android/server/net/NetworkManagementService.java
index 5ea3e70..74f0d9c 100644
--- a/services/core/java/com/android/server/net/NetworkManagementService.java
+++ b/services/core/java/com/android/server/net/NetworkManagementService.java
@@ -81,8 +81,6 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.HexDump;
-import com.android.modules.utils.build.SdkLevel;
-import com.android.net.module.util.NetdUtils;
 import com.android.net.module.util.PermissionUtils;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -833,144 +831,6 @@
     }
 
     @Override
-    public boolean getIpForwardingEnabled() throws IllegalStateException{
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException(
-                    "NMS#getIpForwardingEnabled not supported in V+");
-        }
-        try {
-            return mNetdService.ipfwdEnabled();
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void setIpForwardingEnabled(boolean enable) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException(
-                    "NMS#setIpForwardingEnabled not supported in V+");
-        }        try {
-            if (enable) {
-                mNetdService.ipfwdEnableForwarding("tethering");
-            } else {
-                mNetdService.ipfwdDisableForwarding("tethering");
-            }
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void startTethering(String[] dhcpRange) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException("NMS#startTethering not supported in V+");
-        }
-        try {
-            NetdUtils.tetherStart(mNetdService, true /* usingLegacyDnsProxy */, dhcpRange);
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void stopTethering() {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException("NMS#stopTethering not supported in V+");
-        }
-        try {
-            mNetdService.tetherStop();
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public boolean isTetheringStarted() {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException("NMS#isTetheringStarted not supported in V+");
-        }
-        try {
-            return mNetdService.tetherIsEnabled();
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void tetherInterface(String iface) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException("NMS#tetherInterface not supported in V+");
-        }
-        try {
-            final LinkAddress addr = getInterfaceConfig(iface).getLinkAddress();
-            final IpPrefix dest = new IpPrefix(addr.getAddress(), addr.getPrefixLength());
-            NetdUtils.tetherInterface(mNetdService, iface, dest);
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void untetherInterface(String iface) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException("NMS#untetherInterface not supported in V+");
-        }
-        try {
-            NetdUtils.untetherInterface(mNetdService, iface);
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public String[] listTetheredInterfaces() {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException(
-                    "NMS#listTetheredInterfaces not supported in V+");
-        }
-        try {
-            return mNetdService.tetherInterfaceList();
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void enableNat(String internalInterface, String externalInterface) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException("NMS#enableNat not supported in V+");
-        }
-        try {
-            mNetdService.tetherAddForward(internalInterface, externalInterface);
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
-    public void disableNat(String internalInterface, String externalInterface) {
-        PermissionUtils.enforceNetworkStackPermission(mContext);
-        if (SdkLevel.isAtLeastV()) {
-            throw new UnsupportedOperationException("NMS#disableNat not supported in V+");
-        }
-        try {
-            mNetdService.tetherRemoveForward(internalInterface, externalInterface);
-        } catch (RemoteException | ServiceSpecificException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    @Override
     public void setInterfaceQuota(String iface, long quotaBytes) {
         PermissionUtils.enforceNetworkStackPermission(mContext);
 
@@ -1126,30 +986,19 @@
             }
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setDataSaverModeEnabled");
             try {
-                if (SdkLevel.isAtLeastV()) {
-                    // setDataSaverEnabled throws if it fails to set data saver.
-                    mContext.getSystemService(ConnectivityManager.class)
-                            .setDataSaverEnabled(enable);
-                    mDataSaverMode = enable;
-                    if (mUseMeteredFirewallChains) {
-                        // Copy mDataSaverMode state to FIREWALL_CHAIN_METERED_ALLOW
-                        // until ConnectivityService allows manipulation of the data saver mode via
-                        // FIREWALL_CHAIN_METERED_ALLOW.
-                        synchronized (mRulesLock) {
-                            mFirewallChainStates.put(FIREWALL_CHAIN_METERED_ALLOW, enable);
-                        }
+                // setDataSaverEnabled throws if it fails to set data saver.
+                mContext.getSystemService(ConnectivityManager.class).setDataSaverEnabled(enable);
+                mDataSaverMode = enable;
+                if (mUseMeteredFirewallChains) {
+                    // Copy mDataSaverMode state to FIREWALL_CHAIN_METERED_ALLOW
+                    // until ConnectivityService allows manipulation of the data saver mode via
+                    // FIREWALL_CHAIN_METERED_ALLOW.
+                    synchronized (mRulesLock) {
+                        mFirewallChainStates.put(FIREWALL_CHAIN_METERED_ALLOW, enable);
                     }
-                    return true;
-                } else {
-                    final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
-                    if (changed) {
-                        mDataSaverMode = enable;
-                    } else {
-                        Log.e(TAG, "setDataSaverMode(" + enable + "): failed to set iptables");
-                    }
-                    return changed;
                 }
-            } catch (RemoteException | IllegalStateException e) {
+                return true;
+            } catch (IllegalStateException e) {
                 Log.e(TAG, "setDataSaverMode(" + enable + "): failed with exception", e);
                 return false;
             } finally {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 079f338..7d2915b 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -530,6 +530,12 @@
      */
     private boolean mUseDifferentDelaysForBackgroundChain;
 
+    /**
+     * Core uids and apps without the internet permission will not have any firewall rules applied
+     * to them.
+     */
+    private boolean mNeverApplyRulesToCoreUids;
+
     // See main javadoc for instructions on how to use these locks.
     final Object mUidRulesFirstLock = new Object();
     final Object mNetworkPoliciesSecondLock = new Object();
@@ -622,16 +628,6 @@
 
     @GuardedBy("mUidRulesFirstLock")
     final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray();
-    @GuardedBy("mUidRulesFirstLock")
-    final SparseIntArray mUidFirewallDozableRules = new SparseIntArray();
-    @GuardedBy("mUidRulesFirstLock")
-    final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
-    @GuardedBy("mUidRulesFirstLock")
-    final SparseIntArray mUidFirewallBackgroundRules = new SparseIntArray();
-    @GuardedBy("mUidRulesFirstLock")
-    final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray();
-    @GuardedBy("mUidRulesFirstLock")
-    final SparseIntArray mUidFirewallLowPowerStandbyModeRules = new SparseIntArray();
 
     /** Set of states for the child firewall chains. True if the chain is active. */
     @GuardedBy("mUidRulesFirstLock")
@@ -770,7 +766,8 @@
 
     /** List of apps indexed by uid and whether they have the internet permission */
     @GuardedBy("mUidRulesFirstLock")
-    private final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
+    @VisibleForTesting
+    final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
 
     /**
      * Map of uid -> UidStateCallbackInfo objects holding the data received from
@@ -1048,6 +1045,7 @@
 
             mUseMeteredFirewallChains = Flags.useMeteredFirewallChains();
             mUseDifferentDelaysForBackgroundChain = Flags.useDifferentDelaysForBackgroundChain();
+            mNeverApplyRulesToCoreUids = Flags.neverApplyRulesToCoreUids();
 
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
@@ -4098,6 +4096,8 @@
                         + mUseMeteredFirewallChains);
                 fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": "
                         + mUseDifferentDelaysForBackgroundChain);
+                fout.println(Flags.FLAG_NEVER_APPLY_RULES_TO_CORE_UIDS + ": "
+                        + mNeverApplyRulesToCoreUids);
 
                 fout.println();
                 fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode);
@@ -4118,7 +4118,7 @@
                 fout.increaseIndent();
                 for (int i = 0; i < mSubscriptionPlans.size(); i++) {
                     final int subId = mSubscriptionPlans.keyAt(i);
-                    fout.println("Subscriber ID " + subId + ":");
+                    fout.println("Subscription ID " + subId + ":");
                     fout.increaseIndent();
                     final SubscriptionPlan[] plans = mSubscriptionPlans.valueAt(i);
                     if (!ArrayUtils.isEmpty(plans)) {
@@ -4589,7 +4589,7 @@
     @VisibleForTesting
     @GuardedBy("mUidRulesFirstLock")
     void updateRestrictedModeAllowlistUL() {
-        mUidFirewallRestrictedModeRules.clear();
+        final SparseIntArray uidRules = new SparseIntArray();
         forEachUid("updateRestrictedModeAllowlist", uid -> {
             synchronized (mUidRulesFirstLock) {
                 final int effectiveBlockedReasons = updateBlockedReasonsForRestrictedModeUL(
@@ -4599,13 +4599,13 @@
                 // setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add
                 // non-default rules.
                 if (newFirewallRule != FIREWALL_RULE_DEFAULT) {
-                    mUidFirewallRestrictedModeRules.append(uid, newFirewallRule);
+                    uidRules.append(uid, newFirewallRule);
                 }
             }
         });
         if (mRestrictedNetworkingMode) {
             // firewall rules only need to be set when this mode is being enabled.
-            setUidFirewallRulesUL(FIREWALL_CHAIN_RESTRICTED, mUidFirewallRestrictedModeRules);
+            setUidFirewallRulesUL(FIREWALL_CHAIN_RESTRICTED, uidRules);
         }
         enableFirewallChainUL(FIREWALL_CHAIN_RESTRICTED, mRestrictedNetworkingMode);
     }
@@ -4689,8 +4689,7 @@
     void updateRulesForPowerSaveUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForPowerSaveUL");
         try {
-            updateRulesForAllowlistedPowerSaveUL(mRestrictPower, FIREWALL_CHAIN_POWERSAVE,
-                    mUidFirewallPowerSaveRules);
+            updateRulesForAllowlistedPowerSaveUL(mRestrictPower, FIREWALL_CHAIN_POWERSAVE);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
@@ -4705,8 +4704,7 @@
     void updateRulesForDeviceIdleUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForDeviceIdleUL");
         try {
-            updateRulesForAllowlistedPowerSaveUL(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE,
-                    mUidFirewallDozableRules);
+            updateRulesForAllowlistedPowerSaveUL(mDeviceIdleMode, FIREWALL_CHAIN_DOZABLE);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
@@ -4720,13 +4718,11 @@
     // NOTE: since both fw_dozable and fw_powersave uses the same map
     // (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method.
     @GuardedBy("mUidRulesFirstLock")
-    private void updateRulesForAllowlistedPowerSaveUL(boolean enabled, int chain,
-            SparseIntArray rules) {
+    private void updateRulesForAllowlistedPowerSaveUL(boolean enabled, int chain) {
         if (enabled) {
             // Sync the allowlists before enabling the chain.  We don't care about the rules if
             // we are disabling the chain.
-            final SparseIntArray uidRules = rules;
-            uidRules.clear();
+            final SparseIntArray uidRules = new SparseIntArray();
             final List<UserInfo> users = mUserManager.getUsers();
             for (int ui = users.size() - 1; ui >= 0; ui--) {
                 UserInfo user = users.get(ui);
@@ -4755,9 +4751,7 @@
     private void updateRulesForBackgroundChainUL() {
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForBackgroundChainUL");
         try {
-            final SparseIntArray uidRules = mUidFirewallBackgroundRules;
-            uidRules.clear();
-
+            final SparseIntArray uidRules = new SparseIntArray();
             final List<UserInfo> users = mUserManager.getUsers();
             for (int ui = users.size() - 1; ui >= 0; ui--) {
                 final UserInfo user = users.get(ui);
@@ -4794,17 +4788,17 @@
         Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForLowPowerStandbyUL");
         try {
             if (mLowPowerStandbyActive) {
-                mUidFirewallLowPowerStandbyModeRules.clear();
+                final SparseIntArray uidRules = new SparseIntArray();
                 for (int i = mUidState.size() - 1; i >= 0; i--) {
                     final int uid = mUidState.keyAt(i);
                     final int effectiveBlockedReasons = getEffectiveBlockedReasons(uid);
                     if (hasInternetPermissionUL(uid) && (effectiveBlockedReasons
                                     & BLOCKED_REASON_LOW_POWER_STANDBY) == 0) {
-                        mUidFirewallLowPowerStandbyModeRules.put(uid, FIREWALL_RULE_ALLOW);
+                        uidRules.put(uid, FIREWALL_RULE_ALLOW);
                     }
                 }
                 setUidFirewallRulesUL(FIREWALL_CHAIN_LOW_POWER_STANDBY,
-                        mUidFirewallLowPowerStandbyModeRules, CHAIN_TOGGLE_ENABLE);
+                        uidRules, CHAIN_TOGGLE_ENABLE);
             } else {
                 setUidFirewallRulesUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, null, CHAIN_TOGGLE_DISABLE);
             }
@@ -4822,10 +4816,8 @@
         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 {
-            mUidFirewallLowPowerStandbyModeRules.delete(uid);
             setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT);
         }
     }
@@ -4896,6 +4888,12 @@
                 int[] idleUids = mUsageStats.getIdleUidsForUser(user.id);
                 for (int uid : idleUids) {
                     if (!mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid), false)) {
+                        if (mNeverApplyRulesToCoreUids && !isUidValidForRulesUL(uid)) {
+                            // This check is needed to keep mUidFirewallStandbyRules free of any
+                            // such uids. Doing this keeps it in sync with the actual rules applied
+                            // in the underlying connectivity stack.
+                            continue;
+                        }
                         // quick check: if this uid doesn't have INTERNET permission, it
                         // doesn't have network access anyway, so it is a waste to mess
                         // with it here.
@@ -5198,6 +5196,11 @@
 
     @GuardedBy("mUidRulesFirstLock")
     private boolean isUidValidForAllowlistRulesUL(int uid) {
+        return isUidValidForRulesUL(uid);
+    }
+
+    @GuardedBy("mUidRulesFirstLock")
+    private boolean isUidValidForRulesUL(int uid) {
         return UserHandle.isApp(uid) && hasInternetPermissionUL(uid);
     }
 
@@ -5313,16 +5316,11 @@
         mActivityManagerInternal.onUidBlockedReasonsChanged(uid, BLOCKED_REASON_NONE);
         mUidPolicy.delete(uid);
         mUidFirewallStandbyRules.delete(uid);
-        mUidFirewallDozableRules.delete(uid);
-        mUidFirewallPowerSaveRules.delete(uid);
-        mUidFirewallBackgroundRules.delete(uid);
         mBackgroundTransitioningUids.delete(uid);
         mPowerSaveWhitelistExceptIdleAppIds.delete(uid);
         mPowerSaveWhitelistAppIds.delete(uid);
         mPowerSaveTempWhitelistAppIds.delete(uid);
         mAppIdleTempWhitelistAppIds.delete(uid);
-        mUidFirewallRestrictedModeRules.delete(uid);
-        mUidFirewallLowPowerStandbyModeRules.delete(uid);
         synchronized (mUidStateCallbackInfos) {
             mUidStateCallbackInfos.remove(uid);
         }
@@ -6217,41 +6215,33 @@
         }
     }
 
-    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.
      */
+    @GuardedBy("mUidRulesFirstLock")
     private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
-        addSdkSandboxUidsIfNeeded(uidRules);
         try {
             int size = uidRules.size();
-            int[] uids = new int[size];
-            int[] rules = new int[size];
+            final IntArray uids = new IntArray(size);
+            final IntArray rules = new IntArray(size);
             for(int index = size - 1; index >= 0; --index) {
-                uids[index] = uidRules.keyAt(index);
-                rules[index] = uidRules.valueAt(index);
+                final int uid = uidRules.keyAt(index);
+                if (mNeverApplyRulesToCoreUids && !isUidValidForRulesUL(uid)) {
+                    continue;
+                }
+                uids.add(uid);
+                rules.add(uidRules.valueAt(index));
+                if (Process.isApplicationUid(uid)) {
+                    uids.add(Process.toSdkSandboxUid(uid));
+                    rules.add(uidRules.valueAt(index));
+                }
             }
-            mNetworkManager.setFirewallUidRules(chain, uids, rules);
-            mLogger.firewallRulesChanged(chain, uids, rules);
+            final int[] uidArray = uids.toArray();
+            final int[] ruleArray = rules.toArray();
+            mNetworkManager.setFirewallUidRules(chain, uidArray, ruleArray);
+            mLogger.firewallRulesChanged(chain, uidArray, ruleArray);
         } catch (IllegalStateException e) {
             Log.wtf(TAG, "problem setting firewall uid rules", e);
         } catch (RemoteException e) {
@@ -6264,26 +6254,17 @@
      */
     @GuardedBy("mUidRulesFirstLock")
     private void setUidFirewallRuleUL(int chain, int uid, int rule) {
+        if (mNeverApplyRulesToCoreUids && !isUidValidForRulesUL(uid)) {
+            return;
+        }
         if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
             Trace.traceBegin(Trace.TRACE_TAG_NETWORK,
                     "setUidFirewallRuleUL: " + chain + "/" + uid + "/" + rule);
         }
         try {
-            if (chain == FIREWALL_CHAIN_DOZABLE) {
-                mUidFirewallDozableRules.put(uid, rule);
-            } else if (chain == FIREWALL_CHAIN_STANDBY) {
+            if (chain == FIREWALL_CHAIN_STANDBY) {
                 mUidFirewallStandbyRules.put(uid, rule);
-            } else if (chain == FIREWALL_CHAIN_POWERSAVE) {
-                mUidFirewallPowerSaveRules.put(uid, rule);
-            } else if (chain == FIREWALL_CHAIN_RESTRICTED) {
-                mUidFirewallRestrictedModeRules.put(uid, rule);
-            } else if (chain == FIREWALL_CHAIN_LOW_POWER_STANDBY) {
-                mUidFirewallLowPowerStandbyModeRules.put(uid, rule);
-            } else if (chain == FIREWALL_CHAIN_BACKGROUND) {
-                mUidFirewallBackgroundRules.put(uid, rule);
             }
-            // Note that we do not need keep a separate cache of uid rules for chains that we do
-            // not call #setUidFirewallRulesUL for.
 
             try {
                 mNetworkManager.setFirewallUidRule(chain, uid, rule);
@@ -6328,6 +6309,8 @@
      * Resets all firewall rules associated with an UID.
      */
     private void resetUidFirewallRules(int uid) {
+        // Resetting rules for uids with isUidValidForRulesUL = false should be OK as no rules
+        // should be previously set and the downstream code will skip no-op changes.
         try {
             mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid,
                     FIREWALL_RULE_DEFAULT);
diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig
index 586baf0..7f04e66 100644
--- a/services/core/java/com/android/server/net/flags.aconfig
+++ b/services/core/java/com/android/server/net/flags.aconfig
@@ -27,3 +27,13 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "never_apply_rules_to_core_uids"
+    namespace: "backstage_power"
+    description: "Removes all rule bookkeeping and evaluation logic for core uids and uids without the internet permission"
+    bug: "356956588"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/net/watchlist/OWNERS b/services/core/java/com/android/server/net/watchlist/OWNERS
index d0c4e55..eef1e46 100644
--- a/services/core/java/com/android/server/net/watchlist/OWNERS
+++ b/services/core/java/com/android/server/net/watchlist/OWNERS
@@ -1,2 +1 @@
[email protected]
 [email protected]
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index ee755dd..8c1eab9 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -156,7 +156,8 @@
             UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
             UserManager.DISALLOW_SIM_GLOBALLY,
             UserManager.DISALLOW_ASSIST_CONTENT,
-            UserManager.DISALLOW_THREAD_NETWORK
+            UserManager.DISALLOW_THREAD_NETWORK,
+            UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
     });
 
     public static final Set<String> DEPRECATED_USER_RESTRICTIONS = Sets.newArraySet(
@@ -208,7 +209,8 @@
             UserManager.DISALLOW_CELLULAR_2G,
             UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
             UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
-            UserManager.DISALLOW_THREAD_NETWORK
+            UserManager.DISALLOW_THREAD_NETWORK,
+            UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
     );
 
     /**
@@ -255,8 +257,9 @@
                     UserManager.DISALLOW_CELLULAR_2G,
                     UserManager.DISALLOW_ULTRA_WIDEBAND_RADIO,
                     UserManager.DISALLOW_NEAR_FIELD_COMMUNICATION_RADIO,
-                    UserManager.DISALLOW_THREAD_NETWORK
-    );
+                    UserManager.DISALLOW_THREAD_NETWORK,
+                    UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO
+            );
 
     /**
      * Special user restrictions that profile owner of an organization-owned managed profile can
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index f8c678a..e9407c2 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -135,6 +135,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.KeepForWeakReference;
 import com.android.internal.camera.flags.Flags;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.os.BackgroundThread;
@@ -2006,8 +2007,12 @@
     }
 
     private class CallStateHelper {
-        private OutgoingEmergencyStateCallback mEmergencyStateCallback;
-        private CallStateCallback mCallStateCallback;
+        // TelephonyCallback instances are only weakly referenced when registered, so we need
+        // to ensure these fields are kept during optimization to preserve lifecycle semantics.
+        @KeepForWeakReference
+        private final OutgoingEmergencyStateCallback mEmergencyStateCallback;
+        @KeepForWeakReference
+        private final CallStateCallback mCallStateCallback;
 
         private boolean mIsInEmergencyCall;
         private boolean mMicUnmutedForEmergencyCall;
diff --git a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
index aa2b74e..58c3ba5 100644
--- a/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessor.java
@@ -56,12 +56,17 @@
         // enables immediate failover to a secondary provider, one that might provide valid IDs for
         // the same location, which should provide better behavior than just ignoring the event.
         if (hasInvalidZones(event)) {
-            TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder(
-                    event.getTimeZoneProviderStatus())
-                    .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
-                    .build();
-            return TimeZoneProviderEvent.createUncertainEvent(
-                    event.getCreationElapsedMillis(), providerStatus);
+            TimeZoneProviderStatus providerStatus = event.getTimeZoneProviderStatus();
+            TimeZoneProviderStatus.Builder providerStatusBuilder;
+            if (providerStatus != null) {
+                providerStatusBuilder = new TimeZoneProviderStatus.Builder(providerStatus);
+            } else {
+                providerStatusBuilder = new TimeZoneProviderStatus.Builder();
+            }
+            return TimeZoneProviderEvent.createUncertainEvent(event.getCreationElapsedMillis(),
+                    providerStatusBuilder
+                            .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
+                            .build());
         }
 
         return event;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 67c2b28..ccb6ba7 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -367,6 +367,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.KeepForWeakReference;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.content.ReferrerIntent;
@@ -938,6 +939,8 @@
 
     private RemoteCallbackList<IScreenCaptureObserver> mCaptureCallbacks;
 
+    // Ensure the field is kept during optimization to preserve downstream weak refs.
+    @KeepForWeakReference
     private final ColorDisplayService.ColorTransformController mColorTransformController =
             (matrix, translation) -> mWmService.mH.post(() -> {
                 synchronized (mWmService.mGlobalLock) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1353ff0..a310992 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2291,7 +2291,7 @@
         // Apply crop to root tasks only and clear the crops of the descendant tasks.
         int width = 0;
         int height = 0;
-        if (isRootTask()) {
+        if (isRootTask() && !mTransitionController.mIsWaitingForDisplayEnabled) {
             final Rect taskBounds = getBounds();
             width = taskBounds.width();
             height = taskBounds.height();
diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS
index 02cba21..3a83b46 100644
--- a/services/core/jni/OWNERS
+++ b/services/core/jni/OWNERS
@@ -30,7 +30,8 @@
 per-file com_android_server_security_* = file:/core/java/android/security/OWNERS
 per-file com_android_server_tv_* = file:/media/java/android/media/tv/OWNERS
 per-file com_android_server_vibrator_* = file:/services/core/java/com/android/server/vibrator/OWNERS
-per-file com_android_server_am_CachedAppOptimizer.cpp = [email protected], [email protected], [email protected], [email protected], [email protected]
+per-file com_android_server_am_CachedAppOptimizer.cpp = file:/PERFORMANCE_OWNERS
+per-file com_android_server_am_Freezer.cpp = file:/PERFORMANCE_OWNERS
 per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS
 
 # Memory
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1c22087..6e65a74 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1115,9 +1115,9 @@
         }
 
         @Override
-        public void onUserUnlocked(@NonNull TargetUser user) {
-            if (user.isPreCreated()) return;
-            mService.handleOnUserUnlocked(user.getUserIdentifier());
+        public void onUserSwitching(@NonNull TargetUser from, @NonNull TargetUser to) {
+            if (to.isPreCreated()) return;
+            mService.handleOnUserSwitching(from.getUserIdentifier(), to.getUserIdentifier());
         }
     }
 
@@ -3680,8 +3680,8 @@
         mDevicePolicyEngine.handleUnlockUser(userId);
     }
 
-    void handleOnUserUnlocked(int userId) {
-        showNewUserDisclaimerIfNecessary(userId);
+    void handleOnUserSwitching(int fromUserId, int toUserId) {
+        showNewUserDisclaimerIfNecessary(toUserId);
     }
 
     void handleStopUser(int userId) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
index 1000bfa..dbd60c5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/EnterpriseSpecificIdCalculator.java
@@ -17,6 +17,7 @@
 package com.android.server.devicepolicy;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.content.pm.VerifierDeviceIdentity;
 import android.net.wifi.WifiManager;
 import android.os.Build;
@@ -66,13 +67,14 @@
         mMeid = meid;
         mSerialNumber = Build.getSerial();
         WifiManager wifiManager = context.getSystemService(WifiManager.class);
-        Preconditions.checkState(wifiManager != null, "Unable to access WiFi service");
-        final String[] macAddresses = wifiManager.getFactoryMacAddresses();
-        if (macAddresses == null || macAddresses.length == 0) {
-            mMacAddress = "";
-        } else {
-            mMacAddress = macAddresses[0];
+        String macAddress = "";
+        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+            final String[] macAddresses = wifiManager.getFactoryMacAddresses();
+            if (macAddresses != null && macAddresses.length > 0) {
+                macAddress = macAddresses[0];
+            }
         }
+        mMacAddress = macAddress;
     }
 
     private static String getPaddedTruncatedString(String input, int maxLength) {
diff --git a/services/fakes/Android.bp b/services/fakes/Android.bp
index 148054b..d44bb5a 100644
--- a/services/fakes/Android.bp
+++ b/services/fakes/Android.bp
@@ -16,5 +16,5 @@
         "java/**/*.java",
     ],
     path: "java",
-    visibility: ["//frameworks/base"],
+    visibility: ["//frameworks/base/ravenwood:__subpackages__"],
 }
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 3ed6ad7..dab3978 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -354,6 +354,9 @@
 
     private static void createAndUploadReport(ProfcollectForwardingService pfs) {
         BackgroundThread.get().getThreadHandler().post(() -> {
+            if (pfs.mIProfcollect == null) {
+                return;
+            }
             String reportName;
             try {
                 reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip";
@@ -398,17 +401,19 @@
                 if (randomNum >= traceFrequency) {
                     return;
                 }
-                // For a small percentage a traces, we collect the initialization behavior.
-                boolean traceInitialization = ThreadLocalRandom.current().nextInt(10) < 1;
-                int traceDelay = traceInitialization ? 0 : 1000;
-                String traceTag = traceInitialization ? "camera_init" : "camera";
-                BackgroundThread.get().getThreadHandler().postDelayed(() -> {
+                final int traceDuration = 5000;
+                final String traceTag = "camera";
+                BackgroundThread.get().getThreadHandler().post(() -> {
+                    if (mIProfcollect == null) {
+                        return;
+                    }
                     try {
-                        mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider");
+                        mIProfcollect.trace_process(traceTag, "android.hardware.camera.provider",
+                                traceDuration);
                     } catch (RemoteException e) {
                         Log.e(LOG_TAG, "Failed to initiate trace: " + e.getMessage());
                     }
-                }, traceDelay);
+                });
             }
         }, null);
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index adcbf5c..194bf4b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -442,20 +442,24 @@
                 IMPORTANCE_FOREGROUND_SERVICE,       // importance
                 null);                               // description
 
-        // Case 4: Create a process from another package with kill from lmkd
+        /*
+         * Case 4: Create a process from another package with kill from lmkd
+         * We expect LMKD's reported RSS to be the process' last seen RSS.
+         */
         final int app2UidUser2 = 1010234;
         final int app2PidUser2 = 12348;
         final long app2Pss1 = 54321;
         final long app2Rss1 = 65432;
+        final long lmkd_reported_rss = 43215;
         final String app2ProcessName = "com.android.test.stub2:process";
         final String app2PackageName = "com.android.test.stub2";
 
         sleep(1);
         final long now4 = System.currentTimeMillis();
-        doReturn(new Pair<Long, Object>(now4, Integer.valueOf(0)))
+        doReturn(null)
                 .when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
                 .remove(anyInt(), anyInt());
-        doReturn(new Pair<Long, Object>(now4, null))
+        doReturn(new Pair<Long, Object>(now4, Long.valueOf(lmkd_reported_rss)))
                 .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd)
                 .remove(anyInt(), anyInt());
 
@@ -490,7 +494,7 @@
                 null,                                     // subReason
                 0,                                        // status
                 app2Pss1,                                 // pss
-                app2Rss1,                                 // rss
+                lmkd_reported_rss,                        // rss
                 IMPORTANCE_CACHED,                        // importance
                 null);                                    // description
 
@@ -499,6 +503,11 @@
         mAppExitInfoTracker.getExitInfo(null, app2UidUser2, 0, 0, list);
         assertEquals(1, list.size());
 
+        info = list.get(0);
+
+        // Verify the AppExitInfo has the LMKD reported RSS
+        assertEquals(lmkd_reported_rss, info.getRss());
+
         // Case 5: App native crash
         final int app3UidUser2 = 1010345;
         final int app3PidUser2 = 12349;
@@ -599,7 +608,7 @@
                 null,                                     // subReason
                 0,                                        // status
                 app2Pss1,                                 // pss
-                app2Rss1,                                 // rss
+                lmkd_reported_rss,                        // rss
                 IMPORTANCE_CACHED,                        // importance
                 null);                                    // description
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
index 2cbc226..4fac647 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS
@@ -1,3 +1,4 @@
 include /services/core/java/com/android/server/am/OWNERS
 
 per-file ApplicationStartInfoTest.java = [email protected], [email protected], [email protected]
+per-file CachedAppOptimizerTest.java = file:/PERFORMANCE_OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 67ae998..a4e636c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -26,6 +26,7 @@
 import static com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_WAKE_UP_MESSAGE;
+import static com.android.server.hdmi.HdmiControlService.WAKE_UP_SCREEN_ON;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -1878,4 +1879,61 @@
 
         assertThat(mPowerManager.isInteractive()).isTrue();
     }
+
+    @Test
+    public void handleReportPhysicalAddress_DeviceDiscoveryActionInProgress_noNewDeviceAction() {
+        mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
+        mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
+        mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage reportPhysicalAddressFromPlayback1 =
+                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                        ADDR_PLAYBACK_1, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
+        HdmiCecMessage reportPhysicalAddressFromPlayback2 =
+                HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+                        ADDR_PLAYBACK_2, 0x2000, HdmiDeviceInfo.DEVICE_PLAYBACK);
+        HdmiCecMessage giveOsdName = HdmiCecMessageBuilder.buildGiveOsdNameCommand(
+                ADDR_TV, ADDR_PLAYBACK_2);
+        // Skip state waiting for <Report Physical Address> for DeviceDiscoveryAction s.t. message
+        // can be dispatched to local device TV.
+        mNativeWrapper.onCecMessage(reportPhysicalAddressFromPlayback1);
+        mNativeWrapper.clearResultMessages();
+        mTestLooper.dispatchAll();
+
+        mNativeWrapper.onCecMessage(reportPhysicalAddressFromPlayback2);
+        mTestLooper.dispatchAll();
+
+        // NewDeviceAction did not start and <Give OSD Name> was not sent.
+        assertThat(mNativeWrapper.getResultMessages()).doesNotContain(giveOsdName);
+    }
+
+    @Test
+    public void onOneTouchPlay_wakeUp_addCecDevice() {
+        assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+                .isEmpty();
+        mHdmiCecLocalDeviceTv.mService.getHdmiCecConfig().setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
+                HdmiControlManager.TV_WAKE_ON_ONE_TOUCH_PLAY_ENABLED);
+        mPowerManager.setInteractive(false);
+        mTestLooper.dispatchAll();
+
+        HdmiCecMessage textViewOn = HdmiCecMessageBuilder.buildTextViewOn(ADDR_PLAYBACK_1,
+                mTvLogicalAddress);
+        HdmiCecMessage activeSource = HdmiCecMessageBuilder.buildActiveSource(ADDR_PLAYBACK_1,
+                0x1000);
+        assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(textViewOn)).isEqualTo(Constants.HANDLED);
+        assertThat(mHdmiCecLocalDeviceTv.dispatchMessage(activeSource)).isEqualTo(
+                Constants.HANDLED);
+        mTestLooper.dispatchAll();
+        assertThat(mPowerManager.isInteractive()).isTrue();
+
+        // FakePowerManagerWrapper#wakeUp() doesn't broadcast Intent.ACTION_SCREEN_ON so we have to
+        // manually call this method.
+        mHdmiControlService.onWakeUp(WAKE_UP_SCREEN_ON);
+        mTestLooper.dispatchAll();
+        assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+                .hasSize(1);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 0aa72d0..ce7dbda 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -239,6 +239,9 @@
     public void isValid_setAnalogueTimer_clearAnalogueTimer() {
         assertMessageValidity("04:33:0C:08:10:1E:04:30:08:00:13:AD:06").isEqualTo(OK);
         assertMessageValidity("04:34:04:0C:16:0F:08:37:00:02:EA:60:03:34").isEqualTo(OK);
+        // Allow [Recording Sequence] set multiple days of the week.
+        // e.g. Monday (0x02) | Tuesday (0x04) -> Monday or Tuesday (0x06)
+        assertMessageValidity("04:34:04:0C:16:0F:08:37:06:02:EA:60:03:34").isEqualTo(OK);
 
         assertMessageValidity("0F:33:0C:08:10:1E:04:30:08:00:13:AD:06")
                 .isEqualTo(ERROR_DESTINATION);
@@ -307,7 +310,7 @@
         // Invalid Recording Sequence
         assertMessageValidity("04:99:12:06:0C:2D:5A:19:90:91:04:00:B1").isEqualTo(ERROR_PARAMETER);
         // Invalid Recording Sequence
-        assertMessageValidity("04:97:0C:08:15:05:04:1E:21:00:C4:C2:11:D8:75:30")
+        assertMessageValidity("04:97:0C:08:15:05:04:1E:A1:00:C4:C2:11:D8:75:30")
                 .isEqualTo(ERROR_PARAMETER);
 
         // Invalid Digital Broadcast System
@@ -353,7 +356,7 @@
         // Invalid Recording Sequence
         assertMessageValidity("40:A2:14:09:12:28:4B:19:84:05:10:00").isEqualTo(ERROR_PARAMETER);
         // Invalid Recording Sequence
-        assertMessageValidity("40:A1:0C:08:15:05:04:1E:14:04:20").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:A1:0C:08:15:05:04:1E:94:04:20").isEqualTo(ERROR_PARAMETER);
         // Invalid external source specifier
         assertMessageValidity("40:A2:14:09:12:28:4B:19:10:08:10:00").isEqualTo(ERROR_PARAMETER);
         // Invalid External PLug
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 3d68849..dddab65 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -108,6 +108,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isA;
 import static org.mockito.Mockito.CALLS_REAL_METHODS;
@@ -165,6 +166,7 @@
 import android.os.PowerManager;
 import android.os.PowerManagerInternal;
 import android.os.PowerSaveState;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SimpleClock;
 import android.os.SystemClock;
@@ -197,6 +199,7 @@
 import androidx.test.filters.MediumTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent;
 import com.android.internal.util.test.FsUtil;
@@ -2310,6 +2313,70 @@
         assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
     }
 
+    @SuppressWarnings("GuardedBy") // For not holding mUidRulesFirstLock
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_NEVER_APPLY_RULES_TO_CORE_UIDS)
+    public void testRulesNeverAppliedToCoreUids() throws Exception {
+        clearInvocations(mNetworkManager);
+
+        final int coreAppId = Process.FIRST_APPLICATION_UID - 102;
+        final int coreUid = UserHandle.getUid(USER_ID, coreAppId);
+
+        // Enable all restrictions and add this core uid to all allowlists.
+        mService.mDeviceIdleMode = true;
+        mService.mRestrictPower = true;
+        setRestrictBackground(true);
+        expectHasUseRestrictedNetworksPermission(coreUid, true);
+        enableRestrictedMode(true);
+        final NetworkPolicyManagerInternal internal = LocalServices.getService(
+                NetworkPolicyManagerInternal.class);
+        internal.setLowPowerStandbyActive(true);
+        internal.setLowPowerStandbyAllowlist(new int[]{coreUid});
+        internal.onTempPowerSaveWhitelistChange(coreAppId, true, REASON_OTHER, "testing");
+
+        when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean()))
+                .thenReturn(new int[]{coreAppId});
+        mPowerAllowlistReceiver.onReceive(mServiceContext, null);
+
+        // A normal uid would undergo a rule change from denied to allowed on all chains, but we
+        // should not request any rule change for this core uid.
+        verify(mNetworkManager, never()).setFirewallUidRule(anyInt(), eq(coreUid), anyInt());
+        verify(mNetworkManager, never()).setFirewallUidRules(anyInt(),
+                argThat(ar -> ArrayUtils.contains(ar, coreUid)), any(int[].class));
+    }
+
+    @SuppressWarnings("GuardedBy") // For not holding mUidRulesFirstLock
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_NEVER_APPLY_RULES_TO_CORE_UIDS)
+    public void testRulesNeverAppliedToUidsWithoutInternetPermission() throws Exception {
+        clearInvocations(mNetworkManager);
+
+        mService.mInternetPermissionMap.clear();
+        expectHasInternetPermission(UID_A, false);
+
+        // Enable all restrictions and add this uid to all allowlists.
+        mService.mDeviceIdleMode = true;
+        mService.mRestrictPower = true;
+        setRestrictBackground(true);
+        expectHasUseRestrictedNetworksPermission(UID_A, true);
+        enableRestrictedMode(true);
+        final NetworkPolicyManagerInternal internal = LocalServices.getService(
+                NetworkPolicyManagerInternal.class);
+        internal.setLowPowerStandbyActive(true);
+        internal.setLowPowerStandbyAllowlist(new int[]{UID_A});
+        internal.onTempPowerSaveWhitelistChange(APP_ID_A, true, REASON_OTHER, "testing");
+
+        when(mPowerExemptionManager.getAllowListedAppIds(anyBoolean()))
+                .thenReturn(new int[]{APP_ID_A});
+        mPowerAllowlistReceiver.onReceive(mServiceContext, null);
+
+        // A normal uid would undergo a rule change from denied to allowed on all chains, but we
+        // should not request any rule this uid without the INTERNET permission.
+        verify(mNetworkManager, never()).setFirewallUidRule(anyInt(), eq(UID_A), anyInt());
+        verify(mNetworkManager, never()).setFirewallUidRules(anyInt(),
+                argThat(ar -> ArrayUtils.contains(ar, UID_A)), any(int[].class));
+    }
+
     private boolean isUidState(int uid, int procState, int procStateSeq, int capability) {
         final NetworkPolicyManager.UidState uidState = mService.getUidStateForTest(uid);
         if (uidState == null) {
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
index f3440f7..ea3b409 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/location/ZoneInfoDbTimeZoneProviderEventPreProcessorTest.java
@@ -39,13 +39,23 @@
 
     private static final long ARBITRARY_TIME_MILLIS = 11223344;
 
+    private final List<String> mNonExistingTimeZones = Arrays.asList(
+            "SystemV/HST10", "Atlantic/Atlantis", "EUROPE/LONDON", "Etc/GMT-5:30");
     private final ZoneInfoDbTimeZoneProviderEventPreProcessor mPreProcessor =
             new ZoneInfoDbTimeZoneProviderEventPreProcessor();
 
+    private static final TimeZoneProviderStatus ARBITRARY_TIME_ZONE_PROVIDER_STATUS =
+            new TimeZoneProviderStatus.Builder()
+                    .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
+                    .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
+                    .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
+                    .build();
+
     @Test
     public void timeZoneIdsFromZoneInfoDbAreValid() {
         for (String timeZone : TimeZone.getAvailableIDs()) {
-            TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone);
+            TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone,
+                    ARBITRARY_TIME_ZONE_PROVIDER_STATUS);
             assertWithMessage("Time zone %s should be supported", timeZone)
                     .that(mPreProcessor.preProcess(event)).isEqualTo(event);
         }
@@ -53,11 +63,9 @@
 
     @Test
     public void eventWithNonExistingZones_areMappedToUncertainEvent() {
-        List<String> nonExistingTimeZones = Arrays.asList(
-                "SystemV/HST10", "Atlantic/Atlantis", "EUROPE/LONDON", "Etc/GMT-5:30");
-
-        for (String timeZone : nonExistingTimeZones) {
-            TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone);
+        for (String timeZone : mNonExistingTimeZones) {
+            TimeZoneProviderEvent event = timeZoneProviderEvent(timeZone,
+                    ARBITRARY_TIME_ZONE_PROVIDER_STATUS);
 
             TimeZoneProviderStatus expectedProviderStatus =
                     new TimeZoneProviderStatus.Builder(event.getTimeZoneProviderStatus())
@@ -73,14 +81,31 @@
         }
     }
 
-    private static TimeZoneProviderEvent timeZoneProviderEvent(String... timeZoneIds) {
-        TimeZoneProviderStatus providerStatus = new TimeZoneProviderStatus.Builder()
-                .setLocationDetectionDependencyStatus(DEPENDENCY_STATUS_OK)
-                .setConnectivityDependencyStatus(DEPENDENCY_STATUS_OK)
-                .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_OK)
-                .build();
+    @Test
+    public void eventWithNullProviderStatus_areMappedToUncertainEvent() {
+        for (String timeZone : mNonExistingTimeZones) {
+            TimeZoneProviderEvent eventWithNullStatus = timeZoneProviderEvent(timeZone,
+                    /* providerStatus= */ null);
+
+            TimeZoneProviderStatus expectedProviderStatus =
+                    new TimeZoneProviderStatus.Builder()
+                            .setTimeZoneResolutionOperationStatus(OPERATION_STATUS_FAILED)
+                            .build();
+
+            TimeZoneProviderEvent expectedResultEvent =
+                    TimeZoneProviderEvent.createUncertainEvent(
+                            eventWithNullStatus.getCreationElapsedMillis(),
+                            expectedProviderStatus);
+            assertWithMessage(timeZone + " with null time zone provider status")
+                    .that(mPreProcessor.preProcess(eventWithNullStatus))
+                    .isEqualTo(expectedResultEvent);
+        }
+    }
+
+    private static TimeZoneProviderEvent timeZoneProviderEvent(String timeZoneId,
+            TimeZoneProviderStatus providerStatus) {
         TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
-                .setTimeZoneIds(Arrays.asList(timeZoneIds))
+                .setTimeZoneIds(Arrays.asList(timeZoneId))
                 .setElapsedRealtimeMillis(ARBITRARY_TIME_MILLIS)
                 .build();
         return TimeZoneProviderEvent.createSuggestionEvent(
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index e29d321..71f3033 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -47,14 +47,16 @@
     compile_dex: true,
     default_to_stubs: true,
     dist_group: "android",
+
+    // This module cannot generate stubs from the api signature files as stubs depends on the
+    // private APIs, which are not visible in the api signature files.
+    build_from_text_stub: false,
 }
 
 java_library {
     name: "android.test.mock.ravenwood",
+    defaults: ["ravenwood-internal-only-visibility-java"],
     srcs: [":android-test-mock-sources"],
-    visibility: [
-        "//frameworks/base",
-    ],
 }
 
 android_ravenwood_test {
diff --git a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
index 3722fef..c0e90f9 100644
--- a/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/CrashRecoveryTest.java
@@ -35,6 +35,7 @@
 
 import android.Manifest;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.VersionedPackage;
@@ -77,6 +78,7 @@
 import java.io.File;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -413,6 +415,311 @@
         verify(rescuePartyObserver, never()).executeBootLoopMitigation(2);
     }
 
+    @Test
+    @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+    public void testCrashLoopWithRescuePartyAndRollbackObserver() throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+        RollbackPackageHealthObserver rollbackObserver =
+                setUpRollbackPackageHealthObserver(watchdog);
+        VersionedPackage versionedPackageA = new VersionedPackage(APP_A, VERSION_CODE);
+
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+            ApplicationInfo info = new ApplicationInfo();
+            info.flags |= ApplicationInfo.FLAG_PERSISTENT
+                    | ApplicationInfo.FLAG_SYSTEM;
+            return info;
+        });
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: SCOPED_DEVICE_CONFIG_RESET
+        verify(rescuePartyObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: ALL_DEVICE_CONFIG_RESET
+        verify(rescuePartyObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: WARM_REBOOT
+        verify(rescuePartyObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Low impact rollback
+        verify(rollbackObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+
+        // update available rollbacks to mock rollbacks being applied after the call to
+        // rollbackObserver.execute
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+                List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+    public void testCrashLoopWithRescuePartyAndRollbackObserverEnableDeprecateFlagReset()
+            throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+        RollbackPackageHealthObserver rollbackObserver =
+                setUpRollbackPackageHealthObserver(watchdog);
+        VersionedPackage versionedPackageA = new VersionedPackage(APP_A, VERSION_CODE);
+
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+            ApplicationInfo info = new ApplicationInfo();
+            info.flags |= ApplicationInfo.FLAG_PERSISTENT
+                    | ApplicationInfo.FLAG_SYSTEM;
+            return info;
+        });
+
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: WARM_REBOOT
+        verify(rescuePartyObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Low impact rollback
+        verify(rollbackObserver).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        // update available rollbacks to mock rollbacks being applied after the call to
+        // rollbackObserver.execute
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+                List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageA), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // DEFAULT_MAJOR_USER_IMPACT_LEVEL_THRESHOLD reached. No more mitigations applied
+        verify(rescuePartyObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageA,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+    public void testCrashLoopSystemUIWithRescuePartyAndRollbackObserver() throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+        RollbackPackageHealthObserver rollbackObserver =
+                setUpRollbackPackageHealthObserver(watchdog);
+        String systemUi = "com.android.systemui";
+        VersionedPackage versionedPackageUi = new VersionedPackage(
+                systemUi, VERSION_CODE);
+        RollbackInfo rollbackInfoUi = getRollbackInfo(systemUi, VERSION_CODE, 1,
+                PackageManager.ROLLBACK_USER_IMPACT_LOW);
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW,
+                ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL, rollbackInfoUi));
+
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+            ApplicationInfo info = new ApplicationInfo();
+            info.flags |= ApplicationInfo.FLAG_PERSISTENT
+                    | ApplicationInfo.FLAG_SYSTEM;
+            return info;
+        });
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: SCOPED_DEVICE_CONFIG_RESET
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: ALL_DEVICE_CONFIG_RESET
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: WARM_REBOOT
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Low impact rollback
+        verify(rollbackObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        // update available rollbacks to mock rollbacks being applied after the call to
+        // rollbackObserver.execute
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+                List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: RESET_SETTINGS_UNTRUSTED_DEFAULTS
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 4);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 5);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: RESET_SETTINGS_UNTRUSTED_CHANGES
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 5);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 6);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: RESET_SETTINGS_TRUSTED_DEFAULTS
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 6);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 7);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Factory reset. High impact rollbacks are performed only for boot loops.
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 7);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 8);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_DEPRECATE_FLAGS_AND_SETTINGS_RESETS)
+    public void testCrashLoopSystemUIWithRescuePartyAndRollbackObserverEnableDeprecateFlagReset()
+            throws Exception {
+        PackageWatchdog watchdog = createWatchdog();
+        RescuePartyObserver rescuePartyObserver = setUpRescuePartyObserver(watchdog);
+        RollbackPackageHealthObserver rollbackObserver =
+                setUpRollbackPackageHealthObserver(watchdog);
+        String systemUi = "com.android.systemui";
+        VersionedPackage versionedPackageUi = new VersionedPackage(
+                systemUi, VERSION_CODE);
+        RollbackInfo rollbackInfoUi = getRollbackInfo(systemUi, VERSION_CODE, 1,
+                PackageManager.ROLLBACK_USER_IMPACT_LOW);
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(List.of(ROLLBACK_INFO_LOW,
+                ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL, rollbackInfoUi));
+
+        when(mMockPackageManager.getApplicationInfo(anyString(), anyInt())).then(inv -> {
+            ApplicationInfo info = new ApplicationInfo();
+            info.flags |= ApplicationInfo.FLAG_PERSISTENT
+                    | ApplicationInfo.FLAG_SYSTEM;
+            return info;
+        });
+
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: WARM_REBOOT
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Low impact rollback
+        verify(rollbackObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 1);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+
+        // update available rollbacks to mock rollbacks being applied after the call to
+        // rollbackObserver.execute
+        when(mRollbackManager.getAvailableRollbacks()).thenReturn(
+                List.of(ROLLBACK_INFO_HIGH, ROLLBACK_INFO_MANUAL));
+
+        raiseFatalFailureAndDispatch(watchdog,
+                Arrays.asList(versionedPackageUi), PackageWatchdog.FAILURE_REASON_APP_CRASH);
+
+        // Mitigation: Factory reset. High impact rollbacks are performed only for boot loops.
+        verify(rescuePartyObserver).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+        verify(rescuePartyObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 3);
+        verify(rollbackObserver, never()).execute(versionedPackageUi,
+                PackageWatchdog.FAILURE_REASON_APP_CRASH, 2);
+    }
+
     RollbackPackageHealthObserver setUpRollbackPackageHealthObserver(PackageWatchdog watchdog) {
         RollbackPackageHealthObserver rollbackObserver =
                 spy(new RollbackPackageHealthObserver(mSpyContext, mApexManager));
@@ -424,7 +731,6 @@
         watchdog.registerHealthObserver(rollbackObserver);
         return rollbackObserver;
     }
-
     RescuePartyObserver setUpRescuePartyObserver(PackageWatchdog watchdog) {
         setCrashRecoveryPropRescueBootCount(0);
         RescuePartyObserver rescuePartyObserver = spy(RescuePartyObserver.getInstance(mSpyContext));
@@ -686,4 +992,20 @@
         mTestLooper.moveTimeForward(milliSeconds);
         mTestLooper.dispatchAll();
     }
+
+    private void raiseFatalFailureAndDispatch(PackageWatchdog watchdog,
+            List<VersionedPackage> packages, int failureReason) {
+        long triggerFailureCount = watchdog.getTriggerFailureCount();
+        if (failureReason == PackageWatchdog.FAILURE_REASON_EXPLICIT_HEALTH_CHECK
+                || failureReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+            triggerFailureCount = 1;
+        }
+        for (int i = 0; i < triggerFailureCount; i++) {
+            watchdog.onPackageFailure(packages, failureReason);
+        }
+        mTestLooper.dispatchAll();
+        if (Flags.recoverabilityDetection()) {
+            moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS);
+        }
+    }
 }
diff --git a/tools/systemfeatures/OWNERS b/tools/systemfeatures/OWNERS
new file mode 100644
index 0000000..66c8506
--- /dev/null
+++ b/tools/systemfeatures/OWNERS
@@ -0,0 +1 @@
+include /PERFORMANCE_OWNERS
diff --git a/tools/systemfeatures/README.md b/tools/systemfeatures/README.md
new file mode 100644
index 0000000..5836f81
--- /dev/null
+++ b/tools/systemfeatures/README.md
@@ -0,0 +1,11 @@
+# Build-time system feature support
+
+## Overview
+
+System features exposed from `PackageManager` are defined and aggregated as
+`<feature>` xml attributes across various partitions, and are currently queried
+at runtime through the framework. This directory contains tooling that will
+support *build-time* queries of select system features, enabling optimizations
+like code stripping and conditionally dependencies when so configured.
+
+### TODO(b/203143243): Expand readme after landing codegen.