blob: 1efd6bc2aba60f4cac93489943dc2220b1ae05c1 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and limitations under the
* License.
*
*/
package com.android.benchmark.ui.automation;
import android.annotation.TargetApi;
import android.app.Instrumentation;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.view.FrameMetrics;
import android.view.MotionEvent;
import android.view.ViewTreeObserver;
import android.view.Window;
import com.android.benchmark.results.GlobalResultsStore;
import com.android.benchmark.results.UiBenchmarkResult;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@TargetApi(24)
public class Automator extends HandlerThread
implements ViewTreeObserver.OnGlobalLayoutListener, CollectorThread.CollectorListener {
public static final long FRAME_PERIOD_MILLIS = 16;
private static final int PRE_READY_STATE_COUNT = 3;
private static final String TAG = "Benchmark.Automator";
private final AtomicInteger mReadyState;
private AutomateCallback mCallback;
private Window mWindow;
private AutomatorHandler mHandler;
private CollectorThread mCollectorThread;
private int mRunId;
private int mIteration;
private String mTestName;
public static class AutomateCallback {
public void onAutomate() {}
public void onPostInteraction(List<FrameMetrics> metrics) {}
public void onPostAutomate() {}
protected final void addInteraction(Interaction interaction) {
if (mInteractions == null) {
return;
}
mInteractions.add(interaction);
}
protected final void setInteractions(List<Interaction> interactions) {
mInteractions = interactions;
}
private List<Interaction> mInteractions;
}
private static final class AutomatorHandler extends Handler {
public static final int MSG_NEXT_INTERACTION = 0;
public static final int MSG_ON_AUTOMATE = 1;
public static final int MSG_ON_POST_INTERACTION = 2;
private final String mTestName;
private final int mRunId;
private final int mIteration;
private Instrumentation mInstrumentation;
private volatile boolean mCancelled;
private CollectorThread mCollectorThread;
private AutomateCallback mCallback;
private Window mWindow;
LinkedList<Interaction> mInteractions;
private UiBenchmarkResult mResults;
AutomatorHandler(Looper looper, Window window, CollectorThread collectorThread,
AutomateCallback callback, String testName, int runId, int iteration) {
super(looper);
mInstrumentation = new Instrumentation();
mCallback = callback;
mWindow = window;
mCollectorThread = collectorThread;
mInteractions = new LinkedList<>();
mTestName = testName;
mRunId = runId;
mIteration = iteration;
}
@Override
public void handleMessage(Message msg) {
if (mCancelled) {
return;
}
switch (msg.what) {
case MSG_NEXT_INTERACTION:
if (!nextInteraction()) {
stopCollector();
writeResults();
mCallback.onPostAutomate();
}
break;
case MSG_ON_AUTOMATE:
mCollectorThread.attachToWindow(mWindow);
mCallback.setInteractions(mInteractions);
mCallback.onAutomate();
postNextInteraction();
break;
case MSG_ON_POST_INTERACTION:
List<FrameMetrics> collectedStats = (List<FrameMetrics>)msg.obj;
persistResults(collectedStats);
mCallback.onPostInteraction(collectedStats);
postNextInteraction();
break;
}
}
public void cancel() {
mCancelled = true;
stopCollector();
}
private void stopCollector() {
mCollectorThread.quitCollector();
}
private boolean nextInteraction() {
Interaction interaction = mInteractions.poll();
if (interaction != null) {
doInteraction(interaction);
return true;
}
return false;
}
private void doInteraction(Interaction interaction) {
if (mCancelled) {
return;
}
mCollectorThread.markInteractionStart();
if (interaction.getType() == Interaction.Type.KEY_EVENT) {
for (int code : interaction.getKeyCodes()) {
if (!mCancelled) {
mInstrumentation.sendKeyDownUpSync(code);
} else {
break;
}
}
} else {
for (MotionEvent event : interaction.getEvents()) {
if (!mCancelled) {
mInstrumentation.sendPointerSync(event);
} else {
break;
}
}
}
}
protected void postNextInteraction() {
final Message msg = obtainMessage(AutomatorHandler.MSG_NEXT_INTERACTION);
sendMessage(msg);
}
private void persistResults(List<FrameMetrics> stats) {
if (stats.isEmpty()) {
return;
}
if (mResults == null) {
mResults = new UiBenchmarkResult(stats);
} else {
mResults.update(stats);
}
}
private void writeResults() {
GlobalResultsStore.getInstance(mWindow.getContext())
.storeRunResults(mTestName, mRunId, mIteration, mResults);
}
}
private void initHandler() {
mHandler = new AutomatorHandler(getLooper(), mWindow, mCollectorThread, mCallback,
mTestName, mRunId, mIteration);
mWindow = null;
mCallback = null;
mCollectorThread = null;
mTestName = null;
mRunId = 0;
mIteration = 0;
}
@Override
public final void onGlobalLayout() {
if (!mCollectorThread.isAlive()) {
mCollectorThread.start();
mWindow.getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this);
mReadyState.decrementAndGet();
}
}
@Override
public void onCollectorThreadReady() {
if (mReadyState.decrementAndGet() == 0) {
initHandler();
postOnAutomate();
}
}
@Override
protected void onLooperPrepared() {
if (mReadyState.decrementAndGet() == 0) {
initHandler();
postOnAutomate();
}
}
@Override
public void onPostInteraction(List<FrameMetrics> stats) {
Message m = mHandler.obtainMessage(AutomatorHandler.MSG_ON_POST_INTERACTION, stats);
mHandler.sendMessage(m);
}
protected void postOnAutomate() {
final Message msg = mHandler.obtainMessage(AutomatorHandler.MSG_ON_AUTOMATE);
mHandler.sendMessage(msg);
}
public void cancel() {
mHandler.removeMessages(AutomatorHandler.MSG_NEXT_INTERACTION);
mHandler.cancel();
mHandler = null;
}
public Automator(String testName, int runId, int iteration,
Window window, AutomateCallback callback) {
super("AutomatorThread");
mTestName = testName;
mRunId = runId;
mIteration = iteration;
mCallback = callback;
mWindow = window;
mWindow.getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(this);
mCollectorThread = new CollectorThread(this);
mReadyState = new AtomicInteger(PRE_READY_STATE_COUNT);
}
}