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 final WindowState mainWindow = activity.findMainWindow(false); 150 if (mainWindow == null) { 151 Slog.w(TAG, "TaskSnapshotSurface.create: no main window in " + activity); 152 return null; 153 } 154 if (activity.mDisplayContent.getRotation() != taskSnapshot.getRotation()) { 155 // The snapshot should have been checked by ActivityRecord#isSnapshotCompatible 156 // that the activity will be updated to the same rotation as the snapshot. Since 157 // the transition is not started yet, fixed rotation transform needs to be applied 158 // earlier to make the snapshot show in a rotated container. 159 activity.mDisplayContent.handleTopActivityLaunchingInDifferentOrientation( 160 activity, false /* checkOpening */); 161 } 162 final TaskOrganizerController controller = 163 mService.mAtmService.mTaskOrganizerController; 164 if (controller.addStartingWindow(task, activity, 0 /* launchTheme */, taskSnapshot)) { 165 return new StartingSurface(task, controller.getTaskOrganizer()); 166 } 167 return null; 168 } 169 170 private static final class DeferringStartingWindowRecord { 171 final ActivityRecord mDeferring; 172 final ActivityRecord mPrev; 173 final ActivityRecord mSource; 174 DeferringStartingWindowRecord(ActivityRecord deferring, ActivityRecord prev, ActivityRecord source)175 DeferringStartingWindowRecord(ActivityRecord deferring, ActivityRecord prev, 176 ActivityRecord source) { 177 mDeferring = deferring; 178 mPrev = prev; 179 mSource = source; 180 } 181 } 182 183 /** 184 * Shows a starting window while starting a new activity. Do not use this method to create a 185 * starting window for an existing activity. 186 */ showStartingWindow(ActivityRecord target, ActivityRecord prev, boolean newTask, boolean isTaskSwitch, ActivityRecord source)187 void showStartingWindow(ActivityRecord target, ActivityRecord prev, 188 boolean newTask, boolean isTaskSwitch, ActivityRecord source) { 189 if (mDeferringAddStartingWindow) { 190 addDeferringRecord(target, prev, newTask, isTaskSwitch, source); 191 } else { 192 target.showStartingWindow(prev, newTask, isTaskSwitch, true /* startActivity */, 193 source); 194 } 195 } 196 197 /** 198 * Queueing the starting activity status while deferring add starting window. 199 * @see Task#startActivityLocked 200 */ addDeferringRecord(ActivityRecord deferring, ActivityRecord prev, boolean newTask, boolean isTaskSwitch, ActivityRecord source)201 private void addDeferringRecord(ActivityRecord deferring, ActivityRecord prev, 202 boolean newTask, boolean isTaskSwitch, ActivityRecord source) { 203 // Set newTask, taskSwitch, processRunning form first activity because those can change 204 // after first activity started. 205 if (mDeferringAddStartActivities.isEmpty()) { 206 mInitProcessRunning = deferring.isProcessRunning(); 207 mInitNewTask = newTask; 208 mInitTaskSwitch = isTaskSwitch; 209 } 210 mDeferringAddStartActivities.add(new DeferringStartingWindowRecord( 211 deferring, prev, source)); 212 } 213 showStartingWindowFromDeferringActivities(ActivityOptions topOptions)214 private void showStartingWindowFromDeferringActivities(ActivityOptions topOptions) { 215 // Attempt to add starting window from the top-most activity. 216 for (int i = mDeferringAddStartActivities.size() - 1; i >= 0; --i) { 217 final DeferringStartingWindowRecord next = mDeferringAddStartActivities.get(i); 218 if (next.mDeferring.getTask() == null) { 219 Slog.e(TAG, "No task exists: " + next.mDeferring.shortComponentName 220 + " parent: " + next.mDeferring.getParent()); 221 continue; 222 } 223 next.mDeferring.showStartingWindow(next.mPrev, mInitNewTask, mInitTaskSwitch, 224 mInitProcessRunning, true /* startActivity */, next.mSource, topOptions); 225 // If one succeeds, it is done. 226 if (next.mDeferring.mStartingData != null) { 227 break; 228 } 229 } 230 mDeferringAddStartActivities.clear(); 231 } 232 233 /** 234 * Begin deferring add starting window in one pass. 235 * This is used to deferring add starting window while starting multiples activities because 236 * system only need to provide a starting window to the top-visible activity. 237 * Most call {@link #endDeferAddStartingWindow} when starting activities process finished. 238 * @see #endDeferAddStartingWindow() 239 */ beginDeferAddStartingWindow()240 void beginDeferAddStartingWindow() { 241 mDeferringAddStartingWindow = true; 242 } 243 244 /** 245 * End deferring add starting window. 246 */ endDeferAddStartingWindow(ActivityOptions topOptions)247 void endDeferAddStartingWindow(ActivityOptions topOptions) { 248 mDeferringAddStartingWindow = false; 249 showStartingWindowFromDeferringActivities(topOptions); 250 } 251 252 final class StartingSurface { 253 private final Task mTask; 254 // The task organizer which hold the client side reference of this surface. 255 final ITaskOrganizer mTaskOrganizer; 256 StartingSurface(Task task, ITaskOrganizer taskOrganizer)257 StartingSurface(Task task, ITaskOrganizer taskOrganizer) { 258 mTask = task; 259 mTaskOrganizer = taskOrganizer; 260 } 261 262 /** 263 * Removes the starting window surface. Do not hold the window manager lock when calling 264 * this method! 265 * 266 * @param animate Whether need to play the default exit animation for starting window. 267 * @param hasImeSurface Whether the starting window has IME surface. 268 */ remove(boolean animate, boolean hasImeSurface)269 public void remove(boolean animate, boolean hasImeSurface) { 270 synchronized (mService.mGlobalLock) { 271 mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask, 272 mTaskOrganizer, animate, hasImeSurface); 273 } 274 } 275 } 276 } 277