• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and limitations under the
13  * License.
14  *
15  */
16 
17 package com.android.benchmark.ui.automation;
18 
19 import android.annotation.TargetApi;
20 import android.app.Instrumentation;
21 import android.os.Handler;
22 import android.os.HandlerThread;
23 import android.os.Looper;
24 import android.os.Message;
25 import android.view.FrameMetrics;
26 import android.view.MotionEvent;
27 import android.view.ViewTreeObserver;
28 import android.view.Window;
29 
30 import com.android.benchmark.results.GlobalResultsStore;
31 import com.android.benchmark.results.UiBenchmarkResult;
32 
33 import java.util.LinkedList;
34 import java.util.List;
35 import java.util.concurrent.atomic.AtomicInteger;
36 
37 @TargetApi(24)
38 public class Automator extends HandlerThread
39         implements ViewTreeObserver.OnGlobalLayoutListener, CollectorThread.CollectorListener {
40     public static final long FRAME_PERIOD_MILLIS = 16;
41 
42     private static final int PRE_READY_STATE_COUNT = 3;
43     private static final String TAG = "Benchmark.Automator";
44     private final AtomicInteger mReadyState;
45 
46     private AutomateCallback mCallback;
47     private Window mWindow;
48     private AutomatorHandler mHandler;
49     private CollectorThread mCollectorThread;
50     private int mRunId;
51     private int mIteration;
52     private String mTestName;
53 
54     public static class AutomateCallback {
onAutomate()55         public void onAutomate() {}
onPostInteraction(List<FrameMetrics> metrics)56         public void onPostInteraction(List<FrameMetrics> metrics) {}
onPostAutomate()57         public void onPostAutomate() {}
58 
addInteraction(Interaction interaction)59         protected final void addInteraction(Interaction interaction) {
60             if (mInteractions == null) {
61                 return;
62             }
63 
64             mInteractions.add(interaction);
65         }
66 
setInteractions(List<Interaction> interactions)67         protected final void setInteractions(List<Interaction> interactions) {
68             mInteractions = interactions;
69         }
70 
71         private List<Interaction> mInteractions;
72     }
73 
74     private static final class AutomatorHandler extends Handler {
75         public static final int MSG_NEXT_INTERACTION = 0;
76         public static final int MSG_ON_AUTOMATE = 1;
77         public static final int MSG_ON_POST_INTERACTION = 2;
78         private final String mTestName;
79         private final int mRunId;
80         private final int mIteration;
81 
82         private Instrumentation mInstrumentation;
83         private volatile boolean mCancelled;
84         private CollectorThread mCollectorThread;
85         private AutomateCallback mCallback;
86         private Window mWindow;
87 
88         LinkedList<Interaction> mInteractions;
89         private UiBenchmarkResult mResults;
90 
AutomatorHandler(Looper looper, Window window, CollectorThread collectorThread, AutomateCallback callback, String testName, int runId, int iteration)91         AutomatorHandler(Looper looper, Window window, CollectorThread collectorThread,
92                          AutomateCallback callback, String testName, int runId, int iteration) {
93             super(looper);
94 
95             mInstrumentation = new Instrumentation();
96 
97             mCallback = callback;
98             mWindow = window;
99             mCollectorThread = collectorThread;
100             mInteractions = new LinkedList<>();
101             mTestName = testName;
102             mRunId = runId;
103             mIteration = iteration;
104         }
105 
106         @Override
handleMessage(Message msg)107         public void handleMessage(Message msg) {
108             if (mCancelled) {
109                 return;
110             }
111 
112             switch (msg.what) {
113                 case MSG_NEXT_INTERACTION:
114                     if (!nextInteraction()) {
115                         stopCollector();
116                         writeResults();
117                         mCallback.onPostAutomate();
118                     }
119                     break;
120                 case MSG_ON_AUTOMATE:
121                     mCollectorThread.attachToWindow(mWindow);
122                     mCallback.setInteractions(mInteractions);
123                     mCallback.onAutomate();
124                     postNextInteraction();
125                     break;
126                 case MSG_ON_POST_INTERACTION:
127                     List<FrameMetrics> collectedStats = (List<FrameMetrics>)msg.obj;
128                     persistResults(collectedStats);
129                     mCallback.onPostInteraction(collectedStats);
130                     postNextInteraction();
131                     break;
132             }
133         }
134 
cancel()135         public void cancel() {
136             mCancelled = true;
137             stopCollector();
138         }
139 
stopCollector()140         private void stopCollector() {
141             mCollectorThread.quitCollector();
142         }
143 
nextInteraction()144         private boolean nextInteraction() {
145 
146             Interaction interaction = mInteractions.poll();
147             if (interaction != null) {
148                 doInteraction(interaction);
149                 return true;
150             }
151             return false;
152         }
153 
doInteraction(Interaction interaction)154         private void doInteraction(Interaction interaction) {
155             if (mCancelled) {
156                 return;
157             }
158 
159             mCollectorThread.markInteractionStart();
160 
161             if (interaction.getType() == Interaction.Type.KEY_EVENT) {
162                 for (int code : interaction.getKeyCodes()) {
163                     if (!mCancelled) {
164                         mInstrumentation.sendKeyDownUpSync(code);
165                     } else {
166                         break;
167                     }
168                 }
169             } else {
170                 for (MotionEvent event : interaction.getEvents()) {
171                     if (!mCancelled) {
172                         mInstrumentation.sendPointerSync(event);
173                     } else {
174                         break;
175                     }
176                 }
177             }
178         }
179 
postNextInteraction()180         protected void postNextInteraction() {
181             final Message msg = obtainMessage(AutomatorHandler.MSG_NEXT_INTERACTION);
182             sendMessage(msg);
183         }
184 
persistResults(List<FrameMetrics> stats)185         private void persistResults(List<FrameMetrics> stats) {
186             if (stats.isEmpty()) {
187                 return;
188             }
189 
190             if (mResults == null) {
191                 mResults = new UiBenchmarkResult(stats);
192             } else {
193                 mResults.update(stats);
194             }
195         }
196 
writeResults()197         private void writeResults() {
198             GlobalResultsStore.getInstance(mWindow.getContext())
199                     .storeRunResults(mTestName, mRunId, mIteration, mResults);
200         }
201     }
202 
initHandler()203     private void initHandler() {
204         mHandler = new AutomatorHandler(getLooper(), mWindow, mCollectorThread, mCallback,
205                 mTestName, mRunId, mIteration);
206         mWindow = null;
207         mCallback = null;
208         mCollectorThread = null;
209         mTestName = null;
210         mRunId = 0;
211         mIteration = 0;
212     }
213 
214     @Override
onGlobalLayout()215     public final void onGlobalLayout() {
216         if (!mCollectorThread.isAlive()) {
217             mCollectorThread.start();
218             mWindow.getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
219             mReadyState.decrementAndGet();
220         }
221     }
222 
223     @Override
onCollectorThreadReady()224     public void onCollectorThreadReady() {
225         if (mReadyState.decrementAndGet() == 0) {
226             initHandler();
227             postOnAutomate();
228         }
229     }
230 
231     @Override
onLooperPrepared()232     protected void onLooperPrepared() {
233         if (mReadyState.decrementAndGet() == 0) {
234             initHandler();
235             postOnAutomate();
236         }
237     }
238 
239     @Override
onPostInteraction(List<FrameMetrics> stats)240     public void onPostInteraction(List<FrameMetrics> stats) {
241         Message m = mHandler.obtainMessage(AutomatorHandler.MSG_ON_POST_INTERACTION, stats);
242         mHandler.sendMessage(m);
243     }
244 
postOnAutomate()245     protected void postOnAutomate() {
246         final Message msg = mHandler.obtainMessage(AutomatorHandler.MSG_ON_AUTOMATE);
247         mHandler.sendMessage(msg);
248     }
249 
cancel()250     public void cancel() {
251         mHandler.removeMessages(AutomatorHandler.MSG_NEXT_INTERACTION);
252         mHandler.cancel();
253         mHandler = null;
254     }
255 
Automator(String testName, int runId, int iteration, Window window, AutomateCallback callback)256     public Automator(String testName, int runId, int iteration,
257                      Window window, AutomateCallback callback) {
258         super("AutomatorThread");
259 
260         mTestName = testName;
261         mRunId = runId;
262         mIteration = iteration;
263         mCallback = callback;
264         mWindow = window;
265         mWindow.getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(this);
266         mCollectorThread = new CollectorThread(this);
267         mReadyState = new AtomicInteger(PRE_READY_STATE_COUNT);
268     }
269 }
270