diff options
16 files changed, 1297 insertions, 3 deletions
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp index e092499cb41f..65bc8ccd12f8 100644 --- a/apct-tests/perftests/core/Android.bp +++ b/apct-tests/perftests/core/Android.bp @@ -44,6 +44,7 @@ android_test { "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 000000000000..bdc2a829a95b --- /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 000000000000..c69ae39846bd --- /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 000000000000..f8a3d5f2ed04 --- /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 000000000000..1a7258a802df --- /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 000000000000..dd9f4eb7e8d3 --- /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 000000000000..0655f45726ba --- /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 000000000000..7efabfd3758c --- /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 000000000000..4defe71fcddd --- /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 000000000000..3631c3f29287 --- /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 000000000000..ba2a65a17e84 --- /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 000000000000..78fe73262e4c --- /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 c7e5d88c299d..a6eed50a594a 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4419,7 +4419,8 @@ public abstract class Context { * @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 @@ public abstract class Context { * <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 e0cf0a5f8178..a475c2925881 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -932,7 +932,8 @@ public class ContextWrapper extends Context { } @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 1dfdd29e480a..d0f797e5c6b8 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 5966c9f759fb..62ed66cdce67 100644 --- a/packages/SettingsLib/OWNERS +++ b/packages/SettingsLib/OWNERS @@ -11,3 +11,6 @@ ykhung@google.com # 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 |