diff options
| -rw-r--r-- | services/core/java/com/android/server/display/mode/ModeChangeObserver.java | 8 | ||||
| -rw-r--r-- | services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt | 187 |
2 files changed, 193 insertions, 2 deletions
diff --git a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java index 50782a2f22c8..debfb067b710 100644 --- a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java +++ b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java @@ -25,6 +25,8 @@ import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayEventReceiver; +import androidx.annotation.VisibleForTesting; + import java.util.HashSet; import java.util.Set; @@ -35,8 +37,10 @@ final class ModeChangeObserver { private final DisplayModeDirector.Injector mInjector; @SuppressWarnings("unused") - private DisplayEventReceiver mModeChangeListener; - private DisplayManager.DisplayListener mDisplayListener; + @VisibleForTesting + DisplayEventReceiver mModeChangeListener; + @VisibleForTesting + DisplayManager.DisplayListener mDisplayListener; private final LongSparseArray<Set<Integer>> mRejectedModesMap = new LongSparseArray<>(); private final LongSparseArray<Integer> mPhysicalIdToLogicalIdMap = new LongSparseArray<>(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt new file mode 100644 index 000000000000..a5fb6cdc8d4f --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2023 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.server.display.mode + +import android.os.Looper +import android.view.Display +import android.view.DisplayAddress +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +private const val PHYSICAL_DISPLAY_ID_1 = 1L +private const val PHYSICAL_DISPLAY_ID_2 = 2L +private const val MODE_ID_1 = 3 +private const val MODE_ID_2 = 4 +private const val LOGICAL_DISPLAY_ID = 5 +private val physicalAddress1 = DisplayAddress.fromPhysicalDisplayId(PHYSICAL_DISPLAY_ID_1) +private val physicalAddress2 = DisplayAddress.fromPhysicalDisplayId(PHYSICAL_DISPLAY_ID_2) + +/** + * Tests for ModeChangeObserver, comply with changes in b/31925610 + */ +@SmallTest +@RunWith(AndroidJUnit4::class) +public class ModeChangeObserverTest { + @get:Rule + val mockitoRule = MockitoJUnit.rule() + + // System Under Test + private lateinit var modeChangeObserver: ModeChangeObserver + + // Non Mocks + private val looper = Looper.getMainLooper() + private val votesStorage = VotesStorage({}, null) + + // Mocks + private val mockInjector = mock<DisplayModeDirector.Injector>() + private val mockDisplay1 = mock<Display>() + private val mockDisplay2 = mock<Display>() + + @Before + fun setUp() { + whenever(mockInjector.getDisplay(LOGICAL_DISPLAY_ID)).thenReturn(mockDisplay1) + whenever(mockDisplay1.getAddress()).thenReturn(physicalAddress1) + whenever(mockInjector.getDisplays()).thenReturn(arrayOf<Display>()) + modeChangeObserver = ModeChangeObserver(votesStorage, mockInjector, looper) + modeChangeObserver.observe() + } + + @Test + fun testOnModeRejectedBeforeDisplayAdded() { + val rejectedModes = HashSet<Int>() + rejectedModes.add(MODE_ID_1) + rejectedModes.add(MODE_ID_2) + + // ModeRejected is called before display is mapped, hence votes are null + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1) + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_2) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // Display is mapped to a Logical Display Id, now the Rejected Mode Votes get updated + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(newVotes.size()).isEqualTo(1) + val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(vote).isInstanceOf(RejectedModesVote::class.java) + val rejectedModesVote = vote as RejectedModesVote + assertThat(rejectedModesVote.mModeIds.size).isEqualTo(rejectedModes.size) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_2) + } + + @Test + fun testOnDisplayAddedBeforeOnModeRejected() { + // Display is mapped to the corresponding Logical Id, but Mode Rejected no received yet + // Verify that the Vote is still Null + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // ModeRejected Event received for the mapped display + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1) + val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(newVotes.size()).isEqualTo(1) + val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(vote).isInstanceOf(RejectedModesVote::class.java) + val rejectedModesVote = vote as RejectedModesVote + assertThat(rejectedModesVote.mModeIds.size).isEqualTo(1) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1) + } + + @Test + fun testOnDisplayAddedThenRejectedThenRemoved() { + // Display is mapped to its Logical Display Id + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // ModeRejected Event is received for mapped display + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1) + val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(newVotes.size()).isEqualTo(1) + val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(vote).isInstanceOf(RejectedModesVote::class.java) + val rejectedModesVote = vote as RejectedModesVote + assertThat(rejectedModesVote.mModeIds.size).isEqualTo(1) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1) + + // Display mapping is removed, hence remove the votes + modeChangeObserver.mDisplayListener.onDisplayRemoved(LOGICAL_DISPLAY_ID) + val finalVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(finalVotes.size()).isEqualTo(0) + } + + @Test + fun testForModesRejectedAfterDisplayChanged() { + // Mock Display 1 is mapped to logicalId + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // Mode Rejected received for PhysicalId2 not mapped yet, so votes are null + whenever(mockInjector.getDisplay(LOGICAL_DISPLAY_ID)).thenReturn(mockDisplay2) + whenever(mockDisplay2.getAddress()).thenReturn(physicalAddress2) + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_2, MODE_ID_2) + val changedVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(changedVotes.size()).isEqualTo(0) + + // Display mapping changed, now PhysicalId2 is mapped to the LogicalId, votes get updated + modeChangeObserver.mDisplayListener.onDisplayChanged(LOGICAL_DISPLAY_ID) + val finalVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(finalVotes.size()).isEqualTo(1) + val finalVote = finalVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(finalVote).isInstanceOf(RejectedModesVote::class.java) + val newRejectedModesVote = finalVote as RejectedModesVote + assertThat(newRejectedModesVote.mModeIds.size).isEqualTo(1) + assertThat(newRejectedModesVote.mModeIds).contains(MODE_ID_2) + } + + @Test + fun testForModesNotRejectedAfterDisplayChanged() { + // Mock Display 1 is added + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // Mode Rejected received for Display 1, votes added for rejected mode + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1) + val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(newVotes.size()).isEqualTo(1) + val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(vote).isInstanceOf(RejectedModesVote::class.java) + val rejectedModesVote = vote as RejectedModesVote + assertThat(rejectedModesVote.mModeIds.size).isEqualTo(1) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1) + + // Display Changed such that logical Id corresponds to PhysicalDisplayId2 + // Rejected Modes Vote is removed + whenever(mockInjector.getDisplay(LOGICAL_DISPLAY_ID)).thenReturn(mockDisplay2) + whenever(mockDisplay2.getAddress()).thenReturn(physicalAddress2) + modeChangeObserver.mDisplayListener.onDisplayChanged(LOGICAL_DISPLAY_ID) + val finalVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(finalVotes.size()).isEqualTo(0) + } +}
\ No newline at end of file |