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

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

Merge "Place additional constraints on restricted jobs."

parents e178c317 ae9d6beb
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -48,6 +48,13 @@ public class JobParameters implements Parcelable {
    public static final int REASON_DEVICE_IDLE = JobProtoEnums.STOP_REASON_DEVICE_IDLE; // 4.
    /** @hide */
    public static final int REASON_DEVICE_THERMAL = JobProtoEnums.STOP_REASON_DEVICE_THERMAL; // 5.
    /**
     * The job is in the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
     * bucket.
     *
     * @hide
     */
    public static final int REASON_RESTRAINED = JobProtoEnums.STOP_REASON_RESTRAINED; // 6.

    /**
     * All the stop reason codes. This should be regarded as an immutable array at runtime.
@@ -65,6 +72,7 @@ public class JobParameters implements Parcelable {
            REASON_TIMEOUT,
            REASON_DEVICE_IDLE,
            REASON_DEVICE_THERMAL,
            REASON_RESTRAINED,
    };

    /**
@@ -80,6 +88,7 @@ public class JobParameters implements Parcelable {
            case REASON_TIMEOUT: return "timeout";
            case REASON_DEVICE_IDLE: return "device_idle";
            case REASON_DEVICE_THERMAL: return "thermal";
            case REASON_RESTRAINED: return "restrained";
            default: return "unknown:" + reason;
        }
    }
+69 −9
Original line number Diff line number Diff line
@@ -101,6 +101,7 @@ import com.android.server.job.controllers.DeviceIdleJobsController;
import com.android.server.job.controllers.IdleController;
import com.android.server.job.controllers.JobStatus;
import com.android.server.job.controllers.QuotaController;
import com.android.server.job.controllers.RestrictingController;
import com.android.server.job.controllers.StateController;
import com.android.server.job.controllers.StorageController;
import com.android.server.job.controllers.TimeController;
@@ -241,6 +242,11 @@ public class JobSchedulerService extends com.android.server.SystemService

    /** List of controllers that will notify this service of updates to jobs. */
    final List<StateController> mControllers;
    /**
     * List of controllers that will apply to all jobs in the RESTRICTED bucket. This is a subset of
     * {@link #mControllers}.
     */
    private final List<RestrictingController> mRestrictiveControllers;
    /** Need direct access to this for testing. */
    private final BatteryController mBatteryController;
    /** Need direct access to this for testing. */
@@ -313,6 +319,9 @@ public class JobSchedulerService extends com.android.server.SystemService
    public static final int FREQUENT_INDEX = 2;
    public static final int RARE_INDEX = 3;
    public static final int NEVER_INDEX = 4;
    // Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping
    // (ScheduledJobStateChanged and JobStatusDumpProto).
    public static final int RESTRICTED_INDEX = 5;

    // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --

@@ -1367,6 +1376,40 @@ public class JobSchedulerService extends com.android.server.SystemService
        }
    }

    @Override
    public void onRestrictedBucketChanged(List<JobStatus> jobs) {
        final int len = jobs.size();
        if (len == 0) {
            Slog.wtf(TAG, "onRestrictedBucketChanged called with no jobs");
            return;
        }
        synchronized (mLock) {
            for (int i = 0; i < len; ++i) {
                JobStatus js = jobs.get(i);
                for (int j = mRestrictiveControllers.size() - 1; j >= 0; --j) {
                    // Effective standby bucket can change after this in some situations so use
                    // the real bucket so that the job is tracked by the controllers.
                    if (js.getStandbyBucket() == RESTRICTED_INDEX) {
                        js.addDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
                        js.addDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
                        js.addDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
                        js.addDynamicConstraint(JobStatus.CONSTRAINT_IDLE);

                        mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js);
                    } else {
                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_BATTERY_NOT_LOW);
                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_CHARGING);
                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_CONNECTIVITY);
                        js.removeDynamicConstraint(JobStatus.CONSTRAINT_IDLE);

                        mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js);
                    }
                }
            }
        }
        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
    }

    void reportActiveLocked() {
        // active is true if pending queue contains jobs OR some job is running.
        boolean active = mPendingJobs.size() > 0;
@@ -1443,9 +1486,11 @@ public class JobSchedulerService extends com.android.server.SystemService

        // Create the controllers.
        mControllers = new ArrayList<StateController>();
        mControllers.add(new ConnectivityController(this));
        final ConnectivityController connectivityController = new ConnectivityController(this);
        mControllers.add(connectivityController);
        mControllers.add(new TimeController(this));
        mControllers.add(new IdleController(this));
        final IdleController idleController = new IdleController(this);
        mControllers.add(idleController);
        mBatteryController = new BatteryController(this);
        mControllers.add(mBatteryController);
        mStorageController = new StorageController(this);
@@ -1457,6 +1502,11 @@ public class JobSchedulerService extends com.android.server.SystemService
        mQuotaController = new QuotaController(this);
        mControllers.add(mQuotaController);

        mRestrictiveControllers = new ArrayList<>();
        mRestrictiveControllers.add(mBatteryController);
        mRestrictiveControllers.add(connectivityController);
        mRestrictiveControllers.add(idleController);

        // Create restrictions
        mJobRestrictions = new ArrayList<>();
        mJobRestrictions.add(new ThermalStatusRestriction(this));
@@ -2127,11 +2177,13 @@ public class JobSchedulerService extends com.android.server.SystemService
                    }
                } catch (RemoteException e) {
                }
                if (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
                // Restricted jobs must always be batched
                if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX
                        || (mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
                        && job.getEffectiveStandbyBucket() != ACTIVE_INDEX
                        && (job.getFirstForceBatchedTimeElapsed() == 0
                        || sElapsedRealtimeClock.millis() - job.getFirstForceBatchedTimeElapsed()
                                < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS)) {
                                < mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS))) {
                    // Force batching non-ACTIVE jobs. Don't include them in the other counts.
                    forceBatchedCount++;
                    if (job.getFirstForceBatchedTimeElapsed() == 0) {
@@ -2538,11 +2590,19 @@ public class JobSchedulerService extends com.android.server.SystemService

    public static int standbyBucketToBucketIndex(int bucket) {
        // Normalize AppStandby constants to indices into our bookkeeping
        if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
        else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
        else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
        else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
        else return ACTIVE_INDEX;
        if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) {
            return NEVER_INDEX;
        } else if (bucket > UsageStatsManager.STANDBY_BUCKET_RARE) {
            return RESTRICTED_INDEX;
        } else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) {
            return RARE_INDEX;
        } else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
            return FREQUENT_INDEX;
        } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
            return WORKING_INDEX;
        } else {
            return ACTIVE_INDEX;
        }
    }

    // Static to support external callers
+10 −0
Original line number Diff line number Diff line
@@ -16,8 +16,12 @@

package com.android.server.job;

import android.annotation.NonNull;

import com.android.server.job.controllers.JobStatus;

import java.util.List;

/**
 * Interface through which a {@link com.android.server.job.controllers.StateController} informs
 * the {@link com.android.server.job.JobSchedulerService} that there are some tasks potentially
@@ -39,4 +43,10 @@ public interface StateChangedListener {
    public void onRunJobNow(JobStatus jobStatus);

    public void onDeviceIdleStateChanged(boolean deviceIdle);

    /**
     * Called when these jobs are added or removed from the
     * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
     */
    void onRestrictedBucketChanged(@NonNull List<JobStatus> jobs);
}
+13 −1
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ import java.util.function.Predicate;
 * be charging when it's been plugged in for more than two minutes, and the system has broadcast
 * ACTION_BATTERY_OK.
 */
public final class BatteryController extends StateController {
public final class BatteryController extends RestrictingController {
    private static final String TAG = "JobScheduler.Battery";
    private static final boolean DEBUG = JobSchedulerService.DEBUG
            || Log.isLoggable(TAG, Log.DEBUG);
@@ -72,6 +72,11 @@ public final class BatteryController extends StateController {
        }
    }

    @Override
    public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
        maybeStartTrackingJobLocked(jobStatus, null);
    }

    @Override
    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
        if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
@@ -79,6 +84,13 @@ public final class BatteryController extends StateController {
        }
    }

    @Override
    public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
        if (!jobStatus.hasPowerConstraint()) {
            maybeStopTrackingJobLocked(jobStatus, null, false);
        }
    }

    private void maybeReportNewChargingStateLocked() {
        final boolean stablePower = mChargeTracker.isOnStablePower();
        final boolean batteryNotLow = mChargeTracker.isBatteryNotLow();
+32 −6
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;

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

import android.app.job.JobInfo;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
@@ -63,7 +65,7 @@ import java.util.function.Predicate;
 *
 * Test: atest com.android.server.job.controllers.ConnectivityControllerTest
 */
public final class ConnectivityController extends StateController implements
public final class ConnectivityController extends RestrictingController implements
        ConnectivityManager.OnNetworkActiveListener {
    private static final String TAG = "JobScheduler.Connectivity";
    private static final boolean DEBUG = JobSchedulerService.DEBUG
@@ -138,8 +140,22 @@ public final class ConnectivityController extends StateController implements
        }
    }

    @Override
    public void startTrackingRestrictedJobLocked(JobStatus jobStatus) {
        // Don't need to start tracking the job. If the job needed network, it would already be
        // tracked.
        updateConstraintsSatisfied(jobStatus);
    }

    @Override
    public void stopTrackingRestrictedJobLocked(JobStatus jobStatus) {
        // Shouldn't stop tracking the job here. If the job was tracked, it still needs network,
        // even after being unrestricted.
        updateConstraintsSatisfied(jobStatus);
    }

    /**
     * Returns true if the job's requested network is available. This DOES NOT necesarilly mean
     * Returns true if the job's requested network is available. This DOES NOT necessarily mean
     * that the UID has been granted access to the network.
     */
    public boolean isNetworkAvailable(JobStatus job) {
@@ -353,14 +369,24 @@ public final class ConnectivityController extends StateController implements

    private static boolean isStrictSatisfied(JobStatus jobStatus, Network network,
            NetworkCapabilities capabilities, Constants constants) {
        return jobStatus.getJob().getRequiredNetwork().networkCapabilities
                .satisfiedByNetworkCapabilities(capabilities);
        final NetworkCapabilities required;
        // A restricted job that's out of quota MUST use an unmetered network.
        if (jobStatus.getEffectiveStandbyBucket() == RESTRICTED_INDEX
                && !jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_WITHIN_QUOTA)) {
            required = new NetworkCapabilities(
                    jobStatus.getJob().getRequiredNetwork().networkCapabilities)
                    .addCapability(NET_CAPABILITY_NOT_METERED);
        } else {
            required = jobStatus.getJob().getRequiredNetwork().networkCapabilities;
        }

        return required.satisfiedByNetworkCapabilities(capabilities);
    }

    private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
            NetworkCapabilities capabilities, Constants constants) {
        // Only consider doing this for prefetching jobs
        if (!jobStatus.getJob().isPrefetch()) {
        // Only consider doing this for unrestricted prefetching jobs
        if (!jobStatus.getJob().isPrefetch() || jobStatus.getStandbyBucket() == RESTRICTED_INDEX) {
            return false;
        }

Loading