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

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

Merge "Satisfy charging constraint earlier for top apps."

parents cd4c1c32 9bbddb73
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1556,7 +1556,7 @@ public class JobSchedulerService extends com.android.server.SystemService
                    Slog.d(TAG, "UID " + uid + " bias changed from " + prevBias + " to " + newBias);
                }
                for (int c = 0; c < mControllers.size(); ++c) {
                    mControllers.get(c).onUidBiasChangedLocked(uid, newBias);
                    mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias);
                }
            }
        }
+154 −8
Original line number Diff line number Diff line
@@ -18,6 +18,14 @@ package com.android.server.job.controllers;

import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;

import android.annotation.NonNull;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
@@ -27,6 +35,7 @@ import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.GuardedBy;
import com.android.server.JobSchedulerBackgroundThread;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;

@@ -42,10 +51,26 @@ public final class BatteryController extends RestrictingController {
    private static final boolean DEBUG = JobSchedulerService.DEBUG
            || Log.isLoggable(TAG, Log.DEBUG);

    @GuardedBy("mLock")
    private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
    /**
     * List of jobs that started while the UID was in the TOP state.
     */
    @GuardedBy("mLock")
    private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();

    private final PowerTracker mPowerTracker;

    /**
     * Helper set to avoid too much GC churn from frequent calls to
     * {@link #maybeReportNewChargingStateLocked()}.
     */
    private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();

    public BatteryController(JobSchedulerService service) {
        super(service);
        mPowerTracker = new PowerTracker();
        mPowerTracker.startTracking();
    }

    @Override
@@ -54,8 +79,15 @@ public final class BatteryController extends RestrictingController {
            final long nowElapsed = sElapsedRealtimeClock.millis();
            mTrackedTasks.add(taskStatus);
            taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY);
            if (taskStatus.hasChargingConstraint()) {
                if (hasTopExemptionLocked(taskStatus)) {
                    taskStatus.setChargingConstraintSatisfied(nowElapsed,
                            mPowerTracker.isPowerConnected());
                } else {
                    taskStatus.setChargingConstraintSatisfied(nowElapsed,
                            mService.isBatteryCharging() && mService.isBatteryNotLow());
                }
            }
            taskStatus.setBatteryNotLowConstraintSatisfied(nowElapsed, mService.isBatteryNotLow());
        }
    }
@@ -65,10 +97,33 @@ public final class BatteryController extends RestrictingController {
        maybeStartTrackingJobLocked(jobStatus, null);
    }

    @Override
    @GuardedBy("mLock")
    public void prepareForExecutionLocked(JobStatus jobStatus) {
        if (DEBUG) {
            Slog.d(TAG, "Prepping for " + jobStatus.toShortString());
        }

        final int uid = jobStatus.getSourceUid();
        if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) {
            if (DEBUG) {
                Slog.d(TAG, jobStatus.toShortString() + " is top started job");
            }
            mTopStartedJobs.add(jobStatus);
        }
    }

    @Override
    @GuardedBy("mLock")
    public void unprepareFromExecutionLocked(JobStatus jobStatus) {
        mTopStartedJobs.remove(jobStatus);
    }

    @Override
    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
        if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
            mTrackedTasks.remove(taskStatus);
            mTopStartedJobs.remove(taskStatus);
        }
    }

@@ -90,33 +145,124 @@ public final class BatteryController extends RestrictingController {
        });
    }

    @Override
    @GuardedBy("mLock")
    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
        if (prevBias == JobInfo.BIAS_TOP_APP || newBias == JobInfo.BIAS_TOP_APP) {
            maybeReportNewChargingStateLocked();
        }
    }

    @GuardedBy("mLock")
    private boolean hasTopExemptionLocked(@NonNull JobStatus taskStatus) {
        return mService.getUidBias(taskStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
                || mTopStartedJobs.contains(taskStatus);
    }

    @GuardedBy("mLock")
    private void maybeReportNewChargingStateLocked() {
        final boolean powerConnected = mPowerTracker.isPowerConnected();
        final boolean stablePower = mService.isBatteryCharging() && mService.isBatteryNotLow();
        final boolean batteryNotLow = mService.isBatteryNotLow();
        if (DEBUG) {
            Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower);
            Slog.d(TAG, "maybeReportNewChargingStateLocked: "
                    + powerConnected + "/" + stablePower + "/" + batteryNotLow);
        }
        final long nowElapsed = sElapsedRealtimeClock.millis();
        boolean reportChange = false;
        for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
            final JobStatus ts = mTrackedTasks.valueAt(i);
            reportChange |= ts.setChargingConstraintSatisfied(nowElapsed, stablePower);
            reportChange |= ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow);
            if (ts.hasChargingConstraint()) {
                if (hasTopExemptionLocked(ts)
                        && ts.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
                    // If the job started while the app was on top or the app is currently on top,
                    // let the job run as long as there's power connected, even if the device isn't
                    // officially charging.
                    // For user requested/initiated jobs, users may be confused when the task stops
                    // running even though the device is plugged in.
                    // Low priority jobs don't need to be exempted.
                    if (ts.setChargingConstraintSatisfied(nowElapsed, powerConnected)) {
                        mChangedJobs.add(ts);
                    }
                } else if (ts.setChargingConstraintSatisfied(nowElapsed, stablePower)) {
                    mChangedJobs.add(ts);
                }
            }
            if (ts.hasBatteryNotLowConstraint()
                    && ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow)) {
                mChangedJobs.add(ts);
            }
        }
        if (stablePower || batteryNotLow) {
            // If one of our conditions has been satisfied, always schedule any newly ready jobs.
            mStateChangedListener.onRunJobNow(null);
        } else if (reportChange) {
        } else if (mChangedJobs.size() > 0) {
            // Otherwise, just let the job scheduler know the state has changed and take care of it
            // as it thinks is best.
            mStateChangedListener.onControllerStateChanged(mTrackedTasks);
            mStateChangedListener.onControllerStateChanged(mChangedJobs);
        }
        mChangedJobs.clear();
    }

    private final class PowerTracker extends BroadcastReceiver {
        /**
         * Track whether there is power connected. It doesn't mean the device is charging.
         * Use {@link JobSchedulerService#isBatteryCharging()} to determine if the device is
         * charging.
         */
        private boolean mPowerConnected;

        PowerTracker() {
        }

        void startTracking() {
            IntentFilter filter = new IntentFilter();

            filter.addAction(Intent.ACTION_POWER_CONNECTED);
            filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
            mContext.registerReceiver(this, filter);

            // Initialize tracker state.
            BatteryManagerInternal batteryManagerInternal =
                    LocalServices.getService(BatteryManagerInternal.class);
            mPowerConnected = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
        }

        boolean isPowerConnected() {
            return mPowerConnected;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            synchronized (mLock) {
                final String action = intent.getAction();

                if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
                    if (DEBUG) {
                        Slog.d(TAG, "Power connected @ " + sElapsedRealtimeClock.millis());
                    }
                    if (mPowerConnected) {
                        return;
                    }
                    mPowerConnected = true;
                } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
                    if (DEBUG) {
                        Slog.d(TAG, "Power disconnected @ " + sElapsedRealtimeClock.millis());
                    }
                    if (!mPowerConnected) {
                        return;
                    }
                    mPowerConnected = false;
                }

                maybeReportNewChargingStateLocked();
            }
        }
    }

    @Override
    public void dumpControllerStateLocked(IndentingPrintWriter pw,
            Predicate<JobStatus> predicate) {
        pw.println("Power connected: " + mPowerTracker.isPowerConnected());
        pw.println("Stable power: " + (mService.isBatteryCharging() && mService.isBatteryNotLow()));
        pw.println("Not low: " + mService.isBatteryNotLow());

+1 −1
Original line number Diff line number Diff line
@@ -517,7 +517,7 @@ public final class ConnectivityController extends RestrictingController implemen

    @GuardedBy("mLock")
    @Override
    public void onUidBiasChangedLocked(int uid, int newBias) {
    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
        UidStats uidStats = mUidStats.get(uid);
        if (uidStats != null && uidStats.baseBias != newBias) {
            uidStats.baseBias = newBias;
+4 −13
Original line number Diff line number Diff line
@@ -40,7 +40,6 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;

import com.android.internal.annotations.GuardedBy;
@@ -81,9 +80,6 @@ public class PrefetchController extends StateController {
     */
    @GuardedBy("mLock")
    private final SparseArrayMap<String, Long> mEstimatedLaunchTimes = new SparseArrayMap<>();
    /** Cached list of UIDs in the TOP state. */
    @GuardedBy("mLock")
    private final SparseBooleanArray mTopUids = new SparseBooleanArray();
    private final ThresholdAlarmListener mThresholdAlarmListener;

    /**
@@ -186,15 +182,9 @@ public class PrefetchController extends StateController {

    @GuardedBy("mLock")
    @Override
    public void onUidBiasChangedLocked(int uid, int newBias) {
    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
        final boolean isNowTop = newBias == JobInfo.BIAS_TOP_APP;
        final boolean wasTop = mTopUids.get(uid);
        if (isNowTop) {
            mTopUids.put(uid, true);
        } else {
            // Delete entries of non-top apps so the set doesn't get too large.
            mTopUids.delete(uid);
        }
        final boolean wasTop = prevBias == JobInfo.BIAS_TOP_APP;
        if (isNowTop != wasTop) {
            mHandler.obtainMessage(MSG_PROCESS_TOP_STATE_CHANGE, uid, 0).sendToTarget();
        }
@@ -314,7 +304,8 @@ public class PrefetchController extends StateController {
        //   3. The app is not open but has an active widget (we can't tell if a widget displays
        //      status/data, so this assumes the prefetch job is to update the data displayed on
        //      the widget).
        final boolean appIsOpen = mTopUids.get(jobStatus.getSourceUid());
        final boolean appIsOpen =
                mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP;
        final boolean satisfied;
        if (!appIsOpen) {
            final int userId = jobStatus.getSourceUserId();
+1 −1
Original line number Diff line number Diff line
@@ -144,7 +144,7 @@ public abstract class StateController {
     * important the UID is.
     */
    @GuardedBy("mLock")
    public void onUidBiasChangedLocked(int uid, int newBias) {
    public void onUidBiasChangedLocked(int uid, int prevBias, int newBias) {
    }

    protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) {
Loading