Merge "Background broadcast constants should be print in the dumpsys info." into main
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index e092499..65bc8cc 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -44,6 +44,7 @@
         "apct-perftests-resources-manager-apps",
         "apct-perftests-utils",
         "collector-device-lib",
+        "conscrypt-test-support",
         "compatibility-device-util-axt",
         "junit",
         "junit-params",
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/BufferType.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/BufferType.java
new file mode 100644
index 0000000..bdc2a82
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/BufferType.java
@@ -0,0 +1,48 @@
+/*
+ * 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 java.nio.ByteBuffer;
+import javax.net.ssl.SSLEngine;
+
+/**
+ * Enumeration that provides allocation of direct or heap buffers.
+ */
+@SuppressWarnings("unused")
+public enum BufferType {
+    HEAP {
+        @Override
+        ByteBuffer newBuffer(int size) {
+            return ByteBuffer.allocate(size);
+        }
+    },
+    DIRECT {
+        @Override
+        ByteBuffer newBuffer(int size) {
+            return ByteBuffer.allocateDirect(size);
+        }
+    };
+
+    abstract ByteBuffer newBuffer(int size);
+
+    ByteBuffer newApplicationBuffer(SSLEngine engine) {
+        return newBuffer(engine.getSession().getApplicationBufferSize());
+    }
+
+    ByteBuffer newPacketBuffer(SSLEngine engine) {
+        return newBuffer(engine.getSession().getPacketBufferSize());
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
new file mode 100644
index 0000000..c69ae39
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherEncryptPerfTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import androidx.test.filters.LargeTest;
+
+import org.conscrypt.TestUtils;
+
+import java.nio.ByteBuffer;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Benchmark for comparing cipher encrypt performance.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class CipherEncryptPerfTest {
+
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    public enum BufferType {
+        ARRAY,
+        HEAP_HEAP,
+        HEAP_DIRECT,
+        DIRECT_DIRECT,
+        DIRECT_HEAP
+    }
+
+    private enum MyCipherFactory implements CipherFactory {
+        JDK {
+            @Override
+            public Cipher newCipher(String transformation)
+                throws NoSuchPaddingException, NoSuchAlgorithmException {
+                return Cipher.getInstance(transformation);
+            }
+        },
+        CONSCRYPT {
+            @Override
+            public Cipher newCipher(String transformation)
+                throws NoSuchPaddingException, NoSuchAlgorithmException {
+                return Cipher.getInstance(transformation, TestUtils.getConscryptProvider());
+            }
+        };
+    }
+
+    private class Config {
+        BufferType b_bufferType;
+        CipherFactory c_provider;
+        Transformation a_tx;
+        Config(BufferType bufferType, CipherFactory cipherFactory, Transformation transformation) {
+          b_bufferType = bufferType;
+          c_provider = cipherFactory;
+          a_tx = transformation;
+        }
+        public BufferType bufferType() {
+            return b_bufferType;
+        }
+
+        public CipherFactory cipherFactory() {
+            return c_provider;
+        }
+
+        public Transformation transformation() {
+            return a_tx;
+        }
+    }
+
+    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)},
+        };
+    }
+
+    private EncryptStrategy encryptStrategy;
+
+    @Test
+    @Parameters(method = "getParams")
+    public void encrypt(Config config) throws Exception {
+        switch (config.bufferType()) {
+            case ARRAY:
+                encryptStrategy = new ArrayStrategy(config);
+                break;
+            default:
+                encryptStrategy = new ByteBufferStrategy(config);
+                break;
+        }
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+          encryptStrategy.encrypt();
+        }
+    }
+
+    private static abstract class EncryptStrategy {
+        private final Key key;
+        final Cipher cipher;
+        final int outputSize;
+
+        EncryptStrategy(Config config) throws Exception {
+            Transformation tx = config.transformation();
+            key = tx.newEncryptKey();
+            cipher = config.cipherFactory().newCipher(tx.toFormattedString());
+            initCipher();
+
+            int messageSize = messageSize(tx.toFormattedString());
+            outputSize = cipher.getOutputSize(messageSize);
+        }
+
+        final void initCipher() throws Exception {
+            cipher.init(Cipher.ENCRYPT_MODE, key);
+        }
+
+        final int messageSize(String transformation) throws Exception {
+            Cipher conscryptCipher = Cipher.getInstance(
+                transformation, TestUtils.getConscryptProvider());
+            conscryptCipher.init(Cipher.ENCRYPT_MODE, key);
+            return conscryptCipher.getBlockSize() > 0 ?
+                conscryptCipher.getBlockSize() : 128;
+        }
+
+        final byte[] newMessage() {
+            return TestUtils.newTextMessage(cipher.getBlockSize());
+        }
+
+        abstract int encrypt() throws Exception;
+    }
+
+    private static final class ArrayStrategy extends EncryptStrategy {
+        private final byte[] plainBytes;
+        private final byte[] cipherBytes;
+
+        ArrayStrategy(Config config) throws Exception {
+            super(config);
+
+            plainBytes = newMessage();
+            cipherBytes = new byte[outputSize];
+        }
+
+        @Override
+        int encrypt() throws Exception {
+            initCipher();
+            return cipher.doFinal(plainBytes, 0, plainBytes.length, cipherBytes, 0);
+        }
+    }
+
+    private static final class ByteBufferStrategy extends EncryptStrategy {
+        private final ByteBuffer input;
+        private final ByteBuffer output;
+
+        ByteBufferStrategy(Config config) throws Exception {
+            super(config);
+
+            switch (config.bufferType()) {
+                case HEAP_HEAP:
+                    input = ByteBuffer.wrap(newMessage());
+                    output = ByteBuffer.allocate(outputSize);
+                    break;
+                case HEAP_DIRECT:
+                    input = ByteBuffer.wrap(newMessage());
+                    output = ByteBuffer.allocateDirect(outputSize);
+                    break;
+                case DIRECT_DIRECT:
+                    input = toDirect(newMessage());
+                    output = ByteBuffer.allocateDirect(outputSize);
+                    break;
+                case DIRECT_HEAP:
+                    input = toDirect(newMessage());
+                    output = ByteBuffer.allocate(outputSize);
+                    break;
+                default: {
+                    throw new IllegalStateException(
+                            "Unexpected buffertype: " + config.bufferType());
+                }
+            }
+        }
+
+        @Override
+        int encrypt() throws Exception {
+            initCipher();
+            input.position(0);
+            output.clear();
+            return cipher.doFinal(input, output);
+        }
+
+        private static ByteBuffer toDirect(byte[] data) {
+            ByteBuffer buffer = ByteBuffer.allocateDirect(data.length);
+            buffer.put(data);
+            buffer.flip();
+            return buffer;
+        }
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherFactory.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherFactory.java
new file mode 100644
index 0000000..f8a3d5f
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/CipherFactory.java
@@ -0,0 +1,27 @@
+/*
+ * 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 java.security.NoSuchAlgorithmException;
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+
+/**
+ * Factory for {@link Cipher} instances.
+ */
+public interface CipherFactory {
+  Cipher newCipher(String transformation) throws NoSuchPaddingException, NoSuchAlgorithmException;
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java
new file mode 100644
index 0000000..1a7258a
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java
@@ -0,0 +1,110 @@
+/*
+ * 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 java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.nio.channels.ClosedChannelException;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.conscrypt.ChannelType;
+
+/**
+ * Client-side endpoint. Provides basic services for sending/receiving messages from the client
+ * socket.
+ */
+final class ClientEndpoint {
+    private final SSLSocket socket;
+    private InputStream input;
+    private OutputStream output;
+
+    ClientEndpoint(SSLSocketFactory socketFactory, ChannelType channelType, int port,
+            String[] protocols, String[] ciphers) throws IOException {
+        socket = channelType.newClientSocket(socketFactory, InetAddress.getLoopbackAddress(), port);
+        socket.setEnabledProtocols(protocols);
+        socket.setEnabledCipherSuites(ciphers);
+    }
+
+    void start() {
+        try {
+            socket.startHandshake();
+            input = socket.getInputStream();
+            output = socket.getOutputStream();
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+    void stop() {
+        try {
+            socket.close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    int readMessage(byte[] buffer) {
+        try {
+            int totalBytesRead = 0;
+            while (totalBytesRead < buffer.length) {
+                int remaining = buffer.length - totalBytesRead;
+                int bytesRead = input.read(buffer, totalBytesRead, remaining);
+                if (bytesRead == -1) {
+                    break;
+                }
+                totalBytesRead += bytesRead;
+            }
+            return totalBytesRead;
+        } catch (SSLException e) {
+            if (e.getCause() instanceof EOFException) {
+                return -1;
+            }
+            throw new RuntimeException(e);
+        } catch (ClosedChannelException e) {
+            // Thrown for channel-based sockets. Just treat like EOF.
+            return -1;
+        }  catch (SocketException e) {
+            // The socket was broken. Just treat like EOF.
+            return -1;
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    void sendMessage(byte[] data) {
+        try {
+            output.write(data);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    void flush() {
+        try {
+            output.flush();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
new file mode 100644
index 0000000..dd9f4eb
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import org.conscrypt.ChannelType;
+import org.conscrypt.TestUtils;
+import static org.conscrypt.TestUtils.getCommonProtocolSuites;
+import static org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import androidx.test.filters.LargeTest;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.SocketException;
+import java.security.NoSuchAlgorithmException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import android.conscrypt.ServerEndpoint.MessageProcessor;
+
+/**
+ * Benchmark for comparing performance of server socket implementations.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class ClientSocketPerfTest {
+
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    /**
+     * Provider for the test configuration
+     */
+    private class Config {
+        EndpointFactory a_clientFactory;
+        EndpointFactory b_serverFactory;
+        int c_messageSize;
+        String d_cipher;
+        ChannelType e_channelType;
+        PerfTestProtocol f_protocol;
+        Config(EndpointFactory clientFactory,
+            EndpointFactory serverFactory,
+            int messageSize,
+            String cipher,
+            ChannelType channelType,
+            PerfTestProtocol protocol) {
+          a_clientFactory = clientFactory;
+          b_serverFactory = serverFactory;
+          c_messageSize = messageSize;
+          d_cipher = cipher;
+          e_channelType = channelType;
+          f_protocol = protocol;
+        }
+        public EndpointFactory clientFactory() {
+            return a_clientFactory;
+        }
+
+        public EndpointFactory serverFactory() {
+            return b_serverFactory;
+        }
+
+        public int messageSize() {
+            return c_messageSize;
+        }
+
+        public String cipher() {
+            return d_cipher;
+        }
+
+        public ChannelType channelType() {
+            return e_channelType;
+        }
+
+        public PerfTestProtocol protocol() {
+            return f_protocol;
+        }
+    }
+
+    private Object[] getParams() {
+        return new Object[][] {
+            new Object[] {new Config(
+                              EndpointFactory.CONSCRYPT,
+                              EndpointFactory.CONSCRYPT,
+                              64,
+                              "AES128-GCM",
+                              ChannelType.CHANNEL,
+                              PerfTestProtocol.TLSv13)},
+        };
+    }
+
+
+    private ClientEndpoint client;
+    private ServerEndpoint server;
+    private byte[] message;
+    private ExecutorService executor;
+    private Future<?> sendingFuture;
+    private volatile boolean stopping;
+
+    private static final AtomicLong bytesCounter = new AtomicLong();
+    private AtomicBoolean recording = new AtomicBoolean();
+
+    private void setup(Config config) throws Exception {
+        message = newTextMessage(512);
+
+        // Always use the same server for consistency across the benchmarks.
+        server = config.serverFactory().newServer(
+                ChannelType.CHANNEL, config.messageSize(), config.protocol().getProtocols(),
+                ciphers(config));
+
+        server.setMessageProcessor(new ServerEndpoint.MessageProcessor() {
+            @Override
+            public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
+                if (recording.get()) {
+                    // Server received a message, increment the count.
+                    bytesCounter.addAndGet(numBytes);
+                }
+            }
+        });
+        Future<?> connectedFuture = server.start();
+
+        client = config.clientFactory().newClient(
+            config.channelType(), server.port(), config.protocol().getProtocols(), ciphers(config));
+        client.start();
+
+        // Wait for the initial connection to complete.
+        connectedFuture.get(5, TimeUnit.SECONDS);
+
+        executor = Executors.newSingleThreadExecutor();
+        sendingFuture = executor.submit(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread thread = Thread.currentThread();
+                    while (!stopping && !thread.isInterrupted()) {
+                        client.sendMessage(message);
+                    }
+                } finally {
+                    client.flush();
+                }
+            }
+        });
+    }
+
+    void close() throws Exception {
+        stopping = true;
+
+        // Wait for the sending thread to stop.
+        sendingFuture.get(5, TimeUnit.SECONDS);
+
+        client.stop();
+        server.stop();
+        executor.shutdown();
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Simple benchmark for the amount of time to send a given number of messages
+     */
+    @Test
+    @Parameters(method = "getParams")
+    public void time(Config config) throws Exception {
+        reset();
+        setup(config);
+        recording.set(true);
+
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+          while (bytesCounter.get() < config.messageSize()) {
+          }
+          bytesCounter.set(0);
+        }
+        recording.set(false);
+        close();
+    }
+
+    void reset() {
+        stopping = false;
+        bytesCounter.set(0);
+    }
+
+    private String[] ciphers(Config config) {
+        return new String[] {config.cipher()};
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
new file mode 100644
index 0000000..0655f45
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/EndpointFactory.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+import org.conscrypt.ChannelType;
+import org.conscrypt.TestUtils;
+import java.io.IOException;
+import java.security.Provider;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * Utility for creating test client and server instances.
+ */
+public enum EndpointFactory {
+  CONSCRYPT(newConscryptFactories(false)),
+  CONSCRYPT_ENGINE(newConscryptFactories(true));
+
+  private final Factories factories;
+
+  EndpointFactory(Factories factories) {
+    this.factories = factories;
+  }
+
+  public ClientEndpoint newClient(ChannelType channelType, int port, String[] protocols,
+      String[] ciphers) throws IOException {
+    return new ClientEndpoint(
+        factories.clientFactory, channelType, port, protocols, ciphers);
+  }
+
+  public ServerEndpoint newServer(ChannelType channelType, int messageSize,
+      String[] protocols, String[] ciphers) throws IOException {
+    return new ServerEndpoint(factories.serverFactory, factories.serverSocketFactory,
+        channelType, messageSize, protocols, ciphers);
+  }
+
+  private static final class Factories {
+    final SSLSocketFactory clientFactory;
+    final SSLSocketFactory serverFactory;
+    final SSLServerSocketFactory serverSocketFactory;
+
+    private Factories(SSLSocketFactory clientFactory, SSLSocketFactory serverFactory,
+        SSLServerSocketFactory serverSocketFactory) {
+      this.clientFactory = clientFactory;
+      this.serverFactory = serverFactory;
+      this.serverSocketFactory = serverSocketFactory;
+    }
+  }
+
+  private static Factories newConscryptFactories(boolean useEngineSocket) {
+    Provider provider = TestUtils.getConscryptProvider();
+    SSLContext clientContext = TestUtils.newClientSslContext(provider);
+    SSLContext serverContext = TestUtils.newServerSslContext(provider);
+    final SSLSocketFactory clientFactory = clientContext.getSocketFactory();
+    final SSLSocketFactory serverFactory = serverContext.getSocketFactory();
+    final SSLServerSocketFactory serverSocketFactory = serverContext.getServerSocketFactory();
+    TestUtils.setUseEngineSocket(clientFactory, useEngineSocket);
+    TestUtils.setUseEngineSocket(serverFactory, useEngineSocket);
+    TestUtils.setUseEngineSocket(serverSocketFactory, useEngineSocket);
+    return new Factories(clientFactory, serverFactory, serverSocketFactory);
+  }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/OWNERS b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/OWNERS
new file mode 100644
index 0000000..7efabfd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 24949
+include platform/libcore:/OWNERS
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/PerfTestProtocol.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/PerfTestProtocol.java
new file mode 100644
index 0000000..4defe71
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/PerfTestProtocol.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.conscrypt;
+
+public enum PerfTestProtocol {
+
+    TLSv13("TLSv1.3"),
+    TLSv12("TLSv1.2");
+
+    private final String[] protocols;
+
+    PerfTestProtocol(String... protocols) {
+        this.protocols = protocols;
+    }
+
+    public String[] getProtocols() {
+        return protocols.clone();
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
new file mode 100644
index 0000000..3631c3f
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
@@ -0,0 +1,199 @@
+/*
+ * 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 java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.ServerSocket;
+import java.net.SocketException;
+import java.nio.channels.ClosedChannelException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.conscrypt.ChannelType;
+
+/**
+ * A simple socket-based test server.
+ */
+final class ServerEndpoint {
+    /**
+     * A processor for receipt of a single message.
+     */
+    public interface MessageProcessor {
+        void processMessage(byte[] message, int numBytes, OutputStream os);
+    }
+
+    /**
+     * A {@link MessageProcessor} that simply echos back the received message to the client.
+     */
+    public static final class EchoProcessor implements MessageProcessor {
+        @Override
+        public void processMessage(byte[] message, int numBytes, OutputStream os) {
+            try {
+                os.write(message, 0, numBytes);
+                os.flush();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private final ServerSocket serverSocket;
+    private final ChannelType channelType;
+    private final SSLSocketFactory socketFactory;
+    private final int messageSize;
+    private final String[] protocols;
+    private final String[] cipherSuites;
+    private final byte[] buffer;
+    private SSLSocket socket;
+    private ExecutorService executor;
+    private InputStream inputStream;
+    private OutputStream outputStream;
+    private volatile boolean stopping;
+    private volatile MessageProcessor messageProcessor = new EchoProcessor();
+    private volatile Future<?> processFuture;
+
+    ServerEndpoint(SSLSocketFactory socketFactory, SSLServerSocketFactory serverSocketFactory,
+            ChannelType channelType, int messageSize, String[] protocols,
+            String[] cipherSuites) throws IOException {
+        this.serverSocket = channelType.newServerSocket(serverSocketFactory);
+        this.socketFactory = socketFactory;
+        this.channelType = channelType;
+        this.messageSize = messageSize;
+        this.protocols = protocols;
+        this.cipherSuites = cipherSuites;
+        buffer = new byte[messageSize];
+    }
+
+    void setMessageProcessor(MessageProcessor messageProcessor) {
+        this.messageProcessor = messageProcessor;
+    }
+
+    Future<?> start() throws IOException {
+        executor = Executors.newSingleThreadExecutor();
+        return executor.submit(new AcceptTask());
+    }
+
+    void stop() {
+        try {
+            stopping = true;
+
+            if (socket != null) {
+                socket.close();
+                socket = null;
+            }
+
+            if (processFuture != null) {
+                processFuture.get(5, TimeUnit.SECONDS);
+            }
+
+            serverSocket.close();
+
+            if (executor != null) {
+                executor.shutdown();
+                executor.awaitTermination(5, TimeUnit.SECONDS);
+                executor = null;
+            }
+        } catch (IOException | InterruptedException | ExecutionException | TimeoutException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public int port() {
+        return serverSocket.getLocalPort();
+    }
+
+    private final class AcceptTask implements Runnable {
+        @Override
+        public void run() {
+            try {
+                if (stopping) {
+                    return;
+                }
+                socket = channelType.accept(serverSocket, socketFactory);
+                socket.setEnabledProtocols(protocols);
+                socket.setEnabledCipherSuites(cipherSuites);
+
+                socket.startHandshake();
+
+                inputStream = socket.getInputStream();
+                outputStream = socket.getOutputStream();
+
+                if (stopping) {
+                    return;
+                }
+                processFuture = executor.submit(new ProcessTask());
+            } catch (IOException e) {
+                e.printStackTrace();
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private final class ProcessTask implements Runnable {
+        @Override
+        public void run() {
+            try {
+                Thread thread = Thread.currentThread();
+                while (!stopping && !thread.isInterrupted()) {
+                    int bytesRead = readMessage();
+                    if (!stopping && !thread.isInterrupted()) {
+                        messageProcessor.processMessage(buffer, bytesRead, outputStream);
+                    }
+                }
+            } catch (Throwable e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        private int readMessage() throws IOException {
+            int totalBytesRead = 0;
+            while (!stopping && totalBytesRead < messageSize) {
+                try {
+                    int remaining = messageSize - totalBytesRead;
+                    int bytesRead = inputStream.read(buffer, totalBytesRead, remaining);
+                    if (bytesRead == -1) {
+                        break;
+                    }
+                    totalBytesRead += bytesRead;
+                } catch (SSLException e) {
+                    if (e.getCause() instanceof EOFException) {
+                        break;
+                    }
+                    throw e;
+                } catch (ClosedChannelException e) {
+                    // Thrown for channel-based sockets. Just treat like EOF.
+                    break;
+                } catch (SocketException e) {
+                    // The socket was broken. Just treat like EOF.
+                    break;
+                }
+            }
+            return totalBytesRead;
+        }
+    }
+}
\ 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
new file mode 100644
index 0000000..ba2a65a
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.ChannelType;
+import static org.conscrypt.TestUtils.getCommonProtocolSuites;
+import static org.conscrypt.TestUtils.newTextMessage;
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.SocketException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import android.conscrypt.ServerEndpoint.MessageProcessor;
+
+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 for comparing performance of server socket implementations.
+ */
+@RunWith(JUnitParamsRunner.class)
+@LargeTest
+public final class ServerSocketPerfTest {
+
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    /**
+     * Provider for the benchmark configuration
+     */
+    private class Config {
+        EndpointFactory a_clientFactory;
+        EndpointFactory b_serverFactory;
+        int c_messageSize;
+        String d_cipher;
+        ChannelType e_channelType;
+        Config(EndpointFactory clientFactory,
+            EndpointFactory serverFactory,
+            int messageSize,
+            String cipher,
+            ChannelType channelType) {
+          a_clientFactory = clientFactory;
+          b_serverFactory = serverFactory;
+          c_messageSize = messageSize;
+          d_cipher = cipher;
+          e_channelType = channelType;
+        }
+        public EndpointFactory clientFactory() {
+            return a_clientFactory;
+        }
+
+        public EndpointFactory serverFactory() {
+            return b_serverFactory;
+        }
+
+        public int messageSize() {
+            return c_messageSize;
+        }
+
+        public String cipher() {
+            return d_cipher;
+        }
+
+        public ChannelType channelType() {
+            return e_channelType;
+        }
+    }
+
+    private Object[] getParams() {
+        return new Object[][] {
+            new Object[] {new Config(
+                              EndpointFactory.CONSCRYPT,
+                              EndpointFactory.CONSCRYPT,
+                              64,
+                              "AES128-GCM",
+                              ChannelType.CHANNEL)},
+        };
+    }
+
+    private ClientEndpoint client;
+    private ServerEndpoint server;
+    private ExecutorService executor;
+    private Future<?> receivingFuture;
+    private volatile boolean stopping;
+    private static final AtomicLong bytesCounter = new AtomicLong();
+    private AtomicBoolean recording = new AtomicBoolean();
+
+    private void setup(final Config config) throws Exception {
+        recording.set(false);
+
+        byte[] message = newTextMessage(config.messageSize());
+
+        final ChannelType channelType = config.channelType();
+
+        server = config.serverFactory().newServer(
+            channelType, config.messageSize(), getCommonProtocolSuites(), ciphers(config));
+        server.setMessageProcessor(new MessageProcessor() {
+            @Override
+            public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
+                try {
+                    try {
+                        while (!stopping) {
+                            os.write(inMessage, 0, numBytes);
+                        }
+                    } finally {
+                        os.flush();
+                    }
+                } catch (SocketException e) {
+                    // Just ignore.
+                } catch (IOException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+
+        Future<?> connectedFuture = server.start();
+
+        // Always use the same client for consistency across the benchmarks.
+        client = config.clientFactory().newClient(
+                ChannelType.CHANNEL, server.port(), getCommonProtocolSuites(), ciphers(config));
+        client.start();
+
+        // Wait for the initial connection to complete.
+        connectedFuture.get(5, TimeUnit.SECONDS);
+
+        // Start the server-side streaming by sending a message to the server.
+        client.sendMessage(message);
+        client.flush();
+
+        executor = Executors.newSingleThreadExecutor();
+        receivingFuture = executor.submit(new Runnable() {
+            @Override
+            public void run() {
+                Thread thread = Thread.currentThread();
+                byte[] buffer = new byte[config.messageSize()];
+                while (!stopping && !thread.isInterrupted()) {
+                    int numBytes = client.readMessage(buffer);
+                    if (numBytes < 0) {
+                        return;
+                    }
+                    assertEquals(config.messageSize(), numBytes);
+
+                    // Increment the message counter if we're recording.
+                    if (recording.get()) {
+                        bytesCounter.addAndGet(numBytes);
+                    }
+                }
+            }
+        });
+    }
+
+    void close() throws Exception {
+        stopping = true;
+        // Stop and wait for sending to complete.
+        server.stop();
+        client.stop();
+        executor.shutdown();
+        receivingFuture.get(5, TimeUnit.SECONDS);
+        executor.awaitTermination(5, TimeUnit.SECONDS);
+    }
+
+    @Test
+    @Parameters(method = "getParams")
+    public void throughput(Config config) throws Exception {
+        setup(config);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+          recording.set(true);
+          while (bytesCounter.get() < config.messageSize()) {
+          }
+          bytesCounter.set(0);
+          recording.set(false);
+        }
+        close();
+    }
+
+    private String[] ciphers(Config config) {
+        return new String[] {config.cipher()};
+    }
+}
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
new file mode 100644
index 0000000..78fe732
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/Transformation.java
@@ -0,0 +1,88 @@
+/*
+ * 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 java.security.Key;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.KeyGenerator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/**
+ * Supported cipher transformations.
+ */
+@SuppressWarnings({"ImmutableEnumChecker", "unused"})
+public enum Transformation {
+    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) {
+        this.algorithm = algorithm;
+        this.mode = mode;
+        this.padding = padding;
+        this.keyGen = keyGen;
+    }
+
+    final String algorithm;
+    final String mode;
+    final String padding;
+    final KeyGen keyGen;
+
+    String toFormattedString() {
+        return algorithm + "/" + mode + "/" + padding;
+    }
+
+    Key newEncryptKey() {
+        return keyGen.newEncryptKey();
+    }
+
+    private interface KeyGen { Key newEncryptKey(); }
+
+    private static final class RsaKeyGen implements KeyGen {
+        @Override
+        public Key newEncryptKey() {
+            try {
+                // Use Bouncy castle
+                KeyPairGenerator generator =
+                        KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
+                generator.initialize(2048);
+                KeyPair pair = generator.generateKeyPair();
+                return pair.getPublic();
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private static final class AesKeyGen implements KeyGen {
+        @Override
+        public Key newEncryptKey() {
+            try {
+                // Just use the JDK's provider.
+                KeyGenerator keyGen = KeyGenerator.getInstance("AES");
+                keyGen.init(256);
+                return keyGen.generateKey();
+            } catch (NoSuchAlgorithmException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/api/current.txt b/core/api/current.txt
index 2866e71..d645938 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3454,6 +3454,8 @@
     field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
     field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa
     field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8
+    field @FlaggedApi("android.view.accessibility.global_action_media_play_pause") public static final int GLOBAL_ACTION_MEDIA_PLAY_PAUSE = 22; // 0x16
+    field @FlaggedApi("android.view.accessibility.global_action_menu") public static final int GLOBAL_ACTION_MENU = 21; // 0x15
     field public static final int GLOBAL_ACTION_NOTIFICATIONS = 4; // 0x4
     field public static final int GLOBAL_ACTION_POWER_DIALOG = 6; // 0x6
     field public static final int GLOBAL_ACTION_QUICK_SETTINGS = 5; // 0x5
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index d70fa19..81cc674 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -67,6 +67,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.accessibility.Flags;
 import android.view.inputmethod.EditorInfo;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -625,6 +626,18 @@
      */
     public static final int GLOBAL_ACTION_DPAD_CENTER = 20;
 
+    /**
+     * Action to trigger menu key event.
+     */
+    @FlaggedApi(Flags.FLAG_GLOBAL_ACTION_MENU)
+    public static final int GLOBAL_ACTION_MENU = 21;
+
+    /**
+     * Action to trigger media play/pause key event.
+     */
+    @FlaggedApi(Flags.FLAG_GLOBAL_ACTION_MEDIA_PLAY_PAUSE)
+    public static final int GLOBAL_ACTION_MEDIA_PLAY_PAUSE = 22;
+
     private static final String LOG_TAG = "AccessibilityService";
 
     /**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c7e5d88..a6eed50 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4419,7 +4419,8 @@
      * @see #DISPLAY_HASH_SERVICE
      * @see android.view.displayhash.DisplayHashManager
      */
-    public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
+    // TODO(b/347269120): Re-add @Nullable
+    public abstract Object getSystemService(@ServiceName @NonNull String name);
 
     /**
      * Return the handle to a system-level service by class.
@@ -4463,7 +4464,8 @@
      * <b>never</b> throw a {@link RuntimeException} if the name is not supported.
      */
     @SuppressWarnings("unchecked")
-    public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) {
+    // TODO(b/347269120): Re-add @Nullable
+    public final <T> T getSystemService(@NonNull Class<T> serviceClass) {
         // Because subclasses may override getSystemService(String) we cannot
         // perform a lookup by class alone.  We must first map the class to its
         // service name then invoke the string-based method.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e0cf0a5..a475c29 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -932,7 +932,8 @@
     }
 
     @Override
-    public @Nullable Object getSystemService(String name) {
+    // TODO(b/347269120): Re-add @Nullable
+    public Object getSystemService(String name) {
         return mBase.getSystemService(name);
     }
 
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 685654a..a62efdf 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -82,6 +82,20 @@
 
 flag {
     namespace: "accessibility"
+    name: "global_action_menu"
+    description: "Allow AccessibilityService to perform GLOBAL_ACTION_MENU"
+    bug: "334954140"
+}
+
+flag {
+    namespace: "accessibility"
+    name: "global_action_media_play_pause"
+    description: "Allow AccessibilityService to perform GLOBAL_ACTION_MEDIA_PLAY_PAUSE"
+    bug: "334954140"
+}
+
+flag {
+    namespace: "accessibility"
     name: "granular_scrolling"
     description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
     bug: "302376158"
diff --git a/core/java/android/window/flags/OWNERS b/core/java/android/window/flags/OWNERS
index fd73d35..0472b6c4 100644
--- a/core/java/android/window/flags/OWNERS
+++ b/core/java/android/window/flags/OWNERS
@@ -1,3 +1,4 @@
 per-file responsible_apis.aconfig = file:/BAL_OWNERS
 per-file large_screen_experiences_app_compat.aconfig = file:/LSE_APP_COMPAT_OWNERS
 per-file accessibility.aconfig = file:/core/java/android/view/accessibility/OWNERS
+per-file lse_desktop_experience.aconfig = file:/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 59066eb..0b9bde8 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5937,6 +5937,10 @@
     <string name="accessibility_system_action_hardware_a11y_shortcut_label">Accessibility Shortcut</string>
     <!-- Label for dismissing the notification shade [CHAR LIMIT=NONE] -->
     <string name="accessibility_system_action_dismiss_notification_shade">Dismiss Notification Shade</string>
+     <!-- Label for menu action [CHAR LIMIT=NONE] -->
+    <string name="accessibility_system_action_menu_label">Menu</string>
+     <!-- Label for media play/pause action [CHAR LIMIT=NONE] -->
+    <string name="accessibility_system_action_media_play_pause_label">Media Play/Pause</string>
     <!-- Label for Dpad up action [CHAR LIMIT=NONE] -->
     <string name="accessibility_system_action_dpad_up_label">Dpad Up</string>
     <!-- Label for Dpad down action [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 54ab147..8b13a11 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4433,6 +4433,8 @@
   <java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" />
   <java-symbol type="string" name="accessibility_system_action_hardware_a11y_shortcut_label" />
   <java-symbol type="string" name="accessibility_system_action_dismiss_notification_shade" />
+  <java-symbol type="string" name="accessibility_system_action_menu_label" />
+  <java-symbol type="string" name="accessibility_system_action_media_play_pause_label" />
   <java-symbol type="string" name="accessibility_system_action_dpad_up_label" />
   <java-symbol type="string" name="accessibility_system_action_dpad_down_label" />
   <java-symbol type="string" name="accessibility_system_action_dpad_left_label" />
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index 24aea37..ecf4eb4 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -17,7 +17,6 @@
 package android.security;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceSpecificException;
@@ -112,29 +111,6 @@
     }
 
     /**
-     * Informs Keystore 2.0 about changing user's password
-     *
-     * @param userId   - Android user id of the user
-     * @param password - a secret derived from the synthetic password provided by the
-     *                 LockSettingsService
-     * @return 0 if successful or a {@code ResponseCode}
-     * @hide
-     */
-    public static int onUserPasswordChanged(int userId, @Nullable byte[] password) {
-        StrictMode.noteDiskWrite();
-        try {
-            getService().onUserPasswordChanged(userId, password);
-            return 0;
-        } catch (ServiceSpecificException e) {
-            Log.e(TAG, "onUserPasswordChanged failed", e);
-            return e.errorCode;
-        } catch (Exception e) {
-            Log.e(TAG, "Can not connect to keystore", e);
-            return SYSTEM_ERROR;
-        }
-    }
-
-    /**
      * Tells Keystore that a user's LSKF is being removed, ie the user's lock screen is changing to
      * Swipe or None.  Keystore uses this notification to delete the user's auth-bound keys.
      *
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 1385f42..7ad68aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/OWNERS
@@ -5,3 +5,4 @@
 nmusgrave@google.com
 pbdr@google.com
 tkachenkoi@google.com
+vaniadesmonda@google.com
diff --git a/nfc/lint-baseline.xml b/nfc/lint-baseline.xml
index 1dfdd29..d0f797e 100644
--- a/nfc/lint-baseline.xml
+++ b/nfc/lint-baseline.xml
@@ -210,4 +210,59 @@
             column="23"/>
     </issue>
 
+    <issue
+        id="FlaggedApi"
+        message="Method `PollingFrame()` is a flagged API and should be inside an `if (Flags.nfcReadPollingLoop())` check (or annotate the surrounding method `handleMessage` with `@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) to transfer requirement to caller`)"
+        errorLine1="                        pollingFrames.add(new PollingFrame(frame));"
+        errorLine2="                                          ~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/HostApduService.java"
+            line="335"
+            column="43"/>
+    </issue>
+
+    <issue
+        id="FlaggedApi"
+        message="Method `processPollingFrames()` is a flagged API and should be inside an `if (Flags.nfcReadPollingLoop())` check (or annotate the surrounding method `handleMessage` with `@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP) to transfer requirement to caller`)"
+        errorLine1="                    processPollingFrames(pollingFrames);"
+        errorLine2="                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/cardemulation/HostApduService.java"
+            line="337"
+            column="21"/>
+    </issue>
+
+    <issue
+        id="FlaggedApi"
+        message="Method `NfcOemExtension()` is a flagged API and should be inside an `if (Flags.nfcOemExtension())` check (or annotate the surrounding method `NfcAdapter` with `@FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) to transfer requirement to caller`)"
+        errorLine1="        mNfcOemExtension = new NfcOemExtension(mContext, this);"
+        errorLine2="                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/NfcAdapter.java"
+            line="895"
+            column="28"/>
+    </issue>
+
+    <issue
+        id="FlaggedApi"
+        message="Method `onVendorNciResponse()` is a flagged API and should be inside an `if (Flags.nfcVendorCmd())` check (or annotate the surrounding method `onVendorResponseReceived` with `@FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) to transfer requirement to caller`)"
+        errorLine1="                    executor.execute(() -> callback.onVendorNciResponse(gid, oid, payload));"
+        errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/NfcVendorNciCallbackListener.java"
+            line="88"
+            column="44"/>
+    </issue>
+
+    <issue
+        id="FlaggedApi"
+        message="Method `onVendorNciNotification()` is a flagged API and should be inside an `if (Flags.nfcVendorCmd())` check (or annotate the surrounding method `onVendorNotificationReceived` with `@FlaggedApi(Flags.FLAG_NFC_VENDOR_CMD) to transfer requirement to caller`)"
+        errorLine1="                    executor.execute(() -> callback.onVendorNciNotification(gid, oid, payload));"
+        errorLine2="                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="frameworks/base/nfc/java/android/nfc/NfcVendorNciCallbackListener.java"
+            line="106"
+            column="44"/>
+    </issue>
+
 </issues>
\ No newline at end of file
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index 5966c9f..62ed66c 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -11,3 +11,6 @@
 
 # Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
 per-file *.xml=*
+
+# Notification-related utilities
+per-file */notification/* = file:/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index da49201..5c5ff1e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -42,6 +42,7 @@
 import android.view.KeyEvent;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.Flags;
 
 import com.android.internal.R;
 import com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity;
@@ -178,6 +179,18 @@
     private static final int SYSTEM_ACTION_ID_DPAD_CENTER =
             AccessibilityService.GLOBAL_ACTION_DPAD_CENTER; // 20
 
+    /**
+     * Action ID to trigger menu key event.
+     */
+    private static final int SYSTEM_ACTION_ID_MENU =
+            AccessibilityService.GLOBAL_ACTION_MENU; // 21
+
+    /**
+     * Action ID to trigger media play/pause key event.
+     */
+    private static final int SYSTEM_ACTION_ID_MEDIA_PLAY_PAUSE =
+            AccessibilityService.GLOBAL_ACTION_MEDIA_PLAY_PAUSE; // 22
+
     private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
     private final SystemActionsBroadcastReceiver mReceiver;
@@ -305,6 +318,14 @@
                 R.string.accessibility_system_action_dpad_center_label,
                 SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER);
 
+        RemoteAction actionMenu = createRemoteAction(
+                R.string.accessibility_system_action_menu_label,
+                SystemActionsBroadcastReceiver.INTENT_ACTION_MENU);
+
+        RemoteAction actionMediaPlayPause = createRemoteAction(
+                R.string.accessibility_system_action_media_play_pause_label,
+                SystemActionsBroadcastReceiver.INTENT_ACTION_MEDIA_PLAY_PAUSE);
+
         mA11yManager.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK);
         mA11yManager.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME);
         mA11yManager.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS);
@@ -324,6 +345,8 @@
         mA11yManager.registerSystemAction(actionDpadLeft, SYSTEM_ACTION_ID_DPAD_LEFT);
         mA11yManager.registerSystemAction(actionDpadRight, SYSTEM_ACTION_ID_DPAD_RIGHT);
         mA11yManager.registerSystemAction(actionDpadCenter, SYSTEM_ACTION_ID_DPAD_CENTER);
+        mA11yManager.registerSystemAction(actionMenu, SYSTEM_ACTION_ID_MENU);
+        mA11yManager.registerSystemAction(actionMediaPlayPause, SYSTEM_ACTION_ID_MEDIA_PLAY_PAUSE);
         registerOrUnregisterDismissNotificationShadeAction();
     }
 
@@ -433,6 +456,14 @@
                 labelId = R.string.accessibility_system_action_dpad_center_label;
                 intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER;
                 break;
+            case SYSTEM_ACTION_ID_MENU:
+                labelId = R.string.accessibility_system_action_menu_label;
+                intent = SystemActionsBroadcastReceiver.INTENT_ACTION_MENU;
+                break;
+            case SYSTEM_ACTION_ID_MEDIA_PLAY_PAUSE:
+                labelId = R.string.accessibility_system_action_media_play_pause_label;
+                intent = SystemActionsBroadcastReceiver.INTENT_ACTION_MEDIA_PLAY_PAUSE;
+                break;
             default:
                 return;
         }
@@ -569,6 +600,16 @@
         sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER);
     }
 
+    @VisibleForTesting
+    void handleMenu() {
+        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MENU);
+    }
+
+    @VisibleForTesting
+    void handleMediaPlayPause() {
+        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+    }
+
     private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
         private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
         private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
@@ -592,6 +633,9 @@
         private static final String INTENT_ACTION_DPAD_LEFT = "SYSTEM_ACTION_DPAD_LEFT";
         private static final String INTENT_ACTION_DPAD_RIGHT = "SYSTEM_ACTION_DPAD_RIGHT";
         private static final String INTENT_ACTION_DPAD_CENTER = "SYSTEM_ACTION_DPAD_CENTER";
+        private static final String INTENT_ACTION_MENU = "SYSTEM_ACTION_MENU";
+        private static final String INTENT_ACTION_MEDIA_PLAY_PAUSE =
+                "SYSTEM_ACTION_MEDIA_PLAY_PAUSE";
 
         private PendingIntent createPendingIntent(Context context, String intentAction) {
             switch (intentAction) {
@@ -612,7 +656,9 @@
                 case INTENT_ACTION_DPAD_DOWN:
                 case INTENT_ACTION_DPAD_LEFT:
                 case INTENT_ACTION_DPAD_RIGHT:
-                case INTENT_ACTION_DPAD_CENTER: {
+                case INTENT_ACTION_DPAD_CENTER:
+                case INTENT_ACTION_MENU:
+                case INTENT_ACTION_MEDIA_PLAY_PAUSE: {
                     Intent intent = new Intent(intentAction);
                     intent.setPackage(context.getPackageName());
                     intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -645,6 +691,8 @@
             intentFilter.addAction(INTENT_ACTION_DPAD_LEFT);
             intentFilter.addAction(INTENT_ACTION_DPAD_RIGHT);
             intentFilter.addAction(INTENT_ACTION_DPAD_CENTER);
+            intentFilter.addAction(INTENT_ACTION_MENU);
+            intentFilter.addAction(INTENT_ACTION_MEDIA_PLAY_PAUSE);
             return intentFilter;
         }
 
@@ -724,6 +772,18 @@
                     handleDpadCenter();
                     break;
                 }
+                case INTENT_ACTION_MENU: {
+                    if (Flags.globalActionMenu()) {
+                        handleMenu();
+                    }
+                    break;
+                }
+                case INTENT_ACTION_MEDIA_PLAY_PAUSE: {
+                    if (Flags.globalActionMediaPlayPause()) {
+                        handleMediaPlayPause();
+                    }
+                    break;
+                }
                 default:
                     break;
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
index b478d5c..da3e487 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/SystemActionsTest.java
@@ -17,6 +17,7 @@
 package com.android.systemui.accessibility;
 
 import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doAnswer;
@@ -25,11 +26,15 @@
 
 import android.hardware.input.InputManager;
 import android.os.RemoteException;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.KeyEvent;
+import android.view.accessibility.Flags;
 
 import androidx.test.filters.SmallTest;
 
@@ -43,6 +48,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -76,6 +82,9 @@
 
     private SystemActions mSystemActions;
 
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     @Before
     public void setUp() throws RemoteException {
         MockitoAnnotations.initMocks(this);
@@ -130,4 +139,40 @@
 
         verify(mTelecomManager).endCall();
     }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_GLOBAL_ACTION_MENU)
+    public void handleMenu_injectsKeyEvents() {
+        final List<KeyEvent> keyEvents = new ArrayList<>();
+        doAnswer(invocation -> {
+            keyEvents.add(new KeyEvent(invocation.getArgument(0)));
+            return null;
+        }).when(mInputManager).injectInputEvent(any(), anyInt());
+
+        mSystemActions.handleMenu();
+
+        assertThat(keyEvents.size()).isEqualTo(2);
+        assertThat(keyEvents.get(0).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_MENU);
+        assertThat(keyEvents.get(0).getAction()).isEqualTo(KeyEvent.ACTION_DOWN);
+        assertThat(keyEvents.get(1).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_MENU);
+        assertThat(keyEvents.get(1).getAction()).isEqualTo(KeyEvent.ACTION_UP);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_GLOBAL_ACTION_MEDIA_PLAY_PAUSE)
+    public void handleMediaPlayPause_injectsKeyEvents() {
+        final List<KeyEvent> keyEvents = new ArrayList<>();
+        doAnswer(invocation -> {
+            keyEvents.add(new KeyEvent(invocation.getArgument(0)));
+            return null;
+        }).when(mInputManager).injectInputEvent(any(), anyInt());
+
+        mSystemActions.handleMediaPlayPause();
+
+        assertThat(keyEvents.size()).isEqualTo(2);
+        assertThat(keyEvents.get(0).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertThat(keyEvents.get(0).getAction()).isEqualTo(KeyEvent.ACTION_DOWN);
+        assertThat(keyEvents.get(1).getKeyCode()).isEqualTo(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+        assertThat(keyEvents.get(1).getAction()).isEqualTo(KeyEvent.ACTION_UP);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 9747579..2945af5 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -34,6 +34,7 @@
 import android.view.KeyEvent;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.accessibility.Flags;
 
 import com.android.internal.R;
 import com.android.internal.accessibility.util.AccessibilityUtils;
@@ -328,6 +329,18 @@
                     sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER,
                             InputDevice.SOURCE_KEYBOARD | InputDevice.SOURCE_DPAD);
                     return true;
+                case AccessibilityService.GLOBAL_ACTION_MENU:
+                    if (Flags.globalActionMenu()) {
+                        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MENU,
+                                InputDevice.SOURCE_KEYBOARD);
+                    }
+                    return true;
+                case AccessibilityService.GLOBAL_ACTION_MEDIA_PLAY_PAUSE:
+                    if (Flags.globalActionMediaPlayPause()) {
+                        sendDownAndUpKeyEvents(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
+                                InputDevice.SOURCE_KEYBOARD);
+                    }
+                    return true;
                 default:
                     Slog.e(TAG, "Invalid action id: " + actionId);
                     return false;
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ac29f85..7157646 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -252,9 +252,6 @@
     private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce";
     private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys";
 
-    private static final boolean FIX_UNLOCKED_DEVICE_REQUIRED_KEYS =
-            android.security.Flags.fixUnlockedDeviceRequiredKeysV2();
-
     // Duration that LockSettingsService will store the gatekeeper password for. This allows
     // multiple biometric enrollments without prompting the user to enter their password via
     // ConfirmLockPassword/ConfirmLockPattern multiple times. This needs to be at least the duration
@@ -662,7 +659,6 @@
         mActivityManager = injector.getActivityManager();
 
         IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_ADDED);
         filter.addAction(Intent.ACTION_USER_STARTING);
         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
         injector.getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter,
@@ -899,13 +895,7 @@
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
-                if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
-                    // Notify keystore that a new user was added.
-                    final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
-                    AndroidKeyStoreMaintenance.onUserAdded(userHandle);
-                }
-            } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
+            if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
                 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                 mStorage.prefetchUser(userHandle);
             } else if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
@@ -1089,32 +1079,14 @@
             // Note: if this migration gets interrupted (e.g. by the device powering off), there
             // shouldn't be a problem since this will run again on the next boot, and
             // setCeStorageProtection() and initKeystoreSuperKeys(..., true) are idempotent.
-            if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
-                if (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
-                    for (UserInfo user : mUserManager.getAliveUsers()) {
-                        removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
-                        synchronized (mSpManager) {
-                            migrateUserToSpWithBoundKeysLocked(user.id);
-                        }
+            if (!getBoolean(MIGRATED_SP_FULL, false, 0)) {
+                for (UserInfo user : mUserManager.getAliveUsers()) {
+                    removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
+                    synchronized (mSpManager) {
+                        migrateUserToSpWithBoundKeysLocked(user.id);
                     }
-                    setBoolean(MIGRATED_SP_FULL, true, 0);
                 }
-            } else {
-                if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null) {
-                    for (UserInfo user : mUserManager.getAliveUsers()) {
-                        removeStateForReusedUserIdIfNecessary(user.id, user.serialNumber);
-                        synchronized (mSpManager) {
-                            migrateUserToSpWithBoundCeKeyLocked(user.id);
-                        }
-                    }
-                    setString(MIGRATED_SP_CE_ONLY, "true", 0);
-                }
-
-                if (getBoolean(MIGRATED_SP_FULL, false, 0)) {
-                    // The FIX_UNLOCKED_DEVICE_REQUIRED_KEYS flag was enabled but then got disabled.
-                    // Ensure the full migration runs again the next time the flag is enabled...
-                    setBoolean(MIGRATED_SP_FULL, false, 0);
-                }
+                setBoolean(MIGRATED_SP_FULL, true, 0);
             }
 
             mThirdPartyAppsStarted = true;
@@ -1122,30 +1094,6 @@
     }
 
     @GuardedBy("mSpManager")
-    private void migrateUserToSpWithBoundCeKeyLocked(@UserIdInt int userId) {
-        if (isUserSecure(userId)) {
-            Slogf.d(TAG, "User %d is secured; no migration needed", userId);
-            return;
-        }
-        long protectorId = getCurrentLskfBasedProtectorId(userId);
-        if (protectorId == SyntheticPasswordManager.NULL_PROTECTOR_ID) {
-            Slogf.i(TAG, "Migrating unsecured user %d to SP-based credential", userId);
-            initializeSyntheticPassword(userId);
-        } else {
-            Slogf.i(TAG, "Existing unsecured user %d has a synthetic password; re-encrypting CE " +
-                    "key with it", userId);
-            AuthenticationResult result = mSpManager.unlockLskfBasedProtector(
-                    getGateKeeperService(), protectorId, LockscreenCredential.createNone(), userId,
-                    null);
-            if (result.syntheticPassword == null) {
-                Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId);
-                return;
-            }
-            setCeStorageProtection(userId, result.syntheticPassword);
-        }
-    }
-
-    @GuardedBy("mSpManager")
     private void migrateUserToSpWithBoundKeysLocked(@UserIdInt int userId) {
         if (isUserSecure(userId)) {
             Slogf.d(TAG, "User %d is secured; no migration needed", userId);
@@ -1454,11 +1402,6 @@
     }
 
     @VisibleForTesting /** Note: this method is overridden in unit tests */
-    void setKeystorePassword(byte[] password, int userHandle) {
-        AndroidKeyStoreMaintenance.onUserPasswordChanged(userHandle, password);
-    }
-
-    @VisibleForTesting /** Note: this method is overridden in unit tests */
     void initKeystoreSuperKeys(@UserIdInt int userId, SyntheticPassword sp, boolean allowExisting) {
         final byte[] password = sp.deriveKeyStorePassword();
         try {
@@ -2195,9 +2138,7 @@
                 return;
             }
             onSyntheticPasswordUnlocked(userId, result.syntheticPassword);
-            if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
-                unlockKeystore(userId, result.syntheticPassword);
-            }
+            unlockKeystore(userId, result.syntheticPassword);
             unlockCeStorage(userId, result.syntheticPassword);
         }
     }
@@ -2503,9 +2444,7 @@
         // long time, so for now we keep doing it just in case it's ever important.  Don't wait
         // until initKeystoreSuperKeys() to do this; that can be delayed if the user is being
         // created during early boot, and maybe something will use Keystore before then.
-        if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
-            AndroidKeyStoreMaintenance.onUserAdded(userId);
-        }
+        AndroidKeyStoreMaintenance.onUserAdded(userId);
 
         synchronized (mUserCreationAndRemovalLock) {
             // During early boot, don't actually create the synthetic password yet, but rather
@@ -2931,9 +2870,7 @@
                     LockscreenCredential.createNone(), sp, userId);
             setCurrentLskfBasedProtectorId(protectorId, userId);
             setCeStorageProtection(userId, sp);
-            if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
-                initKeystoreSuperKeys(userId, sp, /* allowExisting= */ false);
-            }
+            initKeystoreSuperKeys(userId, sp, /* allowExisting= */ false);
             onSyntheticPasswordCreated(userId, sp);
             Slogf.i(TAG, "Successfully initialized synthetic password for user %d", userId);
             return sp;
@@ -3048,9 +2985,6 @@
             if (!mSpManager.hasSidForUser(userId)) {
                 mSpManager.newSidForUser(getGateKeeperService(), sp, userId);
                 mSpManager.verifyChallenge(getGateKeeperService(), sp, 0L, userId);
-                if (!FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
-                    setKeystorePassword(sp.deriveKeyStorePassword(), userId);
-                }
             }
         } else {
             // Cache all profile password if they use unified challenge. This will later be used to
@@ -3061,11 +2995,7 @@
             gateKeeperClearSecureUserId(userId);
             unlockCeStorage(userId, sp);
             unlockKeystore(userId, sp);
-            if (FIX_UNLOCKED_DEVICE_REQUIRED_KEYS) {
-                AndroidKeyStoreMaintenance.onUserLskfRemoved(userId);
-            } else {
-                setKeystorePassword(null, userId);
-            }
+            AndroidKeyStoreMaintenance.onUserLskfRemoved(userId);
             removeBiometricsForUser(userId);
         }
         setCurrentLskfBasedProtectorId(newProtectorId, userId);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index f62c76e..b3c31a9 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -267,6 +267,10 @@
             return KeyStoreAuthorization.getInstance();
         }
 
+        AlarmManager getAlarmManager() {
+            return mContext.getSystemService(AlarmManager.class);
+        }
+
         Looper getLooper() {
             return Looper.myLooper();
         }
@@ -285,7 +289,7 @@
         mLockPatternUtils = injector.getLockPatternUtils();
         mKeyStoreAuthorization = injector.getKeyStoreAuthorization();
         mStrongAuthTracker = new StrongAuthTracker(context, injector.getLooper());
-        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        mAlarmManager = injector.getAlarmManager();
     }
 
     @Override
@@ -845,12 +849,7 @@
                 continue;
             }
 
-            final boolean trusted;
-            if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()) {
-                trusted = getUserTrustStateInner(id) == TrustState.TRUSTED;
-            } else {
-                trusted = aggregateIsTrusted(id);
-            }
+            final boolean trusted = getUserTrustStateInner(id) == TrustState.TRUSTED;
             boolean showingKeyguard = true;
             boolean biometricAuthenticated = false;
             boolean currentUserIsUnlocked = false;
@@ -911,19 +910,15 @@
 
     private void notifyKeystoreOfDeviceLockState(int userId, boolean isLocked) {
         if (isLocked) {
-            if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()) {
-                // A profile with unified challenge is unlockable not by its own biometrics and
-                // trust agents, but rather by those of the parent user.  Therefore, when protecting
-                // the profile's UnlockedDeviceRequired keys, we must use the parent's list of
-                // biometric SIDs and weak unlock methods, not the profile's.
-                int authUserId = mLockPatternUtils.isProfileWithUnifiedChallenge(userId)
-                        ? resolveProfileParent(userId) : userId;
+            // A profile with unified challenge is unlockable not by its own biometrics and
+            // trust agents, but rather by those of the parent user.  Therefore, when protecting
+            // the profile's UnlockedDeviceRequired keys, we must use the parent's list of
+            // biometric SIDs and weak unlock methods, not the profile's.
+            int authUserId = mLockPatternUtils.isProfileWithUnifiedChallenge(userId)
+                    ? resolveProfileParent(userId) : userId;
 
-                mKeyStoreAuthorization.onDeviceLocked(userId, getBiometricSids(authUserId),
-                        isWeakUnlockMethodEnabled(authUserId));
-            } else {
-                mKeyStoreAuthorization.onDeviceLocked(userId, getBiometricSids(userId), false);
-            }
+            mKeyStoreAuthorization.onDeviceLocked(userId, getBiometricSids(authUserId),
+                    isWeakUnlockMethodEnabled(authUserId));
         } else {
             // Notify Keystore that the device is now unlocked for the user.  Note that for unlocks
             // with LSKF, this is redundant with the call from LockSettingsService which provides
diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
index 0532e04..7aec42b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.trust;
 
+import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
@@ -26,12 +28,22 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+
+import static java.util.Collections.singleton;
+
 import android.Manifest;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.AlarmManager;
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.ITrustListener;
@@ -60,20 +72,24 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 import android.security.KeyStoreAuthorization;
+import android.service.trust.GrantTrustResult;
+import android.service.trust.ITrustAgentService;
+import android.service.trust.ITrustAgentServiceCallback;
 import android.service.trust.TrustAgentService;
 import android.testing.TestableContext;
+import android.util.Log;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.StrongAuthFlags;
+import com.android.internal.widget.LockSettingsInternal;
 import com.android.modules.utils.testing.ExtendedMockitoRule;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -81,15 +97,19 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class TrustManagerServiceTest {
 
@@ -101,9 +121,6 @@
             .build();
 
     @Rule
-    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
-    @Rule
     public final MockContext mMockContext = new MockContext(
             ApplicationProvider.getApplicationContext());
 
@@ -115,21 +132,28 @@
     private static final int PROFILE_USER_ID = 70;
     private static final long[] PARENT_BIOMETRIC_SIDS = new long[] { 600L, 601L };
     private static final long[] PROFILE_BIOMETRIC_SIDS = new long[] { 700L, 701L };
+    private static final long RENEWABLE_TRUST_DURATION = 10000L;
+    private static final String GRANT_TRUST_MESSAGE = "granted";
+    private static final String TAG = "TrustManagerServiceTest";
 
     private final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>();
     private final ArrayList<ComponentName> mKnownTrustAgents = new ArrayList<>();
     private final ArrayList<ComponentName> mEnabledTrustAgents = new ArrayList<>();
+    private final Map<ComponentName, ITrustAgentService.Stub> mMockTrustAgents = new HashMap<>();
 
     private @Mock ActivityManager mActivityManager;
+    private @Mock AlarmManager mAlarmManager;
     private @Mock BiometricManager mBiometricManager;
     private @Mock DevicePolicyManager mDevicePolicyManager;
     private @Mock FaceManager mFaceManager;
     private @Mock FingerprintManager mFingerprintManager;
     private @Mock KeyStoreAuthorization mKeyStoreAuthorization;
     private @Mock LockPatternUtils mLockPatternUtils;
+    private @Mock LockSettingsInternal mLockSettingsInternal;
     private @Mock PackageManager mPackageManager;
     private @Mock UserManager mUserManager;
     private @Mock IWindowManager mWindowManager;
+    private @Mock ITrustListener mTrustListener;
 
     private HandlerThread mHandlerThread;
     private TrustManagerService mService;
@@ -158,6 +182,9 @@
             return null;
         }).when(mLockPatternUtils).setEnabledTrustAgents(any(), eq(TEST_USER_ID));
 
+        LocalServices.removeServiceForTest(LockSettingsInternal.class);
+        LocalServices.addService(LockSettingsInternal.class, mLockSettingsInternal);
+
         ArgumentMatcher<Intent> trustAgentIntentMatcher = new ArgumentMatcher<Intent>() {
             @Override
             public boolean matches(Intent argument) {
@@ -176,6 +203,7 @@
         when(mWindowManager.isKeyguardLocked()).thenReturn(true);
 
         mMockContext.addMockSystemService(ActivityManager.class, mActivityManager);
+        mMockContext.addMockSystemService(AlarmManager.class, mAlarmManager);
         mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager);
         mMockContext.addMockSystemService(FaceManager.class, mFaceManager);
         mMockContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
@@ -197,6 +225,7 @@
         verify(() -> ServiceManager.addService(eq(Context.TRUST_SERVICE),
                     binderArgumentCaptor.capture(), anyBoolean(), anyInt()));
         mTrustManager = ITrustManager.Stub.asInterface(binderArgumentCaptor.getValue());
+        mTrustManager.registerTrustListener(mTrustListener);
     }
 
     private class MockInjector extends TrustManagerService.Injector {
@@ -215,6 +244,11 @@
         }
 
         @Override
+        AlarmManager getAlarmManager() {
+            return mAlarmManager;
+        }
+
+        @Override
         Looper getLooper() {
             return mHandlerThread.getLooper();
         }
@@ -367,12 +401,10 @@
 
     @Test
     public void reportEnabledTrustAgentsChangedInformsListener() throws RemoteException {
-        final ITrustListener trustListener = mock(ITrustListener.class);
-        mTrustManager.registerTrustListener(trustListener);
         mService.waitForIdle();
         mTrustManager.reportEnabledTrustAgentsChanged(TEST_USER_ID);
         mService.waitForIdle();
-        verify(trustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
+        verify(mTrustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
     }
 
     // Tests that when the device is locked for a managed profile with a *unified* challenge, the
@@ -380,7 +412,6 @@
     // user, not the profile.  This matches the authentication that is needed to unlock the device
     // for the profile again.
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testLockDeviceForManagedProfileWithUnifiedChallenge_usesParentBiometricSids()
             throws Exception {
         setupMocksForProfile(/* unifiedChallenge= */ true);
@@ -416,7 +447,169 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
+    public void testSuccessfulUnlock_bindsTrustAgent() throws Exception {
+        when(mUserManager.getAliveUsers())
+                .thenReturn(List.of(new UserInfo(TEST_USER_ID, "test", UserInfo.FLAG_FULL)));
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgentService =
+                setUpTrustAgentWithStrongAuthRequired(
+                        trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        assertThat(getCallback(trustAgentService)).isNotNull();
+    }
+
+    @Test
+    public void testSuccessfulUnlock_notifiesTrustAgent() throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgentService =
+                setUpTrustAgentWithStrongAuthRequired(
+                        trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(trustAgentService).onUnlockAttempt(/* successful= */ true);
+    }
+
+    @Test
+    public void testSuccessfulUnlock_notifiesTrustListenerOfChangeInManagedTrust()
+            throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+        mService.waitForIdle();
+        Mockito.reset(mTrustListener);
+
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(mTrustListener).onTrustManagedChanged(false, TEST_USER_ID);
+    }
+
+    @Test
+    @Ignore("TODO: b/340891566 - Trustagent always refreshes trustable timer for user 0 on unlock")
+    public void testSuccessfulUnlock_refreshesTrustableTimers() throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgent =
+                setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+        setUpRenewableTrust(trustAgent);
+
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        // Idle and hard timeout alarms for first renewable trust granted
+        // Idle timeout alarm refresh for second renewable trust granted
+        // Idle and hard timeout alarms refresh for last report
+        verify(mAlarmManager, times(3))
+                .setExact(
+                        eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                        anyLong(),
+                        anyString(),
+                        any(AlarmManager.OnAlarmListener.class),
+                        any(Handler.class));
+    }
+
+    @Test
+    public void testFailedUnlock_doesNotBindTrustAgent() throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgentService =
+                setUpTrustAgentWithStrongAuthRequired(
+                        trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+        attemptFailedUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(trustAgentService, never()).setCallback(any());
+    }
+
+    @Test
+    public void testFailedUnlock_notifiesTrustAgent() throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgentService =
+                setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+
+        attemptFailedUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(trustAgentService).onUnlockAttempt(/* successful= */ false);
+    }
+
+    @Test
+    public void testFailedUnlock_doesNotNotifyTrustListenerOfChangeInManagedTrust()
+            throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+        Mockito.reset(mTrustListener);
+
+        attemptFailedUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(mTrustListener, never()).onTrustManagedChanged(anyBoolean(), anyInt());
+    }
+
+    private void setUpRenewableTrust(ITrustAgentService trustAgent) throws RemoteException {
+        ITrustAgentServiceCallback callback = getCallback(trustAgent);
+        callback.setManagingTrust(true);
+        mService.waitForIdle();
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+        when(mWindowManager.isKeyguardLocked()).thenReturn(false);
+        grantRenewableTrust(callback);
+    }
+
+    private ITrustAgentService setUpTrustAgentWithStrongAuthRequired(
+            ComponentName agentName, @StrongAuthFlags int strongAuthFlags) throws Exception {
+        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(TEST_USER_ID);
+        addTrustAgent(agentName, true);
+        mLockPatternUtils.setKnownTrustAgents(singleton(agentName), TEST_USER_ID);
+        mLockPatternUtils.setEnabledTrustAgents(singleton(agentName), TEST_USER_ID);
+        when(mUserManager.isUserUnlockingOrUnlocked(TEST_USER_ID)).thenReturn(true);
+        setupStrongAuthTracker(strongAuthFlags, false);
+        mService.waitForIdle();
+        return getOrCreateMockTrustAgent(agentName);
+    }
+
+    private void attemptSuccessfulUnlock(int userId) throws RemoteException {
+        mTrustManager.reportUnlockAttempt(/* successful= */ true, userId);
+    }
+
+    private void attemptFailedUnlock(int userId) throws RemoteException {
+        mTrustManager.reportUnlockAttempt(/* successful= */ false, userId);
+    }
+
+    private void grantRenewableTrust(ITrustAgentServiceCallback callback) throws RemoteException {
+        Log.i(TAG, "Granting trust");
+        AndroidFuture<GrantTrustResult> future = new AndroidFuture<>();
+        callback.grantTrust(
+                GRANT_TRUST_MESSAGE,
+                RENEWABLE_TRUST_DURATION,
+                FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE,
+                future);
+        mService.waitForIdle();
+    }
+
+    /**
+     * Retrieve the ITrustAgentServiceCallback attached to a TrustAgentService after it has been
+     * bound to by the TrustManagerService. Will fail if no binding was established.
+     */
+    private ITrustAgentServiceCallback getCallback(ITrustAgentService trustAgentService)
+            throws RemoteException {
+        ArgumentCaptor<ITrustAgentServiceCallback> callbackCaptor =
+                ArgumentCaptor.forClass(ITrustAgentServiceCallback.class);
+        verify(trustAgentService).setCallback(callbackCaptor.capture());
+        return callbackCaptor.getValue();
+    }
+
+    @Test
     public void testKeystoreWeakUnlockEnabled_whenWeakFingerprintIsSetupAndAllowed()
             throws Exception {
         setupStrongAuthTrackerToAllowEverything();
@@ -425,7 +618,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockEnabled_whenWeakFaceIsSetupAndAllowed() throws Exception {
         setupStrongAuthTrackerToAllowEverything();
         setupFace(SensorProperties.STRENGTH_WEAK);
@@ -433,7 +625,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockEnabled_whenConvenienceFingerprintIsSetupAndAllowed()
             throws Exception {
         setupStrongAuthTrackerToAllowEverything();
@@ -442,7 +633,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockEnabled_whenConvenienceFaceIsSetupAndAllowed()
             throws Exception {
         setupStrongAuthTrackerToAllowEverything();
@@ -451,7 +641,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockDisabled_whenStrongAuthRequired() throws Exception {
         setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, true);
         setupFace(SensorProperties.STRENGTH_WEAK);
@@ -459,7 +648,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockDisabled_whenNonStrongBiometricNotAllowed() throws Exception {
         setupStrongAuthTracker(StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED,
                 /* isNonStrongBiometricAllowed= */ false);
@@ -468,7 +656,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockDisabled_whenWeakFingerprintSensorIsPresentButNotEnrolled()
             throws Exception {
         setupStrongAuthTrackerToAllowEverything();
@@ -477,7 +664,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockDisabled_whenWeakFaceSensorIsPresentButNotEnrolled()
             throws Exception {
         setupStrongAuthTrackerToAllowEverything();
@@ -486,7 +672,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void
             testKeystoreWeakUnlockDisabled_whenWeakFingerprintIsSetupButForbiddenByDevicePolicy()
             throws Exception {
@@ -498,7 +683,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockDisabled_whenWeakFaceIsSetupButForbiddenByDevicePolicy()
             throws Exception {
         setupStrongAuthTrackerToAllowEverything();
@@ -509,7 +693,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockDisabled_whenOnlyStrongFingerprintIsSetup() throws Exception {
         setupStrongAuthTrackerToAllowEverything();
         setupFingerprint(SensorProperties.STRENGTH_STRONG);
@@ -517,7 +700,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockDisabled_whenOnlyStrongFaceIsSetup() throws Exception {
         setupStrongAuthTrackerToAllowEverything();
         setupFace(SensorProperties.STRENGTH_STRONG);
@@ -525,7 +707,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockDisabled_whenNoBiometricsAreSetup() throws Exception {
         setupStrongAuthTrackerToAllowEverything();
         verifyWeakUnlockDisabled();
@@ -637,6 +818,20 @@
         ResolveInfo resolveInfo = new ResolveInfo();
         resolveInfo.serviceInfo = serviceInfo;
         mTrustAgentResolveInfoList.add(resolveInfo);
+        ITrustAgentService.Stub mockService = getOrCreateMockTrustAgent(agentComponentName);
+        mMockContext.addMockService(agentComponentName, mockService);
+        mMockTrustAgents.put(agentComponentName, mockService);
+    }
+
+    private ITrustAgentService.Stub getOrCreateMockTrustAgent(ComponentName agentComponentName) {
+        return mMockTrustAgents.computeIfAbsent(
+                agentComponentName,
+                (componentName) -> {
+                    ITrustAgentService.Stub mockTrustAgent = mock(ITrustAgentService.Stub.class);
+                    when(mockTrustAgent.queryLocalInterface(anyString()))
+                            .thenReturn(mockTrustAgent);
+                    return mockTrustAgent;
+                });
     }
 
     private void bootService() {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index f9077c4..93fc071a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -196,11 +196,6 @@
     }
 
     @Override
-    void setKeystorePassword(byte[] password, int userHandle) {
-
-    }
-
-    @Override
     void initKeystoreSuperKeys(int userId, SyntheticPassword sp, boolean allowExisting) {
     }
 
diff --git a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
index d0e5626..0c3c7e2 100644
--- a/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
+++ b/tests/TrustTests/src/android/trust/test/GrantAndRevokeTrustTest.kt
@@ -17,9 +17,6 @@
 package android.trust.test
 
 import android.content.pm.PackageManager
-import android.platform.test.annotations.RequiresFlagsDisabled
-import android.platform.test.annotations.RequiresFlagsEnabled
-import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.service.trust.GrantTrustResult
 import android.trust.BaseTrustAgentService
 import android.trust.TrustTestActivity
@@ -58,7 +55,6 @@
         .around(ScreenLockRule())
         .around(lockStateTrackingRule)
         .around(trustAgentRule)
-        .around(DeviceFlagsValueProvider.createCheckFlagsRule())
 
     @Before
     fun manageTrust() {
@@ -93,7 +89,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     fun grantCannotActivelyUnlockDevice() {
         // On automotive, trust agents can actively unlock the device.
         assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
@@ -120,24 +115,6 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
-    fun grantCouldCauseWrongDeviceLockedStateDueToBug() {
-        // On automotive, trust agents can actively unlock the device.
-        assumeFalse(packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE))
-
-        // Verify that b/296464083 exists.  That is, when the device is locked
-        // and a trust agent grants trust, the deviceLocked state incorrectly
-        // becomes false even though the device correctly remains locked.
-        uiDevice.sleep()
-        lockStateTrackingRule.assertLocked()
-        trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 10000, 0) {}
-        uiDevice.wakeUp()
-        uiDevice.sleep()
-        await()
-        lockStateTrackingRule.assertUnlockedButNotReally()
-    }
-
-    @Test
     fun grantDoesNotCallBack() {
         val callback = mock<(GrantTrustResult) -> Unit>()
         trustAgentRule.agent.grantTrust(GRANT_MESSAGE, 0, 0, callback)
diff --git a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
index 0121809..80d7947 100644
--- a/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/LockStateTrackingRule.kt
@@ -64,13 +64,6 @@
         wait("not trusted") { trustState.trusted == false }
     }
 
-    // TODO(b/299298338) remove this when removing FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2
-    fun assertUnlockedButNotReally() {
-        wait("device unlocked") { !keyguardManager.isDeviceLocked }
-        wait("not trusted") { trustState.trusted == false }
-        wait("keyguard locked") { windowManager.isKeyguardLocked }
-    }
-
     fun assertUnlockedAndTrusted() {
         wait("device unlocked") { !keyguardManager.isDeviceLocked }
         wait("trusted") { trustState.trusted == true }