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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
20 
21 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION;
22 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS;
23 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS;
24 
25 import android.app.Activity;
26 import android.content.Intent;
27 import android.metrics.LogMaker;
28 import android.metrics.MetricsReader;
29 import android.perftests.utils.PerfTestActivity;
30 import android.perftests.utils.WindowPerfTestBase;
31 import android.util.SparseArray;
32 
33 import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
34 import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
35 import androidx.test.runner.lifecycle.Stage;
36 
37 import org.junit.runner.Description;
38 import org.junit.runners.model.Statement;
39 
40 import java.io.File;
41 import java.util.ArrayList;
42 import java.util.concurrent.TimeUnit;
43 
44 public class WindowManagerPerfTestBase extends WindowPerfTestBase {
45     static final long TIME_5_S_IN_NS = 5 * NANOS_PER_S;
46 
47     /**
48      * The out directory matching the directory-keys of collector in AndroidTest.xml. The directory
49      * is in /data because while enabling method profiling of system server, it cannot write the
50      * trace to external storage.
51      */
52     static final File BASE_OUT_PATH = new File("/data/local/tmp/WmPerfTests");
53 
startProfiling(String outFileName)54     static void startProfiling(String outFileName) {
55         startProfiling(BASE_OUT_PATH, outFileName);
56     }
57 
58     /**
59      * Provides an activity that is able to wait for a stable lifecycle stage.
60      */
61     static class PerfTestActivityRule extends PerfTestActivityRuleBase {
62         private final LifecycleListener mLifecycleListener = new LifecycleListener();
63 
PerfTestActivityRule()64         PerfTestActivityRule() {
65         }
66 
PerfTestActivityRule(boolean launchActivity)67         PerfTestActivityRule(boolean launchActivity) {
68             super(launchActivity);
69         }
70 
71         @Override
apply(Statement base, Description description)72         public Statement apply(Statement base, Description description) {
73             final Statement wrappedStatement = new Statement() {
74                 @Override
75                 public void evaluate() throws Throwable {
76                     ActivityLifecycleMonitorRegistry.getInstance()
77                             .addLifecycleCallback(mLifecycleListener);
78                     base.evaluate();
79                     ActivityLifecycleMonitorRegistry.getInstance()
80                             .removeLifecycleCallback(mLifecycleListener);
81                 }
82             };
83             return super.apply(wrappedStatement, description);
84         }
85 
86         @Override
launchActivity(Intent intent)87         public PerfTestActivity launchActivity(Intent intent) {
88             final PerfTestActivity activity = super.launchActivity(intent);
89             mLifecycleListener.setTargetActivity(activity);
90             return activity;
91         }
92 
waitForIdleSync(Stage state)93         void waitForIdleSync(Stage state) {
94             mLifecycleListener.waitForIdleSync(state);
95         }
96     }
97 
98     static class LifecycleListener implements ActivityLifecycleCallback {
99         private Activity mTargetActivity;
100         private Stage mWaitingStage;
101         private Stage mReceivedStage;
102 
setTargetActivity(Activity activity)103         void setTargetActivity(Activity activity) {
104             mTargetActivity = activity;
105             mReceivedStage = mWaitingStage = null;
106         }
107 
waitForIdleSync(Stage stage)108         void waitForIdleSync(Stage stage) {
109             synchronized (this) {
110                 if (stage != mReceivedStage) {
111                     mWaitingStage = stage;
112                     try {
113                         wait(TimeUnit.NANOSECONDS.toMillis(TIME_5_S_IN_NS));
114                     } catch (InterruptedException impossible) { }
115                 }
116                 mWaitingStage = mReceivedStage = null;
117             }
118             getInstrumentation().waitForIdleSync();
119         }
120 
121         @Override
onActivityLifecycleChanged(Activity activity, Stage stage)122         public void onActivityLifecycleChanged(Activity activity, Stage stage) {
123             if (mTargetActivity != activity) {
124                 return;
125             }
126 
127             synchronized (this) {
128                 mReceivedStage = stage;
129                 if (mWaitingStage == mReceivedStage) {
130                     notifyAll();
131                 }
132             }
133         }
134     }
135 
136     static class TransitionMetricsReader {
137         final MetricsReader mMetricsReader = new MetricsReader();
138 
139         static class TransitionMetrics {
140             int mTransitionDelayMs;
141             int mWindowsDrawnDelayMs;
142         }
143 
getMetrics()144         TransitionMetrics[] getMetrics() {
145             mMetricsReader.read(0);
146             final ArrayList<LogMaker> logs = new ArrayList<>();
147             final LogMaker logTemplate = new LogMaker(APP_TRANSITION);
148             while (mMetricsReader.hasNext()) {
149                 final LogMaker b = mMetricsReader.next();
150                 if (logTemplate.isSubsetOf(b)) {
151                     logs.add(b);
152                 }
153             }
154 
155             final TransitionMetrics[] infoArray = new TransitionMetrics[logs.size()];
156             for (int i = 0; i < infoArray.length; i++) {
157                 final LogMaker log = logs.get(i);
158                 final SparseArray<Object> data = log.getEntries();
159                 final TransitionMetrics info = new TransitionMetrics();
160                 infoArray[i] = info;
161                 info.mTransitionDelayMs =
162                         (int) data.get(APP_TRANSITION_DELAY_MS, -1);
163                 info.mWindowsDrawnDelayMs =
164                         (int) data.get(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, -1);
165             }
166             return infoArray;
167         }
168 
setCheckpoint()169         void setCheckpoint() {
170             mMetricsReader.checkpoint();
171         }
172     }
173 }
174