Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7bb69b6 authored by Shane's avatar Shane
Browse files

Basic framework of the UI toolkit variable refresh rate project

The original CL was reverted due to a performance issue - ag/24826019

In this CL, I use a read-only flag to avoid cauding the performance
issue. See Patchset 2 and 3 for detiails.

Atest UiBenchMicrobenchmark / uibench-dialoglist / uibench-invalidate are passed locally

This CL includes two major changes for variable refresh rate (VRR) project:

1. Migrated the TouchBoost logic from SurfaceFlinger to
ViewRootImpl. When users touch (MotionEvent.ACTION_DOWN) the screen or press and move (MotionEvent.ACTION_MOVE), we increase the frame rate / refresh rate to the maximum value to offer users a smooth user experience. The frame rate will then be lowered after a certain period.

2. Built the frame rate voting mechanism. This enables Views to vote for
their preferred frame rate. Those votes will be aggregated in
ViewRootImpl to determine a final one. The preferred frame rate of a
View is determined based on its size.

The implementation is gated by a Trunk Stable flag.

Bug: 278731105
Bug: 278731091
Bug: 293505054
Bug: 296459487
Bug: 293513140
Bug: 293513467
Test: atest ViewRootImplTest / ViewTest / UiBenchMicrobenchmark
Change-Id: If6a342eb137e0f36834d2b439997e752685f7ee2
parent 80d939a4
Loading
Loading
Loading
Loading
+53 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,8 @@ package android.view;
import static android.content.res.Resources.ID_NULL;
import static android.content.res.Resources.ID_NULL;
import static android.os.Trace.TRACE_TAG_APP;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -27,6 +29,7 @@ import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ER
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.flags.Flags.viewVelocityApi;
import static android.view.flags.Flags.viewVelocityApi;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__DEEP_PRESS;
@@ -114,6 +117,7 @@ import android.sysprop.DisplayProperties;
import android.text.InputType;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.FloatProperty;
import android.util.FloatProperty;
import android.util.LayoutDirection;
import android.util.LayoutDirection;
import android.util.Log;
import android.util.Log;
@@ -2300,6 +2304,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
     */
    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
    private static boolean sToolkitSetFrameRateReadOnlyFlagValue;
    static {
    static {
        EMPTY_STATE_SET = StateSet.get(0);
        EMPTY_STATE_SET = StateSet.get(0);
@@ -2381,6 +2387,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
                StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED
                StateSet.VIEW_STATE_WINDOW_FOCUSED | StateSet.VIEW_STATE_SELECTED
                        | StateSet.VIEW_STATE_FOCUSED| StateSet.VIEW_STATE_ENABLED
                        | StateSet.VIEW_STATE_FOCUSED| StateSet.VIEW_STATE_ENABLED
                        | StateSet.VIEW_STATE_PRESSED);
                        | StateSet.VIEW_STATE_PRESSED);
        sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
    }
    }
    /**
    /**
@@ -5508,6 +5516,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private ViewTranslationResponse mViewTranslationResponse;
    private ViewTranslationResponse mViewTranslationResponse;
    /**
     * A threshold value to determine the frame rate category of the View based on the size.
     */
    private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
    /**
    /**
     * Simple constructor to use when creating a view from code.
     * Simple constructor to use when creating a view from code.
     *
     *
@@ -20183,6 +20196,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
            return;
            return;
        }
        }
        // For VRR to vote the preferred frame rate
        votePreferredFrameRate();
        // Reset content capture caches
        // Reset content capture caches
        mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
        mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
        mContentCaptureSessionCached = false;
        mContentCaptureSessionCached = false;
@@ -20285,6 +20301,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
     */
    protected void damageInParent() {
    protected void damageInParent() {
        if (mParent != null && mAttachInfo != null) {
        if (mParent != null && mAttachInfo != null) {
            // For VRR to vote the preferred frame rate
            votePreferredFrameRate();
            mParent.onDescendantInvalidated(this, this);
            mParent.onDescendantInvalidated(this, this);
        }
        }
    }
    }
@@ -32981,6 +32999,41 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return null;
        return null;
    }
    }
    private float getSizePercentage() {
        if (mResources == null || getAlpha() == 0 || getVisibility() != VISIBLE) {
            return 0;
        }
        DisplayMetrics displayMetrics = mResources.getDisplayMetrics();
        int screenSize = displayMetrics.widthPixels
                * displayMetrics.heightPixels;
        int viewSize = getWidth() * getHeight();
        if (screenSize == 0 || viewSize == 0) {
            return 0f;
        }
        return (float) viewSize / screenSize;
    }
    private int calculateFrameRateCategory() {
        float sizePercentage = getSizePercentage();
        if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
            return FRAME_RATE_CATEGORY_LOW;
        } else {
            return FRAME_RATE_CATEGORY_NORMAL;
        }
    }
    private void votePreferredFrameRate() {
        // use toolkitSetFrameRate flag to gate the change
        ViewRootImpl viewRootImpl = getViewRootImpl();
        if (sToolkitSetFrameRateReadOnlyFlagValue && viewRootImpl != null
                && getSizePercentage() > 0) {
            viewRootImpl.votePreferredFrameRateCategory(calculateFrameRateCategory());
        }
    }
    /**
    /**
     * Set the current velocity of the View, we only track positive value.
     * Set the current velocity of the View, we only track positive value.
     * We will use the velocity information to adjust the frame rate when applicable.
     * We will use the velocity information to adjust the frame rate when applicable.
+182 −0
Original line number Original line Diff line number Diff line
@@ -24,6 +24,8 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsSource.ID_IME;
import static android.view.InsetsSource.ID_IME;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.PFLAG_DRAW_ANIMATION;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
@@ -74,7 +76,10 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_E
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
@@ -87,6 +92,7 @@ import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.accessibility.Flags.forceInvertColor;
import static android.view.accessibility.Flags.forceInvertColor;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;


import android.Manifest;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityService;
@@ -738,6 +744,7 @@ public final class ViewRootImpl implements ViewParent,
    private SurfaceControl mBoundsLayer;
    private SurfaceControl mBoundsLayer;
    private final SurfaceSession mSurfaceSession = new SurfaceSession();
    private final SurfaceSession mSurfaceSession = new SurfaceSession();
    private final Transaction mTransaction = new Transaction();
    private final Transaction mTransaction = new Transaction();
    private final Transaction mFrameRateTransaction = new Transaction();


    @UnsupportedAppUsage
    @UnsupportedAppUsage
    boolean mAdded;
    boolean mAdded;
@@ -961,6 +968,34 @@ public final class ViewRootImpl implements ViewParent,


    private AccessibilityWindowAttributes mAccessibilityWindowAttributes;
    private AccessibilityWindowAttributes mAccessibilityWindowAttributes;


    /*
     * for Variable Refresh Rate project
     */

    // The preferred frame rate category of the view that
    // could be updated on a frame-by-frame basis.
    private int mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
    // The preferred frame rate category of the last frame that
    // could be used to lower frame rate after touch boost
    private int mLastPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
    // The preferred frame rate of the view that is mainly used for
    // touch boosting, view velocity handling, and TextureView.
    private float mPreferredFrameRate = 0;
    // Used to check if there were any view invalidations in
    // the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
    private boolean mHasInvalidation = false;
    // Used to check if it is in the touch boosting period.
    private boolean mIsFrameRateBoosting = false;
    // Used to check if there is a message in the message queue
    // for idleness handling.
    private boolean mHasIdledMessage = false;
    // time for touch boost period.
    private static final int FRAME_RATE_TOUCH_BOOST_TIME = 1500;
    // time for checking idle status periodically.
    private static final int FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS = 500;
    // time for revaluating the idle status before lowering the frame rate.
    private static final int FRAME_RATE_IDLENESS_REEVALUATE_TIME = 500;

    /**
    /**
     * A temporary object used so relayoutWindow can return the latest SyncSeqId
     * A temporary object used so relayoutWindow can return the latest SyncSeqId
     * system. The SyncSeqId system was designed to work without synchronous relayout
     * system. The SyncSeqId system was designed to work without synchronous relayout
@@ -1010,6 +1045,12 @@ public final class ViewRootImpl implements ViewParent,


    private String mTag = TAG;
    private String mTag = TAG;


    private static boolean sToolkitSetFrameRateReadOnlyFlagValue;

    static {
        sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
    }

    public ViewRootImpl(Context context, Display display) {
    public ViewRootImpl(Context context, Display display) {
        this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());
        this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout());
    }
    }
@@ -3947,6 +3988,12 @@ public final class ViewRootImpl implements ViewParent,
                mWmsRequestSyncGroupState = WMS_SYNC_NONE;
                mWmsRequestSyncGroupState = WMS_SYNC_NONE;
            }
            }
        }
        }

        // For the variable refresh rate project.
        setPreferredFrameRate(mPreferredFrameRate);
        setPreferredFrameRateCategory(mPreferredFrameRateCategory);
        mLastPreferredFrameRateCategory = mPreferredFrameRateCategory;
        mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
    }
    }


    private void createSyncIfNeeded() {
    private void createSyncIfNeeded() {
@@ -6022,6 +6069,8 @@ public final class ViewRootImpl implements ViewParent,
    private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
    private static final int MSG_REPORT_KEEP_CLEAR_RECTS = 36;
    private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37;
    private static final int MSG_PAUSED_FOR_SYNC_TIMEOUT = 37;
    private static final int MSG_DECOR_VIEW_GESTURE_INTERCEPTION = 38;
    private static final int MSG_DECOR_VIEW_GESTURE_INTERCEPTION = 38;
    private static final int MSG_TOUCH_BOOST_TIMEOUT = 39;
    private static final int MSG_CHECK_INVALIDATION_IDLE = 40;


    final class ViewRootHandler extends Handler {
    final class ViewRootHandler extends Handler {
        @Override
        @Override
@@ -6317,6 +6366,32 @@ public final class ViewRootImpl implements ViewParent,
                    mNumPausedForSync = 0;
                    mNumPausedForSync = 0;
                    scheduleTraversals();
                    scheduleTraversals();
                    break;
                    break;
                case MSG_TOUCH_BOOST_TIMEOUT:
                    /**
                     * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
                     */
                    mIsFrameRateBoosting = false;
                    setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory,
                            mLastPreferredFrameRateCategory));
                    break;
                case MSG_CHECK_INVALIDATION_IDLE:
                    if (!mHasInvalidation && !mIsFrameRateBoosting) {
                        mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
                        setPreferredFrameRateCategory(mPreferredFrameRateCategory);
                        mHasIdledMessage = false;
                    } else {
                        /**
                         * If there is no invalidation within a certain period,
                         * we consider the display is idled.
                         * We then set the frame rate catetogry to NO_PREFERENCE.
                         * Note that SurfaceFlinger also has a mechanism to lower the refresh rate
                         * if there is no updates of the buffer.
                         */
                        mHasInvalidation = false;
                        mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
                                FRAME_RATE_IDLENESS_REEVALUATE_TIME);
                    }
                    break;
            }
            }
        }
        }
    }
    }
@@ -7259,6 +7334,7 @@ public final class ViewRootImpl implements ViewParent,


        private int processPointerEvent(QueuedInputEvent q) {
        private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            final MotionEvent event = (MotionEvent)q.mEvent;
            final int action = event.getAction();
            boolean handled = mHandwritingInitiator.onTouchEvent(event);
            boolean handled = mHandwritingInitiator.onTouchEvent(event);
            if (handled) {
            if (handled) {
                // If handwriting is started, toolkit doesn't receive ACTION_UP.
                // If handwriting is started, toolkit doesn't receive ACTION_UP.
@@ -7279,6 +7355,22 @@ public final class ViewRootImpl implements ViewParent,
                    scheduleConsumeBatchedInputImmediately();
                    scheduleConsumeBatchedInputImmediately();
                }
                }
            }
            }

            // For the variable refresh rate project
            if (handled && shouldTouchBoost(action, mWindowAttributes.type)) {
                // set the frame rate to the maximum value.
                mIsFrameRateBoosting = true;
                setPreferredFrameRateCategory(mPreferredFrameRateCategory);
            }
            /**
             * We want to lower the refresh rate when MotionEvent.ACTION_UP,
             * MotionEvent.ACTION_CANCEL is detected.
             * Not using ACTION_MOVE to avoid checking and sending messages too frequently.
             */
            if (mIsFrameRateBoosting && (action == MotionEvent.ACTION_UP
                    || action == MotionEvent.ACTION_CANCEL)) {
                sendDelayedEmptyMessage(MSG_TOUCH_BOOST_TIMEOUT, FRAME_RATE_TOUCH_BOOST_TIME);
            }
            return handled ? FINISH_HANDLED : FORWARD;
            return handled ? FINISH_HANDLED : FORWARD;
        }
        }


@@ -11894,6 +11986,96 @@ public final class ViewRootImpl implements ViewParent,
        Log.d(mTag, msg);
        Log.d(mTag, msg);
    }
    }


    private void setPreferredFrameRateCategory(int preferredFrameRateCategory) {
        if (!shouldSetFrameRateCategory()) {
            return;
        }

        int frameRateCategory = mIsFrameRateBoosting
                ? FRAME_RATE_CATEGORY_HIGH : preferredFrameRateCategory;

        try {
            mFrameRateTransaction.setFrameRateCategory(mSurfaceControl,
                    frameRateCategory, false).apply();
        } catch (Exception e) {
            Log.e(mTag, "Unable to set frame rate category", e);
        }

        if (mPreferredFrameRateCategory != FRAME_RATE_CATEGORY_NO_PREFERENCE && !mHasIdledMessage) {
            // Check where the display is idled periodically.
            // If so, set the frame rate category to NO_PREFERENCE
            mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
                    FRAME_RATE_IDLENESS_CHECK_TIME_MILLIS);
            mHasIdledMessage = true;
        }
    }

    private void setPreferredFrameRate(float preferredFrameRate) {
        if (!shouldSetFrameRate()) {
            return;
        }

        try {
            mFrameRateTransaction.setFrameRate(mSurfaceControl,
                    preferredFrameRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT).apply();
        } catch (Exception e) {
            Log.e(mTag, "Unable to set frame rate", e);
        }
    }

    private void sendDelayedEmptyMessage(int message, int delayedTime) {
        mHandler.removeMessages(message);

        mHandler.sendEmptyMessageDelayed(message, delayedTime);
    }

    private boolean shouldSetFrameRateCategory() {
        // use toolkitSetFrameRate flag to gate the change
        return  mSurface.isValid() && sToolkitSetFrameRateReadOnlyFlagValue;
    }

    private boolean shouldSetFrameRate() {
        // use toolkitSetFrameRate flag to gate the change
        return mPreferredFrameRate > 0 && sToolkitSetFrameRateReadOnlyFlagValue;
    }

    private boolean shouldTouchBoost(int motionEventAction, int windowType) {
        boolean desiredAction = motionEventAction == MotionEvent.ACTION_DOWN
                || motionEventAction == MotionEvent.ACTION_MOVE
                || motionEventAction == MotionEvent.ACTION_UP;
        boolean desiredType = windowType == TYPE_BASE_APPLICATION || windowType == TYPE_APPLICATION
                || windowType == TYPE_APPLICATION_STARTING || windowType == TYPE_DRAWN_APPLICATION;
        // use toolkitSetFrameRate flag to gate the change
        return desiredAction && desiredType && sToolkitSetFrameRateReadOnlyFlagValue;
    }

    /**
     * Allow Views to vote for the preferred frame rate category
     *
     * @param frameRateCategory the preferred frame rate category of a View
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
    public void votePreferredFrameRateCategory(int frameRateCategory) {
        mPreferredFrameRateCategory = Math.max(mPreferredFrameRateCategory, frameRateCategory);
        mHasInvalidation = true;
    }

    /**
     * Get the value of mPreferredFrameRateCategory
     */
    @VisibleForTesting
    public int getPreferredFrameRateCategory() {
        return mPreferredFrameRateCategory;
    }

    /**
     * Get the value of mPreferredFrameRate
     */
    @VisibleForTesting
    public float getPreferredFrameRate() {
        return mPreferredFrameRate;
    }

    @Override
    @Override
    public boolean transferHostTouchGestureToEmbedded(
    public boolean transferHostTouchGestureToEmbedded(
            @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
            @NonNull SurfaceControlViewHost.SurfacePackage surfacePackage) {
+8 −0
Original line number Original line Diff line number Diff line
@@ -14,6 +14,14 @@ flag {
    bug: "293512962"
    bug: "293512962"
}
}


flag {
    name: "toolkit_set_frame_rate_read_only"
    namespace: "toolkit"
    description: "Feature flag for toolkit to set frame rate"
    bug: "293512962"
    is_fixed_read_only: true
}

flag {
flag {
    name: "expected_presentation_time_api"
    name: "expected_presentation_time_api"
    namespace: "toolkit"
    namespace: "toolkit"
+2 −0
Original line number Original line Diff line number Diff line
@@ -65,6 +65,7 @@ android_test {
        "device-time-shell-utils",
        "device-time-shell-utils",
        "testables",
        "testables",
        "com.android.text.flags-aconfig-java",
        "com.android.text.flags-aconfig-java",
        "flag-junit",
    ],
    ],


    libs: [
    libs: [
@@ -75,6 +76,7 @@ android_test {
        "framework",
        "framework",
        "ext",
        "ext",
        "framework-res",
        "framework-res",
        "android.view.flags-aconfig-java",
    ],
    ],
    jni_libs: [
    jni_libs: [
        "libpowermanagertest_jni",
        "libpowermanagertest_jni",
+129 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,11 @@
package android.view;
package android.view;


import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
import static android.view.accessibility.Flags.FLAG_FORCE_INVERT_COLOR;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
@@ -53,6 +58,7 @@ import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Log;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type;
@@ -447,6 +453,129 @@ public class ViewRootImplTest {
        assertThat(result).isFalse();
        assertThat(result).isFalse();
    }
    }


    /**
     * Test the default values are properly set
     */
    @UiThreadTest
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_getDefaultValues() {
        ViewRootImpl viewRootImpl = new ViewRootImpl(sContext,
                sContext.getDisplayNoVerify());
        assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
                FRAME_RATE_CATEGORY_NO_PREFERENCE);
        assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
    }

    /**
     * Test the value of the frame rate cateogry based on the visibility of a view
     * Invsible: FRAME_RATE_CATEGORY_NO_PREFERENCE
     * Visible: FRAME_RATE_CATEGORY_NORMAL
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_visibility() {
        View view = new View(sContext);
        attachViewToWindow(view);
        ViewRootImpl viewRootImpl = view.getViewRootImpl();
        sInstrumentation.runOnMainSync(() -> {
            view.setVisibility(View.INVISIBLE);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
                    FRAME_RATE_CATEGORY_NO_PREFERENCE);
        });

        sInstrumentation.runOnMainSync(() -> {
            view.setVisibility(View.VISIBLE);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
                    FRAME_RATE_CATEGORY_NORMAL);
        });
    }

    /**
     * Test the value of the frame rate cateogry based on the size of a view.
     * The current threshold value is 7% of the screen size
     * <7%: FRAME_RATE_CATEGORY_LOW
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_smallSize() {
        View view = new View(sContext);
        WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
        wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
        wmlp.width = 1;
        wmlp.height = 1;

        sInstrumentation.runOnMainSync(() -> {
            WindowManager wm = sContext.getSystemService(WindowManager.class);
            wm.addView(view, wmlp);
        });
        sInstrumentation.waitForIdleSync();

        ViewRootImpl viewRootImpl = view.getViewRootImpl();
        sInstrumentation.runOnMainSync(() -> {
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
        });
    }

    /**
     * Test the value of the frame rate cateogry based on the size of a view.
     * The current threshold value is 7% of the screen size
     * >=7% : FRAME_RATE_CATEGORY_NORMAL
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_normalSize() {
        View view = new View(sContext);
        WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
        wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check

        sInstrumentation.runOnMainSync(() -> {
            WindowManager wm = sContext.getSystemService(WindowManager.class);
            Display display = wm.getDefaultDisplay();
            DisplayMetrics metrics = new DisplayMetrics();
            display.getMetrics(metrics);
            wmlp.width = (int) (metrics.widthPixels * 0.9);
            wmlp.height = (int) (metrics.heightPixels * 0.9);
            wm.addView(view, wmlp);
        });
        sInstrumentation.waitForIdleSync();

        ViewRootImpl viewRootImpl = view.getViewRootImpl();
        sInstrumentation.runOnMainSync(() -> {
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
        });
    }

    /**
     * Test how values of the frame rate cateogry are aggregated.
     * It should take the max value among all of the voted categories per frame.
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRateCategory_aggregate() {
        View view = new View(sContext);
        attachViewToWindow(view);
        sInstrumentation.runOnMainSync(() -> {
            ViewRootImpl viewRootImpl = view.getViewRootImpl();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
                    FRAME_RATE_CATEGORY_NO_PREFERENCE);
            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_HIGH);
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_NORMAL);
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
            viewRootImpl.votePreferredFrameRateCategory(FRAME_RATE_CATEGORY_LOW);
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
        });
    }

    @Test
    @Test
    public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
    public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);