1 /*
2  * Copyright (C) 2023 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.policy;
18 
19 import static android.os.PowerManager.WAKE_REASON_CAMERA_LAUNCH;
20 import static android.os.PowerManager.WAKE_REASON_LID;
21 import static android.os.PowerManager.WAKE_REASON_GESTURE;
22 import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON;
23 import static android.os.PowerManager.WAKE_REASON_WAKE_KEY;
24 import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
25 import static android.view.InputDevice.SOURCE_ROTARY_ENCODER;
26 import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
27 import static android.view.KeyEvent.KEYCODE_HOME;
28 import static android.view.KeyEvent.KEYCODE_POWER;
29 import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY;
30 
31 import static com.android.internal.R.bool.config_allowTheaterModeWakeFromKey;
32 import static com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey;
33 import static com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion;
34 import static com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens;
35 import static com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch;
36 import static com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture;
37 import static com.android.server.policy.Flags.FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE;
38 
39 import static com.google.common.truth.Truth.assertThat;
40 import static com.google.common.truth.Truth.assertWithMessage;
41 
42 import static org.mockito.ArgumentMatchers.anyBoolean;
43 import static org.mockito.ArgumentMatchers.anyInt;
44 import static org.mockito.ArgumentMatchers.anyLong;
45 import static org.mockito.ArgumentMatchers.anyString;
46 import static org.mockito.Mockito.never;
47 import static org.mockito.Mockito.spy;
48 import static org.mockito.Mockito.verify;
49 import static org.mockito.Mockito.when;
50 
51 import android.content.Context;
52 import android.content.ContextWrapper;
53 import android.content.res.Resources;
54 import android.os.PowerManager;
55 import android.platform.test.flag.junit.SetFlagsRule;
56 import android.provider.Settings;
57 import android.view.Display;
58 import android.view.WindowManager;
59 
60 import androidx.test.InstrumentationRegistry;
61 
62 import com.android.internal.os.Clock;
63 import com.android.internal.util.test.FakeSettingsProvider;
64 import com.android.internal.util.test.FakeSettingsProviderRule;
65 import com.android.server.LocalServices;
66 
67 import org.junit.Before;
68 import org.junit.Rule;
69 import org.junit.Test;
70 import org.mockito.Mock;
71 import org.mockito.Mockito;
72 import org.mockito.junit.MockitoJUnit;
73 import org.mockito.junit.MockitoRule;
74 
75 import java.util.function.BooleanSupplier;
76 /**
77  * Test class for {@link WindowWakeUpPolicy}.
78  *
79  * <p>Build/Install/Run: atest WmTests:WindowWakeUpPolicyTests
80  */
81 public final class WindowWakeUpPolicyTests {
82     @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
83     @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
84     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
85 
86     @Mock PowerManager mPowerManager;
87     @Mock WindowManager mWindowManager;
88     @Mock Display mDefaultDisplay;
89     @Mock Clock mClock;
90     @Mock WindowWakeUpPolicyInternal.InputWakeUpDelegate mInputWakeUpDelegate;
91 
92     private Context mContextSpy;
93     private Resources mResourcesSpy;
94 
95     private WindowWakeUpPolicy mPolicy;
96 
97     @Before
setUp()98     public void setUp() {
99         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
100         mResourcesSpy = spy(mContextSpy.getResources());
101         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
102         when(mContextSpy.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
103         when(mContextSpy.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
104         when(mWindowManager.getDefaultDisplay()).thenReturn(mDefaultDisplay);
105         LocalServices.removeServiceForTest(WindowWakeUpPolicyInternal.class);
106         setDefaultDisplayState(Display.STATE_OFF);
107     }
108 
109     @Test
testSupportsInputWakeDelegatse_publishesLocalService()110     public void testSupportsInputWakeDelegatse_publishesLocalService() {
111         mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
112 
113         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
114 
115         assertThat(LocalServices.getService(WindowWakeUpPolicyInternal.class)).isNotNull();
116     }
117 
118     @Test
testDoesNotSupportInputWakeDelegatse_doesNotPublishLocalService()119     public void testDoesNotSupportInputWakeDelegatse_doesNotPublishLocalService() {
120         mSetFlagsRule.disableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
121 
122         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
123 
124         assertThat(LocalServices.getService(WindowWakeUpPolicyInternal.class)).isNull();
125     }
126 
127     @Test
testMotionWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake()128     public void testMotionWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() {
129         setTheaterModeEnabled(false);
130         mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
131         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
132         LocalServices.getService(WindowWakeUpPolicyInternal.class)
133                 .setInputWakeUpDelegate(mInputWakeUpDelegate);
134 
135         setDelegatedMotionWakeUpResult(true);
136 
137         // Verify the policy wake up call succeeds because of the call on the delegate, and not
138         // because of a PowerManager wake up.
139         assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isTrue();
140         verify(mInputWakeUpDelegate).wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true);
141         verifyNoPowerManagerWakeUp();
142 
143         setDelegatedMotionWakeUpResult(false);
144 
145         // Verify the policy wake up call succeeds because of the PowerManager wake up, since the
146         // delegate would not handle the wake up request.
147         assertThat(mPolicy.wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false)).isTrue();
148         verify(mInputWakeUpDelegate).wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false);
149         verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
150     }
151 
152     @Test
testKeyWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake()153     public void testKeyWakeUpDelegation_wakePowerManagerIfDelegateDoesNotHandleWake() {
154         setTheaterModeEnabled(false);
155         mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
156         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
157         LocalServices.getService(WindowWakeUpPolicyInternal.class)
158                 .setInputWakeUpDelegate(mInputWakeUpDelegate);
159 
160         setDelegatedKeyWakeUpResult(true);
161 
162         // Verify the policy wake up call succeeds because of the call on the delegate, and not
163         // because of a PowerManager wake up.
164         assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isTrue();
165         verify(mInputWakeUpDelegate).wakeUpFromKey(200, KEYCODE_POWER, true);
166         verifyNoPowerManagerWakeUp();
167 
168         setDelegatedKeyWakeUpResult(false);
169 
170         // Verify the policy wake up call succeeds because of the PowerManager wake up, since the
171         // delegate would not handle the wake up request.
172         assertThat(mPolicy.wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false)).isTrue();
173         verify(mInputWakeUpDelegate).wakeUpFromKey(300, KEYCODE_STEM_PRIMARY, false);
174         verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_KEY, "android.policy:KEY");
175     }
176 
177     @Test
testDelegatedKeyWakeIsSubjectToPolicyChecks()178     public void testDelegatedKeyWakeIsSubjectToPolicyChecks() {
179         mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
180         setDelegatedKeyWakeUpResult(true);
181         setTheaterModeEnabled(true);
182         setBooleanRes(config_allowTheaterModeWakeFromKey, false);
183         setBooleanRes(config_allowTheaterModeWakeFromPowerKey, false);
184         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
185         LocalServices.getService(WindowWakeUpPolicyInternal.class)
186                 .setInputWakeUpDelegate(mInputWakeUpDelegate);
187 
188         // Check that the wake up does not happen because the theater mode policy check fails.
189         assertThat(mPolicy.wakeUpFromKey(200, KEYCODE_POWER, true)).isFalse();
190         verify(mInputWakeUpDelegate, never()).wakeUpFromKey(anyLong(), anyInt(), anyBoolean());
191     }
192 
193     @Test
testDelegatedMotionWakeIsSubjectToPolicyChecks()194     public void testDelegatedMotionWakeIsSubjectToPolicyChecks() {
195         mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
196         setDelegatedMotionWakeUpResult(true);
197         setTheaterModeEnabled(true);
198         setBooleanRes(config_allowTheaterModeWakeFromMotion, false);
199         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
200         LocalServices.getService(WindowWakeUpPolicyInternal.class)
201                 .setInputWakeUpDelegate(mInputWakeUpDelegate);
202 
203         // Check that the wake up does not happen because the theater mode policy check fails.
204         assertThat(mPolicy.wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true)).isFalse();
205         verify(mInputWakeUpDelegate, never()).wakeUpFromMotion(anyLong(), anyInt(), anyBoolean());
206     }
207 
208     @Test
testTheaterModeChecksNotAppliedWhenScreenIsOn()209     public void testTheaterModeChecksNotAppliedWhenScreenIsOn() {
210         mSetFlagsRule.enableFlags(FLAG_SUPPORT_INPUT_WAKEUP_DELEGATE);
211         setDefaultDisplayState(Display.STATE_ON);
212         setTheaterModeEnabled(true);
213         setBooleanRes(config_allowTheaterModeWakeFromMotion, false);
214         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
215 
216         mPolicy.wakeUpFromMotion(200L, SOURCE_TOUCHSCREEN, true);
217 
218         verify(mPowerManager).wakeUp(200L, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
219     }
220 
221     @Test
testWakeUpFromMotion()222     public void testWakeUpFromMotion() {
223         runPowerManagerUpChecks(
224                 () -> mPolicy.wakeUpFromMotion(mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true),
225                 config_allowTheaterModeWakeFromMotion,
226                 WAKE_REASON_WAKE_MOTION,
227                 "android.policy:MOTION");
228     }
229 
230     @Test
testWakeUpFromKey_nonPowerKey()231     public void testWakeUpFromKey_nonPowerKey() {
232         runPowerManagerUpChecks(
233                 () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_HOME, true),
234                 config_allowTheaterModeWakeFromKey,
235                 WAKE_REASON_WAKE_KEY,
236                 "android.policy:KEY");
237     }
238 
239     @Test
testWakeUpFromKey_powerKey()240     public void testWakeUpFromKey_powerKey() {
241         // Disable the resource affecting all wake keys because it affects power key as well.
242         // That way, power key wake during theater mode will solely be controlled by
243         // `config_allowTheaterModeWakeFromPowerKey` in the checks.
244         setBooleanRes(config_allowTheaterModeWakeFromKey, false);
245 
246         // Test with power key
247         runPowerManagerUpChecks(
248                 () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, true),
249                 config_allowTheaterModeWakeFromPowerKey,
250                 WAKE_REASON_POWER_BUTTON,
251                 "android.policy:POWER");
252 
253         // Test that power key wake ups happen during theater mode as long as wake-keys are allowed
254         // even if the power-key specific theater mode config is disabled.
255         setBooleanRes(config_allowTheaterModeWakeFromPowerKey, false);
256         runPowerManagerUpChecks(
257                 () -> mPolicy.wakeUpFromKey(mClock.uptimeMillis(), KEYCODE_POWER, false),
258                 config_allowTheaterModeWakeFromKey,
259                 WAKE_REASON_POWER_BUTTON,
260                 "android.policy:POWER");
261     }
262 
263     @Test
testWakeUpFromLid()264     public void testWakeUpFromLid() {
265         runPowerManagerUpChecks(
266                 () -> mPolicy.wakeUpFromLid(),
267                 config_allowTheaterModeWakeFromLidSwitch,
268                 WAKE_REASON_LID,
269                 "android.policy:LID");
270     }
271 
272     @Test
testWakeUpFromWakeGesture()273     public void testWakeUpFromWakeGesture() {
274         runPowerManagerUpChecks(
275                 () -> mPolicy.wakeUpFromWakeGesture(),
276                 config_allowTheaterModeWakeFromGesture,
277                 WAKE_REASON_GESTURE,
278                 "android.policy:GESTURE");
279     }
280 
281     @Test
testwakeUpFromCameraCover()282     public void testwakeUpFromCameraCover() {
283         runPowerManagerUpChecks(
284                 () -> mPolicy.wakeUpFromCameraCover(mClock.uptimeMillis()),
285                 config_allowTheaterModeWakeFromCameraLens,
286                 WAKE_REASON_CAMERA_LAUNCH,
287                 "android.policy:CAMERA_COVER");
288     }
289 
290     @Test
testWakeUpFromPowerKeyCameraGesture()291     public void testWakeUpFromPowerKeyCameraGesture() {
292         // Disable the resource affecting all wake keys because it affects power key as well.
293         // That way, power key wake during theater mode will solely be controlled by
294         // `config_allowTheaterModeWakeFromPowerKey` in the checks.
295         setBooleanRes(config_allowTheaterModeWakeFromKey, false);
296 
297         runPowerManagerUpChecks(
298                 () -> mPolicy.wakeUpFromPowerKeyCameraGesture(),
299                 config_allowTheaterModeWakeFromPowerKey,
300                 WAKE_REASON_CAMERA_LAUNCH,
301                 "android.policy:CAMERA_GESTURE_PREVENT_LOCK");
302     }
303 
runPowerManagerUpChecks( BooleanSupplier wakeUpCall, int theatherModeWakeResId, int expectedWakeReason, String expectedWakeDetails)304     private void runPowerManagerUpChecks(
305             BooleanSupplier wakeUpCall,
306             int theatherModeWakeResId,
307             int expectedWakeReason,
308             String expectedWakeDetails) {
309         // Test under theater mode enabled.
310         setTheaterModeEnabled(true);
311 
312         Mockito.reset(mPowerManager);
313         setBooleanRes(theatherModeWakeResId, true);
314         LocalServices.removeServiceForTest(WindowWakeUpPolicyInternal.class);
315         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
316         setUptimeMillis(200);
317         assertWithMessage("Wake should happen in theater mode when config allows it.")
318                 .that(wakeUpCall.getAsBoolean()).isTrue();
319         verify(mPowerManager).wakeUp(200L, expectedWakeReason, expectedWakeDetails);
320 
321         Mockito.reset(mPowerManager);
322         setBooleanRes(theatherModeWakeResId, false);
323         LocalServices.removeServiceForTest(WindowWakeUpPolicyInternal.class);
324         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
325         setUptimeMillis(250);
326         assertWithMessage("Wake should not happen in theater mode when config disallows it.")
327                 .that(wakeUpCall.getAsBoolean()).isFalse();
328         verifyNoPowerManagerWakeUp();
329 
330         // Cases when theater mode is disabled.
331         setTheaterModeEnabled(false);
332 
333         Mockito.reset(mPowerManager);
334         setBooleanRes(theatherModeWakeResId, true);
335         LocalServices.removeServiceForTest(WindowWakeUpPolicyInternal.class);
336         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
337         setUptimeMillis(300);
338         assertWithMessage("Wake should happen when not in theater mode.")
339                 .that(wakeUpCall.getAsBoolean()).isTrue();
340         verify(mPowerManager).wakeUp(300L, expectedWakeReason, expectedWakeDetails);
341 
342         Mockito.reset(mPowerManager);
343         setBooleanRes(theatherModeWakeResId, false);
344         LocalServices.removeServiceForTest(WindowWakeUpPolicyInternal.class);
345         mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
346         setUptimeMillis(350);
347         assertWithMessage("Wake should happen when not in theater mode.")
348                 .that(wakeUpCall.getAsBoolean()).isTrue();
349         verify(mPowerManager).wakeUp(350L, expectedWakeReason, expectedWakeDetails);
350     }
351 
verifyNoPowerManagerWakeUp()352     private void verifyNoPowerManagerWakeUp() {
353         verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
354     }
355 
setBooleanRes(int resId, boolean val)356     private void setBooleanRes(int resId, boolean val) {
357         when(mResourcesSpy.getBoolean(resId)).thenReturn(val);
358     }
359 
setUptimeMillis(long uptimeMillis)360     private void setUptimeMillis(long uptimeMillis) {
361         when(mClock.uptimeMillis()).thenReturn(uptimeMillis);
362     }
363 
setTheaterModeEnabled(boolean enabled)364     private void setTheaterModeEnabled(boolean enabled) {
365         Settings.Global.putInt(
366                 mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, enabled ? 1 : 0);
367     }
368 
setDelegatedMotionWakeUpResult(boolean result)369     private void setDelegatedMotionWakeUpResult(boolean result) {
370         when(mInputWakeUpDelegate.wakeUpFromMotion(anyLong(), anyInt(), anyBoolean()))
371                 .thenReturn(result);
372     }
373 
setDelegatedKeyWakeUpResult(boolean result)374     private void setDelegatedKeyWakeUpResult(boolean result) {
375         when(mInputWakeUpDelegate.wakeUpFromKey(anyLong(), anyInt(), anyBoolean()))
376                 .thenReturn(result);
377     }
378 
setDefaultDisplayState(int displayState)379     private void setDefaultDisplayState(int displayState) {
380         when(mDefaultDisplay.getState()).thenReturn(displayState);
381     }
382 }
383