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

Commit 523a7fb6 authored by Kweku Adams's avatar Kweku Adams
Browse files

Make sure jobs started from TOP app don't consume credits.

If a job is started while an app is in the TOP state, then it shouldn't
consume any of the app's credits, even if the app leaves the TOP state.

Bug: 158300259
Test: Android builds
Change-Id: Ie66a70a5a9342fb7e25129f7701ee72697e9a87b
parent a38d92b8
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1559,6 +1559,13 @@ public class JobSchedulerService extends com.android.server.SystemService
        }
    }

    /** Return the current bias of the given UID. */
    public int getUidBias(int uid) {
        synchronized (mLock) {
            return mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT);
        }
    }

    @Override
    public void onDeviceIdleStateChanged(boolean deviceIdle) {
        synchronized (mLock) {
+0 −25
Original line number Diff line number Diff line
@@ -366,9 +366,6 @@ public final class JobServiceContext implements ServiceConnection {
            } catch (RemoteException e) {
                // Whatever.
            }
            mEconomyManagerInternal.noteOngoingEventStarted(
                    job.getSourceUserId(), job.getSourcePackageName(),
                    getRunningActionId(job), String.valueOf(job.getJobId()));
            final String jobPackage = job.getSourcePackageName();
            final int jobUserId = job.getSourceUserId();
            UsageStatsManagerInternal usageStats =
@@ -401,25 +398,6 @@ public final class JobServiceContext implements ServiceConnection {
        }
    }

    @EconomicPolicy.AppAction
    private static int getRunningActionId(@NonNull JobStatus job) {
        switch (job.getEffectivePriority()) {
            case JobInfo.PRIORITY_MAX:
                return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
            case JobInfo.PRIORITY_HIGH:
                return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING;
            case JobInfo.PRIORITY_LOW:
                return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING;
            case JobInfo.PRIORITY_MIN:
                return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING;
            default:
                Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority()));
                // Intentional fallthrough
            case JobInfo.PRIORITY_DEFAULT:
                return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
        }
    }

    /**
     * Used externally to query the running job. Will return null if there is no job running.
     */
@@ -1043,9 +1021,6 @@ public final class JobServiceContext implements ServiceConnection {
        } catch (RemoteException e) {
            // Whatever.
        }
        mEconomyManagerInternal.noteOngoingEventStopped(
                mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),
                getRunningActionId(mRunningJob), String.valueOf(mRunningJob.getJobId()));
        if (mParams.getStopReason() == JobParameters.STOP_REASON_TIMEOUT) {
            mEconomyManagerInternal.noteInstantaneousEvent(
                    mRunningJob.getSourceUserId(), mRunningJob.getSourcePackageName(),
+89 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.job.controllers;

import static android.app.job.JobInfo.getPriorityString;

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

import android.annotation.NonNull;
@@ -31,6 +33,7 @@ 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.tare.EconomicPolicy;
import com.android.server.tare.EconomyManagerInternal;
import com.android.server.tare.EconomyManagerInternal.ActionBill;
import com.android.server.tare.JobSchedulerEconomicPolicy;
@@ -285,6 +288,14 @@ public class TareController extends StateController {
                }
            };

    /**
     * List of jobs that started while the UID was in the TOP state. There will be no more than
     * 16 ({@link JobSchedulerService#MAX_JOB_CONTEXTS_COUNT}) running at once, so an ArraySet is
     * fine.
     */
    @GuardedBy("mLock")
    private final ArraySet<JobStatus> mTopStartedJobs = new ArraySet<>();

    @GuardedBy("mLock")
    private boolean mIsEnabled;

@@ -299,6 +310,7 @@ public class TareController extends StateController {
    }

    @Override
    @GuardedBy("mLock")
    public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
        final long nowElapsed = sElapsedRealtimeClock.millis();
        jobStatus.setTareWealthConstraintSatisfied(nowElapsed, hasEnoughWealthLocked(jobStatus));
@@ -312,6 +324,7 @@ public class TareController extends StateController {
    }

    @Override
    @GuardedBy("mLock")
    public void prepareForExecutionLocked(JobStatus jobStatus) {
        final int userId = jobStatus.getSourceUserId();
        final String pkgName = jobStatus.getSourcePackageName();
@@ -325,12 +338,29 @@ public class TareController extends StateController {
            }
        }
        addJobToBillList(jobStatus, getRunningBill(jobStatus));

        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);
            // Top jobs won't count towards quota so there's no need to involve the EconomyManager.
        } else {
            mEconomyManagerInternal.noteOngoingEventStarted(userId, pkgName,
                    getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
        }
    }

    @Override
    @GuardedBy("mLock")
    public void unprepareFromExecutionLocked(JobStatus jobStatus) {
        final int userId = jobStatus.getSourceUserId();
        final String pkgName = jobStatus.getSourcePackageName();
        mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
                getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
        mTopStartedJobs.remove(jobStatus);

        final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus);
        ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
                mRegisteredBillsAndJobs.get(userId, pkgName);
@@ -347,10 +377,14 @@ public class TareController extends StateController {
    }

    @Override
    @GuardedBy("mLock")
    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
            boolean forUpdate) {
        final int userId = jobStatus.getSourceUserId();
        final String pkgName = jobStatus.getSourcePackageName();
        mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
                getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
        mTopStartedJobs.remove(jobStatus);
        ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
                mRegisteredBillsAndJobs.get(userId, pkgName);
        if (billToJobMap != null) {
@@ -391,8 +425,17 @@ public class TareController extends StateController {
        if (!mIsEnabled) {
            return true;
        }
        if (jobStatus.getEffectivePriority() == JobInfo.PRIORITY_MAX) {
            return canAffordBillLocked(jobStatus, BILL_JOB_START_MAX_EXPEDITED);
        }
        return canAffordBillLocked(jobStatus, BILL_JOB_START_HIGH_EXPEDITED);
    }

    /** @return true if the job was started while the app was in the TOP state. */
    @GuardedBy("mLock")
    private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) {
        return mTopStartedJobs.contains(jobStatus);
    }

    @GuardedBy("mLock")
    public long getMaxJobExecutionTimeMsLocked(@NonNull JobStatus jobStatus) {
@@ -459,6 +502,9 @@ public class TareController extends StateController {
            }
        }
        switch (jobStatus.getEffectivePriority()) {
            case JobInfo.PRIORITY_MAX:
                bills.add(BILL_JOB_START_MAX);
                break;
            case JobInfo.PRIORITY_HIGH:
                bills.add(BILL_JOB_START_HIGH);
                break;
@@ -471,6 +517,10 @@ public class TareController extends StateController {
            case JobInfo.PRIORITY_MIN:
                bills.add(BILL_JOB_START_MIN);
                break;
            default:
                Slog.wtf(TAG, "Unexpected priority: "
                        + JobInfo.getPriorityString(jobStatus.getEffectivePriority()));
                break;
        }
        return bills;
    }
@@ -502,11 +552,36 @@ public class TareController extends StateController {
        }
    }

    @EconomicPolicy.AppAction
    private static int getRunningActionId(@NonNull JobStatus job) {
        switch (job.getEffectivePriority()) {
            case JobInfo.PRIORITY_MAX:
                return JobSchedulerEconomicPolicy.ACTION_JOB_MAX_RUNNING;
            case JobInfo.PRIORITY_HIGH:
                return JobSchedulerEconomicPolicy.ACTION_JOB_HIGH_RUNNING;
            case JobInfo.PRIORITY_LOW:
                return JobSchedulerEconomicPolicy.ACTION_JOB_LOW_RUNNING;
            case JobInfo.PRIORITY_MIN:
                return JobSchedulerEconomicPolicy.ACTION_JOB_MIN_RUNNING;
            default:
                Slog.wtf(TAG, "Unknown priority: " + getPriorityString(job.getEffectivePriority()));
                // Intentional fallthrough
            case JobInfo.PRIORITY_DEFAULT:
                return JobSchedulerEconomicPolicy.ACTION_JOB_DEFAULT_RUNNING;
        }
    }

    @GuardedBy("mLock")
    private boolean canAffordBillLocked(@NonNull JobStatus jobStatus, @NonNull ActionBill bill) {
        if (!mIsEnabled) {
            return true;
        }
        if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
                || isTopStartedJobLocked(jobStatus)) {
            // Jobs for the top app should always be allowed to run, and any jobs started while
            // the app is on top shouldn't consume any credits.
            return true;
        }
        final int userId = jobStatus.getSourceUserId();
        final String pkgName = jobStatus.getSourcePackageName();
        ArrayMap<ActionBill, Boolean> actionAffordability =
@@ -533,6 +608,12 @@ public class TareController extends StateController {
        if (!jobStatus.isRequestedExpeditedJob()) {
            return false;
        }
        if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
                || isTopStartedJobLocked(jobStatus)) {
            // Jobs for the top app should always be allowed to run, and any jobs started while
            // the app is on top shouldn't consume any credits.
            return true;
        }
        if (mService.isCurrentlyRunningLocked(jobStatus)) {
            return canAffordBillLocked(jobStatus, getRunningBill(jobStatus));
        }
@@ -548,6 +629,12 @@ public class TareController extends StateController {
        if (!mIsEnabled) {
            return true;
        }
        if (mService.getUidBias(jobStatus.getSourceUid()) == JobInfo.BIAS_TOP_APP
                || isTopStartedJobLocked(jobStatus)) {
            // Jobs for the top app should always be allowed to run, and any jobs started while
            // the app is on top shouldn't consume any credits.
            return true;
        }
        if (mService.isCurrentlyRunningLocked(jobStatus)) {
            if (jobStatus.isRequestedExpeditedJob()) {
                return canAffordBillLocked(jobStatus, getRunningBill(jobStatus))