blob: 3110fb9a40fee20387c917f611e120caaa10d3bf [file] [log] [blame]
Adrian Roos4d18a2e2017-12-19 19:08:05 +01001/*
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
17package com.android.server.wm;
18
19import static android.view.SurfaceControl.HIDDEN;
20
Adrian Roos7af9d972018-11-30 15:26:27 +010021import android.graphics.Point;
Adrian Roos4d18a2e2017-12-19 19:08:05 +010022import android.graphics.Rect;
Riddle Hsu192fe762019-01-15 23:41:57 +080023import android.os.Binder;
24import android.os.Process;
25import android.view.InputChannel;
26import android.view.InputEventReceiver;
27import android.view.InputWindowHandle;
Adrian Roos4d18a2e2017-12-19 19:08:05 +010028import android.view.SurfaceControl;
Riddle Hsu192fe762019-01-15 23:41:57 +080029import android.view.WindowManager;
30
31import com.android.server.UiThread;
Adrian Roos4d18a2e2017-12-19 19:08:05 +010032
33import java.util.function.Supplier;
34
35/**
36 * Manages a set of {@link SurfaceControl}s to draw a black letterbox between an
37 * outer rect and an inner rect.
38 */
39public class Letterbox {
40
41 private static final Rect EMPTY_RECT = new Rect();
Adrian Roos7af9d972018-11-30 15:26:27 +010042 private static final Point ZERO_POINT = new Point(0, 0);
Adrian Roos4d18a2e2017-12-19 19:08:05 +010043
44 private final Supplier<SurfaceControl.Builder> mFactory;
45 private final Rect mOuter = new Rect();
46 private final Rect mInner = new Rect();
47 private final LetterboxSurface mTop = new LetterboxSurface("top");
48 private final LetterboxSurface mLeft = new LetterboxSurface("left");
49 private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
50 private final LetterboxSurface mRight = new LetterboxSurface("right");
Riddle Hsu192fe762019-01-15 23:41:57 +080051 private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
Adrian Roos4d18a2e2017-12-19 19:08:05 +010052
53 /**
54 * Constructs a Letterbox.
55 *
56 * @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s
57 */
58 public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory) {
59 mFactory = surfaceControlFactory;
60 }
61
62 /**
Adrian Roos23df3a32018-03-15 15:41:13 +010063 * Lays out the letterbox, such that the area between the outer and inner
Adrian Roos4d18a2e2017-12-19 19:08:05 +010064 * frames will be covered by black color surfaces.
65 *
Adrian Roos23df3a32018-03-15 15:41:13 +010066 * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
Adrian Roos4d18a2e2017-12-19 19:08:05 +010067 * @param outer the outer frame of the letterbox (this frame will be black, except the area
Adrian Roos7af9d972018-11-30 15:26:27 +010068 * that intersects with the {code inner} frame), in global coordinates
69 * @param inner the inner frame of the letterbox (this frame will be clear), in global
70 * coordinates
71 * @param surfaceOrigin the origin of the surface factory in global coordinates
Adrian Roos4d18a2e2017-12-19 19:08:05 +010072 */
Adrian Roos7af9d972018-11-30 15:26:27 +010073 public void layout(Rect outer, Rect inner, Point surfaceOrigin) {
Adrian Roos4d18a2e2017-12-19 19:08:05 +010074 mOuter.set(outer);
75 mInner.set(inner);
76
Adrian Roos73f21b12019-01-18 13:59:54 +010077 mTop.layout(outer.left, outer.top, inner.right, inner.top, surfaceOrigin);
78 mLeft.layout(outer.left, inner.top, inner.left, outer.bottom, surfaceOrigin);
79 mBottom.layout(inner.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin);
80 mRight.layout(inner.right, outer.top, outer.right, inner.bottom, surfaceOrigin);
Adrian Roos4d18a2e2017-12-19 19:08:05 +010081 }
82
Adrian Roos23df3a32018-03-15 15:41:13 +010083
Adrian Roos4d18a2e2017-12-19 19:08:05 +010084 /**
Adrian Roos20e07892018-02-23 19:12:01 +010085 * Gets the insets between the outer and inner rects.
86 */
87 public Rect getInsets() {
88 return new Rect(
89 mLeft.getWidth(),
90 mTop.getHeight(),
91 mRight.getWidth(),
92 mBottom.getHeight());
93 }
94
95 /**
Adrian Roos23df3a32018-03-15 15:41:13 +010096 * Returns true if any part of the letterbox overlaps with the given {@code rect}.
97 */
98 public boolean isOverlappingWith(Rect rect) {
Riddle Hsu192fe762019-01-15 23:41:57 +080099 for (LetterboxSurface surface : mSurfaces) {
100 if (surface.isOverlappingWith(rect)) {
101 return true;
102 }
103 }
104 return false;
Adrian Roos23df3a32018-03-15 15:41:13 +0100105 }
106
107 /**
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100108 * Hides the letterbox.
109 *
Adrian Roos23df3a32018-03-15 15:41:13 +0100110 * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100111 */
Adrian Roos23df3a32018-03-15 15:41:13 +0100112 public void hide() {
Adrian Roos7af9d972018-11-30 15:26:27 +0100113 layout(EMPTY_RECT, EMPTY_RECT, ZERO_POINT);
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100114 }
115
116 /**
117 * Destroys the managed {@link SurfaceControl}s.
118 */
119 public void destroy() {
120 mOuter.setEmpty();
121 mInner.setEmpty();
122
Riddle Hsu192fe762019-01-15 23:41:57 +0800123 for (LetterboxSurface surface : mSurfaces) {
124 surface.remove();
125 }
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100126 }
127
Adrian Roos23df3a32018-03-15 15:41:13 +0100128 /** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
129 public boolean needsApplySurfaceChanges() {
Riddle Hsu192fe762019-01-15 23:41:57 +0800130 for (LetterboxSurface surface : mSurfaces) {
131 if (surface.needsApplySurfaceChanges()) {
132 return true;
133 }
134 }
135 return false;
Adrian Roos23df3a32018-03-15 15:41:13 +0100136 }
137
138 public void applySurfaceChanges(SurfaceControl.Transaction t) {
Riddle Hsu192fe762019-01-15 23:41:57 +0800139 for (LetterboxSurface surface : mSurfaces) {
140 surface.applySurfaceChanges(t);
141 }
142 }
143
144 /** Enables touches to slide into other neighboring surfaces. */
145 void attachInput(WindowState win) {
146 for (LetterboxSurface surface : mSurfaces) {
147 surface.attachInput(win);
148 }
149 }
150
151 void onMovedToDisplay(int displayId) {
152 for (LetterboxSurface surface : mSurfaces) {
153 if (surface.mInputInterceptor != null) {
154 surface.mInputInterceptor.mWindowHandle.displayId = displayId;
155 }
156 }
157 }
158
159 private static class InputInterceptor {
160 final InputChannel mServerChannel;
161 final InputChannel mClientChannel;
162 final InputWindowHandle mWindowHandle;
163 final InputEventReceiver mInputEventReceiver;
164 final WindowManagerService mWmService;
165
166 InputInterceptor(String namePrefix, WindowState win) {
167 mWmService = win.mWmService;
168 final String name = namePrefix + (win.mAppToken != null ? win.mAppToken : win);
169 final InputChannel[] channels = InputChannel.openInputChannelPair(name);
170 mServerChannel = channels[0];
171 mClientChannel = channels[1];
172 mInputEventReceiver = new SimpleInputReceiver(mClientChannel);
173
174 final Binder token = new Binder();
175 mWmService.mInputManager.registerInputChannel(mServerChannel, token);
176
177 mWindowHandle = new InputWindowHandle(null /* inputApplicationHandle */,
178 null /* clientWindow */, win.getDisplayId());
179 mWindowHandle.name = name;
180 mWindowHandle.token = token;
181 mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
182 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
183 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
184 | WindowManager.LayoutParams.FLAG_SLIPPERY;
185 mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
186 mWindowHandle.dispatchingTimeoutNanos =
187 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
188 mWindowHandle.visible = true;
189 mWindowHandle.ownerPid = Process.myPid();
190 mWindowHandle.ownerUid = Process.myUid();
191 mWindowHandle.scaleFactor = 1.0f;
192 }
193
194 void updateTouchableRegion(Rect frame) {
195 mWindowHandle.touchableRegion.set(frame);
196 mWindowHandle.touchableRegion.translate(-frame.left, -frame.top);
197 }
198
199 void dispose() {
200 mWmService.mInputManager.unregisterInputChannel(mServerChannel);
201 mInputEventReceiver.dispose();
202 mServerChannel.dispose();
203 mClientChannel.dispose();
204 }
205
206 private static class SimpleInputReceiver extends InputEventReceiver {
207 SimpleInputReceiver(InputChannel inputChannel) {
208 super(inputChannel, UiThread.getHandler().getLooper());
209 }
210 }
Adrian Roos23df3a32018-03-15 15:41:13 +0100211 }
212
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100213 private class LetterboxSurface {
214
215 private final String mType;
216 private SurfaceControl mSurface;
217
Adrian Roos73f21b12019-01-18 13:59:54 +0100218 private final Rect mSurfaceFrameRelative = new Rect();
219 private final Rect mLayoutFrameGlobal = new Rect();
220 private final Rect mLayoutFrameRelative = new Rect();
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100221
Riddle Hsu192fe762019-01-15 23:41:57 +0800222 private InputInterceptor mInputInterceptor;
223
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100224 public LetterboxSurface(String type) {
225 mType = type;
226 }
227
Adrian Roos73f21b12019-01-18 13:59:54 +0100228 public void layout(int left, int top, int right, int bottom, Point surfaceOrigin) {
229 mLayoutFrameGlobal.set(left, top, right, bottom);
230 mLayoutFrameRelative.set(mLayoutFrameGlobal);
231 mLayoutFrameRelative.offset(-surfaceOrigin.x, -surfaceOrigin.y);
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100232 }
233
234 private void createSurface() {
235 mSurface = mFactory.get().setName("Letterbox - " + mType)
Chavi Weingarten6ef9cc62019-02-07 16:28:45 +0000236 .setFlags(HIDDEN).setColorLayer().build();
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100237 mSurface.setLayer(-1);
238 mSurface.setColor(new float[]{0, 0, 0});
Peiyong Linf4f0f642019-03-01 14:36:05 -0800239 mSurface.setColorSpaceAgnostic(true);
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100240 }
241
Riddle Hsu192fe762019-01-15 23:41:57 +0800242 void attachInput(WindowState win) {
243 if (mInputInterceptor != null) {
244 mInputInterceptor.dispose();
245 }
246 mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", win);
247 }
248
Robert Carr5ea304d2019-02-04 16:04:55 -0800249 public void remove() {
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100250 if (mSurface != null) {
Robert Carr5ea304d2019-02-04 16:04:55 -0800251 mSurface.remove();
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100252 mSurface = null;
253 }
Riddle Hsu192fe762019-01-15 23:41:57 +0800254 if (mInputInterceptor != null) {
255 mInputInterceptor.dispose();
256 mInputInterceptor = null;
257 }
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100258 }
Adrian Roos20e07892018-02-23 19:12:01 +0100259
260 public int getWidth() {
Adrian Roos73f21b12019-01-18 13:59:54 +0100261 return Math.max(0, mLayoutFrameGlobal.width());
Adrian Roos20e07892018-02-23 19:12:01 +0100262 }
263
264 public int getHeight() {
Adrian Roos73f21b12019-01-18 13:59:54 +0100265 return Math.max(0, mLayoutFrameGlobal.height());
Adrian Roos23df3a32018-03-15 15:41:13 +0100266 }
267
Adrian Roos73f21b12019-01-18 13:59:54 +0100268 /**
269 * Returns if the given {@code rect} overlaps with this letterbox piece.
270 * @param rect the area to check for overlap in global coordinates
271 */
Adrian Roos23df3a32018-03-15 15:41:13 +0100272 public boolean isOverlappingWith(Rect rect) {
Adrian Roos73f21b12019-01-18 13:59:54 +0100273 if (mLayoutFrameGlobal.isEmpty()) {
Adrian Roos23df3a32018-03-15 15:41:13 +0100274 return false;
275 }
Adrian Roos73f21b12019-01-18 13:59:54 +0100276 return Rect.intersects(rect, mLayoutFrameGlobal);
Adrian Roos23df3a32018-03-15 15:41:13 +0100277 }
278
279 public void applySurfaceChanges(SurfaceControl.Transaction t) {
Adrian Roos73f21b12019-01-18 13:59:54 +0100280 if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
Adrian Roos23df3a32018-03-15 15:41:13 +0100281 // Nothing changed.
282 return;
283 }
Adrian Roos73f21b12019-01-18 13:59:54 +0100284 mSurfaceFrameRelative.set(mLayoutFrameRelative);
285 if (!mSurfaceFrameRelative.isEmpty()) {
Adrian Roos23df3a32018-03-15 15:41:13 +0100286 if (mSurface == null) {
287 createSurface();
288 }
Adrian Roos73f21b12019-01-18 13:59:54 +0100289 t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
290 t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
291 mSurfaceFrameRelative.height());
Riddle Hsu192fe762019-01-15 23:41:57 +0800292 if (mInputInterceptor != null) {
293 mInputInterceptor.updateTouchableRegion(mSurfaceFrameRelative);
294 t.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle);
295 }
Adrian Roos23df3a32018-03-15 15:41:13 +0100296 t.show(mSurface);
297 } else if (mSurface != null) {
298 t.hide(mSurface);
299 }
300 }
301
302 public boolean needsApplySurfaceChanges() {
Adrian Roos73f21b12019-01-18 13:59:54 +0100303 return !mSurfaceFrameRelative.equals(mLayoutFrameRelative);
Adrian Roos20e07892018-02-23 19:12:01 +0100304 }
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100305 }
306}