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