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.server.wm; 18 19 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; 20 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN; 21 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN; 22 import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; 23 import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN; 24 import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; 25 import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; 26 import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; 27 import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN; 28 29 import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SNAPSHOT; 30 import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_TYPE_SPLASH_SCREEN; 31 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 32 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 33 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.app.ActivityOptions; 37 import android.app.compat.CompatChanges; 38 import android.compat.annotation.ChangeId; 39 import android.compat.annotation.EnabledSince; 40 import android.content.pm.ApplicationInfo; 41 import android.os.UserHandle; 42 import android.util.Slog; 43 import android.window.SplashScreenView; 44 import android.window.TaskSnapshot; 45 46 import java.util.ArrayList; 47 import java.util.function.Supplier; 48 49 /** 50 * Managing to create and release a starting window surface. 51 */ 52 public class StartingSurfaceController { 53 private static final String TAG = TAG_WITH_CLASS_NAME 54 ? StartingSurfaceController.class.getSimpleName() : TAG_WM; 55 /** 56 * Application is allowed to receive the 57 * {@link 58 * android.window.SplashScreen.OnExitAnimationListener#onSplashScreenExit(SplashScreenView)} 59 * callback, even when the splash screen only shows a solid color. 60 */ 61 @ChangeId 62 @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.TIRAMISU) 63 private static final long ALLOW_COPY_SOLID_COLOR_VIEW = 205907456L; 64 65 private final WindowManagerService mService; 66 private final SplashScreenExceptionList mSplashScreenExceptionsList; 67 68 // Cache status while deferring add starting window 69 boolean mInitProcessRunning; 70 boolean mInitNewTask; 71 boolean mInitTaskSwitch; 72 private final ArrayList<DeferringStartingWindowRecord> mDeferringAddStartActivities = 73 new ArrayList<>(); 74 private boolean mDeferringAddStartingWindow; 75 StartingSurfaceController(WindowManagerService wm)76 public StartingSurfaceController(WindowManagerService wm) { 77 mService = wm; 78 mSplashScreenExceptionsList = new SplashScreenExceptionList(wm.mContext.getMainExecutor()); 79 } 80 createSplashScreenStartingSurface(ActivityRecord activity, int theme)81 StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, int theme) { 82 83 synchronized (mService.mGlobalLock) { 84 final Task task = activity.getTask(); 85 if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow( 86 task, activity, theme, null /* taskSnapshot */)) { 87 return new StartingSurface(task); 88 } 89 } 90 return null; 91 } 92 93 /** 94 * @see SplashScreenExceptionList#isException(String, int, Supplier) 95 */ isExceptionApp(@onNull String packageName, int targetSdk, @Nullable Supplier<ApplicationInfo> infoProvider)96 boolean isExceptionApp(@NonNull String packageName, int targetSdk, 97 @Nullable Supplier<ApplicationInfo> infoProvider) { 98 return mSplashScreenExceptionsList.isException(packageName, targetSdk, infoProvider); 99 } 100 makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, boolean isSolidColor, boolean useLegacy, boolean activityDrawn, int startingWindowType, String packageName, int userId)101 static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch, 102 boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, 103 boolean isSolidColor, boolean useLegacy, boolean activityDrawn, int startingWindowType, 104 String packageName, int userId) { 105 int parameter = 0; 106 if (newTask) { 107 parameter |= TYPE_PARAMETER_NEW_TASK; 108 } 109 if (taskSwitch) { 110 parameter |= TYPE_PARAMETER_TASK_SWITCH; 111 } 112 if (processRunning) { 113 parameter |= TYPE_PARAMETER_PROCESS_RUNNING; 114 } 115 if (allowTaskSnapshot) { 116 parameter |= TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; 117 } 118 if (activityCreated || startingWindowType == STARTING_WINDOW_TYPE_SNAPSHOT) { 119 parameter |= TYPE_PARAMETER_ACTIVITY_CREATED; 120 } 121 if (isSolidColor) { 122 parameter |= TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN; 123 } 124 if (useLegacy) { 125 parameter |= TYPE_PARAMETER_LEGACY_SPLASH_SCREEN; 126 } 127 if (activityDrawn) { 128 parameter |= TYPE_PARAMETER_ACTIVITY_DRAWN; 129 } 130 if (startingWindowType == STARTING_WINDOW_TYPE_SPLASH_SCREEN 131 && CompatChanges.isChangeEnabled(ALLOW_COPY_SOLID_COLOR_VIEW, packageName, 132 UserHandle.of(userId))) { 133 parameter |= TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN; 134 } 135 return parameter; 136 } 137 createTaskSnapshotSurface(ActivityRecord activity, TaskSnapshot taskSnapshot)138 StartingSurface createTaskSnapshotSurface(ActivityRecord activity, TaskSnapshot taskSnapshot) { 139 final WindowState topFullscreenOpaqueWindow; 140 final Task task; 141 synchronized (mService.mGlobalLock) { 142 task = activity.getTask(); 143 if (task == null) { 144 Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for activity=" 145 + activity); 146 return null; 147 } 148 final ActivityRecord topFullscreenActivity = 149 activity.getTask().getTopFullscreenActivity(); 150 if (topFullscreenActivity == null) { 151 Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task=" 152 + task); 153 return null; 154 } 155 topFullscreenOpaqueWindow = topFullscreenActivity.getTopFullscreenOpaqueWindow(); 156 if (topFullscreenOpaqueWindow == null) { 157 Slog.w(TAG, "TaskSnapshotSurface.create: no opaque window in " 158 + topFullscreenActivity); 159 return null; 160 } 161 if (activity.mDisplayContent.getRotation() != taskSnapshot.getRotation()) { 162 // The snapshot should have been checked by ActivityRecord#isSnapshotCompatible 163 // that the activity will be updated to the same rotation as the snapshot. Since 164 // the transition is not started yet, fixed rotation transform needs to be applied 165 // earlier to make the snapshot show in a rotated container. 166 activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation( 167 activity, false /* checkOpening */); 168 } 169 mService.mAtmService.mTaskOrganizerController.addStartingWindow(task, 170 activity, 0 /* launchTheme */, taskSnapshot); 171 return new StartingSurface(task); 172 } 173 } 174 175 private static final class DeferringStartingWindowRecord { 176 final ActivityRecord mDeferring; 177 final ActivityRecord mPrev; 178 final ActivityRecord mSource; 179 DeferringStartingWindowRecord(ActivityRecord deferring, ActivityRecord prev, ActivityRecord source)180 DeferringStartingWindowRecord(ActivityRecord deferring, ActivityRecord prev, 181 ActivityRecord source) { 182 mDeferring = deferring; 183 mPrev = prev; 184 mSource = source; 185 } 186 } 187 188 /** 189 * Shows a starting window while starting a new activity. Do not use this method to create a 190 * starting window for an existing activity. 191 */ showStartingWindow(ActivityRecord target, ActivityRecord prev, boolean newTask, boolean isTaskSwitch, ActivityRecord source)192 void showStartingWindow(ActivityRecord target, ActivityRecord prev, 193 boolean newTask, boolean isTaskSwitch, ActivityRecord source) { 194 if (mDeferringAddStartingWindow) { 195 addDeferringRecord(target, prev, newTask, isTaskSwitch, source); 196 } else { 197 target.showStartingWindow(prev, newTask, isTaskSwitch, true /* startActivity */, 198 source); 199 } 200 } 201 202 /** 203 * Queueing the starting activity status while deferring add starting window. 204 * @see Task#startActivityLocked 205 */ addDeferringRecord(ActivityRecord deferring, ActivityRecord prev, boolean newTask, boolean isTaskSwitch, ActivityRecord source)206 private void addDeferringRecord(ActivityRecord deferring, ActivityRecord prev, 207 boolean newTask, boolean isTaskSwitch, ActivityRecord source) { 208 // Set newTask, taskSwitch, processRunning form first activity because those can change 209 // after first activity started. 210 if (mDeferringAddStartActivities.isEmpty()) { 211 mInitProcessRunning = deferring.isProcessRunning(); 212 mInitNewTask = newTask; 213 mInitTaskSwitch = isTaskSwitch; 214 } 215 mDeferringAddStartActivities.add(new DeferringStartingWindowRecord( 216 deferring, prev, source)); 217 } 218 showStartingWindowFromDeferringActivities(ActivityOptions topOptions)219 private void showStartingWindowFromDeferringActivities(ActivityOptions topOptions) { 220 // Attempt to add starting window from the top-most activity. 221 for (int i = mDeferringAddStartActivities.size() - 1; i >= 0; --i) { 222 final DeferringStartingWindowRecord next = mDeferringAddStartActivities.get(i); 223 if (next.mDeferring.getTask() == null) { 224 Slog.e(TAG, "No task exists: " + next.mDeferring.shortComponentName 225 + " parent: " + next.mDeferring.getParent()); 226 continue; 227 } 228 next.mDeferring.showStartingWindow(next.mPrev, mInitNewTask, mInitTaskSwitch, 229 mInitProcessRunning, true /* startActivity */, next.mSource, topOptions); 230 // If one succeeds, it is done. 231 if (next.mDeferring.mStartingData != null) { 232 break; 233 } 234 } 235 mDeferringAddStartActivities.clear(); 236 } 237 238 /** 239 * Begin deferring add starting window in one pass. 240 * This is used to deferring add starting window while starting multiples activities because 241 * system only need to provide a starting window to the top-visible activity. 242 * Most call {@link #endDeferAddStartingWindow} when starting activities process finished. 243 * @see #endDeferAddStartingWindow() 244 */ beginDeferAddStartingWindow()245 void beginDeferAddStartingWindow() { 246 mDeferringAddStartingWindow = true; 247 } 248 249 /** 250 * End deferring add starting window. 251 */ endDeferAddStartingWindow(ActivityOptions topOptions)252 void endDeferAddStartingWindow(ActivityOptions topOptions) { 253 mDeferringAddStartingWindow = false; 254 showStartingWindowFromDeferringActivities(topOptions); 255 } 256 257 final class StartingSurface { 258 private final Task mTask; 259 StartingSurface(Task task)260 StartingSurface(Task task) { 261 mTask = task; 262 } 263 264 /** 265 * Removes the starting window surface. Do not hold the window manager lock when calling 266 * this method! 267 * @param animate Whether need to play the default exit animation for starting window. 268 */ remove(boolean animate)269 public void remove(boolean animate) { 270 synchronized (mService.mGlobalLock) { 271 mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask, animate); 272 } 273 } 274 } 275 } 276