Merge "Adding @vaniadesmonda to wm shell desktopmode OWNERS" 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/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/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