diff options
Diffstat (limited to 'apps/CtsVerifier/src/com/android/cts/verifier/tv/display/HotplugTestActivity.java')
-rw-r--r-- | apps/CtsVerifier/src/com/android/cts/verifier/tv/display/HotplugTestActivity.java | 228 |
1 files changed, 228 insertions, 0 deletions
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/HotplugTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/HotplugTestActivity.java new file mode 100644 index 00000000000..79120b22993 --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/HotplugTestActivity.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2021 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 com.android.cts.verifier.tv.display; + +import android.annotation.StringRes; +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.view.Display; +import android.view.View; + +import com.android.cts.verifier.R; +import com.android.cts.verifier.tv.TestSequence; +import com.android.cts.verifier.tv.TestStepBase; +import com.android.cts.verifier.tv.TvAppVerifierActivity; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Test when the HDMI cable on HDMI source devices (e.g. set-top boxes and TV sticks) is plugged out + * and in {@link android.hardware.display.DisplayManager.DisplayListener#onDisplayChanged}. The + * test also verifies that the display capabilities when the HDMI cable is unplugged contain only + * one display mode and no HDR capabilities. + */ +public class HotplugTestActivity extends TvAppVerifierActivity { + private static final float REFRESH_RATE_TOLERANCE = 0.01f; + private TestSequence mTestSequence; + + @Override + protected void setInfoResources() { + setInfoResources(R.string.tv_hotplug_test, R.string.tv_hotplug_test_info, -1); + } + + @Override + protected void createTestItems() { + List<TestStepBase> testSteps = new ArrayList<>(); + testSteps.add(new NoDisplayTestStep(this)); + mTestSequence = new TestSequence(this, testSteps); + mTestSequence.init(); + } + + @Override + public String getTestDetails() { + return mTestSequence.getFailureDetails(); + } + + private static class DisplayCapabilities { + public Display.Mode[] modes; + public Display.HdrCapabilities hdrCapabilities; + + DisplayCapabilities(Display display) { + this.modes = display.getSupportedModes(); + this.hdrCapabilities = display.getHdrCapabilities(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof DisplayCapabilities)) return false; + DisplayCapabilities that = (DisplayCapabilities) o; + return modesAreEqual(modes, that.modes) + && Objects.equals(hdrCapabilities, that.hdrCapabilities); + } + + private boolean modesAreEqual(Display.Mode[] left, Display.Mode[] right) { + if (left.length != right.length) { + return false; + } + + for (int i = 0; i < left.length; i++) { + if (!modesAreEqual(left[i], right[i])) { + return false; + } + } + + return true; + } + + private boolean modesAreEqual(Display.Mode left, Display.Mode right) { + // Compare all properties except the ID. + return left.getPhysicalHeight() == right.getPhysicalHeight() + && left.getPhysicalWidth() == right.getPhysicalWidth() + && Math.abs(left.getRefreshRate() - right.getRefreshRate()) + < REFRESH_RATE_TOLERANCE + && Arrays.equals( + left.getAlternativeRefreshRates(), right.getAlternativeRefreshRates()); + } + + @Override + public String toString() { + String modesStr = Arrays.stream(modes) + .map(Display.Mode::toString) + .collect(Collectors.joining("\n\t")); + return "DisplayCapabilities{" + + "modes=" + modesStr + + ", hdrCapabilities=" + hdrCapabilities + '}'; + } + } + + private static class NoDisplayTestStep extends AsyncTestStep implements + DisplayManager.DisplayListener { + // All recorded states of the display capabilities. The first element are the initial + // display capabilities; each subsequent element is the state after a received + // onDisplayChanged(). + private List<DisplayCapabilities> mRecordedCapabilities = new ArrayList<>(); + + private int mNumOnDisplayChanged = 0; + private boolean mDisconnectDetected = false; + private boolean mConnectDetected = false; + + private View mDoneButtonView; + private DisplayManager mDisplayManager; + + NoDisplayTestStep(TvAppVerifierActivity context) { + super( + context, + R.string.tv_hotplug_test, + getInstructionText(context), + getButtonStringId()); + } + + private static String getInstructionText(Context context) { + return context.getString( + R.string.tv_hotplug_disconnect_display, + context.getString(getButtonStringId())); + } + + @StringRes + private static int getButtonStringId() { + return R.string.tv_start_test; + } + + @Override + public List<View> createUiElements() { + List<View> list = super.createUiElements(); + mDoneButtonView = TvAppVerifierActivity.createButtonItem( + mContext.getLayoutInflater(), + null, + R.string.tv_done, + (View view) -> recordTestStateAndFinish()); + list.add(mDoneButtonView); + return list; + } + + @Override + public void runTestAsync() { + setButtonEnabled(mDoneButtonView, true); + + mDisplayManager = mContext.getSystemService(DisplayManager.class); + Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY); + mRecordedCapabilities.add(new DisplayCapabilities(display)); + mDisplayManager.registerDisplayListener(this, null); + } + + @Override + public void onDisplayAdded(int displayId) { + getAsserter().withMessage("onDisplayAdded() is not expected").fail(); + } + + @Override + public void onDisplayRemoved(int displayId) { + getAsserter().withMessage("onDisplayRemoved() is not expected").fail(); + } + + @Override + public void onDisplayChanged(int displayId) { + getAsserter().that(displayId).isEqualTo(Display.DEFAULT_DISPLAY); + + mNumOnDisplayChanged++; + DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); + DisplayCapabilities currCapabilities = new DisplayCapabilities(display); + mRecordedCapabilities.add(currCapabilities); + + // Some TVs may send multiple onDisplayChanged() events, for that reason we + // test that in the sequence of states after each onDisplayChanged() there's + // a state which looks like a disconnected display, followed by a state which looks + // like the initial state. + if (!mDisconnectDetected) { + boolean isDisconnectedDisplay = + display.getHdrCapabilities().getSupportedHdrTypes().length == 0 + && display.getSupportedModes().length == 1; + + if (isDisconnectedDisplay) { + mDisconnectDetected = true; + } + } else if (!mConnectDetected) { + DisplayCapabilities initialCapabilities = mRecordedCapabilities.get(0); + if (currCapabilities.equals(initialCapabilities)) { + mConnectDetected = true; + recordTestStateAndFinish(); + } + } + } + + private void recordTestStateAndFinish() { + setButtonEnabled(mDoneButtonView, false); + mDisplayManager.unregisterDisplayListener(this); + if (!mConnectDetected || !mDisconnectDetected) { + String recordedCapabilitiesStr = mRecordedCapabilities.stream() + .map(DisplayCapabilities::toString) + .collect(Collectors.joining("\n")); + String message = "Number of onDisplayChanged() events = " + mNumOnDisplayChanged + + "\n" + "Disconnect detected = " + mDisconnectDetected + "\n" + + "Recorded states = " + recordedCapabilitiesStr; + getAsserter().withMessage(message).fail(); + } + done(); + } + } +} |