diff options
Diffstat (limited to 'libs')
47 files changed, 788 insertions, 173 deletions
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 45540e0fbbb8..4cdc06a999a7 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -162,6 +162,7 @@ android_library { "com_android_wm_shell_flags_lib", "com.android.window.flags.window-aconfig-java", "WindowManager-Shell-proto", + "perfetto_trace_java_protos", "dagger2", "jsr330", ], diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index 901d5fa0cd9a..0e046581cb48 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -1,13 +1,6 @@ package: "com.android.wm.shell" flag { - name: "example_flag" - namespace: "multitasking" - description: "An Example Flag" - bug: "300136750" -} - -flag { name: "enable_app_pairs" namespace: "multitasking" description: "Enables the ability to create and save app pairs to the Home screen" diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index e4abae48c8fd..9854e58dd7cf 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -134,6 +134,13 @@ <!-- Whether the additional education about reachability is enabled --> <bool name="config_letterboxIsReachabilityEducationEnabled">false</bool> + <!-- The minimum tolerance of the percentage of activity bounds within its task to hide + size compat restart button. Value lower than 0 or higher than 100 will be ignored. + 100 is the default value where the activity has to fit exactly within the task to allow + size compat restart button to be hidden. 0 means size compat restart button will always + be hidden. --> + <integer name="config_letterboxRestartButtonHideTolerance">100</integer> + <!-- Whether DragAndDrop capability is enabled --> <bool name="config_enableShellDragDrop">true</bool> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java index 30d5edb59c85..160f922dd928 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.java @@ -199,6 +199,10 @@ public class CrossActivityBackAnimation extends ShellBackAnimation { } private void applyTransform(SurfaceControl leash, RectF targetRect, float targetAlpha) { + if (leash == null || !leash.isValid()) { + return; + } + final float scale = targetRect.width() / mStartTaskRect.width(); mTransformMatrix.reset(); mTransformMatrix.setScale(scale, scale); @@ -211,12 +215,16 @@ public class CrossActivityBackAnimation extends ShellBackAnimation { private void finishAnimation() { if (mEnteringTarget != null) { - mTransaction.setCornerRadius(mEnteringTarget.leash, 0); - mEnteringTarget.leash.release(); + if (mEnteringTarget.leash != null && mEnteringTarget.leash.isValid()) { + mTransaction.setCornerRadius(mEnteringTarget.leash, 0); + mEnteringTarget.leash.release(); + } mEnteringTarget = null; } if (mClosingTarget != null) { - mClosingTarget.leash.release(); + if (mClosingTarget.leash != null) { + mClosingTarget.leash.release(); + } mClosingTarget = null; } if (mBackground != null) { @@ -260,7 +268,9 @@ public class CrossActivityBackAnimation extends ShellBackAnimation { } private void onGestureCommitted() { - if (mEnteringTarget == null || mClosingTarget == null) { + if (mEnteringTarget == null || mClosingTarget == null || mClosingTarget.leash == null + || mEnteringTarget.leash == null || !mEnteringTarget.leash.isValid() + || !mClosingTarget.leash.isValid()) { finishAnimation(); return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java index ac2a1c867462..adc78391f033 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossTaskBackAnimation.java @@ -208,7 +208,9 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { float top = mapRange(progress, mClosingStartRect.top, targetTop); float width = mapRange(progress, mClosingStartRect.width(), targetWidth); float height = mapRange(progress, mClosingStartRect.height(), targetHeight); - mTransaction.setLayer(mClosingTarget.leash, 0); + if (mClosingTarget.leash != null && mClosingTarget.leash.isValid()) { + mTransaction.setLayer(mClosingTarget.leash, 0); + } mClosingCurrentRect.set(left, top, left + width, top + height); applyTransform(mClosingTarget.leash, mClosingCurrentRect, mCornerRadius); @@ -226,7 +228,7 @@ public class CrossTaskBackAnimation extends ShellBackAnimation { /** Transform the target window to match the target rect. */ private void applyTransform(SurfaceControl leash, RectF targetRect, float cornerRadius) { - if (leash == null) { + if (leash == null || !leash.isValid()) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index 7a3210e0a46d..87c8f526c6fa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -64,7 +64,7 @@ public class Bubble implements BubbleViewProvider { private static final String TAG = "Bubble"; /** A string suffix used in app bubbles' {@link #mKey}. */ - private static final String KEY_APP_BUBBLE = "key_app_bubble"; + public static final String KEY_APP_BUBBLE = "key_app_bubble"; /** Whether the bubble is an app bubble. */ private final boolean mIsAppBubble; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index e23e15f95640..a5f7880cfd41 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -137,7 +137,7 @@ import java.util.function.IntConsumer; * The controller manages addition, removal, and visible state of bubbles on screen. */ public class BubbleController implements ConfigurationChangeListener, - RemoteCallable<BubbleController> { + RemoteCallable<BubbleController>, Bubbles.SysuiProxy.Provider { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES; @@ -706,6 +706,7 @@ public class BubbleController implements ConfigurationChangeListener, return mBubbleIconFactory; } + @Override public Bubbles.SysuiProxy getSysuiProxy() { return mSysuiProxy; } @@ -732,8 +733,7 @@ public class BubbleController implements ConfigurationChangeListener, if (mStackView == null) { mStackView = new BubbleStackView( mContext, this, mBubbleData, mSurfaceSynchronizer, - mFloatingContentCoordinator, - mMainExecutor); + mFloatingContentCoordinator, this, mMainExecutor); mStackView.onOrientationChanged(); if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); @@ -1249,7 +1249,7 @@ public class BubbleController implements ConfigurationChangeListener, } String appBubbleKey = Bubble.getAppBubbleKeyForApp(intent.getPackage(), user); - Log.v(TAG, "showOrHideAppBubble, with key: " + appBubbleKey); + Log.i(TAG, "showOrHideAppBubble, with key: " + appBubbleKey); PackageManager packageManager = getPackageManagerForUser(mContext, user.getIdentifier()); if (!isResizableActivity(intent, packageManager, appBubbleKey)) return; @@ -1259,22 +1259,22 @@ public class BubbleController implements ConfigurationChangeListener, if (isStackExpanded()) { if (selectedBubble != null && appBubbleKey.equals(selectedBubble.getKey())) { // App bubble is expanded, lets collapse - Log.v(TAG, " showOrHideAppBubble, selected bubble is app bubble, collapsing"); + Log.i(TAG, " showOrHideAppBubble, selected bubble is app bubble, collapsing"); collapseStack(); } else { // App bubble is not selected, select it - Log.v(TAG, " showOrHideAppBubble, expanded, selecting existing app bubble"); + Log.i(TAG, " showOrHideAppBubble, expanded, selecting existing app bubble"); mBubbleData.setSelectedBubble(existingAppBubble); } } else { // App bubble is not selected, select it & expand - Log.v(TAG, " showOrHideAppBubble, expand and select existing app bubble"); + Log.i(TAG, " showOrHideAppBubble, expand and select existing app bubble"); mBubbleData.setSelectedBubble(existingAppBubble); mBubbleData.setExpanded(true); } } else { // App bubble does not exist, lets add and expand it - Log.v(TAG, " showOrHideAppBubble, creating and expanding app bubble"); + Log.i(TAG, " showOrHideAppBubble, creating and expanding app bubble"); Bubble b = Bubble.createAppBubble(intent, user, icon, mMainExecutor); b.setShouldAutoExpand(true); inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index bbb4b74c2a17..6e0c804f0676 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -17,6 +17,7 @@ package com.android.wm.shell.bubbles; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE; +import static com.android.wm.shell.bubbles.Bubble.KEY_APP_BUBBLE; import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_DATA; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; @@ -649,8 +650,8 @@ public class BubbleData { } private void doRemove(String key, @DismissReason int reason) { - if (DEBUG_BUBBLE_DATA) { - Log.d(TAG, "doRemove: " + key); + if (DEBUG_BUBBLE_DATA || (key != null && key.contains(KEY_APP_BUBBLE))) { + Log.d(TAG, "doRemove: " + key + " reason: " + reason); } // If it was pending remove it if (mPendingBubbles.containsKey(key)) { 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 ca283128891c..a619401301aa 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 @@ -206,6 +206,7 @@ public class BubbleStackView extends FrameLayout }; private final BubbleController mBubbleController; private final BubbleData mBubbleData; + private final Bubbles.SysuiProxy.Provider mSysuiProxyProvider; private StackViewState mStackViewState = new StackViewState(); private final ValueAnimator mDismissBubbleAnimator; @@ -300,6 +301,9 @@ public class BubbleStackView extends FrameLayout */ private int mPointerIndexDown = -1; + /** Indicates whether bubbles should be reordered at the end of a gesture. */ + private boolean mShouldReorderBubblesAfterGestureCompletes = false; + @Nullable private BubblesNavBarGestureTracker mBubblesNavBarGestureTracker; @@ -708,6 +712,11 @@ public class BubbleStackView extends FrameLayout // Hide the stack after a delay, if needed. updateTemporarilyInvisibleAnimation(false /* hideImmediately */); + + if (mShouldReorderBubblesAfterGestureCompletes) { + mShouldReorderBubblesAfterGestureCompletes = false; + updateBubbleOrderInternal(mBubbleData.getBubbles(), true); + } } }; @@ -867,12 +876,14 @@ public class BubbleStackView extends FrameLayout public BubbleStackView(Context context, BubbleController bubbleController, BubbleData data, @Nullable SurfaceSynchronizer synchronizer, FloatingContentCoordinator floatingContentCoordinator, + Bubbles.SysuiProxy.Provider sysuiProxyProvider, ShellExecutor mainExecutor) { super(context); mMainExecutor = mainExecutor; mBubbleController = bubbleController; mBubbleData = data; + mSysuiProxyProvider = sysuiProxyProvider; Resources res = getResources(); mBubbleSize = res.getDimensionPixelSize(R.dimen.bubble_size); @@ -1928,7 +1939,18 @@ public class BubbleStackView extends FrameLayout /** * Update bubble order and pointer position. */ - public void updateBubbleOrder(List<Bubble> bubbles, boolean updatePointerPositoion) { + public void updateBubbleOrder(List<Bubble> bubbles, boolean updatePointerPosition) { + // Don't reorder bubbles in the middle of a gesture because that would remove bubbles from + // view hierarchy and will cancel all touch events. Instead wait until the gesture is + // finished and then reorder. + if (mIsGestureInProgress) { + mShouldReorderBubblesAfterGestureCompletes = true; + return; + } + updateBubbleOrderInternal(bubbles, updatePointerPosition); + } + + private void updateBubbleOrderInternal(List<Bubble> bubbles, boolean updatePointerPosition) { final Runnable reorder = () -> { for (int i = 0; i < bubbles.size(); i++) { Bubble bubble = bubbles.get(i); @@ -1939,13 +1961,13 @@ public class BubbleStackView extends FrameLayout reorder.run(); updateBadges(false /* setBadgeForCollapsedStack */); updateZOrder(); - } else if (!isExpansionAnimating()) { + } else { List<View> bubbleViews = bubbles.stream() .map(b -> b.getIconView()).collect(Collectors.toList()); mStackAnimationController.animateReorder(bubbleViews, reorder); } - if (updatePointerPositoion) { + if (updatePointerPosition) { updatePointerPosition(false /* forIme */); } } @@ -2071,7 +2093,7 @@ public class BubbleStackView extends FrameLayout hideCurrentInputMethod(); - mBubbleController.getSysuiProxy().onStackExpandChanged(shouldExpand); + mSysuiProxyProvider.getSysuiProxy().onStackExpandChanged(shouldExpand); if (wasExpanded) { stopMonitoringSwipeUpGesture(); @@ -3015,7 +3037,7 @@ public class BubbleStackView extends FrameLayout if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) { mManageMenu.setVisibility(View.INVISIBLE); mManageMenuScrim.setVisibility(INVISIBLE); - mBubbleController.getSysuiProxy().onManageMenuExpandChanged(false /* show */); + mSysuiProxyProvider.getSysuiProxy().onManageMenuExpandChanged(false /* show */); return; } if (show) { @@ -3029,7 +3051,7 @@ public class BubbleStackView extends FrameLayout } }; - mBubbleController.getSysuiProxy().onManageMenuExpandChanged(show); + mSysuiProxyProvider.getSysuiProxy().onManageMenuExpandChanged(show); mManageMenuScrim.animate() .setInterpolator(show ? ALPHA_IN : ALPHA_OUT) .alpha(show ? BUBBLE_EXPANDED_SCRIM_ALPHA : 0f) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 759246eb285d..28af0ca6ac6c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -321,6 +321,13 @@ public interface Bubbles { /** Callback to tell SysUi components execute some methods. */ interface SysuiProxy { + + /** Provider interface for {@link SysuiProxy}. */ + interface Provider { + /** Returns {@link SysuiProxy}. */ + SysuiProxy getSysuiProxy(); + } + void isNotificationPanelExpand(Consumer<Boolean> callback); void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java index 09d99b204bdb..fa2e23647a39 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIConfiguration.java @@ -71,6 +71,8 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi private static final String HAS_SEEN_VERTICAL_REACHABILITY_EDUCATION_KEY_PREFIX = "has_seen_vertical_reachability_education"; + private static final int MAX_PERCENTAGE_VAL = 100; + /** * The {@link SharedPreferences} instance for the restart dialog and the reachability * education. @@ -82,6 +84,12 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi */ private final SharedPreferences mLetterboxEduSharedPreferences; + /** + * The minimum tolerance of the percentage of activity bounds within its task to hide + * size compat restart button. + */ + private final int mHideSizeCompatRestartButtonTolerance; + // Whether the extended restart dialog is enabled private boolean mIsRestartDialogEnabled; @@ -106,6 +114,9 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi R.bool.config_letterboxIsRestartDialogEnabled); mIsReachabilityEducationEnabled = context.getResources().getBoolean( R.bool.config_letterboxIsReachabilityEducationEnabled); + final int tolerance = context.getResources().getInteger( + R.integer.config_letterboxRestartButtonHideTolerance); + mHideSizeCompatRestartButtonTolerance = getHideSizeCompatRestartButtonTolerance(tolerance); mIsLetterboxRestartDialogAllowed = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_LETTERBOX_RESTART_DIALOG, DEFAULT_VALUE_ENABLE_LETTERBOX_RESTART_DIALOG); @@ -179,6 +190,10 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi || !hasSeenVerticalReachabilityEducation(taskInfo)); } + int getHideSizeCompatRestartButtonTolerance() { + return mHideSizeCompatRestartButtonTolerance; + } + boolean getHasSeenLetterboxEducation(int userId) { return mLetterboxEduSharedPreferences .getBoolean(dontShowLetterboxEduKey(userId), /* default= */ false); @@ -218,6 +233,15 @@ public class CompatUIConfiguration implements DeviceConfig.OnPropertiesChangedLi } } + // Returns the minimum tolerance of the percentage of activity bounds within its task to hide + // size compat restart button. Value lower than 0 or higher than 100 will be ignored. + // 100 is the default value where the activity has to fit exactly within the task to allow + // size compat restart button to be hidden. 0 means size compat restart button will always + // be hidden. + private int getHideSizeCompatRestartButtonTolerance(int tolerance) { + return tolerance < 0 || tolerance > MAX_PERCENTAGE_VAL ? MAX_PERCENTAGE_VAL : tolerance; + } + private boolean isReachabilityEducationEnabled() { return mIsReachabilityEducationOverrideEnabled || (mIsReachabilityEducationEnabled && mIsLetterboxReachabilityEducationAllowed); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java index 00e0cdb034b6..2dd27430e348 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java @@ -22,7 +22,9 @@ import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPL import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED; import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI; +import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AppCompatTaskInfo; import android.app.AppCompatTaskInfo.CameraCompatControlState; import android.app.TaskInfo; import android.content.Context; @@ -33,6 +35,7 @@ import android.view.LayoutInflater; import android.view.View; import com.android.internal.annotations.VisibleForTesting; +import com.android.window.flags.Flags; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayLayout; @@ -68,6 +71,8 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { @VisibleForTesting CompatUILayout mLayout; + private final float mHideScmTolerance; + CompatUIWindowManager(Context context, TaskInfo taskInfo, SyncTransactionQueue syncQueue, CompatUICallback callback, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout, @@ -75,11 +80,13 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartButtonClicked) { super(context, taskInfo, syncQueue, taskListener, displayLayout); mCallback = callback; - mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat; + mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat + && shouldShowSizeCompatRestartButton(taskInfo); mCameraCompatControlState = taskInfo.appCompatTaskInfo.cameraCompatControlState; mCompatUIHintsState = compatUIHintsState; mCompatUIConfiguration = compatUIConfiguration; mOnRestartButtonClicked = onRestartButtonClicked; + mHideScmTolerance = mCompatUIConfiguration.getHideSizeCompatRestartButtonTolerance(); } @Override @@ -107,6 +114,11 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { mLayout = inflateLayout(); mLayout.inject(this); + final TaskInfo taskInfo = getLastTaskInfo(); + if (taskInfo != null) { + mHasSizeCompat = mHasSizeCompat && shouldShowSizeCompatRestartButton(taskInfo); + } + updateVisibilityOfViews(); if (mHasSizeCompat) { @@ -127,7 +139,8 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { boolean canShow) { final boolean prevHasSizeCompat = mHasSizeCompat; final int prevCameraCompatControlState = mCameraCompatControlState; - mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat; + mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat + && shouldShowSizeCompatRestartButton(taskInfo); mCameraCompatControlState = taskInfo.appCompatTaskInfo.cameraCompatControlState; if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) { @@ -208,6 +221,30 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract { updateSurfacePosition(positionX, positionY); } + @VisibleForTesting + boolean shouldShowSizeCompatRestartButton(@NonNull TaskInfo taskInfo) { + if (!Flags.allowHideScmButton()) { + return true; + } + final AppCompatTaskInfo appCompatTaskInfo = taskInfo.appCompatTaskInfo; + final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds(); + final int letterboxArea = computeArea(appCompatTaskInfo.topActivityLetterboxWidth, + appCompatTaskInfo.topActivityLetterboxHeight); + final int taskArea = computeArea(taskBounds.width(), taskBounds.height()); + if (letterboxArea == 0 || taskArea == 0) { + return false; + } + final float percentageAreaOfLetterboxInTask = (float) letterboxArea / taskArea * 100; + return percentageAreaOfLetterboxInTask < mHideScmTolerance; + } + + private int computeArea(int width, int height) { + if (width == 0 || height == 0) { + return 0; + } + return width * height; + } + private void updateVisibilityOfViews() { if (mLayout == null) { return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt index 95d7ad5c416f..c3a82ce258df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt @@ -99,7 +99,11 @@ class DragToDesktopTransitionHandler( windowDecoration: DesktopModeWindowDecoration ) { if (inProgress) { - error("A drag to desktop is already in progress") + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DragToDesktop: Drag to desktop transition already in progress." + ) + return } val options = ActivityOptions.makeBasic().apply { @@ -144,6 +148,12 @@ class DragToDesktopTransitionHandler( * inside the desktop drop zone. */ fun finishDragToDesktopTransition(wct: WindowContainerTransaction) { + if (!inProgress) { + // Don't attempt to finish a drag to desktop transition since there is no transition in + // progress which means that the drag to desktop transition was never successfully + // started. + return + } if (requireTransitionState().startAborted) { // Don't attempt to complete the drag-to-desktop since the start transition didn't // succeed as expected. Just reset the state as if nothing happened. @@ -161,6 +171,12 @@ class DragToDesktopTransitionHandler( * means the user wants to remain in their current windowing mode. */ fun cancelDragToDesktopTransition() { + if (!inProgress) { + // Don't attempt to cancel a drag to desktop transition since there is no transition in + // progress which means that the drag to desktop transition was never successfully + // started. + return + } val state = requireTransitionState() if (state.startAborted) { // Don't attempt to cancel the drag-to-desktop since the start transition didn't diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index a9a3f788cb7e..07b8f11458be 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -599,9 +599,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (Transitions.ENABLE_SHELL_TRANSITIONS) { if (requestEnterSplit && mSplitScreenOptional.isPresent()) { wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); - mSplitScreenOptional.get().prepareEnterSplitScreen(wct, mTaskInfo, - isPipToTopLeft() - ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); + mSplitScreenOptional.get().onPipExpandToSplit(wct, mTaskInfo); mPipTransitionController.startExitTransition( TRANSIT_EXIT_PIP_TO_SPLIT, wct, destinationBounds); return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 880d95286de6..2ec52bb028c6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -452,6 +452,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } /** + * Performs previous child eviction and such to prepare for the pip task expending into one of + * the split stages + * + * @param taskInfo TaskInfo of the pip task + */ + public void onPipExpandToSplit(WindowContainerTransaction wct, + ActivityManager.RunningTaskInfo taskInfo) { + mStageCoordinator.onPipExpandToSplit(wct, taskInfo); + } + + /** * Doing necessary window transaction for other transition handler need to exit split in * transition. */ 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 0781a9e440f3..af05aa2c5ec6 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 @@ -2982,6 +2982,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } + /** + * Performs previous child eviction and such to prepare for the pip task expending into one of + * the split stages + * + * @param taskInfo TaskInfo of the pip task + */ + public void onPipExpandToSplit(WindowContainerTransaction wct, + ActivityManager.RunningTaskInfo taskInfo) { + prepareEnterSplitScreen(wct, taskInfo, getActivateSplitPosition(taskInfo), + false /*resizeAnim*/); + + if (!isSplitScreenVisible() || mSplitRequest == null) { + return; + } + + boolean replacingMainStage = getMainStagePosition() == mSplitRequest.mActivatePosition; + (replacingMainStage ? mMainStage : mSideStage).evictOtherChildren(wct, taskInfo.taskId); + } + boolean isLaunchToSplit(TaskInfo taskInfo) { return getActivateSplitPosition(taskInfo) != SPLIT_POSITION_UNDEFINED; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java index 9ce46d69815b..e9cd73b0df5e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java @@ -95,8 +95,8 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " - + "entering PIP from an Activity Embedding window"); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for entering PIP from" + + " an Activity Embedding window #%d", info.getDebugId()); // Split into two transitions (wct) TransitionInfo.Change pipChange = null; final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */); @@ -146,6 +146,8 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for opening an intent" + + " with a remote transition and PIP #%d", info.getDebugId()); boolean handledToPip = tryAnimateOpenIntentWithRemoteAndPip( info, startTransaction, finishTransaction, finishCallback); // Consume the transition on remote handler if the leftover handler already handle this @@ -192,8 +194,9 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { } return false; } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Splitting PIP into a separate" - + " animation because remote-animation likely doesn't support it"); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Splitting PIP into a separate" + + " animation because remote-animation likely doesn't support it #%d", + info.getDebugId()); // Split the transition into 2 parts: the pip part and the rest. mInFlightSubAnimations = 2; // make a new startTransaction because pip's startEnterAnimation "consumes" it so @@ -218,6 +221,9 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for unfolding #%d", + info.getDebugId()); + final Transitions.TransitionFinishCallback finishCB = (wct) -> { mInFlightSubAnimations--; if (mInFlightSubAnimations > 0) return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java index 643e0266d7df..4ea71490798c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java @@ -30,9 +30,11 @@ import android.view.SurfaceControl; import android.window.TransitionInfo; import android.window.WindowContainerTransaction; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.splitscreen.StageCoordinator; @@ -77,6 +79,9 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition for Recents during" + + " Desktop #%d", info.getDebugId()); + if (mInfo == null) { mInfo = info; mFinishT = finishTransaction; @@ -109,6 +114,9 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for Recents during" + + " Keyguard #%d", info.getDebugId()); + if (mInfo == null) { mInfo = info; mFinishT = finishTransaction; @@ -122,6 +130,9 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for Recents during" + + " split screen #%d", info.getDebugId()); + for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); // Pip auto-entering info might be appended to recent transition like pressing diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index b0d8b47b170a..3fb0dbfaa63d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -83,6 +83,9 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.tracing.LegacyTransitionTracer; +import com.android.wm.shell.transition.tracing.PerfettoTransitionTracer; +import com.android.wm.shell.transition.tracing.TransitionTracer; import com.android.wm.shell.util.TransitionUtil; import java.io.PrintWriter; @@ -184,7 +187,7 @@ public class Transitions implements RemoteCallable<Transitions>, private final ShellController mShellController; private final ShellTransitionImpl mImpl = new ShellTransitionImpl(); private final SleepHandler mSleepHandler = new SleepHandler(); - private final Tracer mTracer = new Tracer(); + private final TransitionTracer mTransitionTracer; private boolean mIsRegistered = false; /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */ @@ -307,6 +310,12 @@ public class Transitions implements RemoteCallable<Transitions>, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote"); shellInit.addInitCallback(this::onInit, this); mHomeTransitionObserver = observer; + + if (android.tracing.Flags.perfettoTransitionTracing()) { + mTransitionTracer = new PerfettoTransitionTracer(); + } else { + mTransitionTracer = new LegacyTransitionTracer(); + } } private void onInit() { @@ -868,7 +877,7 @@ public class Transitions implements RemoteCallable<Transitions>, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while" + " %s is still animating. Notify the animating transition" + " in case they can be merged", ready, playing); - mTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId()); + mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId()); playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT, playing.mToken, (wct) -> onMerged(playing, ready)); } @@ -902,7 +911,7 @@ public class Transitions implements RemoteCallable<Transitions>, for (int i = 0; i < mObservers.size(); ++i) { mObservers.get(i).onTransitionMerged(merged.mToken, playing.mToken); } - mTracer.logMerged(merged.mInfo.getDebugId(), playing.mInfo.getDebugId()); + mTransitionTracer.logMerged(merged.mInfo.getDebugId(), playing.mInfo.getDebugId()); // See if we should merge another transition. processReadyQueue(track); } @@ -923,7 +932,7 @@ public class Transitions implements RemoteCallable<Transitions>, active.mStartT, active.mFinishT, (wct) -> onFinish(active, wct)); if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler"); - mTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler); + mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler); return; } } @@ -948,7 +957,7 @@ public class Transitions implements RemoteCallable<Transitions>, if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s", mHandlers.get(i)); - mTracer.logDispatched(info.getDebugId(), mHandlers.get(i)); + mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i)); return mHandlers.get(i); } } @@ -978,7 +987,7 @@ public class Transitions implements RemoteCallable<Transitions>, final Track track = mTracks.get(transition.getTrack()); transition.mAborted = true; - mTracer.logAborted(transition.mInfo.getDebugId()); + mTransitionTracer.logAborted(transition.mInfo.getDebugId()); if (transition.mHandler != null) { // Notifies to clean-up the aborted transition. @@ -1506,12 +1515,18 @@ public class Transitions implements RemoteCallable<Transitions>, } } - @Override public boolean onShellCommand(String[] args, PrintWriter pw) { switch (args[0]) { case "tracing": { - mTracer.onShellCommand(Arrays.copyOfRange(args, 1, args.length), pw); + if (!android.tracing.Flags.perfettoTransitionTracing()) { + ((LegacyTransitionTracer) mTransitionTracer) + .onShellCommand(Arrays.copyOfRange(args, 1, args.length), pw); + } else { + pw.println("Command not supported. Use the Perfetto command instead to start " + + "and stop this trace instead."); + return false; + } return true; } default: { @@ -1524,8 +1539,10 @@ public class Transitions implements RemoteCallable<Transitions>, @Override public void printShellCommandHelp(PrintWriter pw, String prefix) { - pw.println(prefix + "tracing"); - mTracer.printShellCommandHelp(pw, prefix + " "); + if (!android.tracing.Flags.perfettoTransitionTracing()) { + pw.println(prefix + "tracing"); + ((LegacyTransitionTracer) mTransitionTracer).printShellCommandHelp(pw, prefix + " "); + } } private void dump(@NonNull PrintWriter pw, String prefix) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/LegacyTransitionTracer.java index 5919aad133c7..9c848869e0f8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Tracer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/LegacyTransitionTracer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.wm.shell.transition; +package com.android.wm.shell.transition.tracing; import static android.os.Build.IS_USER; @@ -29,6 +29,7 @@ import android.util.Log; import com.android.internal.util.TraceBuffer; import com.android.wm.shell.sysui.ShellCommandHandler; +import com.android.wm.shell.transition.Transitions; import com.google.protobuf.nano.MessageNano; @@ -45,7 +46,8 @@ import java.util.concurrent.TimeUnit; /** * Helper class to collect and dump transition traces. */ -public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { +public class LegacyTransitionTracer + implements ShellCommandHandler.ShellCommandActionHandler, TransitionTracer { private static final int ALWAYS_ON_TRACING_CAPACITY = 15 * 1024; // 15 KB private static final int ACTIVE_TRACING_BUFFER_CAPACITY = 5000 * 1024; // 5 MB @@ -60,33 +62,33 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { private final TraceBuffer.ProtoProvider mProtoProvider = new TraceBuffer.ProtoProvider<MessageNano, - com.android.wm.shell.nano.WmShellTransitionTraceProto, - com.android.wm.shell.nano.Transition>() { - @Override - public int getItemSize(MessageNano proto) { - return proto.getCachedSize(); - } - - @Override - public byte[] getBytes(MessageNano proto) { - return MessageNano.toByteArray(proto); - } - - @Override - public void write( - com.android.wm.shell.nano.WmShellTransitionTraceProto encapsulatingProto, - Queue<com.android.wm.shell.nano.Transition> buffer, OutputStream os) + com.android.wm.shell.nano.WmShellTransitionTraceProto, + com.android.wm.shell.nano.Transition>() { + @Override + public int getItemSize(MessageNano proto) { + return proto.getCachedSize(); + } + + @Override + public byte[] getBytes(MessageNano proto) { + return MessageNano.toByteArray(proto); + } + + @Override + public void write( + com.android.wm.shell.nano.WmShellTransitionTraceProto encapsulatingProto, + Queue<com.android.wm.shell.nano.Transition> buffer, OutputStream os) throws IOException { - encapsulatingProto.transitions = buffer.toArray( - new com.android.wm.shell.nano.Transition[0]); - os.write(getBytes(encapsulatingProto)); - } - }; + encapsulatingProto.transitions = buffer.toArray( + new com.android.wm.shell.nano.Transition[0]); + os.write(getBytes(encapsulatingProto)); + } + }; private final TraceBuffer<MessageNano, com.android.wm.shell.nano.WmShellTransitionTraceProto, - com.android.wm.shell.nano.Transition> mTraceBuffer - = new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY, mProtoProvider, - (proto) -> handleOnEntryRemovedFromTrace(proto)); + com.android.wm.shell.nano.Transition> mTraceBuffer = + new TraceBuffer(ALWAYS_ON_TRACING_CAPACITY, mProtoProvider, + this::handleOnEntryRemovedFromTrace); private final Map<Object, Runnable> mRemovedFromTraceCallbacks = new HashMap<>(); private final Map<Transitions.TransitionHandler, Integer> mHandlerIds = new HashMap<>(); @@ -99,6 +101,7 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { * @param transitionId The id of the transition being dispatched. * @param handler The handler the transition is being dispatched to. */ + @Override public void logDispatched(int transitionId, Transitions.TransitionHandler handler) { final int handlerId; if (mHandlerIds.containsKey(handler)) { @@ -130,6 +133,7 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { * * @param mergeRequestedTransitionId The id of the transition we are requesting to be merged. */ + @Override public void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) { com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition(); proto.id = mergeRequestedTransitionId; @@ -145,6 +149,7 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { * @param mergedTransitionId The id of the transition that was merged. * @param playingTransitionId The id of the transition the transition was merged into. */ + @Override public void logMerged(int mergedTransitionId, int playingTransitionId) { com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition(); proto.id = mergedTransitionId; @@ -159,6 +164,7 @@ public class Tracer implements ShellCommandHandler.ShellCommandActionHandler { * * @param transitionId The id of the transition that was aborted. */ + @Override public void logAborted(int transitionId) { com.android.wm.shell.nano.Transition proto = new com.android.wm.shell.nano.Transition(); proto.id = transitionId; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java new file mode 100644 index 000000000000..99df6a31beac --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/PerfettoTransitionTracer.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2023 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.transition.tracing; + +import android.internal.perfetto.protos.PerfettoTrace; +import android.os.SystemClock; +import android.tracing.perfetto.DataSourceInstance; +import android.tracing.perfetto.DataSourceParams; +import android.tracing.perfetto.InitArguments; +import android.tracing.perfetto.Producer; +import android.tracing.perfetto.TracingContext; +import android.tracing.transition.TransitionDataSource; +import android.util.proto.ProtoOutputStream; + +import com.android.wm.shell.transition.Transitions; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Helper class to collect and dump transition traces. + */ +public class PerfettoTransitionTracer implements TransitionTracer { + private final AtomicInteger mActiveTraces = new AtomicInteger(0); + private final TransitionDataSource mDataSource = new TransitionDataSource( + mActiveTraces::incrementAndGet, + this::onFlush, + mActiveTraces::decrementAndGet); + + public PerfettoTransitionTracer() { + Producer.init(InitArguments.DEFAULTS); + mDataSource.register(DataSourceParams.DEFAULTS); + } + + /** + * Adds an entry in the trace to log that a transition has been dispatched to a handler. + * + * @param transitionId The id of the transition being dispatched. + * @param handler The handler the transition is being dispatched to. + */ + @Override + public void logDispatched(int transitionId, Transitions.TransitionHandler handler) { + if (!isTracing()) { + return; + } + + mDataSource.trace(ctx -> { + final int handlerId = getHandlerId(handler, ctx); + + final ProtoOutputStream os = ctx.newTracePacket(); + final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION); + os.write(PerfettoTrace.ShellTransition.ID, transitionId); + os.write(PerfettoTrace.ShellTransition.DISPATCH_TIME_NS, + SystemClock.elapsedRealtimeNanos()); + os.write(PerfettoTrace.ShellTransition.HANDLER, handlerId); + os.end(token); + }); + } + + private static int getHandlerId(Transitions.TransitionHandler handler, + TracingContext<DataSourceInstance, TransitionDataSource.TlsState, Void> ctx) { + final Map<String, Integer> handlerMapping = + ctx.getCustomTlsState().handlerMapping; + final int handlerId; + if (handlerMapping.containsKey(handler.getClass().getName())) { + handlerId = handlerMapping.get(handler.getClass().getName()); + } else { + // + 1 to avoid 0 ids which can be confused with missing value when dumped to proto + handlerId = handlerMapping.size() + 1; + handlerMapping.put(handler.getClass().getName(), handlerId); + } + return handlerId; + } + + /** + * Adds an entry in the trace to log that a request to merge a transition was made. + * + * @param mergeRequestedTransitionId The id of the transition we are requesting to be merged. + */ + @Override + public void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId) { + if (!isTracing()) { + return; + } + + mDataSource.trace(ctx -> { + final ProtoOutputStream os = ctx.newTracePacket(); + final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION); + os.write(PerfettoTrace.ShellTransition.ID, mergeRequestedTransitionId); + os.write(PerfettoTrace.ShellTransition.MERGE_REQUEST_TIME_NS, + SystemClock.elapsedRealtimeNanos()); + os.write(PerfettoTrace.ShellTransition.MERGE_TARGET, playingTransitionId); + os.end(token); + }); + } + + /** + * Adds an entry in the trace to log that a transition was merged by the handler. + * + * @param mergedTransitionId The id of the transition that was merged. + * @param playingTransitionId The id of the transition the transition was merged into. + */ + @Override + public void logMerged(int mergedTransitionId, int playingTransitionId) { + if (!isTracing()) { + return; + } + + mDataSource.trace(ctx -> { + final ProtoOutputStream os = ctx.newTracePacket(); + final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION); + os.write(PerfettoTrace.ShellTransition.ID, mergedTransitionId); + os.write(PerfettoTrace.ShellTransition.MERGE_TIME_NS, + SystemClock.elapsedRealtimeNanos()); + os.write(PerfettoTrace.ShellTransition.MERGE_TARGET, playingTransitionId); + os.end(token); + }); + } + + /** + * Adds an entry in the trace to log that a transition was aborted. + * + * @param transitionId The id of the transition that was aborted. + */ + @Override + public void logAborted(int transitionId) { + if (!isTracing()) { + return; + } + + mDataSource.trace(ctx -> { + final ProtoOutputStream os = ctx.newTracePacket(); + final long token = os.start(PerfettoTrace.TracePacket.SHELL_TRANSITION); + os.write(PerfettoTrace.ShellTransition.ID, transitionId); + os.write(PerfettoTrace.ShellTransition.SHELL_ABORT_TIME_NS, + SystemClock.elapsedRealtimeNanos()); + os.end(token); + }); + } + + private boolean isTracing() { + return mActiveTraces.get() > 0; + } + + private void onFlush() { + mDataSource.trace(ctx -> { + final ProtoOutputStream os = ctx.newTracePacket(); + + final Map<String, Integer> handlerMapping = ctx.getCustomTlsState().handlerMapping; + for (String handler : handlerMapping.keySet()) { + final long token = os.start(PerfettoTrace.TracePacket.SHELL_HANDLER_MAPPINGS); + os.write(PerfettoTrace.ShellHandlerMapping.ID, handlerMapping.get(handler)); + os.write(PerfettoTrace.ShellHandlerMapping.NAME, handler); + os.end(token); + } + + ctx.flush(); + }); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/TransitionTracer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/TransitionTracer.java new file mode 100644 index 000000000000..5857ad88e9e6 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/tracing/TransitionTracer.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 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.transition.tracing; + +import com.android.wm.shell.transition.Transitions; + +public interface TransitionTracer { + /** + * Adds an entry in the trace to log that a transition has been dispatched to a handler. + * + * @param transitionId The id of the transition being dispatched. + * @param handler The handler the transition is being dispatched to. + */ + void logDispatched(int transitionId, Transitions.TransitionHandler handler); + + /** + * Adds an entry in the trace to log that a request to merge a transition was made. + * + * @param mergeRequestedTransitionId The id of the transition we are requesting to be merged. + */ + void logMergeRequested(int mergeRequestedTransitionId, int playingTransitionId); + + /** + * Adds an entry in the trace to log that a transition was merged by the handler. + * + * @param mergedTransitionId The id of the transition that was merged. + * @param playingTransitionId The id of the transition the transition was merged into. + */ + void logMerged(int mergedTransitionId, int playingTransitionId); + + /** + * Adds an entry in the trace to log that a transition was aborted. + * + * @param transitionId The id of the transition that was aborted. + */ + void logAborted(int transitionId); +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java index 4aed7c449750..e6d35e83116b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldAnimationController.java @@ -16,8 +16,6 @@ package com.android.wm.shell.unfold; -import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; - import android.annotation.NonNull; import android.app.ActivityManager.RunningTaskInfo; import android.app.TaskInfo; @@ -230,8 +228,7 @@ public class UnfoldAnimationController implements UnfoldListener { } private void maybeResetTask(UnfoldTaskAnimator animator, TaskInfo taskInfo) { - // TODO(b/311084698): the windowing mode check is added here as a work around. - if (!mIsInStageChange || taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) { + if (!mIsInStageChange) { // No need to resetTask if there is no ongoing state change. return; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt index f58332198696..4878df806f82 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleViewInfoTest.kt @@ -125,7 +125,7 @@ class BubbleViewInfoTest : ShellTestCase() { mock<BubbleProperties>()) bubbleStackView = BubbleStackView(context, bubbleController, bubbleData, - surfaceSynchronizer, FloatingContentCoordinator(), mainExecutor) + surfaceSynchronizer, FloatingContentCoordinator(), bubbleController, mainExecutor) bubbleBarLayerView = BubbleBarLayerView(context, bubbleController) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java index 23a4e3956289..4ddc539eb220 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java @@ -127,6 +127,7 @@ public class CompatUILayoutTest extends ShellTestCase { @Test public void testOnClickForSizeCompatHint() { mWindowManager.mHasSizeCompat = true; + doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(mTaskInfo); mWindowManager.createLayout(/* canShow= */ true); final LinearLayout sizeCompatHint = mLayout.findViewById(R.id.size_compat_hint); sizeCompatHint.performClick(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java index d4b97ed55192..2acfd83084ab 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java @@ -20,6 +20,8 @@ import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_DISMISSED; import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN; import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED; import static android.app.AppCompatTaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; import static android.view.WindowInsets.Type.navigationBars; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -27,6 +29,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; @@ -39,6 +42,7 @@ import android.app.AppCompatTaskInfo; import android.app.TaskInfo; import android.content.res.Configuration; import android.graphics.Rect; +import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.util.Pair; import android.view.DisplayInfo; @@ -50,6 +54,7 @@ import android.view.View; import androidx.test.filters.SmallTest; +import com.android.window.flags.Flags; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; @@ -59,6 +64,7 @@ import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState; import junit.framework.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -76,6 +82,8 @@ import java.util.function.Consumer; @RunWith(AndroidTestingRunner.class) @SmallTest public class CompatUIWindowManagerTest extends ShellTestCase { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); private static final int TASK_ID = 1; @@ -107,6 +115,7 @@ public class CompatUIWindowManagerTest extends ShellTestCase { public void testCreateSizeCompatButton() { // Doesn't create layout if show is false. mWindowManager.mHasSizeCompat = true; + doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(mTaskInfo); assertTrue(mWindowManager.createLayout(/* canShow= */ false)); verify(mWindowManager, never()).inflateLayout(); @@ -199,6 +208,7 @@ public class CompatUIWindowManagerTest extends ShellTestCase { // No diff clearInvocations(mWindowManager); TaskInfo taskInfo = createTaskInfo(/* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN); + doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(any()); assertTrue(mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true)); verify(mWindowManager, never()).updateSurfacePosition(); @@ -284,6 +294,7 @@ public class CompatUIWindowManagerTest extends ShellTestCase { @Test public void testUpdateCompatInfoLayoutNotInflatedYet() { mWindowManager.mHasSizeCompat = true; + doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(any()); mWindowManager.createLayout(/* canShow= */ false); verify(mWindowManager, never()).inflateLayout(); @@ -353,6 +364,7 @@ public class CompatUIWindowManagerTest extends ShellTestCase { // Create button if it is not created. mWindowManager.mLayout = null; mWindowManager.mHasSizeCompat = true; + doReturn(true).when(mWindowManager).shouldShowSizeCompatRestartButton(mTaskInfo); mWindowManager.updateVisibility(/* canShow= */ true); verify(mWindowManager).createLayout(/* canShow= */ true); @@ -464,6 +476,37 @@ public class CompatUIWindowManagerTest extends ShellTestCase { Assert.assertTrue(mWindowManager.needsToBeRecreated(newTaskInfo, mTaskListener)); } + @Test + public void testShouldShowSizeCompatRestartButton() { + mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_HIDE_SCM_BUTTON); + + doReturn(86).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance(); + mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue, + mCallback, mTaskListener, new DisplayLayout(), new CompatUIHintsState(), + mCompatUIConfiguration, mOnRestartButtonClicked); + + // Simulate rotation of activity in square display + TaskInfo taskInfo = createTaskInfo(true, CAMERA_COMPAT_CONTROL_HIDDEN); + taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 2000, 2000)); + taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 2000; + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1850; + + assertFalse(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo)); + + // Simulate exiting split screen/folding + taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1000; + assertTrue(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo)); + + // Simulate folding + taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 0, 1000, 2000)); + assertFalse(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo)); + + taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = 1000; + taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = 500; + assertTrue(mWindowManager.shouldShowSizeCompatRestartButton(taskInfo)); + } + private static TaskInfo createTaskInfo(boolean hasSizeCompat, @AppCompatTaskInfo.CameraCompatControlState int cameraCompatControlState) { ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt index 3bc90ade898e..be639e867e0b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandlerTest.kt @@ -34,6 +34,7 @@ import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.kotlin.mock import org.mockito.kotlin.never +import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.verifyZeroInteractions import org.mockito.kotlin.whenever @@ -150,6 +151,23 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { } @Test + fun startDragToDesktop_anotherTransitionInProgress_startDropped() { + val task = createTask() + val dragAnimator = mock<MoveToDesktopAnimator>() + + // Simulate attempt to start two drag to desktop transitions. + startDragToDesktopTransition(task, dragAnimator) + startDragToDesktopTransition(task, dragAnimator) + + // Verify transition only started once. + verify(transitions, times(1)).startTransition( + eq(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP), + any(), + eq(handler) + ) + } + + @Test fun cancelDragToDesktop_startWasReady_cancel() { val task = createTask() val dragAnimator = mock<MoveToDesktopAnimator>() @@ -189,6 +207,32 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() { verifyZeroInteractions(dragAnimator) } + @Test + fun cancelDragToDesktop_transitionNotInProgress_dropCancel() { + // Then cancel is called before the transition was started. + handler.cancelDragToDesktopTransition() + + // Verify cancel is dropped. + verify(transitions, never()).startTransition( + eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP), + any(), + eq(handler) + ) + } + + @Test + fun finishDragToDesktop_transitionNotInProgress_dropFinish() { + // Then finish is called before the transition was started. + handler.finishDragToDesktopTransition(WindowContainerTransaction()) + + // Verify finish is dropped. + verify(transitions, never()).startTransition( + eq(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP), + any(), + eq(handler) + ) + } + private fun startDragToDesktopTransition( task: RunningTaskInfo, dragAnimator: MoveToDesktopAnimator diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index c9d5e074271b..d9166a16cdea 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -21,6 +21,7 @@ #include <algorithm> #include <cstddef> #include <limits> +#include <optional> #include "android-base/logging.h" #include "android-base/stringprintf.h" @@ -50,7 +51,9 @@ namespace { // contiguous block of memory to store both the TypeSpec struct and // the Type structs. struct TypeSpecBuilder { - explicit TypeSpecBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) : header_(header) {} + explicit TypeSpecBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) : header_(header) { + type_entries.reserve(dtohs(header_->typesCount)); + } void AddType(incfs::verified_map_ptr<ResTable_type> type) { TypeSpec::TypeEntry& entry = type_entries.emplace_back(); @@ -59,6 +62,7 @@ struct TypeSpecBuilder { } TypeSpec Build() { + type_entries.shrink_to_fit(); return {header_, std::move(type_entries)}; } @@ -450,7 +454,8 @@ const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, package_property_t property_flags) { ATRACE_NAME("LoadedPackage::Load"); - std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage()); + const bool optimize_name_lookups = (property_flags & PROPERTY_OPTIMIZE_NAME_LOOKUPS) != 0; + std::unique_ptr<LoadedPackage> loaded_package(new LoadedPackage(optimize_name_lookups)); // typeIdOffset was added at some point, but we still must recognize apps built before this // was added. @@ -499,7 +504,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, // A map of TypeSpec builders, each associated with an type index. // We use these to accumulate the set of Types available for a TypeSpec, and later build a single, // contiguous block of memory that holds all the Types together with the TypeSpec. - std::unordered_map<int, std::unique_ptr<TypeSpecBuilder>> type_builder_map; + std::unordered_map<int, std::optional<TypeSpecBuilder>> type_builder_map; ChunkIterator iter(chunk.data_ptr(), chunk.data_size()); while (iter.HasNext()) { @@ -567,14 +572,14 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, return {}; } - if (entry_count * sizeof(uint32_t) > chunk.data_size()) { + if (entry_count * sizeof(uint32_t) > child_chunk.data_size()) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small to hold entries."; return {}; } - std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type_spec->id]; - if (builder_ptr == nullptr) { - builder_ptr = util::make_unique<TypeSpecBuilder>(type_spec.verified()); + auto& maybe_type_builder = type_builder_map[type_spec->id]; + if (!maybe_type_builder) { + maybe_type_builder.emplace(type_spec.verified()); loaded_package->resource_ids_.set(type_spec->id, entry_count); } else { LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", @@ -594,9 +599,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } // Type chunks must be preceded by their TypeSpec chunks. - std::unique_ptr<TypeSpecBuilder>& builder_ptr = type_builder_map[type->id]; - if (builder_ptr != nullptr) { - builder_ptr->AddType(type.verified()); + auto& maybe_type_builder = type_builder_map[type->id]; + if (maybe_type_builder) { + maybe_type_builder->AddType(type.verified()); } else { LOG(ERROR) << StringPrintf( "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 4c992becda7c..2c99f1aa3675 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -447,15 +447,19 @@ Res_png_9patch* Res_png_9patch::deserialize(void* inData) // -------------------------------------------------------------------- // -------------------------------------------------------------------- -ResStringPool::ResStringPool() - : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) -{ +ResStringPool::ResStringPool() : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) { } -ResStringPool::ResStringPool(const void* data, size_t size, bool copyData) - : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) -{ - setTo(data, size, copyData); +ResStringPool::ResStringPool(bool optimize_name_lookups) : ResStringPool() { + if (optimize_name_lookups) { + mIndexLookupCache.emplace(); + } +} + +ResStringPool::ResStringPool(const void* data, size_t size, bool copyData, + bool optimize_name_lookups) + : ResStringPool(optimize_name_lookups) { + setTo(data, size, copyData); } ResStringPool::~ResStringPool() @@ -683,6 +687,14 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD mStylePoolSize = 0; } + if (mIndexLookupCache) { + if ((mHeader->flags & ResStringPool_header::UTF8_FLAG) != 0) { + mIndexLookupCache->first.reserve(mHeader->stringCount); + } else { + mIndexLookupCache->second.reserve(mHeader->stringCount); + } + } + return (mError=NO_ERROR); } @@ -708,6 +720,10 @@ void ResStringPool::uninit() free(mOwnedData); mOwnedData = NULL; } + if (mIndexLookupCache) { + mIndexLookupCache->first.clear(); + mIndexLookupCache->second.clear(); + } } /** @@ -824,11 +840,11 @@ base::expected<StringPiece16, NullOrIOError> ResStringPool::stringAt(size_t idx) // encLen must be less than 0x7FFF due to encoding. if ((uint32_t)(u8str+*u8len-strings) < mStringPoolSize) { - AutoMutex lock(mDecodeLock); + AutoMutex lock(mCachesLock); - if (mCache != NULL && mCache[idx] != NULL) { - return StringPiece16(mCache[idx], *u16len); - } + if (mCache != NULL && mCache[idx] != NULL) { + return StringPiece16(mCache[idx], *u16len); + } // Retrieve the actual length of the utf8 string if the // encoded length was truncated @@ -1093,12 +1109,24 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ // block, start searching at the back. String8 str8(str, strLen); const size_t str8Len = str8.size(); + std::optional<AutoMutex> cacheLock; + if (mIndexLookupCache) { + cacheLock.emplace(mCachesLock); + if (auto it = mIndexLookupCache->first.find(std::string_view(str8)); + it != mIndexLookupCache->first.end()) { + return it->second; + } + } + for (int i=mHeader->stringCount-1; i>=0; i--) { const base::expected<StringPiece, NullOrIOError> s = string8At(i); if (UNLIKELY(IsIOError(s))) { return base::unexpected(s.error()); } if (s.has_value()) { + if (mIndexLookupCache) { + mIndexLookupCache->first.insert({*s, i}); + } if (kDebugStringPoolNoisy) { ALOGI("Looking at %s, i=%d\n", s->data(), i); } @@ -1151,20 +1179,32 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ // most often this happens because we want to get IDs for style // span tags; since those always appear at the end of the string // block, start searching at the back. + std::optional<AutoMutex> cacheLock; + if (mIndexLookupCache) { + cacheLock.emplace(mCachesLock); + if (auto it = mIndexLookupCache->second.find({str, strLen}); + it != mIndexLookupCache->second.end()) { + return it->second; + } + } for (int i=mHeader->stringCount-1; i>=0; i--) { const base::expected<StringPiece16, NullOrIOError> s = stringAt(i); if (UNLIKELY(IsIOError(s))) { return base::unexpected(s.error()); } if (kDebugStringPoolNoisy) { - ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).c_str(), i); + ALOGI("Looking16 at %s, i=%d\n", String8(s->data(), s->size()).c_str(), i); } - if (s.has_value() && strLen == s->size() && - strzcmp16(s->data(), s->size(), str, strLen) == 0) { + if (s.has_value()) { + if (mIndexLookupCache) { + mIndexLookupCache->second.insert({*s, i}); + } + if (strLen == s->size() && strzcmp16(s->data(), s->size(), str, strLen) == 0) { if (kDebugStringPoolNoisy) { - ALOGI("MATCH!"); + ALOGI("MATCH16!"); } return i; + } } } } diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index 60689128dffb..d9f7c2a1ac19 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -71,7 +71,7 @@ class OverlayDynamicRefTable : public DynamicRefTable { // Rewrites a compile-time overlay resource id to the runtime resource id of corresponding target // resource. - virtual status_t lookupResourceIdNoRewrite(uint32_t* resId) const; + status_t lookupResourceIdNoRewrite(uint32_t* resId) const; const Idmap_data_header* data_header_; const Idmap_overlay_entry* entries_; diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 3a7287187781..413b27829474 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -99,6 +99,9 @@ enum : package_property_t { // The apk assets only contain the overlayable declarations information. PROPERTY_ONLY_OVERLAYABLES = 1U << 5U, + + // Optimize the resource lookups by name via an in-memory lookup table. + PROPERTY_OPTIMIZE_NAME_LOOKUPS = 1U << 6U, }; struct OverlayableInfo { @@ -285,7 +288,9 @@ class LoadedPackage { private: DISALLOW_COPY_AND_ASSIGN(LoadedPackage); - LoadedPackage() = default; + explicit LoadedPackage(bool optimize_name_lookups = false) + : type_string_pool_(optimize_name_lookups), key_string_pool_(optimize_name_lookups) { + } ResStringPool type_string_pool_; ResStringPool key_string_pool_; diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index c0514fdff469..3d1403d0e039 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -22,27 +22,28 @@ #include <android-base/expected.h> #include <android-base/unique_fd.h> - +#include <android/configuration.h> #include <androidfw/Asset.h> #include <androidfw/Errors.h> #include <androidfw/LocaleData.h> #include <androidfw/StringPiece.h> #include <utils/ByteOrder.h> #include <utils/Errors.h> +#include <utils/KeyedVector.h> #include <utils/String16.h> #include <utils/Vector.h> -#include <utils/KeyedVector.h> - #include <utils/threads.h> #include <stdint.h> #include <sys/types.h> -#include <android/configuration.h> - #include <array> #include <map> #include <memory> +#include <optional> +#include <string_view> +#include <unordered_map> +#include <utility> namespace android { @@ -512,23 +513,24 @@ struct ResStringPool_span class ResStringPool { public: - ResStringPool(); - ResStringPool(const void* data, size_t size, bool copyData=false); - virtual ~ResStringPool(); + ResStringPool(); + explicit ResStringPool(bool optimize_name_lookups); + ResStringPool(const void* data, size_t size, bool copyData = false, + bool optimize_name_lookups = false); + virtual ~ResStringPool(); - void setToEmpty(); - status_t setTo(incfs::map_ptr<void> data, size_t size, bool copyData=false); + void setToEmpty(); + status_t setTo(incfs::map_ptr<void> data, size_t size, bool copyData = false); - status_t getError() const; + status_t getError() const; - void uninit(); + void uninit(); - // Return string entry as UTF16; if the pool is UTF8, the string will - // be converted before returning. - inline base::expected<StringPiece16, NullOrIOError> stringAt( - const ResStringPool_ref& ref) const { - return stringAt(ref.index); - } + // Return string entry as UTF16; if the pool is UTF8, the string will + // be converted before returning. + inline base::expected<StringPiece16, NullOrIOError> stringAt(const ResStringPool_ref& ref) const { + return stringAt(ref.index); + } virtual base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const; // Note: returns null if the string pool is not UTF8. @@ -557,7 +559,7 @@ private: void* mOwnedData; incfs::verified_map_ptr<ResStringPool_header> mHeader; size_t mSize; - mutable Mutex mDecodeLock; + mutable Mutex mCachesLock; incfs::map_ptr<uint32_t> mEntries; incfs::map_ptr<uint32_t> mEntryStyles; incfs::map_ptr<void> mStrings; @@ -566,6 +568,10 @@ private: incfs::map_ptr<uint32_t> mStyles; uint32_t mStylePoolSize; // number of uint32_t + mutable std::optional<std::pair<std::unordered_map<std::string_view, int>, + std::unordered_map<std::u16string_view, int>>> + mIndexLookupCache; + base::expected<StringPiece, NullOrIOError> stringDecodeAt( size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const; }; @@ -1401,8 +1407,8 @@ struct ResTable_typeSpec // Must be 0. uint8_t res0; - // Must be 0. - uint16_t res1; + // Used to be reserved, if >0 specifies the number of ResTable_type entries for this spec. + uint16_t typesCount; // Number of uint32_t entry configuration masks that follow. uint32_t entryCount; diff --git a/libs/hwui/AutoBackendTextureRelease.cpp b/libs/hwui/AutoBackendTextureRelease.cpp index 4d020c567972..5f5ffe97e953 100644 --- a/libs/hwui/AutoBackendTextureRelease.cpp +++ b/libs/hwui/AutoBackendTextureRelease.cpp @@ -21,6 +21,7 @@ #include <include/gpu/GrDirectContext.h> #include <include/gpu/GrBackendSurface.h> #include <include/gpu/MutableTextureState.h> +#include <include/gpu/vk/VulkanMutableTextureState.h> #include "renderthread/RenderThread.h" #include "utils/Color.h" #include "utils/PaintUtils.h" @@ -142,8 +143,9 @@ void AutoBackendTextureRelease::releaseQueueOwnership(GrDirectContext* context) LOG_ALWAYS_FATAL_IF(Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan); if (mBackendTexture.isValid()) { // Passing in VK_IMAGE_LAYOUT_UNDEFINED means we keep the old layout. - skgpu::MutableTextureState newState(VK_IMAGE_LAYOUT_UNDEFINED, - VK_QUEUE_FAMILY_FOREIGN_EXT); + skgpu::MutableTextureState newState = skgpu::MutableTextureStates::MakeVulkan( + VK_IMAGE_LAYOUT_UNDEFINED, + VK_QUEUE_FAMILY_FOREIGN_EXT); // The unref for this ref happens in the releaseProc passed into setBackendTextureState. The // releaseProc callback will be made when the work to set the new state has finished on the diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h index 00d049cde925..6ebfc63bdf26 100644 --- a/libs/hwui/FeatureFlags.h +++ b/libs/hwui/FeatureFlags.h @@ -41,6 +41,14 @@ inline bool deprecate_ui_fonts() { #endif // __ANDROID__ } +inline bool inter_character_justification() { +#ifdef __ANDROID__ + return com_android_text_flags_inter_character_justification(); +#else + return true; +#endif // __ANDROID__ +} + } // namespace text_feature } // namespace android diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp index f4ee36ec66d1..bbb142014ed8 100644 --- a/libs/hwui/hwui/MinikinSkia.cpp +++ b/libs/hwui/hwui/MinikinSkia.cpp @@ -131,11 +131,6 @@ std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation( const std::vector<minikin::FontVariation>& variations) const { SkFontArguments args; - int ttcIndex; - std::unique_ptr<SkStreamAsset> stream(mTypeface->openStream(&ttcIndex)); - LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed"); - - args.setCollectionIndex(ttcIndex); std::vector<SkFontArguments::VariationPosition::Coordinate> skVariation; skVariation.resize(variations.size()); for (size_t i = 0; i < variations.size(); i++) { @@ -143,11 +138,10 @@ std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation( skVariation[i].value = SkFloatToScalar(variations[i].value); } args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())}); - sk_sp<SkFontMgr> fm = android::FreeTypeFontMgr(); - sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), args)); + sk_sp<SkTypeface> face = mTypeface->makeClone(args); return std::make_shared<MinikinFontSkia>(std::move(face), mSourceId, mFontData, mFontSize, - mFilePath, ttcIndex, variations); + mFilePath, mTtcIndex, variations); } // hinting<<16 | edging<<8 | bools:5bits diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index 833069f363c8..56133699d5f5 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -72,10 +72,13 @@ minikin::Layout MinikinUtils::doLayout(const Paint* paint, minikin::Bidi bidiFla const minikin::Range contextRange(contextStart, contextStart + contextCount); const minikin::StartHyphenEdit startHyphen = paint->getStartHyphenEdit(); const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit(); + const minikin::RunFlag minikinRunFlag = text_feature::inter_character_justification() + ? paint->getRunFlag() + : minikin::RunFlag::NONE; if (mt == nullptr) { return minikin::Layout(textBuf.substr(contextRange), range - contextStart, bidiFlags, - minikinPaint, startHyphen, endHyphen); + minikinPaint, startHyphen, endHyphen, minikinRunFlag); } else { return mt->buildLayout(textBuf, range, contextRange, minikinPaint, startHyphen, endHyphen); } @@ -102,9 +105,12 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, const minikin::Range range(start, start + count); const minikin::StartHyphenEdit startHyphen = paint->getStartHyphenEdit(); const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit(); + const minikin::RunFlag minikinRunFlag = text_feature::inter_character_justification() + ? paint->getRunFlag() + : minikin::RunFlag::NONE; return minikin::Layout::measureText(textBuf, range, bidiFlags, minikinPaint, startHyphen, - endHyphen, advances, bounds, clusterCount); + endHyphen, advances, bounds, clusterCount, minikinRunFlag); } minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags, diff --git a/libs/hwui/hwui/Paint.h b/libs/hwui/hwui/Paint.h index ef4dce57bf46..708f96e5a070 100644 --- a/libs/hwui/hwui/Paint.h +++ b/libs/hwui/hwui/Paint.h @@ -25,6 +25,7 @@ #include <minikin/FontFamily.h> #include <minikin/FontFeature.h> #include <minikin/Hyphenator.h> +#include <minikin/Layout.h> #include <string> @@ -144,6 +145,9 @@ public: bool isDevKern() const { return mDevKern; } void setDevKern(bool d) { mDevKern = d; } + minikin::RunFlag getRunFlag() const { return mRunFlag; } + void setRunFlag(minikin::RunFlag runFlag) { mRunFlag = runFlag; } + // Deprecated -- bitmapshaders will be taking this flag explicitly bool isFilterBitmap() const { return mFilterBitmap; } void setFilterBitmap(bool filter) { mFilterBitmap = filter; } @@ -188,6 +192,7 @@ private: bool mStrikeThru = false; bool mUnderline = false; bool mDevKern = false; + minikin::RunFlag mRunFlag = minikin::RunFlag::NONE; }; } // namespace android diff --git a/libs/hwui/hwui/PaintImpl.cpp b/libs/hwui/hwui/PaintImpl.cpp index aac928f85924..c32ea01db7b7 100644 --- a/libs/hwui/hwui/PaintImpl.cpp +++ b/libs/hwui/hwui/PaintImpl.cpp @@ -47,8 +47,8 @@ Paint::Paint(const Paint& paint) , mFilterBitmap(paint.mFilterBitmap) , mStrikeThru(paint.mStrikeThru) , mUnderline(paint.mUnderline) - , mDevKern(paint.mDevKern) {} - + , mDevKern(paint.mDevKern) + , mRunFlag(paint.mRunFlag) {} Paint::~Paint() {} @@ -68,21 +68,19 @@ Paint& Paint::operator=(const Paint& other) { mStrikeThru = other.mStrikeThru; mUnderline = other.mUnderline; mDevKern = other.mDevKern; + mRunFlag = other.mRunFlag; return *this; } bool operator==(const Paint& a, const Paint& b) { - return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && - a.mFont == b.mFont && - a.mLooper == b.mLooper && - a.mLetterSpacing == b.mLetterSpacing && a.mWordSpacing == b.mWordSpacing && - a.mFontFeatureSettings == b.mFontFeatureSettings && + return static_cast<const SkPaint&>(a) == static_cast<const SkPaint&>(b) && a.mFont == b.mFont && + a.mLooper == b.mLooper && a.mLetterSpacing == b.mLetterSpacing && + a.mWordSpacing == b.mWordSpacing && a.mFontFeatureSettings == b.mFontFeatureSettings && a.mMinikinLocaleListId == b.mMinikinLocaleListId && a.mFamilyVariant == b.mFamilyVariant && a.mHyphenEdit == b.mHyphenEdit && a.mTypeface == b.mTypeface && a.mAlign == b.mAlign && - a.mFilterBitmap == b.mFilterBitmap && - a.mStrikeThru == b.mStrikeThru && a.mUnderline == b.mUnderline && - a.mDevKern == b.mDevKern; + a.mFilterBitmap == b.mFilterBitmap && a.mStrikeThru == b.mStrikeThru && + a.mUnderline == b.mUnderline && a.mDevKern == b.mDevKern && a.mRunFlag == b.mRunFlag; } void Paint::reset() { @@ -96,6 +94,7 @@ void Paint::reset() { mStrikeThru = false; mUnderline = false; mDevKern = false; + mRunFlag = minikin::RunFlag::NONE; } void Paint::setLooper(sk_sp<BlurDrawLooper> looper) { @@ -133,6 +132,8 @@ static const uint32_t sForceAutoHinting = 0x800; // flags related to minikin::Paint static const uint32_t sUnderlineFlag = 0x08; static const uint32_t sStrikeThruFlag = 0x10; +static const uint32_t sTextRunLeftEdge = 0x2000; +static const uint32_t sTextRunRightEdge = 0x4000; // flags no longer supported on native side (but mirrored for compatibility) static const uint32_t sDevKernFlag = 0x100; @@ -186,6 +187,12 @@ uint32_t Paint::getJavaFlags() const { flags |= -(int)mUnderline & sUnderlineFlag; flags |= -(int)mDevKern & sDevKernFlag; flags |= -(int)mFilterBitmap & sFilterBitmapFlag; + if (mRunFlag & minikin::RunFlag::LEFT_EDGE) { + flags |= sTextRunLeftEdge; + } + if (mRunFlag & minikin::RunFlag::RIGHT_EDGE) { + flags |= sTextRunRightEdge; + } return flags; } @@ -196,6 +203,15 @@ void Paint::setJavaFlags(uint32_t flags) { mUnderline = (flags & sUnderlineFlag) != 0; mDevKern = (flags & sDevKernFlag) != 0; mFilterBitmap = (flags & sFilterBitmapFlag) != 0; + + std::underlying_type<minikin::RunFlag>::type rawFlag = minikin::RunFlag::NONE; + if (flags & sTextRunLeftEdge) { + rawFlag |= minikin::RunFlag::LEFT_EDGE; + } + if (flags & sTextRunRightEdge) { + rawFlag |= minikin::RunFlag::RIGHT_EDGE; + } + mRunFlag = static_cast<minikin::RunFlag>(rawFlag); } } // namespace android diff --git a/libs/hwui/jni/PathMeasure.cpp b/libs/hwui/jni/PathMeasure.cpp index acf893e9544c..79acb6cc35e5 100644 --- a/libs/hwui/jni/PathMeasure.cpp +++ b/libs/hwui/jni/PathMeasure.cpp @@ -17,7 +17,11 @@ #include "GraphicsJNI.h" +#include "SkMatrix.h" +#include "SkPath.h" #include "SkPathMeasure.h" +#include "SkPoint.h" +#include "SkScalar.h" /* We declare an explicit pair, so that we don't have to rely on the java client to be sure not to edit the path while we have an active measure diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index 8ba750372d18..d5725935551a 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -613,6 +613,12 @@ static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray c Paint* paint = reinterpret_cast<Paint*>(paintHandle); const Typeface* typeface = paint->getAndroidTypeface(); ScopedCharArrayRO text(env, charArray); + + // The drawText API is designed to draw entire line, so ignore the text run flag and draw the + // text as entire line mode. + const minikin::RunFlag originalRunFlag = paint->getRunFlag(); + paint->setRunFlag(minikin::RunFlag::WHOLE_LINE); + // drawTextString and drawTextChars doesn't use context info get_canvas(canvasHandle)->drawText( text.get() + index, count, // text buffer @@ -620,6 +626,7 @@ static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray c 0, count, // context range x, y, // draw position static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */); + paint->setRunFlag(originalRunFlag); } static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring strObj, @@ -629,6 +636,12 @@ static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring str Paint* paint = reinterpret_cast<Paint*>(paintHandle); const Typeface* typeface = paint->getAndroidTypeface(); const int count = end - start; + + // The drawText API is designed to draw entire line, so ignore the text run flag and draw the + // text as entire line mode. + const minikin::RunFlag originalRunFlag = paint->getRunFlag(); + paint->setRunFlag(minikin::RunFlag::WHOLE_LINE); + // drawTextString and drawTextChars doesn't use context info get_canvas(canvasHandle)->drawText( text.get() + start, count, // text buffer @@ -636,6 +649,7 @@ static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring str 0, count, // context range x, y, // draw position static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */); + paint->setRunFlag(originalRunFlag); } static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray, @@ -681,9 +695,15 @@ static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharA jchar* jchars = env->GetCharArrayElements(text, NULL); + // The drawText API is designed to draw entire line, so ignore the text run flag and draw the + // text as entire line mode. + const minikin::RunFlag originalRunFlag = paint->getRunFlag(); + paint->setRunFlag(minikin::RunFlag::WHOLE_LINE); + get_canvas(canvasHandle)->drawTextOnPath(jchars + index, count, static_cast<minikin::Bidi>(bidiFlags), *path, hOffset, vOffset, *paint, typeface); + paint->setRunFlag(originalRunFlag); env->ReleaseCharArrayElements(text, jchars, 0); } @@ -697,9 +717,15 @@ static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstri const jchar* jchars = env->GetStringChars(text, NULL); int count = env->GetStringLength(text); + // The drawText API is designed to draw entire line, so ignore the text run flag and draw the + // text as entire line mode. + const minikin::RunFlag originalRunFlag = paint->getRunFlag(); + paint->setRunFlag(minikin::RunFlag::WHOLE_LINE); + get_canvas(canvasHandle)->drawTextOnPath(jchars, count, static_cast<minikin::Bidi>(bidiFlags), *path, hOffset, vOffset, *paint, typeface); + paint->setRunFlag(originalRunFlag); env->ReleaseStringChars(text, jchars); } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index c5ffbb70213e..c8d598702a7c 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -118,7 +118,7 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler, - const HardwareBufferRenderParams& bufferParams) { + const HardwareBufferRenderParams& bufferParams, std::mutex& profilerLock) { if (!isCapturingSkp() && !mHardwareBuffer) { mEglManager.damageFrame(frame, dirty); } @@ -171,6 +171,7 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || ProfileType::None != Properties::getProfileType())) { + std::scoped_lock lock(profilerLock); SkCanvas* profileCanvas = surface->getCanvas(); SkiaProfileRenderer profileRenderer(profileCanvas, frame.width(), frame.height()); profiler->draw(profileRenderer); diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 098a74655831..ebe8b6e15d44 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -42,7 +42,8 @@ public: const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler, - const renderthread::HardwareBufferRenderParams& bufferParams) override; + const renderthread::HardwareBufferRenderParams& bufferParams, + std::mutex& profilerLock) override; GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; } bool swapBuffers(const renderthread::Frame& frame, IRenderPipeline::DrawResult& drawResult, const SkRect& screenDirty, FrameInfo* currentFrameInfo, diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index d74748936d15..fd0a8e06f39c 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -75,7 +75,7 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler, - const HardwareBufferRenderParams& bufferParams) { + const HardwareBufferRenderParams& bufferParams, std::mutex& profilerLock) { sk_sp<SkSurface> backBuffer; SkMatrix preTransform; if (mHardwareBuffer) { @@ -103,6 +103,7 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || ProfileType::None != Properties::getProfileType())) { + std::scoped_lock lock(profilerLock); SkCanvas* profileCanvas = backBuffer->getCanvas(); SkAutoCanvasRestore saver(profileCanvas, true); profileCanvas->concat(preTransform); diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index e2ea57d32cd5..624eaa51a584 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -42,7 +42,8 @@ public: const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler, - const renderthread::HardwareBufferRenderParams& bufferParams) override; + const renderthread::HardwareBufferRenderParams& bufferParams, + std::mutex& profilerLock) override; GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; } bool swapBuffers(const renderthread::Frame& frame, IRenderPipeline::DrawResult& drawResult, const SkRect& screenDirty, FrameInfo* currentFrameInfo, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 56b52dc2abe7..9c7f7cc24266 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -625,14 +625,9 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { { // FrameInfoVisualizer accesses the frame events, which cannot be mutated mid-draw // or it can lead to memory corruption. - // This lock is overly broad, but it's the quickest fix since this mutex is otherwise - // not visible to IRenderPipeline much less FrameInfoVisualizer. And since this is - // the thread we're primarily concerned about being responsive, this being too broad - // shouldn't pose a performance issue. - std::scoped_lock lock(mFrameMetricsReporterMutex); - drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, - &mLayerUpdateQueue, mContentDrawBounds, mOpaque, - mLightInfo, mRenderNodes, &(profiler()), mBufferParams); + drawResult = mRenderPipeline->draw( + frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds, + mOpaque, mLightInfo, mRenderNodes, &(profiler()), mBufferParams, profilerLock()); } uint64_t frameCompleteNr = getFrameNumber(); @@ -762,7 +757,7 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { mCurrentFrameInfo->markFrameCompleted(); mCurrentFrameInfo->set(FrameInfoIndex::GpuCompleted) = mCurrentFrameInfo->get(FrameInfoIndex::FrameCompleted); - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); mJankTracker.finishFrame(*mCurrentFrameInfo, mFrameMetricsReporter, frameCompleteNr, mSurfaceControlGenerationId); } @@ -791,7 +786,7 @@ void CanvasContext::draw(bool solelyTextureViewUpdates) { void CanvasContext::reportMetricsWithPresentTime() { { // acquire lock - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter == nullptr) { return; } @@ -826,7 +821,7 @@ void CanvasContext::reportMetricsWithPresentTime() { forthBehind->set(FrameInfoIndex::DisplayPresentTime) = presentTime; { // acquire lock - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter != nullptr) { mFrameMetricsReporter->reportFrameMetrics(forthBehind->data(), true /*hasPresentTime*/, frameNumber, surfaceControlId); @@ -835,7 +830,7 @@ void CanvasContext::reportMetricsWithPresentTime() { } void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) { - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter.get() == nullptr) { mFrameMetricsReporter.reset(new FrameMetricsReporter()); } @@ -849,7 +844,7 @@ void CanvasContext::addFrameMetricsObserver(FrameMetricsObserver* observer) { } void CanvasContext::removeFrameMetricsObserver(FrameMetricsObserver* observer) { - std::scoped_lock lock(mFrameMetricsReporterMutex); + std::scoped_lock lock(mFrameInfoMutex); if (mFrameMetricsReporter.get() != nullptr) { mFrameMetricsReporter->removeObserver(observer); if (!mFrameMetricsReporter->hasObservers()) { @@ -886,7 +881,7 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceContro FrameInfo* frameInfo = instance->getFrameInfoFromLast4(frameNumber, surfaceControlId); if (frameInfo != nullptr) { - std::scoped_lock lock(instance->mFrameMetricsReporterMutex); + std::scoped_lock lock(instance->mFrameInfoMutex); frameInfo->set(FrameInfoIndex::FrameCompleted) = std::max(gpuCompleteTime, frameInfo->get(FrameInfoIndex::SwapBuffersCompleted)); frameInfo->set(FrameInfoIndex::GpuCompleted) = std::max( diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index be9b649030ae..e2e3fa35b9b0 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -164,6 +164,7 @@ public: void notifyFramePending(); FrameInfoVisualizer& profiler() { return mProfiler; } + std::mutex& profilerLock() { return mFrameInfoMutex; } void dumpFrames(int fd); void resetFrameStats(); @@ -342,9 +343,8 @@ private: std::string mName; JankTracker mJankTracker; FrameInfoVisualizer mProfiler; - std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter - GUARDED_BY(mFrameMetricsReporterMutex); - std::mutex mFrameMetricsReporterMutex; + std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter GUARDED_BY(mFrameInfoMutex); + std::mutex mFrameInfoMutex; std::set<RenderNode*> mPrefetchedLayers; diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 9c879d5a58d1..b8c3a4de2bd4 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -68,7 +68,8 @@ public: const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler, - const HardwareBufferRenderParams& bufferParams) = 0; + const HardwareBufferRenderParams& bufferParams, + std::mutex& profilerLock) = 0; virtual bool swapBuffers(const Frame& frame, IRenderPipeline::DrawResult&, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; |