Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package android.wm; |
| 18 | |
Riddle Hsu | f5622d2 | 2019-09-24 17:56:05 -0600 | [diff] [blame] | 19 | import static android.perftests.utils.ManualBenchmarkState.StatsReport; |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 20 | |
| 21 | import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; |
| 22 | |
| 23 | import static org.hamcrest.core.AnyOf.anyOf; |
| 24 | import static org.hamcrest.core.Is.is; |
| 25 | |
Tracy Zhou | 8089ffa | 2019-07-30 17:30:43 -0700 | [diff] [blame] | 26 | import android.app.ActivityManager.TaskSnapshot; |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 27 | import android.app.ActivityTaskManager; |
| 28 | import android.app.IActivityTaskManager; |
| 29 | import android.content.ComponentName; |
| 30 | import android.content.Context; |
| 31 | import android.content.Intent; |
| 32 | import android.content.pm.PackageManager; |
| 33 | import android.graphics.Rect; |
| 34 | import android.os.RemoteException; |
| 35 | import android.os.SystemClock; |
| 36 | import android.perftests.utils.ManualBenchmarkState; |
| 37 | import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest; |
| 38 | import android.perftests.utils.PerfManualStatusReporter; |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 39 | import android.util.Pair; |
| 40 | import android.view.IRecentsAnimationController; |
| 41 | import android.view.IRecentsAnimationRunner; |
| 42 | import android.view.RemoteAnimationTarget; |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 43 | |
| 44 | import androidx.test.filters.LargeTest; |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 45 | import androidx.test.runner.lifecycle.Stage; |
| 46 | |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 47 | import org.junit.AfterClass; |
| 48 | import org.junit.Assume; |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 49 | import org.junit.BeforeClass; |
| 50 | import org.junit.Rule; |
| 51 | import org.junit.Test; |
| 52 | import org.junit.runner.RunWith; |
| 53 | import org.junit.runners.Parameterized; |
| 54 | |
| 55 | import java.util.ArrayList; |
| 56 | import java.util.Arrays; |
| 57 | import java.util.Collection; |
| 58 | import java.util.concurrent.Semaphore; |
| 59 | import java.util.concurrent.TimeUnit; |
| 60 | |
| 61 | @RunWith(Parameterized.class) |
| 62 | @LargeTest |
| 63 | public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase { |
| 64 | private static Intent sRecentsIntent; |
| 65 | |
| 66 | @Rule |
| 67 | public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter(); |
| 68 | |
| 69 | @Rule |
Riddle Hsu | f5622d2 | 2019-09-24 17:56:05 -0600 | [diff] [blame] | 70 | public final PerfTestActivityRule mActivityRule = |
| 71 | new PerfTestActivityRule(true /* launchActivity */); |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 72 | |
| 73 | private long mMeasuredTimeNs; |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 74 | |
| 75 | @Parameterized.Parameter(0) |
| 76 | public int intervalBetweenOperations; |
| 77 | |
| 78 | @Parameterized.Parameters(name = "interval{0}ms") |
| 79 | public static Collection<Object[]> getParameters() { |
| 80 | return Arrays.asList(new Object[][] { |
| 81 | { 0 }, |
| 82 | { 100 }, |
| 83 | { 300 }, |
| 84 | }); |
| 85 | } |
| 86 | |
| 87 | @BeforeClass |
| 88 | public static void setUpClass() { |
| 89 | // Get the permission to invoke startRecentsActivity. |
| 90 | sUiAutomation.adoptShellPermissionIdentity(); |
| 91 | |
| 92 | final Context context = getInstrumentation().getContext(); |
| 93 | final PackageManager pm = context.getPackageManager(); |
| 94 | final ComponentName defaultHome = pm.getHomeActivities(new ArrayList<>()); |
| 95 | |
| 96 | try { |
| 97 | final ComponentName recentsComponent = |
| 98 | ComponentName.unflattenFromString(context.getResources().getString( |
| 99 | com.android.internal.R.string.config_recentsComponentName)); |
| 100 | final int enabledState = pm.getComponentEnabledSetting(recentsComponent); |
| 101 | Assume.assumeThat(enabledState, anyOf( |
| 102 | is(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT), |
| 103 | is(PackageManager.COMPONENT_ENABLED_STATE_ENABLED))); |
| 104 | |
| 105 | final boolean homeIsRecents = |
| 106 | recentsComponent.getPackageName().equals(defaultHome.getPackageName()); |
| 107 | sRecentsIntent = |
| 108 | new Intent().setComponent(homeIsRecents ? defaultHome : recentsComponent); |
| 109 | } catch (Exception e) { |
| 110 | Assume.assumeNoException(e); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | @AfterClass |
| 115 | public static void tearDownClass() { |
| 116 | sUiAutomation.dropShellPermissionIdentity(); |
| 117 | } |
| 118 | |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 119 | /** Simulate the timing of touch. */ |
| 120 | private void makeInterval() { |
| 121 | SystemClock.sleep(intervalBetweenOperations); |
| 122 | } |
| 123 | |
| 124 | /** |
| 125 | * <pre> |
| 126 | * Steps: |
| 127 | * (1) Start recents activity (only make it visible). |
| 128 | * (2) Finish animation, take turns to execute (a), (b). |
| 129 | * (a) Move recents activity to top. |
| 130 | * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_TOP}) |
| 131 | * Move test app to top by startActivityFromRecents. |
| 132 | * (b) Cancel (it is similar to swipe a little distance and give up to enter recents). |
| 133 | * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_ORIGINAL_POSITION}) |
| 134 | * (3) Loop (1). |
| 135 | * </pre> |
| 136 | */ |
| 137 | @Test |
| 138 | @ManualBenchmarkTest( |
| 139 | warmupDurationNs = TIME_1_S_IN_NS, |
| 140 | targetTestDurationNs = TIME_5_S_IN_NS, |
Riddle Hsu | f5622d2 | 2019-09-24 17:56:05 -0600 | [diff] [blame] | 141 | statsReport = @StatsReport(flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN |
| 142 | | StatsReport.FLAG_COEFFICIENT_VAR)) |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 143 | public void testRecentsAnimation() throws Throwable { |
| 144 | final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState(); |
| 145 | final IActivityTaskManager atm = ActivityTaskManager.getService(); |
| 146 | |
| 147 | final ArrayList<Pair<String, Boolean>> finishCases = new ArrayList<>(); |
| 148 | // Real launch the recents activity. |
| 149 | finishCases.add(new Pair<>("finishMoveToTop", true)); |
| 150 | // Return to the original top. |
| 151 | finishCases.add(new Pair<>("finishCancel", false)); |
| 152 | |
| 153 | // Ensure startRecentsActivity won't be called before finishing the animation. |
| 154 | final Semaphore recentsSemaphore = new Semaphore(1); |
| 155 | |
| 156 | final int testActivityTaskId = mActivityRule.getActivity().getTaskId(); |
| 157 | final IRecentsAnimationRunner.Stub anim = new IRecentsAnimationRunner.Stub() { |
| 158 | int mIteration; |
| 159 | |
| 160 | @Override |
| 161 | public void onAnimationStart(IRecentsAnimationController controller, |
Winson Chung | d585219 | 2019-09-06 17:20:28 -0700 | [diff] [blame] | 162 | RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, |
| 163 | Rect homeContentInsets, Rect minimizedHomeBounds) throws RemoteException { |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 164 | final Pair<String, Boolean> finishCase = finishCases.get(mIteration++ % 2); |
| 165 | final boolean moveRecentsToTop = finishCase.second; |
| 166 | makeInterval(); |
| 167 | |
| 168 | long startTime = SystemClock.elapsedRealtimeNanos(); |
| 169 | controller.finish(moveRecentsToTop, false /* sendUserLeaveHint */); |
| 170 | final long elapsedTimeNsOfFinish = SystemClock.elapsedRealtimeNanos() - startTime; |
| 171 | mMeasuredTimeNs += elapsedTimeNsOfFinish; |
| 172 | state.addExtraResult(finishCase.first, elapsedTimeNsOfFinish); |
| 173 | |
| 174 | if (moveRecentsToTop) { |
Riddle Hsu | f5622d2 | 2019-09-24 17:56:05 -0600 | [diff] [blame] | 175 | mActivityRule.waitForIdleSync(Stage.STOPPED); |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 176 | |
| 177 | startTime = SystemClock.elapsedRealtimeNanos(); |
| 178 | atm.startActivityFromRecents(testActivityTaskId, null /* options */); |
| 179 | final long elapsedTimeNs = SystemClock.elapsedRealtimeNanos() - startTime; |
| 180 | mMeasuredTimeNs += elapsedTimeNs; |
| 181 | state.addExtraResult("startFromRecents", elapsedTimeNs); |
| 182 | |
Riddle Hsu | f5622d2 | 2019-09-24 17:56:05 -0600 | [diff] [blame] | 183 | mActivityRule.waitForIdleSync(Stage.RESUMED); |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 184 | } |
| 185 | |
| 186 | makeInterval(); |
| 187 | recentsSemaphore.release(); |
| 188 | } |
| 189 | |
| 190 | @Override |
Tracy Zhou | 8089ffa | 2019-07-30 17:30:43 -0700 | [diff] [blame] | 191 | public void onAnimationCanceled(TaskSnapshot taskSnapshot) throws RemoteException { |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 192 | Assume.assumeNoException( |
| 193 | new AssertionError("onAnimationCanceled should not be called")); |
| 194 | } |
lumark | 04bceb9 | 2020-03-07 00:03:33 +0800 | [diff] [blame^] | 195 | |
| 196 | @Override |
| 197 | public void onTaskAppeared(RemoteAnimationTarget app) throws RemoteException { |
| 198 | /* no-op */ |
| 199 | } |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 200 | }; |
| 201 | |
Riddle Hsu | f5622d2 | 2019-09-24 17:56:05 -0600 | [diff] [blame] | 202 | recentsSemaphore.tryAcquire(); |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 203 | while (state.keepRunning(mMeasuredTimeNs)) { |
Riddle Hsu | f5622d2 | 2019-09-24 17:56:05 -0600 | [diff] [blame] | 204 | mMeasuredTimeNs = 0; |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 205 | |
| 206 | final long startTime = SystemClock.elapsedRealtimeNanos(); |
| 207 | atm.startRecentsActivity(sRecentsIntent, null /* unused */, anim); |
| 208 | final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime; |
| 209 | mMeasuredTimeNs += elapsedTimeNsOfStart; |
| 210 | state.addExtraResult("start", elapsedTimeNsOfStart); |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 211 | |
Riddle Hsu | f5622d2 | 2019-09-24 17:56:05 -0600 | [diff] [blame] | 212 | // Ensure the animation callback is done. |
| 213 | Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS)); |
Riddle Hsu | 5ef56dd6 | 2019-07-26 21:28:51 -0600 | [diff] [blame] | 214 | } |
| 215 | } |
| 216 | } |