| /* |
| * 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 android.view.cts; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.junit.Assume.assumeTrue; |
| |
| import android.Manifest; |
| import android.content.Context; |
| import android.hardware.display.DisplayManager; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.view.Display; |
| import android.view.Display.Mode; |
| import android.view.Window; |
| import android.view.WindowManager; |
| |
| import androidx.test.InstrumentationRegistry; |
| import androidx.test.annotation.UiThreadTest; |
| import androidx.test.filters.FlakyTest; |
| import androidx.test.filters.MediumTest; |
| import androidx.test.filters.SmallTest; |
| import androidx.test.rule.ActivityTestRule; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import com.android.compatibility.common.util.AdoptShellPermissionsRule; |
| import com.android.compatibility.common.util.DisplayUtil; |
| |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.time.Duration; |
| import java.util.Arrays; |
| import java.util.Optional; |
| |
| @FlakyTest |
| @RunWith(AndroidJUnit4.class) |
| public class ChoreographerNativeTest { |
| private long mChoreographerPtr; |
| |
| @Rule |
| public ActivityTestRule<CtsActivity> mTestActivityRule = |
| new ActivityTestRule<>( |
| CtsActivity.class); |
| |
| @Rule |
| public final AdoptShellPermissionsRule mShellPermissionsRule = |
| new AdoptShellPermissionsRule( |
| InstrumentationRegistry.getInstrumentation().getUiAutomation(), |
| Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS, |
| Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE); |
| |
| private static native long nativeGetChoreographer(); |
| private static native boolean nativePrepareChoreographerTests(long ptr, long[] refreshPeriods); |
| private static native void nativeTestPostCallbackWithoutDelayEventuallyRunsCallbacks(long ptr); |
| private static native void nativeTestPostCallbackWithDelayEventuallyRunsCallbacks(long ptr); |
| private static native void nativeTestPostCallback64WithoutDelayEventuallyRunsCallbacks( |
| long ptr); |
| private static native void nativeTestPostCallback64WithDelayEventuallyRunsCallbacks(long ptr); |
| private static native void nativeTestPostCallbackMixedWithoutDelayEventuallyRunsCallbacks( |
| long ptr); |
| private static native void nativeTestPostCallbackMixedWithDelayEventuallyRunsCallbacks( |
| long ptr); |
| private static native void nativeTestRefreshRateCallback( |
| long ptr); |
| private static native void nativeTestUnregisteringRefreshRateCallback(long ptr); |
| private static native void nativeTestMultipleRefreshRateCallbacks(long ptr); |
| private static native void nativeTestAttemptToAddRefreshRateCallbackTwiceDoesNotAddTwice( |
| long ptr); |
| private static native void nativeTestRefreshRateCallbackMixedWithFrameCallbacks(long ptr); |
| private native void nativeTestRefreshRateCallbacksAreSyncedWithDisplayManager(); |
| |
| private Context mContext; |
| private DisplayManager mDisplayManager; |
| private Display mDefaultDisplay; |
| private long[] mSupportedPeriods; |
| |
| static { |
| System.loadLibrary("ctsview_jni"); |
| } |
| |
| @UiThreadTest |
| @Before |
| public void setup() { |
| mContext = InstrumentationRegistry.getInstrumentation().getContext(); |
| mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); |
| |
| Optional<Display> defaultDisplayOpt = Arrays.stream(mDisplayManager.getDisplays()) |
| .filter(display -> display.getDisplayId() == Display.DEFAULT_DISPLAY) |
| .findFirst(); |
| |
| assertTrue(defaultDisplayOpt.isPresent()); |
| mDefaultDisplay = defaultDisplayOpt.get(); |
| |
| mSupportedPeriods = Arrays.stream(mDefaultDisplay.getSupportedModes()) |
| .mapToLong(mode -> (long) (Duration.ofSeconds(1).toNanos() / mode.getRefreshRate())) |
| .toArray(); |
| |
| mChoreographerPtr = nativeGetChoreographer(); |
| if (!nativePrepareChoreographerTests(mChoreographerPtr, mSupportedPeriods)) { |
| fail("Failed to setup choreographer tests"); |
| } |
| } |
| |
| @MediumTest |
| @Test |
| public void testPostCallback64WithoutDelayEventuallyRunsCallbacks() { |
| nativeTestPostCallback64WithoutDelayEventuallyRunsCallbacks(mChoreographerPtr); |
| } |
| |
| @MediumTest |
| @Test |
| public void testPostCallback64WithDelayEventuallyRunsCallbacks() { |
| nativeTestPostCallback64WithDelayEventuallyRunsCallbacks(mChoreographerPtr); |
| } |
| |
| @MediumTest |
| @Test |
| public void testPostCallbackWithoutDelayEventuallyRunsCallbacks() { |
| nativeTestPostCallbackWithoutDelayEventuallyRunsCallbacks(mChoreographerPtr); |
| } |
| |
| @SmallTest |
| @Test |
| public void testPostCallbackWithDelayEventuallyRunsCallbacks() { |
| nativeTestPostCallbackWithDelayEventuallyRunsCallbacks(mChoreographerPtr); |
| } |
| |
| @SmallTest |
| @Test |
| public void testPostCallbackMixedWithoutDelayEventuallyRunsCallbacks() { |
| nativeTestPostCallbackMixedWithoutDelayEventuallyRunsCallbacks(mChoreographerPtr); |
| } |
| |
| @SmallTest |
| @Test |
| public void testPostCallbackMixedWithDelayEventuallyRunsCallbacks() { |
| nativeTestPostCallbackMixedWithDelayEventuallyRunsCallbacks(mChoreographerPtr); |
| } |
| |
| @SmallTest |
| @Test |
| public void testRefreshRateCallback() { |
| nativeTestRefreshRateCallback(mChoreographerPtr); |
| } |
| |
| @SmallTest |
| @Test |
| public void testUnregisteringRefreshRateCallback() { |
| nativeTestUnregisteringRefreshRateCallback(mChoreographerPtr); |
| } |
| |
| @SmallTest |
| @Test |
| public void testMultipleRefreshRateCallbacks() { |
| nativeTestMultipleRefreshRateCallbacks(mChoreographerPtr); |
| } |
| |
| @SmallTest |
| @Test |
| public void testAttemptToAddRefreshRateCallbackTwiceDoesNotAddTwice() { |
| nativeTestAttemptToAddRefreshRateCallbackTwiceDoesNotAddTwice(mChoreographerPtr); |
| } |
| |
| @UiThreadTest |
| @SmallTest |
| @Test |
| public void testRefreshRateCallbackMixedWithFrameCallbacks() { |
| nativeTestRefreshRateCallbackMixedWithFrameCallbacks(mChoreographerPtr); |
| } |
| |
| @SmallTest |
| @Test |
| public void testRefreshRateCallbacksIsSyncedWithDisplayManager() { |
| assumeTrue(mSupportedPeriods.length >= 2); |
| assumeTrue(findModeForSeamlessSwitch().isPresent()); |
| |
| int initialMatchContentFrameRate = 0; |
| try { |
| |
| // Set-up just for this particular test: |
| // We must force the screen to be on for this window, and DisplayManager must be |
| // configured to always respect the app-requested refresh rate. |
| Handler handler = new Handler(Looper.getMainLooper()); |
| handler.post(() -> { |
| mTestActivityRule.getActivity().getWindow().addFlags( |
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
| | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
| | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); |
| }); |
| mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true); |
| initialMatchContentFrameRate = toSwitchingType( |
| mDisplayManager.getMatchContentFrameRateUserPreference()); |
| mDisplayManager.setRefreshRateSwitchingType(DisplayManager.SWITCHING_TYPE_NONE); |
| nativeTestRefreshRateCallbacksAreSyncedWithDisplayManager(); |
| } finally { |
| mDisplayManager.setRefreshRateSwitchingType(initialMatchContentFrameRate); |
| mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false); |
| } |
| } |
| |
| // Called by jni in a refresh rate callback |
| private void checkRefreshRateIsCurrentAndSwitch(int refreshRate) { |
| assertEquals(Math.round(mDefaultDisplay.getRefreshRate()), refreshRate); |
| |
| Optional<Mode> maybeNextMode = findModeForSeamlessSwitch(); |
| assertTrue(maybeNextMode.isPresent()); |
| Mode mode = maybeNextMode.get(); |
| |
| Handler handler = new Handler(Looper.getMainLooper()); |
| handler.post(() -> { |
| Window window = mTestActivityRule.getActivity().getWindow(); |
| WindowManager.LayoutParams params = window.getAttributes(); |
| params.preferredDisplayModeId = mode.getModeId(); |
| window.setAttributes(params); |
| }); |
| } |
| |
| private Optional<Mode> findModeForSeamlessSwitch() { |
| Mode activeMode = mDefaultDisplay.getMode(); |
| int refreshRate = Math.round(mDefaultDisplay.getRefreshRate()); |
| return Arrays.stream(mDefaultDisplay.getSupportedModes()) |
| .filter(mode -> DisplayUtil.isModeSwitchSeamless(activeMode, mode)) |
| .filter(mode -> Math.round(mode.getRefreshRate()) != refreshRate) |
| .findFirst(); |
| } |
| |
| private int toSwitchingType(int matchContentFrameRateUserPreference) { |
| switch (matchContentFrameRateUserPreference) { |
| case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER: |
| return DisplayManager.SWITCHING_TYPE_NONE; |
| case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY: |
| return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS; |
| case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS: |
| return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; |
| default: |
| return -1; |
| } |
| } |
| } |