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.server.wm;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
22 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
23 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
24 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
25 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
26 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
27 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
28 import static android.view.WindowManager.TRANSIT_CLOSE;
29 import static android.view.WindowManager.TRANSIT_OPEN;
30 
31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
35 
36 import static com.google.common.truth.Truth.assertThat;
37 
38 import static org.junit.Assert.assertEquals;
39 import static org.junit.Assert.assertFalse;
40 import static org.junit.Assert.assertNotEquals;
41 import static org.junit.Assert.assertTrue;
42 import static org.mockito.ArgumentMatchers.anyBoolean;
43 import static org.mockito.ArgumentMatchers.anyFloat;
44 import static org.mockito.ArgumentMatchers.anyInt;
45 import static org.mockito.ArgumentMatchers.eq;
46 import static org.mockito.Mockito.verify;
47 
48 import android.content.pm.ActivityInfo;
49 import android.content.res.Configuration;
50 import android.content.res.Resources;
51 import android.graphics.Rect;
52 import android.os.IBinder;
53 import android.os.RemoteException;
54 import android.platform.test.annotations.Presubmit;
55 import android.view.DisplayCutout;
56 import android.view.DisplayInfo;
57 import android.view.DisplayShape;
58 import android.view.Gravity;
59 import android.view.InsetsState;
60 import android.view.PrivacyIndicatorBounds;
61 import android.view.RoundedCorners;
62 import android.view.Surface;
63 import android.view.SurfaceControl;
64 import android.view.WindowManager;
65 
66 import androidx.test.filters.SmallTest;
67 
68 import org.junit.Before;
69 import org.junit.Test;
70 import org.junit.runner.RunWith;
71 
72 import java.util.List;
73 
74 /**
75  * Tests for the {@link WallpaperController} class.
76  *
77  * Build/Install/Run:
78  *  atest WmTests:WallpaperControllerTests
79  */
80 @SmallTest
81 @Presubmit
82 @RunWith(WindowTestRunner.class)
83 public class WallpaperControllerTests extends WindowTestsBase {
84     private static final int INITIAL_WIDTH = 600;
85     private static final int INITIAL_HEIGHT = 900;
86     private static final int SECOND_WIDTH = 300;
87 
88     @Before
setup()89     public void setup() {
90         Resources resources = mWm.mContext.getResources();
91         spyOn(resources);
92         doReturn(false).when(resources).getBoolean(
93                 com.android.internal.R.bool.config_offsetWallpaperToCenterOfLargestDisplay);
94     }
95 
96     @Test
testWallpaperScreenshot()97     public void testWallpaperScreenshot() {
98         WindowSurfaceController windowSurfaceController = mock(WindowSurfaceController.class);
99 
100         // No wallpaper
101         final DisplayContent dc = createNewDisplay();
102         assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
103 
104         // No wallpaper WSA Surface
105         final WindowState wallpaperWindow = createWallpaperWindow(dc);
106         assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
107 
108         // Wallpaper with not visible WSA surface.
109         wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController;
110         wallpaperWindow.mWinAnimator.mLastAlpha = 1;
111         assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
112 
113         when(windowSurfaceController.getShown()).thenReturn(true);
114 
115         // Wallpaper with WSA alpha set to 0.
116         wallpaperWindow.mWinAnimator.mLastAlpha = 0;
117         assertFalse(dc.mWallpaperController.canScreenshotWallpaper());
118 
119         // Wallpaper window with WSA Surface
120         wallpaperWindow.mWinAnimator.mLastAlpha = 1;
121         assertTrue(dc.mWallpaperController.canScreenshotWallpaper());
122     }
123 
124     @Test
testWallpaperSizeWithFixedTransform()125     public void testWallpaperSizeWithFixedTransform() {
126         // No wallpaper
127         final DisplayContent dc = mDisplayContent;
128         if (dc.mBaseDisplayHeight == dc.mBaseDisplayWidth) {
129             // Make sure the size is different when changing orientation.
130             resizeDisplay(dc, 500, 1000);
131         }
132 
133         // No wallpaper WSA Surface
134         final WindowState wallpaperWindow = createWallpaperWindow(dc);
135 
136         WindowManager.LayoutParams attrs = wallpaperWindow.getAttrs();
137         Rect bounds = dc.getBounds();
138         int displayWidth = dc.getBounds().width();
139         int displayHeight = dc.getBounds().height();
140 
141         // Use a wallpaper with a different ratio than the display
142         int wallpaperWidth = bounds.width() * 2;
143         int wallpaperHeight = (int) (bounds.height() * 1.10);
144 
145         // Simulate what would be done on the client's side
146         final float layoutScale = Math.max(
147                 displayWidth / (float) wallpaperWidth, displayHeight / (float) wallpaperHeight);
148         attrs.width = (int) (wallpaperWidth * layoutScale + .5f);
149         attrs.height = (int) (wallpaperHeight * layoutScale + .5f);
150         attrs.flags |= FLAG_LAYOUT_NO_LIMITS | FLAG_SCALED;
151         attrs.gravity = Gravity.TOP | Gravity.LEFT;
152         wallpaperWindow.getWindowFrames().mParentFrame.set(dc.getBounds());
153 
154         dc.getDisplayPolicy().layoutWindowLw(wallpaperWindow, null, dc.mDisplayFrames);
155 
156         assertEquals(Configuration.ORIENTATION_PORTRAIT, dc.getConfiguration().orientation);
157         int expectedWidth = (int) (wallpaperWidth * layoutScale + .5f);
158 
159         // Check that the wallpaper is correctly scaled
160         assertEquals(expectedWidth, wallpaperWindow.getFrame().width());
161         assertEquals(displayHeight, wallpaperWindow.getFrame().height());
162         Rect portraitFrame = wallpaperWindow.getFrame();
163 
164         // Rotate the display
165         dc.getDisplayRotation().updateOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, true);
166         dc.sendNewConfiguration();
167 
168         // Apply the fixed transform
169         Configuration config = new Configuration();
170         final DisplayInfo info = dc.computeScreenConfiguration(config, Surface.ROTATION_0);
171         final DisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0);
172         final DisplayFrames displayFrames = new DisplayFrames(new InsetsState(),
173                 info, cutout, RoundedCorners.NO_ROUNDED_CORNERS, new PrivacyIndicatorBounds(),
174                 DisplayShape.NONE);
175         wallpaperWindow.mToken.applyFixedRotationTransform(info, displayFrames, config);
176 
177         // Check that the wallpaper has the same frame in landscape than in portrait
178         assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getConfiguration().orientation);
179         assertEquals(portraitFrame, wallpaperWindow.getFrame());
180     }
181 
182     @Test
testWallpaperZoom()183     public void testWallpaperZoom() throws RemoteException {
184         final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
185         final WindowState wallpaperWindow = createWallpaperWindow(dc);
186         wallpaperWindow.getAttrs().privateFlags |=
187                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
188 
189         final WindowState homeWindow = createWallpaperTargetWindow(dc);
190 
191         spyOn(dc.mWallpaperController);
192         doReturn(true).when(dc.mWallpaperController).isWallpaperVisible();
193 
194         dc.mWallpaperController.adjustWallpaperWindows();
195 
196         spyOn(wallpaperWindow.mClient);
197 
198         float zoom = .5f;
199         dc.mWallpaperController.setWallpaperZoomOut(homeWindow, zoom);
200         assertEquals(zoom, wallpaperWindow.mWallpaperZoomOut, .01f);
201         verify(wallpaperWindow.mClient).dispatchWallpaperOffsets(anyFloat(), anyFloat(), anyFloat(),
202                 anyFloat(), eq(zoom), anyBoolean());
203     }
204 
205     @Test
testWallpaperZoom_shouldNotScaleWallpaper()206     public void testWallpaperZoom_shouldNotScaleWallpaper() throws RemoteException {
207         final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
208         final WindowState wallpaperWindow = createWallpaperWindow(dc);
209         wallpaperWindow.getAttrs().privateFlags |=
210                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
211 
212         final WindowState homeWindow = createWallpaperTargetWindow(dc);
213 
214         spyOn(dc.mWallpaperController);
215         doReturn(true).when(dc.mWallpaperController).isWallpaperVisible();
216 
217         dc.mWallpaperController.adjustWallpaperWindows();
218 
219         spyOn(wallpaperWindow.mClient);
220 
221         float newZoom = .5f;
222         wallpaperWindow.mShouldScaleWallpaper = false;
223         // Set zoom, and make sure the window animator scale didn't actually change, but the zoom
224         // value did, and we do dispatch the zoom to the wallpaper service
225         dc.mWallpaperController.setWallpaperZoomOut(homeWindow, newZoom);
226         assertEquals(newZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
227         assertEquals(1f, wallpaperWindow.mWallpaperScale, .01f);
228         verify(wallpaperWindow.mClient).dispatchWallpaperOffsets(anyFloat(), anyFloat(), anyFloat(),
229                 anyFloat(), eq(newZoom), anyBoolean());
230     }
231 
232     @Test
testWallpaperZoom_multipleCallers()233     public void testWallpaperZoom_multipleCallers() {
234         final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
235         final WindowState wallpaperWindow = createWallpaperWindow(dc);
236         wallpaperWindow.getAttrs().privateFlags |=
237                 WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS;
238 
239 
240         spyOn(dc.mWallpaperController);
241         doReturn(true).when(dc.mWallpaperController).isWallpaperVisible();
242 
243         final WindowState homeWindow = createWallpaperTargetWindow(dc);
244 
245         WindowState otherWindow = createWindow(null /* parent */, TYPE_APPLICATION, dc,
246                 "otherWindow");
247 
248         dc.mWallpaperController.adjustWallpaperWindows();
249 
250         spyOn(wallpaperWindow.mClient);
251 
252         // Set zoom from 2 windows
253         float homeWindowInitialZoom = .5f;
254         float otherWindowInitialZoom = .7f;
255         dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowInitialZoom);
256         dc.mWallpaperController.setWallpaperZoomOut(otherWindow, otherWindowInitialZoom);
257         // Make sure the largest one wins
258         assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
259 
260         // Change zoom to a larger zoom from homeWindow
261         float homeWindowZoom2 = .8f;
262         dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowZoom2);
263         // New zoom should be current
264         assertEquals(homeWindowZoom2, wallpaperWindow.mWallpaperZoomOut, .01f);
265 
266         // Set homeWindow zoom to a lower zoom, but keep the one from otherWindow
267         dc.mWallpaperController.setWallpaperZoomOut(homeWindow, homeWindowInitialZoom);
268 
269         // Zoom from otherWindow should be the current.
270         assertEquals(otherWindowInitialZoom, wallpaperWindow.mWallpaperZoomOut, .01f);
271     }
272 
273     @Test
testUpdateWallpaperTarget()274     public void testUpdateWallpaperTarget() {
275         final DisplayContent dc = mDisplayContent;
276         final WindowState homeWin = createWallpaperTargetWindow(dc);
277         final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, "app");
278         final RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
279         doReturn(true).when(recentsController).isWallpaperVisible(eq(appWin));
280         mWm.setRecentsAnimationController(recentsController);
281 
282         dc.mWallpaperController.adjustWallpaperWindows();
283         assertEquals(appWin, dc.mWallpaperController.getWallpaperTarget());
284         // The wallpaper target is gone, so it should adjust to the next target.
285         appWin.removeImmediately();
286         assertEquals(homeWin, dc.mWallpaperController.getWallpaperTarget());
287     }
288 
289     @Test
testShowWhenLockedWallpaperTarget()290     public void testShowWhenLockedWallpaperTarget() {
291         final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
292         wallpaperWindow.mToken.asWallpaperToken().setShowWhenLocked(true);
293         final WindowState behind = createWindow(null, TYPE_BASE_APPLICATION, "behind");
294         final WindowState topTranslucent = createWindow(null, TYPE_BASE_APPLICATION,
295                 "topTranslucent");
296         behind.mAttrs.width = behind.mAttrs.height = topTranslucent.mAttrs.width =
297                 topTranslucent.mAttrs.height = WindowManager.LayoutParams.MATCH_PARENT;
298         topTranslucent.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
299         doReturn(true).when(behind.mActivityRecord).fillsParent();
300         doReturn(false).when(topTranslucent.mActivityRecord).fillsParent();
301 
302         spyOn(mWm.mPolicy);
303         doReturn(true).when(mWm.mPolicy).isKeyguardLocked();
304         doReturn(true).when(mWm.mPolicy).isKeyguardOccluded();
305         mDisplayContent.mWallpaperController.adjustWallpaperWindows();
306         // Wallpaper is visible because the show-when-locked activity is translucent.
307         assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(wallpaperWindow));
308 
309         behind.mActivityRecord.setShowWhenLocked(true);
310         mDisplayContent.mWallpaperController.adjustWallpaperWindows();
311         // Wallpaper is invisible because the lowest show-when-locked activity is opaque.
312         assertTrue(mDisplayContent.mWallpaperController.isWallpaperTarget(null));
313     }
314 
315     /**
316      * Tests that the windowing mode of the wallpaper window must always be fullscreen.
317      */
318     @Test
testWallpaperTokenWindowingMode()319     public void testWallpaperTokenWindowingMode() {
320         final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
321         final WallpaperWindowToken token = new WallpaperWindowToken(mWm, mock(IBinder.class),
322                 true, dc, true /* ownerCanManageAppTokens */);
323 
324         // The wallpaper should have requested override fullscreen windowing mode, so the
325         // configuration (windowing mode) propagation from display won't change it.
326         dc.setWindowingMode(WINDOWING_MODE_FREEFORM);
327         assertEquals(WINDOWING_MODE_FULLSCREEN, token.getWindowingMode());
328         dc.setWindowingMode(WINDOWING_MODE_UNDEFINED);
329         assertEquals(WINDOWING_MODE_FULLSCREEN, token.getWindowingMode());
330     }
331 
332     @Test
testFixedRotationRecentsAnimatingTask()333     public void testFixedRotationRecentsAnimatingTask() {
334         final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
335         final WallpaperWindowToken wallpaperToken = wallpaperWindow.mToken.asWallpaperToken();
336         final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, "app");
337         makeWindowVisible(appWin);
338         final ActivityRecord r = appWin.mActivityRecord;
339         final RecentsAnimationController recentsController = mock(RecentsAnimationController.class);
340         doReturn(true).when(recentsController).isWallpaperVisible(eq(appWin));
341         mWm.setRecentsAnimationController(recentsController);
342 
343         r.applyFixedRotationTransform(mDisplayContent.getDisplayInfo(),
344                 mDisplayContent.mDisplayFrames, mDisplayContent.getConfiguration());
345         // Invisible requested activity should not share its rotation transform.
346         r.setVisibleRequested(false);
347         mDisplayContent.mWallpaperController.adjustWallpaperWindows();
348         assertFalse(wallpaperToken.hasFixedRotationTransform());
349 
350         // Wallpaper should link the transform of its target.
351         r.setVisibleRequested(true);
352         mDisplayContent.mWallpaperController.adjustWallpaperWindows();
353         assertEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget());
354         assertTrue(r.hasFixedRotationTransform());
355         assertTrue(wallpaperToken.hasFixedRotationTransform());
356 
357         // The case with shell transition.
358         registerTestTransitionPlayer();
359         final Transition t = r.mTransitionController.createTransition(TRANSIT_OPEN);
360         final ActivityRecord recents = mock(ActivityRecord.class);
361         t.collect(r.getTask());
362         r.mTransitionController.setTransientLaunch(recents, r.getTask());
363         // The activity in restore-below task should not be the target if keyguard is not locked.
364         mDisplayContent.mWallpaperController.adjustWallpaperWindows();
365         assertNotEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget());
366         // The activity in restore-below task should be the target if keyguard is occluded.
367         doReturn(true).when(mDisplayContent).isKeyguardLocked();
368         mDisplayContent.mWallpaperController.adjustWallpaperWindows();
369         assertEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget());
370     }
371 
372     @Test
testWallpaperReportConfigChange()373     public void testWallpaperReportConfigChange() {
374         final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent);
375         createWallpaperTargetWindow(mDisplayContent);
376         final WallpaperWindowToken wallpaperToken = wallpaperWindow.mToken.asWallpaperToken();
377         makeWindowVisible(wallpaperWindow);
378         wallpaperWindow.mLayoutSeq = mDisplayContent.mLayoutSeq;
379         // Assume the token was invisible and the latest config was reported.
380         wallpaperToken.commitVisibility(false);
381         makeLastConfigReportedToClient(wallpaperWindow, false /* visible */);
382         assertTrue(wallpaperWindow.isLastConfigReportedToClient());
383 
384         final Rect bounds = wallpaperToken.getBounds();
385         wallpaperToken.setBounds(new Rect(0, 0, bounds.width() / 2, bounds.height() / 2));
386         assertFalse(wallpaperWindow.isLastConfigReportedToClient());
387         // If there is a pending config change when changing to visible, it should tell the client
388         // to redraw by WindowState#reportResized.
389         wallpaperToken.commitVisibility(true);
390         waitUntilHandlersIdle();
391         assertTrue(wallpaperWindow.isLastConfigReportedToClient());
392     }
393 
394     @Test
testWallpaperTokenVisibility()395     public void testWallpaperTokenVisibility() {
396         final DisplayContent dc = mWm.mRoot.getDefaultDisplay();
397         final WindowState wallpaperWindow = createWallpaperWindow(dc);
398         final WallpaperWindowToken token = wallpaperWindow.mToken.asWallpaperToken();
399         wallpaperWindow.setHasSurface(true);
400 
401         // Set-up mock shell transitions
402         registerTestTransitionPlayer();
403 
404         Transition transit =
405                 mWm.mAtmService.getTransitionController().createTransition(TRANSIT_OPEN);
406 
407         // wallpaper windows are immediately visible when set to visible even during a transition
408         token.setVisibility(true);
409         assertTrue(wallpaperWindow.isVisible());
410         assertTrue(token.isVisibleRequested());
411         assertTrue(token.isVisible());
412         transit.abort();
413 
414         // In a transition, setting invisible should ONLY set requestedVisible false; otherwise
415         // wallpaper should remain "visible" until transition is over.
416         transit = mWm.mAtmService.getTransitionController().createTransition(TRANSIT_CLOSE);
417         transit.start();
418         token.setVisibility(false);
419         assertTrue(wallpaperWindow.isVisible());
420         assertFalse(token.isVisibleRequested());
421         assertTrue(token.isVisible());
422 
423         final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
424         token.finishSync(t, token.getSyncGroup(), false /* cancel */);
425         transit.onTransactionReady(transit.getSyncId(), t);
426         dc.mTransitionController.finishTransition(transit);
427         assertFalse(wallpaperWindow.isVisible());
428         assertFalse(token.isVisible());
429     }
430 
prepareSmallerSecondDisplay(DisplayContent dc, int width, int height)431     private static void prepareSmallerSecondDisplay(DisplayContent dc, int width, int height) {
432         spyOn(dc.mWmService);
433         DisplayInfo firstDisplay = dc.getDisplayInfo();
434         DisplayInfo secondDisplay = new DisplayInfo(firstDisplay);
435         // Second display is narrower than first display.
436         secondDisplay.logicalWidth = width;
437         secondDisplay.logicalHeight = height;
438         doReturn(List.of(firstDisplay, secondDisplay)).when(
439                 dc.mWmService).getPossibleDisplayInfoLocked(anyInt());
440     }
441 
resizeDisplayAndWallpaper(DisplayContent dc, WindowState wallpaperWindow, int width, int height)442     private static void resizeDisplayAndWallpaper(DisplayContent dc, WindowState wallpaperWindow,
443             int width, int height) {
444         dc.setBounds(0, 0, width, height);
445         dc.updateOrientation();
446         dc.sendNewConfiguration();
447         spyOn(wallpaperWindow);
448         doReturn(new Rect(0, 0, width, height)).when(wallpaperWindow).getParentFrame();
449     }
450 
451     @Test
testUpdateWallpaperOffset_initial_shouldCenterDisabled()452     public void testUpdateWallpaperOffset_initial_shouldCenterDisabled() {
453         final DisplayContent dc = new TestDisplayContent.Builder(mAtm, INITIAL_WIDTH,
454                 INITIAL_HEIGHT).build();
455         dc.mWallpaperController.setShouldOffsetWallpaperCenter(false);
456         prepareSmallerSecondDisplay(dc, SECOND_WIDTH, INITIAL_HEIGHT);
457         final WindowState wallpaperWindow = createWallpaperWindow(dc, INITIAL_WIDTH,
458                 INITIAL_HEIGHT);
459 
460         dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
461 
462         // Wallpaper centering is disabled, so no offset.
463         assertThat(wallpaperWindow.mXOffset).isEqualTo(0);
464         assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
465     }
466 
467     @Test
testUpdateWallpaperOffset_initial_shouldCenterEnabled()468     public void testUpdateWallpaperOffset_initial_shouldCenterEnabled() {
469         final DisplayContent dc = new TestDisplayContent.Builder(mAtm, INITIAL_WIDTH,
470                 INITIAL_HEIGHT).build();
471         dc.mWallpaperController.setShouldOffsetWallpaperCenter(true);
472         prepareSmallerSecondDisplay(dc, SECOND_WIDTH, INITIAL_HEIGHT);
473         final WindowState wallpaperWindow = createWallpaperWindow(dc, INITIAL_WIDTH,
474                 INITIAL_HEIGHT);
475 
476         dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
477 
478         // Wallpaper matches first display, so has no offset.
479         assertThat(wallpaperWindow.mXOffset).isEqualTo(0);
480         assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
481     }
482 
483     @Test
testUpdateWallpaperOffset_resize_shouldCenterEnabled()484     public void testUpdateWallpaperOffset_resize_shouldCenterEnabled() {
485         final DisplayContent dc = new TestDisplayContent.Builder(mAtm, INITIAL_WIDTH,
486                 INITIAL_HEIGHT).build();
487         dc.mWallpaperController.setShouldOffsetWallpaperCenter(true);
488         prepareSmallerSecondDisplay(dc, SECOND_WIDTH, INITIAL_HEIGHT);
489         final WindowState wallpaperWindow = createWallpaperWindow(dc, INITIAL_WIDTH,
490                 INITIAL_HEIGHT);
491 
492         dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
493 
494         // Resize display to match second display bounds.
495         resizeDisplayAndWallpaper(dc, wallpaperWindow, SECOND_WIDTH, INITIAL_HEIGHT);
496 
497         dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
498 
499         // Wallpaper is 300 wider than second display.
500         assertThat(wallpaperWindow.mXOffset).isEqualTo(-Math.abs(INITIAL_WIDTH - SECOND_WIDTH) / 2);
501         assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
502     }
503 
504     @Test
testUpdateWallpaperOffset_resize_shouldCenterDisabled()505     public void testUpdateWallpaperOffset_resize_shouldCenterDisabled() {
506         final DisplayContent dc = new TestDisplayContent.Builder(mAtm, INITIAL_WIDTH,
507                 INITIAL_HEIGHT).build();
508         dc.mWallpaperController.setShouldOffsetWallpaperCenter(false);
509         prepareSmallerSecondDisplay(dc, SECOND_WIDTH, INITIAL_HEIGHT);
510         final WindowState wallpaperWindow = createWallpaperWindow(dc, INITIAL_WIDTH,
511                 INITIAL_HEIGHT);
512 
513         dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
514 
515         // Resize display to match second display bounds.
516         resizeDisplayAndWallpaper(dc, wallpaperWindow, SECOND_WIDTH, INITIAL_HEIGHT);
517 
518         dc.mWallpaperController.updateWallpaperOffset(wallpaperWindow, false);
519 
520         // Wallpaper is 300 wider than second display, but offset disabled.
521         assertThat(wallpaperWindow.mXOffset).isEqualTo(0);
522         assertThat(wallpaperWindow.mYOffset).isEqualTo(0);
523     }
524 
createWallpaperWindow(DisplayContent dc, int width, int height)525     private WindowState createWallpaperWindow(DisplayContent dc, int width, int height) {
526         final WindowState wallpaperWindow = createWallpaperWindow(dc);
527         // Wallpaper is cropped to match first display.
528         wallpaperWindow.getWindowFrames().mParentFrame.set(new Rect(0, 0, width, height));
529         wallpaperWindow.getWindowFrames().mFrame.set(0, 0, width, height);
530         return wallpaperWindow;
531     }
532 
createWallpaperWindow(DisplayContent dc)533     private WindowState createWallpaperWindow(DisplayContent dc) {
534         final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class),
535                 true /* explicit */, dc, true /* ownerCanManageAppTokens */);
536         return createWindow(null /* parent */, TYPE_WALLPAPER, wallpaperWindowToken,
537                 "wallpaperWindow");
538     }
539 
createWallpaperTargetWindow(DisplayContent dc)540     private WindowState createWallpaperTargetWindow(DisplayContent dc) {
541         final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService)
542                 .setTask(dc.getDefaultTaskDisplayArea().getRootHomeTask())
543                 .build();
544         homeActivity.setVisibility(true);
545 
546         WindowState appWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
547                 homeActivity, "wallpaperTargetWindow");
548         appWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
549         appWindow.mHasSurface = true;
550         spyOn(appWindow);
551         doReturn(true).when(appWindow).isDrawFinishedLw();
552 
553         homeActivity.addWindow(appWindow);
554         return appWindow;
555     }
556 }
557