• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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