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 * 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 android.view.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertTrue; 21 22 import android.app.Activity; 23 import android.app.Instrumentation; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.Looper; 27 import android.os.SystemClock; 28 import android.support.test.InstrumentationRegistry; 29 import android.support.test.filters.MediumTest; 30 import android.support.test.rule.ActivityTestRule; 31 import android.support.test.runner.AndroidJUnit4; 32 import android.view.FrameMetrics; 33 import android.view.Window; 34 import android.widget.ScrollView; 35 36 import com.android.compatibility.common.util.PollingCheck; 37 import com.android.compatibility.common.util.WidgetTestUtils; 38 39 import org.junit.Before; 40 import org.junit.Rule; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 44 import java.util.ArrayList; 45 import java.util.concurrent.atomic.AtomicInteger; 46 47 @MediumTest 48 @RunWith(AndroidJUnit4.class) 49 public class FrameMetricsListenerTest { 50 private Instrumentation mInstrumentation; 51 private Activity mActivity; 52 53 @Rule 54 public ActivityTestRule<MockActivity> mActivityRule = 55 new ActivityTestRule<>(MockActivity.class); 56 57 @Before 58 public void setup() { 59 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 60 mActivity = mActivityRule.getActivity(); 61 } 62 63 private void layout(final int layoutId) throws Throwable { 64 mActivityRule.runOnUiThread(() -> mActivity.setContentView(layoutId)); 65 mInstrumentation.waitForIdleSync(); 66 } 67 68 @Test 69 public void testReceiveData() throws Throwable { 70 layout(R.layout.scrollview_layout); 71 final ScrollView scrollView = (ScrollView) mActivity.findViewById(R.id.scroll_view); 72 final ArrayList<FrameMetrics> data = new ArrayList<>(); 73 final Handler handler = new Handler(Looper.getMainLooper()); 74 final Window myWindow = mActivity.getWindow(); 75 final Window.OnFrameMetricsAvailableListener listener = 76 (Window window, FrameMetrics frameMetrics, int dropCount) -> { 77 assertEquals(myWindow, window); 78 assertEquals(0, dropCount); 79 callGetMetric(frameMetrics); 80 data.add(new FrameMetrics(frameMetrics)); 81 }; 82 mActivityRule.runOnUiThread(() -> mActivity.getWindow(). 83 addOnFrameMetricsAvailableListener(listener, handler)); 84 85 scrollView.postInvalidate(); 86 87 PollingCheck.waitFor(() -> data.size() != 0); 88 89 mActivityRule.runOnUiThread(() -> { 90 mActivity.getWindow().removeOnFrameMetricsAvailableListener(listener); 91 }); 92 93 data.clear(); 94 95 // Produce 5 frames and assert no metric listeners were invoked 96 for (int i = 0; i < 5; i++) { 97 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, scrollView, null); 98 } 99 assertEquals(0, data.size()); 100 } 101 102 @Test 103 public void testMultipleListeners() throws Throwable { 104 layout(R.layout.scrollview_layout); 105 final ScrollView scrollView = (ScrollView) mActivity.findViewById(R.id.scroll_view); 106 final ArrayList<FrameMetrics> data1 = new ArrayList<>(); 107 final Handler handler = new Handler(Looper.getMainLooper()); 108 final Window myWindow = mActivity.getWindow(); 109 110 final Window.OnFrameMetricsAvailableListener frameMetricsListener1 = 111 (Window window, FrameMetrics frameMetrics, int dropCount) -> { 112 assertEquals(myWindow, window); 113 assertEquals(0, dropCount); 114 callGetMetric(frameMetrics); 115 data1.add(new FrameMetrics(frameMetrics)); 116 }; 117 final ArrayList<FrameMetrics> data2 = new ArrayList<>(); 118 final Window.OnFrameMetricsAvailableListener frameMetricsListener2 = 119 (Window window, FrameMetrics frameMetrics, int dropCount) -> { 120 assertEquals(myWindow, window); 121 assertEquals(0, dropCount); 122 callGetMetric(frameMetrics); 123 data2.add(new FrameMetrics(frameMetrics)); 124 }; 125 mActivityRule.runOnUiThread(() -> { 126 mActivity.getWindow().addOnFrameMetricsAvailableListener( 127 frameMetricsListener1, handler); 128 mActivity.getWindow().addOnFrameMetricsAvailableListener( 129 frameMetricsListener2, handler); 130 }); 131 132 mInstrumentation.waitForIdleSync(); 133 134 mActivityRule.runOnUiThread(() -> scrollView.fling(-100)); 135 136 mInstrumentation.waitForIdleSync(); 137 PollingCheck.waitFor(() -> data1.size() != 0 && data1.size() == data2.size()); 138 139 mActivityRule.runOnUiThread(() -> { 140 mActivity.getWindow().removeOnFrameMetricsAvailableListener(frameMetricsListener1); 141 mActivity.getWindow().removeOnFrameMetricsAvailableListener(frameMetricsListener2); 142 }); 143 } 144 145 @Test 146 public void testDropCount() throws Throwable { 147 layout(R.layout.scrollview_layout); 148 final ScrollView scrollView = (ScrollView) mActivity.findViewById(R.id.scroll_view); 149 150 final AtomicInteger framesDropped = new AtomicInteger(); 151 152 final HandlerThread thread = new HandlerThread("Listener"); 153 thread.start(); 154 final Window.OnFrameMetricsAvailableListener frameMetricsListener = 155 (Window window, FrameMetrics frameMetrics, int dropCount) -> { 156 SystemClock.sleep(100); 157 callGetMetric(frameMetrics); 158 framesDropped.addAndGet(dropCount); 159 }; 160 161 mActivityRule.runOnUiThread(() -> mActivity.getWindow(). 162 addOnFrameMetricsAvailableListener(frameMetricsListener, 163 new Handler(thread.getLooper()))); 164 165 mInstrumentation.waitForIdleSync(); 166 167 mActivityRule.runOnUiThread(() -> scrollView.fling(-100)); 168 169 mInstrumentation.waitForIdleSync(); 170 PollingCheck.waitFor(() -> framesDropped.get() > 0); 171 172 mActivityRule.runOnUiThread(() -> mActivity.getWindow(). 173 removeOnFrameMetricsAvailableListener(frameMetricsListener)); 174 } 175 176 private void callGetMetric(FrameMetrics frameMetrics) { 177 // The return values for non-boolean metrics do not have expected values. Here we 178 // are verifying that calling getMetrics does not crash 179 frameMetrics.getMetric(FrameMetrics.UNKNOWN_DELAY_DURATION); 180 frameMetrics.getMetric(FrameMetrics.INPUT_HANDLING_DURATION); 181 frameMetrics.getMetric(FrameMetrics.ANIMATION_DURATION); 182 frameMetrics.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION); 183 frameMetrics.getMetric(FrameMetrics.DRAW_DURATION); 184 frameMetrics.getMetric(FrameMetrics.SYNC_DURATION); 185 frameMetrics.getMetric(FrameMetrics.COMMAND_ISSUE_DURATION); 186 frameMetrics.getMetric(FrameMetrics.SWAP_BUFFERS_DURATION); 187 frameMetrics.getMetric(FrameMetrics.TOTAL_DURATION); 188 189 // Perform basic checks on timestamp values. 190 long intended_vsync = frameMetrics.getMetric(FrameMetrics.INTENDED_VSYNC_TIMESTAMP); 191 long vsync = frameMetrics.getMetric(FrameMetrics.VSYNC_TIMESTAMP); 192 long now = System.nanoTime(); 193 assertTrue(intended_vsync > 0); 194 assertTrue(vsync > 0); 195 assertTrue(intended_vsync < now); 196 assertTrue(vsync < now); 197 assertTrue(vsync >= intended_vsync); 198 199 // This is the only boolean metric so far 200 final long firstDrawFrameMetric = frameMetrics.getMetric(FrameMetrics.FIRST_DRAW_FRAME); 201 assertTrue("First draw frame metric should be boolean but is " + firstDrawFrameMetric, 202 (firstDrawFrameMetric == 0) || (firstDrawFrameMetric == 1)); 203 } 204 } 205