Home | History | Annotate | Download | only in automation
      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 {
     55         public void onAutomate() {}
     56         public void onPostInteraction(List<FrameMetrics> metrics) {}
     57         public void onPostAutomate() {}
     58 
     59         protected final void addInteraction(Interaction interaction) {
     60             if (mInteractions == null) {
     61                 return;
     62             }
     63 
     64             mInteractions.add(interaction);
     65         }
     66 
     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 
     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
    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 
    135         public void cancel() {
    136             mCancelled = true;
    137             stopCollector();
    138         }
    139 
    140         private void stopCollector() {
    141             mCollectorThread.quitCollector();
    142         }
    143 
    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 
    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 
    180         protected void postNextInteraction() {
    181             final Message msg = obtainMessage(AutomatorHandler.MSG_NEXT_INTERACTION);
    182             sendMessage(msg);
    183         }
    184 
    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 
    197         private void writeResults() {
    198             GlobalResultsStore.getInstance(mWindow.getContext())
    199                     .storeRunResults(mTestName, mRunId, mIteration, mResults);
    200         }
    201     }
    202 
    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
    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
    224     public void onCollectorThreadReady() {
    225         if (mReadyState.decrementAndGet() == 0) {
    226             initHandler();
    227             postOnAutomate();
    228         }
    229     }
    230 
    231     @Override
    232     protected void onLooperPrepared() {
    233         if (mReadyState.decrementAndGet() == 0) {
    234             initHandler();
    235             postOnAutomate();
    236         }
    237     }
    238 
    239     @Override
    240     public void onPostInteraction(List<FrameMetrics> stats) {
    241         Message m = mHandler.obtainMessage(AutomatorHandler.MSG_ON_POST_INTERACTION, stats);
    242         mHandler.sendMessage(m);
    243     }
    244 
    245     protected void postOnAutomate() {
    246         final Message msg = mHandler.obtainMessage(AutomatorHandler.MSG_ON_AUTOMATE);
    247         mHandler.sendMessage(msg);
    248     }
    249 
    250     public void cancel() {
    251         mHandler.removeMessages(AutomatorHandler.MSG_NEXT_INTERACTION);
    252         mHandler.cancel();
    253         mHandler = null;
    254     }
    255 
    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