• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.wm.shell.windowdecor;
18 
19 import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
20 import static android.view.WindowInsets.Type.captionBar;
21 import static android.view.WindowInsets.Type.mandatorySystemGestures;
22 import static android.view.WindowInsets.Type.statusBars;
23 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
24 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
25 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
26 
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.app.ActivityManager.RunningTaskInfo;
30 import android.app.WindowConfiguration.WindowingMode;
31 import android.content.Context;
32 import android.content.res.Configuration;
33 import android.content.res.Resources;
34 import android.graphics.Color;
35 import android.graphics.PixelFormat;
36 import android.graphics.Point;
37 import android.graphics.Rect;
38 import android.graphics.Region;
39 import android.os.Binder;
40 import android.os.Trace;
41 import android.view.Display;
42 import android.view.InsetsSource;
43 import android.view.InsetsState;
44 import android.view.LayoutInflater;
45 import android.view.SurfaceControl;
46 import android.view.SurfaceControlViewHost;
47 import android.view.View;
48 import android.view.WindowManager;
49 import android.view.WindowlessWindowManager;
50 import android.window.DesktopExperienceFlags;
51 import android.window.DesktopModeFlags;
52 import android.window.SurfaceSyncGroup;
53 import android.window.TaskConstants;
54 import android.window.WindowContainerToken;
55 import android.window.WindowContainerTransaction;
56 
57 import com.android.internal.annotations.VisibleForTesting;
58 import com.android.wm.shell.ShellTaskOrganizer;
59 import com.android.wm.shell.common.DisplayController;
60 import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
61 import com.android.wm.shell.windowdecor.WindowDecoration.RelayoutParams.OccludingCaptionElement;
62 import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer;
63 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
64 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
65 import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
66 
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.List;
70 import java.util.Objects;
71 import java.util.function.Supplier;
72 
73 /**
74  * Manages a container surface and a windowless window to show window decoration. Responsible to
75  * update window decoration window state and layout parameters on task info changes and so that
76  * window decoration is in correct state and bounds.
77  *
78  * The container surface is a child of the task display area in the same display, so that window
79  * decorations can be drawn out of the task bounds and receive input events from out of the task
80  * bounds to support drag resizing.
81  *
82  * The windowless window that hosts window decoration is positioned in front of all activities, to
83  * allow the foreground activity to draw its own background behind window decorations, such as
84  * the window captions.
85  *
86  * @param <T> The type of the root view
87  */
88 public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
89         implements AutoCloseable {
90 
91     /**
92      * The Z-order of the caption surface.
93      * <p>
94      * We use {@link #mDecorationContainerSurface} to define input window for task resizing; by
95      * layering it in front of the caption surface, we can allow it to handle input
96      * prior to caption view itself, treating corner inputs as resize events rather than
97      * repositioning.
98      */
99     static final int CAPTION_LAYER_Z_ORDER = -1;
100     /**
101      * The Z-order of the task input sink in {@link DragPositioningCallback}.
102      * <p>
103      * This task input sink is used to prevent undesired dispatching of motion events out of task
104      * bounds; by layering it behind the caption surface, we allow captions to handle
105      * input events first.
106      */
107     static final int INPUT_SINK_Z_ORDER = -2;
108 
109     /**
110      * Invalid corner radius that signifies that corner radius should not be set.
111      */
112     static final int INVALID_CORNER_RADIUS = -1;
113     /**
114      * Invalid corner radius that signifies that shadow radius should not be set.
115      */
116     static final int INVALID_SHADOW_RADIUS = -1;
117 
118     /**
119      * System-wide context. Only used to create context with overridden configurations.
120      */
121     final Context mContext;
122     final @NonNull Context mUserContext;
123     final @NonNull DisplayController mDisplayController;
124     final @NonNull DesktopModeEventLogger mDesktopModeEventLogger;
125     final ShellTaskOrganizer mTaskOrganizer;
126     final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
127     final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
128     final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
129     final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
130     @NonNull private final WindowDecorViewHostSupplier<WindowDecorViewHost>
131             mWindowDecorViewHostSupplier;
132     private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
133             new DisplayController.OnDisplaysChangedListener() {
134                 @Override
135                 public void onDisplayAdded(int displayId) {
136                     if (mTaskInfo.displayId != displayId) {
137                         return;
138                     }
139 
140                     mDisplayController.removeDisplayWindowListener(this);
141                     relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion);
142                 }
143             };
144 
145     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
146     public RunningTaskInfo mTaskInfo;
147     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
148     public Context mDecorWindowContext;
149     int mLayoutResId;
150     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
151     public final SurfaceControl mTaskSurface;
152 
153     Display mDisplay;
154     SurfaceControl mDecorationContainerSurface;
155 
156     private WindowDecorViewHost mViewHost;
157     private Configuration mWindowDecorConfig;
158     TaskDragResizer mTaskDragResizer;
159     boolean mIsCaptionVisible;
160 
161     boolean mIsStatusBarVisible;
162     boolean mIsKeyguardVisibleAndOccluded;
163     boolean mHasGlobalFocus;
164     final Region mExclusionRegion = Region.obtain();
165 
166     /** The most recent set of insets applied to this window decoration. */
167     private WindowDecorationInsets mWindowDecorationInsets;
168     private final Binder mOwner = new Binder();
169     private final float[] mTmpColor = new float[3];
170 
WindowDecoration( Context context, @NonNull Context userContext, DisplayController displayController, ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, SurfaceControl taskSurface, @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier)171     WindowDecoration(
172             Context context,
173             @NonNull Context userContext,
174             DisplayController displayController,
175             ShellTaskOrganizer taskOrganizer,
176             RunningTaskInfo taskInfo,
177             SurfaceControl taskSurface,
178             @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier) {
179         this(context, userContext, displayController, taskOrganizer, taskInfo,
180                 taskSurface, SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
181                 WindowContainerTransaction::new, SurfaceControl::new,
182                 new SurfaceControlViewHostFactory() {}, windowDecorViewHostSupplier,
183                 new DesktopModeEventLogger());
184     }
185 
WindowDecoration( Context context, @NonNull Context userContext, @NonNull DisplayController displayController, ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, @NonNull SurfaceControl taskSurface, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, Supplier<SurfaceControl> surfaceControlSupplier, SurfaceControlViewHostFactory surfaceControlViewHostFactory, @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, @NonNull DesktopModeEventLogger desktopModeEventLogger )186     WindowDecoration(
187             Context context,
188             @NonNull Context userContext,
189             @NonNull DisplayController displayController,
190             ShellTaskOrganizer taskOrganizer,
191             RunningTaskInfo taskInfo,
192             @NonNull SurfaceControl taskSurface,
193             Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
194             Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
195             Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
196             Supplier<SurfaceControl> surfaceControlSupplier,
197             SurfaceControlViewHostFactory surfaceControlViewHostFactory,
198             @NonNull WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
199             @NonNull DesktopModeEventLogger desktopModeEventLogger
200     ) {
201         mContext = context;
202         mUserContext = userContext;
203         mDisplayController = displayController;
204         mTaskOrganizer = taskOrganizer;
205         mTaskInfo = taskInfo;
206         mTaskSurface = cloneSurfaceControl(taskSurface, surfaceControlSupplier);
207         mDesktopModeEventLogger = desktopModeEventLogger;
208         mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
209         mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
210         mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
211         mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
212         mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
213         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
214         final InsetsState insetsState = mDisplayController.getInsetsState(mTaskInfo.displayId);
215         mIsStatusBarVisible = insetsState != null
216                 && InsetsStateKt.isVisible(insetsState, statusBars());
217     }
218 
219     /**
220      * Gets the decoration's task leash.
221      * @return the decoration' task surface used to manipulate the task.
222      */
getLeash()223     public SurfaceControl getLeash() {
224         return mTaskSurface;
225     }
226 
227     /**
228      * Used by {@link WindowDecoration} to trigger a new relayout because the requirements for a
229      * relayout weren't satisfied are satisfied now.
230      *
231      * @param taskInfo The previous {@link RunningTaskInfo} passed into {@link #relayout} or the
232      *                 constructor.
233      * @param hasGlobalFocus Whether the task is focused
234      */
relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus, @NonNull Region displayExclusionRegion)235     abstract void relayout(RunningTaskInfo taskInfo, boolean hasGlobalFocus,
236             @NonNull Region displayExclusionRegion);
237 
238     /**
239      * Used by the {@link DragPositioningCallback} associated with the implementing class to
240      * enforce drags ending in a valid position. A null result means no restriction.
241      */
242     @Nullable
calculateValidDragArea()243     abstract Rect calculateValidDragArea();
244 
relayout(RelayoutParams params, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult)245     void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
246             SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
247             RelayoutResult<T> outResult) {
248         Trace.beginSection("WindowDecoration#relayout");
249         outResult.reset();
250         if (params.mRunningTaskInfo != null) {
251             mTaskInfo = params.mRunningTaskInfo;
252         }
253         mHasGlobalFocus = params.mHasGlobalFocus;
254         mExclusionRegion.set(params.mDisplayExclusionRegion);
255         final int oldLayoutResId = mLayoutResId;
256         mLayoutResId = params.mLayoutResId;
257 
258         if (!mTaskInfo.isVisible) {
259             releaseViews(wct);
260             if (params.mSetTaskVisibilityPositionAndCrop) {
261                 finishT.hide(mTaskSurface);
262             }
263             Trace.endSection(); // WindowDecoration#relayout
264             return;
265         }
266         Trace.beginSection("WindowDecoration#relayout-inflateIfNeeded");
267         inflateIfNeeded(params, wct, rootView, oldLayoutResId, outResult);
268         Trace.endSection();
269         final boolean hasCaptionView = outResult.mRootView != null;
270         if (!hasCaptionView) {
271             Trace.endSection(); // WindowDecoration#relayout
272             return;
273         }
274 
275         Trace.beginSection("WindowDecoration#relayout-updateCaptionVisibility");
276         updateCaptionVisibility(outResult.mRootView, params);
277         Trace.endSection();
278 
279         final Rect taskBounds = mTaskInfo.getConfiguration().windowConfiguration.getBounds();
280         outResult.mWidth = taskBounds.width();
281         outResult.mHeight = taskBounds.height();
282         outResult.mRootView.setTaskFocusState(mHasGlobalFocus);
283         final Resources resources = mDecorWindowContext.getResources();
284         outResult.mCaptionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId)
285                 + params.mCaptionTopPadding;
286         outResult.mCaptionWidth = params.mCaptionWidthId != Resources.ID_NULL
287                 ? loadDimensionPixelSize(resources, params.mCaptionWidthId) : taskBounds.width();
288         outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
289         outResult.mCaptionY = 0;
290         outResult.mCaptionTopPadding = params.mCaptionTopPadding;
291         if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
292             outResult.mCornerRadius = params.mCornerRadiusId == Resources.ID_NULL
293                     ? INVALID_CORNER_RADIUS : loadDimensionPixelSize(resources,
294                     params.mCornerRadiusId);
295             outResult.mShadowRadius = params.mShadowRadiusId == Resources.ID_NULL
296                     ? INVALID_SHADOW_RADIUS : loadDimensionPixelSize(resources,
297                     params.mShadowRadiusId);
298         }
299 
300         Trace.beginSection("relayout-createViewHostIfNeeded");
301         createViewHostIfNeeded(mDecorWindowContext, mDisplay);
302         Trace.endSection();
303 
304         Trace.beginSection("WindowDecoration#relayout-updateSurfacesAndInsets");
305         final SurfaceControl captionSurface = mViewHost.getSurfaceControl();
306         updateDecorationContainerSurface(startT, outResult);
307         updateCaptionContainerSurface(captionSurface, startT, outResult);
308         updateCaptionInsets(params, wct, outResult, taskBounds);
309         updateTaskSurface(params, startT, finishT, outResult);
310         Trace.endSection();
311 
312         Trace.beginSection("WindowDecoration#relayout-updateViewHost");
313         outResult.mRootView.setPadding(
314                 outResult.mRootView.getPaddingLeft(),
315                 params.mCaptionTopPadding,
316                 outResult.mRootView.getPaddingRight(),
317                 outResult.mRootView.getPaddingBottom());
318         final Rect localCaptionBounds = new Rect(
319                 outResult.mCaptionX,
320                 outResult.mCaptionY,
321                 outResult.mCaptionX + outResult.mCaptionWidth,
322                 outResult.mCaptionY + outResult.mCaptionHeight);
323         final Region touchableRegion = params.mLimitTouchRegionToSystemAreas
324                 ? calculateLimitedTouchableRegion(params, localCaptionBounds)
325                 : null;
326         updateViewHierarchy(params, outResult, startT, touchableRegion);
327         Trace.endSection();
328 
329         Trace.endSection(); // WindowDecoration#relayout
330     }
331 
createViewHostIfNeeded(@onNull Context context, @NonNull Display display)332     private void createViewHostIfNeeded(@NonNull Context context, @NonNull Display display) {
333         if (mViewHost == null) {
334             mViewHost = mWindowDecorViewHostSupplier.acquire(context, display);
335         }
336     }
337 
updateViewHierarchy(@onNull RelayoutParams params, @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT, @Nullable Region touchableRegion)338     private void updateViewHierarchy(@NonNull RelayoutParams params,
339             @NonNull RelayoutResult<T> outResult, @NonNull SurfaceControl.Transaction startT,
340             @Nullable Region touchableRegion) {
341         Trace.beginSection("WindowDecoration#updateViewHierarchy");
342         final WindowManager.LayoutParams lp =
343                 new WindowManager.LayoutParams(
344                         outResult.mCaptionWidth,
345                         outResult.mCaptionHeight,
346                         TYPE_APPLICATION,
347                         FLAG_NOT_FOCUSABLE,
348                         PixelFormat.TRANSPARENT);
349         lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
350         lp.setTrustedOverlay();
351         lp.inputFeatures = params.mInputFeatures;
352         if (params.mAsyncViewHost) {
353             if (params.mApplyStartTransactionOnDraw) {
354                 throw new IllegalArgumentException("Cannot use sync draw tx with async relayout");
355             }
356             mViewHost.updateViewAsync(outResult.mRootView, lp, mTaskInfo.configuration,
357                     touchableRegion);
358         } else {
359             mViewHost.updateView(outResult.mRootView, lp, mTaskInfo.configuration,
360                     touchableRegion, params.mApplyStartTransactionOnDraw ? startT : null);
361         }
362         Trace.endSection();
363     }
364 
inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct, T rootView, int oldLayoutResId, RelayoutResult<T> outResult)365     private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct,
366             T rootView, int oldLayoutResId, RelayoutResult<T> outResult) {
367         if (rootView == null && params.mLayoutResId == 0) {
368             throw new IllegalArgumentException("layoutResId and rootView can't both be invalid.");
369         }
370 
371         outResult.mRootView = rootView;
372         final boolean fontScaleChanged = mWindowDecorConfig != null
373                 && mWindowDecorConfig.fontScale != mTaskInfo.configuration.fontScale;
374         final boolean localeListChanged = mWindowDecorConfig != null
375                 && !mWindowDecorConfig.getLocales()
376                     .equals(mTaskInfo.getConfiguration().getLocales());
377         final int oldDensityDpi = mWindowDecorConfig != null
378                 ? mWindowDecorConfig.densityDpi : DENSITY_DPI_UNDEFINED;
379         final int oldNightMode =  mWindowDecorConfig != null
380                 ? (mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
381                 : Configuration.UI_MODE_NIGHT_UNDEFINED;
382         mWindowDecorConfig = params.mWindowDecorConfig != null ? params.mWindowDecorConfig
383                 : mTaskInfo.getConfiguration();
384         final int newDensityDpi = mWindowDecorConfig.densityDpi;
385         final int newNightMode =  mWindowDecorConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK;
386         if (oldDensityDpi != newDensityDpi
387                 || mDisplay == null
388                 || mDisplay.getDisplayId() != mTaskInfo.displayId
389                 || oldLayoutResId != mLayoutResId
390                 || oldNightMode != newNightMode
391                 || mDecorWindowContext == null
392                 || fontScaleChanged
393                 || localeListChanged) {
394             releaseViews(wct);
395 
396             if (!obtainDisplayOrRegisterListener()) {
397                 outResult.mRootView = null;
398                 return;
399             }
400             mDecorWindowContext = mContext.createConfigurationContext(mWindowDecorConfig);
401             mDecorWindowContext.setTheme(mContext.getThemeResId());
402             if (params.mLayoutResId != 0) {
403                 outResult.mRootView = inflateLayout(mDecorWindowContext, params.mLayoutResId);
404             }
405         }
406 
407         if (outResult.mRootView == null) {
408             outResult.mRootView = inflateLayout(mDecorWindowContext, params.mLayoutResId);
409         }
410     }
411 
412     @VisibleForTesting
inflateLayout(Context context, int layoutResId)413     T inflateLayout(Context context, int layoutResId) {
414         return (T) LayoutInflater.from(context).inflate(layoutResId, null);
415     }
416 
updateDecorationContainerSurface( SurfaceControl.Transaction startT, RelayoutResult<T> outResult)417     private void updateDecorationContainerSurface(
418             SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
419         if (mDecorationContainerSurface == null) {
420             final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
421             mDecorationContainerSurface = builder
422                     .setName("Decor container of Task=" + mTaskInfo.taskId)
423                     .setContainerLayer()
424                     .setParent(mTaskSurface)
425                     .setCallsite("WindowDecoration.updateDecorationContainerSurface")
426                     .build();
427 
428             startT.setTrustedOverlay(mDecorationContainerSurface, true)
429                     .setLayer(mDecorationContainerSurface,
430                             TaskConstants.TASK_CHILD_LAYER_WINDOW_DECORATIONS);
431         }
432 
433         startT.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight)
434                 .show(mDecorationContainerSurface);
435     }
436 
updateCaptionContainerSurface(@onNull SurfaceControl captionSurface, SurfaceControl.Transaction startT, RelayoutResult<T> outResult)437     private void updateCaptionContainerSurface(@NonNull SurfaceControl captionSurface,
438             SurfaceControl.Transaction startT, RelayoutResult<T> outResult) {
439         startT.reparent(captionSurface, mDecorationContainerSurface)
440                 .setWindowCrop(captionSurface, outResult.mCaptionWidth, outResult.mCaptionHeight)
441                 .setPosition(captionSurface, outResult.mCaptionX, 0 /* y */)
442                 .setLayer(captionSurface, CAPTION_LAYER_Z_ORDER)
443                 .show(captionSurface);
444     }
445 
updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct, RelayoutResult<T> outResult, Rect taskBounds)446     private void updateCaptionInsets(RelayoutParams params, WindowContainerTransaction wct,
447             RelayoutResult<T> outResult, Rect taskBounds) {
448         if (!mIsCaptionVisible || !params.mIsInsetSource) {
449             if (mWindowDecorationInsets != null) {
450                 mWindowDecorationInsets.remove(wct);
451                 mWindowDecorationInsets = null;
452             }
453             return;
454         }
455         // Caption inset is the full width of the task with the |captionHeight| and
456         // positioned at the top of the task bounds, also in absolute coordinates.
457         // So just reuse the task bounds and adjust the bottom coordinate.
458         final Rect captionInsetsRect = new Rect(taskBounds);
459         captionInsetsRect.bottom = captionInsetsRect.top + outResult.mCaptionHeight;
460 
461         // Caption bounding rectangles: these are optional, and are used to present finer
462         // insets than traditional |Insets| to apps about where their content is occluded.
463         // These are also in absolute coordinates.
464         final Rect[] boundingRects;
465         final int numOfElements = params.mOccludingCaptionElements.size();
466         if (numOfElements == 0) {
467             boundingRects = null;
468         } else {
469             // The customizable region can at most be equal to the caption bar.
470             if (params.hasInputFeatureSpy()) {
471                 outResult.mCustomizableCaptionRegion.set(captionInsetsRect);
472             }
473             final Resources resources = mDecorWindowContext.getResources();
474             boundingRects = new Rect[numOfElements];
475             for (int i = 0; i < numOfElements; i++) {
476                 final OccludingCaptionElement element =
477                         params.mOccludingCaptionElements.get(i);
478                 final int elementWidthPx =
479                         resources.getDimensionPixelSize(element.mWidthResId);
480                 boundingRects[i] =
481                         calculateBoundingRectLocal(element, elementWidthPx, captionInsetsRect);
482                 // Subtract the regions used by the caption elements, the rest is
483                 // customizable.
484                 if (params.hasInputFeatureSpy()) {
485                     outResult.mCustomizableCaptionRegion.op(boundingRects[i],
486                             Region.Op.DIFFERENCE);
487                 }
488             }
489         }
490 
491         final WindowDecorationInsets newInsets = new WindowDecorationInsets(
492                 mTaskInfo.token, mOwner, captionInsetsRect, taskBounds, boundingRects,
493                 params.mInsetSourceFlags, params.mIsInsetSource, params.mShouldSetAppBounds);
494         if (!newInsets.equals(mWindowDecorationInsets)) {
495             // Add or update this caption as an insets source.
496             mWindowDecorationInsets = newInsets;
497             mWindowDecorationInsets.update(wct);
498         }
499     }
500 
updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, RelayoutResult<T> outResult)501     private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT,
502             SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) {
503         if (params.mSetTaskVisibilityPositionAndCrop) {
504             final Point taskPosition = mTaskInfo.positionInParent;
505             startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight);
506             finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight)
507                     .setPosition(mTaskSurface, taskPosition.x, taskPosition.y);
508         }
509 
510         if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
511             if (outResult.mShadowRadius != INVALID_SHADOW_RADIUS) {
512                 startT.setShadowRadius(mTaskSurface, outResult.mShadowRadius);
513                 finishT.setShadowRadius(mTaskSurface, outResult.mShadowRadius);
514             }
515         } else {
516             if (params.mShadowRadius != INVALID_SHADOW_RADIUS) {
517                 startT.setShadowRadius(mTaskSurface, params.mShadowRadius);
518                 finishT.setShadowRadius(mTaskSurface, params.mShadowRadius);
519             }
520         }
521 
522         if (params.mSetTaskVisibilityPositionAndCrop) {
523             startT.show(mTaskSurface);
524         }
525 
526         if (params.mShouldSetBackground) {
527             final int backgroundColorInt = mTaskInfo.taskDescription != null
528                     ? mTaskInfo.taskDescription.getBackgroundColor() : Color.BLACK;
529             mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
530             mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
531             mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
532             startT.setColor(mTaskSurface, mTmpColor);
533         } else {
534             startT.unsetColor(mTaskSurface);
535         }
536 
537         if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
538             if (outResult.mCornerRadius != INVALID_CORNER_RADIUS) {
539                 startT.setCornerRadius(mTaskSurface, outResult.mCornerRadius);
540                 finishT.setCornerRadius(mTaskSurface, outResult.mCornerRadius);
541             }
542         } else {
543             if (params.mCornerRadius != INVALID_CORNER_RADIUS) {
544                 startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
545                 finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
546             }
547         }
548     }
549 
550     @NonNull
calculateLimitedTouchableRegion( RelayoutParams params, @NonNull Rect localCaptionBounds)551     private Region calculateLimitedTouchableRegion(
552             RelayoutParams params,
553             @NonNull Rect localCaptionBounds) {
554         // Make caption bounds relative to display to align with exclusion region.
555         final Point positionInParent = params.mRunningTaskInfo.positionInParent;
556         final Rect captionBoundsInDisplay = new Rect(localCaptionBounds);
557         captionBoundsInDisplay.offsetTo(positionInParent.x, positionInParent.y);
558 
559         final Region boundingRects = calculateBoundingRectsRegion(params, captionBoundsInDisplay);
560 
561         final Region customizedRegion = Region.obtain();
562         customizedRegion.set(captionBoundsInDisplay);
563         customizedRegion.op(boundingRects, Region.Op.DIFFERENCE);
564         customizedRegion.op(params.mDisplayExclusionRegion, Region.Op.INTERSECT);
565 
566         final Region touchableRegion = Region.obtain();
567         touchableRegion.set(captionBoundsInDisplay);
568         touchableRegion.op(customizedRegion, Region.Op.DIFFERENCE);
569         // Return resulting region back to window coordinates.
570         touchableRegion.translate(-positionInParent.x, -positionInParent.y);
571 
572         boundingRects.recycle();
573         customizedRegion.recycle();
574         return touchableRegion;
575     }
576 
577     @NonNull
calculateBoundingRectsRegion( @onNull RelayoutParams params, @NonNull Rect captionBoundsInDisplay)578     private Region calculateBoundingRectsRegion(
579             @NonNull RelayoutParams params,
580             @NonNull Rect captionBoundsInDisplay) {
581         final int numOfElements = params.mOccludingCaptionElements.size();
582         final Region region = Region.obtain();
583         if (numOfElements == 0) {
584             // The entire caption is a bounding rect.
585             region.set(captionBoundsInDisplay);
586             return region;
587         }
588         final Resources resources = mDecorWindowContext.getResources();
589         for (int i = 0; i < numOfElements; i++) {
590             final OccludingCaptionElement element = params.mOccludingCaptionElements.get(i);
591             final int elementWidthPx = resources.getDimensionPixelSize(element.mWidthResId);
592             final Rect boundingRect = calculateBoundingRectLocal(element, elementWidthPx,
593                     captionBoundsInDisplay);
594             // Bounding rect is initially calculated relative to the caption, so offset it to make
595             // it relative to the display.
596             boundingRect.offset(captionBoundsInDisplay.left, captionBoundsInDisplay.top);
597             region.union(boundingRect);
598         }
599         return region;
600     }
601 
calculateBoundingRectLocal(@onNull OccludingCaptionElement element, int elementWidthPx, @NonNull Rect captionRect)602     private Rect calculateBoundingRectLocal(@NonNull OccludingCaptionElement element,
603             int elementWidthPx, @NonNull Rect captionRect) {
604         final boolean isRtl =
605                 mDecorWindowContext.getResources().getConfiguration().getLayoutDirection()
606                         == View.LAYOUT_DIRECTION_RTL;
607         switch (element.mAlignment) {
608             case START -> {
609                 if (isRtl) {
610                     return new Rect(captionRect.width() - elementWidthPx, 0,
611                             captionRect.width(), captionRect.height());
612                 } else {
613                     return new Rect(0, 0, elementWidthPx, captionRect.height());
614                 }
615             }
616             case END -> {
617                 if (isRtl) {
618                     return new Rect(0, 0, elementWidthPx, captionRect.height());
619                 } else {
620                     return new Rect(captionRect.width() - elementWidthPx, 0,
621                             captionRect.width(), captionRect.height());
622                 }
623             }
624         }
625         throw new IllegalArgumentException("Unexpected alignment " + element.mAlignment);
626     }
627 
onKeyguardStateChanged(boolean visible, boolean occluded)628     void onKeyguardStateChanged(boolean visible, boolean occluded) {
629         final boolean prevVisAndOccluded = mIsKeyguardVisibleAndOccluded;
630         mIsKeyguardVisibleAndOccluded = visible && occluded;
631         final boolean changed = prevVisAndOccluded != mIsKeyguardVisibleAndOccluded;
632         if (changed) {
633             relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion);
634         }
635     }
636 
onInsetsStateChanged(@onNull InsetsState insetsState)637     void onInsetsStateChanged(@NonNull InsetsState insetsState) {
638         final boolean prevStatusBarVisibility = mIsStatusBarVisible;
639         mIsStatusBarVisible = InsetsStateKt.isVisible(insetsState, statusBars());
640         final boolean changed = prevStatusBarVisibility != mIsStatusBarVisible;
641 
642         if (changed) {
643             relayout(mTaskInfo, mHasGlobalFocus, mExclusionRegion);
644         }
645     }
646 
onExclusionRegionChanged(@onNull Region exclusionRegion)647     void onExclusionRegionChanged(@NonNull Region exclusionRegion) {
648         relayout(mTaskInfo, mHasGlobalFocus, exclusionRegion);
649     }
650 
651     /**
652      * Update caption visibility state and views.
653      */
updateCaptionVisibility(View rootView, @NonNull RelayoutParams params)654     private void updateCaptionVisibility(View rootView, @NonNull RelayoutParams params) {
655         mIsCaptionVisible = params.mIsCaptionVisible;
656         if (!DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
657             setCaptionVisibility(rootView, mIsCaptionVisible);
658         }
659     }
660 
setTaskDragResizer(TaskDragResizer taskDragResizer)661     void setTaskDragResizer(TaskDragResizer taskDragResizer) {
662         mTaskDragResizer = taskDragResizer;
663     }
664 
665     // TODO(b/346441962): Move these three methods closer to implementing or View-level classes to
666     //  keep implementation details more encapsulated.
setCaptionVisibility(View rootView, boolean visible)667     private void setCaptionVisibility(View rootView, boolean visible) {
668         if (rootView == null) {
669             return;
670         }
671         final int v = visible ? View.VISIBLE : View.GONE;
672         final View captionView = rootView.findViewById(getCaptionViewId());
673         captionView.setVisibility(v);
674     }
675 
getCaptionHeightId(@indowingMode int windowingMode)676     int getCaptionHeightId(@WindowingMode int windowingMode) {
677         return Resources.ID_NULL;
678     }
679 
getCaptionViewId()680     int getCaptionViewId() {
681         return Resources.ID_NULL;
682     }
683 
684     /**
685      * Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or
686      * registers {@link #mOnDisplaysChangedListener} if it doesn't.
687      *
688      * @return {@code true} if the {@link Display} instance exists; or {@code false} otherwise
689      */
obtainDisplayOrRegisterListener()690     private boolean obtainDisplayOrRegisterListener() {
691         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
692         if (mDisplay == null) {
693             mDisplayController.addDisplayWindowListener(mOnDisplaysChangedListener);
694             return false;
695         }
696         return true;
697     }
698 
releaseViews(WindowContainerTransaction wct)699     void releaseViews(WindowContainerTransaction wct) {
700         final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
701         boolean released = false;
702         if (mViewHost != null) {
703             mWindowDecorViewHostSupplier.release(mViewHost, t);
704             mViewHost = null;
705             released = true;
706         }
707 
708         if (mDecorationContainerSurface != null) {
709             t.remove(mDecorationContainerSurface);
710             mDecorationContainerSurface = null;
711             released = true;
712         }
713 
714         if (released) {
715             t.apply();
716         }
717 
718         if (mWindowDecorationInsets != null) {
719             mWindowDecorationInsets.remove(wct);
720             mWindowDecorationInsets = null;
721         }
722     }
723 
724     @Override
close()725     public void close() {
726         Trace.beginSection("WindowDecoration#close");
727         mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
728         if (mTaskDragResizer != null) {
729             mTaskDragResizer.close();
730         }
731         final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
732         releaseViews(wct);
733         mTaskOrganizer.applyTransaction(wct);
734         mTaskSurface.release();
735         Trace.endSection();
736     }
737 
loadDimensionPixelSize(Resources resources, int resourceId)738     static int loadDimensionPixelSize(Resources resources, int resourceId) {
739         if (resourceId == Resources.ID_NULL) {
740             return 0;
741         }
742         return resources.getDimensionPixelSize(resourceId);
743     }
744 
loadDimension(Resources resources, int resourceId)745     static float loadDimension(Resources resources, int resourceId) {
746         if (resourceId == Resources.ID_NULL) {
747             return 0;
748         }
749         return resources.getDimension(resourceId);
750     }
751 
cloneSurfaceControl(SurfaceControl sc, Supplier<SurfaceControl> surfaceControlSupplier)752     private static SurfaceControl cloneSurfaceControl(SurfaceControl sc,
753             Supplier<SurfaceControl> surfaceControlSupplier) {
754         final SurfaceControl copy = surfaceControlSupplier.get();
755         copy.copyFrom(sc, "WindowDecoration");
756         return copy;
757     }
758 
759     /**
760      * Create a window associated with this WindowDecoration.
761      * Note that subclass must dispose of this when the task is hidden/closed.
762      *
763      * @param v            View to attach to the window
764      * @param t            the transaction to apply
765      * @param xPos         x position of new window
766      * @param yPos         y position of new window
767      * @param width        width of new window
768      * @param height       height of new window
769      * @return the {@link AdditionalViewHostViewContainer} that was added.
770      */
addWindow(@onNull View v, @NonNull String namePrefix, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height)771     AdditionalViewHostViewContainer addWindow(@NonNull View v, @NonNull String namePrefix,
772             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceSyncGroup ssg,
773             int xPos, int yPos, int width, int height) {
774         final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
775         SurfaceControl windowSurfaceControl = builder
776                 .setName(namePrefix + " of Task=" + mTaskInfo.taskId)
777                 .setContainerLayer()
778                 .setParent(mDecorationContainerSurface)
779                 .setCallsite("WindowDecoration.addWindow")
780                 .build();
781         t.setPosition(windowSurfaceControl, xPos, yPos)
782                 .setWindowCrop(windowSurfaceControl, width, height)
783                 .show(windowSurfaceControl);
784         final WindowManager.LayoutParams lp =
785                 new WindowManager.LayoutParams(
786                         width,
787                         height,
788                         TYPE_APPLICATION,
789                         FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_TOUCH,
790                         PixelFormat.TRANSPARENT);
791         lp.setTitle("Additional window of Task=" + mTaskInfo.taskId);
792         lp.setTrustedOverlay();
793         WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration,
794                 windowSurfaceControl, null /* hostInputToken */);
795         SurfaceControlViewHost viewHost = mSurfaceControlViewHostFactory
796                 .create(mDecorWindowContext, mDisplay, windowManager);
797         ssg.add(viewHost.getSurfacePackage(), () -> viewHost.setView(v, lp));
798         return new AdditionalViewHostViewContainer(windowSurfaceControl, viewHost,
799                 mSurfaceControlTransactionSupplier);
800     }
801 
802     /**
803      * Create a window associated with this WindowDecoration.
804      * Note that subclass must dispose of this when the task is hidden/closed.
805      *
806      * @param layoutId     layout to make the window from
807      * @param t            the transaction to apply
808      * @param xPos         x position of new window
809      * @param yPos         y position of new window
810      * @param width        width of new window
811      * @param height       height of new window
812      * @return the {@link AdditionalViewHostViewContainer} that was added.
813      */
addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t, SurfaceSyncGroup ssg, int xPos, int yPos, int width, int height)814     AdditionalViewHostViewContainer addWindow(int layoutId, String namePrefix,
815             SurfaceControl.Transaction t, SurfaceSyncGroup ssg, int xPos, int yPos,
816             int width, int height) {
817         final View v = LayoutInflater.from(mDecorWindowContext).inflate(layoutId, null);
818         return addWindow(v, namePrefix, t, ssg, xPos, yPos, width, height);
819     }
820 
821     /**
822      * Adds caption inset source to a WCT
823      */
addCaptionInset(WindowContainerTransaction wct)824     public void addCaptionInset(WindowContainerTransaction wct) {
825         final int captionHeightId = getCaptionHeightId(mTaskInfo.getWindowingMode());
826         if (captionHeightId == Resources.ID_NULL || !mIsCaptionVisible) {
827             return;
828         }
829 
830         final int captionHeight = loadDimensionPixelSize(mContext.getResources(), captionHeightId);
831         final Rect captionInsets = new Rect(0, 0, 0, captionHeight);
832         final WindowDecorationInsets newInsets = new WindowDecorationInsets(mTaskInfo.token,
833                 mOwner, captionInsets, null  /* taskFrame */,  null /* boundingRects */,
834                 0 /* flags */, true /* shouldAddCaptionInset */, false /* excludedFromAppBounds */);
835         if (!newInsets.equals(mWindowDecorationInsets)) {
836             mWindowDecorationInsets = newInsets;
837             mWindowDecorationInsets.update(wct);
838         }
839     }
840 
841     static class RelayoutParams {
842         RunningTaskInfo mRunningTaskInfo;
843         int mLayoutResId;
844         int mCaptionHeightId;
845         int mCaptionWidthId;
846         final List<OccludingCaptionElement> mOccludingCaptionElements = new ArrayList<>();
847         boolean mLimitTouchRegionToSystemAreas;
848         int mInputFeatures;
849         boolean mIsInsetSource = true;
850         @InsetsSource.Flags int mInsetSourceFlags;
851         final Region mDisplayExclusionRegion = Region.obtain();
852 
853         @Deprecated
854         int mShadowRadius = INVALID_SHADOW_RADIUS;
855         @Deprecated
856         int mCornerRadius = INVALID_CORNER_RADIUS;
857 
858         int mShadowRadiusId = Resources.ID_NULL;
859         int mCornerRadiusId = Resources.ID_NULL;
860 
861         int mCaptionTopPadding;
862         boolean mIsCaptionVisible;
863 
864         Configuration mWindowDecorConfig;
865         boolean mAsyncViewHost;
866 
867         boolean mApplyStartTransactionOnDraw;
868         boolean mSetTaskVisibilityPositionAndCrop;
869         boolean mHasGlobalFocus;
870         boolean mShouldSetAppBounds;
871         boolean mShouldSetBackground;
872 
reset()873         void reset() {
874             mLayoutResId = Resources.ID_NULL;
875             mCaptionHeightId = Resources.ID_NULL;
876             mCaptionWidthId = Resources.ID_NULL;
877             mOccludingCaptionElements.clear();
878             mLimitTouchRegionToSystemAreas = false;
879             mInputFeatures = 0;
880             mIsInsetSource = true;
881             mInsetSourceFlags = 0;
882             mDisplayExclusionRegion.setEmpty();
883             if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
884                 mShadowRadiusId = Resources.ID_NULL;
885                 mCornerRadiusId = Resources.ID_NULL;
886             } else {
887                 mShadowRadius = INVALID_SHADOW_RADIUS;
888                 mCornerRadius = INVALID_SHADOW_RADIUS;
889             }
890 
891             mCaptionTopPadding = 0;
892             mIsCaptionVisible = false;
893 
894             mApplyStartTransactionOnDraw = false;
895             mSetTaskVisibilityPositionAndCrop = false;
896             mWindowDecorConfig = null;
897             mAsyncViewHost = false;
898             mHasGlobalFocus = false;
899             mShouldSetAppBounds = false;
900             mShouldSetBackground = false;
901         }
902 
hasInputFeatureSpy()903         boolean hasInputFeatureSpy() {
904             return (mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_SPY) != 0;
905         }
906 
907         /**
908          * Describes elements within the caption bar that could occlude app content, and should be
909          * sent as bounding rectangles to the insets system.
910          */
911         static class OccludingCaptionElement {
912             int mWidthResId;
913             Alignment mAlignment;
914 
915             enum Alignment {
916                 START, END
917             }
918         }
919     }
920 
921     static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
922         int mCaptionHeight;
923         int mCaptionWidth;
924         int mCaptionX;
925         int mCaptionY;
926         int mCaptionTopPadding;
927         final Region mCustomizableCaptionRegion = Region.obtain();
928         int mWidth;
929         int mHeight;
930         T mRootView;
931         int mCornerRadius;
932         int mShadowRadius;
933 
reset()934         void reset() {
935             mWidth = 0;
936             mHeight = 0;
937             mCaptionHeight = 0;
938             mCaptionWidth = 0;
939             mCaptionX = 0;
940             mCaptionY = 0;
941             mCaptionTopPadding = 0;
942             mCustomizableCaptionRegion.setEmpty();
943             mRootView = null;
944             if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
945                 mCornerRadius = INVALID_CORNER_RADIUS;
946                 mShadowRadius = INVALID_SHADOW_RADIUS;
947             }
948         }
949     }
950 
951     private static class CaptionWindowlessWindowManager extends WindowlessWindowManager {
CaptionWindowlessWindowManager( @onNull Configuration configuration, @NonNull SurfaceControl rootSurface)952         CaptionWindowlessWindowManager(
953                 @NonNull Configuration configuration,
954                 @NonNull SurfaceControl rootSurface) {
955             super(configuration, rootSurface, /* hostInputToken= */ null);
956         }
957 
958         /** Set the view host's touchable region. */
setTouchRegion(@onNull SurfaceControlViewHost viewHost, @NonNull Region region)959         void setTouchRegion(@NonNull SurfaceControlViewHost viewHost, @NonNull Region region) {
960             setTouchRegion(viewHost.getWindowToken().asBinder(), region);
961         }
962     }
963 
964     public interface SurfaceControlViewHostFactory {
create(Context c, Display d, WindowlessWindowManager wmm)965         default SurfaceControlViewHost create(Context c, Display d, WindowlessWindowManager wmm) {
966             return new SurfaceControlViewHost(c, d, wmm, "WindowDecoration");
967         }
create(Context c, Display d, WindowlessWindowManager wmm, String callsite)968         default SurfaceControlViewHost create(Context c, Display d,
969                 WindowlessWindowManager wmm, String callsite) {
970             return new SurfaceControlViewHost(c, d, wmm, callsite);
971         }
972     }
973 
974     private static class WindowDecorationInsets {
975         private static final int INDEX = 0;
976         private final WindowContainerToken mToken;
977         private final Binder mOwner;
978         private final Rect mFrame;
979         private final Rect mTaskFrame;
980         private final Rect[] mBoundingRects;
981         private final @InsetsSource.Flags int mFlags;
982         private final boolean mShouldAddCaptionInset;
983         private final boolean mExcludedFromAppBounds;
984 
WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame, Rect taskFrame, Rect[] boundingRects, @InsetsSource.Flags int flags, boolean shouldAddCaptionInset, boolean excludedFromAppBounds)985         private WindowDecorationInsets(WindowContainerToken token, Binder owner, Rect frame,
986                 Rect taskFrame, Rect[] boundingRects, @InsetsSource.Flags int flags,
987                 boolean shouldAddCaptionInset, boolean excludedFromAppBounds) {
988             mToken = token;
989             mOwner = owner;
990             mFrame = frame;
991             mTaskFrame = taskFrame;
992             mBoundingRects = boundingRects;
993             mFlags = flags;
994             mShouldAddCaptionInset = shouldAddCaptionInset;
995             mExcludedFromAppBounds = excludedFromAppBounds;
996         }
997 
update(WindowContainerTransaction wct)998         void update(WindowContainerTransaction wct) {
999             if (mShouldAddCaptionInset) {
1000                 wct.addInsetsSource(mToken, mOwner, INDEX, captionBar(), mFrame, mBoundingRects,
1001                         mFlags);
1002                 wct.addInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures(), mFrame,
1003                         mBoundingRects, 0 /* flags */);
1004                 if (mExcludedFromAppBounds) {
1005                     final Rect appBounds = new Rect(mTaskFrame);
1006                     appBounds.top += mFrame.height();
1007                     wct.setAppBounds(mToken, appBounds);
1008                 }
1009             }
1010         }
1011 
remove(WindowContainerTransaction wct)1012         void remove(WindowContainerTransaction wct) {
1013             wct.removeInsetsSource(mToken, mOwner, INDEX, captionBar());
1014             wct.removeInsetsSource(mToken, mOwner, INDEX, mandatorySystemGestures());
1015             if (mExcludedFromAppBounds) {
1016                 wct.setAppBounds(mToken, new Rect());
1017             }
1018         }
1019 
1020         @Override
equals(Object o)1021         public boolean equals(Object o) {
1022             if (this == o) return true;
1023             if (!(o instanceof WindowDecoration.WindowDecorationInsets that)) return false;
1024             return Objects.equals(mToken, that.mToken) && Objects.equals(mOwner,
1025                     that.mOwner) && Objects.equals(mFrame, that.mFrame)
1026                     && Objects.equals(mTaskFrame, that.mTaskFrame)
1027                     && Objects.deepEquals(mBoundingRects, that.mBoundingRects)
1028                     && mFlags == that.mFlags
1029                     && mShouldAddCaptionInset == that.mShouldAddCaptionInset
1030                     && mExcludedFromAppBounds == that.mExcludedFromAppBounds;
1031         }
1032 
1033         @Override
hashCode()1034         public int hashCode() {
1035             return Objects.hash(mToken, mOwner, mFrame, Arrays.hashCode(mBoundingRects), mFlags);
1036         }
1037     }
1038 }
1039