1 /* 2 * Copyright (C) 2018 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.systemui.shade; 18 19 import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 20 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 21 import static android.view.WindowManager.LayoutParams.FLAG_SECURE; 22 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.mockito.ArgumentMatchers.any; 27 import static org.mockito.ArgumentMatchers.anyInt; 28 import static org.mockito.ArgumentMatchers.eq; 29 import static org.mockito.Mockito.atLeastOnce; 30 import static org.mockito.Mockito.clearInvocations; 31 import static org.mockito.Mockito.never; 32 import static org.mockito.Mockito.reset; 33 import static org.mockito.Mockito.spy; 34 import static org.mockito.Mockito.verify; 35 import static org.mockito.Mockito.verifyNoMoreInteractions; 36 import static org.mockito.Mockito.when; 37 38 import android.app.IActivityManager; 39 import android.content.pm.ActivityInfo; 40 import android.content.res.Configuration; 41 import android.testing.AndroidTestingRunner; 42 import android.testing.TestableLooper.RunWithLooper; 43 import android.view.View; 44 import android.view.WindowManager; 45 46 import androidx.test.filters.SmallTest; 47 48 import com.android.internal.colorextraction.ColorExtractor; 49 import com.android.systemui.R; 50 import com.android.systemui.SysuiTestCase; 51 import com.android.systemui.biometrics.AuthController; 52 import com.android.systemui.colorextraction.SysuiColorExtractor; 53 import com.android.systemui.dump.DumpManager; 54 import com.android.systemui.keyguard.KeyguardViewMediator; 55 import com.android.systemui.plugins.statusbar.StatusBarStateController; 56 import com.android.systemui.statusbar.StatusBarState; 57 import com.android.systemui.statusbar.SysuiStatusBarStateController; 58 import com.android.systemui.statusbar.phone.DozeParameters; 59 import com.android.systemui.statusbar.phone.KeyguardBypassController; 60 import com.android.systemui.statusbar.phone.ScreenOffAnimationController; 61 import com.android.systemui.statusbar.phone.ScrimController; 62 import com.android.systemui.statusbar.policy.ConfigurationController; 63 import com.android.systemui.statusbar.policy.KeyguardStateController; 64 65 import com.google.common.util.concurrent.MoreExecutors; 66 67 import org.junit.Before; 68 import org.junit.Test; 69 import org.junit.runner.RunWith; 70 import org.mockito.ArgumentCaptor; 71 import org.mockito.Captor; 72 import org.mockito.Mock; 73 import org.mockito.MockitoAnnotations; 74 import org.mockito.Spy; 75 76 import java.util.List; 77 import java.util.concurrent.Executor; 78 79 @RunWith(AndroidTestingRunner.class) 80 @RunWithLooper 81 @SmallTest 82 public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { 83 84 @Mock private WindowManager mWindowManager; 85 @Mock private DozeParameters mDozeParameters; 86 @Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy( 87 new NotificationShadeWindowView(mContext, null)); 88 @Mock private IActivityManager mActivityManager; 89 @Mock private SysuiStatusBarStateController mStatusBarStateController; 90 @Mock private ConfigurationController mConfigurationController; 91 @Mock private KeyguardViewMediator mKeyguardViewMediator; 92 @Mock private KeyguardBypassController mKeyguardBypassController; 93 @Mock private SysuiColorExtractor mColorExtractor; 94 @Mock ColorExtractor.GradientColors mGradientColors; 95 @Mock private DumpManager mDumpManager; 96 @Mock private KeyguardStateController mKeyguardStateController; 97 @Mock private ScreenOffAnimationController mScreenOffAnimationController; 98 @Mock private AuthController mAuthController; 99 @Mock private ShadeExpansionStateManager mShadeExpansionStateManager; 100 @Mock private ShadeWindowLogger mShadeWindowLogger; 101 @Captor private ArgumentCaptor<WindowManager.LayoutParams> mLayoutParameters; 102 @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListener; 103 private final Executor mBackgroundExecutor = MoreExecutors.directExecutor(); 104 105 private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; 106 private float mPreferredRefreshRate = -1; 107 108 @Before setUp()109 public void setUp() { 110 MockitoAnnotations.initMocks(this); 111 // Preferred refresh rate is equal to the first displayMode's refresh rate 112 mPreferredRefreshRate = mContext.getDisplay().getSupportedModes()[0].getRefreshRate(); 113 overrideResource( 114 R.integer.config_keyguardRefreshRate, 115 (int) mPreferredRefreshRate 116 ); 117 118 when(mDozeParameters.getAlwaysOn()).thenReturn(true); 119 when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); 120 121 mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, 122 mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, 123 mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, 124 mBackgroundExecutor, mColorExtractor, mDumpManager, mKeyguardStateController, 125 mScreenOffAnimationController, mAuthController, mShadeExpansionStateManager, 126 mShadeWindowLogger) { 127 @Override 128 protected boolean isDebuggable() { 129 return false; 130 } 131 }; 132 mNotificationShadeWindowController.setScrimsVisibilityListener((visibility) -> {}); 133 mNotificationShadeWindowController.setWindowRootView(mNotificationShadeWindowView); 134 135 mNotificationShadeWindowController.attach(); 136 verify(mWindowManager).addView(eq(mNotificationShadeWindowView), any()); 137 verify(mStatusBarStateController).addCallback(mStateListener.capture(), anyInt()); 138 } 139 140 @Test testSetDozing_hidesSystemOverlays()141 public void testSetDozing_hidesSystemOverlays() { 142 mNotificationShadeWindowController.setDozing(true); 143 ArgumentCaptor<WindowManager.LayoutParams> captor = 144 ArgumentCaptor.forClass(WindowManager.LayoutParams.class); 145 verify(mWindowManager).updateViewLayout(any(), captor.capture()); 146 int flag = captor.getValue().privateFlags 147 & WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 148 assertThat(flag).isNotEqualTo(0); 149 150 reset(mWindowManager); 151 mNotificationShadeWindowController.setDozing(false); 152 verify(mWindowManager).updateViewLayout(any(), captor.capture()); 153 flag = captor.getValue().privateFlags 154 & WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS; 155 assertThat(flag).isEqualTo(0); 156 } 157 158 @Test testOnThemeChanged_doesntCrash()159 public void testOnThemeChanged_doesntCrash() { 160 mNotificationShadeWindowController.onThemeChanged(); 161 } 162 163 @Test testAdd_updatesVisibilityFlags()164 public void testAdd_updatesVisibilityFlags() { 165 verify(mNotificationShadeWindowView).setSystemUiVisibility(anyInt()); 166 } 167 168 @Test testSetForcePluginOpen_beforeStatusBarInitialization()169 public void testSetForcePluginOpen_beforeStatusBarInitialization() { 170 mNotificationShadeWindowController.setForcePluginOpen(true, this); 171 } 172 173 @Test attach_visibleWithWallpaper()174 public void attach_visibleWithWallpaper() { 175 clearInvocations(mWindowManager); 176 when(mKeyguardViewMediator.isShowingAndNotOccluded()).thenReturn(true); 177 mNotificationShadeWindowController.attach(); 178 179 verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE)); 180 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 181 assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) != 0).isTrue(); 182 } 183 184 @Test attach_lightScrimHidesWallpaper()185 public void attach_lightScrimHidesWallpaper() { 186 when(mKeyguardViewMediator.isShowingAndNotOccluded()).thenReturn(true); 187 mNotificationShadeWindowController.attach(); 188 189 clearInvocations(mWindowManager); 190 mNotificationShadeWindowController.setLightRevealScrimOpaque(true); 191 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 192 assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) == 0).isTrue(); 193 } 194 195 @Test attach_scrimDoesntHideWallpaper()196 public void attach_scrimDoesntHideWallpaper() { 197 when(mKeyguardViewMediator.isShowingAndNotOccluded()).thenReturn(true); 198 mNotificationShadeWindowController.attach(); 199 200 clearInvocations(mWindowManager); 201 mNotificationShadeWindowController.setScrimsVisibility(ScrimController.OPAQUE); 202 // The scrim used to remove the wallpaper flag, but this causes a relayout. 203 // Instead, we're not relying on SurfaceControl#setOpaque on 204 // NotificationShadeDepthController. 205 verify(mWindowManager, never()).updateViewLayout(any(), mLayoutParameters.capture()); 206 } 207 208 @Test setScrimsVisibility_earlyReturn()209 public void setScrimsVisibility_earlyReturn() { 210 clearInvocations(mWindowManager); 211 mNotificationShadeWindowController.setScrimsVisibility(ScrimController.TRANSPARENT); 212 // Abort early if value didn't change 213 verify(mWindowManager, never()).updateViewLayout(any(), mLayoutParameters.capture()); 214 } 215 216 @Test attach_animatingKeyguardAndSurface_wallpaperVisible()217 public void attach_animatingKeyguardAndSurface_wallpaperVisible() { 218 clearInvocations(mWindowManager); 219 when(mKeyguardViewMediator.isShowingAndNotOccluded()).thenReturn(true); 220 when(mKeyguardViewMediator 221 .isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) 222 .thenReturn(true); 223 mNotificationShadeWindowController.attach(); 224 225 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 226 assertThat((mLayoutParameters.getValue().flags & FLAG_SHOW_WALLPAPER) != 0).isTrue(); 227 } 228 229 @Test setBackgroundBlurRadius_expandedWithBlurs()230 public void setBackgroundBlurRadius_expandedWithBlurs() { 231 mNotificationShadeWindowController.setBackgroundBlurRadius(10); 232 verify(mNotificationShadeWindowView).setVisibility(eq(View.VISIBLE)); 233 234 mNotificationShadeWindowController.setBackgroundBlurRadius(0); 235 verify(mNotificationShadeWindowView).setVisibility(eq(View.INVISIBLE)); 236 } 237 238 @Test setBouncerShowing_isFocusable_whenNeedsInput()239 public void setBouncerShowing_isFocusable_whenNeedsInput() { 240 mNotificationShadeWindowController.setKeyguardNeedsInput(true); 241 clearInvocations(mWindowManager); 242 mNotificationShadeWindowController.setBouncerShowing(true); 243 244 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 245 assertThat((mLayoutParameters.getValue().flags & FLAG_NOT_FOCUSABLE) == 0).isTrue(); 246 assertThat((mLayoutParameters.getValue().flags & FLAG_ALT_FOCUSABLE_IM) == 0).isTrue(); 247 } 248 249 @Test setKeyguardShowing_focusable_notAltFocusable_whenNeedsInput()250 public void setKeyguardShowing_focusable_notAltFocusable_whenNeedsInput() { 251 mNotificationShadeWindowController.setKeyguardShowing(true); 252 clearInvocations(mWindowManager); 253 mNotificationShadeWindowController.setKeyguardNeedsInput(true); 254 255 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 256 assertThat((mLayoutParameters.getValue().flags & FLAG_NOT_FOCUSABLE) == 0).isTrue(); 257 assertThat((mLayoutParameters.getValue().flags & FLAG_ALT_FOCUSABLE_IM) == 0).isTrue(); 258 } 259 260 @Test setPanelExpanded_notFocusable_altFocusable_whenPanelIsOpen()261 public void setPanelExpanded_notFocusable_altFocusable_whenPanelIsOpen() { 262 mNotificationShadeWindowController.onShadeExpansionFullyChanged(true); 263 clearInvocations(mWindowManager); 264 mNotificationShadeWindowController.onShadeExpansionFullyChanged(true); 265 verifyNoMoreInteractions(mWindowManager); 266 mNotificationShadeWindowController.setNotificationShadeFocusable(true); 267 268 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 269 assertThat((mLayoutParameters.getValue().flags & FLAG_NOT_FOCUSABLE) == 0).isTrue(); 270 assertThat((mLayoutParameters.getValue().flags & FLAG_ALT_FOCUSABLE_IM) != 0).isTrue(); 271 } 272 273 @Test setKeyguardShowing_notFocusable_byDefault()274 public void setKeyguardShowing_notFocusable_byDefault() { 275 mNotificationShadeWindowController.setKeyguardShowing(false); 276 277 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 278 assertThat((mLayoutParameters.getValue().flags & FLAG_NOT_FOCUSABLE) != 0).isTrue(); 279 assertThat((mLayoutParameters.getValue().flags & FLAG_ALT_FOCUSABLE_IM) == 0).isTrue(); 280 } 281 282 @Test setKeyguardShowing_enablesSecureFlag()283 public void setKeyguardShowing_enablesSecureFlag() { 284 mNotificationShadeWindowController.setBouncerShowing(true); 285 286 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 287 assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) != 0).isTrue(); 288 } 289 290 @Test setKeyguardNotShowing_disablesSecureFlag()291 public void setKeyguardNotShowing_disablesSecureFlag() { 292 mNotificationShadeWindowController.setBouncerShowing(false); 293 294 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 295 assertThat((mLayoutParameters.getValue().flags & FLAG_SECURE) == 0).isTrue(); 296 } 297 298 @Test rotationBecameAllowed_layoutParamsUpdated()299 public void rotationBecameAllowed_layoutParamsUpdated() { 300 mNotificationShadeWindowController.setKeyguardShowing(true); 301 when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false); 302 mNotificationShadeWindowController.onConfigChanged(new Configuration()); 303 clearInvocations(mWindowManager); 304 305 when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true); 306 mNotificationShadeWindowController.onConfigChanged(new Configuration()); 307 308 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 309 assertThat(mLayoutParameters.getValue().screenOrientation) 310 .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_USER); 311 } 312 313 @Test rotationBecameNotAllowed_layoutParamsUpdated()314 public void rotationBecameNotAllowed_layoutParamsUpdated() { 315 mNotificationShadeWindowController.setKeyguardShowing(true); 316 when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(true); 317 mNotificationShadeWindowController.onConfigChanged(new Configuration()); 318 clearInvocations(mWindowManager); 319 320 when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false); 321 mNotificationShadeWindowController.onConfigChanged(new Configuration()); 322 323 verify(mWindowManager).updateViewLayout(any(), mLayoutParameters.capture()); 324 assertThat(mLayoutParameters.getValue().screenOrientation) 325 .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); 326 } 327 328 @Test batchApplyWindowLayoutParams_doesNotDispatchEvents()329 public void batchApplyWindowLayoutParams_doesNotDispatchEvents() { 330 mNotificationShadeWindowController.setForceDozeBrightness(true); 331 verify(mWindowManager).updateViewLayout(any(), any()); 332 mNotificationShadeWindowController.setForceDozeBrightness(true); 333 verifyNoMoreInteractions(mWindowManager); 334 335 clearInvocations(mWindowManager); 336 mNotificationShadeWindowController.batchApplyWindowLayoutParams(() -> { 337 mNotificationShadeWindowController.setForceDozeBrightness(false); 338 verify(mWindowManager, never()).updateViewLayout(any(), any()); 339 }); 340 verify(mWindowManager).updateViewLayout(any(), any()); 341 } 342 343 @Test bouncerShowing_OrientationNoSensor()344 public void bouncerShowing_OrientationNoSensor() { 345 mNotificationShadeWindowController.setKeyguardShowing(true); 346 mNotificationShadeWindowController.setKeyguardOccluded(true); 347 mNotificationShadeWindowController.setBouncerShowing(true); 348 when(mKeyguardStateController.isKeyguardScreenRotationAllowed()).thenReturn(false); 349 mNotificationShadeWindowController.onConfigChanged(new Configuration()); 350 351 verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture()); 352 assertThat(mLayoutParameters.getValue().screenOrientation) 353 .isEqualTo(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR); 354 } 355 356 @Test udfpsEnrolled_minAndMaxRefreshRateSetToPreferredRefreshRate()357 public void udfpsEnrolled_minAndMaxRefreshRateSetToPreferredRefreshRate() { 358 // GIVEN udfps is enrolled 359 when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true); 360 361 // WHEN keyguard is showing 362 setKeyguardShowing(); 363 364 // THEN min and max refresh rate is set to the preferredRefreshRate 365 verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture()); 366 final List<WindowManager.LayoutParams> lpList = mLayoutParameters.getAllValues(); 367 final WindowManager.LayoutParams lp = lpList.get(lpList.size() - 1); 368 assertThat(lp.preferredMaxDisplayRefreshRate).isEqualTo(mPreferredRefreshRate); 369 assertThat(lp.preferredMinDisplayRefreshRate).isEqualTo(mPreferredRefreshRate); 370 } 371 372 @Test udfpsNotEnrolled_refreshRateUnset()373 public void udfpsNotEnrolled_refreshRateUnset() { 374 // GIVEN udfps is NOT enrolled 375 when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false); 376 377 // WHEN keyguard is showing 378 setKeyguardShowing(); 379 380 // THEN min and max refresh rate aren't set (set to 0) 381 verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture()); 382 final List<WindowManager.LayoutParams> lpList = mLayoutParameters.getAllValues(); 383 final WindowManager.LayoutParams lp = lpList.get(lpList.size() - 1); 384 assertThat(lp.preferredMaxDisplayRefreshRate).isEqualTo(0); 385 assertThat(lp.preferredMinDisplayRefreshRate).isEqualTo(0); 386 } 387 388 @Test keyguardNotShowing_refreshRateUnset()389 public void keyguardNotShowing_refreshRateUnset() { 390 // GIVEN UDFPS is enrolled 391 when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(true); 392 393 // WHEN keyguard is NOT showing 394 mNotificationShadeWindowController.setKeyguardShowing(false); 395 396 // THEN min and max refresh rate aren't set (set to 0) 397 verify(mWindowManager, atLeastOnce()).updateViewLayout(any(), mLayoutParameters.capture()); 398 final List<WindowManager.LayoutParams> lpList = mLayoutParameters.getAllValues(); 399 final WindowManager.LayoutParams lp = lpList.get(lpList.size() - 1); 400 assertThat(lp.preferredMaxDisplayRefreshRate).isEqualTo(0); 401 assertThat(lp.preferredMinDisplayRefreshRate).isEqualTo(0); 402 } 403 setKeyguardShowing()404 private void setKeyguardShowing() { 405 mNotificationShadeWindowController.setKeyguardShowing(true); 406 mNotificationShadeWindowController.setKeyguardGoingAway(false); 407 mNotificationShadeWindowController.setKeyguardFadingAway(false); 408 mStateListener.getValue().onStateChanged(StatusBarState.KEYGUARD); 409 } 410 } 411