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

Commit 960649d6 authored by Sanath Kumar's avatar Sanath Kumar Committed by Android (Google) Code Review
Browse files

Merge "JobScheduler: Enable abandoned job overrides" into main

parents 7c4ce2fb e38ab1a9
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -23,6 +23,10 @@ import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.usage.UsageStatsManager;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.pm.PackageManager;
@@ -349,6 +353,16 @@ public class JobParameters implements Parcelable {
    private JobCleanupCallback mJobCleanupCallback;
    @Nullable
    private Cleaner.Cleanable mCleanable;
    /**
     * Override handling of abandoned jobs in the system. Overriding this change
     * will prevent the system to handle abandoned jobs and report it as a new
     * stop reason STOP_REASON_TIMEOUT_ABANDONED.
     * @hide
     */
    @ChangeId
    @Disabled
    @Overridable
    public static final long OVERRIDE_HANDLE_ABANDONED_JOBS = 372529068L;

    /** @hide */
    public JobParameters(IBinder callback, String namespace, int jobId, PersistableBundle extras,
@@ -677,6 +691,10 @@ public class JobParameters implements Parcelable {
     * @hide
     */
    public void enableCleaner() {
        if (!Flags.handleAbandonedJobs()
                || Compatibility.isChangeEnabled(OVERRIDE_HANDLE_ABANDONED_JOBS)) {
            return;
        }
        // JobParameters objects are passed by reference in local Binder
        // transactions for clients running as SYSTEM. The life cycle of the
        // JobParameters objects are no longer controlled by the client.
@@ -695,6 +713,10 @@ public class JobParameters implements Parcelable {
     * @hide
     */
    public void disableCleaner() {
        if (!Flags.handleAbandonedJobs()
                || Compatibility.isChangeEnabled(OVERRIDE_HANDLE_ABANDONED_JOBS)) {
            return;
        }
        if (mJobCleanupCallback != null) {
            mJobCleanupCallback.disableCleaner();
            if (mCleanable != null) {
+3 −7
Original line number Diff line number Diff line
@@ -165,11 +165,9 @@ public abstract class JobServiceEngine {
                case MSG_EXECUTE_JOB: {
                    final JobParameters params = (JobParameters) msg.obj;
                    try {
                        if (Flags.handleAbandonedJobs()) {
                        params.enableCleaner();
                        }
                        boolean workOngoing = JobServiceEngine.this.onStartJob(params);
                        if (Flags.handleAbandonedJobs() && !workOngoing) {
                        if (!workOngoing) {
                            params.disableCleaner();
                        }
                        ackStartMessage(params, workOngoing);
@@ -196,9 +194,7 @@ public abstract class JobServiceEngine {
                    IJobCallback callback = params.getCallback();
                    if (callback != null) {
                        try {
                            if (Flags.handleAbandonedJobs()) {
                            params.disableCleaner();
                            }
                            callback.jobFinished(params.getJobId(), needsReschedule);
                        } catch (RemoteException e) {
                            Log.e(TAG, "Error reporting job finish to system: binder has gone" +
+15 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.job;

import static android.app.job.JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
@@ -1986,8 +1987,8 @@ public class JobSchedulerService extends com.android.server.SystemService
                    jobStatus.getNumAbandonedFailures(),
                    /* 0 is reserved for UNKNOWN_POLICY */
                    jobStatus.getJob().getBackoffPolicy() + 1,
                    shouldUseAggressiveBackoff(jobStatus.getNumAbandonedFailures()));

                    shouldUseAggressiveBackoff(
                            jobStatus.getNumAbandonedFailures(), jobStatus.getSourceUid()));

            // If the job is immediately ready to run, then we can just immediately
            // put it in the pending list and try to schedule it.  This is especially
@@ -2432,7 +2433,8 @@ public class JobSchedulerService extends com.android.server.SystemService
                    cancelled.getNumAbandonedFailures(),
                    /* 0 is reserved for UNKNOWN_POLICY */
                    cancelled.getJob().getBackoffPolicy() + 1,
                    shouldUseAggressiveBackoff(cancelled.getNumAbandonedFailures()));
                    shouldUseAggressiveBackoff(
                            cancelled.getNumAbandonedFailures(), cancelled.getSourceUid()));
        }
        // If this is a replacement, bring in the new version of the job
        if (incomingJob != null) {
@@ -3024,6 +3026,7 @@ public class JobSchedulerService extends com.android.server.SystemService
        int numFailures = failureToReschedule.getNumFailures();
        int numAbandonedFailures = failureToReschedule.getNumAbandonedFailures();
        int numSystemStops = failureToReschedule.getNumSystemStops();
        final int uid = failureToReschedule.getSourceUid();
        // We should back off slowly if JobScheduler keeps stopping the job,
        // but back off immediately if the issue appeared to be the app's fault
        // or the user stopped the job somehow.
@@ -3033,6 +3036,7 @@ public class JobSchedulerService extends com.android.server.SystemService
                || stopReason == JobParameters.STOP_REASON_USER) {
            numFailures++;
        } else if (android.app.job.Flags.handleAbandonedJobs()
                && !CompatChanges.isChangeEnabled(OVERRIDE_HANDLE_ABANDONED_JOBS, uid)
                && internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED) {
            numAbandonedFailures++;
            numFailures++;
@@ -3041,7 +3045,7 @@ public class JobSchedulerService extends com.android.server.SystemService
        }

        int backoffPolicy = job.getBackoffPolicy();
        if (shouldUseAggressiveBackoff(numAbandonedFailures)) {
        if (shouldUseAggressiveBackoff(numAbandonedFailures, uid)) {
            backoffPolicy = JobInfo.BACKOFF_POLICY_EXPONENTIAL;
        }

@@ -3112,8 +3116,9 @@ public class JobSchedulerService extends com.android.server.SystemService
     * @return {@code true} if the given number of abandoned failures indicates that JobScheduler
     *     should use an aggressive backoff policy.
     */
    public boolean shouldUseAggressiveBackoff(int numAbandonedFailures) {
    public boolean shouldUseAggressiveBackoff(int numAbandonedFailures, int uid) {
        return android.app.job.Flags.handleAbandonedJobs()
                && !CompatChanges.isChangeEnabled(OVERRIDE_HANDLE_ABANDONED_JOBS, uid)
                && numAbandonedFailures
                        > mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF;
    }
@@ -3223,7 +3228,9 @@ public class JobSchedulerService extends com.android.server.SystemService
    @VisibleForTesting
    void maybeProcessBuggyJob(@NonNull JobStatus jobStatus, int debugStopReason) {
        boolean jobTimedOut = debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT;
        if (android.app.job.Flags.handleAbandonedJobs()) {
        if (android.app.job.Flags.handleAbandonedJobs()
                && !CompatChanges.isChangeEnabled(
                        OVERRIDE_HANDLE_ABANDONED_JOBS, jobStatus.getSourceUid())) {
            jobTimedOut |= (debugStopReason
                == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
        }
@@ -3309,6 +3316,8 @@ public class JobSchedulerService extends com.android.server.SystemService
        final JobStatus rescheduledJob = needsReschedule
                ? getRescheduleJobForFailureLocked(jobStatus, stopReason, debugStopReason) : null;
        final boolean isStopReasonAbandoned = android.app.job.Flags.handleAbandonedJobs()
                && !CompatChanges.isChangeEnabled(
                        OVERRIDE_HANDLE_ABANDONED_JOBS, jobStatus.getSourceUid())
                && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED);
        if (rescheduledJob != null
                && !rescheduledJob.shouldTreatAsUserInitiatedJob()
+10 −3
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.job;

import static android.app.job.JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS;

import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.JobSchedulerService.safelyScaleBytesToKBForHistogram;
@@ -550,7 +552,8 @@ public final class JobServiceContext implements ServiceConnection {
                    job.getNumAbandonedFailures(),
                    /* 0 is reserved for UNKNOWN_POLICY */
                    job.getJob().getBackoffPolicy() + 1,
                    mService.shouldUseAggressiveBackoff(job.getNumAbandonedFailures()));
                    mService.shouldUseAggressiveBackoff(
                            job.getNumAbandonedFailures(), job.getSourceUid()));
            sEnqueuedJwiAtJobStart.logSampleWithUid(job.getUid(), job.getWorkCount());
            final String sourcePackage = job.getSourcePackageName();
            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
@@ -1461,7 +1464,10 @@ public final class JobServiceContext implements ServiceConnection {
                    final StringBuilder debugStopReason = new StringBuilder("client timed out");

                    if (android.app.job.Flags.handleAbandonedJobs()
                            && executing != null && executing.isAbandoned()) {
                            && executing != null
                            && !CompatChanges.isChangeEnabled(
                                    OVERRIDE_HANDLE_ABANDONED_JOBS, executing.getSourceUid())
                            && executing.isAbandoned()) {
                        final String abandonedMessage = " and maybe abandoned";
                        stopReason = JobParameters.STOP_REASON_TIMEOUT_ABANDONED;
                        internalStopReason = JobParameters.INTERNAL_STOP_REASON_TIMEOUT_ABANDONED;
@@ -1689,7 +1695,8 @@ public final class JobServiceContext implements ServiceConnection {
                completedJob.getNumAbandonedFailures(),
                /* 0 is reserved for UNKNOWN_POLICY */
                completedJob.getJob().getBackoffPolicy() + 1,
                mService.shouldUseAggressiveBackoff(completedJob.getNumAbandonedFailures()));
                mService.shouldUseAggressiveBackoff(
                        completedJob.getNumAbandonedFailures(), completedJob.getSourceUid()));
        if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
            Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER,
                    JobSchedulerService.TRACE_TRACK_NAME, getId());
+40 −4
Original line number Diff line number Diff line
@@ -29,15 +29,20 @@ import android.app.job.IJobCallback;
import android.app.job.JobParameters;
import android.net.Uri;
import android.os.Parcel;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.platform.test.flag.junit.SetFlagsRule;

import libcore.junit.util.compat.CoreCompatChangeRule;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
@@ -47,7 +52,10 @@ public class JobParametersTest {
    private static final int TEST_JOB_ID_1 = 123;
    private static final String TEST_NAMESPACE = "TEST_NAMESPACE";
    private static final String TEST_DEBUG_STOP_REASON = "TEST_DEBUG_STOP_REASON";
    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
    @Rule
    public TestRule compatChangeRule = new CoreCompatChangeRule();

    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -129,9 +137,10 @@ public class JobParametersTest {
    }

    /** Test to verify that the JobParameters Cleaner is disabled */
    @RequiresFlagsEnabled(FLAG_HANDLE_ABANDONED_JOBS)
    @Test
    public void testCleanerWithLeakedJobCleanerDisabled_flagHandleAbandonedJobs() {
    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
    @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
    public void testCleanerWithLeakedNoJobCleaner_EnableFlagDisableCompatHandleAbandonedJobs() {
        // Inject real JobCallbackCleanup
        JobParameters jobParameters = JobParameters.CREATOR.createFromParcel(mMockParcel);

@@ -150,4 +159,31 @@ public class JobParametersTest {
        assertThat(jobParameters.getCleanable()).isNull();
        assertThat(jobParameters.getJobCleanupCallback()).isNull();
    }

    /**
     * Test to verify that the JobParameters Cleaner is not enabled
     * when the compat change is enabled and the flag is enabled
     */
    @Test
    @EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
    @EnableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
    public void testCleanerWithLeakedNoJobCleaner_EnableFlagEnableCompatHandleAbandonedJobs() {
        // Inject real JobCallbackCleanup
        JobParameters jobParameters = JobParameters.CREATOR.createFromParcel(mMockParcel);

        // Enable the cleaner
        jobParameters.enableCleaner();

        // Verify the cleaner is not enabled
        assertThat(jobParameters.getCleanable()).isNull();
        assertThat(jobParameters.getJobCleanupCallback()).isNull();

        // Disable the cleaner
        jobParameters.disableCleaner();

        // Verify the cleaner is disabled
        assertThat(jobParameters.getCleanable()).isNull();
        assertThat(jobParameters.getJobCleanupCallback()).isNull();
    }

}
Loading