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.annotation.Nullable;
29 import android.graphics.Point;
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.WindowManager;
37 import android.view.animation.Animation;
38 
39 /**
40  * Represents a piece of the hierarchy under which a client Shell can manage sub-windows.
41  */
42 public class ShellRoot {
43     private static final String TAG = "ShellRoot";
44     private final DisplayContent mDisplayContent;
45     private final int mShellRootLayer;
46     private IWindow mClient;
47     private WindowToken mToken;
48     private final IBinder.DeathRecipient mDeathRecipient;
49     private SurfaceControl mSurfaceControl = null;
50     private IWindow mAccessibilityWindow;
51     private IBinder.DeathRecipient mAccessibilityWindowDeath;
52     private int mWindowType;
53 
ShellRoot(@onNull IWindow client, @NonNull DisplayContent dc, @WindowManager.ShellRootLayer final int shellRootLayer)54     ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc,
55             @WindowManager.ShellRootLayer final int shellRootLayer) {
56         mDisplayContent = dc;
57         mShellRootLayer = shellRootLayer;
58         mDeathRecipient = () -> mDisplayContent.removeShellRoot(shellRootLayer);
59         try {
60             client.asBinder().linkToDeath(mDeathRecipient, 0);
61         } catch (RemoteException e) {
62             Slog.e(TAG, "Unable to add shell root layer " + shellRootLayer + " on display "
63                     + dc.getDisplayId(), e);
64             return;
65         }
66         mClient = client;
67         switch (shellRootLayer) {
68             case SHELL_ROOT_LAYER_DIVIDER:
69                 mWindowType = TYPE_DOCK_DIVIDER;
70                 break;
71             case SHELL_ROOT_LAYER_PIP:
72                 mWindowType = TYPE_APPLICATION_OVERLAY;
73                 break;
74             default:
75                 throw new IllegalArgumentException(shellRootLayer
76                         + " is not an acceptable shell root layer.");
77         }
78         mToken = new WindowToken.Builder(dc.mWmService, client.asBinder(), mWindowType)
79                 .setDisplayContent(dc)
80                 .setPersistOnEmpty(true)
81                 .setOwnerCanManageAppTokens(true)
82                 .build();
83         mSurfaceControl = mToken.makeChildSurface(null)
84                 .setContainerLayer()
85                 .setName("Shell Root Leash " + dc.getDisplayId())
86                 .setCallsite("ShellRoot")
87                 .build();
88         mToken.getPendingTransaction().show(mSurfaceControl);
89     }
90 
getWindowType()91     int getWindowType() {
92         return mWindowType;
93     }
94 
clear()95     void clear() {
96         if (mClient != null) {
97             mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
98             mClient = null;
99         }
100         if (mToken != null) {
101             mToken.removeImmediately();
102             mToken = null;
103         }
104     }
105 
getSurfaceControl()106     SurfaceControl getSurfaceControl() {
107         return mSurfaceControl;
108     }
109 
getClient()110     IWindow getClient() {
111         return mClient;
112     }
113 
startAnimation(Animation anim)114     void startAnimation(Animation anim) {
115         // Only do this for the divider
116         if (mToken.windowType != TYPE_DOCK_DIVIDER) {
117             return;
118         }
119 
120         DisplayInfo displayInfo = mToken.getFixedRotationTransformDisplayInfo();
121         if (displayInfo == null) {
122             displayInfo = mDisplayContent.getDisplayInfo();
123         }
124 
125         // Mostly copied from WindowState to enable keyguard transition animation
126         anim.initialize(displayInfo.logicalWidth, displayInfo.logicalHeight,
127                 displayInfo.appWidth, displayInfo.appHeight);
128         anim.restrictDuration(MAX_ANIMATION_DURATION);
129         anim.scaleCurrentDuration(mDisplayContent.mWmService.getWindowAnimationScaleLocked());
130         final AnimationAdapter adapter = new LocalAnimationAdapter(
131                 new WindowAnimationSpec(anim, new Point(0, 0), false /* canSkipFirstFrame */,
132                         0 /* windowCornerRadius */),
133                 mDisplayContent.mWmService.mSurfaceAnimationRunner);
134         mToken.startAnimation(mToken.getPendingTransaction(), adapter, false /* hidden */,
135                 ANIMATION_TYPE_WINDOW_ANIMATION);
136     }
137 
138     @Nullable
getAccessibilityWindowToken()139     IBinder getAccessibilityWindowToken() {
140         if (mAccessibilityWindow != null) {
141             return mAccessibilityWindow.asBinder();
142         }
143         return null;
144     }
145 
setAccessibilityWindow(IWindow window)146     void setAccessibilityWindow(IWindow window) {
147         if (mAccessibilityWindow != null) {
148             mAccessibilityWindow.asBinder().unlinkToDeath(mAccessibilityWindowDeath, 0);
149         }
150         mAccessibilityWindow = window;
151         if (mAccessibilityWindow != null) {
152             try {
153                 mAccessibilityWindowDeath = () -> {
154                     synchronized (mDisplayContent.mWmService.mGlobalLock) {
155                         mAccessibilityWindow = null;
156                         setAccessibilityWindow(null);
157                     }
158                 };
159                 mAccessibilityWindow.asBinder().linkToDeath(mAccessibilityWindowDeath, 0);
160             } catch (RemoteException e) {
161                 mAccessibilityWindow = null;
162             }
163         }
164     }
165 }
166