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

Commit 63a027a1 authored by Kweku Adams's avatar Kweku Adams Committed by Android (Google) Code Review
Browse files

Merge "Make applied flex constraints configurable." into main

parents 26d431de a6738a47
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1769,7 +1769,8 @@ public final class ConnectivityController extends RestrictingController implemen

    @VisibleForTesting
    class CcConfig {
        private boolean mFlexIsEnabled = FlexibilityController.FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
        private boolean mFlexIsEnabled =
                FlexibilityController.FcConfig.DEFAULT_APPLIED_CONSTRAINTS != 0;
        private boolean mShouldReprocessNetworkCapabilities = false;

        /**
+124 −61
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVITY;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE;
import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE;

import android.annotation.ElapsedRealtimeLong;
@@ -74,17 +73,10 @@ public final class FlexibilityController extends StateController {
    private static final int JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS = CONSTRAINT_CONNECTIVITY;

    /** List of all flexible constraints. */
    private static final int FLEXIBLE_CONSTRAINTS =
    @VisibleForTesting
    static final int FLEXIBLE_CONSTRAINTS =
            JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS | SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;

    private static final int NUM_JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS =
            Integer.bitCount(JOB_SPECIFIC_FLEXIBLE_CONSTRAINTS);

    static final int NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS =
            Integer.bitCount(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);

    static final int NUM_FLEXIBLE_CONSTRAINTS = Integer.bitCount(FLEXIBLE_CONSTRAINTS);

    private static final long NO_LIFECYCLE_END = Long.MAX_VALUE;

    /**
@@ -100,9 +92,15 @@ public final class FlexibilityController extends StateController {
    private long mUnseenConstraintGracePeriodMs =
            FcConfig.DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS;

    @VisibleForTesting
    /** Set of constraints supported on this device for flex scheduling. */
    private final int mSupportedFlexConstraints;

    @GuardedBy("mLock")
    private boolean mFlexibilityEnabled;

    /** Set of constraints that will be used in the flex policy. */
    @GuardedBy("mLock")
    boolean mFlexibilityEnabled = FcConfig.DEFAULT_FLEXIBILITY_ENABLED;
    private int mAppliedConstraints = FcConfig.DEFAULT_APPLIED_CONSTRAINTS;

    private long mMinTimeBetweenFlexibilityAlarmsMs =
            FcConfig.DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS;
@@ -118,9 +116,6 @@ public final class FlexibilityController extends StateController {
     */
    private int[] mPercentToDropConstraints;

    @VisibleForTesting
    boolean mDeviceSupportsFlexConstraints;

    /**
     * Keeps track of what flexible constraints are satisfied at the moment.
     * Is updated by the other controllers.
@@ -178,7 +173,7 @@ public final class FlexibilityController extends StateController {
                            if (!js.hasFlexibilityConstraint()) {
                                continue;
                            }
                            mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
                            mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
                            mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
                        }
                    }
@@ -186,15 +181,23 @@ public final class FlexibilityController extends StateController {
            };

    private static final int MSG_UPDATE_JOBS = 0;
    private static final int MSG_UPDATE_JOB = 1;

    public FlexibilityController(
            JobSchedulerService service, PrefetchController prefetchController) {
        super(service);
        mHandler = new FcHandler(AppSchedulingModuleThread.get().getLooper());
        mDeviceSupportsFlexConstraints = !mContext.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_AUTOMOTIVE);
        mFlexibilityEnabled &= mDeviceSupportsFlexConstraints;
        mFlexibilityTracker = new FlexibilityTracker(NUM_FLEXIBLE_CONSTRAINTS);
        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
                || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) {
            // Embedded devices have no user-installable apps. Assume all jobs are critical
            // and can't be flexed.
            mSupportedFlexConstraints = 0;
        } else {
            // TODO(236261941): handle devices without a battery
            mSupportedFlexConstraints = FLEXIBLE_CONSTRAINTS;
        }
        mFlexibilityEnabled = (mAppliedConstraints & mSupportedFlexConstraints) != 0;
        mFlexibilityTracker = new FlexibilityTracker(Integer.bitCount(mSupportedFlexConstraints));
        mFcConfig = new FcConfig();
        mFlexibilityAlarmQueue = new FlexibilityAlarmQueue(
                mContext, AppSchedulingModuleThread.get().getLooper());
@@ -218,10 +221,12 @@ public final class FlexibilityController extends StateController {
    public void maybeStartTrackingJobLocked(JobStatus js, JobStatus lastJob) {
        if (js.hasFlexibilityConstraint()) {
            final long nowElapsed = sElapsedRealtimeClock.millis();
            if (!mDeviceSupportsFlexConstraints) {
            if (mSupportedFlexConstraints == 0) {
                js.setFlexibilityConstraintSatisfied(nowElapsed, true);
                return;
            }
            js.setNumAppliedFlexibleConstraints(
                    Integer.bitCount(getRelevantAppliedConstraintsLocked(js)));
            js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
            mFlexibilityTracker.add(js);
            js.setTrackingController(JobStatus.TRACKING_FLEXIBILITY);
@@ -266,6 +271,14 @@ public final class FlexibilityController extends StateController {
                || mService.isCurrentlyRunningLocked(js);
    }

    @VisibleForTesting
    @GuardedBy("mLock")
    int getRelevantAppliedConstraintsLocked(@NonNull JobStatus js) {
        final int relevantConstraints = SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
                | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0);
        return mAppliedConstraints & relevantConstraints;
    }

    /**
     * Returns whether there are enough constraints satisfied to allow running the job from flex's
     * perspective. This takes into account unseen constraint combinations and expectations around
@@ -274,7 +287,7 @@ public final class FlexibilityController extends StateController {
    @VisibleForTesting
    @GuardedBy("mLock")
    boolean hasEnoughSatisfiedConstraintsLocked(@NonNull JobStatus js) {
        final int satisfiedConstraints = mSatisfiedFlexibleConstraints
        final int satisfiedConstraints = mSatisfiedFlexibleConstraints & mAppliedConstraints
                & (SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
                        | (js.areTransportAffinitiesSatisfied() ? CONSTRAINT_CONNECTIVITY : 0));
        final int numSatisfied = Integer.bitCount(satisfiedConstraints);
@@ -296,8 +309,7 @@ public final class FlexibilityController extends StateController {
        // count have not been seen recently enough, then assume they won't be seen anytime soon,
        // so don't force the job to wait longer. If any combinations with a higher count have been
        // seen recently, then the job can potentially wait for those combinations.
        final int irrelevantConstraints = ~(SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
                | (js.canApplyTransportAffinities() ? CONSTRAINT_CONNECTIVITY : 0));
        final int irrelevantConstraints = ~getRelevantAppliedConstraintsLocked(js);
        for (int i = mLastSeenConstraintTimesElapsed.size() - 1; i >= 0; --i) {
            final int constraints = mLastSeenConstraintTimesElapsed.keyAt(i);
            if ((constraints & irrelevantConstraints) != 0) {
@@ -515,9 +527,9 @@ public final class FlexibilityController extends StateController {
                    for (int j = 0; j < mFlexibilityTracker.size(); j++) {
                        final ArraySet<JobStatus> jobs = mFlexibilityTracker
                                .getJobsByNumRequiredConstraints(j);
                        for (int i = 0; i < jobs.size(); i++) {
                        for (int i = jobs.size() - 1; i >= 0; --i) {
                            JobStatus js = jobs.valueAt(i);
                            mFlexibilityTracker.resetJobNumDroppedConstraints(js, nowElapsed);
                            mFlexibilityTracker.updateFlexibleConstraints(js, nowElapsed);
                            mFlexibilityAlarmQueue.scheduleDropNumConstraintsAlarm(js, nowElapsed);
                            if (js.setFlexibilityConstraintSatisfied(
                                    nowElapsed, isFlexibilitySatisfiedLocked(js))) {
@@ -579,18 +591,46 @@ public final class FlexibilityController extends StateController {
            mTrackedJobs.get(js.getNumRequiredFlexibleConstraints()).remove(js);
        }

        public void resetJobNumDroppedConstraints(JobStatus js, long nowElapsed) {
        /**
         * Updates applied and dropped constraints for the job.
         */
        public void updateFlexibleConstraints(JobStatus js, long nowElapsed) {
            final int prevNumRequired = js.getNumRequiredFlexibleConstraints();

            final int numAppliedConstraints =
                    Integer.bitCount(getRelevantAppliedConstraintsLocked(js));
            js.setNumAppliedFlexibleConstraints(numAppliedConstraints);

            final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed);
            int toDrop = 0;
            final int jsMaxFlexibleConstraints = NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS
                    + (js.canApplyTransportAffinities() ? 1 : 0);
            for (int i = 0; i < numAppliedConstraints; i++) {
                if (curPercent >= mPercentToDropConstraints[i]) {
                    toDrop++;
                }
            }
            js.setNumDroppedFlexibleConstraints(toDrop);

            if (prevNumRequired == js.getNumRequiredFlexibleConstraints()) {
                return;
            }
            mTrackedJobs.get(prevNumRequired).remove(js);
            add(js);
        }

        /**
         * Calculates the number of constraints that should be dropped for the job, based on how
         * far along the job is into its lifecycle.
         */
        public void calculateNumDroppedConstraints(JobStatus js, long nowElapsed) {
            final int curPercent = getCurPercentOfLifecycleLocked(js, nowElapsed);
            int toDrop = 0;
            final int jsMaxFlexibleConstraints = js.getNumAppliedFlexibleConstraints();
            for (int i = 0; i < jsMaxFlexibleConstraints; i++) {
                if (curPercent >= mPercentToDropConstraints[i]) {
                    toDrop++;
                }
            }
            adjustJobsRequiredConstraints(
                    js, js.getNumDroppedFlexibleConstraints() - toDrop, nowElapsed);
            setNumDroppedFlexibleConstraints(js, toDrop);
        }

        /** Returns all tracked jobs. */
@@ -599,17 +639,14 @@ public final class FlexibilityController extends StateController {
        }

        /**
         * Adjusts number of required flexible constraints and sorts it into the tracker.
         * Returns false if the job status's number of flexible constraints is now 0.
         * Updates the number of dropped flexible constraints and sorts it into the tracker.
         */
        public boolean adjustJobsRequiredConstraints(JobStatus js, int adjustBy, long nowElapsed) {
            if (adjustBy != 0) {
        public void setNumDroppedFlexibleConstraints(JobStatus js, int numDropped) {
            if (numDropped != js.getNumDroppedFlexibleConstraints()) {
                remove(js);
                js.adjustNumRequiredFlexibleConstraints(adjustBy);
                js.setFlexibilityConstraintSatisfied(nowElapsed, isFlexibilitySatisfiedLocked(js));
                js.setNumDroppedFlexibleConstraints(numDropped);
                add(js);
            }
            return js.getNumRequiredFlexibleConstraints() > 0;
        }

        public int size() {
@@ -658,8 +695,10 @@ public final class FlexibilityController extends StateController {
                if (DEBUG) {
                    Slog.d(TAG, "scheduleDropNumConstraintsAlarm: "
                            + js.getSourcePackageName() + " " + js.getSourceUserId()
                            + " numApplied: " + js.getNumAppliedFlexibleConstraints()
                            + " numRequired: " + js.getNumRequiredFlexibleConstraints()
                            + " numSatisfied: " + Integer.bitCount(mSatisfiedFlexibleConstraints)
                            + " numSatisfied: " + Integer.bitCount(
                            mSatisfiedFlexibleConstraints & getRelevantAppliedConstraintsLocked(js))
                            + " curTime: " + nowElapsed
                            + " earliest: " + earliest
                            + " latest: " + latest
@@ -669,8 +708,9 @@ public final class FlexibilityController extends StateController {
                    if (DEBUG) {
                        Slog.d(TAG, "deadline proximity met: " + js);
                    }
                    mFlexibilityTracker.adjustJobsRequiredConstraints(js,
                            -js.getNumRequiredFlexibleConstraints(), nowElapsed);
                    mFlexibilityTracker.setNumDroppedFlexibleConstraints(js,
                            js.getNumAppliedFlexibleConstraints());
                    mHandler.obtainMessage(MSG_UPDATE_JOB, js).sendToTarget();
                    return;
                }
                if (nextTimeElapsed == NO_LIFECYCLE_END) {
@@ -696,12 +736,15 @@ public final class FlexibilityController extends StateController {
                final long nowElapsed = sElapsedRealtimeClock.millis();
                for (int i = 0; i < expired.size(); i++) {
                    JobStatus js = expired.valueAt(i);
                    boolean wasFlexibilitySatisfied = js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE);

                    if (mFlexibilityTracker.adjustJobsRequiredConstraints(js, -1, nowElapsed)) {
                    if (DEBUG) {
                        Slog.d(TAG, "Alarm fired for " + js.toShortString());
                    }
                    mFlexibilityTracker.calculateNumDroppedConstraints(js, nowElapsed);
                    if (js.getNumRequiredFlexibleConstraints() > 0) {
                        scheduleDropNumConstraintsAlarm(js, nowElapsed);
                    }
                    if (wasFlexibilitySatisfied != js.isConstraintSatisfied(CONSTRAINT_FLEXIBLE)) {
                    if (js.setFlexibilityConstraintSatisfied(nowElapsed,
                            isFlexibilitySatisfiedLocked(js))) {
                        changedJobs.add(js);
                    }
                }
@@ -725,7 +768,9 @@ public final class FlexibilityController extends StateController {
                        final long nowElapsed = sElapsedRealtimeClock.millis();
                        final ArraySet<JobStatus> changedJobs = new ArraySet<>();

                        for (int o = 0; o <= NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS; ++o) {
                        final int numAppliedSystemWideConstraints = Integer.bitCount(
                                mAppliedConstraints & SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS);
                        for (int o = 0; o <= numAppliedSystemWideConstraints; ++o) {
                            final ArraySet<JobStatus> jobsByNumConstraints = mFlexibilityTracker
                                    .getJobsByNumRequiredConstraints(o);

@@ -744,6 +789,23 @@ public final class FlexibilityController extends StateController {
                        }
                    }
                    break;

                case MSG_UPDATE_JOB:
                    synchronized (mLock) {
                        final JobStatus js = (JobStatus) msg.obj;
                        if (DEBUG) {
                            Slog.d("blah", "Checking on " + js.toShortString());
                        }
                        final long nowElapsed = sElapsedRealtimeClock.millis();
                        if (js.setFlexibilityConstraintSatisfied(
                                nowElapsed, isFlexibilitySatisfiedLocked(js))) {
                            // TODO(141645789): add method that will take a single job
                            ArraySet<JobStatus> changedJob = new ArraySet<>();
                            changedJob.add(js);
                            mStateChangedListener.onControllerStateChanged(changedJob);
                        }
                    }
                    break;
            }
        }
    }
@@ -754,7 +816,8 @@ public final class FlexibilityController extends StateController {
        /** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
        private static final String FC_CONFIG_PREFIX = "fc_";

        static final String KEY_FLEXIBILITY_ENABLED = FC_CONFIG_PREFIX + "enable_flexibility";
        @VisibleForTesting
        static final String KEY_APPLIED_CONSTRAINTS = FC_CONFIG_PREFIX + "applied_constraints";
        static final String KEY_DEADLINE_PROXIMITY_LIMIT =
                FC_CONFIG_PREFIX + "flexibility_deadline_proximity_limit_ms";
        static final String KEY_FALLBACK_FLEXIBILITY_DEADLINE =
@@ -770,7 +833,7 @@ public final class FlexibilityController extends StateController {
        static final String KEY_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS =
                FC_CONFIG_PREFIX + "unseen_constraint_grace_period_ms";

        static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
        static final int DEFAULT_APPLIED_CONSTRAINTS = 0;
        @VisibleForTesting
        static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
        @VisibleForTesting
@@ -783,11 +846,8 @@ public final class FlexibilityController extends StateController {
        @VisibleForTesting
        static final long DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = 3 * DAY_IN_MILLIS;

        /**
         * If false the controller will not track new jobs
         * and the flexibility constraint will always be satisfied.
         */
        public boolean FLEXIBILITY_ENABLED = DEFAULT_FLEXIBILITY_ENABLED;
        /** Which constraints to apply/consider in flex policy. */
        public int APPLIED_CONSTRAINTS = DEFAULT_APPLIED_CONSTRAINTS;
        /** How close to a jobs' deadline all flexible constraints will be dropped. */
        public long DEADLINE_PROXIMITY_LIMIT_MS = DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS;
        /** For jobs that lack a deadline, the time that will be used to drop all constraints by. */
@@ -811,16 +871,19 @@ public final class FlexibilityController extends StateController {
        public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
                @NonNull String key) {
            switch (key) {
                case KEY_FLEXIBILITY_ENABLED:
                    FLEXIBILITY_ENABLED = properties.getBoolean(key, DEFAULT_FLEXIBILITY_ENABLED)
                            && mDeviceSupportsFlexConstraints;
                    if (mFlexibilityEnabled != FLEXIBILITY_ENABLED) {
                        mFlexibilityEnabled = FLEXIBILITY_ENABLED;
                case KEY_APPLIED_CONSTRAINTS:
                    APPLIED_CONSTRAINTS =
                            properties.getInt(key, DEFAULT_APPLIED_CONSTRAINTS)
                                    & mSupportedFlexConstraints;
                    if (mAppliedConstraints != APPLIED_CONSTRAINTS) {
                        mAppliedConstraints = APPLIED_CONSTRAINTS;
                        mShouldReevaluateConstraints = true;
                        if (mFlexibilityEnabled) {
                        if (mAppliedConstraints != 0) {
                            mFlexibilityEnabled = true;
                            mPrefetchController
                                    .registerPrefetchChangedListener(mPrefetchChangedListener);
                        } else {
                            mFlexibilityEnabled = false;
                            mPrefetchController
                                    .unRegisterPrefetchChangedListener(mPrefetchChangedListener);
                        }
@@ -893,7 +956,7 @@ public final class FlexibilityController extends StateController {

        private int[] parsePercentToDropString(String s) {
            String[] dropPercentString = s.split(",");
            int[] dropPercentInt = new int[NUM_FLEXIBLE_CONSTRAINTS];
            int[] dropPercentInt = new int[Integer.bitCount(FLEXIBLE_CONSTRAINTS)];
            if (dropPercentInt.length != dropPercentString.length) {
                return DEFAULT_PERCENT_TO_DROP_FLEXIBLE_CONSTRAINTS;
            }
@@ -922,7 +985,7 @@ public final class FlexibilityController extends StateController {
            pw.println(":");
            pw.increaseIndent();

            pw.print(KEY_FLEXIBILITY_ENABLED, FLEXIBILITY_ENABLED).println();
            pw.print(KEY_APPLIED_CONSTRAINTS, APPLIED_CONSTRAINTS).println();
            pw.print(KEY_DEADLINE_PROXIMITY_LIMIT, DEADLINE_PROXIMITY_LIMIT_MS).println();
            pw.print(KEY_FALLBACK_FLEXIBILITY_DEADLINE, FALLBACK_FLEXIBILITY_DEADLINE_MS).println();
            pw.print(KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS,
+15 −10
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.controllers.FlexibilityController.NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;
import static com.android.server.job.controllers.FlexibilityController.SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS;

import android.annotation.ElapsedRealtimeLong;
@@ -155,7 +154,7 @@ public final class JobStatus {
    /**
     * Keeps track of how many flexible constraints must be satisfied for the job to execute.
     */
    private final int mNumRequiredFlexibleConstraints;
    private int mNumAppliedFlexibleConstraints;

    /**
     * Number of required flexible constraints that have been dropped.
@@ -697,11 +696,7 @@ public final class JobStatus {
                && satisfiesMinWindowException
                && (numFailures + numSystemStops) != 1
                && lacksSomeFlexibleConstraints) {
            mNumRequiredFlexibleConstraints =
                    NUM_SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS + (mCanApplyTransportAffinities ? 1 : 0);
            requiredConstraints |= CONSTRAINT_FLEXIBLE;
        } else {
            mNumRequiredFlexibleConstraints = 0;
        }

        this.requiredConstraints = requiredConstraints;
@@ -1527,9 +1522,14 @@ public final class JobStatus {
        return (requiredConstraints & CONSTRAINT_FLEXIBLE) != 0;
    }

    /** Returns the number of flexible job constraints being applied to the job. */
    public int getNumAppliedFlexibleConstraints() {
        return mNumAppliedFlexibleConstraints;
    }

    /** Returns the number of flexible job constraints required to be satisfied to execute */
    public int getNumRequiredFlexibleConstraints() {
        return mNumRequiredFlexibleConstraints - mNumDroppedFlexibleConstraints;
        return mNumAppliedFlexibleConstraints - mNumDroppedFlexibleConstraints;
    }

    /**
@@ -2112,9 +2112,14 @@ public final class JobStatus {
    }

    /** Adjusts the number of required flexible constraints by the given number */
    public void adjustNumRequiredFlexibleConstraints(int adjustment) {
        mNumDroppedFlexibleConstraints = Math.max(0, Math.min(mNumRequiredFlexibleConstraints,
                mNumDroppedFlexibleConstraints - adjustment));
    public void setNumAppliedFlexibleConstraints(int count) {
        mNumAppliedFlexibleConstraints = count;
    }

    /** Sets the number of dropped flexible constraints to the given number */
    public void setNumDroppedFlexibleConstraints(int count) {
        mNumDroppedFlexibleConstraints = Math.max(0,
                Math.min(mNumAppliedFlexibleConstraints, count));
    }

    /**