Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2016 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 org.junit.Before;
     20 import org.junit.Test;
     21 import org.junit.runner.RunWith;
     22 
     23 import android.app.ActivityManager.TaskDescription;
     24 import android.content.Context;
     25 import android.graphics.Rect;
     26 import android.platform.test.annotations.Presubmit;
     27 import android.support.test.InstrumentationRegistry;
     28 import android.support.test.filters.SmallTest;
     29 import android.support.test.runner.AndroidJUnit4;
     30 import android.view.DisplayInfo;
     31 import android.view.Gravity;
     32 import android.view.IWindow;
     33 import android.view.WindowManager;
     34 
     35 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
     36 import static android.view.WindowManager.LayoutParams.FILL_PARENT;
     37 import static org.junit.Assert.assertEquals;
     38 import static org.junit.Assert.assertTrue;
     39 
     40 /**
     41  * Tests for the {@link WindowState#computeFrameLw} method and other window frame machinery.
     42  *
     43  * Build/Install/Run: bit FrameworksServicesTests:com.android.server.wm.WindowFrameTests
     44  */
     45 @SmallTest
     46 @Presubmit
     47 @RunWith(AndroidJUnit4.class)
     48 public class WindowFrameTests extends WindowTestsBase {
     49 
     50     private WindowToken mWindowToken;
     51     private final IWindow mIWindow = new TestIWindow();
     52 
     53     class WindowStateWithTask extends WindowState {
     54         final Task mTask;
     55         boolean mDockedResizingForTest = false;
     56         WindowStateWithTask(WindowManager.LayoutParams attrs, Task t) {
     57             super(sWm, null, mIWindow, mWindowToken, null, 0, 0, attrs, 0, 0,
     58                     false /* ownerCanAddInternalSystemWindow */);
     59             mTask = t;
     60         }
     61 
     62         @Override
     63         Task getTask() {
     64             return mTask;
     65         }
     66 
     67         @Override
     68         boolean isDockedResizing() {
     69             return mDockedResizingForTest;
     70         }
     71     };
     72 
     73     class TaskWithBounds extends Task {
     74         final Rect mBounds;
     75         final Rect mInsetBounds = new Rect();
     76         boolean mFullscreenForTest = true;
     77         TaskWithBounds(Rect bounds) {
     78             super(0, mStubStack, 0, sWm, null, null, 0, false, false, new TaskDescription(), null);
     79             mBounds = bounds;
     80         }
     81         @Override
     82         void getBounds(Rect outBounds) {
     83             outBounds.set(mBounds);
     84         }
     85         @Override
     86         void getTempInsetBounds(Rect outBounds) {
     87             outBounds.set(mInsetBounds);
     88         }
     89         @Override
     90         boolean isFullscreen() {
     91             return mFullscreenForTest;
     92         }
     93     }
     94 
     95     TaskStack mStubStack;
     96 
     97     @Before
     98     public void setUp() throws Exception {
     99         super.setUp();
    100 
    101         // Just any non zero value.
    102         sWm.mSystemDecorLayer = 10000;
    103 
    104         mWindowToken = new WindowTestUtils.TestAppWindowToken(sWm.getDefaultDisplayContentLocked());
    105         mStubStack = new TaskStack(sWm, 0);
    106     }
    107 
    108     public void assertRect(Rect rect, int left, int top, int right, int bottom) {
    109         assertEquals(left, rect.left);
    110         assertEquals(top, rect.top);
    111         assertEquals(right, rect.right);
    112         assertEquals(bottom, rect.bottom);
    113     }
    114 
    115     @Test
    116     public void testLayoutInFullscreenTaskInsets() throws Exception {
    117         Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
    118         WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
    119         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
    120 
    121         final int bottomContentInset = 100;
    122         final int topContentInset = 50;
    123         final int bottomVisibleInset = 30;
    124         final int topVisibleInset = 70;
    125         final int leftStableInset = 20;
    126         final int rightStableInset = 90;
    127 
    128         // With no insets or system decor all the frames incoming from PhoneWindowManager
    129         // are identical.
    130         final Rect pf = new Rect(0, 0, 1000, 1000);
    131         final Rect df = pf;
    132         final Rect of = df;
    133         final Rect cf = new Rect(pf);
    134         // Produce some insets
    135         cf.top += 50;
    136         cf.bottom -= 100;
    137         final Rect vf = new Rect(pf);
    138         vf.top += topVisibleInset;
    139         vf.bottom -= bottomVisibleInset;
    140         final Rect sf = new Rect(pf);
    141         sf.left += leftStableInset;
    142         sf.right -= rightStableInset;
    143 
    144         final Rect dcf = pf;
    145         // When mFrame extends past cf, the content insets are
    146         // the difference between mFrame and ContentFrame. Visible
    147         // and stable frames work the same way.
    148         w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
    149         assertRect(w.mFrame,0, 0, 1000, 1000);
    150         assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
    151         assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
    152         assertRect(w.mStableInsets, leftStableInset, 0, rightStableInset, 0);
    153         // The frames remain as passed in shrunk to the window frame
    154         assertTrue(cf.equals(w.getContentFrameLw()));
    155         assertTrue(vf.equals(w.getVisibleFrameLw()));
    156         assertTrue(sf.equals(w.getStableFrameLw()));
    157         // On the other hand mFrame doesn't extend past cf we won't get any insets
    158         w.mAttrs.x = 100;
    159         w.mAttrs.y = 100;
    160         w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
    161         w.mRequestedWidth = 100;
    162         w.mRequestedHeight = 100;
    163         w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
    164         assertRect(w.mFrame, 100, 100, 200, 200);
    165         assertRect(w.mContentInsets, 0, 0, 0, 0);
    166         // In this case the frames are shrunk to the window frame.
    167         assertTrue(w.mFrame.equals(w.getContentFrameLw()));
    168         assertTrue(w.mFrame.equals(w.getVisibleFrameLw()));
    169         assertTrue(w.mFrame.equals(w.getStableFrameLw()));
    170     }
    171 
    172     @Test
    173     public void testLayoutInFullscreenTaskNoInsets() throws Exception {
    174         Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
    175         WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
    176         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
    177 
    178         // With no insets or system decor all the frames incoming from PhoneWindowManager
    179         // are identical.
    180         final Rect pf = new Rect(0, 0, 1000, 1000);
    181 
    182         // Here the window has FILL_PARENT, FILL_PARENT
    183         // so we expect it to fill the entire available frame.
    184         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    185         assertRect(w.mFrame, 0, 0, 1000, 1000);
    186 
    187         // It can select various widths and heights within the bounds.
    188         // Strangely the window attribute width is ignored for normal windows
    189         // and we use mRequestedWidth/mRequestedHeight
    190         w.mAttrs.width = 300;
    191         w.mAttrs.height = 300;
    192         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    193         // Explicit width and height without requested width/height
    194         // gets us nothing.
    195         assertRect(w.mFrame, 0, 0, 0, 0);
    196 
    197         w.mRequestedWidth = 300;
    198         w.mRequestedHeight = 300;
    199         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    200         // With requestedWidth/Height we can freely choose our size within the
    201         // parent bounds.
    202         assertRect(w.mFrame, 0, 0, 300, 300);
    203 
    204         // With FLAG_SCALED though, requestedWidth/height is used to control
    205         // the unscaled surface size, and mAttrs.width/height becomes the
    206         // layout controller.
    207         w.mAttrs.flags = WindowManager.LayoutParams.FLAG_SCALED;
    208         w.mRequestedHeight = -1;
    209         w.mRequestedWidth = -1;
    210         w.mAttrs.width = 100;
    211         w.mAttrs.height = 100;
    212         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    213         assertRect(w.mFrame, 0, 0, 100, 100);
    214         w.mAttrs.flags = 0;
    215 
    216         // But sizes too large will be clipped to the containing frame
    217         w.mRequestedWidth = 1200;
    218         w.mRequestedHeight = 1200;
    219         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    220         assertRect(w.mFrame, 0, 0, 1000, 1000);
    221 
    222         // Before they are clipped though windows will be shifted
    223         w.mAttrs.x = 300;
    224         w.mAttrs.y = 300;
    225         w.mRequestedWidth = 1000;
    226         w.mRequestedHeight = 1000;
    227         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    228         assertRect(w.mFrame, 0, 0, 1000, 1000);
    229 
    230         // If there is room to move around in the parent frame the window will be shifted according
    231         // to gravity.
    232         w.mAttrs.x = 0;
    233         w.mAttrs.y = 0;
    234         w.mRequestedWidth = 300;
    235         w.mRequestedHeight = 300;
    236         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
    237         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    238         assertRect(w.mFrame, 700, 0, 1000, 300);
    239         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
    240         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    241         assertRect(w.mFrame, 700, 700, 1000, 1000);
    242         // Window specified  x and y are interpreted as offsets in the opposite
    243         // direction of gravity
    244         w.mAttrs.x = 100;
    245         w.mAttrs.y = 100;
    246         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    247         assertRect(w.mFrame, 600, 600, 900, 900);
    248     }
    249 
    250     @Test
    251     public void testLayoutNonfullscreenTask() {
    252         final DisplayInfo displayInfo = sWm.getDefaultDisplayContentLocked().getDisplayInfo();
    253         final int logicalWidth = displayInfo.logicalWidth;
    254         final int logicalHeight = displayInfo.logicalHeight;
    255 
    256         final int taskLeft = logicalWidth / 4;
    257         final int taskTop = logicalHeight / 4;
    258         final int taskRight = logicalWidth / 4 * 3;
    259         final int taskBottom = logicalHeight / 4 * 3;
    260         final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
    261         TaskWithBounds task = new TaskWithBounds(taskBounds);
    262         task.mFullscreenForTest = false;
    263         WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
    264         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
    265 
    266         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
    267         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, null);
    268         // For non fullscreen tasks the containing frame is based off the
    269         // task bounds not the parent frame.
    270         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
    271         assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
    272         assertRect(w.mContentInsets, 0, 0, 0, 0);
    273 
    274         pf.set(0, 0, logicalWidth, logicalHeight);
    275         // We still produce insets against the containing frame the same way.
    276         final int cfRight = logicalWidth / 2;
    277         final int cfBottom = logicalHeight / 2;
    278         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
    279         w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
    280         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
    281         int contentInsetRight = taskRight - cfRight;
    282         int contentInsetBottom = taskBottom - cfBottom;
    283         assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
    284         assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight,
    285                 taskBottom - contentInsetBottom);
    286 
    287         pf.set(0, 0, logicalWidth, logicalHeight);
    288         // However if we set temp inset bounds, the insets will be computed
    289         // as if our window was laid out there,  but it will be laid out according to
    290         // the task bounds.
    291         final int insetLeft = logicalWidth / 5;
    292         final int insetTop = logicalHeight / 5;
    293         final int insetRight = insetLeft + (taskRight - taskLeft);
    294         final int insetBottom = insetTop + (taskBottom - taskTop);
    295         task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
    296         w.computeFrameLw(pf, pf, pf, cf, cf, pf, cf, null);
    297         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
    298         contentInsetRight = insetRight - cfRight;
    299         contentInsetBottom = insetBottom - cfBottom;
    300         assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
    301         assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight,
    302                 taskBottom - contentInsetBottom);
    303     }
    304 
    305     @Test
    306     public void testCalculatePolicyCrop() {
    307         final WindowStateWithTask w = createWindow(
    308                 new TaskWithBounds(null), FILL_PARENT, FILL_PARENT);
    309         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
    310 
    311         final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
    312         final int logicalWidth = displayInfo.logicalWidth;
    313         final int logicalHeight = displayInfo.logicalHeight;
    314         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
    315         final Rect df = pf;
    316         final Rect of = df;
    317         final Rect cf = new Rect(pf);
    318         // Produce some insets
    319         cf.top += displayInfo.logicalWidth / 10;
    320         cf.bottom -= displayInfo.logicalWidth / 5;
    321         final Rect vf = cf;
    322         final Rect sf = vf;
    323         // We use a decor content frame with insets to produce cropping.
    324         Rect dcf = cf;
    325 
    326         final Rect policyCrop = new Rect();
    327 
    328         w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
    329         w.calculatePolicyCrop(policyCrop);
    330         // If we were above system decor we wouldnt' get any cropping though
    331         w.mLayer = sWm.mSystemDecorLayer + 1;
    332         w.calculatePolicyCrop(policyCrop);
    333         assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
    334         w.mLayer = 1;
    335         dcf.setEmpty();
    336         // Likewise with no decor frame we would get no crop
    337         w.computeFrameLw(pf, df, of, cf, vf, dcf, sf, null);
    338         w.calculatePolicyCrop(policyCrop);
    339         assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
    340 
    341         // Now we set up a window which doesn't fill the entire decor frame.
    342         // Normally it would be cropped to it's frame but in the case of docked resizing
    343         // we need to account for the fact the windows surface will be made
    344         // fullscreen and thus also make the crop fullscreen.
    345         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
    346         w.mAttrs.width = logicalWidth / 2;
    347         w.mAttrs.height = logicalHeight / 2;
    348         w.mRequestedWidth = logicalWidth / 2;
    349         w.mRequestedHeight = logicalHeight / 2;
    350         w.computeFrameLw(pf, pf, pf, pf, pf, pf, pf, pf);
    351 
    352         w.calculatePolicyCrop(policyCrop);
    353         // Normally the crop is shrunk from the decor frame
    354         // to the computed window frame.
    355         assertRect(policyCrop, 0, 0, logicalWidth / 2, logicalHeight / 2);
    356 
    357         w.mDockedResizingForTest = true;
    358         w.calculatePolicyCrop(policyCrop);
    359         // But if we are docked resizing it won't be, however we will still be
    360         // shrunk to the decor frame and the display.
    361         assertRect(policyCrop, 0, 0,
    362                 Math.min(pf.width(), displayInfo.logicalWidth),
    363                 Math.min(pf.height(), displayInfo.logicalHeight));
    364     }
    365 
    366     @Test
    367     public void testLayoutLetterboxedWindow() {
    368         // First verify task behavior in multi-window mode.
    369         final DisplayInfo displayInfo = sWm.getDefaultDisplayContentLocked().getDisplayInfo();
    370         final int logicalWidth = displayInfo.logicalWidth;
    371         final int logicalHeight = displayInfo.logicalHeight;
    372 
    373         final int taskLeft = logicalWidth / 5;
    374         final int taskTop = logicalHeight / 5;
    375         final int taskRight = logicalWidth / 4 * 3;
    376         final int taskBottom = logicalHeight / 4 * 3;
    377         final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom);
    378         TaskWithBounds task = new TaskWithBounds(taskBounds);
    379         task.mInsetBounds.set(taskLeft, taskTop, taskRight, taskBottom);
    380         task.mFullscreenForTest = false;
    381         WindowState w = createWindow(task, FILL_PARENT, FILL_PARENT);
    382         w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
    383 
    384         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
    385         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
    386                 pf /* contentFrame */, pf /* visibleFrame */, pf /* decorFrame */,
    387                 pf /* stableFrame */, null /* outsetFrame */);
    388         // For non fullscreen tasks the containing frame is based off the
    389         // task bounds not the parent frame.
    390         assertRect(w.mFrame, taskLeft, taskTop, taskRight, taskBottom);
    391         assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
    392         assertRect(w.mContentInsets, 0, 0, 0, 0);
    393 
    394         // Now simulate switch to fullscreen for letterboxed app.
    395         final int xInset = logicalWidth / 10;
    396         final int yInset = logicalWidth / 10;
    397         final Rect cf = new Rect(xInset, yInset, logicalWidth - xInset, logicalHeight - yInset);
    398         w.mAppToken.onOverrideConfigurationChanged(w.mAppToken.getOverrideConfiguration(), cf);
    399         pf.set(0, 0, logicalWidth, logicalHeight);
    400         task.mFullscreenForTest = true;
    401 
    402         w.computeFrameLw(pf /* parentFrame */, pf /* displayFrame */, pf /* overscanFrame */,
    403                 cf /* contentFrame */, cf /* visibleFrame */, pf /* decorFrame */,
    404                 cf /* stableFrame */, null /* outsetFrame */);
    405         assertEquals(cf, w.mFrame);
    406         assertEquals(cf, w.getContentFrameLw());
    407         assertRect(w.mContentInsets, 0, 0, 0, 0);
    408     }
    409 
    410     private WindowStateWithTask createWindow(Task task, int width, int height) {
    411         final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION);
    412         attrs.width = width;
    413         attrs.height = height;
    414 
    415         return new WindowStateWithTask(attrs, task);
    416     }
    417 }
    418