summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/display/mode/ModeChangeObserver.java8
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt187
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