1 /* 2 * Copyright (C) 2015 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 com.android.benchmark.ui.automation; 18 19 import android.annotation.TargetApi; 20 import android.os.Handler; 21 import android.os.HandlerThread; 22 import android.os.Message; 23 import android.os.SystemClock; 24 import android.view.FrameMetrics; 25 import android.view.Window; 26 27 import java.lang.ref.WeakReference; 28 import java.util.LinkedList; 29 import java.util.List; 30 31 /** 32 * 33 */ 34 final class CollectorThread extends HandlerThread { 35 private FrameStatsCollector mCollector; 36 private Window mAttachedWindow; 37 private List<FrameMetrics> mFrameTimingStats; 38 private long mLastFrameTime; 39 private WatchdogHandler mWatchdog; 40 private WeakReference<CollectorListener> mListener; 41 42 private volatile boolean mCollecting; 43 44 45 interface CollectorListener { onCollectorThreadReady()46 void onCollectorThreadReady(); onPostInteraction(List<FrameMetrics> stats)47 void onPostInteraction(List<FrameMetrics> stats); 48 } 49 50 private final class WatchdogHandler extends Handler { 51 private static final long SCHEDULE_INTERVAL_MILLIS = 20 * Automator.FRAME_PERIOD_MILLIS; 52 53 private static final int MSG_SCHEDULE = 0; 54 55 @Override handleMessage(Message msg)56 public void handleMessage(Message msg) { 57 if (!mCollecting) { 58 return; 59 } 60 61 long currentTime = SystemClock.uptimeMillis(); 62 if (mLastFrameTime + SCHEDULE_INTERVAL_MILLIS <= currentTime) { 63 // haven't seen a frame in a while, interaction is probably done 64 mCollecting = false; 65 CollectorListener listener = mListener.get(); 66 if (listener != null) { 67 listener.onPostInteraction(mFrameTimingStats); 68 } 69 } else { 70 schedule(); 71 } 72 } 73 schedule()74 public void schedule() { 75 sendMessageDelayed(obtainMessage(MSG_SCHEDULE), SCHEDULE_INTERVAL_MILLIS); 76 } 77 deschedule()78 public void deschedule() { 79 removeMessages(MSG_SCHEDULE); 80 } 81 } 82 83 static boolean tripleBuffered = false; 84 static int janks = 0; 85 static int total = 0; 86 @TargetApi(24) 87 private class FrameStatsCollector implements Window.OnFrameMetricsAvailableListener { 88 @Override onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCount)89 public void onFrameMetricsAvailable(Window window, FrameMetrics frameMetrics, int dropCount) { 90 if (!mCollecting) { 91 return; 92 } 93 mFrameTimingStats.add(new FrameMetrics(frameMetrics)); 94 mLastFrameTime = SystemClock.uptimeMillis(); 95 } 96 } 97 CollectorThread(CollectorListener listener)98 public CollectorThread(CollectorListener listener) { 99 super("FrameStatsCollectorThread"); 100 mFrameTimingStats = new LinkedList<>(); 101 mListener = new WeakReference<>(listener); 102 } 103 104 @TargetApi(24) attachToWindow(Window window)105 public void attachToWindow(Window window) { 106 if (mAttachedWindow != null) { 107 mAttachedWindow.removeOnFrameMetricsAvailableListener(mCollector); 108 } 109 110 mAttachedWindow = window; 111 window.addOnFrameMetricsAvailableListener(mCollector, new Handler(getLooper())); 112 } 113 114 @TargetApi(24) detachFromWindow()115 public synchronized void detachFromWindow() { 116 if (mAttachedWindow != null) { 117 mAttachedWindow.removeOnFrameMetricsAvailableListener(mCollector); 118 } 119 120 mAttachedWindow = null; 121 } 122 123 @TargetApi(24) 124 @Override onLooperPrepared()125 protected void onLooperPrepared() { 126 super.onLooperPrepared(); 127 mCollector = new FrameStatsCollector(); 128 mWatchdog = new WatchdogHandler(); 129 130 CollectorListener listener = mListener.get(); 131 if (listener != null) { 132 listener.onCollectorThreadReady(); 133 } 134 } 135 quitCollector()136 public boolean quitCollector() { 137 stopCollecting(); 138 detachFromWindow(); 139 System.out.println("Jank Percentage: " + (100 * janks/ (double) total) + "%"); 140 tripleBuffered = false; 141 total = 0; 142 janks = 0; 143 return quit(); 144 } 145 stopCollecting()146 void stopCollecting() { 147 if (!mCollecting) { 148 return; 149 } 150 151 mCollecting = false; 152 mWatchdog.deschedule(); 153 154 155 } 156 markInteractionStart()157 public void markInteractionStart() { 158 mLastFrameTime = 0; 159 mFrameTimingStats.clear(); 160 mCollecting = true; 161 162 mWatchdog.schedule(); 163 } 164 } 165