Analyze window resizing
This article based on
AOSP
9.0.
We click the shadow of freeform window, and drag it to resize window. And this article will analyze window resizing logic to show how Android window handling resizing actions.
Input flinger
The InputDispatcher.cpp
in input flinger will dispatch the input event to java part. For resizing, it will find
the window which touchable region contains the input event to dispatch the input event.
In InputDispatcher::findTouchedWindowTargetsLocked
, it uses InputWindowHandle.cpp
’s touchableRegionContainsPoint
to check whether the click point is in its touchable region based on its touchableRegion
field, a Region
instance.
The touchableRegion
field value of InputWindowHandle.cpp
is just a copy of InputWindowHandle.java
’s touchableRegion
.
Okay, who sets it?
In InputMonitor.addInputWindowHandle
, it will invoke WindowState.getTouchableRegion
to set the
InputWindowHandle.java
’s touchableRegion
:
int getTouchableRegion(Region region, int flags) {
final boolean modal = (flags & (FLAG_NOT_TOUCH_MODAL | FLAG_NOT_FOCUSABLE)) == 0;
if (modal && mAppToken != null) {
// Limit the outer touch to the activity stack region.
flags |= FLAG_NOT_TOUCH_MODAL;
// If this is a modal window we need to dismiss it if it's not full screen and the
// touch happens outside of the frame that displays the content. This means we
// need to intercept touches outside of that window. The dim layer user
// associated with the window (task or stack) will give us the good bounds, as
// they would be used to display the dim layer.
final Task task = getTask();
if (task != null) {
task.getDimBounds(mTmpRect);
} else {
getStack().getDimBounds(mTmpRect);
}
if (inFreeformWindowingMode()) {
// For freeform windows we the touch region to include the whole surface for the
// shadows.
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
final int delta = WindowManagerService.dipToPixel(
RESIZE_HANDLE_WIDTH_IN_DP, displayMetrics);
mTmpRect.inset(-delta, -delta);
}
region.set(mTmpRect);
cropRegionToStackBoundsIfNeeded(region);
} else {
// Not modal or full screen modal
getTouchableRegion(region);
}
return flags;
}
If the window is freeform, the getTouchableRegion
will add shadow size to window touchable region for resizing.
WMS and AMS
The InputDispatcher.cpp
’s dispatch will trigger android_view_InputEventReceiver.cpp
to dispatch input event to java
part. The android_view_InputEventReceiver.cpp
will invoke InputEventReceiver.java
’s dispatchInputEvent
to dispatch
input event. But there are many InputEventReceiver
implementation, such as PointerEventDispatcher.java
,
WindowInputEventReceiver
in ViewRootImpl.java
. Who will be invoked? The answer is all of them. Their
dispatchInputEvent
will be invoked, but they do different things orthogonal.
The WindowInputEventHandler
will dispatch the event to view as normal, and PointerEventDispatch
will check
the window outside event, and change the focused stack dynamically on the global level.
In WindowManagerService
constructor, we can see the following code:
if(mInputManager != null) {
final InputChannel inputChannel = mInputManager.monitorInput(TAG_WM);
mPointerEventDispatcher = inputChannel != null
? new PointerEventDispatcher(inputChannel) : null;
} else {
mPointerEventDispatcher = null;
}
The WMS will create InputChannel
for itself, the same as normal window, and use it as the channel to receive input event
from InputDispatcher
.
The PointerEventDispatcher
will invoke TaskTapPointerEventListener.onPointEvent
to process event, and
TaskTapPointerEventListener.onPointEvent
will invoke
WindowManagerService.mTaskPositioningController.handleTapOutsideTask
to process event.
void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
mHandler.post(() -> {
int taskId = -1;
synchronized (mService.mWindowMap) {
final Task task = displayContent.findTaskForResizePoint(x, y);
if (task != null) {
if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
task.preserveOrientationOnResize(), x, y)) {
return;
}
taskId = task.mTaskId;
} else {
taskId = displayContent.taskIdFromPoint(x, y);
}
}
if (taskId >= 0) {
try {
mActivityManager.setFocusedTask(taskId);
} catch (RemoteException e) {
}
}
});
}
Firstly, it will check whether the input point is in one task shadow region, if found, then it will invoke startPositioningLocked
to start resizing; if not, it will try to find the task, which content region contains the input point, and then set it to focused task.
When we click one freeform window to make it focused is processed in this method, and this method is also the start point to start resizing window. But there exists a problem, if we open a freeform window on the launcher, and then we click the launcher, the launcher will be focused, and bring back to front to cover up the freeform window. We can fix this problem, just add a minor patch.
private boolean startPositioningLocked(WindowState win, boolean resize,
boolean preserveOrientation, float startX, float startY) {
// check window, input channel and display
......
mTaskPositioner = TaskPositioner.create(mService);
mTaskPositioner.register(displayContent);
mInputMonitor.updateInputWindowsLw(true /*force*/);
// We need to grab the touch focus so that the touch events during the
// resizing/scrolling are not sent to the app. 'win' is the main window
// of the app, it may not have focus since there might be other windows
// on top (eg. a dialog window).
WindowState transferFocusFromWin = win;
if (mService.mCurrentFocus != null && mService.mCurrentFocus != win
&& mService.mCurrentFocus.mAppToken == win.mAppToken) {
transferFocusFromWin = mService.mCurrentFocus;
}
if (!mInputManager.transferTouchFocus(
transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
mTaskPositioner.unregister();
mTaskPositioner = null;
mInputMonitor.updateInputWindowsLw(true /*force*/);
return false;
}
mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
return true;
}
It will do some checking, and create TaskPositioner
instance. And then it will transfer focus from
current task to TaskPositioner.mServerChannel
by InputManager.transferTouchFocus
. The purpose
of transferring is to use another virtual target to consume drag input event, and notify the system
to change the size of window. If not, and the window process the coming drag input event, the system
will do new calculation for every input event, because after resizing, the touchable region has changed.
void register(DisplayContent displayContent) {
final Display display = displayContent.getDisplay();
if (mClientChannel != null) {
Slog.e(TAG, "Task positioner already registered");
return;
}
mDisplay = display;
mDisplay.getMetrics(mDisplayMetrics);
final InputChannel[] channels = InputChannel.openInputChannelPair(TAG);
mServerChannel = channels[0];
mClientChannel = channels[1];
mService.mInputManager.registerInputChannel(mServerChannel, null);
mInputEventReceiver = new WindowPositionerEventReceiver(
mClientChannel, mService.mAnimationHandler.getLooper(),
mService.mAnimator.getChoreographer());
mDragApplicationHandle = new InputApplicationHandle(null);
mDragApplicationHandle.name = TAG;
mDragApplicationHandle.dispatchingTimeoutNanos =
WindowManagerService.DEFAULT_INPUT_DISPATCHING_TIMEOUT_NANOS;
mDragWindowHandle = new InputWindowHandle(mDragApplicationHandle, null, null,
mDisplay.getDisplayId());
mDragWindowHandle.name = TAG;
mDragWindowHandle.inputChannel = mServerChannel;
mDragWindowHandle.layer = mService.getDragLayerLocked();
// Initialization of mDragWindowHandle
...
// The drag window cannot receive new touches.
mDragWindowHandle.touchableRegion.setEmpty();
// The drag window covers the entire display
mDragWindowHandle.frameLeft = 0;
mDragWindowHandle.frameTop = 0;
final Point p = new Point();
mDisplay.getRealSize(p);
mDragWindowHandle.frameRight = p.x;
mDragWindowHandle.frameBottom = p.y;
// Pause rotations before a drag.
mService.pauseRotationLocked();
mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
mDisplay.getRealSize(mMaxVisibleSize);
mDragEnded = false;
}
The register
method of TaskPositioner
will create itself InputChannel
to receive the input event from InputDispatcher
, and the mServerChannel
is the sever part of InputChannel
pair, which we saw before. And then it creates a WindowPositionEventReceiver(inherited from InputEventReceiver)
instance, to receive input event.
And then, it creates an InputWindowHandle
instance called mDragWindowHandle
as the input receiver handle.
It will set the size of mDragWindowHandle
to display size, and the type to TYPE_DRAG
. Also it will set the touchableRegion
of mDragWindowHandle
to empty to reject touch event. In other word, after transferring focus target to TaskPositioner
, the TaskPositioner
will create a full screen drag layer to receive and process the coming input event to resize window, until the resizing finished.
In InputMonitor.updateInputWindowLw(boolean)
, we can see the following code to add TaskPositioner
drag window handle to input window list:
final boolean inPositioning = mService.mTaskPositioningController.isPositioningLocked();
if (inPositioning) {
final InputWindowHandle dragWindowHandle =
mService.mTaskPositioningController.getDragWindowHandleLocked();
if (dragWindowHandle != null) {
addInputWindowHandle(dragWindowHandle);
} else {
Slog.e(TAG_WM,
"Repositioning is in progress but there is no drag window handle.");
}
}
After initializing, TaskPositioningController
will invoke the TaskPositioner.startDrag
to start drag.
void startDrag(WindowState win, boolean resize, boolean preserveOrientation, float startX,
float startY) {
mTask = win.getTask();
// Use the dim bounds, not the original task bounds. The cursor
// movement should be calculated relative to the visible bounds.
// Also, use the dim bounds of the task which accounts for
// multiple app windows. Don't use any bounds from win itself as it
// may not be the same size as the task.
mTask.getDimBounds(mTmpRect);
startDrag(resize, preserveOrientation, startX, startY, mTmpRect);
}
@VisibleForTesting
void startDrag(boolean resize, boolean preserveOrientation,
float startX, float startY, Rect startBounds) {
// Initialization
...
if (resize) {
if (startX < startBounds.left) {
mCtrlType |= CTRL_LEFT;
}
if (startX > startBounds.right) {
mCtrlType |= CTRL_RIGHT;
}
if (startY < startBounds.top) {
mCtrlType |= CTRL_TOP;
}
if (startY > startBounds.bottom) {
mCtrlType |= CTRL_BOTTOM;
}
mResizing = mCtrlType != CTRL_NONE;
}
// In case of !isDockedInEffect we are using the union of all task bounds. These might be
// made up out of multiple windows which are only partially overlapping. When that happens,
// the orientation from the window of interest to the entire stack might diverge. However
// for now we treat them as the same.
mStartOrientationWasLandscape = startBounds.width() >= startBounds.height();
mWindowOriginalBounds.set(startBounds);
// Notify the app that resizing has started, even though we haven't received any new
// bounds yet. This will guarantee that the app starts the backdrop renderer before
// configuration changes which could cause an activity restart.
if (mResizing) {
synchronized (mService.mWindowMap) {
notifyMoveLocked(startX, startY);
}
// Perform the resize on the WMS handler thread when we don't have the WMS lock held
// to ensure that we don't deadlock WMS and AMS. Note that WindowPositionerEventReceiver
// callbacks are delivered on the same handler so this initial resize is always
// guaranteed to happen before subsequent drag resizes.
mService.mH.post(() -> {
try {
mService.mActivityManager.resizeTask(
mTask.mTaskId, startBounds, RESIZE_MODE_USER_FORCED);
} catch (RemoteException e) {
}
});
}
// Make sure we always have valid drag bounds even if the drag ends before any move events
// have been handled.
mWindowDragBounds.set(startBounds);
}
The TaskPositioner
will use the current dim bounds of task as the initial bounds, and compare the initial input point with initial bounds to determine the resize direction. And then it will invoke notifyMoveLocked
to calculate the new window bounds, and notify the task the drag resizing state. Lastly, it will invoke AMS.resizeTask
to resize the task
finally.
The resizeDrag
is a pure calculating method, and it will calculate the distance between current input point and start drag point, and use it to calculate the new window bounds. Task.setDragResizing
will persist the current drag resizing state, and it will be used by WindowState
to determine whether it needs to notify the ViewRootImpl
to relayout.
AMS.resizeTask
notifies the TaskRecord
to change its size, and use its WindowContainerController
to notify the window container in WMS space, which will notify the layer in SurfaceFlinger
.
If TaskPositioner
receives the ACTION_UP
or ACTION_CANCEL
event, it will invoke AMS.resizeTask
to notify TaskRecord
the last time, and invoke TaskPositioningController.finishTaskPositioning
to finish drag resizing. Also it will notify the Task
the drag resizing finished.
The finishTaskPositioning
in TaskPositioningController
is just to unregister TaskPositioner
from system, and remove it from InputWindow list.
ViewRootImpl
From the AMS.resizeTask
, there is an invoking sequence to dispatch resizing event to WindowState
:
AMS.resizeTask
-> TaskRecord.resize
-> TaskWindowContainerController.resize
-> DisplayContent.layoutAndAssignWindowLayersIfNeeded
-> WindowSurfacePlacer.performSurfacePlacement
-> WindowSurfacePlacer.performSurfacePlacementLoop
-> RootWindowContainer.performSurfacePlacement
-> RootWindowContainer.handleResizingWIndows
-> WindowState.reportResized
-> WindowState.dispatchResized
.
private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
MergedConfiguration mergedConfiguration, boolean reportOrientation, int displayId,
DisplayCutout displayCutout)
throws RemoteException {
final boolean forceRelayout = isDragResizeChanged() || reportOrientation;
mClient.resized(frame, overscanInsets, contentInsets, visibleInsets, stableInsets, outsets,
reportDraw, mergedConfiguration, getBackdropFrame(frame), forceRelayout,
mPolicy.isNavBarForcedShownLw(this), displayId,
new DisplayCutout.ParcelableWrapper(displayCutout));
mDragResizingChangeReported = true;
}
The mClient
is W
in ViewRootImpl
, and it is passed to WMS
by Session.addToDisplay
in ViewRootImpl.addView
.
In WindowState.dispatchResized
, it will invok mClinet.resized
method to notify the resize event, actually W.resized
method. W.resized
will invoke ViewRootImpl.dispatchResized
method dispatch resized event continually.
private void dispatchResized(Rect frame, Rect overscanInsets, Rect contentInsets,
Rect visibleInsets, Rect stableInsets, Rect outsets, boolean reportDraw,
MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout,
boolean alwaysConsumeNavBar, int displayId,
DisplayCutout.ParcelableWrapper displayCutout) {
// Tell all listeners that we are resizing the window so that the chrome can get
// updated as fast as possible on a separate thread,
if (mDragResizing && mUseMTRenderer) {
boolean fullscreen = frame.equals(backDropFrame);
synchronized (mWindowCallbacks) {
for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) {
mWindowCallbacks.get(i).onWindowSizeIsChanging(backDropFrame, fullscreen,
visibleInsets, stableInsets);
}
}
}
Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWindow(frame);
mTranslator.translateRectInScreenToAppWindow(overscanInsets);
mTranslator.translateRectInScreenToAppWindow(contentInsets);
mTranslator.translateRectInScreenToAppWindow(visibleInsets);
}
// Inflate message content
........
mHandler.sendMessage(msg);
}
ViewRootImpl.dispatchResized
does two important things, the first is to notify the callback the window size changed event when using multi-thread renderer, and the second is to use Handler
to notify to itself relayout the layout (will invoke reportNextDraw()
, forceLayout(mView)
, requestLayout()
).
The second part will invoke ViewRootImpl.performTraversals
finally. It’s not our focus part, because it is a common process. We will focus on the first part, the callback’s onWindowSizeIsChanging
method. And there is only one important WindowCallbacks
we should focus, the DecorView
.
DecorView
The DecorView
’s onAttachedToWindow
will add it as WindowCallbacks
to ViewRootImpl
.
In DecorView
’s onWindowSizeIsChanging
callback, it will invoke BackdropFrameRenderer.setTargetRect
to set the
resize target information.
private BackdropFrameRenderer mBackdropFrameRenderer = null;
@Override
public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
Rect stableInsets) {
if (mBackdropFrameRenderer != null) {
mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
}
}
In ViewRootImpl.performTraversal
, it will invoke its startDragResizing
, and startDragResizing
will invoke DecorView.onWindowDragResizeStart
, when starting to drag resizing.
@Override
public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
Rect stableInsets, int resizeMode) {
if (mWindow.isDestroyed()) {
// If the owner's window is gone, we should not be able to come here anymore.
releaseThreadedRenderer();
return;
}
if (mBackdropFrameRenderer != null) {
return;
}
final ThreadedRenderer renderer = getThreadedRenderer();
if (renderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer = new BackdropFrameRenderer(.....);
updateElevation();
updateColorViews(null /* insets */, false);
}
mResizeMode = resizeMode;
getViewRootImpl().requestInvalidateRootRenderNode();
}
onWindowDragResizeStart
will initialize BackDropFrameRenderer
. So what is BackDropFrameRenderer
?
BackDropFrameRenderer
In short word, when window is resizing, we should add a background under its window to fill the part the showing window doesn’t cover to gain a acceptable visual effects.
public class BackdropFrameRenderer extends Thread implements Choreographer.FrameCallback {
public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor,
boolean fullscreen, Rect systemInsets, Rect stableInsets, int resizeMode) {
setName("ResizeFrame");
mRenderer = renderer;
onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable,
userCaptionBackgroundDrawable, statusBarColor, navigationBarColor);
// Create a render node for the content and frame backdrop
// which can be resized independently from the content.
mFrameAndBackdropNode = RenderNode.create("FrameAndBackdropNode", null);
mRenderer.addRenderNode(mFrameAndBackdropNode, true);
// Set the initial bounds and draw once so that we do not get a broken frame.
mTargetRect.set(initialBounds);
mFullscreen = fullscreen;
mOldFullscreen = fullscreen;
mSystemInsets.set(systemInsets);
mStableInsets.set(stableInsets);
mOldSystemInsets.set(systemInsets);
mOldStableInsets.set(stableInsets);
mResizeMode = resizeMode;
// Kick off our draw thread.
start();
}
// Other code
}
The BackdropFrameRenderer
is just a Thread
, keeps the ThreadedRenderer
instance of window, and creates its RenderNode
called FrameAndBackdropNode
. When window says it wants to use hardware accelerated by WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
, the ViewRootImpl will create a ThreadedRenderer
instance to it, and use it to render window content by skia
based on OpenGL
or directly OpenGL
to utilize hardware to speed up the rendering. And ThreadedRenderer
uses RenderNode
inner too. So BackdropFrameRenderer
’s mFrameAndBackdropNode
will use skia
based on OpenGL
or directly OpenGL
to draw content too to utilize hardware.
From above code, we know DecorView
will invoke BackdropFrameRenderer.setTargetRect
to update the target resizing window information, including window bounds, when window drag resizing. The BackdropFrameRenderer.setTargetRect
will trigger its redrawLocked
method to update the bounds of mFrameAndBackdropNode
. And when ViewRootImpl
starts to draw the content, it will invoke DecorView.onContentDrawn
to update its size, and will trigger mBackdropFrameRenderer.onContentDrawn
to update the mRenderer
’s bounds, if mBackdropFrameRenderer
is not null, in other word, the window is resizing. Its two route to update the mBackdropFrameNode
and mRenderer
bounds.
From the above constructor, we can see one line code:
mRenderer.addRenderNode(mFrameAndBackdropNode, true);// The true represents add the render node as the first node.
It will add mFrameAndBackdropNode
to the first node of mRenderer
. In the native level, they will think the first node of ThreadedRenderer
as the back drop render node. And use it to restrict the final drawn location of window content.
In AOSP
9.0, there is a file called FrameBuilder.cpp
does that thing for emulator, when emulator uses the OpenGL
directly:
void FrameBuilder::deferRenderNodeScene(const std::vector<sp<RenderNode> >& nodes,
const Rect& contentDrawBounds) {
if (nodes.size() < 1) return;
if (nodes.size() == 1) {
if (!nodes[0]->nothingToDraw()) {
deferRenderNode(*nodes[0]);
}
return;
}
// It there are multiple render nodes, they are laid out as follows:
// #0 - backdrop (content + caption)
// #1 - content (local bounds are at (0,0), will be translated and clipped to backdrop)
// #2 - additional overlay nodes
// Usually the backdrop cannot be seen since it will be entirely covered by the content. While
// resizing however it might become partially visible. The following render loop will crop the
// backdrop against the content and draw the remaining part of it. It will then draw the content
// cropped to the backdrop (since that indicates a shrinking of the window).
//
// Additional nodes will be drawn on top with no particular clipping semantics.
// Usually the contents bounds should be mContentDrawBounds - however - we will
// move it towards the fixed edge to give it a more stable appearance (for the moment).
// If there is no content bounds we ignore the layering as stated above and start with 2.
// Backdrop bounds in render target space
const Rect backdrop = nodeBounds(*nodes[0]);
// Bounds that content will fill in render target space (note content node bounds may be bigger)
Rect content(contentDrawBounds.getWidth(), contentDrawBounds.getHeight());
content.translate(backdrop.left, backdrop.top);
if (!content.contains(backdrop) && !nodes[0]->nothingToDraw()) {
// Content doesn't entirely overlap backdrop, so fill around content (right/bottom)
// Note: in the future, if content doesn't snap to backdrop's left/top, this may need to
// also fill left/top. Currently, both 2up and freeform position content at the top/left of
// the backdrop, so this isn't necessary.
if (content.right < backdrop.right) {
// draw backdrop to right side of content
deferRenderNode(0, 0,
Rect(content.right, backdrop.top, backdrop.right, backdrop.bottom),
*nodes[0]);
}
if (content.bottom < backdrop.bottom) {
// draw backdrop to bottom of content
// Note: bottom fill uses content left/right, to avoid overdrawing left/right fill
deferRenderNode(0, 0,
Rect(content.left, content.bottom, content.right, backdrop.bottom),
*nodes[0]);
}
}
if (!nodes[1]->nothingToDraw()) {
if (!backdrop.isEmpty()) {
// content node translation to catch up with backdrop
float dx = contentDrawBounds.left - backdrop.left;
float dy = contentDrawBounds.top - backdrop.top;
Rect contentLocalClip = backdrop;
contentLocalClip.translate(dx, dy);
deferRenderNode(-dx, -dy, contentLocalClip, *nodes[1]);
} else {
deferRenderNode(*nodes[1]);
}
}
// remaining overlay nodes, simply defer
for (size_t index = 2; index < nodes.size(); index++) {
if (!nodes[index]->nothingToDraw()) {
deferRenderNode(*nodes[index]);
}
}
}
In AOSP
9.0, when we drag to resize the window, the window will jump to the right bottom location when starting drag resizing, and restore to the correct location after dragging resizing. The reason is the mBackdropFrameNode
’s bounds and mRenderer
’s bounds use different coordinate system for passed value.
Summary
When resizing, the input flinger will use touchable region to find the window to receive the input event. In WindowState
, if the window is freeform, it will add shadow size to its touchable region. So when drag the shadow of freeform window to resize the window, the window will be selected as input target for starting dragging. The WMS will analyze the input event firstly to check whether the user is trying to resize the window, and if yes, it will create a new fullscreen input target to receive the coming input events and transfer current input focus to new input target. The new input target will calculate the commit input events location with origin location to calculate the new window size, and notify the WMS
and AMS
to update it. For better resizing effects, the DecorView
creates BackdropFrameRenderer
to draw the dim layer under window. After resizing, the WMS
will destroy the new input target.
In AOSP
9.0, the WMS
sets the new input target to input flinger directly, but in master
, the setting is done by SurfaceFlinger
. The WMS
will create a new surface for new input target, and set the input target to the new surface. And then notifying the SurfaceFlinger
to add the new input target to input flinger.