Initial port of BenchmarkState to androidx

Bug: 72877822
Test: ./gradlew benchmark:cC

Added minimal tests, no renaming at this point.

Change-Id: I509dd6e0d9104dc290bb3ee9bed1a78b36d0c8a1
diff --git a/benchmark/build.gradle b/benchmark/build.gradle
new file mode 100644
index 0000000..babffd4
--- /dev/null
+++ b/benchmark/build.gradle
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+import androidx.build.LibraryGroups
+import androidx.build.LibraryVersions
+
+plugins {
+    id("SupportAndroidLibraryPlugin")
+    id("kotlin-android")
+}
+
+dependencies {
+    api(project(":annotation"))
+
+    implementation(TEST_RUNNER)
+
+    androidTestImplementation(JUNIT)
+    androidTestImplementation(KOTLIN_STDLIB)
+}
+
+supportLibrary {
+    name = "Android Benchmark"
+    publish = false
+    mavenVersion = LibraryVersions.BENCHMARK
+    mavenGroup = LibraryGroups.BENCHMARK
+    inceptionYear = "2018"
+    description = "Android Benchmark"
+    failOnUncheckedWarnings = true
+    failOnDeprecationWarnings = true
+}
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt b/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
new file mode 100644
index 0000000..fc9f4a5
--- /dev/null
+++ b/benchmark/src/androidTest/java/androidx/benchmark/BenchmarkStateTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 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 androidx.benchmark
+
+import android.support.test.filters.SmallTest
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import java.util.concurrent.TimeUnit
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BenchmarkStateTest {
+    fun ms2ns(ms: Long): Long = TimeUnit.MILLISECONDS.toNanos(ms)
+
+    @Test
+    fun simple() {
+        // would be better to mock the clock, but going with minimal changes for now
+        val state = BenchmarkState()
+        while (state.keepRunning()) {
+            Thread.sleep(3)
+            state.pauseTiming()
+            Thread.sleep(5)
+            state.resumeTiming()
+        }
+        val median = state.stats.median
+        assertTrue("median $median should be between 2ms and 4ms",
+                ms2ns(2) < median && median < ms2ns(4))
+    }
+}
diff --git a/benchmark/src/androidTest/java/androidx/benchmark/StatsTest.kt b/benchmark/src/androidTest/java/androidx/benchmark/StatsTest.kt
new file mode 100644
index 0000000..5880a36
--- /dev/null
+++ b/benchmark/src/androidTest/java/androidx/benchmark/StatsTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 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 androidx.benchmark
+
+import android.support.test.filters.SmallTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class StatsTest {
+    @Test
+    fun simple() {
+        val stats = Stats(listOf(10, 10, 10, 10))
+        assertEquals(10.0, stats.mean, 0.0)
+        assertEquals(10, stats.median)
+        assertEquals(10, stats.max)
+        assertEquals(10, stats.min)
+        assertEquals(0.0, stats.standardDeviation, 0.0)
+        assertEquals(10, stats.percentile90)
+        assertEquals(10, stats.percentile95)
+    }
+
+    @Test
+    fun percentile() {
+        val stats = Stats((1L..100L).toList())
+        assertEquals(50.5, stats.mean, 0.0)
+        assertTrue(stats.median == 50L || stats.median == 51L)
+        assertEquals(100, stats.max)
+        assertEquals(1, stats.min)
+        assertEquals(29.01, stats.standardDeviation, 0.05)
+        assertEquals(90, stats.percentile90)
+        assertEquals(95, stats.percentile95)
+    }
+}
diff --git a/benchmark/src/main/AndroidManifest.xml b/benchmark/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f4d89ba
--- /dev/null
+++ b/benchmark/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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.
+  -->
+<manifest package="androidx.benchmark"/>
diff --git a/benchmark/src/main/java/androidx/benchmark/BenchmarkState.java b/benchmark/src/main/java/androidx/benchmark/BenchmarkState.java
new file mode 100644
index 0000000..f6ac275
--- /dev/null
+++ b/benchmark/src/main/java/androidx/benchmark/BenchmarkState.java
@@ -0,0 +1,262 @@
+/*
+ * 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 androidx.benchmark;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Debug;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provides a benchmark framework.
+ *
+ * Example usage:
+ * // Executes the code while keepRunning returning true.
+ *
+ * public void sampleMethod() {
+ *     BenchmarkState state = new BenchmarkState();
+ *
+ *     int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+ *     while (state.keepRunning()) {
+ *         int[] dest = new int[src.length];
+ *         System.arraycopy(src, 0, dest, 0, src.length);
+ *     }
+ *     System.out.println(state.summaryLine());
+ * }
+ */
+public final class BenchmarkState {
+
+    private static final String TAG = "BenchmarkState";
+    private static final boolean ENABLE_PROFILING = false;
+
+    private static final int NOT_STARTED = 0;  // The benchmark has not started yet.
+    private static final int WARMUP = 1; // The benchmark is warming up.
+    private static final int RUNNING = 2;  // The benchmark is running.
+    private static final int FINISHED = 3;  // The benchmark has stopped.
+
+    private int mState = NOT_STARTED;  // Current benchmark state.
+
+    private static final long WARMUP_DURATION_NS = ms2ns(250); // warm-up for at least 250ms
+    private static final int WARMUP_MIN_ITERATIONS = 16; // minimum iterations to warm-up for
+
+    // TODO: Tune these values.
+    private static final long TARGET_TEST_DURATION_NS = ms2ns(500); // target testing for 500 ms
+    private static final int MAX_TEST_ITERATIONS = 1000000;
+    private static final int MIN_TEST_ITERATIONS = 10;
+    private static final int REPEAT_COUNT = 5;
+
+    private long mStartTimeNs = 0;  // Previously captured System.nanoTime().
+    private boolean mPaused;
+    private long mPausedTimeNs = 0; // The System.nanoTime() when the pauseTiming() is called.
+    private long mPausedDurationNs = 0;  // The duration of paused state in nano sec.
+
+    private int mIteration = 0;
+    private int mMaxIterations = 0;
+
+    private int mRepeatCount = 0;
+
+    // Statistics. These values will be filled when the benchmark has finished.
+    // The computation needs double precision, but long int is fine for final reporting.
+    private Stats mStats;
+
+    // Individual duration in nano seconds.
+    private ArrayList<Long> mResults = new ArrayList<>();
+
+    private static long ms2ns(long ms) {
+        return TimeUnit.MILLISECONDS.toNanos(ms);
+    }
+
+    /**
+     * Stops the benchmark timer.
+     * <p>
+     * This method can be called only when the timer is running.
+     */
+    public void pauseTiming() {
+        if (mPaused) {
+            throw new IllegalStateException(
+                    "Unable to pause the benchmark. The benchmark has already paused.");
+        }
+        mPausedTimeNs = System.nanoTime();
+        mPaused = true;
+    }
+
+    /**
+     * Starts the benchmark timer.
+     * <p>
+     * This method can be called only when the timer is stopped.
+     */
+    public void resumeTiming() {
+        if (!mPaused) {
+            throw new IllegalStateException(
+                    "Unable to resume the benchmark. The benchmark is already running.");
+        }
+        mPausedDurationNs += System.nanoTime() - mPausedTimeNs;
+        mPausedTimeNs = 0;
+        mPaused = false;
+    }
+
+    private void beginWarmup() {
+        mStartTimeNs = System.nanoTime();
+        mIteration = 0;
+        mState = WARMUP;
+    }
+
+    private void beginBenchmark(long warmupDuration, int iterations) {
+        if (ENABLE_PROFILING && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            // TODO: support data dir for old platforms
+            File f = new File(InstrumentationRegistry.getContext().getDataDir(), "benchprof");
+            Log.d(TAG, "Tracing to: " + f.getAbsolutePath());
+            Debug.startMethodTracingSampling(f.getAbsolutePath(), 16 * 1024 * 1024, 100);
+        }
+        mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
+        mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
+                Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
+        mPausedDurationNs = 0;
+        mIteration = 0;
+        mRepeatCount = 0;
+        mState = RUNNING;
+        mStartTimeNs = System.nanoTime();
+    }
+
+    private boolean startNextTestRun() {
+        final long currentTime = System.nanoTime();
+        mResults.add((currentTime - mStartTimeNs - mPausedDurationNs) / mMaxIterations);
+        mRepeatCount++;
+        if (mRepeatCount >= REPEAT_COUNT) {
+            if (ENABLE_PROFILING) {
+                Debug.stopMethodTracing();
+            }
+            mStats = new Stats(mResults);
+            mState = FINISHED;
+            return false;
+        }
+        mPausedDurationNs = 0;
+        mIteration = 0;
+        mStartTimeNs = System.nanoTime();
+        return true;
+    }
+
+    /**
+     * Judges whether the benchmark needs more samples.
+     *
+     * For the usage, see class comment.
+     */
+    public boolean keepRunning() {
+        switch (mState) {
+            case NOT_STARTED:
+                beginWarmup();
+                return true;
+            case WARMUP:
+                mIteration++;
+                // Only check nanoTime on every iteration in WARMUP since we
+                // don't yet have a target iteration count.
+                final long duration = System.nanoTime() - mStartTimeNs;
+                if (mIteration >= WARMUP_MIN_ITERATIONS && duration >= WARMUP_DURATION_NS) {
+                    beginBenchmark(duration, mIteration);
+                }
+                return true;
+            case RUNNING:
+                mIteration++;
+                if (mIteration >= mMaxIterations) {
+                    return startNextTestRun();
+                }
+                if (mPaused) {
+                    throw new IllegalStateException("Benchmark step finished with paused state. "
+                            + "Resume the benchmark before finishing each step.");
+                }
+                return true;
+            case FINISHED:
+                throw new IllegalStateException("The benchmark has finished.");
+            default:
+                throw new IllegalStateException("The benchmark is in unknown state.");
+        }
+    }
+
+    /**
+     * Get the end of run benchmark statistics.
+     * <p>
+     * This method may only be called keepRunning() returns {@code false}.
+     *
+     * @return Stats from run.
+     */
+    @NonNull
+    public Stats getStats() {
+        if (mState != FINISHED) {
+            throw new IllegalStateException("The benchmark hasn't finished");
+        }
+        return mStats;
+    }
+
+    private long mean() {
+        return (long) getStats().getMean();
+    }
+
+    private long median() {
+        return getStats().getMedian();
+    }
+
+    private long min() {
+        return getStats().getMin();
+    }
+
+    private long standardDeviation() {
+        return (long) getStats().getStandardDeviation();
+    }
+
+    private String summaryLine() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Summary: ");
+        sb.append("median=").append(median()).append("ns, ");
+        sb.append("mean=").append(mean()).append("ns, ");
+        sb.append("min=").append(min()).append("ns, ");
+        sb.append("sigma=").append(standardDeviation()).append(", ");
+        sb.append("iteration=").append(mResults.size()).append(", ");
+        // print out the first few iterations' number for double checking.
+        int sampleNumber = Math.min(mResults.size(), 16);
+        for (int i = 0; i < sampleNumber; i++) {
+            sb.append("No ").append(i).append(" result is ").append(mResults.get(i)).append(", ");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Submit status report bundle as a RESULT_OK to the passed Instrumentation
+     *
+     * @param instrumentation Instrumentation used to signal result.
+     * @param key Run identifier, prepended to bundle properties.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public void sendFullStatusReport(@NonNull Instrumentation instrumentation,
+            @NonNull String key) {
+        Log.i(TAG, key + summaryLine());
+        Bundle status = new Bundle();
+        status.putLong(key + "_median", median());
+        status.putLong(key + "_mean", mean());
+        status.putLong(key + "_min", min());
+        status.putLong(key + "_standardDeviation", standardDeviation());
+        instrumentation.sendStatus(Activity.RESULT_OK, status);
+    }
+}
diff --git a/benchmark/src/main/java/androidx/benchmark/PerfStatusReporter.java b/benchmark/src/main/java/androidx/benchmark/PerfStatusReporter.java
new file mode 100644
index 0000000..d508d00
--- /dev/null
+++ b/benchmark/src/main/java/androidx/benchmark/PerfStatusReporter.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2018 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 androidx.benchmark;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Use this rule to make sure we report the status after the test success.
+ *
+ * <code>
+ *
+ * @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ * @Test public void functionName() {
+ *     ...
+ *     BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ *     while (state.keepRunning()) {
+ *         // DO YOUR TEST HERE!
+ *     }
+ *     ...
+ * }
+ * </code>
+ *
+ * When test succeeded, the status report will use the key as
+ * "functionName[optional subTestName]_*"
+ *
+ * Notice that optional subTestName can't be just numbers, that means each sub test needs to have a
+ * name when using parameterization.
+ */
+
+public class PerfStatusReporter implements TestRule {
+    private static final String TAG = "PerfStatusReporter";
+    @SuppressWarnings("WeakerAccess") // synthetic access
+    final BenchmarkState mState = new BenchmarkState();
+
+    @NonNull
+    public BenchmarkState getBenchmarkState() {
+        return mState;
+    }
+
+    @NonNull
+    @Override
+    public Statement apply(@NonNull final Statement base, @NonNull final Description description) {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                String invokeMethodName = description.getMethodName();
+                Log.i(TAG, "Running " + description.getClassName() + "#" + invokeMethodName);
+
+                // validate and simplify the function name.
+                // First, remove the "test" prefix which normally comes from CTS test.
+                // Then make sure the [subTestName] is valid, not just numbers like [0].
+                if (invokeMethodName.startsWith("test")) {
+                    assertTrue("The test name " + invokeMethodName + " is too short",
+                            invokeMethodName.length() > 5);
+                    invokeMethodName = invokeMethodName.substring(4, 5).toLowerCase()
+                            + invokeMethodName.substring(5);
+                }
+
+                int index = invokeMethodName.lastIndexOf('[');
+                if (index > 0) {
+                    boolean allDigits = true;
+                    for (int i = index + 1; i < invokeMethodName.length() - 1; i++) {
+                        if (!Character.isDigit(invokeMethodName.charAt(i))) {
+                            allDigits = false;
+                            break;
+                        }
+                    }
+                    assertFalse("The name in [] can't contain only digits for " + invokeMethodName,
+                            allDigits);
+                }
+
+                base.evaluate();
+
+                mState.sendFullStatusReport(InstrumentationRegistry.getInstrumentation(),
+                        invokeMethodName);
+            }
+        };
+    }
+}
+
diff --git a/benchmark/src/main/java/androidx/benchmark/Stats.java b/benchmark/src/main/java/androidx/benchmark/Stats.java
new file mode 100644
index 0000000..28c2bbc
--- /dev/null
+++ b/benchmark/src/main/java/androidx/benchmark/Stats.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 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 androidx.benchmark;
+
+import androidx.annotation.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Provides statistics such as mean, median, min, max, and percentiles, given a list of input
+ * values.
+ */
+public class Stats {
+    private long mMedian, mMin, mMax, mPercentile90, mPercentile95;
+    private double mMean, mStandardDeviation;
+
+    /* Calculate stats in constructor. */
+    public Stats(@NonNull List<Long> values) {
+        // make a copy since we're modifying it
+        values = new ArrayList<>(values);
+        final int size = values.size();
+        if (size < 2) {
+            throw new IllegalArgumentException("At least two results are necessary.");
+        }
+
+        Collections.sort(values);
+
+        mMin = values.get(0);
+        mMax = values.get(values.size() - 1);
+
+        mMedian = size % 2 == 0
+                ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2
+                : values.get(size / 2);
+        mPercentile90 = getPercentile(values, 90);
+        mPercentile95 = getPercentile(values, 95);
+
+        for (int i = 0; i < size; ++i) {
+            long result = values.get(i);
+            mMean += result;
+        }
+        mMean /= (double) size;
+
+        for (int i = 0; i < size; ++i) {
+            final double tmp = values.get(i) - mMean;
+            mStandardDeviation += tmp * tmp;
+        }
+        mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1));
+    }
+
+    public double getMean() {
+        return mMean;
+    }
+
+    public long getMedian() {
+        return mMedian;
+    }
+
+    public long getMax() {
+        return mMax;
+    }
+
+    public long getMin() {
+        return mMin;
+    }
+
+    public double getStandardDeviation() {
+        return mStandardDeviation;
+    }
+
+    public long getPercentile90() {
+        return mPercentile90;
+    }
+
+    public long getPercentile95() {
+        return mPercentile95;
+    }
+
+    private static long getPercentile(List<Long> values, int percentile) {
+        if (percentile < 0 || percentile > 100) {
+            throw new IllegalArgumentException(
+                    "invalid percentile " + percentile + ", should be 0-100");
+        }
+        int idx = (values.size() - 1) * percentile / 100;
+        return values.get(idx);
+    }
+}
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
index f206a87..78e93f0 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryGroups.kt
@@ -24,6 +24,7 @@
     const val APPCOMPAT = "androidx.appcompat"
     const val ASYNCLAYOUTINFLATER = "androidx.asynclayoutinflater"
     const val BROWSER = "androidx.browser"
+    const val BENCHMARK = "androidx.benchmark"
     const val CAR = "androidx.car"
     const val CARDVIEW = "androidx.cardview"
     const val COLLECTION = "androidx.collection"
diff --git a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
index b5be3c8..d66e216 100644
--- a/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/LibraryVersions.kt
@@ -112,6 +112,11 @@
      */
     val MEDIA2 = Version("1.0.0-alpha01")
 
+    /*
+     * Version code for Benchmark
+     */
+    val BENCHMARK = Version("1.0.0-alpha01")
+
     /**
      * Version code for Leanback
      */
diff --git a/settings.gradle b/settings.gradle
index 507a94b..deb845d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -39,6 +39,7 @@
 includeProject(":arch:core-testing", "app-toolkit/core-testing")
 includeProject(":arch:core-runtime", "app-toolkit/runtime")
 includeProject(":asynclayoutinflater", "asynclayoutinflater")
+includeProject(":benchmark", "benchmark")
 includeProject(":browser", "browser")
 includeProject(":car", "car")
 includeProject(":cardview", "cardview")