1 /* 2 * Copyright (C) 2022 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 com.android.server.wm; 18 19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertTrue; 24 25 import android.content.Context; 26 import android.content.res.Resources; 27 import android.hardware.devicestate.DeviceStateManager; 28 import android.platform.test.annotations.Presubmit; 29 import android.util.Pair; 30 31 import androidx.test.filters.SmallTest; 32 33 import com.android.internal.R; 34 35 import com.google.common.util.concurrent.MoreExecutors; 36 37 import org.junit.Before; 38 import org.junit.Test; 39 40 import java.util.List; 41 import java.util.concurrent.Executor; 42 import java.util.function.Consumer; 43 44 /** 45 * Test class for {@link DeviceStateController}. 46 * 47 * Build/Install/Run: 48 * atest WmTests:DeviceStateControllerTests 49 */ 50 @SmallTest 51 @Presubmit 52 public class DeviceStateControllerTests { 53 54 private DeviceStateController mTarget; 55 private DeviceStateControllerBuilder mBuilder; 56 57 private Context mMockContext; 58 private DeviceStateManager mMockDeviceStateManager; 59 private DeviceStateController.DeviceState mCurrentState = 60 DeviceStateController.DeviceState.UNKNOWN; 61 private Consumer<DeviceStateController.DeviceState> mDelegate; 62 private Executor mExecutor = MoreExecutors.directExecutor(); 63 64 @Before setUp()65 public void setUp() { 66 mBuilder = new DeviceStateControllerBuilder(); 67 mCurrentState = DeviceStateController.DeviceState.UNKNOWN; 68 } 69 initialize(boolean supportFold, boolean supportHalfFold)70 private void initialize(boolean supportFold, boolean supportHalfFold) { 71 mBuilder.setSupportFold(supportFold, supportHalfFold); 72 mDelegate = (newFoldState) -> { 73 mCurrentState = newFoldState; 74 }; 75 mBuilder.setDelegate(mDelegate); 76 mBuilder.build(); 77 } 78 79 @Test testInitialization()80 public void testInitialization() { 81 initialize(true /* supportFold */, true /* supportHalfFolded */); 82 mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); 83 assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); 84 } 85 86 @Test testInitializationWithNoFoldSupport()87 public void testInitializationWithNoFoldSupport() { 88 initialize(false /* supportFold */, false /* supportHalfFolded */); 89 mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); 90 // Note that the folded state is ignored. 91 assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); 92 } 93 94 @Test testWithFoldSupported()95 public void testWithFoldSupported() { 96 initialize(true /* supportFold */, false /* supportHalfFolded */); 97 mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); 98 assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); 99 mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); 100 assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); 101 mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]); 102 assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored 103 } 104 105 @Test testWithHalfFoldSupported()106 public void testWithHalfFoldSupported() { 107 initialize(true /* supportFold */, true /* supportHalfFolded */); 108 mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); 109 assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); 110 mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); 111 assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); 112 mTarget.onDeviceStateReceivedByDisplayManager(mHalfFoldedStates[0]); 113 assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState); 114 mTarget.onDeviceStateReceivedByDisplayManager(mConcurrentDisplayState); 115 assertEquals(DeviceStateController.DeviceState.CONCURRENT, mCurrentState); 116 } 117 118 @Test testUnregisterDeviceStateCallback()119 public void testUnregisterDeviceStateCallback() { 120 initialize(true /* supportFold */, true /* supportHalfFolded */); 121 assertEquals(1, mTarget.mDeviceStateCallbacks.size()); 122 assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDelegate)); 123 124 mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); 125 assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState); 126 mTarget.onDeviceStateReceivedByDisplayManager(mFoldedStates[0]); 127 assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState); 128 129 // The callback should not receive state change when it is unregistered. 130 mTarget.unregisterDeviceStateCallback(mDelegate); 131 assertTrue(mTarget.mDeviceStateCallbacks.isEmpty()); 132 mTarget.onDeviceStateReceivedByDisplayManager(mOpenDeviceStates[0]); 133 assertEquals(DeviceStateController.DeviceState.FOLDED /* unchanged */, mCurrentState); 134 } 135 136 @Test testCopyDeviceStateCallbacks()137 public void testCopyDeviceStateCallbacks() { 138 initialize(true /* supportFold */, true /* supportHalfFolded */); 139 assertEquals(1, mTarget.mDeviceStateCallbacks.size()); 140 assertTrue(mTarget.mDeviceStateCallbacks.containsKey(mDelegate)); 141 142 List<Pair<Consumer<DeviceStateController.DeviceState>, Executor>> entries = 143 mTarget.copyDeviceStateCallbacks(); 144 mTarget.unregisterDeviceStateCallback(mDelegate); 145 146 // In contrast to List<Map.Entry> where the entries are tied to changes in the backing map, 147 // List<Pair> should still contain non-null callbacks and executors even though they were 148 // removed from the backing map via the unregister method above. 149 assertEquals(1, entries.size()); 150 assertEquals(mDelegate, entries.get(0).first); 151 assertEquals(mExecutor, entries.get(0).second); 152 } 153 154 private final int[] mFoldedStates = {0}; 155 private final int[] mOpenDeviceStates = {1}; 156 private final int[] mHalfFoldedStates = {2}; 157 private final int[] mRearDisplayStates = {3}; 158 private final int mConcurrentDisplayState = 4; 159 160 private class DeviceStateControllerBuilder { 161 private boolean mSupportFold = false; 162 private boolean mSupportHalfFold = false; 163 private Consumer<DeviceStateController.DeviceState> mDelegate; 164 setSupportFold( boolean supportFold, boolean supportHalfFold)165 DeviceStateControllerBuilder setSupportFold( 166 boolean supportFold, boolean supportHalfFold) { 167 mSupportFold = supportFold; 168 mSupportHalfFold = supportHalfFold; 169 return this; 170 } 171 setDelegate( Consumer<DeviceStateController.DeviceState> delegate)172 DeviceStateControllerBuilder setDelegate( 173 Consumer<DeviceStateController.DeviceState> delegate) { 174 mDelegate = delegate; 175 return this; 176 } 177 mockFold(boolean enableFold, boolean enableHalfFold)178 private void mockFold(boolean enableFold, boolean enableHalfFold) { 179 if (enableFold || enableHalfFold) { 180 when(mMockContext.getResources() 181 .getIntArray(R.array.config_openDeviceStates)) 182 .thenReturn(mOpenDeviceStates); 183 when(mMockContext.getResources() 184 .getIntArray(R.array.config_rearDisplayDeviceStates)) 185 .thenReturn(mRearDisplayStates); 186 when(mMockContext.getResources() 187 .getInteger(R.integer.config_deviceStateConcurrentRearDisplay)) 188 .thenReturn(mConcurrentDisplayState); 189 } else { 190 // Match the default value in framework resources 191 when(mMockContext.getResources() 192 .getInteger(R.integer.config_deviceStateConcurrentRearDisplay)) 193 .thenReturn(-1); 194 } 195 196 if (enableFold) { 197 when(mMockContext.getResources() 198 .getIntArray(R.array.config_foldedDeviceStates)) 199 .thenReturn(mFoldedStates); 200 } 201 if (enableHalfFold) { 202 when(mMockContext.getResources() 203 .getIntArray(R.array.config_halfFoldedDeviceStates)) 204 .thenReturn(mHalfFoldedStates); 205 } 206 } 207 build()208 private void build() { 209 mMockContext = mock(Context.class); 210 mMockDeviceStateManager = mock(DeviceStateManager.class); 211 when(mMockContext.getSystemService(DeviceStateManager.class)) 212 .thenReturn(mMockDeviceStateManager); 213 Resources mockRes = mock(Resources.class); 214 when(mMockContext.getResources()).thenReturn((mockRes)); 215 mockFold(mSupportFold, mSupportHalfFold); 216 mTarget = new DeviceStateController(mMockContext, new WindowManagerGlobalLock()); 217 mTarget.registerDeviceStateCallback(mDelegate, mExecutor); 218 } 219 } 220 } 221