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