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