1 /* 2 * Copyright (C) 2020 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.wm.shell.startingsurface; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 20 import static android.graphics.Color.WHITE; 21 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 22 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; 23 24 import android.annotation.BinderThread; 25 import android.annotation.NonNull; 26 import android.app.ActivityManager; 27 import android.app.ActivityManager.TaskDescription; 28 import android.graphics.Paint; 29 import android.graphics.Point; 30 import android.graphics.Rect; 31 import android.os.Bundle; 32 import android.os.IBinder; 33 import android.os.RemoteException; 34 import android.os.Trace; 35 import android.util.MergedConfiguration; 36 import android.util.Slog; 37 import android.view.IWindowSession; 38 import android.view.InputChannel; 39 import android.view.InsetsSourceControl; 40 import android.view.InsetsState; 41 import android.view.SurfaceControl; 42 import android.view.View; 43 import android.view.WindowManager; 44 import android.view.WindowManagerGlobal; 45 import android.window.ClientWindowFrames; 46 import android.window.SnapshotDrawerUtils; 47 import android.window.StartingWindowInfo; 48 import android.window.TaskSnapshot; 49 50 import com.android.internal.protolog.common.ProtoLog; 51 import com.android.internal.view.BaseIWindow; 52 import com.android.wm.shell.common.ShellExecutor; 53 import com.android.wm.shell.protolog.ShellProtoLogGroup; 54 55 import java.lang.ref.WeakReference; 56 57 /** 58 * This class represents a starting window that shows a snapshot. 59 * 60 * @hide 61 */ 62 public class TaskSnapshotWindow { 63 private static final String TAG = StartingWindowController.TAG; 64 private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId="; 65 66 private final Window mWindow; 67 private final Runnable mClearWindowHandler; 68 private final ShellExecutor mSplashScreenExecutor; 69 private final IWindowSession mSession; 70 private boolean mHasDrawn; 71 private final Paint mBackgroundPaint = new Paint(); 72 private final int mOrientationOnCreation; 73 74 private final boolean mHasImeSurface; 75 create(StartingWindowInfo info, IBinder appToken, TaskSnapshot snapshot, ShellExecutor splashScreenExecutor, @NonNull Runnable clearWindowHandler)76 static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken, 77 TaskSnapshot snapshot, ShellExecutor splashScreenExecutor, 78 @NonNull Runnable clearWindowHandler) { 79 final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; 80 final int taskId = runningTaskInfo.taskId; 81 82 // if we're in PIP we don't want to create the snapshot 83 if (runningTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) { 84 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, 85 "did not create taskSnapshot due to being in PIP"); 86 return null; 87 } 88 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, 89 "create taskSnapshot surface for task: %d", taskId); 90 91 final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState; 92 93 final WindowManager.LayoutParams layoutParams = SnapshotDrawerUtils.createLayoutParameters( 94 info, TITLE_FORMAT + taskId, TYPE_APPLICATION_STARTING, 95 snapshot.getHardwareBuffer().getFormat(), appToken); 96 if (layoutParams == null) { 97 Slog.e(TAG, "TaskSnapshotWindow no layoutParams"); 98 return null; 99 } 100 101 final Point taskSize = snapshot.getTaskSize(); 102 final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y); 103 final int orientation = snapshot.getOrientation(); 104 final int displayId = runningTaskInfo.displayId; 105 106 final IWindowSession session = WindowManagerGlobal.getWindowSession(); 107 final SurfaceControl surfaceControl = new SurfaceControl(); 108 final ClientWindowFrames tmpFrames = new ClientWindowFrames(); 109 110 final InsetsSourceControl.Array tmpControls = new InsetsSourceControl.Array(); 111 final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); 112 113 final TaskDescription taskDescription = 114 SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo); 115 116 final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow( 117 snapshot, taskDescription, orientation, 118 clearWindowHandler, splashScreenExecutor); 119 final Window window = snapshotSurface.mWindow; 120 121 final InsetsState tmpInsetsState = new InsetsState(); 122 final InputChannel tmpInputChannel = new InputChannel(); 123 final float[] sizeCompatScale = { 1f }; 124 125 try { 126 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#addToDisplay"); 127 final int res = session.addToDisplay(window, layoutParams, View.GONE, displayId, 128 info.requestedVisibleTypes, tmpInputChannel, tmpInsetsState, tmpControls, 129 new Rect(), sizeCompatScale); 130 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 131 if (res < 0) { 132 Slog.w(TAG, "Failed to add snapshot starting window res=" + res); 133 return null; 134 } 135 } catch (RemoteException e) { 136 snapshotSurface.clearWindowSynced(); 137 } 138 window.setOuter(snapshotSurface); 139 try { 140 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "TaskSnapshot#relayout"); 141 session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, 0, 0, 142 tmpFrames, tmpMergedConfiguration, surfaceControl, tmpInsetsState, 143 tmpControls, new Bundle()); 144 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 145 } catch (RemoteException e) { 146 snapshotSurface.clearWindowSynced(); 147 Slog.w(TAG, "Failed to relayout snapshot starting window"); 148 return null; 149 } 150 151 SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot, 152 taskBounds, tmpFrames.frame, topWindowInsetsState, true /* releaseAfterDraw */); 153 snapshotSurface.mHasDrawn = true; 154 snapshotSurface.reportDrawn(); 155 156 return snapshotSurface; 157 } 158 TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription, int currentOrientation, Runnable clearWindowHandler, ShellExecutor splashScreenExecutor)159 public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription, 160 int currentOrientation, Runnable clearWindowHandler, 161 ShellExecutor splashScreenExecutor) { 162 mSplashScreenExecutor = splashScreenExecutor; 163 mSession = WindowManagerGlobal.getWindowSession(); 164 mWindow = new Window(); 165 mWindow.setSession(mSession); 166 int backgroundColor = taskDescription.getBackgroundColor(); 167 mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); 168 mOrientationOnCreation = currentOrientation; 169 mClearWindowHandler = clearWindowHandler; 170 mHasImeSurface = snapshot.hasImeSurface(); 171 } 172 getBackgroundColor()173 int getBackgroundColor() { 174 return mBackgroundPaint.getColor(); 175 } 176 hasImeSurface()177 boolean hasImeSurface() { 178 return mHasImeSurface; 179 } 180 removeImmediately()181 void removeImmediately() { 182 try { 183 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, 184 "Removing taskSnapshot surface, mHasDrawn=%b", mHasDrawn); 185 mSession.remove(mWindow); 186 } catch (RemoteException e) { 187 // nothing 188 } 189 } 190 191 /** 192 * Clear window from drawer, must be post on main executor. 193 */ clearWindowSynced()194 private void clearWindowSynced() { 195 mSplashScreenExecutor.executeDelayed(mClearWindowHandler, 0); 196 } 197 reportDrawn()198 private void reportDrawn() { 199 try { 200 mSession.finishDrawing(mWindow, null /* postDrawTransaction */, Integer.MAX_VALUE); 201 } catch (RemoteException e) { 202 clearWindowSynced(); 203 } 204 } 205 206 static class Window extends BaseIWindow { 207 private WeakReference<TaskSnapshotWindow> mOuter; 208 setOuter(TaskSnapshotWindow outer)209 public void setOuter(TaskSnapshotWindow outer) { 210 mOuter = new WeakReference<>(outer); 211 } 212 213 @BinderThread 214 @Override resized(ClientWindowFrames frames, boolean reportDraw, MergedConfiguration mergedConfiguration, InsetsState insetsState, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int seqId, boolean dragResizing)215 public void resized(ClientWindowFrames frames, boolean reportDraw, 216 MergedConfiguration mergedConfiguration, InsetsState insetsState, 217 boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int seqId, 218 boolean dragResizing) { 219 final TaskSnapshotWindow snapshot = mOuter.get(); 220 if (snapshot == null) { 221 return; 222 } 223 snapshot.mSplashScreenExecutor.execute(() -> { 224 if (mergedConfiguration != null 225 && snapshot.mOrientationOnCreation 226 != mergedConfiguration.getMergedConfiguration().orientation) { 227 // The orientation of the screen is changing. We better remove the snapshot 228 // ASAP as we are going to wait on the new window in any case to unfreeze 229 // the screen, and the starting window is not needed anymore. 230 snapshot.clearWindowSynced(); 231 } else if (reportDraw) { 232 if (snapshot.mHasDrawn) { 233 snapshot.reportDrawn(); 234 } 235 } 236 }); 237 } 238 } 239 } 240