• 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_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