1/* 2 * Copyright (C) 2017 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 17package com.android.server.policy; 18 19import static android.view.Surface.ROTATION_0; 20import static android.view.Surface.ROTATION_180; 21import static android.view.Surface.ROTATION_270; 22import static android.view.Surface.ROTATION_90; 23import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 24import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 25import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 26 27import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates; 28 29import static org.junit.Assert.assertEquals; 30import static org.mockito.Mockito.mock; 31 32import android.content.Context; 33import android.content.ContextWrapper; 34import android.content.pm.PackageManager; 35import android.content.res.Resources; 36import android.graphics.Matrix; 37import android.graphics.Path; 38import android.graphics.PixelFormat; 39import android.graphics.Rect; 40import android.os.IBinder; 41import android.os.UserHandle; 42import android.support.test.InstrumentationRegistry; 43import android.testing.TestableResources; 44import android.util.Pair; 45import android.view.Display; 46import android.view.DisplayCutout; 47import android.view.DisplayInfo; 48import android.view.Gravity; 49import android.view.View; 50import android.view.WindowManager; 51import android.view.WindowManagerGlobal; 52import android.view.accessibility.AccessibilityManager; 53import android.view.accessibility.IAccessibilityManager; 54 55import com.android.server.policy.keyguard.KeyguardServiceDelegate; 56import com.android.server.wm.DisplayFrames; 57import com.android.server.wm.utils.WmDisplayCutout; 58 59import org.junit.Before; 60 61public class PhoneWindowManagerTestBase { 62 static final int DISPLAY_WIDTH = 500; 63 static final int DISPLAY_HEIGHT = 1000; 64 65 static final int STATUS_BAR_HEIGHT = 10; 66 static final int NAV_BAR_HEIGHT = 15; 67 static final int DISPLAY_CUTOUT_HEIGHT = 8; 68 69 TestablePhoneWindowManager mPolicy; 70 TestContextWrapper mContext; 71 DisplayFrames mFrames; 72 73 FakeWindowState mStatusBar; 74 FakeWindowState mNavigationBar; 75 private boolean mHasDisplayCutout; 76 private int mRotation = ROTATION_0; 77 78 @Before 79 public void setUpBase() throws Exception { 80 mContext = new TestContextWrapper(InstrumentationRegistry.getTargetContext()); 81 mContext.getResourceMocker().addOverride( 82 com.android.internal.R.dimen.status_bar_height_portrait, STATUS_BAR_HEIGHT); 83 mContext.getResourceMocker().addOverride( 84 com.android.internal.R.dimen.status_bar_height_landscape, STATUS_BAR_HEIGHT); 85 mContext.getResourceMocker().addOverride( 86 com.android.internal.R.dimen.navigation_bar_height, NAV_BAR_HEIGHT); 87 mContext.getResourceMocker().addOverride( 88 com.android.internal.R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT); 89 mContext.getResourceMocker().addOverride( 90 com.android.internal.R.dimen.navigation_bar_width, NAV_BAR_HEIGHT); 91 92 mPolicy = TestablePhoneWindowManager.create(mContext); 93 94 updateDisplayFrames(); 95 } 96 97 public void setRotation(int rotation) { 98 mRotation = rotation; 99 updateDisplayFrames(); 100 } 101 102 private void updateDisplayFrames() { 103 Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation, 104 mHasDisplayCutout); 105 mFrames = new DisplayFrames(Display.DEFAULT_DISPLAY, info.first, info.second); 106 } 107 108 public void addStatusBar() { 109 mStatusBar = new FakeWindowState(); 110 mStatusBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, STATUS_BAR_HEIGHT, 111 TYPE_STATUS_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT); 112 mStatusBar.attrs.gravity = Gravity.TOP; 113 114 mPolicy.addWindow(mStatusBar); 115 mPolicy.mLastSystemUiFlags |= View.STATUS_BAR_TRANSPARENT; 116 } 117 118 public void addNavigationBar() { 119 mNavigationBar = new FakeWindowState(); 120 mNavigationBar.attrs = new WindowManager.LayoutParams(MATCH_PARENT, NAV_BAR_HEIGHT, 121 TYPE_NAVIGATION_BAR, 0 /* flags */, PixelFormat.TRANSLUCENT); 122 mNavigationBar.attrs.gravity = Gravity.BOTTOM; 123 124 mPolicy.addWindow(mNavigationBar); 125 mPolicy.mHasNavigationBar = true; 126 mPolicy.mLastSystemUiFlags |= View.NAVIGATION_BAR_TRANSPARENT; 127 } 128 129 public void addDisplayCutout() { 130 mHasDisplayCutout = true; 131 updateDisplayFrames(); 132 } 133 134 /** Asserts that {@code actual} is inset by the given amounts from the full display rect. */ 135 public void assertInsetBy(Rect actual, int expectedInsetLeft, int expectedInsetTop, 136 int expectedInsetRight, int expectedInsetBottom) { 137 assertEquals(new Rect(expectedInsetLeft, expectedInsetTop, 138 mFrames.mDisplayWidth - expectedInsetRight, 139 mFrames.mDisplayHeight - expectedInsetBottom), actual); 140 } 141 142 /** 143 * Asserts that {@code actual} is inset by the given amounts from the full display rect. 144 * 145 * Convenience wrapper for when only the top and bottom inset are non-zero. 146 */ 147 public void assertInsetByTopBottom(Rect actual, int expectedInsetTop, int expectedInsetBottom) { 148 assertInsetBy(actual, 0, expectedInsetTop, 0, expectedInsetBottom); 149 } 150 151 public static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) { 152 return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first; 153 } 154 public static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation, 155 boolean withDisplayCutout) { 156 DisplayInfo info = new DisplayInfo(); 157 WmDisplayCutout cutout = null; 158 159 final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270; 160 info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH; 161 info.logicalHeight = flippedDimensions ? DISPLAY_WIDTH : DISPLAY_HEIGHT; 162 info.rotation = rotation; 163 if (withDisplayCutout) { 164 cutout = WmDisplayCutout.computeSafeInsets( 165 displayCutoutForRotation(rotation), info.logicalWidth, 166 info.logicalHeight); 167 info.displayCutout = cutout.getDisplayCutout(); 168 } else { 169 info.displayCutout = null; 170 } 171 return Pair.create(info, cutout); 172 } 173 174 private static DisplayCutout displayCutoutForRotation(int rotation) { 175 Path p = new Path(); 176 p.addRect(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT, 177 Path.Direction.CCW); 178 179 Matrix m = new Matrix(); 180 transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m); 181 p.transform(m); 182 183 return DisplayCutout.fromBounds(p); 184 } 185 186 static class TestContextWrapper extends ContextWrapper { 187 private final TestableResources mResourceMocker; 188 189 public TestContextWrapper(Context targetContext) { 190 super(targetContext); 191 mResourceMocker = new TestableResources(targetContext.getResources()); 192 } 193 194 @Override 195 public int checkPermission(String permission, int pid, int uid) { 196 return PackageManager.PERMISSION_GRANTED; 197 } 198 199 @Override 200 public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { 201 return PackageManager.PERMISSION_GRANTED; 202 } 203 204 @Override 205 public Resources getResources() { 206 return mResourceMocker.getResources(); 207 } 208 209 public TestableResources getResourceMocker() { 210 return mResourceMocker; 211 } 212 } 213 214 static class TestablePhoneWindowManager extends PhoneWindowManager { 215 216 public TestablePhoneWindowManager() { 217 } 218 219 @Override 220 void initializeHdmiState() { 221 // Do nothing. 222 } 223 224 @Override 225 Context getSystemUiContext() { 226 return mContext; 227 } 228 229 void addWindow(WindowState state) { 230 if (state instanceof FakeWindowState) { 231 ((FakeWindowState) state).surfaceLayer = 232 getWindowLayerFromTypeLw(state.getAttrs().type, 233 true /* canAddInternalSystemWindow */); 234 } 235 adjustWindowParamsLw(state, state.getAttrs(), true /* hasStatusBarPermission */); 236 assertEquals(WindowManagerGlobal.ADD_OKAY, prepareAddWindowLw(state, state.getAttrs())); 237 } 238 239 public static TestablePhoneWindowManager create(Context context) { 240 TestablePhoneWindowManager[] policy = new TestablePhoneWindowManager[1]; 241 InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { 242 policy[0] = new TestablePhoneWindowManager(); 243 policy[0].mContext = context; 244 policy[0].mKeyguardDelegate = mock(KeyguardServiceDelegate.class); 245 policy[0].mAccessibilityManager = new AccessibilityManager(context, 246 mock(IAccessibilityManager.class), UserHandle.USER_CURRENT); 247 policy[0].mSystemGestures = mock(SystemGesturesPointerEventListener.class); 248 policy[0].mNavigationBarCanMove = true; 249 policy[0].mPortraitRotation = ROTATION_0; 250 policy[0].mLandscapeRotation = ROTATION_90; 251 policy[0].mUpsideDownRotation = ROTATION_180; 252 policy[0].mSeascapeRotation = ROTATION_270; 253 policy[0].onConfigurationChanged(); 254 }); 255 return policy[0]; 256 } 257 } 258} 259