diff options
Diffstat (limited to 'libs')
33 files changed, 728 insertions, 323 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index 7376d9898ab8..cc353dc991e3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -25,6 +25,7 @@ import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -44,6 +45,7 @@ public class ShellInitImpl { private final FullscreenTaskListener mFullscreenTaskListener; private final ShellExecutor mMainExecutor; private final Transitions mTransitions; + private final Optional<StartingSurface> mStartingSurfaceOptional; private final InitImpl mImpl = new InitImpl(); @@ -53,6 +55,7 @@ public class ShellInitImpl { Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<AppPairsController> appPairsOptional, + Optional<StartingSurface> startingSurfaceOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, ShellExecutor mainExecutor) { @@ -62,6 +65,7 @@ public class ShellInitImpl { legacySplitScreenOptional, splitScreenOptional, appPairsOptional, + startingSurfaceOptional, fullscreenTaskListener, transitions, mainExecutor).mImpl; @@ -73,6 +77,7 @@ public class ShellInitImpl { Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<AppPairsController> appPairsOptional, + Optional<StartingSurface> startingSurfaceOptional, FullscreenTaskListener fullscreenTaskListener, Transitions transitions, ShellExecutor mainExecutor) { @@ -85,6 +90,7 @@ public class ShellInitImpl { mFullscreenTaskListener = fullscreenTaskListener; mTransitions = transitions; mMainExecutor = mainExecutor; + mStartingSurfaceOptional = startingSurfaceOptional; } private void init() { @@ -93,6 +99,7 @@ public class ShellInitImpl { mShellTaskOrganizer.addListenerForType( mFullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN); + mStartingSurfaceOptional.ifPresent(mShellTaskOrganizer::initStartingSurface); // Register the shell organizer mShellTaskOrganizer.registerOrganizer(); 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 efc55c4fbe31..9ddeb2fc6e1e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -44,7 +44,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.sizecompatui.SizeCompatUIController; -import com.android.wm.shell.startingsurface.StartingSurfaceDrawer; +import com.android.wm.shell.startingsurface.StartingSurface; import java.io.PrintWriter; import java.util.ArrayList; @@ -110,7 +110,7 @@ public class ShellTaskOrganizer extends TaskOrganizer { private final ArrayMap<IBinder, TaskListener> mLaunchCookieToListener = new ArrayMap<>(); private final Object mLock = new Object(); - private final StartingSurfaceDrawer mStartingSurfaceDrawer; + private StartingSurface mStartingSurface; /** * In charge of showing size compat UI. Can be {@code null} if device doesn't support size @@ -120,23 +120,19 @@ public class ShellTaskOrganizer extends TaskOrganizer { private final SizeCompatUIController mSizeCompatUI; public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context) { - this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */, - new StartingSurfaceDrawer(context, mainExecutor)); + this(null /* taskOrganizerController */, mainExecutor, context, null /* sizeCompatUI */); } public ShellTaskOrganizer(ShellExecutor mainExecutor, Context context, @Nullable SizeCompatUIController sizeCompatUI) { - this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI, - new StartingSurfaceDrawer(context, mainExecutor)); + this(null /* taskOrganizerController */, mainExecutor, context, sizeCompatUI); } @VisibleForTesting ShellTaskOrganizer(ITaskOrganizerController taskOrganizerController, ShellExecutor mainExecutor, - Context context, @Nullable SizeCompatUIController sizeCompatUI, - StartingSurfaceDrawer startingSurfaceDrawer) { + Context context, @Nullable SizeCompatUIController sizeCompatUI) { super(taskOrganizerController, mainExecutor); mSizeCompatUI = sizeCompatUI; - mStartingSurfaceDrawer = startingSurfaceDrawer; } @Override @@ -163,6 +159,15 @@ public class ShellTaskOrganizer extends TaskOrganizer { } /** + * @hide + */ + public void initStartingSurface(StartingSurface startingSurface) { + synchronized (mLock) { + mStartingSurface = startingSurface; + } + } + + /** * Adds a listener for a specific task id. */ public void addListenerForTaskId(TaskListener listener, int taskId) { @@ -254,17 +259,23 @@ public class ShellTaskOrganizer extends TaskOrganizer { @Override public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { - mStartingSurfaceDrawer.addStartingWindow(info, appToken); + if (mStartingSurface != null) { + mStartingSurface.addStartingWindow(info, appToken); + } } @Override public void removeStartingWindow(int taskId) { - mStartingSurfaceDrawer.removeStartingWindow(taskId); + if (mStartingSurface != null) { + mStartingSurface.removeStartingWindow(taskId); + } } @Override public void copySplashScreenView(int taskId) { - mStartingSurfaceDrawer.copySplashScreenView(taskId); + if (mStartingSurface != null) { + mStartingSurface.copySplashScreenView(taskId); + } } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 5992447bd6da..46884fefd69c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -157,6 +157,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, }); options.setLaunchCookie(launchCookie); options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + options.setRemoveWithTaskOrganizer(true); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java index 19c3cf9c462a..57a2b6c7b9db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java @@ -227,24 +227,30 @@ public class BubbleFlyoutView extends FrameLayout { /* * Fade animation for consecutive flyouts. */ - void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, float stackY) { + void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, PointF stackPos, + boolean hideDot) { final Runnable afterFadeOut = () -> { updateFlyoutMessage(flyoutMessage, parentWidth); // Wait for TextViews to layout with updated height. post(() -> { - mFlyoutY = stackY + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; - fade(true /* in */, () -> {} /* after */); + fade(true /* in */, stackPos, hideDot, () -> {} /* after */); } /* after */ ); }; - fade(false /* in */, afterFadeOut); + fade(false /* in */, stackPos, hideDot, afterFadeOut); } /* * Fade-out above or fade-in from below. */ - private void fade(boolean in, Runnable afterFade) { + private void fade(boolean in, PointF stackPos, boolean hideDot, Runnable afterFade) { + mFlyoutY = stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; + setAlpha(in ? 0f : 1f); setTranslationY(in ? mFlyoutY + FLYOUT_FADE_Y : mFlyoutY); + updateFlyoutX(stackPos.x); + setTranslationX(mRestingTranslationX); + updateDot(stackPos, hideDot); + animate() .alpha(in ? 1f : 0f) .setDuration(in ? FLYOUT_FADE_IN_DURATION : FLYOUT_FADE_OUT_DURATION) @@ -287,6 +293,33 @@ public class BubbleFlyoutView extends FrameLayout { mMessageText.setText(flyoutMessage.message); } + void updateFlyoutX(float stackX) { + // Calculate the translation required to position the flyout next to the bubble stack, + // with the desired padding. + mRestingTranslationX = mArrowPointingLeft + ? stackX + mBubbleSize + mFlyoutSpaceFromBubble + : stackX - getWidth() - mFlyoutSpaceFromBubble; + } + + void updateDot(PointF stackPos, boolean hideDot) { + // Calculate the difference in size between the flyout and the 'dot' so that we can + // transform into the dot later. + final float newDotSize = hideDot ? 0f : mNewDotSize; + mFlyoutToDotWidthDelta = getWidth() - newDotSize; + mFlyoutToDotHeightDelta = getHeight() - newDotSize; + + // Calculate the translation values needed to be in the correct 'new dot' position. + final float adjustmentForScaleAway = hideDot ? 0f : (mOriginalDotSize / 2f); + final float dotPositionX = stackPos.x + mDotCenter[0] - adjustmentForScaleAway; + final float dotPositionY = stackPos.y + mDotCenter[1] - adjustmentForScaleAway; + + final float distanceFromFlyoutLeftToDotCenterX = mRestingTranslationX - dotPositionX; + final float distanceFromLayoutTopToDotCenterY = mFlyoutY - dotPositionY; + + mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX; + mTranslationYWhenDot = -distanceFromLayoutTopToDotCenterY; + } + /** Configures the flyout, collapsed into dot form. */ void setupFlyoutStartingAsDot( Bubble.FlyoutMessage flyoutMessage, @@ -322,29 +355,8 @@ public class BubbleFlyoutView extends FrameLayout { mFlyoutY = stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; setTranslationY(mFlyoutY); - - // Calculate the translation required to position the flyout next to the bubble stack, - // with the desired padding. - mRestingTranslationX = mArrowPointingLeft - ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble - : stackPos.x - getWidth() - mFlyoutSpaceFromBubble; - - // Calculate the difference in size between the flyout and the 'dot' so that we can - // transform into the dot later. - final float newDotSize = hideDot ? 0f : mNewDotSize; - mFlyoutToDotWidthDelta = getWidth() - newDotSize; - mFlyoutToDotHeightDelta = getHeight() - newDotSize; - - // Calculate the translation values needed to be in the correct 'new dot' position. - final float adjustmentForScaleAway = hideDot ? 0f : (mOriginalDotSize / 2f); - final float dotPositionX = stackPos.x + mDotCenter[0] - adjustmentForScaleAway; - final float dotPositionY = stackPos.y + mDotCenter[1] - adjustmentForScaleAway; - - final float distanceFromFlyoutLeftToDotCenterX = mRestingTranslationX - dotPositionX; - final float distanceFromLayoutTopToDotCenterY = mFlyoutY - dotPositionY; - - mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX; - mTranslationYWhenDot = -distanceFromLayoutTopToDotCenterY; + updateFlyoutX(stackPos.x); + updateDot(stackPos, hideDot); if (onLayoutComplete != null) { onLayoutComplete.run(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index a8ab4064455c..78820a8fa870 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -2431,7 +2431,7 @@ public class BubbleStackView extends FrameLayout if (mFlyout.getVisibility() == View.VISIBLE) { mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(), - mStackAnimationController.getStackPosition().y); + mStackAnimationController.getStackPosition(), !bubble.showDot()); } else { mFlyout.setVisibility(INVISIBLE); mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index fb70cbe502b0..4bb8e9b6581f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -39,12 +39,12 @@ import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.ScrollCaptureResponse; import android.view.SurfaceControl; -import android.view.SurfaceSession; import android.view.SurfaceControlViewHost; +import android.view.SurfaceSession; import android.view.View; import android.view.ViewGroup; -import android.view.ViewRootImpl; import android.view.WindowManager; import android.view.WindowlessWindowManager; import android.window.ClientWindowFrames; @@ -371,7 +371,11 @@ public class SystemWindows { @Override public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { try { - callbacks.onUnavailable(); + callbacks.onScrollCaptureResponse( + new ScrollCaptureResponse.Builder() + .setDescription("Not Implemented") + .build()); + } catch (RemoteException ex) { // ignore } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 22c97515ad76..bbfbc4098d92 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -362,8 +362,9 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, @Override public void onSnappedToDismiss(boolean bottomOrRight) { - final boolean mainStageToTop = bottomOrRight - && mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT; + final boolean mainStageToTop = + bottomOrRight ? mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT + : mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT; exitSplitScreen(mainStageToTop ? mMainStage : mSideStage); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java new file mode 100644 index 000000000000..2c4ceffcb8f5 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurface.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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 android.os.IBinder; +import android.window.StartingWindowInfo; + +/** + * Interface to engage starting window feature. + */ +public interface StartingSurface { + /** + * Called when a task need a starting window. + */ + void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken); + /** + * Called when the content of a task is ready to show, starting window can be removed. + */ + void removeStartingWindow(int taskId); + /** + * Called when the Task wants to copy the splash screen. + * @param taskId + */ + void copySplashScreenView(int taskId); +} 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 index 5332291fd1bd..814407164750 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -16,15 +16,9 @@ package com.android.wm.shell.startingsurface; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.content.Context.CONTEXT_RESTRICTED; import static android.content.res.Configuration.EMPTY; import static android.view.Display.DEFAULT_DISPLAY; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; @@ -37,7 +31,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.hardware.display.DisplayManager; import android.os.IBinder; -import android.os.RemoteException; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -46,7 +39,6 @@ import android.view.WindowManager; import android.window.SplashScreenView; import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.StartingWindowInfo; -import android.window.TaskOrganizer; import android.window.TaskSnapshot; import com.android.internal.R; @@ -56,19 +48,13 @@ import com.android.wm.shell.common.ShellExecutor; 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. + * A class which able to draw splash screen or snapshot as the starting window for a task. * @hide */ public class StartingSurfaceDrawer { static final String TAG = StartingSurfaceDrawer.class.getSimpleName(); - static final boolean DEBUG_SPLASH_SCREEN = false; - static final boolean DEBUG_TASK_SNAPSHOT = false; + static final boolean DEBUG_SPLASH_SCREEN = StartingWindowController.DEBUG_SPLASH_SCREEN; + static final boolean DEBUG_TASK_SNAPSHOT = StartingWindowController.DEBUG_TASK_SNAPSHOT; private final Context mContext; private final DisplayManager mDisplayManager; @@ -107,106 +93,10 @@ public class StartingSurfaceDrawer { return context.createDisplayContext(targetDisplay); } - private static class PreferredStartingTypeHelper { - private static final int STARTING_TYPE_NO = 0; - private static final int STARTING_TYPE_SPLASH_SCREEN = 1; - private static final int STARTING_TYPE_SNAPSHOT = 2; - - TaskSnapshot mSnapshot; - int mPreferredType; - - PreferredStartingTypeHelper(StartingWindowInfo taskInfo) { - final int parameter = taskInfo.startingWindowTypeParameter; - final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0; - final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0; - final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0; - final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0; - final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0; - mPreferredType = preferredStartingWindowType(taskInfo, newTask, taskSwitch, - processRunning, allowTaskSnapshot, activityCreated); - } - - // reference from ActivityRecord#getStartingWindowType - private int preferredStartingWindowType(StartingWindowInfo windowInfo, - boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated) { - if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { - Slog.d(TAG, "preferredStartingWindowType newTask " + newTask - + " taskSwitch " + taskSwitch - + " processRunning " + processRunning - + " allowTaskSnapshot " + allowTaskSnapshot - + " activityCreated " + activityCreated); - } - - if (newTask || !processRunning || (taskSwitch && !activityCreated)) { - return STARTING_TYPE_SPLASH_SCREEN; - } else if (taskSwitch && allowTaskSnapshot) { - final TaskSnapshot snapshot = getTaskSnapshot(windowInfo.taskInfo.taskId); - if (isSnapshotCompatible(windowInfo, snapshot)) { - return STARTING_TYPE_SNAPSHOT; - } - if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) { - return STARTING_TYPE_SPLASH_SCREEN; - } - return STARTING_TYPE_NO; - } else { - return STARTING_TYPE_NO; - } - } - - /** - * Returns {@code true} if the task snapshot is compatible with this activity (at least the - * rotation must be the same). - */ - private boolean isSnapshotCompatible(StartingWindowInfo windowInfo, TaskSnapshot snapshot) { - if (snapshot == null) { - if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { - Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId); - } - return false; - } - - final int taskRotation = windowInfo.taskInfo.configuration - .windowConfiguration.getRotation(); - final int snapshotRotation = snapshot.getRotation(); - if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { - Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation - + " snapshot " + snapshotRotation); - } - return taskRotation == snapshotRotation; - } - - private TaskSnapshot getTaskSnapshot(int taskId) { - if (mSnapshot != null) { - return mSnapshot; - } - try { - mSnapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId, - false/* isLowResolution */); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to get snapshot for task: " + taskId + ", from: " + e); - return null; - } - return mSnapshot; - } - } - /** - * Called when a task need a starting window. + * Called when a task need a splash screen starting window. */ - public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { - final PreferredStartingTypeHelper helper = - new PreferredStartingTypeHelper(windowInfo); - if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SPLASH_SCREEN) { - addSplashScreenStartingWindow(windowInfo, appToken); - } else if (helper.mPreferredType == PreferredStartingTypeHelper.STARTING_TYPE_SNAPSHOT) { - final TaskSnapshot snapshot = helper.mSnapshot; - makeTaskSnapshotWindow(windowInfo, appToken, snapshot); - } - // If prefer don't show, then don't show! - } - - private void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { + public void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { final RunningTaskInfo taskInfo = windowInfo.taskInfo; final ActivityInfo activityInfo = taskInfo.topActivityInfo; if (activityInfo == null) { @@ -378,8 +268,8 @@ public class StartingSurfaceDrawer { /** * Called when a task need a snapshot starting window. */ - private void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, - IBinder appToken, TaskSnapshot snapshot) { + void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, IBinder appToken, + TaskSnapshot snapshot) { final int taskId = startingWindowInfo.taskInfo.taskId; final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken, snapshot, mMainExecutor, () -> removeWindowSynced(taskId) /* clearWindow */); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java new file mode 100644 index 000000000000..73bf8ac90c29 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -0,0 +1,196 @@ +/* + * 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.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; +import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; + +import android.app.ActivityTaskManager; +import android.content.Context; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; +import android.window.StartingWindowInfo; +import android.window.TaskOrganizer; +import android.window.TaskSnapshot; + +import com.android.wm.shell.common.ShellExecutor; + +/** + * 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 StartingWindowController { + private static final String TAG = StartingWindowController.class.getSimpleName(); + static final boolean DEBUG_SPLASH_SCREEN = false; + static final boolean DEBUG_TASK_SNAPSHOT = false; + + private final StartingSurfaceDrawer mStartingSurfaceDrawer; + private final StartingTypeChecker mStartingTypeChecker = new StartingTypeChecker(); + private final StartingSurfaceImpl mImpl = new StartingSurfaceImpl(); + + public StartingWindowController(Context context, ShellExecutor mainExecutor) { + mStartingSurfaceDrawer = new StartingSurfaceDrawer(context, mainExecutor); + } + + /** + * Provide the implementation for Shell Module. + */ + public StartingSurface asStartingSurface() { + return mImpl; + } + + private static class StartingTypeChecker { + TaskSnapshot mSnapshot; + + StartingTypeChecker() { } + + private void reset() { + mSnapshot = null; + } + + private @StartingWindowInfo.StartingWindowType int + estimateStartingWindowType(StartingWindowInfo windowInfo) { + reset(); + final int parameter = windowInfo.startingWindowTypeParameter; + final boolean newTask = (parameter & TYPE_PARAMETER_NEW_TASK) != 0; + final boolean taskSwitch = (parameter & TYPE_PARAMETER_TASK_SWITCH) != 0; + final boolean processRunning = (parameter & TYPE_PARAMETER_PROCESS_RUNNING) != 0; + final boolean allowTaskSnapshot = (parameter & TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT) != 0; + final boolean activityCreated = (parameter & TYPE_PARAMETER_ACTIVITY_CREATED) != 0; + return estimateStartingWindowType(windowInfo, newTask, taskSwitch, + processRunning, allowTaskSnapshot, activityCreated); + } + + // reference from ActivityRecord#getStartingWindowType + private int estimateStartingWindowType(StartingWindowInfo windowInfo, + boolean newTask, boolean taskSwitch, boolean processRunning, + boolean allowTaskSnapshot, boolean activityCreated) { + if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { + Slog.d(TAG, "preferredStartingWindowType newTask " + newTask + + " taskSwitch " + taskSwitch + + " processRunning " + processRunning + + " allowTaskSnapshot " + allowTaskSnapshot + + " activityCreated " + activityCreated); + } + if (newTask || !processRunning || (taskSwitch && !activityCreated)) { + return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } + if (taskSwitch && allowTaskSnapshot) { + final TaskSnapshot snapshot = getTaskSnapshot(windowInfo.taskInfo.taskId); + if (isSnapshotCompatible(windowInfo, snapshot)) { + return STARTING_WINDOW_TYPE_SNAPSHOT; + } + if (windowInfo.taskInfo.topActivityType != ACTIVITY_TYPE_HOME) { + return STARTING_WINDOW_TYPE_SPLASH_SCREEN; + } + } + return STARTING_WINDOW_TYPE_NONE; + } + + /** + * Returns {@code true} if the task snapshot is compatible with this activity (at least the + * rotation must be the same). + */ + private boolean isSnapshotCompatible(StartingWindowInfo windowInfo, TaskSnapshot snapshot) { + if (snapshot == null) { + if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { + Slog.d(TAG, "isSnapshotCompatible no snapshot " + windowInfo.taskInfo.taskId); + } + return false; + } + + final int taskRotation = windowInfo.taskInfo.configuration + .windowConfiguration.getRotation(); + final int snapshotRotation = snapshot.getRotation(); + if (DEBUG_SPLASH_SCREEN || DEBUG_TASK_SNAPSHOT) { + Slog.d(TAG, "isSnapshotCompatible rotation " + taskRotation + + " snapshot " + snapshotRotation); + } + return taskRotation == snapshotRotation; + } + + private TaskSnapshot getTaskSnapshot(int taskId) { + if (mSnapshot != null) { + return mSnapshot; + } + try { + mSnapshot = ActivityTaskManager.getService().getTaskSnapshot(taskId, + false/* isLowResolution */); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to get snapshot for task: " + taskId + ", from: " + e); + return null; + } + return mSnapshot; + } + } + + /** + * Called when a task need a starting window. + */ + void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { + final int suggestionType = mStartingTypeChecker.estimateStartingWindowType(windowInfo); + if (suggestionType == STARTING_WINDOW_TYPE_SPLASH_SCREEN) { + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken); + } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { + final TaskSnapshot snapshot = mStartingTypeChecker.mSnapshot; + mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, snapshot); + } + // If prefer don't show, then don't show! + } + + void copySplashScreenView(int taskId) { + mStartingSurfaceDrawer.copySplashScreenView(taskId); + } + + /** + * Called when the content of a task is ready to show, starting window can be removed. + */ + void removeStartingWindow(int taskId) { + mStartingSurfaceDrawer.removeStartingWindow(taskId); + } + + private class StartingSurfaceImpl implements StartingSurface { + + @Override + public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { + StartingWindowController.this.addStartingWindow(windowInfo, appToken); + } + + @Override + public void removeStartingWindow(int taskId) { + StartingWindowController.this.removeStartingWindow(taskId); + } + + @Override + public void copySplashScreenView(int taskId) { + StartingWindowController.this.copySplashScreenView(taskId); + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt index eb53783f32f7..5f003ba62b2d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt @@ -71,16 +71,16 @@ class RotateTwoLaunchedAppsInAppPairsMode( } } - @FlakyTest + @Presubmit @Test fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() - @FlakyTest + @Presubmit @Test fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) - @FlakyTest + @Presubmit @Test fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt index 39c94842fe6e..d4792088ac31 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt @@ -76,17 +76,8 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( @Presubmit @Test - fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(isRotated) - testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) - } - - @FlakyTest - @Test - fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(isRotated) - testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) - } + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( + Surface.ROTATION_0, testSpec.config.endRotation) @Presubmit @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt index 41e2864481fe..9011f1a9fb6a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt @@ -18,7 +18,6 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -125,7 +124,7 @@ class EnterPipToOtherOrientationTest( } } - @FlakyTest + @Presubmit @Test fun testAppLayerCoversFullScreen() { testSpec.assertLayersEnd { @@ -133,11 +132,11 @@ class EnterPipToOtherOrientationTest( } } - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt index 06ef79a2b243..3e331761f767 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 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. @@ -16,12 +16,9 @@ package com.android.wm.shell.flicker.pip -import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder @@ -35,21 +32,10 @@ import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import org.junit.FixMethodOrder import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -/** - * Test Pip launch. - * To run this test: `atest WMShellFlickerTests:PipToHomeTest` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { +abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) { override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = true) { configuration -> setup { @@ -62,30 +48,27 @@ class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { this.setRotation(Surface.ROTATION_0) } } - transitions { - pipApp.closePipWindow(wmHelper) - } } - @Postsubmit + @Presubmit @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() @Presubmit @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() @Presubmit @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() @Presubmit @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() @Presubmit @Test - fun pipWindowBecomesInvisible() { + open fun pipWindowBecomesInvisible() { testSpec.assertWm { this.showsAppWindow(PIP_WINDOW_TITLE) .then() @@ -95,7 +78,7 @@ class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @Presubmit @Test - fun pipLayerBecomesInvisible() { + open fun pipLayerBecomesInvisible() { testSpec.assertLayers { this.isVisible(PIP_WINDOW_TITLE) .then() @@ -105,22 +88,22 @@ class PipToHomeTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @Presubmit @Test - fun statusBarLayerRotatesScales() = + open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - @Postsubmit + @Presubmit @Test - fun noUncoveredRegions() = + open fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) - @Postsubmit + @Presubmit @Test - fun navBarLayerRotatesAndScales() = + open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) @FlakyTest(bugId = 151179149) @Test - fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity") + open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity") companion object { @Parameterized.Parameters(name = "{0}") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt new file mode 100644 index 000000000000..0408421c72a5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithDismissButtonTest.kt @@ -0,0 +1,44 @@ +/* + * 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.flicker.pip + +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.dsl.FlickerBuilder +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch. + * To run this test: `atest WMShellFlickerTests:PipCloseWithDismissButton` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipCloseWithDismissButtonTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + transitions { + pipApp.closePipWindow(wmHelper) + } + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt new file mode 100644 index 000000000000..afaf33a7c46f --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseWithSwipeTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2021 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.flicker.pip + +import android.platform.test.annotations.Postsubmit +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch. + * To run this test: `atest WMShellFlickerTests:PipCloseWithSwipe` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipCloseWithSwipeTest(testSpec: FlickerTestParameter) : PipCloseTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) + transitions { + val pipRegion = wmHelper.getWindowRegion(pipApp.component).bounds + val pipCenterX = pipRegion.centerX() + val pipCenterY = pipRegion.centerY() + val displayCenterX = device.displayWidth / 2 + device.swipe(pipCenterX, pipCenterY, displayCenterX, device.displayHeight, 5) + } + } + + @Postsubmit + @Test + override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible() + + @Postsubmit + @Test + override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + override fun pipWindowBecomesInvisible() = super.pipWindowBecomesInvisible() + + @Postsubmit + @Test + override fun pipLayerBecomesInvisible() = super.pipLayerBecomesInvisible() + + @Postsubmit + @Test + override fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @Postsubmit + @Test + override fun noUncoveredRegions() = super.noUncoveredRegions() + + @Postsubmit + @Test + override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt new file mode 100644 index 000000000000..4c95da284d9e --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021 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.flicker.pip + +import android.platform.test.annotations.Postsubmit +import android.view.Surface +import androidx.test.filters.RequiresDevice +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.google.common.truth.Truth +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test Pip launch. + * To run this test: `atest WMShellFlickerTests:PipMovesInAllApps` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec) { + private val taplInstrumentation = LauncherInstrumentation() + + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = buildTransition(eachRun = false) { + teardown { + eachRun { + taplInstrumentation.pressHome() + } + } + transitions { + taplInstrumentation.pressHome().switchToAllApps() + wmHelper.waitForAppTransitionIdle() + } + } + + @Postsubmit + @Test + fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Postsubmit + @Test + fun pipAlwaysVisible() = testSpec.assertWm { this.showsAppWindow(pipApp.windowName) } + + @Postsubmit + @Test + fun pipWindowMovesUp() = testSpec.assertWmEnd { + val initialState = this.trace?.first()?.wmState + ?: error("Trace should not be empty") + val startPos = initialState.pinnedWindows.first().frame + val currPos = this.wmState.pinnedWindows.first().frame + val subject = Truth.assertWithMessage("Pip should have moved up") + subject.that(currPos.top).isGreaterThan(startPos.top) + subject.that(currPos.bottom).isGreaterThan(startPos.bottom) + subject.that(currPos.left).isEqualTo(startPos.left) + subject.that(currPos.right).isEqualTo(startPos.right) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0), repetitions = 5) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index 37b49c70708d..df835d21e73f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -88,11 +88,11 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, testSpec.config.endRotation, allStates = false) - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt index 704fd1d61c33..1bb1d2861f3f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt @@ -66,7 +66,7 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() @@ -107,12 +107,12 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @FlakyTest + @Presubmit @Test fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt index 1fcc7d066aa0..7916ce59af52 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -123,11 +123,11 @@ class SetRequestedOrientationWhilePinnedTest( } } - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - @FlakyTest(bugId = 140855415) + @Presubmit @Test fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() 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 a0e9f43218f2..06b492dcb409 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 @@ -53,7 +53,6 @@ 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.sizecompatui.SizeCompatUIController; -import com.android.wm.shell.startingsurface.StartingSurfaceDrawer; import org.junit.Before; import org.junit.Test; @@ -79,8 +78,6 @@ public class ShellTaskOrganizerTests { private Context mContext; @Mock private SizeCompatUIController mSizeCompatUI; - @Mock - private StartingSurfaceDrawer mStartingSurfaceDrawer; ShellTaskOrganizer mOrganizer; private final SyncTransactionQueue mSyncTransactionQueue = mock(SyncTransactionQueue.class); @@ -116,7 +113,7 @@ public class ShellTaskOrganizerTests { .when(mTaskOrganizerController).registerTaskOrganizer(any()); } catch (RemoteException e) {} mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mTestExecutor, mContext, - mSizeCompatUI, mStartingSurfaceDrawer)); + mSizeCompatUI)); } @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 index de7d6c74bb06..b9af9ce8895d 100644 --- 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 @@ -125,7 +125,7 @@ public class StartingSurfaceDrawerTests { final Handler mainLoop = new Handler(Looper.getMainLooper()); final StartingWindowInfo windowInfo = createWindowInfo(taskId, android.R.style.Theme); - mStartingSurfaceDrawer.addStartingWindow(windowInfo, mBinder); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder); waitHandlerIdle(mainLoop); verify(mStartingSurfaceDrawer).postAddWindow( eq(taskId), eq(mBinder), any(), any(), any(), any()); @@ -143,7 +143,7 @@ public class StartingSurfaceDrawerTests { final Handler mainLoop = new Handler(Looper.getMainLooper()); final StartingWindowInfo windowInfo = createWindowInfo(taskId, 0); - mStartingSurfaceDrawer.addStartingWindow(windowInfo, mBinder); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder); waitHandlerIdle(mainLoop); verify(mStartingSurfaceDrawer).postAddWindow( eq(taskId), eq(mBinder), any(), any(), any(), any()); diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 9c743cea592a..76366fce2aea 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -156,7 +156,11 @@ std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_ std::move(loaded_idmap))); } -const std::string& ApkAssets::GetPath() const { +std::optional<std::string_view> ApkAssets::GetPath() const { + return assets_provider_->GetPath(); +} + +const std::string& ApkAssets::GetDebugName() const { return assets_provider_->GetDebugName(); } diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 36bde5ccf267..c0ef7be8b673 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -116,8 +116,10 @@ void AssetManager2::BuildDynamicRefTable() { package_groups_.clear(); package_ids_.fill(0xff); - // A mapping from apk assets path to the runtime package id of its first loaded package. - std::unordered_map<std::string, uint8_t> apk_assets_package_ids; + // A mapping from path of apk assets that could be target packages of overlays to the runtime + // package id of its first loaded package. Overlays currently can only override resources in the + // first package in the target resource table. + std::unordered_map<std::string, uint8_t> target_assets_package_ids; // Overlay resources are not directly referenced by an application so their resource ids // can change throughout the application's lifetime. Assign overlay package ids last. @@ -140,8 +142,8 @@ void AssetManager2::BuildDynamicRefTable() { if (auto loaded_idmap = apk_assets->GetLoadedIdmap(); loaded_idmap != nullptr) { // The target package must precede the overlay package in the apk assets paths in order // to take effect. - auto iter = apk_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath())); - if (iter == apk_assets_package_ids.end()) { + auto iter = target_assets_package_ids.find(std::string(loaded_idmap->TargetApkPath())); + if (iter == target_assets_package_ids.end()) { LOG(INFO) << "failed to find target package for overlay " << loaded_idmap->OverlayApkPath(); } else { @@ -205,7 +207,10 @@ void AssetManager2::BuildDynamicRefTable() { package_name, static_cast<uint8_t>(entry.package_id)); } - apk_assets_package_ids.insert(std::make_pair(apk_assets->GetPath(), package_id)); + if (auto apk_assets_path = apk_assets->GetPath()) { + // Overlay target ApkAssets must have been created using path based load apis. + target_assets_package_ids.insert(std::make_pair(std::string(*apk_assets_path), package_id)); + } } } @@ -227,7 +232,7 @@ void AssetManager2::DumpToLog() const { std::string list; for (const auto& apk_assets : apk_assets_) { - base::StringAppendF(&list, "%s,", apk_assets->GetPath().c_str()); + base::StringAppendF(&list, "%s,", apk_assets->GetDebugName().c_str()); } LOG(INFO) << "ApkAssets: " << list; @@ -383,8 +388,8 @@ void AssetManager2::SetConfiguration(const ResTable_config& configuration) { } } -std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const { - std::set<std::string> non_system_overlays; +std::set<const ApkAssets*> AssetManager2::GetNonSystemOverlays() const { + std::set<const ApkAssets*> non_system_overlays; for (const PackageGroup& package_group : package_groups_) { bool found_system_package = false; for (const ConfiguredPackage& package : package_group.packages_) { @@ -396,7 +401,7 @@ std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const { if (!found_system_package) { for (const ConfiguredOverlay& overlay : package_group.overlays_) { - non_system_overlays.insert(apk_assets_[overlay.cookie]->GetPath()); + non_system_overlays.insert(apk_assets_[overlay.cookie]); } } } @@ -408,7 +413,7 @@ base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceCon bool exclude_system, bool exclude_mipmap) const { ATRACE_NAME("AssetManager::GetResourceConfigurations"); const auto non_system_overlays = - (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>(); + (exclude_system) ? GetNonSystemOverlays() : std::set<const ApkAssets*>(); std::set<ResTable_config> configurations; for (const PackageGroup& package_group : package_groups_) { @@ -419,8 +424,8 @@ base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceCon } auto apk_assets = apk_assets_[package_group.cookies_[i]]; - if (exclude_system && apk_assets->IsOverlay() - && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) { + if (exclude_system && apk_assets->IsOverlay() && + non_system_overlays.find(apk_assets) == non_system_overlays.end()) { // Exclude overlays that target system resources. continue; } @@ -439,7 +444,7 @@ std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system, ATRACE_NAME("AssetManager::GetResourceLocales"); std::set<std::string> locales; const auto non_system_overlays = - (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>(); + (exclude_system) ? GetNonSystemOverlays() : std::set<const ApkAssets*>(); for (const PackageGroup& package_group : package_groups_) { for (size_t i = 0; i < package_group.packages_.size(); i++) { @@ -449,8 +454,8 @@ std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system, } auto apk_assets = apk_assets_[package_group.cookies_[i]]; - if (exclude_system && apk_assets->IsOverlay() - && non_system_overlays.find(apk_assets->GetPath()) == non_system_overlays.end()) { + if (exclude_system && apk_assets->IsOverlay() && + non_system_overlays.find(apk_assets) == non_system_overlays.end()) { // Exclude overlays that target system resources. continue; } @@ -491,7 +496,7 @@ std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) con AssetDir::FileInfo info; info.setFileName(String8(name.data(), name.size())); info.setFileType(type); - info.setSourceName(String8(apk_assets->GetPath().c_str())); + info.setSourceName(String8(apk_assets->GetDebugName().c_str())); files->add(info); }; @@ -846,7 +851,7 @@ std::string AssetManager2::GetLastResourceResolution() const { } log_stream << "\n\t" << prefix->second << ": " << *step.package_name << " (" - << apk_assets_[step.cookie]->GetPath() << ")"; + << apk_assets_[step.cookie]->GetDebugName() << ")"; if (!step.config_name.isEmpty()) { log_stream << " -" << step.config_name; } @@ -1556,41 +1561,32 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { std::map<ApkAssetsCookie, SourceToDestinationRuntimePackageMap> src_asset_cookie_id_map; // Determine which ApkAssets are loaded in both theme AssetManagers. - std::vector<const ApkAssets*> src_assets = o.asset_manager_->GetApkAssets(); + const auto src_assets = o.asset_manager_->GetApkAssets(); for (size_t i = 0; i < src_assets.size(); i++) { const ApkAssets* src_asset = src_assets[i]; - std::vector<const ApkAssets*> dest_assets = asset_manager_->GetApkAssets(); + const auto dest_assets = asset_manager_->GetApkAssets(); for (size_t j = 0; j < dest_assets.size(); j++) { const ApkAssets* dest_asset = dest_assets[j]; + if (src_asset != dest_asset) { + // ResourcesManager caches and reuses ApkAssets when the same apk must be present in + // multiple AssetManagers. Two ApkAssets point to the same version of the same resources + // if they are the same instance. + continue; + } - // Map the runtime package of the source apk asset to the destination apk asset. - if (src_asset->GetPath() == dest_asset->GetPath()) { - const auto& src_packages = src_asset->GetLoadedArsc()->GetPackages(); - const auto& dest_packages = dest_asset->GetLoadedArsc()->GetPackages(); - - SourceToDestinationRuntimePackageMap package_map; - - // The source and destination package should have the same number of packages loaded in - // the same order. - const size_t N = src_packages.size(); - CHECK(N == dest_packages.size()) - << " LoadedArsc " << src_asset->GetPath() << " differs number of packages."; - for (size_t p = 0; p < N; p++) { - auto& src_package = src_packages[p]; - auto& dest_package = dest_packages[p]; - CHECK(src_package->GetPackageName() == dest_package->GetPackageName()) - << " Package " << src_package->GetPackageName() << " differs in load order."; - - int src_package_id = o.asset_manager_->GetAssignedPackageId(src_package.get()); - int dest_package_id = asset_manager_->GetAssignedPackageId(dest_package.get()); - package_map[src_package_id] = dest_package_id; - } - - src_to_dest_asset_cookies.insert(std::make_pair(i, j)); - src_asset_cookie_id_map.insert(std::make_pair(i, package_map)); - break; + // Map the package ids of the asset in the source AssetManager to the package ids of the + // asset in th destination AssetManager. + SourceToDestinationRuntimePackageMap package_map; + for (const auto& loaded_package : src_asset->GetLoadedArsc()->GetPackages()) { + const int src_package_id = o.asset_manager_->GetAssignedPackageId(loaded_package.get()); + const int dest_package_id = asset_manager_->GetAssignedPackageId(loaded_package.get()); + package_map[src_package_id] = dest_package_id; } + + src_to_dest_asset_cookies.insert(std::make_pair(i, j)); + src_asset_cookie_id_map.insert(std::make_pair(i, package_map)); + break; } } diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp index f3c48f7f9fc8..0aaf0b359b60 100644 --- a/libs/androidfw/AssetsProvider.cpp +++ b/libs/androidfw/AssetsProvider.cpp @@ -261,6 +261,13 @@ std::optional<uint32_t> ZipAssetsProvider::GetCrc(std::string_view path) const { return entry.crc32; } +std::optional<std::string_view> ZipAssetsProvider::GetPath() const { + if (name_.GetPath() != nullptr) { + return *name_.GetPath(); + } + return {}; +} + const std::string& ZipAssetsProvider::GetDebugName() const { return name_.GetDebugName(); } @@ -318,6 +325,10 @@ bool DirectoryAssetsProvider::ForEachFile( return true; } +std::optional<std::string_view> DirectoryAssetsProvider::GetPath() const { + return dir_; +} + const std::string& DirectoryAssetsProvider::GetDebugName() const { return dir_; } @@ -336,13 +347,9 @@ MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& prima std::unique_ptr<AssetsProvider>&& secondary) : primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)), secondary_(std::forward<std::unique_ptr<AssetsProvider>>(secondary)) { - if (primary_->GetDebugName() == kEmptyDebugString) { - debug_name_ = secondary_->GetDebugName(); - } else if (secondary_->GetDebugName() == kEmptyDebugString) { - debug_name_ = primary_->GetDebugName(); - } else { - debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName(); - } + debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName(); + path_ = (primary_->GetDebugName() != kEmptyDebugString) ? primary_->GetPath() + : secondary_->GetPath(); } std::unique_ptr<AssetsProvider> MultiAssetsProvider::Create( @@ -367,6 +374,10 @@ bool MultiAssetsProvider::ForEachFile(const std::string& root_path, return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f); } +std::optional<std::string_view> MultiAssetsProvider::GetPath() const { + return path_; +} + const std::string& MultiAssetsProvider::GetDebugName() const { return debug_name_; } @@ -394,6 +405,10 @@ bool EmptyAssetsProvider::ForEachFile( return true; } +std::optional<std::string_view> EmptyAssetsProvider::GetPath() const { + return {}; +} + const std::string& EmptyAssetsProvider::GetDebugName() const { const static std::string kEmpty = kEmptyDebugString; return kEmpty; diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index d0019ed6be30..6f88f41976cd 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -34,7 +34,6 @@ namespace android { // Holds an APK. class ApkAssets { public: - // Creates an ApkAssets from a path on device. static std::unique_ptr<ApkAssets> Load(const std::string& path, package_property_t flags = 0U); @@ -61,12 +60,11 @@ class ApkAssets { static std::unique_ptr<ApkAssets> LoadOverlay(const std::string& idmap_path, package_property_t flags = 0U); - // TODO(177101983): Remove all uses of GetPath for checking whether two ApkAssets are the same. - // With the introduction of ResourcesProviders, not all ApkAssets have paths. This could cause - // bugs when path is used for comparison because multiple ApkAssets could have the same "firendly - // name". Use pointer equality instead. ResourceManager caches and reuses ApkAssets so the - // same asset should have the same pointer. - const std::string& GetPath() const; + // Path to the contents of the ApkAssets on disk. The path could represent an APk, a directory, + // or some other file type. + std::optional<std::string_view> GetPath() const; + + const std::string& GetDebugName() const; const AssetsProvider* GetAssetsProvider() const { return assets_provider_.get(); diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 2255973f1039..119f531b8634 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -412,7 +412,7 @@ class AssetManager2 { void RebuildFilterList(); // Retrieves the APK paths of overlays that overlay non-system packages. - std::set<std::string> GetNonSystemOverlayPaths() const; + std::set<const ApkAssets*> GetNonSystemOverlays() const; // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already // been seen while traversing bag parents. diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h index 6f16ff453905..63bbdcc9698d 100644 --- a/libs/androidfw/include/androidfw/AssetsProvider.h +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -48,6 +48,10 @@ struct AssetsProvider { virtual bool ForEachFile(const std::string& path, const std::function<void(const StringPiece&, FileType)>& f) const = 0; + // Retrieves the path to the contents of the AssetsProvider on disk. The path could represent an + // APk, a directory, or some other file type. + WARN_UNUSED virtual std::optional<std::string_view> GetPath() const = 0; + // Retrieves a name that represents the interface. This may or may not be the path of the // interface source. WARN_UNUSED virtual const std::string& GetDebugName() const = 0; @@ -85,9 +89,9 @@ struct ZipAssetsProvider : public AssetsProvider { bool ForEachFile(const std::string& root_path, const std::function<void(const StringPiece&, FileType)>& f) const override; + WARN_UNUSED std::optional<std::string_view> GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; - WARN_UNUSED std::optional<uint32_t> GetCrc(std::string_view path) const; ~ZipAssetsProvider() override = default; @@ -125,6 +129,7 @@ struct DirectoryAssetsProvider : public AssetsProvider { bool ForEachFile(const std::string& path, const std::function<void(const StringPiece&, FileType)>& f) const override; + WARN_UNUSED std::optional<std::string_view> GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; @@ -149,6 +154,7 @@ struct MultiAssetsProvider : public AssetsProvider { bool ForEachFile(const std::string& root_path, const std::function<void(const StringPiece&, FileType)>& f) const override; + WARN_UNUSED std::optional<std::string_view> GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; @@ -163,6 +169,7 @@ struct MultiAssetsProvider : public AssetsProvider { std::unique_ptr<AssetsProvider> primary_; std::unique_ptr<AssetsProvider> secondary_; + std::optional<std::string_view> path_; std::string debug_name_; }; @@ -173,6 +180,7 @@ struct EmptyAssetsProvider : public AssetsProvider { bool ForEachFile(const std::string& path, const std::function<void(const StringPiece&, FileType)>& f) const override; + WARN_UNUSED std::optional<std::string_view> GetPath() const override; WARN_UNUSED const std::string& GetDebugName() const override; WARN_UNUSED bool IsUpToDate() const override; diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 389fe7eed7c7..50eea31f984f 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -105,7 +105,6 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con LightingInfo::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, SkMatrix::I()); - layerUpdateQueue->clear(); // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || @@ -113,9 +112,14 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con SkCanvas* profileCanvas = surface->getCanvas(); SkiaProfileRenderer profileRenderer(profileCanvas); profiler->draw(profileRenderer); - profileCanvas->flush(); } + { + ATRACE_NAME("flush commands"); + surface->flushAndSubmit(); + } + layerUpdateQueue->clear(); + // Log memory statistics if (CC_UNLIKELY(Properties::debugLevel != kDebugDisabled)) { dumpResourceCacheUsage(); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 6456e36a847a..1f73ac919a47 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -454,9 +454,6 @@ void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& cli renderOverdraw(clip, nodes, contentDrawBounds, surface, preTransform); } - ATRACE_NAME("flush commands"); - surface->flushAndSubmit(); - Properties::skpCaptureEnabled = previousSkpEnabled; } diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index ad6363b4452d..1bd943f4c21d 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -17,14 +17,15 @@ #include "SkiaVulkanPipeline.h" #include "DeferredLayerUpdater.h" +#include "LightingInfo.h" #include "Readback.h" #include "ShaderCache.h" -#include "LightingInfo.h" #include "SkiaPipeline.h" #include "SkiaProfileRenderer.h" #include "VkInteropFunctorDrawable.h" #include "renderstate/RenderState.h" #include "renderthread/Frame.h" +#include "utils/TraceUtils.h" #include <SkSurface.h> #include <SkTypes.h> @@ -73,8 +74,6 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con LightingInfo::updateLighting(lightGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer, mVkSurface->getCurrentPreTransform()); - ShaderCache::get().onVkFrameFlushed(mRenderThread.getGrContext()); - layerUpdateQueue->clear(); // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || @@ -82,9 +81,14 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con SkCanvas* profileCanvas = backBuffer->getCanvas(); SkiaProfileRenderer profileRenderer(profileCanvas); profiler->draw(profileRenderer); - profileCanvas->flush(); } + { + ATRACE_NAME("flush commands"); + mVkManager.finishFrame(backBuffer.get()); + } + layerUpdateQueue->clear(); + // Log memory statistics if (CC_UNLIKELY(Properties::debugLevel != kDebugDisabled)) { dumpResourceCacheUsage(); diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 3e7ce368f55d..e93824dfbd30 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -31,6 +31,7 @@ #include "Properties.h" #include "RenderThread.h" +#include "pipeline/skia/ShaderCache.h" #include "renderstate/RenderState.h" #include "utils/TraceUtils.h" @@ -476,17 +477,10 @@ static void destroy_semaphore(void* context) { } } -void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) { - if (CC_UNLIKELY(Properties::waitForGpuCompletion)) { - ATRACE_NAME("Finishing GPU work"); - mDeviceWaitIdle(mDevice); - } - - VulkanSurface::NativeBufferInfo* bufferInfo = surface->getCurrentBufferInfo(); - if (!bufferInfo) { - // If VulkanSurface::dequeueNativeBuffer failed earlier, then swapBuffers is a no-op. - return; - } +void VulkanManager::finishFrame(SkSurface* surface) { + ATRACE_NAME("Vulkan finish frame"); + ALOGE_IF(mSwapSemaphore != VK_NULL_HANDLE || mDestroySemaphoreContext != nullptr, + "finishFrame already has an outstanding semaphore"); VkExportSemaphoreCreateInfo exportInfo; exportInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO; @@ -499,32 +493,52 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) semaphoreInfo.flags = 0; VkSemaphore semaphore; VkResult err = mCreateSemaphore(mDevice, &semaphoreInfo, nullptr, &semaphore); - ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to create semaphore"); + ALOGE_IF(VK_SUCCESS != err, "VulkanManager::makeSwapSemaphore(): Failed to create semaphore"); GrBackendSemaphore backendSemaphore; backendSemaphore.initVulkan(semaphore); - int fenceFd = -1; - DestroySemaphoreInfo* destroyInfo = - new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); GrFlushInfo flushInfo; - flushInfo.fNumSemaphores = 1; - flushInfo.fSignalSemaphores = &backendSemaphore; - flushInfo.fFinishedProc = destroy_semaphore; - flushInfo.fFinishedContext = destroyInfo; - GrSemaphoresSubmitted submitted = bufferInfo->skSurface->flush( - SkSurface::BackendSurfaceAccess::kPresent, flushInfo); - GrDirectContext* context = GrAsDirectContext(bufferInfo->skSurface->recordingContext()); + if (err == VK_SUCCESS) { + mDestroySemaphoreContext = new DestroySemaphoreInfo(mDestroySemaphore, mDevice, semaphore); + flushInfo.fNumSemaphores = 1; + flushInfo.fSignalSemaphores = &backendSemaphore; + flushInfo.fFinishedProc = destroy_semaphore; + flushInfo.fFinishedContext = mDestroySemaphoreContext; + } else { + semaphore = VK_NULL_HANDLE; + } + GrSemaphoresSubmitted submitted = + surface->flush(SkSurface::BackendSurfaceAccess::kPresent, flushInfo); + GrDirectContext* context = GrAsDirectContext(surface->recordingContext()); ALOGE_IF(!context, "Surface is not backed by gpu"); context->submit(); - if (submitted == GrSemaphoresSubmitted::kYes) { + if (semaphore != VK_NULL_HANDLE) { + if (submitted == GrSemaphoresSubmitted::kYes) { + mSwapSemaphore = semaphore; + } else { + destroy_semaphore(mDestroySemaphoreContext); + mDestroySemaphoreContext = nullptr; + } + } + skiapipeline::ShaderCache::get().onVkFrameFlushed(context); +} + +void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) { + if (CC_UNLIKELY(Properties::waitForGpuCompletion)) { + ATRACE_NAME("Finishing GPU work"); + mDeviceWaitIdle(mDevice); + } + + int fenceFd = -1; + if (mSwapSemaphore != VK_NULL_HANDLE) { VkSemaphoreGetFdInfoKHR getFdInfo; getFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; getFdInfo.pNext = nullptr; - getFdInfo.semaphore = semaphore; + getFdInfo.semaphore = mSwapSemaphore; getFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT; - err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); + VkResult err = mGetSemaphoreFdKHR(mDevice, &getFdInfo, &fenceFd); ALOGE_IF(VK_SUCCESS != err, "VulkanManager::swapBuffers(): Failed to get semaphore Fd"); } else { ALOGE("VulkanManager::swapBuffers(): Semaphore submission failed"); @@ -532,9 +546,11 @@ void VulkanManager::swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect) std::lock_guard<std::mutex> lock(mGraphicsQueueMutex); mQueueWaitIdle(mGraphicsQueue); } - destroy_semaphore(destroyInfo); + destroy_semaphore(mDestroySemaphoreContext); surface->presentCurrentBuffer(dirtyRect, fenceFd); + mSwapSemaphore = VK_NULL_HANDLE; + mDestroySemaphoreContext = nullptr; } void VulkanManager::destroySurface(VulkanSurface* surface) { diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index 121afc90cfe5..0912369b611d 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -69,6 +69,7 @@ public: void destroySurface(VulkanSurface* surface); Frame dequeueNextBuffer(VulkanSurface* surface); + void finishFrame(SkSurface* surface); void swapBuffers(VulkanSurface* surface, const SkRect& dirtyRect); // Inserts a wait on fence command into the Vulkan command buffer. @@ -200,6 +201,9 @@ private: SwapBehavior mSwapBehavior = SwapBehavior::Discard; GrVkExtensions mExtensions; uint32_t mDriverVersion = 0; + + VkSemaphore mSwapSemaphore = VK_NULL_HANDLE; + void* mDestroySemaphoreContext = nullptr; }; } /* namespace renderthread */ |