diff options
15 files changed, 404 insertions, 61 deletions
diff --git a/api/Android.bp b/api/Android.bp index 9029d252b470..363197a54fac 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -307,10 +307,6 @@ stubs_defaults { "framework-protos", ], flags: [ - "--api-lint-ignore-prefix android.icu.", - "--api-lint-ignore-prefix java.", - "--api-lint-ignore-prefix junit.", - "--api-lint-ignore-prefix org.", "--error NoSettingsProvider", "--error UnhiddenSystemApi", "--error UnflaggedApi", diff --git a/core/api/current.txt b/core/api/current.txt index 008521a0446a..2817e4b3bd49 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -53525,6 +53525,7 @@ package android.view { method public android.transition.Transition getExitTransition(); method protected final int getFeatures(); method protected final int getForcedWindowFlags(); + method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean getFrameRateBoostOnTouchEnabled(); method @Nullable public android.view.WindowInsetsController getInsetsController(); method @NonNull public abstract android.view.LayoutInflater getLayoutInflater(); method protected final int getLocalFeatures(); @@ -53602,6 +53603,7 @@ package android.view { method public abstract void setFeatureInt(int, int); method public void setFlags(int, int); method public void setFormat(int); + method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean); method public void setGravity(int); method @RequiresPermission(android.Manifest.permission.HIDE_OVERLAY_WINDOWS) public final void setHideOverlayWindows(boolean); method public void setIcon(@DrawableRes int); @@ -53946,6 +53948,7 @@ package android.view { method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public float getDesiredHdrHeadroom(); method public int getFitInsetsSides(); method public int getFitInsetsTypes(); + method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public boolean getFrameRateBoostOnTouchEnabled(); method public final CharSequence getTitle(); method public boolean isFitInsetsIgnoringVisibility(); method public boolean isHdrConversionEnabled(); @@ -53957,6 +53960,7 @@ package android.view { method public void setFitInsetsIgnoringVisibility(boolean); method public void setFitInsetsSides(int); method public void setFitInsetsTypes(int); + method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setFrameRateBoostOnTouchEnabled(boolean); method public void setHdrConversionEnabled(boolean); method public final void setTitle(CharSequence); method public void setWallpaperTouchEventsEnabled(boolean); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9f6395e1aab4..185328277dfd 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -252,7 +252,6 @@ import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.function.Predicate; - /** * The top of a view hierarchy, implementing the needed protocol between View * and the WindowManager. This is for the most part an internal implementation @@ -12148,7 +12147,8 @@ public final class ViewRootImpl implements ViewParent, || motionEventAction == MotionEvent.ACTION_UP; boolean undesiredType = windowType == TYPE_INPUT_METHOD; // use toolkitSetFrameRate flag to gate the change - return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue; + return desiredAction && !undesiredType && sToolkitSetFrameRateReadOnlyFlagValue + && getFrameRateBoostOnTouchEnabled(); } /** @@ -12223,6 +12223,15 @@ public final class ViewRootImpl implements ViewParent, return mIsFrameRateBoosting; } + /** + * Get the value of mFrameRateBoostOnTouchEnabled + * Can be used to checked if touch boost is enabled. The default value is true. + */ + @VisibleForTesting + public boolean getFrameRateBoostOnTouchEnabled() { + return mWindowAttributes.getFrameRateBoostOnTouchEnabled(); + } + private void boostFrameRate(int boostTimeOut) { mIsFrameRateBoosting = true; setPreferredFrameRateCategory(mPreferredFrameRateCategory); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 87537fbc9961..7bae7ec6559c 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -334,6 +334,9 @@ public abstract class Window { private boolean mOverlayWithDecorCaptionEnabled = true; private boolean mCloseOnSwipeEnabled = false; + private static boolean sToolkitSetFrameRateReadOnlyFlagValue = + android.view.flags.Flags.toolkitSetFrameRateReadOnly(); + // The current window attributes. @UnsupportedAppUsage private final WindowManager.LayoutParams mWindowAttributes = @@ -1373,6 +1376,39 @@ public abstract class Window { } /** + * Sets whether the frame rate touch boost is enabled for this Window. + * When enabled, the frame rate will be boosted when a user touches the Window. + * + * @param enabled whether the frame rate touch boost is enabled. + * @see #getFrameRateBoostOnTouchEnabled() + * @see WindowManager.LayoutParams#setFrameRateBoostOnTouchEnabled(boolean) + */ + @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public void setFrameRateBoostOnTouchEnabled(boolean enabled) { + if (sToolkitSetFrameRateReadOnlyFlagValue) { + final WindowManager.LayoutParams attrs = getAttributes(); + attrs.setFrameRateBoostOnTouchEnabled(enabled); + dispatchWindowAttributesChanged(attrs); + } + } + + /** + * Get whether frame rate touch boost is enabled + * {@link #setFrameRateBoostOnTouchEnabled(boolean)} + * + * @return whether the frame rate touch boost is enabled. + * @see #setFrameRateBoostOnTouchEnabled(boolean) + * @see WindowManager.LayoutParams#getFrameRateBoostOnTouchEnabled() + */ + @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public boolean getFrameRateBoostOnTouchEnabled() { + if (sToolkitSetFrameRateReadOnlyFlagValue) { + return getAttributes().getFrameRateBoostOnTouchEnabled(); + } + return true; + } + + /** * If {@code isPreferred} is true, this method requests that the connected display does minimal * post processing when this window is visible on the screen. Otherwise, it requests that the * display switches back to standard image processing. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 07a347ace313..f76822f14189 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -4332,6 +4332,13 @@ public interface WindowManager extends ViewManager { private float mDesiredHdrHeadroom = 0; /** + * For variable refresh rate project. + */ + private boolean mFrameRateBoostOnTouch = true; + private static boolean sToolkitSetFrameRateReadOnlyFlagValue = + android.view.flags.Flags.toolkitSetFrameRateReadOnly(); + + /** * Carries the requests about {@link WindowInsetsController.Appearance} and * {@link WindowInsetsController.Behavior} to the system windows which can produce insets. * @@ -4766,6 +4773,32 @@ public interface WindowManager extends ViewManager { } /** + * Set the value whether we should enable Touch Boost + * + * @param enabled Whether we should enable Touch Boost + */ + @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public void setFrameRateBoostOnTouchEnabled(boolean enabled) { + if (sToolkitSetFrameRateReadOnlyFlagValue) { + mFrameRateBoostOnTouch = enabled; + } + } + + /** + * Get the value whether we should enable touch boost as set + * by {@link #setFrameRateBoostOnTouchEnabled(boolean)} + * + * @return A boolean value to indicate whether we should enable touch boost + */ + @FlaggedApi(android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public boolean getFrameRateBoostOnTouchEnabled() { + if (sToolkitSetFrameRateReadOnlyFlagValue) { + return mFrameRateBoostOnTouch; + } + return true; + } + + /** * <p> * Blurs the screen behind the window. The effect is similar to that of {@link #dimAmount}, * but instead of dimmed, the content behind the window will be blurred (or combined with @@ -4916,6 +4949,9 @@ public interface WindowManager extends ViewManager { out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */); out.writeInt(mDisplayFlags); out.writeFloat(mDesiredHdrHeadroom); + if (sToolkitSetFrameRateReadOnlyFlagValue) { + out.writeBoolean(mFrameRateBoostOnTouch); + } } public static final @android.annotation.NonNull Parcelable.Creator<LayoutParams> CREATOR @@ -4988,6 +5024,9 @@ public interface WindowManager extends ViewManager { paramsForRotation = in.createTypedArray(LayoutParams.CREATOR); mDisplayFlags = in.readInt(); mDesiredHdrHeadroom = in.readFloat(); + if (sToolkitSetFrameRateReadOnlyFlagValue) { + mFrameRateBoostOnTouch = in.readBoolean(); + } } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -5324,6 +5363,12 @@ public interface WindowManager extends ViewManager { changes |= LAYOUT_CHANGED; } + if (sToolkitSetFrameRateReadOnlyFlagValue + && mFrameRateBoostOnTouch != o.mFrameRateBoostOnTouch) { + mFrameRateBoostOnTouch = o.mFrameRateBoostOnTouch; + changes |= LAYOUT_CHANGED; + } + return changes; } @@ -5546,6 +5591,11 @@ public interface WindowManager extends ViewManager { sb.append(prefix).append(" forciblyShownTypes=").append( WindowInsets.Type.toString(forciblyShownTypes)); } + if (sToolkitSetFrameRateReadOnlyFlagValue && mFrameRateBoostOnTouch) { + sb.append(System.lineSeparator()); + sb.append(prefix).append(" frameRateBoostOnTouch="); + sb.append(mFrameRateBoostOnTouch); + } if (paramsForRotation != null && paramsForRotation.length != 0) { sb.append(System.lineSeparator()); sb.append(prefix).append(" paramsForRotation:"); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index b30a0c8fbbd3..cf3eb12498ca 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -659,8 +659,6 @@ public class ViewRootImplTest { ViewRootImpl viewRootImpl = view.getViewRootImpl(); sInstrumentation.runOnMainSync(() -> { view.invalidate(); - assertEquals(viewRootImpl.getLastPreferredFrameRateCategory(), - FRAME_RATE_CATEGORY_NORMAL); viewRootImpl.notifyInsetsAnimationRunningStateChanged(true); view.invalidate(); }); @@ -672,6 +670,37 @@ public class ViewRootImplTest { }); } + + /** + * Test FrameRateBoostOnTouchEnabled API + */ + @Test + @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY) + public void votePreferredFrameRate_frameRateBoostOnTouch() { + View view = new View(sContext); + attachViewToWindow(view); + sInstrumentation.waitForIdleSync(); + + ViewRootImpl viewRootImpl = view.getViewRootImpl(); + final WindowManager.LayoutParams attrs = viewRootImpl.mWindowAttributes; + assertEquals(attrs.getFrameRateBoostOnTouchEnabled(), true); + assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(), + attrs.getFrameRateBoostOnTouchEnabled()); + + sInstrumentation.runOnMainSync(() -> { + attrs.setFrameRateBoostOnTouchEnabled(false); + viewRootImpl.setLayoutParams(attrs, false); + }); + sInstrumentation.waitForIdleSync(); + + sInstrumentation.runOnMainSync(() -> { + final WindowManager.LayoutParams newAttrs = viewRootImpl.mWindowAttributes; + assertEquals(newAttrs.getFrameRateBoostOnTouchEnabled(), false); + assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(), + newAttrs.getFrameRateBoostOnTouchEnabled()); + }); + } + @Test public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() { mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index f794fef48f27..893a87fe4885 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -48,12 +48,14 @@ public class BubbleBarAnimationHelper { private static final float EXPANDED_VIEW_ANIMATE_OUT_SCALE_AMOUNT = .75f; private static final int EXPANDED_VIEW_ALPHA_ANIMATION_DURATION = 150; private static final int EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION = 100; + private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300; + private static final int EXPANDED_VIEW_DISMISS_DURATION = 250; + private static final int EXPANDED_VIEW_DRAG_ANIMATION_DURATION = 150; /** * Additional scale applied to expanded view when it is positioned inside a magnetic target. */ - private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.75f; - private static final int EXPANDED_VIEW_ANIMATE_POSITION_DURATION = 300; - private static final int EXPANDED_VIEW_DISMISS_DURATION = 250; + private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.6f; + private static final float EXPANDED_VIEW_DRAG_SCALE = 0.5f; /** Spring config for the expanded view scale-in animation. */ private final PhysicsAnimator.SpringConfig mScaleInSpringConfig = @@ -72,6 +74,7 @@ public class BubbleBarAnimationHelper { private final Context mContext; private final BubbleBarLayerView mLayerView; private final BubblePositioner mPositioner; + private final int[] mTmpLocation = new int[2]; private BubbleViewProvider mExpandedBubble; private boolean mIsExpanded = false; @@ -220,6 +223,25 @@ public class BubbleBarAnimationHelper { } /** + * Animate the expanded bubble when it is being dragged + */ + public void animateStartDrag() { + final BubbleBarExpandedView bbev = getExpandedView(); + if (bbev == null) { + Log.w(TAG, "Trying to animate start drag without a bubble"); + return; + } + bbev.setPivotX(bbev.getWidth() / 2f); + bbev.setPivotY(0f); + bbev.animate() + .scaleX(EXPANDED_VIEW_DRAG_SCALE) + .scaleY(EXPANDED_VIEW_DRAG_SCALE) + .setInterpolator(Interpolators.EMPHASIZED) + .setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION) + .start(); + } + + /** * Animates dismissal of currently expanded bubble * * @param endRunnable a runnable to run at the end of the animation @@ -261,7 +283,10 @@ public class BubbleBarAnimationHelper { .setDuration(EXPANDED_VIEW_ANIMATE_POSITION_DURATION) .setInterpolator(Interpolators.EMPHASIZED_DECELERATE) .withStartAction(() -> bbev.setAnimating(true)) - .withEndAction(() -> bbev.setAnimating(false)) + .withEndAction(() -> { + bbev.setAnimating(false); + bbev.resetPivot(); + }) .start(); } @@ -277,25 +302,52 @@ public class BubbleBarAnimationHelper { Log.w(TAG, "Trying to snap the expanded view to target without a bubble"); return; } - Point expandedViewCenter = getViewCenterOnScreen(bbev); - - // Calculate the difference between the target's center coordinates and the object's. - // Animating the object's x/y properties by these values will center the object on top - // of the magnetic target. - float xDiff = target.getCenterOnScreen().x - expandedViewCenter.x; - float yDiff = target.getCenterOnScreen().y - expandedViewCenter.y; // Calculate scale of expanded view so it fits inside the magnetic target float bbevMaxSide = Math.max(bbev.getWidth(), bbev.getHeight()); - float targetMaxSide = Math.max(target.getTargetView().getWidth(), - target.getTargetView().getHeight()); - float scale = (targetMaxSide * EXPANDED_VIEW_IN_TARGET_SCALE) / bbevMaxSide; + View targetView = target.getTargetView(); + float targetMaxSide = Math.max(targetView.getWidth(), targetView.getHeight()); + // Reduce target size to have some padding between the target and expanded view + targetMaxSide *= EXPANDED_VIEW_IN_TARGET_SCALE; + float scaleInTarget = targetMaxSide / bbevMaxSide; + + // Scale around the top center of the expanded view. Same as when dragging. + bbev.setPivotX(bbev.getWidth() / 2f); + bbev.setPivotY(0); + + // When the view animates into the target, it is scaled down with the pivot at center top. + // Find the point on the view that would be the center of the view at its final scale. + // Once we know that, we can calculate x and y distance from the center of the target view + // and use that for the translation animation to ensure that the view at final scale is + // placed at the center of the target. + + // Set mTmpLocation to the current location of the view on the screen, taking into account + // any scale applied. + bbev.getLocationOnScreen(mTmpLocation); + // Since pivotX is at the center of the x-axis, even at final scale, center of the view on + // x-axis will be the same as the center of the view at current size. + // Get scaled width of the view and adjust mTmpLocation so that point on x-axis is at the + // center of the view at its current size. + float currentWidth = bbev.getWidth() * bbev.getScaleX(); + mTmpLocation[0] += currentWidth / 2; + // Since pivotY is at the top of the view, at final scale, top coordinate of the view + // remains the same. + // Get height of the view at final scale and adjust mTmpLocation so that point on y-axis is + // moved down by half of the height at final scale. + float targetHeight = bbev.getHeight() * scaleInTarget; + mTmpLocation[1] += targetHeight / 2; + // mTmpLocation is now set to the point on the view that will be the center of the view once + // scale is applied. + + // Calculate the difference between the target's center coordinates and mTmpLocation + float xDiff = target.getCenterOnScreen().x - mTmpLocation[0]; + float yDiff = target.getCenterOnScreen().y - mTmpLocation[1]; bbev.animate() .translationX(bbev.getTranslationX() + xDiff) .translationY(bbev.getTranslationY() + yDiff) - .scaleX(scale) - .scaleY(scale) + .scaleX(scaleInTarget) + .scaleY(scaleInTarget) .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION) .setInterpolator(Interpolators.EMPHASIZED) .withStartAction(() -> bbev.setAnimating(true)) @@ -319,8 +371,8 @@ public class BubbleBarAnimationHelper { } expandedView .animate() - .scaleX(1f) - .scaleY(1f) + .scaleX(EXPANDED_VIEW_DRAG_SCALE) + .scaleY(EXPANDED_VIEW_DRAG_SCALE) .setDuration(EXPANDED_VIEW_SNAP_TO_DISMISS_DURATION) .setInterpolator(Interpolators.EMPHASIZED) .withStartAction(() -> expandedView.setAnimating(true)) @@ -385,12 +437,4 @@ public class BubbleBarAnimationHelper { final int height = mPositioner.getExpandedViewHeightForBubbleBar(isOverflowExpanded); return new Size(width, height); } - - private Point getViewCenterOnScreen(View view) { - Point center = new Point(); - int[] onScreenLocation = view.getLocationOnScreen(); - center.x = (int) (onScreenLocation[0] + (view.getWidth() / 2f)); - center.y = (int) (onScreenLocation[1] + (view.getHeight() / 2f)); - return center; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt index d21545079cc2..5e634a23955a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt @@ -74,6 +74,9 @@ class BubbleBarExpandedViewDragController( } private inner class HandleDragListener : RelativeTouchListener() { + + private var isMoving = false + override fun onDown(v: View, ev: MotionEvent): Boolean { // While animating, don't allow new touch events return !expandedView.isAnimating @@ -87,6 +90,10 @@ class BubbleBarExpandedViewDragController( dx: Float, dy: Float ) { + if (!isMoving) { + isMoving = true + animationHelper.animateStartDrag() + } expandedView.translationX = expandedViewInitialTranslationX + dx expandedView.translationY = expandedViewInitialTranslationY + dy dismissView.show() @@ -114,6 +121,7 @@ class BubbleBarExpandedViewDragController( animationHelper.animateToRestPosition() dismissView.hide() } + isMoving = false } } diff --git a/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java index b531b0eff854..611e4ed0e1f4 100644 --- a/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java +++ b/services/core/java/com/android/server/policy/DeferredKeyActionExecutor.java @@ -76,6 +76,18 @@ class DeferredKeyActionExecutor { getActionsBufferWithLazyCleanUp(keyCode, downTime).setExecutable(); } + /** + * Clears all the queued action for given key code. + * + * @param keyCode the key code whose queued actions will be cleared. + */ + public void cancelQueuedAction(int keyCode) { + TimedActionsBuffer actionsBuffer = mBuffers.get(keyCode); + if (actionsBuffer != null) { + actionsBuffer.clear(); + } + } + private TimedActionsBuffer getActionsBufferWithLazyCleanUp(int keyCode, long downTime) { TimedActionsBuffer buffer = mBuffers.get(keyCode); if (buffer == null || buffer.getDownTime() != downTime) { @@ -146,6 +158,10 @@ class DeferredKeyActionExecutor { mActions.clear(); } + void clear() { + mActions.clear(); + } + void dump(String prefix, PrintWriter pw) { if (mExecutable) { pw.println(prefix + " " + KeyEvent.keyCodeToString(mKeyCode) + ": executable"); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index e8b54d58c907..3000a1c5c043 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -103,6 +103,7 @@ import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager; +import android.app.ActivityTaskManager.RootTaskInfo; import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.IUiModeManager; @@ -584,6 +585,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { private int mLongPressOnStemPrimaryBehavior; private RecentTaskInfo mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp; + // The focused task at the time when the first STEM_PRIMARY key was released. This can only + // be accessed from the looper thread. + private RootTaskInfo mFocusedTaskInfoOnStemPrimarySingleKeyUp; + private boolean mHandleVolumeKeysInWM; private boolean mPendingKeyguardOccluded; @@ -2135,12 +2140,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { static class Injector { private final Context mContext; private final WindowManagerFuncs mWindowManagerFuncs; - private final Looper mLooper; - Injector(Context context, WindowManagerFuncs funcs, Looper looper) { + Injector(Context context, WindowManagerFuncs funcs) { mContext = context; mWindowManagerFuncs = funcs; - mLooper = looper; } Context getContext() { @@ -2152,7 +2155,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } Looper getLooper() { - return mLooper; + return Looper.myLooper(); } AccessibilityShortcutController getAccessibilityShortcutController( @@ -2195,7 +2198,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override public void init(Context context, WindowManagerFuncs funcs) { - init(new Injector(context, funcs, Looper.myLooper())); + init(new Injector(context, funcs)); } @VisibleForTesting @@ -2723,7 +2726,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override void onPress(long downTime, int unusedDisplayId) { - if (mShouldEarlyShortPressOnStemPrimary) { + if (shouldHandleStemPrimaryEarlyShortPress()) { return; } // Short-press should be triggered only if app doesn't handle it. @@ -2747,6 +2750,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (count == 3 && mTriplePressOnStemPrimaryBehavior == TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY) { + // Cancel any queued actions for current key code to prevent them from being + // launched after a11y layer enabled. If the action happens early, + // undoEarlySinglePress will make sure the correct task is on top. + mDeferredKeyActionExecutor.cancelQueuedAction(KeyEvent.KEYCODE_STEM_PRIMARY); + undoEarlySinglePress(); stemPrimaryPress(count); } else { // Other multi-press gestures should be triggered only if app doesn't handle it. @@ -2755,6 +2763,27 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + /** + * This method undo the previously launched early-single-press action by bringing the + * focused task before launching early-single-press back to top. + */ + private void undoEarlySinglePress() { + if (shouldHandleStemPrimaryEarlyShortPress() + && mFocusedTaskInfoOnStemPrimarySingleKeyUp != null) { + try { + mActivityManagerService.startActivityFromRecents( + mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId, null); + } catch (RemoteException | IllegalArgumentException e) { + Slog.e( + TAG, + "Failed to start task " + + mFocusedTaskInfoOnStemPrimarySingleKeyUp.taskId + + " from recents", + e); + } + } + } + @Override void onKeyUp(long eventTime, int count, int unusedDisplayId) { if (count == 1) { @@ -2763,15 +2792,49 @@ public class PhoneWindowManager implements WindowManagerPolicy { // It is possible that we may navigate away from this task before the double // press is detected, as a result of the first press, so we save the current // most recent task before that happens. + // TODO(b/311497918): guard this with DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP mBackgroundRecentTaskInfoOnStemPrimarySingleKeyUp = mActivityTaskManagerInternal.getMostRecentTaskFromBackground(); - if (mShouldEarlyShortPressOnStemPrimary) { + + mFocusedTaskInfoOnStemPrimarySingleKeyUp = null; + + if (shouldHandleStemPrimaryEarlyShortPress()) { // Key-up gesture should be triggered only if app doesn't handle it. mDeferredKeyActionExecutor.queueKeyAction( - KeyEvent.KEYCODE_STEM_PRIMARY, eventTime, () -> stemPrimaryPress(1)); + KeyEvent.KEYCODE_STEM_PRIMARY, + eventTime, + () -> { + // Save the info of the focused task on screen. This may be used + // later to bring the current focused task back to top. For + // example, stem primary triple press enables the A11y interface + // on top of the current focused task. When early single press is + // enabled for stem primary, the focused task could change to + // something else upon first key up event. In that case, we will + // bring the task recorded by this variable back to top. Then, start + // A11y interface. + try { + mFocusedTaskInfoOnStemPrimarySingleKeyUp = + mActivityManagerService.getFocusedRootTaskInfo(); + } catch (RemoteException e) { + Slog.e( + TAG, + "StemPrimaryKeyRule: onKeyUp: error while getting " + + "focused task " + + "info.", + e); + } + + stemPrimaryPress(1); + }); } } } + + // TODO(b/311497918): make a shouldHandlePowerEarlyShortPress for power button. + private boolean shouldHandleStemPrimaryEarlyShortPress() { + return mShouldEarlyShortPressOnStemPrimary + && mShortPressOnStemPrimaryBehavior == SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS; + } } private void initSingleKeyGestureRules(Looper looper) { diff --git a/services/core/jni/OWNERS b/services/core/jni/OWNERS index 061fe0fc88d9..cc08488742b2 100644 --- a/services/core/jni/OWNERS +++ b/services/core/jni/OWNERS @@ -31,5 +31,5 @@ per-file com_android_server_vibrator_* = file:/services/core/java/com/android/se per-file com_android_server_am_CachedAppOptimizer.cpp = timmurray@google.com, edgararriaga@google.com, dualli@google.com, carmenjackson@google.com, philipcuadra@google.com per-file com_android_server_companion_virtual_InputController.cpp = file:/services/companion/java/com/android/server/companion/virtual/OWNERS -# Bug component : 158088 = per-file com_android_server_utils_AnrTimer*.java -per-file com_android_server_utils_AnrTimer*.java = file:/PERFORMANCE_OWNERS +# Bug component : 158088 = per-file *AnrTimer* +per-file *AnrTimer* = file:/PERFORMANCE_OWNERS diff --git a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java index d2ef1808652f..ca3787ec4546 100644 --- a/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/DeferredKeyActionExecutorTests.java @@ -95,6 +95,26 @@ public final class DeferredKeyActionExecutorTests { assertFalse(action.executed); } + @Test + public void queueKeyAction_beforeAndAfterCancelQueuedActions_onlyActionsAfterCancelExecuted() { + TestAction action1 = new TestAction(); + TestAction action2 = new TestAction(); + TestAction action3 = new TestAction(); + mKeyActionExecutor.queueKeyAction( + KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action1); + mKeyActionExecutor.queueKeyAction( + KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action2); + mKeyActionExecutor.cancelQueuedAction(KeyEvent.KEYCODE_STEM_PRIMARY); + mKeyActionExecutor.queueKeyAction( + KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1, action3); + + mKeyActionExecutor.setActionsExecutable(KeyEvent.KEYCODE_STEM_PRIMARY, /* downTime= */ 1); + + assertFalse(action1.executed); + assertFalse(action2.executed); + assertTrue(action3.executed); + } + static class TestAction implements Runnable { public boolean executed; diff --git a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java index f7ad2a8f5243..50d37ec7749b 100644 --- a/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/StemKeyGestureTests.java @@ -19,14 +19,18 @@ package com.android.server.policy; import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_DOUBLE_PRESS; import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_LONG_PRESS; import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_SHORT_PRESS; +import static android.provider.Settings.Global.STEM_PRIMARY_BUTTON_TRIPLE_PRESS; import static android.view.KeyEvent.KEYCODE_STEM_PRIMARY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.server.policy.PhoneWindowManager.DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP; import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT; import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS; import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY; +import static com.android.server.policy.PhoneWindowManager.TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY; import android.app.ActivityManager.RecentTaskInfo; +import android.app.ActivityTaskManager.RootTaskInfo; import android.content.ComponentName; import android.os.RemoteException; import android.provider.Settings; @@ -50,6 +54,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { public void stemSingleKey_duringSetup_doNothing() { overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); + mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false); mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); mPhoneWindowManager.overrideIsUserSetupComplete(false); @@ -65,6 +70,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { public void stemSingleKey_AfterSetup_openAllApp() { overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); + mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false); mPhoneWindowManager.overrideStartActivity(); mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); mPhoneWindowManager.overrideIsUserSetupComplete(true); @@ -83,6 +89,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_TARGET_ACTIVITY); setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); + mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false); mPhoneWindowManager.overrideStartActivity(); mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); mPhoneWindowManager.overrideIsUserSetupComplete(true); @@ -104,6 +111,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); mPhoneWindowManager.overrideIsUserSetupComplete(true); mPhoneWindowManager.overrideFocusedWindowButtonOverridePermission(true); + setDispatchedKeyHandler(keyEvent -> true); sendKey(KEYCODE_STEM_PRIMARY); @@ -131,6 +139,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { STEM_PRIMARY_BUTTON_LONG_PRESS, LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT); setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); + mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false); mPhoneWindowManager.setupAssistForLaunch(); mPhoneWindowManager.overrideIsUserSetupComplete(true); @@ -144,6 +153,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { STEM_PRIMARY_BUTTON_LONG_PRESS, LONG_PRESS_PRIMARY_LAUNCH_VOICE_ASSISTANT); setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); + mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false); mPhoneWindowManager.setupAssistForLaunch(); mPhoneWindowManager.overrideSearchManager(null); mPhoneWindowManager.overrideStatusBarManagerInternal(); @@ -156,7 +166,8 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { @Test public void stemDoubleKey_EarlyShortPress_AllAppsThenSwitchToMostRecent() throws RemoteException { - overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); + overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); + overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, DOUBLE_PRESS_PRIMARY_SWITCH_RECENT_APP); setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true); mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); @@ -171,14 +182,47 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { sendKey(KEYCODE_STEM_PRIMARY); mPhoneWindowManager.assertOpenAllAppView(); - mPhoneWindowManager.assertSwitchToRecent(referenceId); + mPhoneWindowManager.assertSwitchToTask(referenceId); } @Test - public void stemDoubleKey_NoEarlyShortPress_SwitchToMostRecent() throws RemoteException { + public void stemTripleKey_EarlyShortPress_AllAppsThenBackToOriginalThenToggleA11y() + throws RemoteException { + overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); + overrideBehavior( + STEM_PRIMARY_BUTTON_TRIPLE_PRESS, TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY); + setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); + mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(true); + mPhoneWindowManager.overrideTalkbackShortcutGestureEnabled(true); + mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); + mPhoneWindowManager.overrideIsUserSetupComplete(true); + RootTaskInfo allAppsTask = new RootTaskInfo(); + int referenceId = 777; + allAppsTask.taskId = referenceId; + doReturn(allAppsTask) + .when(mPhoneWindowManager.mActivityManagerService) + .getFocusedRootTaskInfo(); + + mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ false); + + sendKey(KEYCODE_STEM_PRIMARY); + sendKey(KEYCODE_STEM_PRIMARY); + sendKey(KEYCODE_STEM_PRIMARY); + + mPhoneWindowManager.assertOpenAllAppView(); + mPhoneWindowManager.assertSwitchToTask(referenceId); + mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ true); + } + + @Test + public void stemMultiKey_NoEarlyPress_NoOpenAllApp() throws RemoteException { + overrideBehavior(STEM_PRIMARY_BUTTON_SHORT_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); overrideBehavior(STEM_PRIMARY_BUTTON_DOUBLE_PRESS, SHORT_PRESS_PRIMARY_LAUNCH_ALL_APPS); + overrideBehavior( + STEM_PRIMARY_BUTTON_TRIPLE_PRESS, TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY); setUpPhoneWindowManager(/* supportSettingsUpdate= */ true); mPhoneWindowManager.overrideShouldEarlyShortPressOnStemPrimary(false); + mPhoneWindowManager.overrideTalkbackShortcutGestureEnabled(true); mPhoneWindowManager.setKeyguardServiceDelegateIsShowing(false); mPhoneWindowManager.overrideIsUserSetupComplete(true); RecentTaskInfo recentTaskInfo = new RecentTaskInfo(); @@ -189,9 +233,16 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { sendKey(KEYCODE_STEM_PRIMARY); sendKey(KEYCODE_STEM_PRIMARY); + sendKey(KEYCODE_STEM_PRIMARY); + + mPhoneWindowManager.assertNotOpenAllAppView(); + mPhoneWindowManager.assertTalkBack(/* expectEnabled= */ true); + + sendKey(KEYCODE_STEM_PRIMARY); + sendKey(KEYCODE_STEM_PRIMARY); mPhoneWindowManager.assertNotOpenAllAppView(); - mPhoneWindowManager.assertSwitchToRecent(referenceId); + mPhoneWindowManager.assertSwitchToTask(referenceId); } @Test @@ -215,7 +266,7 @@ public class StemKeyGestureTests extends ShortcutKeyTestBase { sendKey(KEYCODE_STEM_PRIMARY); mPhoneWindowManager.assertNotOpenAllAppView(); - mPhoneWindowManager.assertSwitchToRecent(referenceId); + mPhoneWindowManager.assertSwitchToTask(referenceId); } private void overrideBehavior(String key, int expectedBehavior) { diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 0678210e1d2f..7c2f7eedff9d 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -73,6 +73,7 @@ import android.media.AudioManagerInternal; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.os.Looper; import android.os.PowerManager; import android.os.PowerManagerInternal; import android.os.RemoteException; @@ -176,8 +177,9 @@ class TestPhoneWindowManager { private Handler mHandler; private boolean mIsTalkBackEnabled; + private boolean mIsTalkBackShortcutGestureEnabled; - class TestTalkbackShortcutController extends TalkbackShortcutController { + private class TestTalkbackShortcutController extends TalkbackShortcutController { TestTalkbackShortcutController(Context context) { super(context); } @@ -190,13 +192,18 @@ class TestPhoneWindowManager { @Override boolean isTalkBackShortcutGestureEnabled() { - return true; + return mIsTalkBackShortcutGestureEnabled; } } private class TestInjector extends PhoneWindowManager.Injector { TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) { - super(context, funcs, mTestLooper.getLooper()); + super(context, funcs); + } + + @Override + Looper getLooper() { + return mTestLooper.getLooper(); } AccessibilityShortcutController getAccessibilityShortcutController( @@ -410,6 +417,10 @@ class TestPhoneWindowManager { mPhoneWindowManager.mShouldEarlyShortPressOnStemPrimary = shouldEarlyShortPress; } + void overrideTalkbackShortcutGestureEnabled(boolean enabled) { + mIsTalkBackShortcutGestureEnabled = enabled; + } + // Override assist perform function. void overrideLongPressOnPower(int behavior) { mPhoneWindowManager.mLongPressOnPowerBehavior = behavior; @@ -714,7 +725,7 @@ class TestPhoneWindowManager { } void assertOpenAllAppView() { - mTestLooper.dispatchAll(); + moveTimeForward(TEST_SINGLE_KEY_DELAY_MILLIS); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS)) .startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class)); @@ -728,7 +739,7 @@ class TestPhoneWindowManager { } void assertActivityTargetLaunched(ComponentName targetActivity) { - mTestLooper.dispatchAll(); + moveTimeForward(TEST_SINGLE_KEY_DELAY_MILLIS); ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); verify(mContext, timeout(TEST_SINGLE_KEY_DELAY_MILLIS)) .startActivityAsUser(intentCaptor.capture(), isNull(), any(UserHandle.class)); @@ -743,10 +754,15 @@ class TestPhoneWindowManager { expectedModifierState, deviceBus), description(errorMsg)); } - void assertSwitchToRecent(int persistentId) throws RemoteException { + void assertSwitchToTask(int persistentId) throws RemoteException { mTestLooper.dispatchAll(); verify(mActivityManagerService, timeout(TEST_SINGLE_KEY_DELAY_MILLIS)).startActivityFromRecents(eq(persistentId), isNull()); } + + void assertTalkBack(boolean expectEnabled) { + mTestLooper.dispatchAll(); + Assert.assertEquals(expectEnabled, mIsTalkBackEnabled); + } } diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java index db38f8873a02..575ec27622a5 100644 --- a/telephony/java/android/telephony/AnomalyReporter.java +++ b/telephony/java/android/telephony/AnomalyReporter.java @@ -187,14 +187,15 @@ public final class AnomalyReporter { } for (ResolveInfo r : packages) { - if (r.activityInfo == null - || pm.checkPermission( + if (r.activityInfo == null) { + Rlog.w(TAG, "Found package without activity"); + continue; + } else if (pm.checkPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, r.activityInfo.packageName) - != PackageManager.PERMISSION_GRANTED) { - Rlog.w(TAG, - "Found package without proper permissions or no activity" - + r.activityInfo.packageName); + != PackageManager.PERMISSION_GRANTED) { + Rlog.w(TAG, "Found package without proper permissions" + + r.activityInfo.packageName); continue; } Rlog.d(TAG, "Found a valid package " + r.activityInfo.packageName); |