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_DOCK_DIVIDER;
20 
21 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
22 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
23 
24 import android.annotation.NonNull;
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.util.Slog;
30 import android.view.DisplayInfo;
31 import android.view.IWindow;
32 import android.view.SurfaceControl;
33 import android.view.WindowInfo;
34 import android.view.animation.Animation;
35 
36 /**
37  * Represents a piece of the hierarchy under which a client Shell can manage sub-windows.
38  */
39 public class ShellRoot {
40     private static final String TAG = "ShellRoot";
41     private final DisplayContent mDisplayContent;
42     private IWindow mClient;
43     private WindowToken mToken;
44     private final IBinder.DeathRecipient mDeathRecipient;
45     private SurfaceControl mSurfaceControl = null;
46     private IWindow mAccessibilityWindow;
47     private IBinder.DeathRecipient mAccessibilityWindowDeath;
48 
ShellRoot(@onNull IWindow client, @NonNull DisplayContent dc, final int windowType)49     ShellRoot(@NonNull IWindow client, @NonNull DisplayContent dc, final int windowType) {
50         mDisplayContent = dc;
51         mDeathRecipient = () -> mDisplayContent.removeShellRoot(windowType);
52         try {
53             client.asBinder().linkToDeath(mDeathRecipient, 0);
54         } catch (RemoteException e) {
55             Slog.e(TAG, "Unable to add shell root for layer " + windowType + " on display "
56                     + dc.getDisplayId(), e);
57             return;
58         }
59         mClient = client;
60         mToken = new WindowToken(
61                 dc.mWmService, client.asBinder(), windowType, true, dc, true, false);
62         mSurfaceControl = mToken.makeChildSurface(null)
63                 .setContainerLayer()
64                 .setName("Shell Root Leash " + dc.getDisplayId())
65                 .setCallsite("ShellRoot")
66                 .build();
67         mToken.getPendingTransaction().show(mSurfaceControl);
68     }
69 
clear()70     void clear() {
71         if (mClient != null) {
72             mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
73             mClient = null;
74         }
75         if (mToken != null) {
76             mToken.removeImmediately();
77             mToken = null;
78         }
79     }
80 
getSurfaceControl()81     SurfaceControl getSurfaceControl() {
82         return mSurfaceControl;
83     }
84 
getClient()85     IWindow getClient() {
86         return mClient;
87     }
88 
startAnimation(Animation anim)89     void startAnimation(Animation anim) {
90         // Only do this for the divider
91         if (mToken.windowType != TYPE_DOCK_DIVIDER) {
92             return;
93         }
94 
95         DisplayInfo displayInfo = mToken.getFixedRotationTransformDisplayInfo();
96         if (displayInfo == null) {
97             displayInfo = mDisplayContent.getDisplayInfo();
98         }
99 
100         // Mostly copied from WindowState to enable keyguard transition animation
101         anim.initialize(displayInfo.logicalWidth, displayInfo.logicalHeight,
102                 displayInfo.appWidth, displayInfo.appHeight);
103         anim.restrictDuration(MAX_ANIMATION_DURATION);
104         anim.scaleCurrentDuration(mDisplayContent.mWmService.getWindowAnimationScaleLocked());
105         final AnimationAdapter adapter = new LocalAnimationAdapter(
106                 new WindowAnimationSpec(anim, new Point(0, 0), false /* canSkipFirstFrame */,
107                         0 /* windowCornerRadius */),
108                 mDisplayContent.mWmService.mSurfaceAnimationRunner);
109         mToken.startAnimation(mToken.getPendingTransaction(), adapter, false /* hidden */,
110                 ANIMATION_TYPE_WINDOW_ANIMATION);
111     }
112 
getWindowInfo()113     WindowInfo getWindowInfo() {
114         if (mToken.windowType != TYPE_DOCK_DIVIDER) {
115             return null;
116         }
117         if (!mDisplayContent.getDefaultTaskDisplayArea().isSplitScreenModeActivated()) {
118             return null;
119         }
120         if (mAccessibilityWindow == null) {
121             return null;
122         }
123         WindowInfo windowInfo = WindowInfo.obtain();
124         windowInfo.displayId = mToken.getDisplayArea().getDisplayContent().mDisplayId;
125         windowInfo.type = mToken.windowType;
126         windowInfo.layer = mToken.getWindowLayerFromType();
127         windowInfo.token = mAccessibilityWindow.asBinder();
128         windowInfo.title = "Splitscreen Divider";
129         windowInfo.focused = false;
130         windowInfo.inPictureInPicture = false;
131         windowInfo.hasFlagWatchOutsideTouch = false;
132         final Rect regionRect = new Rect();
133         mDisplayContent.getDockedDividerController().getTouchRegion(regionRect);
134         windowInfo.regionInScreen.set(regionRect);
135         return windowInfo;
136     }
137 
setAccessibilityWindow(IWindow window)138     void setAccessibilityWindow(IWindow window) {
139         if (mAccessibilityWindow != null) {
140             mAccessibilityWindow.asBinder().unlinkToDeath(mAccessibilityWindowDeath, 0);
141         }
142         mAccessibilityWindow = window;
143         if (mAccessibilityWindow != null) {
144             try {
145                 mAccessibilityWindowDeath = () -> {
146                     synchronized (mDisplayContent.mWmService.mGlobalLock) {
147                         mAccessibilityWindow = null;
148                         setAccessibilityWindow(null);
149                     }
150                 };
151                 mAccessibilityWindow.asBinder().linkToDeath(mAccessibilityWindowDeath, 0);
152             } catch (RemoteException e) {
153                 mAccessibilityWindow = null;
154             }
155         }
156         if (mDisplayContent.mWmService.mAccessibilityController != null) {
157             mDisplayContent.mWmService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(
158                     mDisplayContent.getDisplayId());
159         }
160     }
161 }
162