summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt7
-rw-r--r--tests/Tracing/src/android/tracing/perfetto/DataSourceTest.java709
-rw-r--r--tests/Tracing/src/android/tracing/perfetto/TestDataSource.java122
-rw-r--r--tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java60
-rw-r--r--tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java58
5 files changed, 922 insertions, 34 deletions
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 2a82d5f9bd7c..351ec4635977 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -212,9 +212,10 @@ class InputManagerServiceTests {
verify(native).setMotionClassifierEnabled(anyBoolean())
verify(native).setMaximumObscuringOpacityForTouch(anyFloat())
verify(native).setStylusPointerIconEnabled(anyBoolean())
- // Called twice at boot, since there are individual callbacks to update the
- // key repeat timeout and the key repeat delay.
- verify(native, times(2)).setKeyRepeatConfiguration(anyInt(), anyInt())
+ // Called thrice at boot, since there are individual callbacks to update the
+ // key repeat timeout, the key repeat delay and whether key repeat enabled.
+ verify(native, times(3)).setKeyRepeatConfiguration(anyInt(), anyInt(),
+ anyBoolean())
}
@Test
diff --git a/tests/Tracing/src/android/tracing/perfetto/DataSourceTest.java b/tests/Tracing/src/android/tracing/perfetto/DataSourceTest.java
new file mode 100644
index 000000000000..bbeb18dfbecd
--- /dev/null
+++ b/tests/Tracing/src/android/tracing/perfetto/DataSourceTest.java
@@ -0,0 +1,709 @@
+/*
+ * Copyright (C) 2023 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.tracing.perfetto;
+
+import static android.internal.perfetto.protos.TestEventOuterClass.TestEvent.PAYLOAD;
+import static android.internal.perfetto.protos.TestEventOuterClass.TestEvent.TestPayload.SINGLE_INT;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.FOR_TESTING;
+
+import static java.io.File.createTempFile;
+import static java.nio.file.Files.createTempDirectory;
+
+import android.internal.perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
+import android.internal.perfetto.protos.TestConfigOuterClass.TestConfig;
+import android.tools.ScenarioBuilder;
+import android.tools.Tag;
+import android.tools.io.TraceType;
+import android.tools.traces.TraceConfig;
+import android.tools.traces.TraceConfigs;
+import android.tools.traces.io.ResultReader;
+import android.tools.traces.io.ResultWriter;
+import android.tools.traces.monitors.PerfettoTraceMonitor;
+import android.tools.traces.monitors.TraceMonitor;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.google.common.truth.Truth;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import perfetto.protos.PerfettoConfig;
+import perfetto.protos.TracePacketOuterClass;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+@RunWith(AndroidJUnit4.class)
+public class DataSourceTest {
+ private final File mTracingDirectory = createTempDirectory("temp").toFile();
+
+ private final ResultWriter mWriter = new ResultWriter()
+ .forScenario(new ScenarioBuilder()
+ .forClass(createTempFile("temp", "").getName()).build())
+ .withOutputDir(mTracingDirectory)
+ .setRunComplete();
+
+ private final TraceConfigs mTraceConfig = new TraceConfigs(
+ new TraceConfig(false, true, false),
+ new TraceConfig(false, true, false),
+ new TraceConfig(false, true, false),
+ new TraceConfig(false, true, false)
+ );
+
+ private static TestDataSource sTestDataSource;
+
+ private static TestDataSource.DataSourceInstanceProvider sInstanceProvider;
+ private static TestDataSource.TlsStateProvider sTlsStateProvider;
+ private static TestDataSource.IncrementalStateProvider sIncrementalStateProvider;
+
+ public DataSourceTest() throws IOException {}
+
+ @BeforeClass
+ public static void beforeAll() {
+ Producer.init(InitArguments.DEFAULTS);
+ setupProviders();
+ sTestDataSource = new TestDataSource(
+ (ds, idx, configStream) -> sInstanceProvider.provide(ds, idx, configStream),
+ args -> sTlsStateProvider.provide(args),
+ args -> sIncrementalStateProvider.provide(args));
+ sTestDataSource.register(DataSourceParams.DEFAULTS);
+ }
+
+ private static void setupProviders() {
+ sInstanceProvider = (ds, idx, configStream) ->
+ new TestDataSource.TestDataSourceInstance(ds, idx);
+ sTlsStateProvider = args -> new TestDataSource.TestTlsState();
+ sIncrementalStateProvider = args -> new TestDataSource.TestIncrementalState();
+ }
+
+ @Before
+ public void setup() {
+ setupProviders();
+ }
+
+ @Test
+ public void canTraceData() throws InvalidProtocolBufferException {
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+
+ sTestDataSource.trace((ctx) -> {
+ final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
+ long forTestingToken = protoOutputStream.start(FOR_TESTING);
+ long payloadToken = protoOutputStream.start(PAYLOAD);
+ protoOutputStream.write(SINGLE_INT, 10);
+ protoOutputStream.end(payloadToken);
+ protoOutputStream.end(forTestingToken);
+ });
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
+ assert rawProtoFromFile != null;
+ final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
+ .parseFrom(rawProtoFromFile);
+
+ Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
+ final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
+ .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
+ final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
+ .filter(it -> it.getForTesting().getPayload().getSingleInt() == 10).toList();
+ Truth.assertThat(matchingPackets).hasSize(1);
+ }
+
+ @Test
+ public void canUseTlsStateForCustomState() {
+ final int expectedStateTestValue = 10;
+ final AtomicInteger actualStateTestValue = new AtomicInteger();
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+
+ sTestDataSource.trace((ctx) -> {
+ TestDataSource.TestTlsState state = ctx.getCustomTlsState();
+ state.testStateValue = expectedStateTestValue;
+ });
+
+ sTestDataSource.trace((ctx) -> {
+ TestDataSource.TestTlsState state = ctx.getCustomTlsState();
+ actualStateTestValue.set(state.testStateValue);
+ });
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ Truth.assertThat(actualStateTestValue.get()).isEqualTo(expectedStateTestValue);
+ }
+
+ @Test
+ public void eachInstanceHasOwnTlsState() {
+ final int[] expectedStateTestValues = new int[] { 1, 2 };
+ final int[] actualStateTestValues = new int[] { 0, 0 };
+
+ final TraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+ final TraceMonitor traceMonitor2 = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor1.start();
+ try {
+ traceMonitor2.start();
+
+ AtomicInteger index = new AtomicInteger(0);
+ sTestDataSource.trace((ctx) -> {
+ TestDataSource.TestTlsState state = ctx.getCustomTlsState();
+ state.testStateValue = expectedStateTestValues[index.getAndIncrement()];
+ });
+
+ index.set(0);
+ sTestDataSource.trace((ctx) -> {
+ TestDataSource.TestTlsState state = ctx.getCustomTlsState();
+ actualStateTestValues[index.getAndIncrement()] = state.testStateValue;
+ });
+ } finally {
+ traceMonitor1.stop(mWriter);
+ }
+ } finally {
+ traceMonitor2.stop(mWriter);
+ }
+
+ Truth.assertThat(actualStateTestValues[0]).isEqualTo(expectedStateTestValues[0]);
+ Truth.assertThat(actualStateTestValues[1]).isEqualTo(expectedStateTestValues[1]);
+ }
+
+ @Test
+ public void eachThreadHasOwnTlsState() throws InterruptedException {
+ final int thread1ExpectedStateValue = 1;
+ final int thread2ExpectedStateValue = 2;
+
+ final AtomicInteger thread1ActualStateValue = new AtomicInteger();
+ final AtomicInteger thread2ActualStateValue = new AtomicInteger();
+
+ final CountDownLatch setUpLatch = new CountDownLatch(2);
+ final CountDownLatch setStateLatch = new CountDownLatch(2);
+ final CountDownLatch setOutStateLatch = new CountDownLatch(2);
+
+ final RunnableCreator createTask = (stateValue, stateOut) -> () -> {
+ Producer.init(InitArguments.DEFAULTS);
+
+ setUpLatch.countDown();
+
+ try {
+ setUpLatch.await(3, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ sTestDataSource.trace((ctx) -> {
+ TestDataSource.TestTlsState state = ctx.getCustomTlsState();
+ state.testStateValue = stateValue;
+ setStateLatch.countDown();
+ });
+
+ try {
+ setStateLatch.await(3, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ sTestDataSource.trace((ctx) -> {
+ stateOut.set(ctx.getCustomTlsState().testStateValue);
+ setOutStateLatch.countDown();
+ });
+ };
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+
+ new Thread(
+ createTask.create(thread1ExpectedStateValue, thread1ActualStateValue)).start();
+ new Thread(
+ createTask.create(thread2ExpectedStateValue, thread2ActualStateValue)).start();
+
+ setOutStateLatch.await(3, TimeUnit.SECONDS);
+
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ Truth.assertThat(thread1ActualStateValue.get()).isEqualTo(thread1ExpectedStateValue);
+ Truth.assertThat(thread2ActualStateValue.get()).isEqualTo(thread2ExpectedStateValue);
+ }
+
+ @Test
+ public void incrementalStateIsReset() throws InterruptedException {
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build())
+ .setIncrementalTimeout(10)
+ .build();
+
+ final AtomicInteger testStateValue = new AtomicInteger();
+ try {
+ traceMonitor.start();
+
+ sTestDataSource.trace(ctx -> ctx.getIncrementalState().testStateValue = 1);
+
+ // Timeout to make sure the incremental state is cleared.
+ Thread.sleep(1000);
+
+ sTestDataSource.trace(ctx ->
+ testStateValue.set(ctx.getIncrementalState().testStateValue));
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ Truth.assertThat(testStateValue.get()).isNotEqualTo(1);
+ }
+
+ @Test
+ public void getInstanceConfigOnCreateInstance() throws IOException {
+ final int expectedDummyIntValue = 10;
+ AtomicReference<ProtoInputStream> configStream = new AtomicReference<>();
+ sInstanceProvider = (ds, idx, config) -> {
+ configStream.set(config);
+ return new TestDataSource.TestDataSourceInstance(ds, idx);
+ };
+
+ final TraceMonitor monitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name)
+ .setForTesting(PerfettoConfig.TestConfig.newBuilder().setDummyFields(
+ PerfettoConfig.TestConfig.DummyFields.newBuilder()
+ .setFieldInt32(expectedDummyIntValue)
+ .build())
+ .build())
+ .build())
+ .build();
+
+ try {
+ monitor.start();
+ } finally {
+ monitor.stop(mWriter);
+ }
+
+ int configDummyIntValue = 0;
+ while (configStream.get().nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (configStream.get().getFieldNumber()
+ == (int) DataSourceConfig.FOR_TESTING) {
+ final long forTestingToken = configStream.get()
+ .start(DataSourceConfig.FOR_TESTING);
+ while (configStream.get().nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (configStream.get().getFieldNumber()
+ == (int) TestConfig.DUMMY_FIELDS) {
+ final long dummyFieldsToken = configStream.get()
+ .start(TestConfig.DUMMY_FIELDS);
+ while (configStream.get().nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (configStream.get().getFieldNumber()
+ == (int) TestConfig.DummyFields.FIELD_INT32) {
+ int val = configStream.get().readInt(
+ TestConfig.DummyFields.FIELD_INT32);
+ if (val != 0) {
+ configDummyIntValue = val;
+ break;
+ }
+ }
+ }
+ configStream.get().end(dummyFieldsToken);
+ break;
+ }
+ }
+ configStream.get().end(forTestingToken);
+ break;
+ }
+ }
+
+ Truth.assertThat(configDummyIntValue).isEqualTo(expectedDummyIntValue);
+ }
+
+ @Test
+ public void multipleTraceInstances() throws IOException, InterruptedException {
+ final int instanceCount = 3;
+
+ final List<TraceMonitor> monitors = new ArrayList<>();
+ final List<ResultWriter> writers = new ArrayList<>();
+
+ for (int i = 0; i < instanceCount; i++) {
+ final ResultWriter writer = new ResultWriter()
+ .forScenario(new ScenarioBuilder()
+ .forClass(createTempFile("temp", "").getName()).build())
+ .withOutputDir(mTracingDirectory)
+ .setRunComplete();
+ writers.add(writer);
+ }
+
+ // Start at 1 because 0 is considered null value so payload will be ignored in that case
+ TestDataSource.TestTlsState.lastIndex = 1;
+
+ final AtomicInteger traceCallCount = new AtomicInteger();
+ final CountDownLatch latch = new CountDownLatch(instanceCount);
+
+ try {
+ // Start instances
+ for (int i = 0; i < instanceCount; i++) {
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+ monitors.add(traceMonitor);
+ traceMonitor.start();
+ }
+
+ // Trace the stateIndex of the tracing instance.
+ sTestDataSource.trace(ctx -> {
+ final int testIntValue = ctx.getCustomTlsState().stateIndex;
+ traceCallCount.incrementAndGet();
+
+ final ProtoOutputStream os = ctx.newTracePacket();
+ long forTestingToken = os.start(FOR_TESTING);
+ long payloadToken = os.start(PAYLOAD);
+ os.write(SINGLE_INT, testIntValue);
+ os.end(payloadToken);
+ os.end(forTestingToken);
+
+ latch.countDown();
+ });
+ } finally {
+ // Stop instances
+ for (int i = 0; i < instanceCount; i++) {
+ final TraceMonitor monitor = monitors.get(i);
+ final ResultWriter writer = writers.get(i);
+ monitor.stop(writer);
+ }
+ }
+
+ latch.await(3, TimeUnit.SECONDS);
+ Truth.assertThat(traceCallCount.get()).isEqualTo(instanceCount);
+
+ for (int i = 0; i < instanceCount; i++) {
+ final int expectedTracedValue = i + 1;
+ final ResultWriter writer = writers.get(i);
+ final ResultReader reader = new ResultReader(writer.write(), mTraceConfig);
+ final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
+ assert rawProtoFromFile != null;
+ final perfetto.protos.TraceOuterClass.Trace trace =
+ perfetto.protos.TraceOuterClass.Trace.parseFrom(rawProtoFromFile);
+
+ Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
+ final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
+ .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
+ Truth.assertWithMessage("One packet has for testing data")
+ .that(tracePackets).hasSize(1);
+
+ final List<TracePacketOuterClass.TracePacket> matchingPackets =
+ tracePackets.stream()
+ .filter(it -> it.getForTesting().getPayload()
+ .getSingleInt() == expectedTracedValue).toList();
+ Truth.assertWithMessage(
+ "One packet has testing data with a payload with the expected value")
+ .that(matchingPackets).hasSize(1);
+ }
+ }
+
+ @Test
+ public void onStartCallbackTriggered() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ final AtomicBoolean callbackCalled = new AtomicBoolean(false);
+ sInstanceProvider = (ds, idx, config) -> new TestDataSource.TestDataSourceInstance(
+ ds,
+ idx,
+ (args) -> {
+ callbackCalled.set(true);
+ latch.countDown();
+ },
+ (args) -> {},
+ (args) -> {}
+ );
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ Truth.assertThat(callbackCalled.get()).isFalse();
+ try {
+ traceMonitor.start();
+ latch.await(3, TimeUnit.SECONDS);
+ Truth.assertThat(callbackCalled.get()).isTrue();
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+ }
+
+ @Test
+ public void onFlushCallbackTriggered() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicBoolean callbackCalled = new AtomicBoolean(false);
+ sInstanceProvider = (ds, idx, config) ->
+ new TestDataSource.TestDataSourceInstance(
+ ds,
+ idx,
+ (args) -> {},
+ (args) -> {
+ callbackCalled.set(true);
+ latch.countDown();
+ },
+ (args) -> {}
+ );
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+ Truth.assertThat(callbackCalled.get()).isFalse();
+ sTestDataSource.trace((ctx) -> {
+ final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
+ long forTestingToken = protoOutputStream.start(FOR_TESTING);
+ long payloadToken = protoOutputStream.start(PAYLOAD);
+ protoOutputStream.write(SINGLE_INT, 10);
+ protoOutputStream.end(payloadToken);
+ protoOutputStream.end(forTestingToken);
+ });
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ latch.await(3, TimeUnit.SECONDS);
+ Truth.assertThat(callbackCalled.get()).isTrue();
+ }
+
+ @Test
+ public void onStopCallbackTriggered() throws InterruptedException {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicBoolean callbackCalled = new AtomicBoolean(false);
+ sInstanceProvider = (ds, idx, config) ->
+ new TestDataSource.TestDataSourceInstance(
+ ds,
+ idx,
+ (args) -> {},
+ (args) -> {},
+ (args) -> {
+ callbackCalled.set(true);
+ latch.countDown();
+ }
+ );
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+ Truth.assertThat(callbackCalled.get()).isFalse();
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ latch.await(3, TimeUnit.SECONDS);
+ Truth.assertThat(callbackCalled.get()).isTrue();
+ }
+
+ @Test
+ public void canUseDataSourceInstanceToCreateTlsState() throws InvalidProtocolBufferException {
+ final Object testObject = new Object();
+
+ sInstanceProvider = (ds, idx, configStream) -> {
+ final TestDataSource.TestDataSourceInstance dsInstance =
+ new TestDataSource.TestDataSourceInstance(ds, idx);
+ dsInstance.testObject = testObject;
+ return dsInstance;
+ };
+
+ sTlsStateProvider = args -> {
+ final TestDataSource.TestTlsState tlsState = new TestDataSource.TestTlsState();
+
+ try (TestDataSource.TestDataSourceInstance dataSourceInstance =
+ args.getDataSourceInstanceLocked()) {
+ if (dataSourceInstance != null) {
+ tlsState.testStateValue = dataSourceInstance.testObject.hashCode();
+ }
+ }
+
+ return tlsState;
+ };
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+ sTestDataSource.trace((ctx) -> {
+ final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
+ long forTestingToken = protoOutputStream.start(FOR_TESTING);
+ long payloadToken = protoOutputStream.start(PAYLOAD);
+ protoOutputStream.write(SINGLE_INT, ctx.getCustomTlsState().testStateValue);
+ protoOutputStream.end(payloadToken);
+ protoOutputStream.end(forTestingToken);
+ });
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
+ assert rawProtoFromFile != null;
+ final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
+ .parseFrom(rawProtoFromFile);
+
+ Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
+ final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
+ .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
+ final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
+ .filter(it -> it.getForTesting().getPayload().getSingleInt()
+ == testObject.hashCode()).toList();
+ Truth.assertThat(matchingPackets).hasSize(1);
+ }
+
+ @Test
+ public void canUseDataSourceInstanceToCreateIncrementalState()
+ throws InvalidProtocolBufferException {
+ final Object testObject = new Object();
+
+ sInstanceProvider = (ds, idx, configStream) -> {
+ final TestDataSource.TestDataSourceInstance dsInstance =
+ new TestDataSource.TestDataSourceInstance(ds, idx);
+ dsInstance.testObject = testObject;
+ return dsInstance;
+ };
+
+ sIncrementalStateProvider = args -> {
+ final TestDataSource.TestIncrementalState incrementalState =
+ new TestDataSource.TestIncrementalState();
+
+ try (TestDataSource.TestDataSourceInstance dataSourceInstance =
+ args.getDataSourceInstanceLocked()) {
+ if (dataSourceInstance != null) {
+ incrementalState.testStateValue = dataSourceInstance.testObject.hashCode();
+ }
+ }
+
+ return incrementalState;
+ };
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+ sTestDataSource.trace((ctx) -> {
+ final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
+ long forTestingToken = protoOutputStream.start(FOR_TESTING);
+ long payloadToken = protoOutputStream.start(PAYLOAD);
+ protoOutputStream.write(SINGLE_INT, ctx.getIncrementalState().testStateValue);
+ protoOutputStream.end(payloadToken);
+ protoOutputStream.end(forTestingToken);
+ });
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
+ assert rawProtoFromFile != null;
+ final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
+ .parseFrom(rawProtoFromFile);
+
+ Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
+ final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
+ .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
+ final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
+ .filter(it -> it.getForTesting().getPayload().getSingleInt()
+ == testObject.hashCode()).toList();
+ Truth.assertThat(matchingPackets).hasSize(1);
+ }
+
+ @Test
+ public void canTraceOnFlush() throws InvalidProtocolBufferException, InterruptedException {
+ final int singleIntValue = 101;
+ sInstanceProvider = (ds, idx, config) ->
+ new TestDataSource.TestDataSourceInstance(
+ ds,
+ idx,
+ (args) -> {},
+ (args) -> sTestDataSource.trace(ctx -> {
+ final ProtoOutputStream protoOutputStream = ctx.newTracePacket();
+ long forTestingToken = protoOutputStream.start(FOR_TESTING);
+ long payloadToken = protoOutputStream.start(PAYLOAD);
+ protoOutputStream.write(SINGLE_INT, singleIntValue);
+ protoOutputStream.end(payloadToken);
+ protoOutputStream.end(forTestingToken);
+ }),
+ (args) -> {}
+ );
+
+ final TraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder()
+ .enableCustomTrace(PerfettoConfig.DataSourceConfig.newBuilder()
+ .setName(sTestDataSource.name).build()).build();
+
+ try {
+ traceMonitor.start();
+ } finally {
+ traceMonitor.stop(mWriter);
+ }
+
+ final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig);
+ final byte[] rawProtoFromFile = reader.readBytes(TraceType.PERFETTO, Tag.ALL);
+ assert rawProtoFromFile != null;
+ final perfetto.protos.TraceOuterClass.Trace trace = perfetto.protos.TraceOuterClass.Trace
+ .parseFrom(rawProtoFromFile);
+
+ Truth.assertThat(trace.getPacketCount()).isGreaterThan(0);
+ final List<TracePacketOuterClass.TracePacket> tracePackets = trace.getPacketList()
+ .stream().filter(TracePacketOuterClass.TracePacket::hasForTesting).toList();
+ final List<TracePacketOuterClass.TracePacket> matchingPackets = tracePackets.stream()
+ .filter(it -> it.getForTesting().getPayload().getSingleInt()
+ == singleIntValue).toList();
+ Truth.assertThat(matchingPackets).hasSize(1);
+ }
+
+ interface RunnableCreator {
+ Runnable create(int state, AtomicInteger stateOut);
+ }
+}
diff --git a/tests/Tracing/src/android/tracing/perfetto/TestDataSource.java b/tests/Tracing/src/android/tracing/perfetto/TestDataSource.java
new file mode 100644
index 000000000000..d78f78b1cb0e
--- /dev/null
+++ b/tests/Tracing/src/android/tracing/perfetto/TestDataSource.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2023 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.tracing.perfetto;
+
+import android.util.proto.ProtoInputStream;
+
+import java.util.UUID;
+import java.util.function.Consumer;
+
+public class TestDataSource extends DataSource<TestDataSource.TestDataSourceInstance,
+ TestDataSource.TestTlsState, TestDataSource.TestIncrementalState> {
+ private final DataSourceInstanceProvider mDataSourceInstanceProvider;
+ private final TlsStateProvider mTlsStateProvider;
+ private final IncrementalStateProvider mIncrementalStateProvider;
+
+ interface DataSourceInstanceProvider {
+ TestDataSourceInstance provide(
+ TestDataSource dataSource, int instanceIndex, ProtoInputStream configStream);
+ }
+
+ interface TlsStateProvider {
+ TestTlsState provide(CreateTlsStateArgs<TestDataSourceInstance> args);
+ }
+
+ interface IncrementalStateProvider {
+ TestIncrementalState provide(CreateIncrementalStateArgs<TestDataSourceInstance> args);
+ }
+
+ public TestDataSource() {
+ this((ds, idx, config) -> new TestDataSourceInstance(ds, idx),
+ args -> new TestTlsState(), args -> new TestIncrementalState());
+ }
+
+ public TestDataSource(
+ DataSourceInstanceProvider dataSourceInstanceProvider,
+ TlsStateProvider tlsStateProvider,
+ IncrementalStateProvider incrementalStateProvider
+ ) {
+ super("android.tracing.perfetto.TestDataSource#" + UUID.randomUUID().toString());
+ this.mDataSourceInstanceProvider = dataSourceInstanceProvider;
+ this.mTlsStateProvider = tlsStateProvider;
+ this.mIncrementalStateProvider = incrementalStateProvider;
+ }
+
+ @Override
+ public TestDataSourceInstance createInstance(ProtoInputStream configStream, int instanceIndex) {
+ return mDataSourceInstanceProvider.provide(this, instanceIndex, configStream);
+ }
+
+ @Override
+ public TestTlsState createTlsState(CreateTlsStateArgs args) {
+ return mTlsStateProvider.provide(args);
+ }
+
+ @Override
+ public TestIncrementalState createIncrementalState(CreateIncrementalStateArgs args) {
+ return mIncrementalStateProvider.provide(args);
+ }
+
+ public static class TestTlsState {
+ public int testStateValue;
+ public int stateIndex = lastIndex++;
+
+ public static int lastIndex = 0;
+ }
+
+ public static class TestIncrementalState {
+ public int testStateValue;
+ }
+
+ public static class TestDataSourceInstance extends DataSourceInstance {
+ public Object testObject;
+ Consumer<StartCallbackArguments> mStartCallback;
+ Consumer<FlushCallbackArguments> mFlushCallback;
+ Consumer<StopCallbackArguments> mStopCallback;
+
+ public TestDataSourceInstance(DataSource dataSource, int instanceIndex) {
+ this(dataSource, instanceIndex, args -> {}, args -> {}, args -> {});
+ }
+
+ public TestDataSourceInstance(
+ DataSource dataSource,
+ int instanceIndex,
+ Consumer<StartCallbackArguments> startCallback,
+ Consumer<FlushCallbackArguments> flushCallback,
+ Consumer<StopCallbackArguments> stopCallback) {
+ super(dataSource, instanceIndex);
+ this.mStartCallback = startCallback;
+ this.mFlushCallback = flushCallback;
+ this.mStopCallback = stopCallback;
+ }
+
+ @Override
+ public void onStart(StartCallbackArguments args) {
+ this.mStartCallback.accept(args);
+ }
+
+ @Override
+ public void onFlush(FlushCallbackArguments args) {
+ this.mFlushCallback.accept(args);
+ }
+
+ @Override
+ public void onStop(StopCallbackArguments args) {
+ this.mStopCallback.accept(args);
+ }
+ }
+}
diff --git a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index e841d9ea0880..cfb2645dee0d 100644
--- a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -16,6 +16,8 @@
package com.android.internal.protolog;
+import static android.tools.traces.Utils.executeShellCommand;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -42,12 +44,13 @@ import android.util.proto.ProtoInputStream;
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.internal.protolog.ProtoLogConfigurationService.ViewerConfigFileTracer;
+import com.android.internal.protolog.ProtoLogConfigurationServiceImpl.ViewerConfigFileTracer;
import com.android.internal.protolog.common.IProtoLogGroup;
import com.android.internal.protolog.common.LogDataType;
import com.android.internal.protolog.common.LogLevel;
import com.google.common.truth.Truth;
+import com.google.protobuf.InvalidProtocolBufferException;
import org.junit.After;
import org.junit.Before;
@@ -57,12 +60,14 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mockito;
+import perfetto.protos.PerfettoConfig.TracingServiceState;
import perfetto.protos.Protolog;
import perfetto.protos.ProtologCommon;
import java.io.File;
import java.io.IOException;
import java.util.List;
+import java.util.Optional;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
@@ -166,7 +171,8 @@ public class PerfettoProtoLogImplTest {
return new ProtoInputStream(sViewerConfigBuilder.build().toByteArray());
});
};
- sProtoLogConfigurationService = new ProtoLogConfigurationService(dataSourceBuilder, tracer);
+ sProtoLogConfigurationService =
+ new ProtoLogConfigurationServiceImpl(dataSourceBuilder, tracer);
if (android.tracing.Flags.clientSideProtoLogging()) {
sProtoLog = new PerfettoProtoLogImpl(
@@ -177,6 +183,8 @@ public class PerfettoProtoLogImplTest {
viewerConfigInputStreamProvider, sReader, () -> sCacheUpdater.run(),
TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService);
}
+
+ waitDataSourceIsAvailable();
}
@Before
@@ -862,6 +870,54 @@ public class PerfettoProtoLogImplTest {
.isEqualTo("This message should also be logged 567");
}
+ private static void waitDataSourceIsAvailable() {
+ final int timeoutMs = 10000;
+ final int busyWaitIntervalMs = 100;
+
+ int elapsedMs = 0;
+
+ while (!isDataSourceAvailable()) {
+ SystemClock.sleep(busyWaitIntervalMs);
+ elapsedMs += busyWaitIntervalMs;
+ if (elapsedMs >= timeoutMs) {
+ throw new RuntimeException("Data source didn't become available."
+ + " Waited for: " + timeoutMs + " ms");
+ }
+ }
+ }
+
+ private static boolean isDataSourceAvailable() {
+ byte[] proto = executeShellCommand("perfetto --query-raw");
+
+ try {
+ TracingServiceState state = TracingServiceState.parseFrom(proto);
+
+ Optional<Integer> producerId = Optional.empty();
+
+ for (TracingServiceState.Producer producer : state.getProducersList()) {
+ if (producer.getPid() == android.os.Process.myPid()) {
+ producerId = Optional.of(producer.getId());
+ break;
+ }
+ }
+
+ if (producerId.isEmpty()) {
+ return false;
+ }
+
+ for (TracingServiceState.DataSource ds : state.getDataSourcesList()) {
+ if (ds.getDsDescriptor().getName().equals(TEST_PROTOLOG_DATASOURCE_NAME)
+ && ds.getProducerId() == producerId.get()) {
+ return true;
+ }
+ }
+ } catch (InvalidProtocolBufferException e) {
+ throw new RuntimeException(e);
+ }
+
+ return false;
+ }
+
private enum TestProtoLogGroup implements IProtoLogGroup {
TEST_GROUP(true, true, false, "TEST_TAG");
diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java
index e1bdd777dc5f..a3d03a8278ed 100644
--- a/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java
+++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java
@@ -150,11 +150,11 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void canRegisterClientWithGroupsOnly() throws RemoteException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, true));
service.registerClient(mMockClient, args);
@@ -165,11 +165,11 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void willDumpViewerConfigOnlyOnceOnTraceStop()
throws RemoteException, InvalidProtocolBufferException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, true))
.setViewerConfigFile(mViewerConfigFile.getAbsolutePath());
service.registerClient(mMockClient, args);
@@ -200,13 +200,13 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void willDumpViewerConfigOnLastClientDisconnected()
throws RemoteException, FileNotFoundException {
- final ProtoLogConfigurationService.ViewerConfigFileTracer tracer =
- Mockito.mock(ProtoLogConfigurationService.ViewerConfigFileTracer.class);
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService(tracer);
+ final ProtoLogConfigurationServiceImpl.ViewerConfigFileTracer tracer =
+ Mockito.mock(ProtoLogConfigurationServiceImpl.ViewerConfigFileTracer.class);
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl(tracer);
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, true))
.setViewerConfigFile(mViewerConfigFile.getAbsolutePath());
service.registerClient(mMockClient, args);
@@ -225,10 +225,10 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void sendEnableLoggingToLogcatToClient() throws RemoteException {
- final var service = new ProtoLogConfigurationService();
+ final var service = new ProtoLogConfigurationServiceImpl();
- final var args = new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final var args = new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, false));
service.registerClient(mMockClient, args);
@@ -242,11 +242,11 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void sendDisableLoggingToLogcatToClient() throws RemoteException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, true));
service.registerClient(mMockClient, args);
@@ -260,11 +260,11 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void doNotSendLoggingToLogcatToClientWithoutRegisteredGroup() throws RemoteException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, false));
service.registerClient(mMockClient, args);
@@ -277,15 +277,15 @@ public class ProtoLogConfigurationServiceTest {
@Test
public void handlesToggleToLogcatBeforeClientIsRegistered() throws RemoteException {
- final ProtoLogConfigurationService service = new ProtoLogConfigurationService();
+ final ProtoLogConfigurationService service = new ProtoLogConfigurationServiceImpl();
Truth.assertThat(service.getGroups()).asList().doesNotContain(TEST_GROUP);
service.enableProtoLogToLogcat(TEST_GROUP);
Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue();
- final ProtoLogConfigurationService.RegisterClientArgs args =
- new ProtoLogConfigurationService.RegisterClientArgs()
- .setGroups(new ProtoLogConfigurationService.RegisterClientArgs
+ final ProtoLogConfigurationServiceImpl.RegisterClientArgs args =
+ new ProtoLogConfigurationServiceImpl.RegisterClientArgs()
+ .setGroups(new ProtoLogConfigurationServiceImpl.RegisterClientArgs
.GroupConfig(TEST_GROUP, false));
service.registerClient(mMockClient, args);