1 /*
2  * Copyright (C) 2011 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.assertTrue;
20 
21 import android.Manifest;
22 import android.content.Context;
23 import android.hardware.display.DisplayManager;
24 import android.os.Handler;
25 import android.os.Looper;
26 import android.os.SystemClock;
27 import android.platform.test.annotations.AppModeSdkSandbox;
28 import android.util.Log;
29 import android.view.Display;
30 import android.view.WindowManager;
31 
32 import androidx.test.filters.LargeTest;
33 import androidx.test.platform.app.InstrumentationRegistry;
34 import androidx.test.rule.ActivityTestRule;
35 import androidx.test.runner.AndroidJUnit4;
36 
37 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
38 
39 import org.junit.After;
40 import org.junit.Before;
41 import org.junit.Rule;
42 import org.junit.Test;
43 import org.junit.runner.RunWith;
44 
45 import java.util.concurrent.CountDownLatch;
46 import java.util.concurrent.TimeUnit;
47 
48 /**
49  * Test that the screen refresh rate claimed by
50  * android.view.Display.getRefreshRate() matches the steady-state framerate
51  * achieved by vsync-limited eglSwapBuffers(). The primary goal is to test
52  * Display.getRefreshRate() -- using GL is just an easy and hopefully reliable
53  * way of measuring the actual refresh rate.
54  */
55 @LargeTest
56 @RunWith(AndroidJUnit4.class)
57 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
58 public class DisplayRefreshRateTest {
59     // The test passes if
60     //   abs(measured_fps - Display.getRefreshRate()) <= FPS_TOLERANCE.
61     // A smaller tolerance requires a more accurate measured_fps in order
62     // to avoid false negatives.
63     private static final float FPS_TOLERANCE = 2.0f;
64 
65     private static final String TAG = "DisplayRefreshRateTest";
66 
67     private DisplayManager mDisplayManager;
68 
69     private Display mDisplay;
70 
71     private int mInitialMatchContentFrameRate;
72 
73     private final DisplayListener mDisplayListener = new DisplayListener();
74 
75     private DisplayRefreshRateCtsActivity mActivity;
76     private DisplayRefreshRateCtsActivity.FpsResult mFpsResult;
77 
78     @Rule(order = 1)
79     public ActivityTestRule<DisplayRefreshRateCtsActivity> mActivityRule =
80             new ActivityTestRule<>(DisplayRefreshRateCtsActivity.class);
81 
82     @Rule(order = 0)
83     public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
84             InstrumentationRegistry.getInstrumentation().getUiAutomation(),
85             Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
86             Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE,
87             Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
88 
89     class DisplayListener implements DisplayManager.DisplayListener {
90         private CountDownLatch mCountDownLatch = new CountDownLatch(1);
91 
waitForModeToChange(int modeId)92         void waitForModeToChange(int modeId) throws InterruptedException {
93             while (modeId != mDisplay.getMode().getModeId()
94                 && mDisplay.getMode().getRefreshRate() != mDisplay.getRefreshRate()) {
95                 mCountDownLatch.await(5, TimeUnit.SECONDS);
96             }
97         }
98 
99         @Override
onDisplayAdded(int displayId)100         public void onDisplayAdded(int displayId) {
101 
102         }
103 
104         @Override
onDisplayRemoved(int displayId)105         public void onDisplayRemoved(int displayId) {
106 
107         }
108 
109         @Override
onDisplayChanged(int displayId)110         public void onDisplayChanged(int displayId) {
111             if (displayId != mDisplay.getDisplayId()) {
112                 return;
113             }
114 
115             mCountDownLatch.countDown();
116         }
117     }
118 
119 
120     @Before
setup()121     public void setup() throws InterruptedException {
122         mActivity = mActivityRule.getActivity();
123         mFpsResult = mActivity.getFpsResult();
124 
125         Context context = mActivity.getApplicationContext();
126         mDisplayManager = context.getSystemService(DisplayManager.class);
127 
128         mInitialMatchContentFrameRate =
129                 toSwitchingType(mDisplayManager.getMatchContentFrameRateUserPreference());
130         mDisplayManager.setRefreshRateSwitchingType(DisplayManager.SWITCHING_TYPE_NONE);
131         mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true);
132 
133         // This tests the fps of the default display.
134         // In consideration of multi-display devices we use getApplicationContext()
135         // to get the default display.
136         WindowManager wm = context.getSystemService(WindowManager.class);
137         mDisplay = wm.getDefaultDisplay();
138 
139         mDisplayManager.registerDisplayListener(mDisplayListener,
140                 new Handler(Looper.getMainLooper()));
141 
142         int highestRefreshRateModeId = getHighestRefreshRateModeId();
143         mActivity.setModeId(highestRefreshRateModeId);
144         mDisplayListener.waitForModeToChange(highestRefreshRateModeId);
145     }
146 
getHighestRefreshRateModeId()147     private int getHighestRefreshRateModeId() {
148         int highestRefreshRateModeId = mDisplay.getMode().getModeId();
149         for (Display.Mode mode : mDisplay.getSupportedModes()) {
150             if (mode.getPhysicalHeight() != mDisplay.getMode().getPhysicalHeight()) {
151                 continue;
152             }
153 
154             if (mode.getPhysicalWidth() != mDisplay.getMode().getPhysicalWidth()) {
155                 continue;
156             }
157 
158             if (mode.getRefreshRate() > mDisplay.getMode().getRefreshRate()) {
159                 highestRefreshRateModeId = mode.getModeId();
160             }
161         }
162         return highestRefreshRateModeId;
163     }
164 
165     @After
tearDown()166     public void tearDown() {
167         mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate);
168         mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false);
169     }
170 
171     @Test
testRefreshRate()172     public void testRefreshRate() {
173         boolean fpsOk = false;
174 
175         for (int i = 0; i < 3; i++) {
176             float claimedFps = mDisplay.getRefreshRate();
177             float achievedFps = mFpsResult.waitResult();
178             Log.d(TAG, "claimed " + claimedFps + " fps, " +
179                        "achieved " + achievedFps + " fps");
180             fpsOk = Math.abs(claimedFps - achievedFps) <= FPS_TOLERANCE;
181             if (fpsOk) {
182                 break;
183             } else {
184                 // it could be other activity like bug report capturing for other failures
185                 // sleep for a while and re-try
186                 SystemClock.sleep(10000);
187                 mFpsResult.restart();
188             }
189         }
190         mActivity.finish();
191         assertTrue(fpsOk);
192     }
193 
toSwitchingType(int matchContentFrameRateUserPreference)194     private static int toSwitchingType(int matchContentFrameRateUserPreference) {
195         switch (matchContentFrameRateUserPreference) {
196             case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER:
197                 return DisplayManager.SWITCHING_TYPE_NONE;
198             case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY:
199                 return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
200             case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS:
201                 return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
202             default:
203                 return -1;
204         }
205     }
206 }
207