• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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