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