1 /*
2  * Copyright (C) 2020 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 android.Manifest.permission.ADD_TRUSTED_DISPLAY;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
23 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
24 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS;
25 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
26 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
27 import static android.view.Display.DEFAULT_DISPLAY;
28 import static android.view.Display.FLAG_OWN_FOCUS;
29 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
30 import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
31 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
32 import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
33 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
34 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
35 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
36 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
37 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
38 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
39 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
40 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST;
41 
42 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
43 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
44 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
45 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
46 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND;
47 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING;
48 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR;
49 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER;
50 
51 import static com.google.common.truth.Truth.assertThat;
52 
53 import static org.junit.Assert.assertEquals;
54 import static org.junit.Assert.assertFalse;
55 import static org.junit.Assert.assertNotNull;
56 import static org.junit.Assert.assertNull;
57 import static org.junit.Assert.assertThrows;
58 import static org.junit.Assert.assertTrue;
59 import static org.mockito.ArgumentMatchers.any;
60 import static org.mockito.ArgumentMatchers.anyBoolean;
61 import static org.mockito.ArgumentMatchers.anyInt;
62 import static org.mockito.ArgumentMatchers.anyString;
63 import static org.mockito.ArgumentMatchers.argThat;
64 import static org.mockito.ArgumentMatchers.eq;
65 import static org.mockito.Mockito.mock;
66 import static org.mockito.Mockito.times;
67 import static org.mockito.Mockito.verify;
68 import static org.mockito.Mockito.when;
69 
70 import android.content.pm.ActivityInfo;
71 import android.graphics.Point;
72 import android.graphics.Rect;
73 import android.hardware.display.VirtualDisplay;
74 import android.os.Binder;
75 import android.os.Bundle;
76 import android.os.IBinder;
77 import android.os.InputConfig;
78 import android.os.Process;
79 import android.os.RemoteException;
80 import android.os.UserHandle;
81 import android.platform.test.annotations.Presubmit;
82 import android.util.DisplayMetrics;
83 import android.util.MergedConfiguration;
84 import android.view.ContentRecordingSession;
85 import android.view.IWindow;
86 import android.view.IWindowSessionCallback;
87 import android.view.InputChannel;
88 import android.view.InsetsSourceControl;
89 import android.view.InsetsState;
90 import android.view.Surface;
91 import android.view.SurfaceControl;
92 import android.view.View;
93 import android.view.WindowInsets;
94 import android.view.WindowManager;
95 import android.view.WindowManager.LayoutParams;
96 import android.window.ClientWindowFrames;
97 import android.window.ScreenCapture;
98 import android.window.WindowContainerToken;
99 
100 import androidx.test.filters.SmallTest;
101 import androidx.test.platform.app.InstrumentationRegistry;
102 
103 import com.android.compatibility.common.util.AdoptShellPermissionsRule;
104 import com.android.internal.os.IResultReceiver;
105 import com.android.server.LocalServices;
106 
107 import org.junit.Rule;
108 import org.junit.Test;
109 import org.junit.runner.RunWith;
110 import org.mockito.ArgumentCaptor;
111 
112 /**
113  * Build/Install/Run:
114  * atest WmTests:WindowManagerServiceTests
115  */
116 @SmallTest
117 @Presubmit
118 @RunWith(WindowTestRunner.class)
119 public class WindowManagerServiceTests extends WindowTestsBase {
120 
121     @Rule
122     public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
123             InstrumentationRegistry.getInstrumentation().getUiAutomation(),
124             ADD_TRUSTED_DISPLAY);
125 
126     @Test
testIsRequestedOrientationMapped()127     public void testIsRequestedOrientationMapped() {
128         mWm.setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled*/ true,
129                 /* fromOrientations */ new int[]{1}, /* toOrientations */ new int[]{2});
130         assertThat(mWm.mapOrientationRequest(1)).isEqualTo(2);
131         assertThat(mWm.mapOrientationRequest(3)).isEqualTo(3);
132 
133         // Mapping disabled
134         mWm.setOrientationRequestPolicy(/* isIgnoreOrientationRequestDisabled*/ false,
135                 /* fromOrientations */ null, /* toOrientations */ null);
136         assertThat(mWm.mapOrientationRequest(1)).isEqualTo(1);
137         assertThat(mWm.mapOrientationRequest(3)).isEqualTo(3);
138     }
139 
140     @Test
testAddWindowToken()141     public void testAddWindowToken() {
142         IBinder token = mock(IBinder.class);
143         mWm.addWindowToken(token, TYPE_TOAST, mDisplayContent.getDisplayId(), null /* options */);
144 
145         WindowToken windowToken = mWm.mRoot.getWindowToken(token);
146         assertFalse(windowToken.mRoundedCornerOverlay);
147         assertFalse(windowToken.isFromClient());
148     }
149 
150     @Test
testTaskFocusChange_rootTaskNotHomeType_focusChanges()151     public void testTaskFocusChange_rootTaskNotHomeType_focusChanges() throws RemoteException {
152         DisplayContent display = createNewDisplay();
153         // Current focused window
154         Task focusedRootTask = createTask(
155                 display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
156         Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */);
157         WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
158         mDisplayContent.mCurrentFocus = focusedWindow;
159         // Tapped task
160         Task tappedRootTask = createTask(
161                 display, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
162         Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
163         spyOn(mWm.mAtmService);
164 
165         mWm.handleTaskFocusChange(tappedTask, null /* window */);
166 
167         verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
168     }
169 
170     @Test
testTaskFocusChange_rootTaskHomeTypeWithSameTaskDisplayArea_focusDoesNotChange()171     public void testTaskFocusChange_rootTaskHomeTypeWithSameTaskDisplayArea_focusDoesNotChange()
172             throws RemoteException {
173         DisplayContent display = createNewDisplay();
174         // Current focused window
175         Task focusedRootTask = createTask(
176                 display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
177         Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */);
178         WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
179         mDisplayContent.mCurrentFocus = focusedWindow;
180         // Tapped home task
181         Task tappedRootTask = createTask(
182                 display, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
183         Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
184         spyOn(mWm.mAtmService);
185 
186         mWm.handleTaskFocusChange(tappedTask, null /* window */);
187 
188         verify(mWm.mAtmService, never()).setFocusedTask(tappedTask.mTaskId, null);
189     }
190 
191     @Test
testTaskFocusChange_rootTaskHomeTypeWithDifferentTaskDisplayArea_focusChanges()192     public void testTaskFocusChange_rootTaskHomeTypeWithDifferentTaskDisplayArea_focusChanges()
193             throws RemoteException {
194         final DisplayContent display = createNewDisplay();
195         final TaskDisplayArea secondTda = createTaskDisplayArea(
196                 display, mWm, "Tapped TDA", FEATURE_VENDOR_FIRST);
197         // Current focused window
198         Task focusedRootTask = createTask(
199                 display, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
200         Task focusedTask = createTaskInRootTask(focusedRootTask, 0 /* userId */);
201         WindowState focusedWindow = createAppWindow(focusedTask, TYPE_APPLICATION, "App Window");
202         mDisplayContent.mCurrentFocus = focusedWindow;
203         // Tapped home task on another task display area
204         Task tappedRootTask = createTask(secondTda, WINDOWING_MODE_FULLSCREEN,
205                 ACTIVITY_TYPE_STANDARD);
206         Task tappedTask = createTaskInRootTask(tappedRootTask, 0 /* userId */);
207         spyOn(mWm.mAtmService);
208 
209         mWm.handleTaskFocusChange(tappedTask, null /* window */);
210 
211         verify(mWm.mAtmService).setFocusedTask(tappedTask.mTaskId, null);
212     }
213 
214     @Test
testDismissKeyguardCanWakeUp()215     public void testDismissKeyguardCanWakeUp() {
216         doReturn(true).when(mWm).checkCallingPermission(anyString(), anyString());
217         doReturn(true).when(mWm.mAtmService.mKeyguardController).isShowingDream();
218         doNothing().when(mWm.mAtmService.mTaskSupervisor).wakeUp(anyString());
219         mWm.dismissKeyguard(null, "test-dismiss-keyguard");
220         verify(mWm.mAtmService.mTaskSupervisor).wakeUp(anyString());
221     }
222 
223     @Test
testRelayoutExitingWindow()224     public void testRelayoutExitingWindow() {
225         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
226         final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
227         win.mWinAnimator.mSurfaceController = surfaceController;
228         win.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
229         doReturn(true).when(surfaceController).hasSurface();
230         spyOn(win.mTransitionController);
231         doReturn(true).when(win.mTransitionController).isShellTransitionsEnabled();
232         doReturn(true).when(win.mTransitionController).inTransition(
233                 eq(win.mActivityRecord));
234         win.mViewVisibility = View.VISIBLE;
235         win.mHasSurface = true;
236         win.mActivityRecord.mAppStopped = true;
237         mWm.mWindowMap.put(win.mClient.asBinder(), win);
238         spyOn(mWm.mWindowPlacerLocked);
239         // Skip unnecessary operations of relayout.
240         doNothing().when(mWm.mWindowPlacerLocked).performSurfacePlacement(anyBoolean());
241         final int w = 100;
242         final int h = 200;
243         final ClientWindowFrames outFrames = new ClientWindowFrames();
244         final MergedConfiguration outConfig = new MergedConfiguration();
245         final SurfaceControl outSurfaceControl = new SurfaceControl();
246         final InsetsState outInsetsState = new InsetsState();
247         final InsetsSourceControl.Array outControls = new InsetsSourceControl.Array();
248         final Bundle outBundle = new Bundle();
249         mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0,
250                 outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
251         // The window is in transition, so its destruction is deferred.
252         assertTrue(win.mAnimatingExit);
253         assertFalse(win.mDestroying);
254         assertTrue(win.mTransitionController.mAnimatingExitWindows.contains(win));
255 
256         win.mAnimatingExit = false;
257         win.mViewVisibility = View.VISIBLE;
258         win.mActivityRecord.setVisibleRequested(false);
259         win.mActivityRecord.setVisible(false);
260         mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.GONE, 0, 0, 0,
261                 outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
262         // Because the window is already invisible, it doesn't need to apply exiting animation
263         // and WMS#tryStartExitingAnimation() will destroy the surface directly.
264         assertFalse(win.mAnimatingExit);
265         assertFalse(win.mHasSurface);
266         assertNull(win.mWinAnimator.mSurfaceController);
267 
268         doReturn(mSystemServicesTestRule.mTransaction).when(SurfaceControl::getGlobalTransaction);
269         // Invisible requested activity should not get the last config even if its view is visible.
270         mWm.relayoutWindow(win.mSession, win.mClient, win.mAttrs, w, h, View.VISIBLE, 0, 0, 0,
271                 outFrames, outConfig, outSurfaceControl, outInsetsState, outControls, outBundle);
272         assertEquals(0, outConfig.getMergedConfiguration().densityDpi);
273         // Non activity window can still get the last config.
274         win.mActivityRecord = null;
275         win.fillClientWindowFramesAndConfiguration(outFrames, outConfig,
276                 false /* useLatestConfig */, true /* relayoutVisible */);
277         assertEquals(win.getConfiguration().densityDpi,
278                 outConfig.getMergedConfiguration().densityDpi);
279     }
280 
281     @Test
testRelayout_firstLayout_dwpcHelperCalledWithCorrectFlags()282     public void testRelayout_firstLayout_dwpcHelperCalledWithCorrectFlags() {
283         // When doing the first layout, the initial flags should be reported as changed to
284         // keepActivityOnWindowFlagsChanged.
285         testRelayoutFlagChanges(
286                 /*firstRelayout=*/ true,
287                 /*startFlags=*/ FLAG_SECURE,
288                 /*startPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
289                 /*newFlags=*/ FLAG_SECURE,
290                 /*newPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
291                 /*expectedChangedFlags=*/ FLAG_SECURE,
292                 /*expectedChangedPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
293                 /*expectedFlagsValue=*/ FLAG_SECURE,
294                 /*expectedPrivateFlagsValue=*/ PRIVATE_FLAG_TRUSTED_OVERLAY);
295     }
296 
297     @Test
testRelayout_secondLayoutFlagAdded_dwpcHelperCalledWithCorrectFlags()298     public void testRelayout_secondLayoutFlagAdded_dwpcHelperCalledWithCorrectFlags() {
299         testRelayoutFlagChanges(
300                 /*firstRelayout=*/ false,
301                 /*startFlags=*/ 0,
302                 /*startPrivateFlags=*/ 0,
303                 /*newFlags=*/ FLAG_SECURE,
304                 /*newPrivateFlags=*/ 0,
305                 /*expectedChangedFlags=*/ FLAG_SECURE,
306                 /*expectedChangedPrivateFlags=*/ 0,
307                 /*expectedFlagsValue=*/ FLAG_SECURE,
308                 /*expectedPrivateFlagsValue=*/ 0);
309     }
310 
311     @Test
testRelayout_secondLayoutMultipleFlagsAddOne_dwpcHelperCalledWithCorrectFlags()312     public void testRelayout_secondLayoutMultipleFlagsAddOne_dwpcHelperCalledWithCorrectFlags() {
313         testRelayoutFlagChanges(
314                 /*firstRelayout=*/ false,
315                 /*startFlags=*/ FLAG_NOT_FOCUSABLE,
316                 /*startPrivateFlags=*/ 0,
317                 /*newFlags=*/ FLAG_SECURE | FLAG_NOT_FOCUSABLE,
318                 /*newPrivateFlags=*/ 0,
319                 /*expectedChangedFlags=*/ FLAG_SECURE,
320                 /*expectedChangedPrivateFlags=*/ 0,
321                 /*expectedFlagsValue=*/ FLAG_SECURE | FLAG_NOT_FOCUSABLE,
322                 /*expectedPrivateFlagsValue=*/ 0);
323     }
324 
325     @Test
testRelayout_secondLayoutPrivateFlagAdded_dwpcHelperCalledWithCorrectFlags()326     public void testRelayout_secondLayoutPrivateFlagAdded_dwpcHelperCalledWithCorrectFlags() {
327         testRelayoutFlagChanges(
328                 /*firstRelayout=*/ false,
329                 /*startFlags=*/ 0,
330                 /*startPrivateFlags=*/ 0,
331                 /*newFlags=*/ 0,
332                 /*newPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
333                 /*expectedChangedFlags=*/ 0,
334                 /*expectedChangedPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
335                 /*expectedFlagsValue=*/ 0,
336                 /*expectedPrivateFlagsValue=*/ PRIVATE_FLAG_TRUSTED_OVERLAY);
337     }
338 
339     @Test
testRelayout_secondLayoutFlagsRemoved_dwpcHelperCalledWithCorrectFlags()340     public void testRelayout_secondLayoutFlagsRemoved_dwpcHelperCalledWithCorrectFlags() {
341         testRelayoutFlagChanges(
342                 /*firstRelayout=*/ false,
343                 /*startFlags=*/ FLAG_SECURE,
344                 /*startPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
345                 /*newFlags=*/ 0,
346                 /*newPrivateFlags=*/ 0,
347                 /*expectedChangedFlags=*/ FLAG_SECURE,
348                 /*expectedChangedPrivateFlags=*/ PRIVATE_FLAG_TRUSTED_OVERLAY,
349                 /*expectedFlagsValue=*/ 0,
350                 /*expectedPrivateFlagsValue=*/ 0);
351     }
352 
353     // Helper method to test relayout of a window, either for the initial layout, or a subsequent
354     // one, and makes sure that the flags and private flags changes and final values are properly
355     // reported to mDwpcHelper.keepActivityOnWindowFlagsChanged.
testRelayoutFlagChanges(boolean firstRelayout, int startFlags, int startPrivateFlags, int newFlags, int newPrivateFlags, int expectedChangedFlags, int expectedChangedPrivateFlags, int expectedFlagsValue, int expectedPrivateFlagsValue)356     private void testRelayoutFlagChanges(boolean firstRelayout, int startFlags,
357             int startPrivateFlags, int newFlags, int newPrivateFlags, int expectedChangedFlags,
358             int expectedChangedPrivateFlags, int expectedFlagsValue,
359             int expectedPrivateFlagsValue) {
360         final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
361         win.mRelayoutCalled = !firstRelayout;
362         mWm.mWindowMap.put(win.mClient.asBinder(), win);
363         spyOn(mDisplayContent.mDwpcHelper);
364         when(mDisplayContent.mDwpcHelper.hasController()).thenReturn(true);
365 
366         win.mAttrs.flags = startFlags;
367         win.mAttrs.privateFlags = startPrivateFlags;
368 
369         LayoutParams newParams = new LayoutParams();
370         newParams.copyFrom(win.mAttrs);
371         newParams.flags = newFlags;
372         newParams.privateFlags = newPrivateFlags;
373 
374         int seq = 1;
375         if (!firstRelayout) {
376             win.mRelayoutSeq = 1;
377             seq = 2;
378         }
379         mWm.relayoutWindow(win.mSession, win.mClient, newParams, 100, 200, View.VISIBLE, 0, seq,
380                 0, new ClientWindowFrames(), new MergedConfiguration(),
381                 new SurfaceControl(), new InsetsState(), new InsetsSourceControl.Array(),
382                 new Bundle());
383 
384         ArgumentCaptor<Integer> changedFlags = ArgumentCaptor.forClass(Integer.class);
385         ArgumentCaptor<Integer> changedPrivateFlags = ArgumentCaptor.forClass(Integer.class);
386         ArgumentCaptor<Integer> flagsValue = ArgumentCaptor.forClass(Integer.class);
387         ArgumentCaptor<Integer> privateFlagsValue = ArgumentCaptor.forClass(Integer.class);
388 
389         verify(mDisplayContent.mDwpcHelper).keepActivityOnWindowFlagsChanged(
390                 any(ActivityInfo.class), changedFlags.capture(), changedPrivateFlags.capture(),
391                 flagsValue.capture(), privateFlagsValue.capture());
392 
393         assertThat(changedFlags.getValue()).isEqualTo(expectedChangedFlags);
394         assertThat(changedPrivateFlags.getValue()).isEqualTo(expectedChangedPrivateFlags);
395         assertThat(flagsValue.getValue()).isEqualTo(expectedFlagsValue);
396         assertThat(privateFlagsValue.getValue()).isEqualTo(expectedPrivateFlagsValue);
397     }
398 
399     @Test
testMoveWindowTokenToDisplay_NullToken_DoNothing()400     public void testMoveWindowTokenToDisplay_NullToken_DoNothing() {
401         mWm.moveWindowTokenToDisplay(null, mDisplayContent.getDisplayId());
402 
403         verify(mDisplayContent, never()).reParentWindowToken(any());
404     }
405 
406     @Test
testMoveWindowTokenToDisplay_SameDisplay_DoNothing()407     public void testMoveWindowTokenToDisplay_SameDisplay_DoNothing() {
408         final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD_DIALOG,
409                 mDisplayContent);
410 
411         mWm.moveWindowTokenToDisplay(windowToken.token, mDisplayContent.getDisplayId());
412 
413         verify(mDisplayContent, never()).reParentWindowToken(any());
414     }
415 
416     @Test
testMoveWindowTokenToDisplay_DifferentDisplay_DoMoveDisplay()417     public void testMoveWindowTokenToDisplay_DifferentDisplay_DoMoveDisplay() {
418         final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD_DIALOG,
419                 mDisplayContent);
420 
421         mWm.moveWindowTokenToDisplay(windowToken.token, DEFAULT_DISPLAY);
422 
423         assertThat(windowToken.getDisplayContent()).isEqualTo(mDefaultDisplay);
424     }
425 
426     @Test
testAttachWindowContextToWindowToken_InvalidToken_EarlyReturn()427     public void testAttachWindowContextToWindowToken_InvalidToken_EarlyReturn() {
428         spyOn(mWm.mWindowContextListenerController);
429 
430         mWm.attachWindowContextToWindowToken(new Binder(), new Binder());
431 
432         verify(mWm.mWindowContextListenerController, never()).getWindowType(any());
433     }
434 
435     @Test(expected = IllegalArgumentException.class)
testAttachWindowContextToWindowToken_InvalidWindowType_ThrowException()436     public void testAttachWindowContextToWindowToken_InvalidWindowType_ThrowException() {
437         spyOn(mWm.mWindowContextListenerController);
438 
439         final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay);
440         doReturn(INVALID_WINDOW_TYPE).when(mWm.mWindowContextListenerController)
441                 .getWindowType(any());
442 
443         mWm.attachWindowContextToWindowToken(new Binder(), windowToken.token);
444     }
445 
446     @Test(expected = IllegalArgumentException.class)
testAttachWindowContextToWindowToken_DifferentWindowType_ThrowException()447     public void testAttachWindowContextToWindowToken_DifferentWindowType_ThrowException() {
448         spyOn(mWm.mWindowContextListenerController);
449 
450         final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay);
451         doReturn(TYPE_APPLICATION).when(mWm.mWindowContextListenerController)
452                 .getWindowType(any());
453 
454         mWm.attachWindowContextToWindowToken(new Binder(), windowToken.token);
455     }
456 
457     @Test
testAttachWindowContextToWindowToken_CallerNotValid_EarlyReturn()458     public void testAttachWindowContextToWindowToken_CallerNotValid_EarlyReturn() {
459         spyOn(mWm.mWindowContextListenerController);
460 
461         final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay);
462         doReturn(TYPE_INPUT_METHOD).when(mWm.mWindowContextListenerController)
463                 .getWindowType(any());
464         doReturn(false).when(mWm.mWindowContextListenerController)
465                 .assertCallerCanModifyListener(any(), anyBoolean(), anyInt());
466 
467         mWm.attachWindowContextToWindowToken(new Binder(), windowToken.token);
468 
469         verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(
470                 any(), any(), anyInt(), anyInt(), any());
471     }
472 
473     @Test
testAttachWindowContextToWindowToken_CallerValid_DoRegister()474     public void testAttachWindowContextToWindowToken_CallerValid_DoRegister() {
475         spyOn(mWm.mWindowContextListenerController);
476 
477         final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay);
478         doReturn(TYPE_INPUT_METHOD).when(mWm.mWindowContextListenerController)
479                 .getWindowType(any());
480         doReturn(true).when(mWm.mWindowContextListenerController)
481                 .assertCallerCanModifyListener(any(), anyBoolean(), anyInt());
482 
483         final IBinder clientToken = new Binder();
484         mWm.attachWindowContextToWindowToken(clientToken, windowToken.token);
485 
486         verify(mWm.mWindowContextListenerController).registerWindowContainerListener(
487                 eq(clientToken), eq(windowToken), anyInt(), eq(TYPE_INPUT_METHOD),
488                 eq(windowToken.mOptions));
489     }
490 
491     @Test
testAddWindowWithSubWindowTypeByWindowContext()492     public void testAddWindowWithSubWindowTypeByWindowContext() {
493         spyOn(mWm.mWindowContextListenerController);
494 
495         final WindowToken windowToken = createTestWindowToken(TYPE_INPUT_METHOD, mDefaultDisplay);
496         final Session session = new Session(mWm, new IWindowSessionCallback.Stub() {
497             @Override
498             public void onAnimatorScaleChanged(float v) throws RemoteException {
499             }
500         });
501         final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
502                 TYPE_APPLICATION_ATTACHED_DIALOG);
503         params.token = windowToken.token;
504         final IBinder windowContextToken = new Binder();
505         params.setWindowContextToken(windowContextToken);
506         doReturn(true).when(mWm.mWindowContextListenerController)
507                 .hasListener(eq(windowContextToken));
508         doReturn(TYPE_INPUT_METHOD).when(mWm.mWindowContextListenerController)
509                 .getWindowType(eq(windowContextToken));
510 
511         mWm.addWindow(session, new TestIWindow(), params, View.VISIBLE, DEFAULT_DISPLAY,
512                 UserHandle.USER_SYSTEM, WindowInsets.Type.defaultVisible(), null, new InsetsState(),
513                 new InsetsSourceControl.Array(), new Rect(), new float[1]);
514 
515         verify(mWm.mWindowContextListenerController, never()).registerWindowContainerListener(any(),
516                 any(), anyInt(), anyInt(), any());
517     }
518 
519     @Test
testSetInTouchMode_instrumentedProcessGetPermissionToSwitchTouchMode()520     public void testSetInTouchMode_instrumentedProcessGetPermissionToSwitchTouchMode() {
521         // Enable global touch mode
522         mWm.mPerDisplayFocusEnabled = true;
523 
524         // Get current touch mode state and setup WMS to run setInTouchMode
525         boolean currentTouchMode = mWm.isInTouchMode(DEFAULT_DISPLAY);
526         int callingPid = Binder.getCallingPid();
527         int callingUid = Binder.getCallingUid();
528         doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString(), anyBoolean());
529         when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
530                 android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(true);
531 
532         mWm.setInTouchMode(!currentTouchMode, DEFAULT_DISPLAY);
533 
534         verify(mWm.mInputManager).setInTouchMode(
535                 !currentTouchMode, callingPid, callingUid, /* hasPermission= */ true,
536                 DEFAULT_DISPLAY);
537     }
538 
539     @Test
testSetInTouchMode_nonInstrumentedProcessDontGetPermissionToSwitchTouchMode()540     public void testSetInTouchMode_nonInstrumentedProcessDontGetPermissionToSwitchTouchMode() {
541         // Enable global touch mode
542         mWm.mPerDisplayFocusEnabled = true;
543 
544         // Get current touch mode state and setup WMS to run setInTouchMode
545         boolean currentTouchMode = mWm.isInTouchMode(DEFAULT_DISPLAY);
546         int callingPid = Binder.getCallingPid();
547         int callingUid = Binder.getCallingUid();
548         doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString(), anyBoolean());
549         when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
550                 android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(false);
551 
552         mWm.setInTouchMode(!currentTouchMode, DEFAULT_DISPLAY);
553 
554         verify(mWm.mInputManager).setInTouchMode(
555                 !currentTouchMode, callingPid, callingUid, /* hasPermission= */ false,
556                 DEFAULT_DISPLAY);
557     }
558 
559     @Test
testSetInTouchMode_multiDisplay_globalTouchModeUpdate()560     public void testSetInTouchMode_multiDisplay_globalTouchModeUpdate() {
561         // Disable global touch mode
562         mWm.mPerDisplayFocusEnabled = false;
563 
564         // Create one extra display
565         final VirtualDisplay virtualDisplay = createVirtualDisplay(/* ownFocus= */ false);
566         final VirtualDisplay virtualDisplayOwnTouchMode =
567                 createVirtualDisplay(/* ownFocus= */ true);
568         final int numberOfDisplays = mWm.mRoot.mChildren.size();
569         assertThat(numberOfDisplays).isAtLeast(3);
570         final int numberOfGlobalTouchModeDisplays = (int) mWm.mRoot.mChildren.stream()
571                 .filter(d -> (d.getDisplay().getFlags() & FLAG_OWN_FOCUS) == 0)
572                 .count();
573         assertThat(numberOfGlobalTouchModeDisplays).isAtLeast(2);
574 
575         // Get current touch mode state and setup WMS to run setInTouchMode
576         boolean currentTouchMode = mWm.isInTouchMode(DEFAULT_DISPLAY);
577         int callingPid = Binder.getCallingPid();
578         int callingUid = Binder.getCallingUid();
579         doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString(), anyBoolean());
580         when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
581                 android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(true);
582 
583         mWm.setInTouchMode(!currentTouchMode, DEFAULT_DISPLAY);
584 
585         verify(mWm.mInputManager, times(numberOfGlobalTouchModeDisplays)).setInTouchMode(
586                 eq(!currentTouchMode), eq(callingPid), eq(callingUid),
587                 /* hasPermission= */ eq(true), /* displayId= */ anyInt());
588     }
589 
590     @Test
testSetInTouchMode_multiDisplay_perDisplayFocus_singleDisplayTouchModeUpdate()591     public void testSetInTouchMode_multiDisplay_perDisplayFocus_singleDisplayTouchModeUpdate() {
592         // Enable global touch mode
593         mWm.mPerDisplayFocusEnabled = true;
594 
595         // Create one extra display
596         final VirtualDisplay virtualDisplay = createVirtualDisplay(/* ownFocus= */ false);
597         final int numberOfDisplays = mWm.mRoot.mChildren.size();
598         assertThat(numberOfDisplays).isAtLeast(2);
599 
600         // Get current touch mode state and setup WMS to run setInTouchMode
601         boolean currentTouchMode = mWm.isInTouchMode(DEFAULT_DISPLAY);
602         int callingPid = Binder.getCallingPid();
603         int callingUid = Binder.getCallingUid();
604         doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString(), anyBoolean());
605         when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
606                 android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(true);
607 
608         mWm.setInTouchMode(!currentTouchMode, virtualDisplay.getDisplay().getDisplayId());
609 
610         // Ensure that new display touch mode state has changed.
611         verify(mWm.mInputManager).setInTouchMode(
612                 !currentTouchMode, callingPid, callingUid, /* hasPermission= */ true,
613                 virtualDisplay.getDisplay().getDisplayId());
614     }
615 
616     @Test
testSetInTouchMode_multiDisplay_ownTouchMode_singleDisplayTouchModeUpdate()617     public void testSetInTouchMode_multiDisplay_ownTouchMode_singleDisplayTouchModeUpdate() {
618         // Disable global touch mode
619         mWm.mPerDisplayFocusEnabled = false;
620 
621         // Create one extra display
622         final VirtualDisplay virtualDisplay = createVirtualDisplay(/* ownFocus= */ true);
623         final int numberOfDisplays = mWm.mRoot.mChildren.size();
624         assertThat(numberOfDisplays).isAtLeast(2);
625 
626         // Get current touch mode state and setup WMS to run setInTouchMode
627         boolean currentTouchMode = mWm.isInTouchMode(DEFAULT_DISPLAY);
628         int callingPid = Binder.getCallingPid();
629         int callingUid = Binder.getCallingUid();
630         doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString(), anyBoolean());
631         when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
632                 android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(true);
633 
634         mWm.setInTouchMode(!currentTouchMode, virtualDisplay.getDisplay().getDisplayId());
635 
636         // Ensure that new display touch mode state has changed.
637         verify(mWm.mInputManager).setInTouchMode(
638                 !currentTouchMode, callingPid, callingUid, /* hasPermission= */ true,
639                 virtualDisplay.getDisplay().getDisplayId());
640     }
641 
642     @Test
testSetInTouchModeOnAllDisplays_perDisplayFocusDisabled()643     public void testSetInTouchModeOnAllDisplays_perDisplayFocusDisabled() {
644         testSetInTouchModeOnAllDisplays(/* perDisplayFocusEnabled= */ false);
645     }
646 
647     @Test
testSetInTouchModeOnAllDisplays_perDisplayFocusEnabled()648     public void testSetInTouchModeOnAllDisplays_perDisplayFocusEnabled() {
649         testSetInTouchModeOnAllDisplays(/* perDisplayFocusEnabled= */ true);
650     }
651 
testSetInTouchModeOnAllDisplays(boolean perDisplayFocusEnabled)652     private void testSetInTouchModeOnAllDisplays(boolean perDisplayFocusEnabled) {
653         // Set global touch mode with the value passed as argument.
654         mWm.mPerDisplayFocusEnabled = perDisplayFocusEnabled;
655 
656         // Create a couple of extra displays.
657         // setInTouchModeOnAllDisplays should ignore the ownFocus setting.
658         final VirtualDisplay virtualDisplay = createVirtualDisplay(/* ownFocus= */ false);
659         final VirtualDisplay virtualDisplayOwnFocus = createVirtualDisplay(/* ownFocus= */ true);
660 
661         int callingPid = Binder.getCallingPid();
662         int callingUid = Binder.getCallingUid();
663         doReturn(false).when(mWm).checkCallingPermission(anyString(), anyString(), anyBoolean());
664         when(mWm.mAtmService.instrumentationSourceHasPermission(callingPid,
665                 android.Manifest.permission.MODIFY_TOUCH_MODE_STATE)).thenReturn(true);
666 
667         for (boolean inTouchMode : new boolean[]{true, false}) {
668             mWm.setInTouchModeOnAllDisplays(inTouchMode);
669             for (int i = 0; i < mWm.mRoot.mChildren.size(); ++i) {
670                 DisplayContent dc = mWm.mRoot.mChildren.get(i);
671                 // All displays that are not already in the desired touch mode are requested to
672                 // change their touch mode.
673                 if (dc.isInTouchMode() != inTouchMode) {
674                     verify(mWm.mInputManager).setInTouchMode(
675                             true, callingPid, callingUid, /* hasPermission= */ true,
676                             dc.getDisplay().getDisplayId());
677                 }
678             }
679         }
680     }
681 
createVirtualDisplay(boolean ownFocus)682     private VirtualDisplay createVirtualDisplay(boolean ownFocus) {
683         // Create virtual display
684         Point surfaceSize = new Point(
685                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().width(),
686                 mDefaultDisplay.getDefaultTaskDisplayArea().getBounds().height());
687         int flags = VIRTUAL_DISPLAY_FLAG_PUBLIC;
688         if (ownFocus) {
689             flags |= VIRTUAL_DISPLAY_FLAG_OWN_FOCUS | VIRTUAL_DISPLAY_FLAG_TRUSTED;
690         }
691         VirtualDisplay virtualDisplay = mWm.mDisplayManager.createVirtualDisplay("VirtualDisplay",
692                 surfaceSize.x, surfaceSize.y, DisplayMetrics.DENSITY_140, new Surface(), flags);
693         final int displayId = virtualDisplay.getDisplay().getDisplayId();
694         mWm.mRoot.onDisplayAdded(displayId);
695 
696         // Ensure that virtual display was properly created and stored in WRC
697         assertThat(mWm.mRoot.getDisplayContent(
698                 virtualDisplay.getDisplay().getDisplayId())).isNotNull();
699 
700         return virtualDisplay;
701     }
702 
703     @Test
testGetTaskWindowContainerTokenForLaunchCookie_nullCookie()704     public void testGetTaskWindowContainerTokenForLaunchCookie_nullCookie() {
705         WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(null);
706         assertThat(wct).isNull();
707     }
708 
709     @Test
testGetTaskWindowContainerTokenForLaunchCookie_invalidCookie()710     public void testGetTaskWindowContainerTokenForLaunchCookie_invalidCookie() {
711         Binder cookie = new Binder("test cookie");
712         WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
713         assertThat(wct).isNull();
714 
715         final ActivityRecord testActivity = new ActivityBuilder(mAtm)
716                 .setCreateTask(true)
717                 .build();
718 
719         wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
720         assertThat(wct).isNull();
721     }
722 
723     @Test
testGetTaskWindowContainerTokenForLaunchCookie_validCookie()724     public void testGetTaskWindowContainerTokenForLaunchCookie_validCookie() {
725         final Binder cookie = new Binder("ginger cookie");
726         final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
727         setupActivityWithLaunchCookie(cookie, launchRootTask);
728 
729         WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie);
730         assertThat(wct).isEqualTo(launchRootTask);
731     }
732 
733     @Test
testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies()734     public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies() {
735         final Binder cookie1 = new Binder("ginger cookie");
736         final WindowContainerToken launchRootTask1 = mock(WindowContainerToken.class);
737         setupActivityWithLaunchCookie(cookie1, launchRootTask1);
738 
739         setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
740                 mock(WindowContainerToken.class));
741 
742         setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
743                 mock(WindowContainerToken.class));
744 
745         WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(cookie1);
746         assertThat(wct).isEqualTo(launchRootTask1);
747     }
748 
749     @Test
testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies_noneValid()750     public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies_noneValid() {
751         setupActivityWithLaunchCookie(new Binder("ginger cookie"),
752                 mock(WindowContainerToken.class));
753 
754         setupActivityWithLaunchCookie(new Binder("choc chip cookie"),
755                 mock(WindowContainerToken.class));
756 
757         setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
758                 mock(WindowContainerToken.class));
759 
760         WindowContainerToken wct = mWm.getTaskWindowContainerTokenForLaunchCookie(
761                 new Binder("some other cookie"));
762         assertThat(wct).isNull();
763     }
764 
765     @Test
setContentRecordingSession_sessionNull_returnsTrue()766     public void setContentRecordingSession_sessionNull_returnsTrue() {
767         WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
768 
769         boolean result = wmInternal.setContentRecordingSession(/* incomingSession= */ null);
770 
771         assertThat(result).isTrue();
772     }
773 
774     @Test
setContentRecordingSession_sessionContentDisplay_returnsTrue()775     public void setContentRecordingSession_sessionContentDisplay_returnsTrue() {
776         WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
777         ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
778                 DEFAULT_DISPLAY);
779 
780         boolean result = wmInternal.setContentRecordingSession(session);
781 
782         assertThat(result).isTrue();
783     }
784 
785     @Test
setContentRecordingSession_sessionContentTask_noMatchingTask_returnsFalse()786     public void setContentRecordingSession_sessionContentTask_noMatchingTask_returnsFalse() {
787         WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
788         IBinder launchCookie = new Binder();
789         ContentRecordingSession session = ContentRecordingSession.createTaskSession(launchCookie);
790 
791         boolean result = wmInternal.setContentRecordingSession(session);
792 
793         assertThat(result).isFalse();
794     }
795 
796     @Test
setContentRecordingSession_sessionContentTask_matchingTask_returnsTrue()797     public void setContentRecordingSession_sessionContentTask_matchingTask_returnsTrue() {
798         WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
799         ActivityRecord activityRecord = createActivityRecord(createTask(mDefaultDisplay));
800         ContentRecordingSession session = ContentRecordingSession.createTaskSession(
801                 activityRecord.mLaunchCookie);
802 
803         boolean result = wmInternal.setContentRecordingSession(session);
804 
805         assertThat(result).isTrue();
806     }
807 
808     @Test
setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerToken()809     public void setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerToken() {
810         WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
811         Task task = createTask(mDefaultDisplay);
812         ActivityRecord activityRecord = createActivityRecord(task);
813         ContentRecordingSession session = ContentRecordingSession.createTaskSession(
814                 activityRecord.mLaunchCookie);
815 
816         wmInternal.setContentRecordingSession(session);
817 
818         assertThat(session.getTokenToRecord()).isEqualTo(
819                 task.mRemoteToken.toWindowContainerToken().asBinder());
820     }
821 
822     @Test
testisLetterboxBackgroundMultiColored()823     public void testisLetterboxBackgroundMultiColored() {
824         assertThat(setupLetterboxConfigurationWithBackgroundType(
825                 LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING)).isTrue();
826         assertThat(setupLetterboxConfigurationWithBackgroundType(
827                 LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND)).isTrue();
828         assertThat(setupLetterboxConfigurationWithBackgroundType(
829                 LETTERBOX_BACKGROUND_WALLPAPER)).isTrue();
830         assertThat(setupLetterboxConfigurationWithBackgroundType(
831                 LETTERBOX_BACKGROUND_SOLID_COLOR)).isFalse();
832     }
833 
834     @Test
testCaptureDisplay()835     public void testCaptureDisplay() {
836         Rect displayBounds = new Rect(0, 0, 100, 200);
837         spyOn(mDisplayContent);
838         when(mDisplayContent.getBounds()).thenReturn(displayBounds);
839 
840         // Null captureArgs
841         ScreenCapture.LayerCaptureArgs resultingArgs =
842                 mWm.getCaptureArgs(DEFAULT_DISPLAY, null /* captureArgs */);
843         assertEquals(displayBounds, resultingArgs.mSourceCrop);
844 
845         // Non null captureArgs, didn't set rect
846         ScreenCapture.CaptureArgs captureArgs = new ScreenCapture.CaptureArgs.Builder<>().build();
847         resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
848         assertEquals(displayBounds, resultingArgs.mSourceCrop);
849 
850         // Non null captureArgs, invalid rect
851         captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
852                 .setSourceCrop(new Rect(0, 0, -1, -1))
853                 .build();
854         resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
855         assertEquals(displayBounds, resultingArgs.mSourceCrop);
856 
857         // Non null captureArgs, null rect
858         captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
859                 .setSourceCrop(null)
860                 .build();
861         resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
862         assertEquals(displayBounds, resultingArgs.mSourceCrop);
863 
864         // Non null captureArgs, valid rect
865         Rect validRect = new Rect(0, 0, 10, 50);
866         captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
867                 .setSourceCrop(validRect)
868                 .build();
869         resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
870         assertEquals(validRect, resultingArgs.mSourceCrop);
871     }
872 
873     @Test
testGrantInputChannel_sanitizeSpyWindowForApplications()874     public void testGrantInputChannel_sanitizeSpyWindowForApplications() {
875         final Session session = mock(Session.class);
876         final int callingUid = Process.FIRST_APPLICATION_UID;
877         final int callingPid = 1234;
878         final SurfaceControl surfaceControl = mock(SurfaceControl.class);
879         final IWindow window = mock(IWindow.class);
880         final IBinder windowToken = mock(IBinder.class);
881         when(window.asBinder()).thenReturn(windowToken);
882         final IBinder focusGrantToken = mock(IBinder.class);
883 
884         final InputChannel inputChannel = new InputChannel();
885         assertThrows(IllegalArgumentException.class, () ->
886                 mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY,
887                         surfaceControl, window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE,
888                         PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY, TYPE_APPLICATION,
889                         null /* windowToken */, focusGrantToken, "TestInputChannel",
890                         inputChannel));
891     }
892 
893     @Test
testGrantInputChannel_allowSpyWindowForInputMonitorPermission()894     public void testGrantInputChannel_allowSpyWindowForInputMonitorPermission() {
895         final Session session = mock(Session.class);
896         final int callingUid = Process.SYSTEM_UID;
897         final int callingPid = 1234;
898         final SurfaceControl surfaceControl = mock(SurfaceControl.class);
899         final IWindow window = mock(IWindow.class);
900         final IBinder windowToken = mock(IBinder.class);
901         when(window.asBinder()).thenReturn(windowToken);
902         final IBinder focusGrantToken = mock(IBinder.class);
903 
904         final InputChannel inputChannel = new InputChannel();
905         mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl,
906                 window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY,
907                 INPUT_FEATURE_SPY, TYPE_APPLICATION, null /* windowToken */, focusGrantToken,
908                 "TestInputChannel", inputChannel);
909 
910         verify(mTransaction).setInputWindowInfo(
911                 eq(surfaceControl),
912                 argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY));
913     }
914 
915     @Test
testUpdateInputChannel_sanitizeSpyWindowForApplications()916     public void testUpdateInputChannel_sanitizeSpyWindowForApplications() {
917         final Session session = mock(Session.class);
918         final int callingUid = Process.FIRST_APPLICATION_UID;
919         final int callingPid = 1234;
920         final SurfaceControl surfaceControl = mock(SurfaceControl.class);
921         final IWindow window = mock(IWindow.class);
922         final IBinder windowToken = mock(IBinder.class);
923         when(window.asBinder()).thenReturn(windowToken);
924         final IBinder focusGrantToken = mock(IBinder.class);
925 
926         final InputChannel inputChannel = new InputChannel();
927         mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl,
928                 window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY,
929                 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, focusGrantToken,
930                 "TestInputChannel", inputChannel);
931         verify(mTransaction).setInputWindowInfo(
932                 eq(surfaceControl),
933                 argThat(h -> (h.inputConfig & InputConfig.SPY) == 0));
934 
935         assertThrows(IllegalArgumentException.class, () ->
936                 mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
937                         FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY,
938                         null /* region */));
939     }
940 
941     @Test
testUpdateInputChannel_allowSpyWindowForInputMonitorPermission()942     public void testUpdateInputChannel_allowSpyWindowForInputMonitorPermission() {
943         final Session session = mock(Session.class);
944         final int callingUid = Process.SYSTEM_UID;
945         final int callingPid = 1234;
946         final SurfaceControl surfaceControl = mock(SurfaceControl.class);
947         final IWindow window = mock(IWindow.class);
948         final IBinder windowToken = mock(IBinder.class);
949         when(window.asBinder()).thenReturn(windowToken);
950         final IBinder focusGrantToken = mock(IBinder.class);
951 
952         final InputChannel inputChannel = new InputChannel();
953         mWm.grantInputChannel(session, callingUid, callingPid, DEFAULT_DISPLAY, surfaceControl,
954                 window, null /* hostInputToken */, FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY,
955                 0 /* inputFeatures */, TYPE_APPLICATION, null /* windowToken */, focusGrantToken,
956                 "TestInputChannel", inputChannel);
957         verify(mTransaction).setInputWindowInfo(
958                 eq(surfaceControl),
959                 argThat(h -> (h.inputConfig & InputConfig.SPY) == 0));
960 
961         mWm.updateInputChannel(inputChannel.getToken(), DEFAULT_DISPLAY, surfaceControl,
962                 FLAG_NOT_FOCUSABLE, PRIVATE_FLAG_TRUSTED_OVERLAY, INPUT_FEATURE_SPY,
963                 null /* region */);
964         verify(mTransaction).setInputWindowInfo(
965                 eq(surfaceControl),
966                 argThat(h -> (h.inputConfig & InputConfig.SPY) == InputConfig.SPY));
967     }
968 
969     @Test
testRequestKeyboardShortcuts_noWindow()970     public void testRequestKeyboardShortcuts_noWindow() {
971         doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
972         doReturn(null).when(mWm).getFocusedWindowLocked();
973         doReturn(null).when(mWm.mRoot).getCurrentInputMethodWindow();
974 
975         TestResultReceiver receiver = new TestResultReceiver();
976         mWm.requestAppKeyboardShortcuts(receiver, 0);
977         assertNotNull(receiver.resultData);
978         assertTrue(receiver.resultData.isEmpty());
979 
980         receiver = new TestResultReceiver();
981         mWm.requestImeKeyboardShortcuts(receiver, 0);
982         assertNotNull(receiver.resultData);
983         assertTrue(receiver.resultData.isEmpty());
984     }
985 
986     @Test
testRequestKeyboardShortcuts()987     public void testRequestKeyboardShortcuts() throws RemoteException {
988         final IWindow window = mock(IWindow.class);
989         final IBinder binder = mock(IBinder.class);
990         doReturn(binder).when(window).asBinder();
991         final WindowState windowState =
992                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "appWin", window);
993         doNothing().when(mWm.mContext).enforceCallingOrSelfPermission(anyString(), anyString());
994         doReturn(windowState).when(mWm).getFocusedWindowLocked();
995         doReturn(windowState).when(mWm.mRoot).getCurrentInputMethodWindow();
996 
997         TestResultReceiver receiver = new TestResultReceiver();
998         mWm.requestAppKeyboardShortcuts(receiver, 0);
999         mWm.requestImeKeyboardShortcuts(receiver, 0);
1000         verify(window, times(2)).requestAppKeyboardShortcuts(receiver, 0);
1001     }
1002 
1003     class TestResultReceiver implements IResultReceiver {
1004         public android.os.Bundle resultData;
1005         private final IBinder mBinder = mock(IBinder.class);
1006 
1007         @Override
send(int resultCode, android.os.Bundle resultData)1008         public void send(int resultCode, android.os.Bundle resultData)
1009                 throws android.os.RemoteException {
1010             this.resultData = resultData;
1011         }
1012 
1013         @Override
asBinder()1014         public android.os.IBinder asBinder() {
1015             return mBinder;
1016         }
1017     }
1018 
setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct)1019     private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
1020         final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
1021         when(remoteToken.toWindowContainerToken()).thenReturn(wct);
1022         final ActivityRecord testActivity = new ActivityBuilder(mAtm)
1023                 .setCreateTask(true)
1024                 .build();
1025         testActivity.mLaunchCookie = launchCookie;
1026         testActivity.getTask().mRemoteToken = remoteToken;
1027     }
1028 
setupLetterboxConfigurationWithBackgroundType( @etterboxConfiguration.LetterboxBackgroundType int letterboxBackgroundType)1029     private boolean setupLetterboxConfigurationWithBackgroundType(
1030             @LetterboxConfiguration.LetterboxBackgroundType int letterboxBackgroundType) {
1031         mWm.mLetterboxConfiguration.setLetterboxBackgroundTypeOverride(letterboxBackgroundType);
1032         return mWm.isLetterboxBackgroundMultiColored();
1033     }
1034 }
1035