• 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 android.app.ActivityManager.RunningTaskInfo;
20 import android.content.Context;
21 import android.content.res.Configuration;
22 import android.content.res.Resources;
23 import android.graphics.Color;
24 import android.graphics.PixelFormat;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.view.Display;
28 import android.view.InsetsState;
29 import android.view.LayoutInflater;
30 import android.view.SurfaceControl;
31 import android.view.SurfaceControlViewHost;
32 import android.view.View;
33 import android.view.ViewRootImpl;
34 import android.view.WindowManager;
35 import android.view.WindowlessWindowManager;
36 import android.window.TaskConstants;
37 import android.window.WindowContainerTransaction;
38 
39 import com.android.wm.shell.ShellTaskOrganizer;
40 import com.android.wm.shell.common.DisplayController;
41 
42 import java.util.function.Supplier;
43 
44 /**
45  * Manages a container surface and a windowless window to show window decoration. Responsible to
46  * update window decoration window state and layout parameters on task info changes and so that
47  * window decoration is in correct state and bounds.
48  *
49  * The container surface is a child of the task display area in the same display, so that window
50  * decorations can be drawn out of the task bounds and receive input events from out of the task
51  * bounds to support drag resizing.
52  *
53  * The windowless window that hosts window decoration is positioned in front of all activities, to
54  * allow the foreground activity to draw its own background behind window decorations, such as
55  * the window captions.
56  *
57  * @param <T> The type of the root view
58  */
59 public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
60         implements AutoCloseable {
61     private static final int[] CAPTION_INSETS_TYPES = { InsetsState.ITYPE_CAPTION_BAR };
62 
63     /**
64      * System-wide context. Only used to create context with overridden configurations.
65      */
66     final Context mContext;
67     final DisplayController mDisplayController;
68     final ShellTaskOrganizer mTaskOrganizer;
69     final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
70     final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
71     final Supplier<WindowContainerTransaction> mWindowContainerTransactionSupplier;
72     final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
73     private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
74             new DisplayController.OnDisplaysChangedListener() {
75                 @Override
76                 public void onDisplayAdded(int displayId) {
77                     if (mTaskInfo.displayId != displayId) {
78                         return;
79                     }
80 
81                     mDisplayController.removeDisplayWindowListener(this);
82                     relayout(mTaskInfo);
83                 }
84             };
85 
86     RunningTaskInfo mTaskInfo;
87     final SurfaceControl mTaskSurface;
88 
89     Display mDisplay;
90     Context mDecorWindowContext;
91     SurfaceControl mDecorationContainerSurface;
92     SurfaceControl mTaskBackgroundSurface;
93 
94     SurfaceControl mCaptionContainerSurface;
95     private WindowlessWindowManager mCaptionWindowManager;
96     private SurfaceControlViewHost mViewHost;
97 
98     private final Rect mCaptionInsetsRect = new Rect();
99     private final Rect mTaskSurfaceCrop = new Rect();
100     private final float[] mTmpColor = new float[3];
101 
WindowDecoration( Context context, DisplayController displayController, ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, SurfaceControl taskSurface)102     WindowDecoration(
103             Context context,
104             DisplayController displayController,
105             ShellTaskOrganizer taskOrganizer,
106             RunningTaskInfo taskInfo,
107             SurfaceControl taskSurface) {
108         this(context, displayController, taskOrganizer, taskInfo, taskSurface,
109                 SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
110                 WindowContainerTransaction::new, new SurfaceControlViewHostFactory() {});
111     }
112 
WindowDecoration( Context context, DisplayController displayController, ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, SurfaceControl taskSurface, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier, Supplier<WindowContainerTransaction> windowContainerTransactionSupplier, SurfaceControlViewHostFactory surfaceControlViewHostFactory)113     WindowDecoration(
114             Context context,
115             DisplayController displayController,
116             ShellTaskOrganizer taskOrganizer,
117             RunningTaskInfo taskInfo,
118             SurfaceControl taskSurface,
119             Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
120             Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
121             Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
122             SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
123         mContext = context;
124         mDisplayController = displayController;
125         mTaskOrganizer = taskOrganizer;
126         mTaskInfo = taskInfo;
127         mTaskSurface = taskSurface;
128         mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
129         mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
130         mWindowContainerTransactionSupplier = windowContainerTransactionSupplier;
131         mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
132 
133         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
134         mDecorWindowContext = mContext.createConfigurationContext(
135                 getConfigurationWithOverrides(mTaskInfo));
136     }
137 
138     /**
139      * Get {@link Configuration} from supplied {@link RunningTaskInfo}.
140      *
141      * Allows values to be overridden before returning the configuration.
142      */
getConfigurationWithOverrides(RunningTaskInfo taskInfo)143     protected Configuration getConfigurationWithOverrides(RunningTaskInfo taskInfo) {
144         return taskInfo.getConfiguration();
145     }
146 
147     /**
148      * Used by {@link WindowDecoration} to trigger a new relayout because the requirements for a
149      * relayout weren't satisfied are satisfied now.
150      *
151      * @param taskInfo The previous {@link RunningTaskInfo} passed into {@link #relayout} or the
152      *                 constructor.
153      */
relayout(RunningTaskInfo taskInfo)154     abstract void relayout(RunningTaskInfo taskInfo);
155 
relayout(RelayoutParams params, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult)156     void relayout(RelayoutParams params, SurfaceControl.Transaction startT,
157             SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView,
158             RelayoutResult<T> outResult) {
159         outResult.reset();
160 
161         final Configuration oldTaskConfig = mTaskInfo.getConfiguration();
162         if (params.mRunningTaskInfo != null) {
163             mTaskInfo = params.mRunningTaskInfo;
164         }
165 
166         if (!mTaskInfo.isVisible) {
167             releaseViews();
168             finishT.hide(mTaskSurface);
169             return;
170         }
171 
172         if (rootView == null && params.mLayoutResId == 0) {
173             throw new IllegalArgumentException("layoutResId and rootView can't both be invalid.");
174         }
175 
176         outResult.mRootView = rootView;
177         rootView = null; // Clear it just in case we use it accidentally
178         final Configuration taskConfig = getConfigurationWithOverrides(mTaskInfo);
179         if (oldTaskConfig.densityDpi != taskConfig.densityDpi
180                 || mDisplay == null
181                 || mDisplay.getDisplayId() != mTaskInfo.displayId) {
182             releaseViews();
183 
184             if (!obtainDisplayOrRegisterListener()) {
185                 outResult.mRootView = null;
186                 return;
187             }
188             mDecorWindowContext = mContext.createConfigurationContext(taskConfig);
189             if (params.mLayoutResId != 0) {
190                 outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
191                                 .inflate(params.mLayoutResId, null);
192             }
193         }
194 
195         if (outResult.mRootView == null) {
196             outResult.mRootView = (T) LayoutInflater.from(mDecorWindowContext)
197                             .inflate(params.mLayoutResId , null);
198         }
199 
200         // DecorationContainerSurface
201         if (mDecorationContainerSurface == null) {
202             final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
203             mDecorationContainerSurface = builder
204                     .setName("Decor container of Task=" + mTaskInfo.taskId)
205                     .setContainerLayer()
206                     .setParent(mTaskSurface)
207                     .build();
208 
209             startT.setTrustedOverlay(mDecorationContainerSurface, true)
210                     .setLayer(mDecorationContainerSurface,
211                             TaskConstants.TASK_CHILD_LAYER_WINDOW_DECORATIONS);
212         }
213 
214         final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
215         final Resources resources = mDecorWindowContext.getResources();
216         outResult.mDecorContainerOffsetX = -loadDimensionPixelSize(resources, params.mOutsetLeftId);
217         outResult.mDecorContainerOffsetY = -loadDimensionPixelSize(resources, params.mOutsetTopId);
218         outResult.mWidth = taskBounds.width()
219                 + loadDimensionPixelSize(resources, params.mOutsetRightId)
220                 - outResult.mDecorContainerOffsetX;
221         outResult.mHeight = taskBounds.height()
222                 + loadDimensionPixelSize(resources, params.mOutsetBottomId)
223                 - outResult.mDecorContainerOffsetY;
224         startT.setPosition(
225                         mDecorationContainerSurface,
226                         outResult.mDecorContainerOffsetX, outResult.mDecorContainerOffsetY)
227                 .setWindowCrop(mDecorationContainerSurface,
228                         outResult.mWidth, outResult.mHeight)
229                 .show(mDecorationContainerSurface);
230 
231         // TaskBackgroundSurface
232         if (mTaskBackgroundSurface == null) {
233             final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
234             mTaskBackgroundSurface = builder
235                     .setName("Background of Task=" + mTaskInfo.taskId)
236                     .setEffectLayer()
237                     .setParent(mTaskSurface)
238                     .build();
239 
240             startT.setLayer(mTaskBackgroundSurface, TaskConstants.TASK_CHILD_LAYER_TASK_BACKGROUND);
241         }
242 
243         float shadowRadius = loadDimension(resources, params.mShadowRadiusId);
244         int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
245         mTmpColor[0] = (float) Color.red(backgroundColorInt) / 255.f;
246         mTmpColor[1] = (float) Color.green(backgroundColorInt) / 255.f;
247         mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
248         startT.setWindowCrop(mTaskBackgroundSurface, taskBounds.width(),
249                         taskBounds.height())
250                 .setShadowRadius(mTaskBackgroundSurface, shadowRadius)
251                 .setColor(mTaskBackgroundSurface, mTmpColor)
252                 .show(mTaskBackgroundSurface);
253 
254         // CaptionContainerSurface, CaptionWindowManager
255         if (mCaptionContainerSurface == null) {
256             final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
257             mCaptionContainerSurface = builder
258                     .setName("Caption container of Task=" + mTaskInfo.taskId)
259                     .setContainerLayer()
260                     .setParent(mDecorationContainerSurface)
261                     .build();
262         }
263 
264         final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
265         final int captionWidth = taskBounds.width();
266 
267         startT.setPosition(
268                         mCaptionContainerSurface,
269                         -outResult.mDecorContainerOffsetX + params.mCaptionX,
270                         -outResult.mDecorContainerOffsetY + params.mCaptionY)
271                 .setWindowCrop(mCaptionContainerSurface, captionWidth, captionHeight)
272                 .show(mCaptionContainerSurface);
273 
274         if (mCaptionWindowManager == null) {
275             // Put caption under a container surface because ViewRootImpl sets the destination frame
276             // of windowless window layers and BLASTBufferQueue#update() doesn't support offset.
277             mCaptionWindowManager = new WindowlessWindowManager(
278                     mTaskInfo.getConfiguration(), mCaptionContainerSurface,
279                     null /* hostInputToken */);
280         }
281 
282         // Caption view
283         mCaptionWindowManager.setConfiguration(taskConfig);
284         final WindowManager.LayoutParams lp =
285                 new WindowManager.LayoutParams(captionWidth, captionHeight,
286                         WindowManager.LayoutParams.TYPE_APPLICATION,
287                         WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
288         lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
289         lp.setTrustedOverlay();
290         if (mViewHost == null) {
291             mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay,
292                     mCaptionWindowManager);
293             mViewHost.setView(outResult.mRootView, lp);
294         } else {
295             mViewHost.relayout(lp);
296         }
297 
298         if (ViewRootImpl.CAPTION_ON_SHELL) {
299             outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
300 
301             // Caption insets
302             mCaptionInsetsRect.set(taskBounds);
303             mCaptionInsetsRect.bottom =
304                     mCaptionInsetsRect.top + captionHeight + params.mCaptionY;
305             wct.addRectInsetsProvider(mTaskInfo.token, mCaptionInsetsRect,
306                     CAPTION_INSETS_TYPES);
307         } else {
308             startT.hide(mCaptionContainerSurface);
309         }
310 
311         // Task surface itself
312         Point taskPosition = mTaskInfo.positionInParent;
313         mTaskSurfaceCrop.set(
314                 outResult.mDecorContainerOffsetX,
315                 outResult.mDecorContainerOffsetY,
316                 outResult.mWidth + outResult.mDecorContainerOffsetX,
317                 outResult.mHeight + outResult.mDecorContainerOffsetY);
318         startT.show(mTaskSurface);
319         finishT.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
320                 .setCrop(mTaskSurface, mTaskSurfaceCrop);
321     }
322 
323     /**
324      * Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or
325      * registers {@link #mOnDisplaysChangedListener} if it doesn't.
326      *
327      * @return {@code true} if the {@link Display} instance exists; or {@code false} otherwise
328      */
obtainDisplayOrRegisterListener()329     private boolean obtainDisplayOrRegisterListener() {
330         mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
331         if (mDisplay == null) {
332             mDisplayController.addDisplayWindowListener(mOnDisplaysChangedListener);
333             return false;
334         }
335         return true;
336     }
337 
releaseViews()338     void releaseViews() {
339         if (mViewHost != null) {
340             mViewHost.release();
341             mViewHost = null;
342         }
343 
344         mCaptionWindowManager = null;
345 
346         final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
347         boolean released = false;
348         if (mCaptionContainerSurface != null) {
349             t.remove(mCaptionContainerSurface);
350             mCaptionContainerSurface = null;
351             released = true;
352         }
353 
354         if (mDecorationContainerSurface != null) {
355             t.remove(mDecorationContainerSurface);
356             mDecorationContainerSurface = null;
357             released = true;
358         }
359 
360         if (mTaskBackgroundSurface != null) {
361             t.remove(mTaskBackgroundSurface);
362             mTaskBackgroundSurface = null;
363             released = true;
364         }
365 
366         if (released) {
367             t.apply();
368         }
369 
370         final WindowContainerTransaction wct = mWindowContainerTransactionSupplier.get();
371         wct.removeInsetsProvider(mTaskInfo.token, CAPTION_INSETS_TYPES);
372         mTaskOrganizer.applyTransaction(wct);
373     }
374 
375     @Override
close()376     public void close() {
377         mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
378         releaseViews();
379     }
380 
loadDimensionPixelSize(Resources resources, int resourceId)381     static int loadDimensionPixelSize(Resources resources, int resourceId) {
382         if (resourceId == Resources.ID_NULL) {
383             return 0;
384         }
385         return resources.getDimensionPixelSize(resourceId);
386     }
387 
loadDimension(Resources resources, int resourceId)388     static float loadDimension(Resources resources, int resourceId) {
389         if (resourceId == Resources.ID_NULL) {
390             return 0;
391         }
392         return resources.getDimension(resourceId);
393     }
394 
395     /**
396      * Create a window associated with this WindowDecoration.
397      * Note that subclass must dispose of this when the task is hidden/closed.
398      * @param layoutId layout to make the window from
399      * @param t the transaction to apply
400      * @param xPos x position of new window
401      * @param yPos y position of new window
402      * @param width width of new window
403      * @param height height of new window
404      * @param shadowRadius radius of the shadow of the new window
405      * @param cornerRadius radius of the corners of the new window
406      * @return the {@link AdditionalWindow} that was added.
407      */
addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t, int xPos, int yPos, int width, int height, int shadowRadius, int cornerRadius)408     AdditionalWindow addWindow(int layoutId, String namePrefix, SurfaceControl.Transaction t,
409             int xPos, int yPos, int width, int height, int shadowRadius, int cornerRadius) {
410         final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
411         SurfaceControl windowSurfaceControl = builder
412                 .setName(namePrefix + " of Task=" + mTaskInfo.taskId)
413                 .setContainerLayer()
414                 .setParent(mDecorationContainerSurface)
415                 .build();
416         View v = LayoutInflater.from(mDecorWindowContext).inflate(layoutId, null);
417 
418         t.setPosition(windowSurfaceControl, xPos, yPos)
419                 .setWindowCrop(windowSurfaceControl, width, height)
420                 .setShadowRadius(windowSurfaceControl, shadowRadius)
421                 .setCornerRadius(windowSurfaceControl, cornerRadius)
422                 .show(windowSurfaceControl);
423         final WindowManager.LayoutParams lp =
424                 new WindowManager.LayoutParams(width, height,
425                         WindowManager.LayoutParams.TYPE_APPLICATION,
426                         WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
427         lp.setTitle("Additional window of Task=" + mTaskInfo.taskId);
428         lp.setTrustedOverlay();
429         WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration,
430                 windowSurfaceControl, null /* hostInputToken */);
431         SurfaceControlViewHost viewHost = mSurfaceControlViewHostFactory
432                 .create(mDecorWindowContext, mDisplay, windowManager);
433         viewHost.setView(v, lp);
434         return new AdditionalWindow(windowSurfaceControl, viewHost,
435                 mSurfaceControlTransactionSupplier);
436     }
437 
438     static class RelayoutParams{
439         RunningTaskInfo mRunningTaskInfo;
440         int mLayoutResId;
441         int mCaptionHeightId;
442         int mCaptionWidthId;
443         int mShadowRadiusId;
444 
445         int mOutsetTopId;
446         int mOutsetBottomId;
447         int mOutsetLeftId;
448         int mOutsetRightId;
449 
450         int mCaptionX;
451         int mCaptionY;
452 
setOutsets(int leftId, int topId, int rightId, int bottomId)453         void setOutsets(int leftId, int topId, int rightId, int bottomId) {
454             mOutsetLeftId = leftId;
455             mOutsetTopId = topId;
456             mOutsetRightId = rightId;
457             mOutsetBottomId = bottomId;
458         }
459 
setCaptionPosition(int left, int top)460         void setCaptionPosition(int left, int top) {
461             mCaptionX = left;
462             mCaptionY = top;
463         }
464 
reset()465         void reset() {
466             mLayoutResId = Resources.ID_NULL;
467             mCaptionHeightId = Resources.ID_NULL;
468             mCaptionWidthId = Resources.ID_NULL;
469             mShadowRadiusId = Resources.ID_NULL;
470 
471             mOutsetTopId = Resources.ID_NULL;
472             mOutsetBottomId = Resources.ID_NULL;
473             mOutsetLeftId = Resources.ID_NULL;
474             mOutsetRightId = Resources.ID_NULL;
475 
476             mCaptionX = 0;
477             mCaptionY = 0;
478         }
479     }
480 
481     static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
482         int mWidth;
483         int mHeight;
484         T mRootView;
485         int mDecorContainerOffsetX;
486         int mDecorContainerOffsetY;
487 
reset()488         void reset() {
489             mWidth = 0;
490             mHeight = 0;
491             mDecorContainerOffsetX = 0;
492             mDecorContainerOffsetY = 0;
493             mRootView = null;
494         }
495     }
496 
497     interface SurfaceControlViewHostFactory {
create(Context c, Display d, WindowlessWindowManager wmm)498         default SurfaceControlViewHost create(Context c, Display d, WindowlessWindowManager wmm) {
499             return new SurfaceControlViewHost(c, d, wmm);
500         }
501     }
502 
503     /**
504      * Subclass for additional windows associated with this WindowDecoration
505      */
506     static class AdditionalWindow {
507         SurfaceControl mWindowSurface;
508         SurfaceControlViewHost mWindowViewHost;
509         Supplier<SurfaceControl.Transaction> mTransactionSupplier;
510 
AdditionalWindow(SurfaceControl surfaceControl, SurfaceControlViewHost surfaceControlViewHost, Supplier<SurfaceControl.Transaction> transactionSupplier)511         private AdditionalWindow(SurfaceControl surfaceControl,
512                 SurfaceControlViewHost surfaceControlViewHost,
513                 Supplier<SurfaceControl.Transaction> transactionSupplier) {
514             mWindowSurface = surfaceControl;
515             mWindowViewHost = surfaceControlViewHost;
516             mTransactionSupplier = transactionSupplier;
517         }
518 
releaseView()519         void releaseView() {
520             WindowlessWindowManager windowManager = mWindowViewHost.getWindowlessWM();
521 
522             if (mWindowViewHost != null) {
523                 mWindowViewHost.release();
524                 mWindowViewHost = null;
525             }
526             windowManager = null;
527             final SurfaceControl.Transaction t = mTransactionSupplier.get();
528             boolean released = false;
529             if (mWindowSurface != null) {
530                 t.remove(mWindowSurface);
531                 mWindowSurface = null;
532                 released = true;
533             }
534             if (released) {
535                 t.apply();
536             }
537         }
538     }
539 }
540