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

Commit 7cdd2a78 authored by Justin Ghan's avatar Justin Ghan Committed by Android (Google) Code Review
Browse files

Merge "[ARR] Use sysprop for velocity to frame rate mapping." into main

parents 8b8367e3 3f7d8c67
Loading
Loading
Loading
Loading
+140 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import static android.view.flags.Flags.sensitiveContentAppProtection;
import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly;
import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.flags.Flags.toolkitVelocitySysprop;
import static android.view.flags.Flags.toolkitViewgroupSetRequestedFrameRateApi;
import static android.view.flags.Flags.viewVelocityApi;
import static android.view.inputmethod.Flags.FLAG_HOME_SCREEN_HANDWRITING_DELEGATOR;
@@ -151,6 +152,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.service.credentials.CredentialProviderService;
import android.sysprop.DisplayProperties;
import android.sysprop.ViewProperties;
import android.text.InputType;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -216,6 +218,7 @@ import android.widget.ScrollBarDrawable;
import android.window.OnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.Preconditions;
@@ -241,6 +244,7 @@ import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -2468,6 +2472,113 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    private static boolean sToolkitViewGroupFrameRateApiFlagValue =
            toolkitViewgroupSetRequestedFrameRateApi();
    // The read-write flag toolkitVelocitySysprop() cannot be initialized at Zygote. To prevent
    // this, initialize inside this class with special name NoPreloadHolder which prevents
    // initialization at Zygote.
    /** @hide */
    @VisibleForTesting
    static final class NoPreloadHolder {
        private static boolean sToolkitVelocitySyspropFlagValue = toolkitVelocitySysprop();
        private static String sFrameRateSysProp =
                ViewProperties.vrr_velocity_threshold().orElse("");
        static {
            if (sToolkitVelocitySyspropFlagValue && !sFrameRateSysProp.isEmpty()) {
                sFrameRateMappings = parseFrameRateMapping(sFrameRateSysProp);
            }
        }
        /**
         * For parsing the frame rate mapping string.
         *
         * @hide
         */
        @VisibleForTesting
        static int[][] parseFrameRateMapping(String mappings) {
            if (mappings.isEmpty()) {
                return null;
            }
            int columnCount = 0;
            int atCount = 0;
            int pairCount = 0;
            int startIndex = 0;
            int endIndex = mappings.length() - 1;
            // Find the first non column character
            while (startIndex <= endIndex && mappings.charAt(startIndex) == ':') {
                startIndex++;
            }
            // Find the last non column character
            while (startIndex <= endIndex && mappings.charAt(endIndex) == ':') {
                endIndex--;
            }
            if (startIndex >= endIndex) {
                return null;
            }
            // First pass: Count the number of mappings
            for (int i = startIndex; i <= endIndex; i++) {
                if (mappings.charAt(i) == ':') {
                    if (((i > 0) && mappings.charAt(i - 1) == ':')) {
                        continue;
                    }
                    pairCount++;
                }
            }
            pairCount++; // Add 1 for the last mapping
            int[][] mappingArray = new int[pairCount][2];
            int currentIndex = startIndex;
            try {
                for (int i = startIndex; i <= endIndex; i++) {
                    if (mappings.charAt(i) == ':') {
                        // handle consecutive columns
                        if (((i > 0) && mappings.charAt(i - 1) == ':')) {
                            currentIndex++;
                            continue;
                        }
                        // assign velocity threshold value
                        mappingArray[columnCount][0] =
                                Integer.parseInt(mappings.substring(currentIndex, i).trim());
                        columnCount++;
                        if (columnCount != atCount) {
                            throw new IllegalArgumentException();
                        }
                        currentIndex = i + 1;
                    } else if (mappings.charAt(i) == '@') {
                        // handle consecutive @
                        if ((i > 0) && mappings.charAt(i - 1) == '@') {
                            currentIndex++;
                            continue;
                        }
                        // assign frame rate value
                        mappingArray[columnCount][1] =
                                Integer.parseInt(mappings.substring(currentIndex, i).trim());
                        atCount++;
                        if (atCount != columnCount + 1) {
                            throw new IllegalArgumentException();
                        }
                        currentIndex = i + 1;
                    }
                }
                if (atCount != columnCount + 1 || atCount != pairCount
                        || currentIndex == mappings.length()) {
                    throw new IllegalArgumentException();
                }
                // the last velocity threshold value
                mappingArray[columnCount][0] =
                        Integer.parseInt(mappings.substring(currentIndex, endIndex + 1).trim());
            } catch (IllegalArgumentException e) {
                Log.e(VIEW_LOG_TAG, "Format should be frameRate1@threshold1:frameRate2@threshold2");
            }
            Arrays.sort(mappingArray, Comparator.comparingInt(pair -> -pair[0]));
            return mappingArray;
        }
    }
    // Used to set frame rate compatibility.
    @Surface.FrameRateCompatibility int mFrameRateCompatibility =
            FRAME_RATE_COMPATIBILITY_FIXED_SOURCE;
@@ -5808,6 +5919,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
    private static int[][] sFrameRateMappings;
    static final float MAX_FRAME_RATE = 120;
    // The preferred frame rate of the view that is mainly used for
@@ -34411,6 +34524,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
    }
    private float convertVelocityToFrameRate(float velocityPps) {
        if (NoPreloadHolder.sToolkitVelocitySyspropFlagValue && sFrameRateMappings != null
                && sFrameRateMappings.length > 0) {
            return getFrameRateByVelocity(sFrameRateMappings, (int) velocityPps);
        }
        // Internal testing has shown that this gives a premium experience:
        // above 300dp/s => 120fps
        // between 300dp/s and 125fps => 80fps
@@ -34569,4 +34686,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
        return rootView.getJankTracker();
    }
    private int getFrameRateByVelocity(int[][] mappings, int velocity) {
        if (mappings == null || mappings.length == 0) {
            return 0;
        }
        int frameRate = 0; // Default return if no matching pair is found
        // pair[0] is the threshold value and pair[1] is the frame rate
        for (int index = 0; index < mappings.length; index++) {
            if (velocity >= mappings[index][0]) {
                frameRate = mappings[index][1];
                break; // Found the first matching pair, no need to continue
            }
        }
        // If no match found, the last value is returned
        if (frameRate == 0 && mappings.length > 0) {
            frameRate = mappings[mappings.length - 1][1];
        }
        return frameRate;
    }
}
+11 −1
Original line number Diff line number Diff line
@@ -95,3 +95,13 @@ flag {
    description: "Feature flag to ennable ARR debug message"
    bug: "394614443"
}

flag {
    name: "toolkit_velocity_sysprop"
    namespace: "toolkit"
    description: "Feature flag to map velocity to frame rate using sysprop"
    bug: "404936438"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+13 −0
Original line number Diff line number Diff line
@@ -27,3 +27,16 @@ prop {
    scope: Internal
    access: Readonly
}

# Provide OEMs with the flexibility to override
# the velocity mapping threshold as needed.
# Please format the values in the following manner:
# frameRate1@threshold1:frameRate2@threshold2
# For example, 120@1000:80@700:60@500
prop {
    api_name: "vrr_velocity_threshold"
    type: String
    prop_name: "ro.view.vrr.velocity_threshold"
    scope: Internal
    access: Readonly
}
+41 −0
Original line number Diff line number Diff line
@@ -1237,6 +1237,47 @@ public class ViewFrameRateTest {
        waitForAfterDraw();
    }

    @Test
    public void parseFrameRateMappings() throws Throwable {
        if (!ViewProperties.vrr_enabled().orElse(true)) {
            return;
        }

        int[] twoPairs = {800, 80, 300, 30};
        int[] threePairs = {1000, 120, 800, 80, 600, 60};

        int[][] mappings = View.NoPreloadHolder.parseFrameRateMapping("");
        assertTrue(mappings == null);

        mappings = View.NoPreloadHolder.parseFrameRateMapping("::");
        assertTrue(mappings == null);

        mappings = View.NoPreloadHolder.parseFrameRateMapping("80@800:30@300");
        for (int i = 0; i < twoPairs.length; i++) {
            assertEquals(twoPairs[i], mappings[i / 2][i % 2]);
        }

        mappings = View.NoPreloadHolder.parseFrameRateMapping("80@800:60@600:120@1000");
        for (int i = 0; i < threePairs.length; i++) {
            assertEquals(threePairs[i], mappings[i / 2][i % 2]);
        }

        mappings = View.NoPreloadHolder.parseFrameRateMapping("80@@800:60@@@600:120@1000");
        for (int i = 0; i < threePairs.length; i++) {
            assertEquals(threePairs[i], mappings[i / 2][i % 2]);
        }

        mappings = View.NoPreloadHolder.parseFrameRateMapping(":120@1000:::60@600::80@800:");
        for (int i = 0; i < threePairs.length; i++) {
            assertEquals(threePairs[i], mappings[i / 2][i % 2]);
        }

        mappings = View.NoPreloadHolder.parseFrameRateMapping(":120@@1000:::60@600::80@@@800:");
        for (int i = 0; i < threePairs.length; i++) {
            assertEquals(threePairs[i], mappings[i / 2][i % 2]);
        }
    }

    private void runAfterDraw(@NonNull Runnable runnable) {
        Handler handler = new Handler(Looper.getMainLooper());
        mAfterDrawLatch = new CountDownLatch(1);