blob: c3ea72f4c2feee1924161ad5e94f3de07fc3de59 [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
Riddle Hsu69de5fb2019-02-23 01:19:50 +080095 /** @return The frame that used to place the content. */
96 Rect getInnerFrame() {
97 return mInner;
98 }
99
Adrian Roos20e07892018-02-23 19:12:01 +0100100 /**
Adrian Roos23df3a32018-03-15 15:41:13 +0100101 * Returns true if any part of the letterbox overlaps with the given {@code rect}.
102 */
103 public boolean isOverlappingWith(Rect rect) {
Riddle Hsu192fe762019-01-15 23:41:57 +0800104 for (LetterboxSurface surface : mSurfaces) {
105 if (surface.isOverlappingWith(rect)) {
106 return true;
107 }
108 }
109 return false;
Adrian Roos23df3a32018-03-15 15:41:13 +0100110 }
111
112 /**
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100113 * Hides the letterbox.
114 *
Adrian Roos23df3a32018-03-15 15:41:13 +0100115 * The caller must use {@link #applySurfaceChanges} to apply the new layout to the surface.
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100116 */
Adrian Roos23df3a32018-03-15 15:41:13 +0100117 public void hide() {
Adrian Roos7af9d972018-11-30 15:26:27 +0100118 layout(EMPTY_RECT, EMPTY_RECT, ZERO_POINT);
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100119 }
120
121 /**
122 * Destroys the managed {@link SurfaceControl}s.
123 */
124 public void destroy() {
125 mOuter.setEmpty();
126 mInner.setEmpty();
127
Riddle Hsu192fe762019-01-15 23:41:57 +0800128 for (LetterboxSurface surface : mSurfaces) {
129 surface.remove();
130 }
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100131 }
132
Adrian Roos23df3a32018-03-15 15:41:13 +0100133 /** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
134 public boolean needsApplySurfaceChanges() {
Riddle Hsu192fe762019-01-15 23:41:57 +0800135 for (LetterboxSurface surface : mSurfaces) {
136 if (surface.needsApplySurfaceChanges()) {
137 return true;
138 }
139 }
140 return false;
Adrian Roos23df3a32018-03-15 15:41:13 +0100141 }
142
143 public void applySurfaceChanges(SurfaceControl.Transaction t) {
Riddle Hsu192fe762019-01-15 23:41:57 +0800144 for (LetterboxSurface surface : mSurfaces) {
145 surface.applySurfaceChanges(t);
146 }
147 }
148
149 /** Enables touches to slide into other neighboring surfaces. */
150 void attachInput(WindowState win) {
151 for (LetterboxSurface surface : mSurfaces) {
152 surface.attachInput(win);
153 }
154 }
155
156 void onMovedToDisplay(int displayId) {
157 for (LetterboxSurface surface : mSurfaces) {
158 if (surface.mInputInterceptor != null) {
159 surface.mInputInterceptor.mWindowHandle.displayId = displayId;
160 }
161 }
162 }
163
164 private static class InputInterceptor {
165 final InputChannel mServerChannel;
166 final InputChannel mClientChannel;
167 final InputWindowHandle mWindowHandle;
168 final InputEventReceiver mInputEventReceiver;
169 final WindowManagerService mWmService;
Riddle Hsu69de5fb2019-02-23 01:19:50 +0800170 final Binder mToken = new Binder();
Riddle Hsu192fe762019-01-15 23:41:57 +0800171
172 InputInterceptor(String namePrefix, WindowState win) {
173 mWmService = win.mWmService;
174 final String name = namePrefix + (win.mAppToken != null ? win.mAppToken : win);
175 final InputChannel[] channels = InputChannel.openInputChannelPair(name);
176 mServerChannel = channels[0];
177 mClientChannel = channels[1];
178 mInputEventReceiver = new SimpleInputReceiver(mClientChannel);
179
Riddle Hsu69de5fb2019-02-23 01:19:50 +0800180 mWmService.mInputManager.registerInputChannel(mServerChannel, mToken);
Riddle Hsu192fe762019-01-15 23:41:57 +0800181
182 mWindowHandle = new InputWindowHandle(null /* inputApplicationHandle */,
183 null /* clientWindow */, win.getDisplayId());
184 mWindowHandle.name = name;
Riddle Hsu69de5fb2019-02-23 01:19:50 +0800185 mWindowHandle.token = mToken;
Riddle Hsu192fe762019-01-15 23:41:57 +0800186 mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
187 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
188 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
189 | WindowManager.LayoutParams.FLAG_SLIPPERY;
190 mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
191 mWindowHandle.dispatchingTimeoutNanos =
192 WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
193 mWindowHandle.visible = true;
194 mWindowHandle.ownerPid = Process.myPid();
195 mWindowHandle.ownerUid = Process.myUid();
196 mWindowHandle.scaleFactor = 1.0f;
197 }
198
199 void updateTouchableRegion(Rect frame) {
Riddle Hsu69de5fb2019-02-23 01:19:50 +0800200 if (frame.isEmpty()) {
201 // Use null token to indicate the surface doesn't need to receive input event (see
202 // the usage of Layer.hasInput in SurfaceFlinger), so InputDispatcher won't keep the
203 // unnecessary records.
204 mWindowHandle.token = null;
205 return;
206 }
207 mWindowHandle.token = mToken;
Riddle Hsu192fe762019-01-15 23:41:57 +0800208 mWindowHandle.touchableRegion.set(frame);
209 mWindowHandle.touchableRegion.translate(-frame.left, -frame.top);
210 }
211
212 void dispose() {
213 mWmService.mInputManager.unregisterInputChannel(mServerChannel);
214 mInputEventReceiver.dispose();
215 mServerChannel.dispose();
216 mClientChannel.dispose();
217 }
218
219 private static class SimpleInputReceiver extends InputEventReceiver {
220 SimpleInputReceiver(InputChannel inputChannel) {
221 super(inputChannel, UiThread.getHandler().getLooper());
222 }
223 }
Adrian Roos23df3a32018-03-15 15:41:13 +0100224 }
225
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100226 private class LetterboxSurface {
227
228 private final String mType;
229 private SurfaceControl mSurface;
230
Adrian Roos73f21b12019-01-18 13:59:54 +0100231 private final Rect mSurfaceFrameRelative = new Rect();
232 private final Rect mLayoutFrameGlobal = new Rect();
233 private final Rect mLayoutFrameRelative = new Rect();
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100234
Riddle Hsu192fe762019-01-15 23:41:57 +0800235 private InputInterceptor mInputInterceptor;
236
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100237 public LetterboxSurface(String type) {
238 mType = type;
239 }
240
Adrian Roos73f21b12019-01-18 13:59:54 +0100241 public void layout(int left, int top, int right, int bottom, Point surfaceOrigin) {
242 mLayoutFrameGlobal.set(left, top, right, bottom);
243 mLayoutFrameRelative.set(mLayoutFrameGlobal);
244 mLayoutFrameRelative.offset(-surfaceOrigin.x, -surfaceOrigin.y);
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100245 }
246
247 private void createSurface() {
248 mSurface = mFactory.get().setName("Letterbox - " + mType)
Chavi Weingarten6ef9cc62019-02-07 16:28:45 +0000249 .setFlags(HIDDEN).setColorLayer().build();
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100250 mSurface.setLayer(-1);
251 mSurface.setColor(new float[]{0, 0, 0});
Peiyong Linf4f0f642019-03-01 14:36:05 -0800252 mSurface.setColorSpaceAgnostic(true);
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100253 }
254
Riddle Hsu192fe762019-01-15 23:41:57 +0800255 void attachInput(WindowState win) {
256 if (mInputInterceptor != null) {
257 mInputInterceptor.dispose();
258 }
259 mInputInterceptor = new InputInterceptor("Letterbox_" + mType + "_", win);
260 }
261
Robert Carr5ea304d2019-02-04 16:04:55 -0800262 public void remove() {
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100263 if (mSurface != null) {
Robert Carr5ea304d2019-02-04 16:04:55 -0800264 mSurface.remove();
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100265 mSurface = null;
266 }
Riddle Hsu192fe762019-01-15 23:41:57 +0800267 if (mInputInterceptor != null) {
268 mInputInterceptor.dispose();
269 mInputInterceptor = null;
270 }
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100271 }
Adrian Roos20e07892018-02-23 19:12:01 +0100272
273 public int getWidth() {
Adrian Roos73f21b12019-01-18 13:59:54 +0100274 return Math.max(0, mLayoutFrameGlobal.width());
Adrian Roos20e07892018-02-23 19:12:01 +0100275 }
276
277 public int getHeight() {
Adrian Roos73f21b12019-01-18 13:59:54 +0100278 return Math.max(0, mLayoutFrameGlobal.height());
Adrian Roos23df3a32018-03-15 15:41:13 +0100279 }
280
Adrian Roos73f21b12019-01-18 13:59:54 +0100281 /**
282 * Returns if the given {@code rect} overlaps with this letterbox piece.
283 * @param rect the area to check for overlap in global coordinates
284 */
Adrian Roos23df3a32018-03-15 15:41:13 +0100285 public boolean isOverlappingWith(Rect rect) {
Adrian Roos73f21b12019-01-18 13:59:54 +0100286 if (mLayoutFrameGlobal.isEmpty()) {
Adrian Roos23df3a32018-03-15 15:41:13 +0100287 return false;
288 }
Adrian Roos73f21b12019-01-18 13:59:54 +0100289 return Rect.intersects(rect, mLayoutFrameGlobal);
Adrian Roos23df3a32018-03-15 15:41:13 +0100290 }
291
292 public void applySurfaceChanges(SurfaceControl.Transaction t) {
Adrian Roos73f21b12019-01-18 13:59:54 +0100293 if (mSurfaceFrameRelative.equals(mLayoutFrameRelative)) {
Adrian Roos23df3a32018-03-15 15:41:13 +0100294 // Nothing changed.
295 return;
296 }
Adrian Roos73f21b12019-01-18 13:59:54 +0100297 mSurfaceFrameRelative.set(mLayoutFrameRelative);
298 if (!mSurfaceFrameRelative.isEmpty()) {
Adrian Roos23df3a32018-03-15 15:41:13 +0100299 if (mSurface == null) {
300 createSurface();
301 }
Adrian Roos73f21b12019-01-18 13:59:54 +0100302 t.setPosition(mSurface, mSurfaceFrameRelative.left, mSurfaceFrameRelative.top);
303 t.setWindowCrop(mSurface, mSurfaceFrameRelative.width(),
304 mSurfaceFrameRelative.height());
Adrian Roos23df3a32018-03-15 15:41:13 +0100305 t.show(mSurface);
306 } else if (mSurface != null) {
307 t.hide(mSurface);
308 }
Riddle Hsu69de5fb2019-02-23 01:19:50 +0800309 if (mSurface != null && mInputInterceptor != null) {
310 mInputInterceptor.updateTouchableRegion(mSurfaceFrameRelative);
311 t.setInputWindowInfo(mSurface, mInputInterceptor.mWindowHandle);
312 }
Adrian Roos23df3a32018-03-15 15:41:13 +0100313 }
314
315 public boolean needsApplySurfaceChanges() {
Adrian Roos73f21b12019-01-18 13:59:54 +0100316 return !mSurfaceFrameRelative.equals(mLayoutFrameRelative);
Adrian Roos20e07892018-02-23 19:12:01 +0100317 }
Adrian Roos4d18a2e2017-12-19 19:08:05 +0100318 }
319}