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