1 /*
2  * Copyright (C) 2019 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.server.wm;
18 
19 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
20 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
21 import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER;
22 import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
23 
24 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
25 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
26 
27 import android.annotation.NonNull;
28 import android.graphics.Point;
29 import android.graphics.Rect;
30 import android.os.IBinder;
31 import android.os.RemoteException;
32 import android.util.Slog;
33 import android.view.DisplayInfo;
34 import android.view.IWindow;
35 import android.view.SurfaceControl;
36 import android.view.WindowInfo;
37 import android.view.WindowManager;
38 import android.view.animation.Animation;
39 
40 /**
41  * Represents a piece of the hierarchy under which a client Shell can manage sub-windows.
42  */
43 public class ShellRoot {
44     private static final String TAG = "ShellRoot";
45     private final DisplayContent mDisplayContent;
46     private final int mShellRootLayer;
47     private IWindow mClient;
48     private WindowToken mToken;
49     private final IBinder.DeathRecipient mDeathRecipient;
50     private SurfaceControl mSurfaceControl = null;
51     private IWindow mAccessibilityWindow;
52     private IBinder.DeathRecipient mAccessibilityWindowDeath;
53     private int mWindowType;
54 
ShellRoot(@onNull IWindow client, @NonNull DisplayContent dc, @WindowManager.ShellRootLayer final int shellRootLayer)55     ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc,
56             @WindowManager.ShellRootLayer final int shellRootLayer) {
57         mDisplayContent = dc;
58         mShellRootLayer = shellRootLayer;
59         mDeathRecipient = () -> mDisplayContent.removeShellRoot(shellRootLayer);
60         try {
61             client.asBinder().linkToDeath(mDeathRecipient, 0);
62         } catch (RemoteException e) {
63             Slog.e(TAG, "Unable to add shell root layer " + shellRootLayer + " on display "
64                     + dc.getDisplayId(), e);
65             return;
66         }
67         mClient = client;
68         switch (shellRootLayer) {
69             case SHELL_ROOT_LAYER_DIVIDER:
70                 mWindowType = TYPE_DOCK_DIVIDER;
71                 break;
72             case SHELL_ROOT_LAYER_PIP:
73                 mWindowType = TYPE_APPLICATION_OVERLAY;
74                 break;
75             default:
76                 throw new IllegalArgumentException(shellRootLayer
77                         + " is not an acceptable shell root layer.");
78         }
79         mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), mWindowType)
80                 .setDisplayContent(dc)
81                 .setPersistOnEmpty(true)
82                 .setOwnerCanManageAppTokens(true)
83                 .build();
84         mSurfaceControl = mToken.makeChildSurface(null)
85                 .setContainerLayer()
86                 .setName("Shell Root Leash " + dc.getDisplayId())
87                 .setCallsite("ShellRoot")
88                 .build();
89         mToken.getPendingTransaction().show(mSurfaceControl);
90     }
91 
getWindowType()92     int getWindowType() {
93         return mWindowType;
94     }
95 
clear()96     void clear() {
97         if (mClient != null) {
98             mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
99             mClient = null;
100         }
101         if (mToken != null) {
102             mToken.removeImmediately();
103             mToken = null;
104         }
105     }
106 
getSurfaceControl()107     SurfaceControl getSurfaceControl() {
108         return mSurfaceControl;
109     }
110 
getClient()111     IWindow getClient() {
112         return mClient;
113     }
114 
startAnimation(Animation anim)115     void startAnimation(Animation anim) {
116         // Only do this for the divider
117         if (mToken.windowType != TYPE_DOCK_DIVIDER) {
118             return;
119         }
120 
121         DisplayInfo displayInfo = mToken.getFixedRotationTransformDisplayInfo();
122         if (displayInfo == null) {
123             displayInfo = mDisplayContent.getDisplayInfo();
124         }
125 
126         // Mostly copied from WindowState to enable keyguard transition animation
127         anim.initialize(displayInfo.logicalWidth, displayInfo.logicalHeight,
128                 displayInfo.appWidth, displayInfo.appHeight);
129         anim.restrictDuration(MAX_ANIMATION_DURATION);
130         anim.scaleCurrentDuration(mDisplayContent.mWmService.getWindowAnimationScaleLocked());
131         final AnimationAdapter adapter = new LocalAnimationAdapter(
132                 new WindowAnimationSpec(anim, new Point(0, 0), false /* canSkipFirstFrame */,
133                         0 /* windowCornerRadius */),
134                 mDisplayContent.mWmService.mSurfaceAnimationRunner);
135         mToken.startAnimation(mToken.getPendingTransaction(), adapter, false /* hidden */,
136                 ANIMATION_TYPE_WINDOW_ANIMATION);
137     }
138 
getWindowInfo()139     WindowInfo getWindowInfo() {
140         if (mShellRootLayer != SHELL_ROOT_LAYER_DIVIDER
141                 && mShellRootLayer != SHELL_ROOT_LAYER_PIP) {
142             return null;
143         }
144         if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER
145                 && !mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) {
146             return null;
147         }
148         if (mShellRootLayer == SHELL_ROOT_LAYER_PIP
149                 && mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask() == null) {
150             return null;
151         }
152         if (mAccessibilityWindow == null) {
153             return null;
154         }
155         WindowInfo windowInfo = WindowInfo.obtain();
156         windowInfo.displayId = mToken.getDisplayArea().getDisplayContent().mDisplayId;
157         windowInfo.type = mToken.windowType;
158         windowInfo.layer = mToken.getWindowLayerFromType();
159         windowInfo.token = mAccessibilityWindow.asBinder();
160         windowInfo.focused = false;
161         windowInfo.hasFlagWatchOutsideTouch = false;
162         final Rect regionRect = new Rect();
163 
164 
165         // DividerView
166         if (mShellRootLayer == SHELL_ROOT_LAYER_DIVIDER) {
167             windowInfo.inPictureInPicture = false;
168             mDisplayContent.getDockedDividerController().getTouchRegion(regionRect);
169             windowInfo.regionInScreen.set(regionRect);
170             windowInfo.title = "Splitscreen Divider";
171         }
172         // PipMenuView
173         if (mShellRootLayer == SHELL_ROOT_LAYER_PIP) {
174             windowInfo.inPictureInPicture = true;
175             mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask().getBounds(regionRect);
176             windowInfo.regionInScreen.set(regionRect);
177             windowInfo.title = "Picture-in-Picture menu";
178         }
179         return windowInfo;
180     }
181 
setAccessibilityWindow(IWindow window)182     void setAccessibilityWindow(IWindow window) {
183         if (mAccessibilityWindow != null) {
184             mAccessibilityWindow.asBinder().unlinkToDeath(mAccessibilityWindowDeath, 0);
185         }
186         mAccessibilityWindow = window;
187         if (mAccessibilityWindow != null) {
188             try {
189                 mAccessibilityWindowDeath = () -> {
190                     synchronized (mDisplayContent.mWmService.mGlobalLock) {
191                         mAccessibilityWindow = null;
192                         setAccessibilityWindow(null);
193                     }
194                 };
195                 mAccessibilityWindow.asBinder().linkToDeath(mAccessibilityWindowDeath, 0);
196             } catch (RemoteException e) {
197                 mAccessibilityWindow = null;
198             }
199         }
200         if (mDisplayContent.mWmService.mAccessibilityController.hasCallbacks()) {
201             mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(
202                     mDisplayContent.getDisplayId());
203         }
204     }
205 }
206