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