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