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 19 import static android.perftests.utils.ManualBenchmarkState.StatsReport; 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 26 import android.app.ActivityManager; 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; 39 import android.util.Pair; 40 import android.view.IRecentsAnimationController; 41 import android.view.IRecentsAnimationRunner; 42 import android.view.RemoteAnimationTarget; 43 import android.window.TaskSnapshot; 44 45 import androidx.test.filters.LargeTest; 46 import androidx.test.runner.lifecycle.Stage; 47 48 import org.junit.AfterClass; 49 import org.junit.Assume; 50 import org.junit.Before; 51 import org.junit.BeforeClass; 52 import org.junit.Rule; 53 import org.junit.Test; 54 import org.junit.runner.RunWith; 55 import org.junit.runners.Parameterized; 56 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collection; 60 import java.util.concurrent.Semaphore; 61 import java.util.concurrent.TimeUnit; 62 63 @RunWith(Parameterized.class) 64 @LargeTest 65 public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase 66 implements ManualBenchmarkState.CustomizedIterationListener { 67 private static Intent sRecentsIntent; 68 69 @Rule 70 public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter(); 71 72 @Rule 73 public final PerfTestActivityRule mActivityRule = 74 new PerfTestActivityRule(true /* launchActivity */); 75 76 private long mMeasuredTimeNs; 77 78 /** 79 * Used to skip each test method if there is error. It cannot be raised in static setup because 80 * that will break the amount of target method count. 81 */ 82 private static Exception sSetUpClassException; 83 84 @Parameterized.Parameter(0) 85 public int intervalBetweenOperations; 86 87 @Parameterized.Parameters(name = "interval{0}ms") getParameters()88 public static Collection<Object[]> getParameters() { 89 return Arrays.asList(new Object[][] { 90 { 0 }, 91 { 100 }, 92 { 300 }, 93 }); 94 } 95 96 @BeforeClass setUpClass()97 public static void setUpClass() { 98 // Get the permission to invoke startRecentsActivity. 99 getUiAutomation().adoptShellPermissionIdentity(); 100 101 final Context context = getInstrumentation().getContext(); 102 final PackageManager pm = context.getPackageManager(); 103 final ComponentName defaultHome = pm.getHomeActivities(new ArrayList<>()); 104 105 try { 106 final ComponentName recentsComponent = 107 ComponentName.unflattenFromString(context.getResources().getString( 108 com.android.internal.R.string.config_recentsComponentName)); 109 final int enabledState = pm.getComponentEnabledSetting(recentsComponent); 110 Assume.assumeThat(enabledState, anyOf( 111 is(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT), 112 is(PackageManager.COMPONENT_ENABLED_STATE_ENABLED))); 113 114 final boolean homeIsRecents = 115 recentsComponent.getPackageName().equals(defaultHome.getPackageName()); 116 sRecentsIntent = 117 new Intent().setComponent(homeIsRecents ? defaultHome : recentsComponent); 118 } catch (Exception e) { 119 sSetUpClassException = e; 120 } 121 } 122 123 @AfterClass tearDownClass()124 public static void tearDownClass() { 125 sSetUpClassException = null; 126 try { 127 // Recents activity may stop app switches. Restore the state to avoid affecting 128 // the next test. 129 ActivityManager.resumeAppSwitches(); 130 } catch (RemoteException ignored) { 131 } 132 getUiAutomation().dropShellPermissionIdentity(); 133 } 134 135 @Before setUp()136 public void setUp() { 137 Assume.assumeNoException(sSetUpClassException); 138 } 139 140 /** Simulate the timing of touch. */ makeInterval()141 private void makeInterval() { 142 SystemClock.sleep(intervalBetweenOperations); 143 } 144 145 /** 146 * <pre> 147 * Steps: 148 * (1) Start recents activity (only make it visible). 149 * (2) Finish animation, take turns to execute (a), (b). 150 * (a) Move recents activity to top. 151 * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_TOP}) 152 * Move test app to top by startActivityFromRecents. 153 * (b) Cancel (it is similar to swipe a little distance and give up to enter recents). 154 * ({@link com.android.server.wm.RecentsAnimationController#REORDER_MOVE_TO_ORIGINAL_POSITION}) 155 * (3) Loop (1). 156 * </pre> 157 */ 158 @Test 159 @ManualBenchmarkTest( 160 warmupDurationNs = TIME_1_S_IN_NS, 161 targetTestDurationNs = TIME_5_S_IN_NS, 162 statsReport = @StatsReport(flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN 163 | StatsReport.FLAG_COEFFICIENT_VAR)) testRecentsAnimation()164 public void testRecentsAnimation() throws Throwable { 165 final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 166 state.setCustomizedIterations(getProfilingIterations(), this); 167 final IActivityTaskManager atm = ActivityTaskManager.getService(); 168 169 final ArrayList<Pair<String, Boolean>> finishCases = new ArrayList<>(); 170 // Real launch the recents activity. 171 finishCases.add(new Pair<>("finishMoveToTop", true)); 172 // Return to the original top. 173 finishCases.add(new Pair<>("finishCancel", false)); 174 175 // Ensure startRecentsActivity won't be called before finishing the animation. 176 final Semaphore recentsSemaphore = new Semaphore(1); 177 178 final int testActivityTaskId = mActivityRule.getActivity().getTaskId(); 179 final IRecentsAnimationRunner.Stub anim = new IRecentsAnimationRunner.Stub() { 180 int mIteration; 181 182 @Override 183 public void onAnimationStart(IRecentsAnimationController controller, 184 RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, 185 Rect homeContentInsets, Rect minimizedHomeBounds) throws RemoteException { 186 final Pair<String, Boolean> finishCase = finishCases.get(mIteration++ % 2); 187 final boolean moveRecentsToTop = finishCase.second; 188 makeInterval(); 189 190 long startTime = SystemClock.elapsedRealtimeNanos(); 191 controller.finish(moveRecentsToTop, false /* sendUserLeaveHint */); 192 final long elapsedTimeNsOfFinish = SystemClock.elapsedRealtimeNanos() - startTime; 193 mMeasuredTimeNs += elapsedTimeNsOfFinish; 194 state.addExtraResult(finishCase.first, elapsedTimeNsOfFinish); 195 196 if (moveRecentsToTop) { 197 mActivityRule.waitForIdleSync(Stage.STOPPED); 198 199 startTime = SystemClock.elapsedRealtimeNanos(); 200 atm.startActivityFromRecents(testActivityTaskId, null /* options */); 201 final long elapsedTimeNs = SystemClock.elapsedRealtimeNanos() - startTime; 202 mMeasuredTimeNs += elapsedTimeNs; 203 state.addExtraResult("startFromRecents", elapsedTimeNs); 204 205 mActivityRule.waitForIdleSync(Stage.RESUMED); 206 } 207 208 makeInterval(); 209 recentsSemaphore.release(); 210 } 211 212 @Override 213 public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) 214 throws RemoteException { 215 Assume.assumeNoException( 216 new AssertionError("onAnimationCanceled should not be called")); 217 } 218 219 @Override 220 public void onTasksAppeared(RemoteAnimationTarget[] app) throws RemoteException { 221 /* no-op */ 222 } 223 }; 224 225 recentsSemaphore.tryAcquire(); 226 while (state.keepRunning(mMeasuredTimeNs)) { 227 mMeasuredTimeNs = 0; 228 229 final long startTime = SystemClock.elapsedRealtimeNanos(); 230 atm.startRecentsActivity(sRecentsIntent, 0 /* eventTime */, anim); 231 final long elapsedTimeNsOfStart = SystemClock.elapsedRealtimeNanos() - startTime; 232 mMeasuredTimeNs += elapsedTimeNsOfStart; 233 state.addExtraResult("start", elapsedTimeNsOfStart); 234 235 // Ensure the animation callback is done. 236 Assume.assumeTrue(recentsSemaphore.tryAcquire( 237 sIsProfilingMethod() ? 10 * TIME_5_S_IN_NS : TIME_5_S_IN_NS, 238 TimeUnit.NANOSECONDS)); 239 } 240 } 241 242 @Override onStart(int iteration)243 public void onStart(int iteration) { 244 startProfiling(RecentsAnimationPerfTest.class.getSimpleName() 245 + "_interval_" + intervalBetweenOperations 246 + "_MethodTracing_" + iteration + ".trace"); 247 } 248 249 @Override onFinished(int iteration)250 public void onFinished(int iteration) { 251 stopProfiling(); 252 } 253 } 254