1 /* 2 * Copyright (C) 2021 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.dreams; 18 19 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.Mockito.clearInvocations; 25 import static org.mockito.Mockito.doAnswer; 26 import static org.mockito.Mockito.doThrow; 27 import static org.mockito.Mockito.inOrder; 28 import static org.mockito.Mockito.never; 29 import static org.mockito.Mockito.verify; 30 import static org.mockito.Mockito.when; 31 32 import android.content.ComponentName; 33 import android.content.Intent; 34 import android.os.IBinder; 35 import android.os.RemoteException; 36 import android.service.dreams.IDreamOverlay; 37 import android.service.dreams.IDreamOverlayCallback; 38 import android.service.dreams.IDreamOverlayClient; 39 import android.service.dreams.IDreamOverlayClientCallback; 40 import android.testing.AndroidTestingRunner; 41 import android.view.View; 42 import android.view.ViewGroup; 43 import android.view.WindowManager; 44 import android.view.WindowManagerImpl; 45 46 import androidx.lifecycle.Lifecycle; 47 import androidx.lifecycle.LifecycleRegistry; 48 import androidx.test.filters.SmallTest; 49 50 import com.android.internal.logging.UiEventLogger; 51 import com.android.keyguard.KeyguardUpdateMonitor; 52 import com.android.systemui.SysuiTestCase; 53 import com.android.systemui.complication.ComplicationLayoutEngine; 54 import com.android.systemui.dreams.complication.HideComplicationTouchHandler; 55 import com.android.systemui.dreams.complication.dagger.ComplicationComponent; 56 import com.android.systemui.dreams.dagger.DreamOverlayComponent; 57 import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; 58 import com.android.systemui.touch.TouchInsetManager; 59 import com.android.systemui.util.concurrency.FakeExecutor; 60 import com.android.systemui.util.time.FakeSystemClock; 61 import com.android.systemui.utils.leaks.LeakCheckedTest; 62 63 import org.junit.Before; 64 import org.junit.Rule; 65 import org.junit.Test; 66 import org.junit.runner.RunWith; 67 import org.mockito.ArgumentCaptor; 68 import org.mockito.Captor; 69 import org.mockito.InOrder; 70 import org.mockito.Mock; 71 import org.mockito.Mockito; 72 import org.mockito.MockitoAnnotations; 73 74 @SmallTest 75 @RunWith(AndroidTestingRunner.class) 76 public class DreamOverlayServiceTest extends SysuiTestCase { 77 private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package", 78 "lowlight"); 79 private static final String DREAM_COMPONENT = "package/dream"; 80 private static final String WINDOW_NAME = "test"; 81 private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); 82 private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); 83 84 @Mock 85 DreamOverlayLifecycleOwner mLifecycleOwner; 86 87 @Mock 88 LifecycleRegistry mLifecycleRegistry; 89 90 @Rule 91 public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck(); 92 93 WindowManager.LayoutParams mWindowParams = new WindowManager.LayoutParams(); 94 95 @Mock 96 IDreamOverlayCallback mDreamOverlayCallback; 97 98 @Mock 99 WindowManagerImpl mWindowManager; 100 101 @Mock 102 com.android.systemui.complication.dagger.ComplicationComponent.Factory 103 mComplicationComponentFactory; 104 105 @Mock 106 com.android.systemui.complication.dagger.ComplicationComponent mComplicationComponent; 107 108 @Mock 109 ComplicationLayoutEngine mComplicationVisibilityController; 110 111 @Mock 112 ComplicationComponent.Factory mDreamComplicationComponentFactory; 113 114 @Mock 115 ComplicationComponent mDreamComplicationComponent; 116 117 @Mock 118 HideComplicationTouchHandler mHideComplicationTouchHandler; 119 120 @Mock 121 DreamOverlayComponent.Factory mDreamOverlayComponentFactory; 122 123 @Mock 124 DreamOverlayComponent mDreamOverlayComponent; 125 126 @Mock 127 DreamOverlayContainerView mDreamOverlayContainerView; 128 129 @Mock 130 DreamOverlayContainerViewController mDreamOverlayContainerViewController; 131 132 @Mock 133 KeyguardUpdateMonitor mKeyguardUpdateMonitor; 134 135 @Mock 136 DreamOverlayTouchMonitor mDreamOverlayTouchMonitor; 137 138 @Mock 139 DreamOverlayStateController mStateController; 140 141 @Mock 142 ViewGroup mDreamOverlayContainerViewParent; 143 144 @Mock 145 TouchInsetManager mTouchInsetManager; 146 147 @Mock 148 UiEventLogger mUiEventLogger; 149 150 @Mock 151 DreamOverlayCallbackController mDreamOverlayCallbackController; 152 153 @Captor 154 ArgumentCaptor<View> mViewCaptor; 155 156 DreamOverlayService mService; 157 158 @Before setup()159 public void setup() { 160 MockitoAnnotations.initMocks(this); 161 162 when(mDreamOverlayComponent.getDreamOverlayContainerViewController()) 163 .thenReturn(mDreamOverlayContainerViewController); 164 when(mLifecycleOwner.getRegistry()) 165 .thenReturn(mLifecycleRegistry); 166 when(mDreamOverlayComponent.getDreamOverlayTouchMonitor()) 167 .thenReturn(mDreamOverlayTouchMonitor); 168 when(mComplicationComponentFactory 169 .create(any(), any(), any(), any())) 170 .thenReturn(mComplicationComponent); 171 when(mComplicationComponent.getVisibilityController()) 172 .thenReturn(mComplicationVisibilityController); 173 when(mDreamComplicationComponent.getHideComplicationTouchHandler()) 174 .thenReturn(mHideComplicationTouchHandler); 175 when(mDreamComplicationComponentFactory 176 .create(any(), any())) 177 .thenReturn(mDreamComplicationComponent); 178 when(mDreamOverlayComponentFactory 179 .create(any(), any(), any(), any())) 180 .thenReturn(mDreamOverlayComponent); 181 when(mDreamOverlayContainerViewController.getContainerView()) 182 .thenReturn(mDreamOverlayContainerView); 183 184 mService = new DreamOverlayService( 185 mContext, 186 mLifecycleOwner, 187 mMainExecutor, 188 mWindowManager, 189 mComplicationComponentFactory, 190 mDreamComplicationComponentFactory, 191 mDreamOverlayComponentFactory, 192 mStateController, 193 mKeyguardUpdateMonitor, 194 mUiEventLogger, 195 mTouchInsetManager, 196 LOW_LIGHT_COMPONENT, 197 mDreamOverlayCallbackController, 198 WINDOW_NAME); 199 } 200 getClient()201 public IDreamOverlayClient getClient() throws RemoteException { 202 final IBinder proxy = mService.onBind(new Intent()); 203 final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); 204 final IDreamOverlayClientCallback callback = 205 Mockito.mock(IDreamOverlayClientCallback.class); 206 overlay.getClient(callback); 207 final ArgumentCaptor<IDreamOverlayClient> clientCaptor = 208 ArgumentCaptor.forClass(IDreamOverlayClient.class); 209 verify(callback).onDreamOverlayClient(clientCaptor.capture()); 210 211 return clientCaptor.getValue(); 212 } 213 214 @Test testOnStartMetricsLogged()215 public void testOnStartMetricsLogged() throws Exception { 216 final IDreamOverlayClient client = getClient(); 217 218 // Inform the overlay service of dream starting. 219 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 220 false /*shouldShowComplication*/); 221 mMainExecutor.runAllReady(); 222 223 verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); 224 verify(mUiEventLogger).log( 225 DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START); 226 } 227 228 @Test testOverlayContainerViewAddedToWindow()229 public void testOverlayContainerViewAddedToWindow() throws Exception { 230 final IDreamOverlayClient client = getClient(); 231 232 // Inform the overlay service of dream starting. 233 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 234 false /*shouldShowComplication*/); 235 mMainExecutor.runAllReady(); 236 237 verify(mWindowManager).addView(any(), any()); 238 } 239 240 // Validates that {@link DreamOverlayService} properly handles the case where the dream's 241 // window is no longer valid by the time start is called. 242 @Test testInvalidWindowAddStart()243 public void testInvalidWindowAddStart() throws Exception { 244 final IDreamOverlayClient client = getClient(); 245 246 doThrow(new WindowManager.BadTokenException()).when(mWindowManager).addView(any(), any()); 247 // Inform the overlay service of dream starting. 248 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 249 false /*shouldShowComplication*/); 250 mMainExecutor.runAllReady(); 251 252 verify(mWindowManager).addView(any(), any()); 253 254 verify(mStateController).setOverlayActive(false); 255 verify(mStateController).setLowLightActive(false); 256 verify(mStateController).setEntryAnimationsFinished(false); 257 258 verify(mStateController, never()).setOverlayActive(true); 259 verify(mUiEventLogger, never()).log( 260 DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START); 261 262 verify(mDreamOverlayCallbackController, never()).onStartDream(); 263 } 264 265 @Test testDreamOverlayContainerViewControllerInitialized()266 public void testDreamOverlayContainerViewControllerInitialized() throws Exception { 267 final IDreamOverlayClient client = getClient(); 268 269 // Inform the overlay service of dream starting. 270 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 271 false /*shouldShowComplication*/); 272 mMainExecutor.runAllReady(); 273 274 verify(mDreamOverlayContainerViewController).init(); 275 } 276 277 @Test testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized()278 public void testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized() 279 throws Exception { 280 when(mDreamOverlayContainerView.getParent()) 281 .thenReturn(mDreamOverlayContainerViewParent) 282 .thenReturn(null); 283 284 final IDreamOverlayClient client = getClient(); 285 286 // Inform the overlay service of dream starting. 287 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 288 false /*shouldShowComplication*/); 289 mMainExecutor.runAllReady(); 290 291 verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView); 292 } 293 294 @Test testShouldShowComplicationsSetByStartDream()295 public void testShouldShowComplicationsSetByStartDream() throws RemoteException { 296 final IDreamOverlayClient client = getClient(); 297 298 // Inform the overlay service of dream starting. 299 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 300 true /*shouldShowComplication*/); 301 mMainExecutor.runAllReady(); 302 303 assertThat(mService.shouldShowComplications()).isTrue(); 304 } 305 306 @Test testLowLightSetByStartDream()307 public void testLowLightSetByStartDream() throws RemoteException { 308 final IDreamOverlayClient client = getClient(); 309 310 // Inform the overlay service of dream starting. 311 client.startDream(mWindowParams, mDreamOverlayCallback, 312 LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); 313 mMainExecutor.runAllReady(); 314 315 assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT); 316 verify(mStateController).setLowLightActive(true); 317 } 318 319 @Test testOnEndDream()320 public void testOnEndDream() throws RemoteException { 321 final IDreamOverlayClient client = getClient(); 322 323 // Inform the overlay service of dream starting. 324 client.startDream(mWindowParams, mDreamOverlayCallback, 325 LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); 326 mMainExecutor.runAllReady(); 327 328 // Verify view added. 329 verify(mWindowManager).addView(mViewCaptor.capture(), any()); 330 331 // Service destroyed. 332 mService.onEndDream(); 333 mMainExecutor.runAllReady(); 334 335 // Verify view removed. 336 verify(mWindowManager).removeView(mViewCaptor.getValue()); 337 338 // Verify state correctly set. 339 verify(mStateController).setOverlayActive(false); 340 verify(mStateController).setLowLightActive(false); 341 verify(mStateController).setEntryAnimationsFinished(false); 342 } 343 344 @Test testImmediateEndDream()345 public void testImmediateEndDream() throws Exception { 346 final IDreamOverlayClient client = getClient(); 347 348 // Start the dream, but don't execute any Runnables put on the executor yet. We delay 349 // executing Runnables as the timing isn't guaranteed and we want to verify that the overlay 350 // starts and finishes in the proper order even if Runnables are delayed. 351 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 352 false /*shouldShowComplication*/); 353 // Immediately end the dream. 354 client.endDream(); 355 // Run any scheduled Runnables. 356 mMainExecutor.runAllReady(); 357 358 // The overlay starts then finishes. 359 InOrder inOrder = inOrder(mWindowManager); 360 inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any()); 361 inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue()); 362 } 363 364 @Test testEndDreamDuringStartDream()365 public void testEndDreamDuringStartDream() throws Exception { 366 final IDreamOverlayClient client = getClient(); 367 368 // Schedule the endDream call in the middle of the startDream implementation, as any 369 // ordering is possible. 370 doAnswer(invocation -> { 371 client.endDream(); 372 return null; 373 }).when(mStateController).setOverlayActive(true); 374 375 // Start the dream. 376 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 377 false /*shouldShowComplication*/); 378 mMainExecutor.runAllReady(); 379 380 // The overlay starts then finishes. 381 InOrder inOrder = inOrder(mWindowManager); 382 inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any()); 383 inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue()); 384 } 385 386 @Test testDestroy()387 public void testDestroy() throws RemoteException { 388 final IDreamOverlayClient client = getClient(); 389 390 // Inform the overlay service of dream starting. 391 client.startDream(mWindowParams, mDreamOverlayCallback, 392 LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); 393 mMainExecutor.runAllReady(); 394 395 // Verify view added. 396 verify(mWindowManager).addView(mViewCaptor.capture(), any()); 397 398 // Service destroyed. 399 mService.onDestroy(); 400 mMainExecutor.runAllReady(); 401 402 // Verify view removed. 403 verify(mWindowManager).removeView(mViewCaptor.getValue()); 404 405 // Verify state correctly set. 406 verify(mKeyguardUpdateMonitor).removeCallback(any()); 407 verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); 408 verify(mStateController).setOverlayActive(false); 409 verify(mStateController).setLowLightActive(false); 410 verify(mStateController).setEntryAnimationsFinished(false); 411 } 412 413 @Test testDoNotRemoveViewOnDestroyIfOverlayNotStarted()414 public void testDoNotRemoveViewOnDestroyIfOverlayNotStarted() { 415 // Service destroyed without ever starting dream. 416 mService.onDestroy(); 417 mMainExecutor.runAllReady(); 418 419 // Verify no view is removed. 420 verify(mWindowManager, never()).removeView(any()); 421 422 // Verify state still correctly set. 423 verify(mKeyguardUpdateMonitor).removeCallback(any()); 424 verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); 425 verify(mStateController).setOverlayActive(false); 426 verify(mStateController).setLowLightActive(false); 427 } 428 429 @Test testDecorViewNotAddedToWindowAfterDestroy()430 public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception { 431 final IDreamOverlayClient client = getClient(); 432 433 // Destroy the service. 434 mService.onDestroy(); 435 mMainExecutor.runAllReady(); 436 437 // Inform the overlay service of dream starting. 438 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 439 false /*shouldShowComplication*/); 440 mMainExecutor.runAllReady(); 441 442 verify(mWindowManager, never()).addView(any(), any()); 443 } 444 445 @Test testNeverRemoveDecorViewIfNotAdded()446 public void testNeverRemoveDecorViewIfNotAdded() { 447 // Service destroyed before dream started. 448 mService.onDestroy(); 449 mMainExecutor.runAllReady(); 450 451 verify(mWindowManager, never()).removeView(any()); 452 } 453 454 @Test testResetCurrentOverlayWhenConnectedToNewDream()455 public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException { 456 final IDreamOverlayClient client = getClient(); 457 458 // Inform the overlay service of dream starting. Do not show dream complications. 459 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 460 false /*shouldShowComplication*/); 461 mMainExecutor.runAllReady(); 462 463 // Verify that a new window is added. 464 verify(mWindowManager).addView(mViewCaptor.capture(), any()); 465 final View windowDecorView = mViewCaptor.getValue(); 466 467 // Assert that the overlay is not showing complications. 468 assertThat(mService.shouldShowComplications()).isFalse(); 469 470 clearInvocations(mDreamOverlayComponent); 471 clearInvocations(mWindowManager); 472 473 // New dream starting with dream complications showing. Note that when a new dream is 474 // binding to the dream overlay service, it receives the same instance of IBinder as the 475 // first one. 476 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 477 true /*shouldShowComplication*/); 478 mMainExecutor.runAllReady(); 479 480 // Assert that the overlay is showing complications. 481 assertThat(mService.shouldShowComplications()).isTrue(); 482 483 // Verify that the old overlay window has been removed, and a new one created. 484 verify(mWindowManager).removeView(windowDecorView); 485 verify(mWindowManager).addView(any(), any()); 486 487 // Verify that new instances of overlay container view controller and overlay touch monitor 488 // are created. 489 verify(mDreamOverlayComponent).getDreamOverlayContainerViewController(); 490 verify(mDreamOverlayComponent).getDreamOverlayTouchMonitor(); 491 } 492 493 @Test testWakeUp()494 public void testWakeUp() throws RemoteException { 495 final IDreamOverlayClient client = getClient(); 496 497 // Inform the overlay service of dream starting. 498 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 499 true /*shouldShowComplication*/); 500 mMainExecutor.runAllReady(); 501 502 mService.onWakeUp(); 503 verify(mDreamOverlayContainerViewController).wakeUp(); 504 verify(mDreamOverlayCallbackController).onWakeUp(); 505 } 506 507 @Test testWakeUpBeforeStartDoesNothing()508 public void testWakeUpBeforeStartDoesNothing() { 509 mService.onWakeUp(); 510 verify(mDreamOverlayContainerViewController, never()).wakeUp(); 511 } 512 513 @Test testSystemFlagShowForAllUsersSetOnWindow()514 public void testSystemFlagShowForAllUsersSetOnWindow() throws RemoteException { 515 final IDreamOverlayClient client = getClient(); 516 517 // Inform the overlay service of dream starting. Do not show dream complications. 518 client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, 519 false /*shouldShowComplication*/); 520 mMainExecutor.runAllReady(); 521 522 final ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor = 523 ArgumentCaptor.forClass(WindowManager.LayoutParams.class); 524 525 // Verify that a new window is added. 526 verify(mWindowManager).addView(any(), paramsCaptor.capture()); 527 528 assertThat((paramsCaptor.getValue().privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) 529 == SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isTrue(); 530 } 531 } 532