Move window manager perf test to a new package

To isolate the test environment.

Bug: 155404627
Test: atest WmPerfTests

Change-Id: I1d9eda840056efc6c2d0696952ece55fd22b1547
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
new file mode 100644
index 0000000..f02cbcf
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2020 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.
+
+android_test {
+    name: "WmPerfTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.annotation_annotation",
+        "apct-perftests-utils",
+    ],
+    test_suites: ["device-tests"],
+    platform_apis: true,
+    certificate: "platform",
+}
diff --git a/apct-tests/perftests/windowmanager/AndroidManifest.xml b/apct-tests/perftests/windowmanager/AndroidManifest.xml
new file mode 100644
index 0000000..7198176
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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 xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.perftests.wm">
+
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.perftests.utils.PerfTestActivity">
+          <intent-filter>
+            <action android:name="com.android.perftests.core.PERFTEST" />
+          </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.perftests.wm"/>
+</manifest>
diff --git a/apct-tests/perftests/windowmanager/AndroidTest.xml b/apct-tests/perftests/windowmanager/AndroidTest.xml
new file mode 100644
index 0000000..69d187f
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<configuration description="Runs WmPerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-metric-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="WmPerfTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.perftests.wm" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/data/local/WmPerfTests" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
+</configuration>
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
new file mode 100644
index 0000000..4ed3b4e
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static android.perftests.utils.ManualBenchmarkState.StatsReport;
+
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.perftests.utils.TraceMarkParser;
+import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.lifecycle.Stage;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.concurrent.TimeUnit;
+
+/** Measure the performance of internal methods in window manager service by trace tag. */
+@LargeTest
+public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
+    private static final String TAG = InternalWindowOperationPerfTest.class.getSimpleName();
+
+    @Rule
+    public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
+
+    @Rule
+    public final PerfTestActivityRule mActivityRule = new PerfTestActivityRule();
+
+    private final TraceMarkParser mTraceMarkParser = new TraceMarkParser(
+            "applyPostLayoutPolicy",
+            "applySurfaceChanges",
+            "AppTransitionReady",
+            "closeSurfaceTransactiom",
+            "openSurfaceTransaction",
+            "performLayout",
+            "performSurfacePlacement",
+            "prepareSurfaces",
+            "updateInputWindows",
+            "WSA#startAnimation",
+            "activityIdle",
+            "activityPaused",
+            "activityStopped",
+            "activityDestroyed",
+            "finishActivity",
+            "startActivityInner");
+
+    @Test
+    @ManualBenchmarkTest(
+            targetTestDurationNs = 20 * TIME_1_S_IN_NS,
+            statsReport = @StatsReport(
+                    flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+                            | StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR))
+    public void testLaunchAndFinishActivity() throws Throwable {
+        final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        long measuredTimeNs = 0;
+        boolean isTraceStarted = false;
+
+        while (state.keepRunning(measuredTimeNs)) {
+            if (!isTraceStarted && !state.isWarmingUp()) {
+                startAsyncAtrace();
+                isTraceStarted = true;
+            }
+            final long startTime = SystemClock.elapsedRealtimeNanos();
+            mActivityRule.launchActivity();
+            mActivityRule.finishActivity();
+            mActivityRule.waitForIdleSync(Stage.DESTROYED);
+            measuredTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
+        }
+
+        stopAsyncAtrace();
+
+        mTraceMarkParser.forAllSlices((key, slices) -> {
+            for (TraceMarkSlice slice : slices) {
+                state.addExtraResult(key, (long) (slice.getDurationInSeconds() * NANOS_PER_S));
+            }
+        });
+
+        Log.i(TAG, String.valueOf(mTraceMarkParser));
+    }
+
+    private void startAsyncAtrace() throws IOException {
+        sUiAutomation.executeShellCommand("atrace -b 32768 --async_start wm");
+        // Avoid atrace isn't ready immediately.
+        SystemClock.sleep(TimeUnit.NANOSECONDS.toMillis(TIME_1_S_IN_NS));
+    }
+
+    private void stopAsyncAtrace() throws IOException {
+        final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand("atrace --async_stop");
+        final InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                mTraceMarkParser.visit(line);
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
new file mode 100644
index 0000000..1667c165
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static android.perftests.utils.ManualBenchmarkState.StatsReport;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.hamcrest.core.AnyOf.anyOf;
+import static org.hamcrest.core.Is.is;
+
+import android.app.ActivityManager.TaskSnapshot;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.util.Pair;
+import android.view.IRecentsAnimationController;
+import android.view.IRecentsAnimationRunner;
+import android.view.RemoteAnimationTarget;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.lifecycle.Stage;
+
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
+    private static Intent sRecentsIntent;
+
+    @Rule
+    public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
+
+    @Rule
+    public final PerfTestActivityRule mActivityRule =
+            new PerfTestActivityRule(true /* launchActivity */);
+
+    private long mMeasuredTimeNs;
+
+    /**
+     * Used to skip each test method if there is error. It cannot be raised in static setup because
+     * that will break the amount of target method count.
+     */
+    private static Exception sSetUpClassException;
+
+    @Parameterized.Parameter(0)
+    public int intervalBetweenOperations;
+
+    @Parameterized.Parameters(name = "interval{0}ms")
+    public static Collection<Object[]> getParameters() {
+        return Arrays.asList(new Object[][] {
+                { 0 },
+                { 100 },
+                { 300 },
+        });
+    }
+
+    @BeforeClass
+    public static void setUpClass() {
+        // Get the permission to invoke startRecentsActivity.
+        sUiAutomation.adoptShellPermissionIdentity();
+
+        final Context context = getInstrumentation().getContext();
+        final PackageManager pm = context.getPackageManager();
+        final ComponentName defaultHome = pm.getHomeActivities(new ArrayList<>());
+
+        try {
+            final ComponentName recentsComponent =
+                    ComponentName.unflattenFromString(context.getResources().getString(
+                            com.android.internal.R.string.config_recentsComponentName));
+            final int enabledState = pm.getComponentEnabledSetting(recentsComponent);
+            Assume.assumeThat(enabledState, anyOf(
+                    is(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT),
+                    is(PackageManager.COMPONENT_ENABLED_STATE_ENABLED)));
+
+            final boolean homeIsRecents =
+                    recentsComponent.getPackageName().equals(defaultHome.getPackageName());
+            sRecentsIntent =
+                    new Intent().setComponent(homeIsRecents ? defaultHome : recentsComponent);
+        } catch (Exception e) {
+            sSetUpClassException = e;
+        }
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        sSetUpClassException = null;
+        sUiAutomation.dropShellPermissionIdentity();
+    }
+
+    @Before
+    public void setUp() {
+        Assume.assumeNoException(sSetUpClassException);
+    }
+
+    /** Simulate the timing of touch. */
+    private void makeInterval() {
+        SystemClock.sleep(intervalBetweenOperations);
+    }
+
+    /**
+     * <pre>
+     * Steps:
+     * (1) Start recents activity (only make it visible).
+     * (2) Finish animation, take turns to execute (a), (b).
+     *     (a) Move recents activity to top.
+     * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_TOP})
+     *         Move test app to top by startActivityFromRecents.
+     *     (b) Cancel (it is similar to swipe a little distance and give up to enter recents).
+     * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_ORIGINAL_POSITION})
+     * (3) Loop (1).
+     * </pre>
+     */
+    @Test
+    @ManualBenchmarkTest(
+            warmupDurationNs = TIME_1_S_IN_NS,
+            targetTestDurationNs = TIME_5_S_IN_NS,
+            statsReport = @StatsReport(flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN
+                    | StatsReport.FLAG_COEFFICIENT_VAR))
+    public void testRecentsAnimation() throws Throwable {
+        final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final IActivityTaskManager atm = ActivityTaskManager.getService();
+
+        final ArrayList<Pair<String, Boolean>> finishCases = new ArrayList<>();
+        // Real launch the recents activity.
+        finishCases.add(new Pair<>("finishMoveToTop", true));
+        // Return to the original top.
+        finishCases.add(new Pair<>("finishCancel", false));
+
+        // Ensure startRecentsActivity won't be called before finishing the animation.
+        final Semaphore recentsSemaphore = new Semaphore(1);
+
+        final int testActivityTaskId = mActivityRule.getActivity().getTaskId();
+        final IRecentsAnimationRunner.Stub anim = new IRecentsAnimationRunner.Stub() {
+            int mIteration;
+
+            @Override
+            public void onAnimationStart(IRecentsAnimationController controller,
+                    RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
+                    Rect homeContentInsets, Rect minimizedHomeBounds) throws RemoteException {
+                final Pair<String, Boolean> finishCase = finishCases.get(mIteration++ % 2);
+                final boolean moveRecentsToTop = finishCase.second;
+                makeInterval();
+
+                long startTime = SystemClock.elapsedRealtimeNanos();
+                controller.finish(moveRecentsToTop, false /* sendUserLeaveHint */);
+                final long elapsedTimeNsOfFinish = SystemClock.elapsedRealtimeNanos() - startTime;
+                mMeasuredTimeNs += elapsedTimeNsOfFinish;
+                state.addExtraResult(finishCase.first, elapsedTimeNsOfFinish);
+
+                if (moveRecentsToTop) {
+                    mActivityRule.waitForIdleSync(Stage.STOPPED);
+
+                    startTime = SystemClock.elapsedRealtimeNanos();
+                    atm.startActivityFromRecents(testActivityTaskId, null /* options */);
+                    final long elapsedTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
+                    mMeasuredTimeNs += elapsedTimeNs;
+                    state.addExtraResult("startFromRecents", elapsedTimeNs);
+
+                    mActivityRule.waitForIdleSync(Stage.RESUMED);
+                }
+
+                makeInterval();
+                recentsSemaphore.release();
+            }
+
+            @Override
+            public void onAnimationCanceled(TaskSnapshot taskSnapshot) throws RemoteException {
+                Assume.assumeNoException(
+                        new AssertionError("onAnimationCanceled should not be called"));
+            }
+
+            @Override
+            public void onTaskAppeared(RemoteAnimationTarget app) throws RemoteException {
+                /* no-op */
+            }
+        };
+
+        recentsSemaphore.tryAcquire();
+        while (state.keepRunning(mMeasuredTimeNs)) {
+            mMeasuredTimeNs = 0;
+
+            final long startTime = SystemClock.elapsedRealtimeNanos();
+            atm.startRecentsActivity(sRecentsIntent, null /* unused */, anim);
+            final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime;
+            mMeasuredTimeNs += elapsedTimeNsOfStart;
+            state.addExtraResult("start", elapsedTimeNsOfStart);
+
+            // Ensure the animation callback is done.
+            Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS));
+        }
+    }
+}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
new file mode 100644
index 0000000..8139a2e
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.perftests.utils.PerfTestActivity;
+import android.util.MergedConfiguration;
+import android.view.DisplayCutout;
+import android.view.IWindow;
+import android.view.IWindowSession;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.widget.LinearLayout;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.function.IntSupplier;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class RelayoutPerfTest extends WindowManagerPerfTestBase {
+    private int mIteration;
+
+    @Rule
+    public final PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Rule
+    public final ActivityTestRule<PerfTestActivity> mActivityRule =
+            new ActivityTestRule<>(PerfTestActivity.class);
+
+    /** This is only a placement to match the input parameters from {@link #getParameters}. */
+    @Parameterized.Parameter(0)
+    public String testName;
+
+    /** The visibilities to loop for relayout. */
+    @Parameterized.Parameter(1)
+    public int[] visibilities;
+
+    /**
+     * Each row will be mapped into {@link #testName} and {@link #visibilities} of a new test
+     * instance according to the index of the parameter.
+     */
+    @Parameterized.Parameters(name = "{0}")
+    public static Collection<Object[]> getParameters() {
+        return Arrays.asList(new Object[][] {
+                { "Visible", new int[] { View.VISIBLE } },
+                { "Invisible~Visible", new int[] { View.INVISIBLE, View.VISIBLE } },
+                { "Gone~Visible", new int[] { View.GONE, View.VISIBLE } },
+                { "Gone~Invisible", new int[] { View.GONE, View.INVISIBLE } }
+        });
+    }
+
+    @Test
+    public void testRelayout() throws Throwable {
+        final Activity activity = mActivityRule.getActivity();
+        final ContentView contentView = new ContentView(activity);
+        mActivityRule.runOnUiThread(() -> {
+            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+            activity.setContentView(contentView);
+        });
+        getInstrumentation().waitForIdleSync();
+
+        final RelayoutRunner relayoutRunner = new RelayoutRunner(activity, contentView.getWindow(),
+                () -> visibilities[mIteration++ % visibilities.length]);
+        relayoutRunner.runBenchmark(mPerfStatusReporter.getBenchmarkState());
+    }
+
+    /** A dummy view to get IWindow. */
+    private static class ContentView extends LinearLayout {
+        ContentView(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected IWindow getWindow() {
+            return super.getWindow();
+        }
+    }
+
+    private static class RelayoutRunner {
+        final Rect mOutFrame = new Rect();
+        final Rect mOutContentInsets = new Rect();
+        final Rect mOutVisibleInsets = new Rect();
+        final Rect mOutStableInsets = new Rect();
+        final Rect mOutBackDropFrame = new Rect();
+        final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
+                new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
+        final MergedConfiguration mOutMergedConfiguration = new MergedConfiguration();
+        final InsetsState mOutInsetsState = new InsetsState();
+        final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
+        final IWindow mWindow;
+        final View mView;
+        final WindowManager.LayoutParams mParams;
+        final int mWidth;
+        final int mHeight;
+        final Point mOutSurfaceSize = new Point();
+        final SurfaceControl mOutSurfaceControl;
+        final SurfaceControl mOutBlastSurfaceControl = new SurfaceControl();
+
+        final IntSupplier mViewVisibility;
+
+        int mSeq;
+        int mFrameNumber;
+        int mFlags;
+
+        RelayoutRunner(Activity activity, IWindow window, IntSupplier visibilitySupplier) {
+            mWindow = window;
+            mView = activity.getWindow().getDecorView();
+            mParams = (WindowManager.LayoutParams) mView.getLayoutParams();
+            mWidth = mView.getMeasuredWidth();
+            mHeight = mView.getMeasuredHeight();
+            mOutSurfaceControl = mView.getViewRootImpl().getSurfaceControl();
+            mViewVisibility = visibilitySupplier;
+        }
+
+        void runBenchmark(BenchmarkState state) throws RemoteException {
+            final IWindowSession session = WindowManagerGlobal.getWindowSession();
+            while (state.keepRunning()) {
+                session.relayout(mWindow, mSeq, mParams, mWidth, mHeight,
+                        mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrame,
+                        mOutContentInsets, mOutVisibleInsets, mOutStableInsets,
+                        mOutBackDropFrame, mOutDisplayCutout, mOutMergedConfiguration,
+                        mOutSurfaceControl, mOutInsetsState, mOutControls, mOutSurfaceSize,
+                        mOutBlastSurfaceControl);
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
new file mode 100644
index 0000000..c72cc9d
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.perftests.utils.ManualBenchmarkState;
+import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest;
+import android.perftests.utils.PerfManualStatusReporter;
+import android.view.Display;
+import android.view.DisplayCutout;
+import android.view.IWindowSession;
+import android.view.InputChannel;
+import android.view.InsetsSourceControl;
+import android.view.InsetsState;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.internal.view.BaseIWindow;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+
+@LargeTest
+public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
+        implements ManualBenchmarkState.CustomizedIterationListener {
+
+    private static final int PROFILED_ITERATIONS = 2;
+
+    @Rule
+    public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
+
+    @BeforeClass
+    public static void setUpClass() {
+        // Get the permission to use most window types.
+        sUiAutomation.adoptShellPermissionIdentity();
+    }
+
+    @AfterClass
+    public static void tearDownClass() {
+        sUiAutomation.dropShellPermissionIdentity();
+    }
+
+    /** The last {@link #PROFILED_ITERATIONS} will provide the information of method profiling. */
+    @Override
+    public void onStart(int iteration) {
+        startProfiling(WindowAddRemovePerfTest.class.getSimpleName()
+                + "_MethodTracing_" + iteration + ".trace");
+    }
+
+    @Override
+    public void onFinished(int iteration) {
+        stopProfiling();
+    }
+
+    @Test
+    @ManualBenchmarkTest(warmupDurationNs = TIME_1_S_IN_NS, targetTestDurationNs = TIME_5_S_IN_NS)
+    public void testAddRemoveWindow() throws Throwable {
+        final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        state.setCustomizedIterations(PROFILED_ITERATIONS, this);
+        new TestWindow().runBenchmark(state);
+    }
+
+    private static class TestWindow extends BaseIWindow {
+        final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();
+        final Rect mOutFrame = new Rect();
+        final Rect mOutContentInsets = new Rect();
+        final Rect mOutStableInsets = new Rect();
+        final DisplayCutout.ParcelableWrapper mOutDisplayCutout =
+                new DisplayCutout.ParcelableWrapper();
+        final InsetsState mOutInsetsState = new InsetsState();
+        final InsetsSourceControl[] mOutControls = new InsetsSourceControl[0];
+
+        TestWindow() {
+            mLayoutParams.setTitle(TestWindow.class.getName());
+            mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+            // Simulate as common phone window.
+            mLayoutParams.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
+        }
+
+        void runBenchmark(ManualBenchmarkState state) throws RemoteException {
+            final IWindowSession session = WindowManagerGlobal.getWindowSession();
+            long elapsedTimeNs = 0;
+            while (state.keepRunning(elapsedTimeNs)) {
+                // InputChannel cannot be reused.
+                final InputChannel inputChannel = new InputChannel();
+
+                long startTime = SystemClock.elapsedRealtimeNanos();
+                session.addToDisplay(this, mSeq, mLayoutParams, View.VISIBLE,
+                        Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets,
+                        mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls);
+                final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
+                state.addExtraResult("add", elapsedTimeNsOfAdd);
+
+                startTime = SystemClock.elapsedRealtimeNanos();
+                session.remove(this);
+                final long elapsedTimeNsOfRemove = SystemClock.elapsedRealtimeNanos() - startTime;
+                state.addExtraResult("remove", elapsedTimeNsOfRemove);
+
+                elapsedTimeNs = elapsedTimeNsOfAdd + elapsedTimeNsOfRemove;
+                inputChannel.dispose();
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
new file mode 100644
index 0000000..9e17e94
--- /dev/null
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2019 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.wm;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.ParcelFileDescriptor;
+import android.perftests.utils.PerfTestActivity;
+import android.provider.Settings;
+
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
+import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
+import androidx.test.runner.lifecycle.Stage;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+public class WindowManagerPerfTestBase {
+    static final UiAutomation sUiAutomation = getInstrumentation().getUiAutomation();
+    static final long NANOS_PER_S = 1000L * 1000 * 1000;
+    static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S;
+    static final long TIME_5_S_IN_NS = 5 * NANOS_PER_S;
+
+    /**
+     * The out directory matching the directory-keys of collector in AndroidTest.xml. The directory
+     * is in /data because while enabling method profling of system server, it cannot write the
+     * trace to external storage.
+     */
+    static final File BASE_OUT_PATH = new File("/data/local/CorePerfTests");
+
+    private static int sOriginalStayOnWhilePluggedIn;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        final Context context = getInstrumentation().getContext();
+        sOriginalStayOnWhilePluggedIn = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+        // Keep the device awake during testing.
+        setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_USB);
+
+        if (!BASE_OUT_PATH.exists()) {
+            executeShellCommand("mkdir -p " + BASE_OUT_PATH);
+        }
+        // In order to be closer to the real use case.
+        executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        executeShellCommand("wm dismiss-keyguard");
+        context.startActivity(new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
+    }
+
+    private static void setStayOnWhilePluggedIn(int value) {
+        executeShellCommand(String.format("settings put global %s %d",
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
+    }
+
+    /**
+     * Executes shell command with reading the output. It may also used to block until the current
+     * command is completed.
+     */
+    static ByteArrayOutputStream executeShellCommand(String command) {
+        final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand(command);
+        final byte[] buf = new byte[512];
+        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        int bytesRead;
+        try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+            while ((bytesRead = fis.read(buf)) != -1) {
+                bytes.write(buf, 0, bytesRead);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        return bytes;
+    }
+
+    /** Starts method tracing on system server. */
+    void startProfiling(String subPath) {
+        executeShellCommand("am profile start system " + new File(BASE_OUT_PATH, subPath));
+    }
+
+    void stopProfiling() {
+        executeShellCommand("am profile stop system");
+    }
+
+    /**
+     * Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
+     */
+    static class PerfTestActivityRule extends ActivityTestRule<PerfTestActivity> {
+        private final Intent mStartIntent =
+                new Intent(getInstrumentation().getTargetContext(), PerfTestActivity.class);
+        private final LifecycleListener mLifecycleListener = new LifecycleListener();
+
+        PerfTestActivityRule() {
+            this(false /* launchActivity */);
+        }
+
+        PerfTestActivityRule(boolean launchActivity) {
+            super(PerfTestActivity.class, false /* initialTouchMode */, launchActivity);
+        }
+
+        @Override
+        public Statement apply(Statement base, Description description) {
+            final Statement wrappedStatement = new Statement() {
+                @Override
+                public void evaluate() throws Throwable {
+                    ActivityLifecycleMonitorRegistry.getInstance()
+                            .addLifecycleCallback(mLifecycleListener);
+                    base.evaluate();
+                    ActivityLifecycleMonitorRegistry.getInstance()
+                            .removeLifecycleCallback(mLifecycleListener);
+                }
+            };
+            return super.apply(wrappedStatement, description);
+        }
+
+        @Override
+        protected Intent getActivityIntent() {
+            return mStartIntent;
+        }
+
+        @Override
+        public PerfTestActivity launchActivity(Intent intent) {
+            final PerfTestActivity activity = super.launchActivity(intent);
+            mLifecycleListener.setTargetActivity(activity);
+            return activity;
+        }
+
+        PerfTestActivity launchActivity() {
+            return launchActivity(mStartIntent);
+        }
+
+        void waitForIdleSync(Stage state) {
+            mLifecycleListener.waitForIdleSync(state);
+        }
+    }
+
+    static class LifecycleListener implements ActivityLifecycleCallback {
+        private Activity mTargetActivity;
+        private Stage mWaitingStage;
+        private Stage mReceivedStage;
+
+        void setTargetActivity(Activity activity) {
+            mTargetActivity = activity;
+            mReceivedStage = mWaitingStage = null;
+        }
+
+        void waitForIdleSync(Stage stage) {
+            synchronized (this) {
+                if (stage != mReceivedStage) {
+                    mWaitingStage = stage;
+                    try {
+                        wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
+                    } catch (InterruptedException impossible) { }
+                }
+                mWaitingStage = mReceivedStage = null;
+            }
+            getInstrumentation().waitForIdleSync();
+        }
+
+        @Override
+        public void onActivityLifecycleChanged(Activity activity, Stage stage) {
+            if (mTargetActivity != activity) {
+                return;
+            }
+
+            synchronized (this) {
+                mReceivedStage = stage;
+                if (mWaitingStage == mReceivedStage) {
+                    notifyAll();
+                }
+            }
+        }
+    }
+}