From c1c4c5a6612fc3134f1ce1233e5851037719c491 Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Tue, 1 Sep 2020 11:32:02 +0800 Subject: [PATCH] Delegate splash screen starting window to SystemUI(1/N) Mirror PhoneWindowManager#addSplashScreen to StartingSurfaceDrawer, which make WMShell able to draw the splash screen starting window. - Use StartingSurfaceController#DEBUG_ENABLE_SHELL_DRAWER to switch drawer when developing this feature. - Temporarily put StartingSurfaceDrawer in ShellTaskOrganizer, the drawer should be controlled by a controller which should be create while porting ActivityRecord#addStartingWindow to Shell. Ref doc: go/delegate_starting_window Bug: 131727939 Test: atest AppWindowTokenTests WindowOrganizerTests ActivityStackTests Test: atest WMShellTest ShellTaskOrganizerTests StartingSurfaceDrawerTests Test: check from winscope that the splash screen window can attach to/detach from the ActivityRecord. Change-Id: I6dfc9ff75807e2f9479149d14f219c91a6527393 --- core/api/test-current.txt | 2 + core/java/android/window/ITaskOrganizer.aidl | 15 + core/java/android/window/TaskOrganizer.java | 28 ++ .../android/wm/shell/ShellTaskOrganizer.java | 23 +- .../StartingSurfaceDrawer.java | 343 ++++++++++++++++++ .../wm/shell/ShellTaskOrganizerTests.java | 5 +- .../StartingSurfaceDrawerTests.java | 147 ++++++++ .../systemui/wmshell/WMShellBaseModule.java | 4 +- .../com/android/server/wm/ActivityRecord.java | 58 +-- .../server/wm/AppTransitionController.java | 4 +- .../server/wm/SplashScreenStartingData.java | 7 +- .../server/wm/StartingSurfaceController.java | 69 ++++ .../server/wm/TaskOrganizerController.java | 51 +++ .../server/wm/WindowManagerService.java | 7 +- .../com/android/server/wm/WindowState.java | 6 +- .../server/wm/WindowStateAnimator.java | 2 +- .../server/wm/AppWindowTokenTests.java | 12 +- .../server/wm/TestWindowManagerPolicy.java | 4 +- .../server/wm/WindowOrganizerTests.java | 28 ++ .../android/server/wm/WindowStateTests.java | 2 +- .../android/server/wm/WindowTestsBase.java | 6 + 21 files changed, 768 insertions(+), 55 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java create mode 100644 libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java create mode 100644 services/core/java/com/android/server/wm/StartingSurfaceController.java diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 1fdb3e4a6fdeb..d7fadfc2ac539 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2417,6 +2417,7 @@ package android.window { public class TaskOrganizer extends android.window.WindowOrganizer { ctor public TaskOrganizer(); + method @BinderThread public void addStartingWindow(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.os.IBinder); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void createRootTask(int, int, @Nullable android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean deleteRootTask(@NonNull android.window.WindowContainerToken); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]); @@ -2427,6 +2428,7 @@ package android.window { method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo); method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo); method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public java.util.List registerOrganizer(); + method @BinderThread public void removeStartingWindow(@NonNull android.app.ActivityManager.RunningTaskInfo); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken); method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void unregisterOrganizer(); diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl index abca136c347eb..b503184f4c510 100644 --- a/core/java/android/window/ITaskOrganizer.aidl +++ b/core/java/android/window/ITaskOrganizer.aidl @@ -25,6 +25,21 @@ import android.window.WindowContainerToken; * {@hide} */ oneway interface ITaskOrganizer { + /** + * Called when a Task is starting and the system would like to show a UI to indicate that an + * application is starting. The client is responsible to add/remove the starting window if it + * has create a starting window for the Task. + * + * @param taskInfo The information about the Task that's available + * @param appToken Token of the application being started. + */ + void addStartingWindow(in ActivityManager.RunningTaskInfo taskInfo, IBinder appToken); + + /** + * Called when the Task want to remove the starting window. + */ + void removeStartingWindow(in ActivityManager.RunningTaskInfo taskInfo); + /** * A callback when the Task is available for the registered organizer. The client is responsible * for releasing the SurfaceControl in the callback. For non-root tasks, the leash may initially diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index 4e209206f2330..eda168dd8553b 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -84,6 +84,25 @@ public class TaskOrganizer extends WindowOrganizer { } } + /** + * Called when a Task is starting and the system would like to show a UI to indicate that an + * application is starting. The client is responsible to add/remove the starting window if it + * has create a starting window for the Task. + * + * @param taskInfo The information about the Task that's available + * @param appToken Token of the application being started. + * context to for resources + */ + @BinderThread + public void addStartingWindow(@NonNull ActivityManager.RunningTaskInfo taskInfo, + @NonNull IBinder appToken) {} + + /** + * Called when the Task want to remove the starting window. + */ + @BinderThread + public void removeStartingWindow(@NonNull ActivityManager.RunningTaskInfo taskInfo) {} + /** * Called when a task with the registered windowing mode can be controlled by this task * organizer. For non-root tasks, the leash may initially be hidden so it is up to the organizer @@ -192,6 +211,15 @@ public class TaskOrganizer extends WindowOrganizer { } private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() { + @Override + public void addStartingWindow(ActivityManager.RunningTaskInfo taskInfo, IBinder appToken) { + TaskOrganizer.this.addStartingWindow(taskInfo, appToken); + } + + @Override + public void removeStartingWindow(ActivityManager.RunningTaskInfo taskInfo) { + TaskOrganizer.this.removeStartingWindow(taskInfo); + } @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 51ddb17daa009..ba014110e33ed 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -27,6 +27,7 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG import android.annotation.IntDef; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration.WindowingMode; +import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.util.ArrayMap; @@ -44,6 +45,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.startingsurface.StartingSurfaceDrawer; import java.io.PrintWriter; import java.util.ArrayList; @@ -104,21 +106,26 @@ public class ShellTaskOrganizer extends TaskOrganizer { private final Transitions mTransitions; private final Object mLock = new Object(); + private final StartingSurfaceDrawer mStartingSurfaceDrawer; public ShellTaskOrganizer(SyncTransactionQueue syncQueue, TransactionPool transactionPool, - ShellExecutor mainExecutor, ShellExecutor animExecutor) { - this(null, syncQueue, transactionPool, mainExecutor, animExecutor); + ShellExecutor mainExecutor, ShellExecutor animExecutor, Context context) { + this(null, syncQueue, transactionPool, mainExecutor, animExecutor, context); } @VisibleForTesting ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, SyncTransactionQueue syncQueue, TransactionPool transactionPool, - ShellExecutor mainExecutor, ShellExecutor animExecutor) { + ShellExecutor mainExecutor, ShellExecutor animExecutor, Context context) { super(taskOrganizerController, mainExecutor); addListenerForType(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN); addListenerForType(new LetterboxTaskListener(syncQueue), TASK_LISTENER_TYPE_LETTERBOX); mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor); if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions); + // TODO(b/131727939) temporarily live here, the starting surface drawer should be controlled + // by a controller, that class should be create while porting + // ActivityRecord#addStartingWindow to WMShell. + mStartingSurfaceDrawer = new StartingSurfaceDrawer(context); } @Override @@ -234,6 +241,16 @@ public class ShellTaskOrganizer extends TaskOrganizer { } } + @Override + public void addStartingWindow(RunningTaskInfo taskInfo, IBinder appToken) { + mStartingSurfaceDrawer.addStartingWindow(taskInfo, appToken); + } + + @Override + public void removeStartingWindow(RunningTaskInfo taskInfo) { + mStartingSurfaceDrawer.removeStartingWindow(taskInfo); + } + @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { synchronized (mLock) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java new file mode 100644 index 0000000000000..ee79824ff4a7c --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.startingsurface; + +import static android.content.Context.CONTEXT_RESTRICTED; +import static android.content.res.Configuration.EMPTY; +import static android.view.Display.DEFAULT_DISPLAY; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.hardware.display.DisplayManager; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.util.Slog; +import android.util.SparseArray; +import android.view.Display; +import android.view.View; +import android.view.WindowManager; +import android.window.TaskOrganizer; + +import com.android.internal.R; +import com.android.internal.policy.PhoneWindow; + +import java.util.function.Consumer; + +/** + * Implementation to draw the starting window to an application, and remove the starting window + * until the application displays its own window. + * + * When receive {@link TaskOrganizer#addStartingWindow} callback, use this class to create a + * starting window and attached to the Task, then when the Task want to remove the starting window, + * the TaskOrganizer will receive {@link TaskOrganizer#removeStartingWindow} callback then use this + * class to remove the starting window of the Task. + * @hide + */ +public class StartingSurfaceDrawer { + private static final String TAG = StartingSurfaceDrawer.class.getSimpleName(); + private static final boolean DEBUG_SPLASH_SCREEN = false; + + private final Context mContext; + private final DisplayManager mDisplayManager; + + // TODO(b/131727939) remove this when clearing ActivityRecord + private static final int REMOVE_WHEN_TIMEOUT = 2000; + + public StartingSurfaceDrawer(Context context) { + mContext = context; + mDisplayManager = mContext.getSystemService(DisplayManager.class); + } + + private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final SparseArray mTaskScreenViews = new SparseArray<>(); + + /** Obtain proper context for showing splash screen on the provided display. */ + private Context getDisplayContext(Context context, int displayId) { + if (displayId == DEFAULT_DISPLAY) { + // The default context fits. + return context; + } + + final Display targetDisplay = mDisplayManager.getDisplay(displayId); + if (targetDisplay == null) { + // Failed to obtain the non-default display where splash screen should be shown, + // lets not show at all. + return null; + } + + return context.createDisplayContext(targetDisplay); + } + + /** + * Called when a task need a starting window. + */ + public void addStartingWindow(ActivityManager.RunningTaskInfo taskInfo, IBinder appToken) { + + final ActivityInfo activityInfo = taskInfo.topActivityInfo; + final int displayId = taskInfo.displayId; + if (activityInfo.packageName == null) { + return; + } + + CharSequence nonLocalizedLabel = activityInfo.nonLocalizedLabel; + int labelRes = activityInfo.labelRes; + if (activityInfo.nonLocalizedLabel == null && activityInfo.labelRes == 0) { + ApplicationInfo app = activityInfo.applicationInfo; + nonLocalizedLabel = app.nonLocalizedLabel; + labelRes = app.labelRes; + } + + Context context = mContext; + final int theme = activityInfo.getThemeResource(); + if (DEBUG_SPLASH_SCREEN) { + Slog.d(TAG, "addSplashScreen " + activityInfo.packageName + + ": nonLocalizedLabel=" + nonLocalizedLabel + " theme=" + + Integer.toHexString(theme) + " task= " + taskInfo.taskId); + } + + // Obtain proper context to launch on the right display. + final Context displayContext = getDisplayContext(context, displayId); + if (displayContext == null) { + // Can't show splash screen on requested display, so skip showing at all. + return; + } + context = displayContext; + + if (theme != context.getThemeResId() || labelRes != 0) { + try { + context = context.createPackageContext( + activityInfo.packageName, CONTEXT_RESTRICTED); + context.setTheme(theme); + } catch (PackageManager.NameNotFoundException e) { + // Ignore + } + } + + final Configuration taskConfig = taskInfo.getConfiguration(); + if (taskConfig != null && !taskConfig.equals(EMPTY)) { + if (DEBUG_SPLASH_SCREEN) { + Slog.d(TAG, "addSplashScreen: creating context based" + + " on task Configuration " + taskConfig + " for splash screen"); + } + final Context overrideContext = context.createConfigurationContext(taskConfig); + overrideContext.setTheme(theme); + final TypedArray typedArray = overrideContext.obtainStyledAttributes( + com.android.internal.R.styleable.Window); + final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0); + if (resId != 0 && overrideContext.getDrawable(resId) != null) { + // We want to use the windowBackground for the override context if it is + // available, otherwise we use the default one to make sure a themed starting + // window is displayed for the app. + if (DEBUG_SPLASH_SCREEN) { + Slog.d(TAG, "addSplashScreen: apply overrideConfig" + + taskConfig + " to starting window resId=" + resId); + } + context = overrideContext; + } + typedArray.recycle(); + } + + int windowFlags = 0; + if ((activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { + windowFlags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; + } + + final boolean[] showWallpaper = new boolean[1]; + final int[] splashscreenContentResId = new int[1]; + getWindowResFromContext(context, a -> { + splashscreenContentResId[0] = + a.getResourceId(R.styleable.Window_windowSplashscreenContent, 0); + showWallpaper[0] = a.getBoolean(R.styleable.Window_windowShowWallpaper, false); + }); + if (showWallpaper[0]) { + windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; + } + + final PhoneWindow win = new PhoneWindow(context); + win.setIsStartingWindow(true); + + CharSequence label = context.getResources().getText(labelRes, null); + // Only change the accessibility title if the label is localized + if (label != null) { + win.setTitle(label, true); + } else { + win.setTitle(nonLocalizedLabel, false); + } + + win.setType(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); + + // Assumes it's safe to show starting windows of launched apps while + // the keyguard is being hidden. This is okay because starting windows never show + // secret information. + // TODO(b/113840485): Occluded may not only happen on default display + if (displayId == DEFAULT_DISPLAY) { + windowFlags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; + } + + // Force the window flags: this is a fake window, so it is not really + // touchable or focusable by the user. We also add in the ALT_FOCUSABLE_IM + // flag because we do know that the next window will take input + // focus, so we want to get the IME window up on top of us right away. + win.setFlags(windowFlags + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, + windowFlags + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); + + final int iconRes = activityInfo.getIconResource(); + final int logoRes = activityInfo.getLogoResource(); + win.setDefaultIcon(iconRes); + win.setDefaultLogo(logoRes); + + win.setLayout(WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT); + + final WindowManager.LayoutParams params = win.getAttributes(); + params.token = appToken; + params.packageName = activityInfo.packageName; + params.windowAnimations = win.getWindowStyle().getResourceId( + com.android.internal.R.styleable.Window_windowAnimationStyle, 0); + params.privateFlags |= + WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED; + params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; + // Setting as trusted overlay to let touches pass through. This is safe because this + // window is controlled by the system. + params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; + + final Resources res = context.getResources(); + final boolean supportsScreen = res != null && (res.getCompatibilityInfo() != null + && res.getCompatibilityInfo().supportsScreen()); + if (!supportsScreen) { + params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; + } + + params.setTitle("Splash Screen " + activityInfo.packageName); + addSplashscreenContent(win, context, splashscreenContentResId[0]); + + final View view = win.getDecorView(); + + if (DEBUG_SPLASH_SCREEN) { + Slog.d(TAG, "Adding splash screen window for " + + activityInfo.packageName + " / " + appToken + ": " + view); + } + final WindowManager wm = context.getSystemService(WindowManager.class); + postAddWindow(taskInfo.taskId, appToken, view, wm, params); + } + + /** + * Called when the content of a task is ready to show, starting window can be removed. + */ + public void removeStartingWindow(ActivityManager.RunningTaskInfo taskInfo) { + if (DEBUG_SPLASH_SCREEN) { + Slog.d(TAG, "Task start finish, remove starting surface for task " + taskInfo.taskId); + } + mHandler.post(() -> removeWindowSynced(taskInfo.taskId)); + } + + protected void postAddWindow(int taskId, IBinder appToken, + View view, WindowManager wm, WindowManager.LayoutParams params) { + mHandler.post(() -> { + boolean shouldSaveView = true; + try { + wm.addView(view, params); + } catch (WindowManager.BadTokenException e) { + // ignore + Slog.w(TAG, appToken + " already running, starting window not displayed. " + + e.getMessage()); + shouldSaveView = false; + } catch (RuntimeException e) { + // don't crash if something else bad happens, for example a + // failure loading resources because we are loading from an app + // on external storage that has been unmounted. + Slog.w(TAG, appToken + " failed creating starting window", e); + shouldSaveView = false; + } finally { + if (view != null && view.getParent() == null) { + Slog.w(TAG, "view not successfully added to wm, removing view"); + wm.removeViewImmediate(view); + shouldSaveView = false; + } + } + + if (shouldSaveView) { + removeWindowSynced(taskId); + mHandler.postDelayed(() -> removeWindowSynced(taskId), REMOVE_WHEN_TIMEOUT); + final TaskScreenView tView = new TaskScreenView(view); + mTaskScreenViews.put(taskId, tView); + } + }); + } + + protected void removeWindowSynced(int taskId) { + final TaskScreenView preView = mTaskScreenViews.get(taskId); + if (preView != null) { + if (preView.mDecorView != null) { + if (DEBUG_SPLASH_SCREEN) { + Slog.v(TAG, "Removing splash screen window for task: " + taskId); + } + final WindowManager wm = preView.mDecorView.getContext() + .getSystemService(WindowManager.class); + wm.removeView(preView.mDecorView); + } + mTaskScreenViews.remove(taskId); + } + } + + private void getWindowResFromContext(Context ctx, Consumer consumer) { + final TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window); + consumer.accept(a); + a.recycle(); + } + + /** + * Record the views in a starting window. + */ + private static class TaskScreenView { + private final View mDecorView; + + TaskScreenView(View decorView) { + mDecorView = decorView; + } + } + + private void addSplashscreenContent(PhoneWindow win, Context ctx, + int splashscreenContentResId) { + if (splashscreenContentResId == 0) { + return; + } + final Drawable drawable = ctx.getDrawable(splashscreenContentResId); + if (drawable == null) { + return; + } + + // We wrap this into a view so the system insets get applied to the drawable. + final View v = new View(ctx); + v.setBackground(drawable); + win.setContentView(v); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index e4155a257ceea..fdf4d31f0281a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import android.app.ActivityManager.RunningTaskInfo; +import android.content.Context; import android.content.pm.ParceledListSlice; import android.graphics.Rect; import android.os.Binder; @@ -71,6 +72,8 @@ public class ShellTaskOrganizerTests { @Mock private ITaskOrganizerController mTaskOrganizerController; + @Mock + private Context mContext; ShellTaskOrganizer mOrganizer; private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class); @@ -106,7 +109,7 @@ public class ShellTaskOrganizerTests { .when(mTaskOrganizerController).registerTaskOrganizer(any()); } catch (RemoteException e) {} mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue, - mTransactionPool, mTestExecutor, mTestExecutor)); + mTransactionPool, mTestExecutor, mTestExecutor, mContext)); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java new file mode 100644 index 0000000000000..f5628abb100f6 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package unittest.src.com.android.wm.shell.startingsurface; + +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.testing.TestableContext; +import android.view.View; +import android.view.WindowManager; +import android.view.WindowMetrics; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.wm.shell.startingsurface.StartingSurfaceDrawer; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for the starting surface drawer. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class StartingSurfaceDrawerTests { + @Mock + private IBinder mBinder; + @Mock + private WindowManager mMockWindowManager; + + TestStartingSurfaceDrawer mStartingSurfaceDrawer; + + static final class TestStartingSurfaceDrawer extends StartingSurfaceDrawer{ + int mAddWindowForTask = 0; + + TestStartingSurfaceDrawer(Context context) { + super(context); + } + + @Override + protected void postAddWindow(int taskId, IBinder appToken, + View view, WindowManager wm, WindowManager.LayoutParams params) { + // listen for addView + mAddWindowForTask = taskId; + } + + @Override + protected void removeWindowSynced(int taskId) { + // listen for removeView + if (mAddWindowForTask == taskId) { + mAddWindowForTask = 0; + } + } + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + final TestableContext context = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + final WindowManager realWindowManager = context.getSystemService(WindowManager.class); + final WindowMetrics metrics = realWindowManager.getMaximumWindowMetrics(); + context.addMockSystemService(WindowManager.class, mMockWindowManager); + + spyOn(context); + spyOn(realWindowManager); + try { + doReturn(context).when(context).createPackageContext(anyString(), anyInt()); + } catch (PackageManager.NameNotFoundException e) { + // + } + doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics(); + doNothing().when(mMockWindowManager).addView(any(), any()); + + mStartingSurfaceDrawer = spy(new TestStartingSurfaceDrawer(context)); + } + + @Test + public void testAddSplashScreenSurface() { + final int taskId = 1; + final Handler mainLoop = new Handler(Looper.getMainLooper()); + final ActivityManager.RunningTaskInfo taskInfo = + createTaskInfo(taskId, WINDOWING_MODE_FULLSCREEN); + mStartingSurfaceDrawer.addStartingWindow(taskInfo, mBinder); + waitHandlerIdle(mainLoop); + verify(mStartingSurfaceDrawer).postAddWindow(eq(taskId), eq(mBinder), any(), any(), any()); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId); + + mStartingSurfaceDrawer.removeStartingWindow(taskInfo); + waitHandlerIdle(mainLoop); + verify(mStartingSurfaceDrawer).removeWindowSynced(eq(taskId)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0); + } + + private ActivityManager.RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { + ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); + final ActivityInfo info = new ActivityInfo(); + info.applicationInfo = new ApplicationInfo(); + info.packageName = "test"; + info.theme = android.R.style.Theme; + taskInfo.topActivityInfo = info; + taskInfo.taskId = taskId; + taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); + return taskInfo; + } + + private static void waitHandlerIdle(Handler handler) { + handler.runWithScissors(() -> { }, 0 /* timeout */); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index bdca503f40c1e..ee79a2bd68fb1 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -176,9 +176,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue, - ShellExecutor mainExecutor, TransactionPool transactionPool) { + ShellExecutor mainExecutor, TransactionPool transactionPool, Context context) { return new ShellTaskOrganizer(syncQueue, transactionPool, - mainExecutor, AnimationThread.instance().getExecutor()); + mainExecutor, AnimationThread.instance().getExecutor(), context); } @BindsOptionalOf diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 4b5518c87d23d..64aeef6e48105 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -652,8 +652,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Information about an application starting window if displayed. // Note: these are de-referenced before the starting window animates away. StartingData mStartingData; - WindowState startingWindow; - WindowManagerPolicy.StartingSurface startingSurface; + WindowState mStartingWindow; + WindowManagerPolicy.StartingSurface mStartingSurface; boolean startingDisplayed; boolean startingMoved; @@ -951,10 +951,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn); pw.print(" mIsExiting="); pw.println(mIsExiting); } - if (startingWindow != null || startingSurface != null + if (mStartingWindow != null || mStartingSurface != null || startingDisplayed || startingMoved || mVisibleSetFromTransferredStartingWindow) { - pw.print(prefix); pw.print("startingWindow="); pw.print(startingWindow); - pw.print(" startingSurface="); pw.print(startingSurface); + pw.print(prefix); pw.print("startingWindow="); pw.print(mStartingWindow); + pw.print(" startingSurface="); pw.print(mStartingSurface); pw.print(" startingDisplayed="); pw.print(startingDisplayed); pw.print(" startingMoved="); pw.print(startingMoved); pw.println(" mVisibleSetFromTransferredStartingWindow=" @@ -1897,16 +1897,16 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Aborted starting %s: startingData=%s", ActivityRecord.this, mStartingData); - startingWindow = null; + mStartingWindow = null; mStartingData = null; abort = true; } else { - startingSurface = surface; + mStartingSurface = surface; } if (!abort) { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Added starting %s: startingWindow=%s startingView=%s", - ActivityRecord.this, startingWindow, startingSurface); + ActivityRecord.this, mStartingWindow, mStartingSurface); } } if (abort) { @@ -1958,7 +1958,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } void removeStartingWindow() { - if (startingWindow == null) { + if (mStartingWindow == null) { if (mStartingData != null) { // Starting window has not been added yet, but it is scheduled to be added. // Go ahead and cancel the request. @@ -1970,10 +1970,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A final WindowManagerPolicy.StartingSurface surface; if (mStartingData != null) { - surface = startingSurface; + surface = mStartingSurface; mStartingData = null; - startingSurface = null; - startingWindow = null; + mStartingSurface = null; + mStartingWindow = null; startingDisplayed = false; if (surface == null) { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "startingWindow was set but " @@ -1989,7 +1989,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s" - + " startingView=%s Callers=%s", this, startingWindow, startingSurface, + + " startingView=%s Callers=%s", this, mStartingWindow, mStartingSurface, Debug.getCallers(5)); @@ -3444,8 +3444,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return false; } - final WindowState tStartingWindow = fromActivity.startingWindow; - if (tStartingWindow != null && fromActivity.startingSurface != null) { + final WindowState tStartingWindow = fromActivity.mStartingWindow; + if (tStartingWindow != null && fromActivity.mStartingSurface != null) { // In this case, the starting icon has already been displayed, so start // letting windows get shown immediately without any more transitions. getDisplayContent().mSkipAppTransitionAnimation = true; @@ -3464,14 +3464,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Transfer the starting window over to the new token. mStartingData = fromActivity.mStartingData; - startingSurface = fromActivity.startingSurface; + mStartingSurface = fromActivity.mStartingSurface; startingDisplayed = fromActivity.startingDisplayed; fromActivity.startingDisplayed = false; - startingWindow = tStartingWindow; + mStartingWindow = tStartingWindow; reportedVisible = fromActivity.reportedVisible; fromActivity.mStartingData = null; - fromActivity.startingSurface = null; - fromActivity.startingWindow = null; + fromActivity.mStartingSurface = null; + fromActivity.mStartingWindow = null; fromActivity.startingMoved = true; tStartingWindow.mToken = this; tStartingWindow.mActivityRecord = this; @@ -4321,9 +4321,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { // If we are being set visible, and the starting window is not yet displayed, // then make sure it doesn't get displayed. - if (startingWindow != null && !startingWindow.isDrawn()) { - startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); - startingWindow.mLegacyPolicyVisibilityAfterAnim = false; + if (mStartingWindow != null && !mStartingWindow.isDrawn()) { + mStartingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY); + mStartingWindow.mLegacyPolicyVisibilityAfterAnim = false; } // We are becoming visible, so better freeze the screen with the windows that are // getting visible so we also wait for them. @@ -5406,7 +5406,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // We now have a good window to show, remove dead placeholders removeDeadWindows(); - if (startingWindow != null) { + if (mStartingWindow != null) { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Finish starting %s" + ": first real window is shown, no animation", win.mToken); // If this initial window is animating, stop it -- we will do an animation to reveal @@ -5581,7 +5581,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean updateDrawnWindowStates(WindowState w) { w.setDrawnStateEvaluated(true /*evaluated*/); - if (DEBUG_STARTING_WINDOW_VERBOSE && w == startingWindow) { + if (DEBUG_STARTING_WINDOW_VERBOSE && w == mStartingWindow) { Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen=" + w.isOnScreen() + " allDrawn=" + allDrawn + " freezingScreen=" + mFreezingScreen); } @@ -5618,7 +5618,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - if (w != startingWindow) { + if (w != mStartingWindow) { if (w.isInteresting()) { // Add non-main window as interesting since the main app has already been added if (findMainWindow(false /* includeStartingApp */) != w) { @@ -5858,7 +5858,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void postWindowRemoveStartingWindowCleanup(WindowState win) { // TODO: Something smells about the code below...Is there a better way? - if (startingWindow == win) { + if (mStartingWindow == win) { ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Notify removed startingWindow %s", win); removeStartingWindow(); } else if (mChildren.size() == 0) { @@ -5872,7 +5872,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // window in the token. setVisible(false); } - } else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) { + } else if (mChildren.size() == 1 && mStartingSurface != null && !isRelaunching()) { // If this is the last window except for a starting transition window, // we need to get rid of the starting transition. ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Last window, removing starting window %s", win); @@ -7704,8 +7704,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A proto.write(NUM_DRAWN_WINDOWS, mNumDrawnWindows); proto.write(ALL_DRAWN, allDrawn); proto.write(LAST_ALL_DRAWN, mLastAllDrawn); - if (startingWindow != null) { - startingWindow.writeIdentifierToProto(proto, STARTING_WINDOW); + if (mStartingWindow != null) { + mStartingWindow.writeIdentifierToProto(proto, STARTING_WINDOW); } proto.write(STARTING_DISPLAYED, startingDisplayed); proto.write(STARTING_MOVED, startingMoved); diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 97912c1965ac4..88f833f9989b7 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -575,7 +575,7 @@ public class AppTransitionController { app.allDrawn = true; // Ensure that apps that are mid-starting are also scheduled to have their // starting windows removed after the animation is complete - if (app.startingWindow != null && !app.startingWindow.mAnimatingExit) { + if (app.mStartingWindow != null && !app.mStartingWindow.mAnimatingExit) { app.removeStartingWindow(); } @@ -651,7 +651,7 @@ public class AppTransitionController { + "startingMoved=%b isRelaunching()=%b startingWindow=%s", activity, activity.allDrawn, activity.startingDisplayed, activity.startingMoved, activity.isRelaunching(), - activity.startingWindow); + activity.mStartingWindow); final boolean allDrawn = activity.allDrawn && !activity.isRelaunching(); diff --git a/services/core/java/com/android/server/wm/SplashScreenStartingData.java b/services/core/java/com/android/server/wm/SplashScreenStartingData.java index 726b7dac69389..50a101d58ce37 100644 --- a/services/core/java/com/android/server/wm/SplashScreenStartingData.java +++ b/services/core/java/com/android/server/wm/SplashScreenStartingData.java @@ -53,8 +53,9 @@ class SplashScreenStartingData extends StartingData { @Override StartingSurface createStartingSurface(ActivityRecord activity) { - return mService.mPolicy.addSplashScreen(activity.token, mPkg, mTheme, mCompatInfo, - mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags, - mMergedOverrideConfiguration, activity.getDisplayContent().getDisplayId()); + return mService.mStartingSurfaceController.createSplashScreenStartingSurface( + activity, mPkg, mTheme, mCompatInfo, mNonLocalizedLabel, mLabelRes, mIcon, + mLogo, mWindowFlags, mMergedOverrideConfiguration, + activity.getDisplayContent().getDisplayId()); } } diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java new file mode 100644 index 0000000000000..6d7ddf607ffef --- /dev/null +++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.content.res.CompatibilityInfo; +import android.content.res.Configuration; +import android.os.SystemProperties; + +import com.android.server.policy.WindowManagerPolicy.StartingSurface; + +/** + * Managing to create and release a starting window surface. + */ +public class StartingSurfaceController { + + /** Set to {@code true} to enable shell starting surface drawer. */ + private static final boolean DEBUG_ENABLE_SHELL_DRAWER = + SystemProperties.getBoolean("persist.debug.shell_starting_surface", false); + + private final WindowManagerService mService; + + public StartingSurfaceController(WindowManagerService wm) { + mService = wm; + } + + StartingSurface createSplashScreenStartingSurface(ActivityRecord activity, String packageName, + int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, + int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) { + if (!DEBUG_ENABLE_SHELL_DRAWER) { + return mService.mPolicy.addSplashScreen(activity.token, packageName, theme, + compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, + overrideConfig, displayId); + } + + final Task task = activity.getTask(); + if (task != null && mService.mAtmService.mTaskOrganizerController.addStartingWindow(task, + activity.token)) { + return new SplashScreenContainerSurface(task); + } + return null; + } + + private final class SplashScreenContainerSurface implements StartingSurface { + private final Task mTask; + + SplashScreenContainerSurface(Task task) { + mTask = task; + } + + @Override + public void remove() { + mService.mAtmService.mTaskOrganizerController.removeStartingWindow(mTask); + } + } +} diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 7423763487bb9..bb8b4a5f966fb 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -116,6 +116,28 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return mTaskOrganizer.asBinder(); } + void addStartingWindow(Task task, IBinder appToken) { + final RunningTaskInfo taskInfo = task.getTaskInfo(); + mDeferTaskOrgCallbacksConsumer.accept(() -> { + try { + mTaskOrganizer.addStartingWindow(taskInfo, appToken); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskStart callback", e); + } + }); + } + + void removeStartingWindow(Task task) { + final RunningTaskInfo taskInfo = task.getTaskInfo(); + mDeferTaskOrgCallbacksConsumer.accept(() -> { + try { + mTaskOrganizer.removeStartingWindow(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onStartTaskFinished callback", e); + } + }); + } + SurfaceControl prepareLeash(Task task, boolean visible, String reason) { SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(), reason); if (!task.mCreatedByOrganizer && !visible) { @@ -218,6 +240,14 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mUid = uid; } + void addStartingWindow(Task t, IBinder appToken) { + mOrganizer.addStartingWindow(t, appToken); + } + + void removeStartingWindow(Task t) { + mOrganizer.removeStartingWindow(t); + } + /** * Register this task with this state, but doesn't trigger the task appeared callback to * the organizer. @@ -390,6 +420,27 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return !ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, winMode); } + boolean addStartingWindow(Task task, IBinder appToken) { + final Task rootTask = task.getRootTask(); + if (rootTask == null || rootTask.mTaskOrganizer == null) { + return false; + } + final TaskOrganizerState state = + mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder()); + state.addStartingWindow(task, appToken); + return true; + } + + void removeStartingWindow(Task task) { + final Task rootTask = task.getRootTask(); + if (rootTask == null || rootTask.mTaskOrganizer == null) { + return; + } + final TaskOrganizerState state = + mTaskOrganizerStates.get(rootTask.mTaskOrganizer.asBinder()); + state.removeStartingWindow(task); + } + void onTaskAppeared(ITaskOrganizer organizer, Task task) { final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); state.addTask(task); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e7d9e6b63bb69..69c09896d6691 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -490,6 +490,7 @@ public class WindowManagerService extends IWindowManager.Stub final Map mKeyInterceptionInfoForToken = Collections.synchronizedMap(new ArrayMap<>()); + final StartingSurfaceController mStartingSurfaceController; private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { @Override @@ -1375,6 +1376,8 @@ public class WindowManagerService extends IWindowManager.Stub mContext.getResources()); setGlobalShadowSettings(); + + mStartingSurfaceController = new StartingSurfaceController(this); } private void setGlobalShadowSettings() { @@ -1559,7 +1562,7 @@ public class WindowManagerService extends IWindowManager.Stub ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token " + ".%s Aborting.", token); return WindowManagerGlobal.ADD_APP_EXITING; - } else if (type == TYPE_APPLICATION_STARTING && activity.startingWindow != null) { + } else if (type == TYPE_APPLICATION_STARTING && activity.mStartingWindow != null) { ProtoLog.w(WM_ERROR, "Attempted to add starting window to token with already existing" + " starting window"); @@ -1708,7 +1711,7 @@ public class WindowManagerService extends IWindowManager.Stub final ActivityRecord tokenActivity = token.asActivityRecord(); if (type == TYPE_APPLICATION_STARTING && tokenActivity != null) { - tokenActivity.startingWindow = win; + tokenActivity.mStartingWindow = win; ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "addWindow: %s startingWindow=%s", activity, win); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 5ab4ba1aaa255..f29f62a3a76f5 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1949,7 +1949,7 @@ class WindowState extends WindowContainer implements WindowManagerP // Unnecessary to redraw a drawn starting window. return; } - } else if (mActivityRecord.startingWindow != null) { + } else if (mActivityRecord.mStartingWindow != null) { // If the activity has an active starting window, there is no need to wait for the // main window. return; @@ -2795,13 +2795,13 @@ class WindowState extends WindowContainer implements WindowManagerP void adjustStartingWindowFlags() { if (mAttrs.type == TYPE_BASE_APPLICATION && mActivityRecord != null - && mActivityRecord.startingWindow != null) { + && mActivityRecord.mStartingWindow != null) { // Special handling of starting window over the base // window of the app: propagate lock screen flags to it, // to provide the correct semantics while starting. final int mask = FLAG_SHOW_WHEN_LOCKED | FLAG_DISMISS_KEYGUARD | FLAG_ALLOW_LOCK_WHILE_SCREEN_ON; - WindowManager.LayoutParams sa = mActivityRecord.startingWindow.mAttrs; + WindowManager.LayoutParams sa = mActivityRecord.mStartingWindow.mAttrs; sa.flags = (sa.flags & ~mask) | (mAttrs.flags & mask); } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 72aa7661a5286..0fd8146082dc0 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -532,7 +532,7 @@ class WindowStateAnimator { void destroySurfaceLocked() { final ActivityRecord activity = mWin.mActivityRecord; if (activity != null) { - if (mWin == activity.startingWindow) { + if (mWin == activity.mStartingWindow) { activity.startingDisplayed = false; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 28d5ffe342a0e..f77454d440f91 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -437,7 +437,7 @@ public class AppWindowTokenTests extends WindowTestsBase { false /* newTask */, false /* keepCurTransition */, null /* options */); middle.makeFinishingLocked(); - assertNull(mActivity.startingWindow); + assertNull(mActivity.mStartingWindow); assertHasStartingWindow(middle); final ActivityRecord top = new ActivityBuilder(mWm.mAtmService) @@ -449,7 +449,7 @@ public class AppWindowTokenTests extends WindowTestsBase { mStack.startActivityLocked(top, null /* focusedTopActivity */, false /* newTask */, false /* keepCurTransition */, null /* options */); - assertNull(middle.startingWindow); + assertNull(middle.mStartingWindow); assertHasStartingWindow(top); assertTrue(top.isVisible()); // The activity was visible by mVisibleSetFromTransferredStartingWindow, so after its @@ -573,14 +573,14 @@ public class AppWindowTokenTests extends WindowTestsBase { } private void assertHasStartingWindow(ActivityRecord atoken) { - assertNotNull(atoken.startingSurface); + assertNotNull(atoken.mStartingSurface); assertNotNull(atoken.mStartingData); - assertNotNull(atoken.startingWindow); + assertNotNull(atoken.mStartingWindow); } private void assertNoStartingWindow(ActivityRecord atoken) { - assertNull(atoken.startingSurface); - assertNull(atoken.startingWindow); + assertNull(atoken.mStartingSurface); + assertNull(atoken.mStartingWindow); assertNull(atoken.mStartingData); atoken.forAllWindows(windowState -> { assertFalse(windowState.getBaseType() == TYPE_APPLICATION_STARTING); diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index db5c7965ebee3..dc639f0d84dc4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -117,7 +117,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, activity, "Starting window", 0 /* ownerId */, 0 /* userId*/, false /* internalWindows */, wm, mock(Session.class), iWindow, mPowerManagerWrapper); - activity.startingWindow = window; + activity.mStartingWindow = window; } if (mRunnableWhenAddingSplashScreen != null) { mRunnableWhenAddingSplashScreen.run(); @@ -126,7 +126,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { return () -> { synchronized (wm.mGlobalLock) { activity.removeChild(window); - activity.startingWindow = null; + activity.mStartingWindow = null; } }; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 7a41c02de3b68..8fe65eb2747da 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -505,6 +505,14 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testTileAddRemoveChild() { ITaskOrganizer listener = new ITaskOrganizer.Stub() { + @Override + public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) { + + } + + @Override + public void removeStartingWindow(ActivityManager.RunningTaskInfo info) { } + @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { } @@ -562,6 +570,14 @@ public class WindowOrganizerTests extends WindowTestsBase { final ArrayList lastReportedTiles = new ArrayList<>(); final boolean[] called = {false}; ITaskOrganizer listener = new ITaskOrganizer.Stub() { + @Override + public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) { + + } + + @Override + public void removeStartingWindow(ActivityManager.RunningTaskInfo info) { } + @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { } @@ -625,6 +641,14 @@ public class WindowOrganizerTests extends WindowTestsBase { public void testHierarchyTransaction() { final ArrayMap lastReportedTiles = new ArrayMap<>(); ITaskOrganizer listener = new ITaskOrganizer.Stub() { + @Override + public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) { + + } + + @Override + public void removeStartingWindow(ActivityManager.RunningTaskInfo info) { } + @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { } @@ -762,6 +786,10 @@ public class WindowOrganizerTests extends WindowTestsBase { class StubOrganizer extends ITaskOrganizer.Stub { RunningTaskInfo mInfo; + @Override + public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) { } + @Override + public void removeStartingWindow(ActivityManager.RunningTaskInfo info) { } @Override public void onTaskAppeared(RunningTaskInfo info, SurfaceControl leash) { mInfo = info; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 88a3f972f83ec..3ce9ee24e6c77 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -551,7 +551,7 @@ public class WindowStateTests extends WindowTestsBase { TYPE_BASE_APPLICATION, "startingApp"); final WindowState startingWindow = createWindow(null /* parent */, TYPE_APPLICATION_STARTING, startingApp.mToken, "starting"); - startingApp.mActivityRecord.startingWindow = startingWindow; + startingApp.mActivityRecord.mStartingWindow = startingWindow; final WindowState keyguardHostWindow = mNotificationShadeWindow; final WindowState allDrawnApp = mAppWindow; allDrawnApp.mActivityRecord.allDrawn = true; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index d5fb3c5f5e3d2..1935a55de9891 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1075,6 +1075,12 @@ class WindowTestsBase extends SystemServiceTestsBase { mMoveToSecondaryOnEnter = move; } @Override + public void addStartingWindow(ActivityManager.RunningTaskInfo info, IBinder appToken) { + } + @Override + public void removeStartingWindow(ActivityManager.RunningTaskInfo info) { + } + @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { } @Override