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

Commit 192929f1 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 12337991 from 784140a0 to 24Q4-release

Change-Id: I16049100d9b358a98de6345dbf94aecdd1d9b39d
parents c990d6e7 784140a0
Loading
Loading
Loading
Loading
+7 −0
Original line number Original line Diff line number Diff line
@@ -23,3 +23,10 @@ flag {
    description: "Introduce a new RUN_BACKUP_JOBS permission and exemption logic allowing for longer running jobs for apps whose primary purpose is to backup or sync content."
    description: "Introduce a new RUN_BACKUP_JOBS permission and exemption logic allowing for longer running jobs for apps whose primary purpose is to backup or sync content."
    bug: "318731461"
    bug: "318731461"
}
}

flag {
   name: "cleanup_empty_jobs"
   namespace: "backstage_power"
   description: "Enables automatic cancellation of jobs due to leaked JobParameters, reducing unnecessary battery drain and improving system efficiency. This includes logging and traces for better issue diagnosis."
   bug: "349688611"
}
+8 −0
Original line number Original line Diff line number Diff line
@@ -85,6 +85,14 @@ interface IJobCallback {
     */
     */
    @UnsupportedAppUsage
    @UnsupportedAppUsage
    void jobFinished(int jobId, boolean reschedule);
    void jobFinished(int jobId, boolean reschedule);

    /*
     * Inform JobScheduler to force finish this job because the client has lost
     * the job handle. jobFinished can no longer be called from the client.
     * @param jobId Unique integer used to identify this job
     */
    void forceJobFinished(int jobId);

    /*
    /*
     * Inform JobScheduler of a change in the estimated transfer payload.
     * Inform JobScheduler of a change in the estimated transfer payload.
     *
     *
+123 −0
Original line number Original line Diff line number Diff line
@@ -34,15 +34,21 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.RemoteException;
import android.system.SystemCleaner;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;


import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.Cleaner;


/**
/**
 * Contains the parameters used to configure/identify your job. You do not create this object
 * Contains the parameters used to configure/identify your job. You do not create this object
 * yourself, instead it is handed in to your application by the System.
 * yourself, instead it is handed in to your application by the System.
 */
 */
public class JobParameters implements Parcelable {
public class JobParameters implements Parcelable {
    private static final String TAG = "JobParameters";


    /** @hide */
    /** @hide */
    public static final int INTERNAL_STOP_REASON_UNKNOWN = -1;
    public static final int INTERNAL_STOP_REASON_UNKNOWN = -1;
@@ -306,6 +312,10 @@ public class JobParameters implements Parcelable {
    private int mStopReason = STOP_REASON_UNDEFINED;
    private int mStopReason = STOP_REASON_UNDEFINED;
    private int mInternalStopReason = INTERNAL_STOP_REASON_UNKNOWN;
    private int mInternalStopReason = INTERNAL_STOP_REASON_UNKNOWN;
    private String debugStopReason; // Human readable stop reason for debugging.
    private String debugStopReason; // Human readable stop reason for debugging.
    @Nullable
    private JobCleanupCallback mJobCleanupCallback;
    @Nullable
    private Cleaner.Cleanable mCleanable;


    /** @hide */
    /** @hide */
    public JobParameters(IBinder callback, String namespace, int jobId, PersistableBundle extras,
    public JobParameters(IBinder callback, String namespace, int jobId, PersistableBundle extras,
@@ -326,6 +336,8 @@ public class JobParameters implements Parcelable {
        this.mTriggeredContentAuthorities = triggeredContentAuthorities;
        this.mTriggeredContentAuthorities = triggeredContentAuthorities;
        this.mNetwork = network;
        this.mNetwork = network;
        this.mJobNamespace = namespace;
        this.mJobNamespace = namespace;
        this.mJobCleanupCallback = null;
        this.mCleanable = null;
    }
    }


    /**
    /**
@@ -597,6 +609,8 @@ public class JobParameters implements Parcelable {
        mStopReason = in.readInt();
        mStopReason = in.readInt();
        mInternalStopReason = in.readInt();
        mInternalStopReason = in.readInt();
        debugStopReason = in.readString();
        debugStopReason = in.readString();
        mJobCleanupCallback = null;
        mCleanable = null;
    }
    }


    /** @hide */
    /** @hide */
@@ -612,6 +626,54 @@ public class JobParameters implements Parcelable {
        this.debugStopReason = debugStopReason;
        this.debugStopReason = debugStopReason;
    }
    }


    /** @hide */
    public void initCleaner(JobCleanupCallback jobCleanupCallback) {
        mJobCleanupCallback = jobCleanupCallback;
        mCleanable = SystemCleaner.cleaner().register(this, mJobCleanupCallback);
    }

    /**
     * Lazy initialize the cleaner and enable it
     *
     * @hide
     */
    public void enableCleaner() {
        if (mJobCleanupCallback == null) {
            initCleaner(new JobCleanupCallback(IJobCallback.Stub.asInterface(callback), jobId));
        }
        mJobCleanupCallback.enableCleaner();
    }

    /**
     * Disable the cleaner from running and unregister it
     *
     * @hide
     */
    public void disableCleaner() {
        if (mJobCleanupCallback != null) {
            mJobCleanupCallback.disableCleaner();
            if (mCleanable != null) {
                mCleanable.clean();
                mCleanable = null;
            }
            mJobCleanupCallback = null;
        }
    }

    /** @hide */
    @VisibleForTesting
    @Nullable
    public Cleaner.Cleanable getCleanable() {
        return mCleanable;
    }

    /** @hide */
    @VisibleForTesting
    @Nullable
    public JobCleanupCallback getJobCleanupCallback() {
        return mJobCleanupCallback;
    }

    @Override
    @Override
    public int describeContents() {
    public int describeContents() {
        return 0;
        return 0;
@@ -647,6 +709,67 @@ public class JobParameters implements Parcelable {
        dest.writeString(debugStopReason);
        dest.writeString(debugStopReason);
    }
    }


    /**
     * JobCleanupCallback is used track JobParameters leak. If the job is started
     * and jobFinish is not called at the time of garbage collection of JobParameters
     * instance, it is considered a job leak. Force finish the job.
     *
     * @hide
     */
    public static class JobCleanupCallback implements Runnable {
        private final IJobCallback mCallback;
        private final int mJobId;
        private boolean mIsCleanerEnabled;

        public JobCleanupCallback(
                IJobCallback callback,
                int jobId) {
            mCallback = callback;
            mJobId = jobId;
            mIsCleanerEnabled = false;
        }

        /**
         * Check if the cleaner is enabled
         *
         * @hide
         */
        public boolean isCleanerEnabled() {
            return mIsCleanerEnabled;
        }

        /**
         * Enable the cleaner to detect JobParameter leak
         *
         * @hide
         */
        public void enableCleaner() {
            mIsCleanerEnabled = true;
        }

        /**
         * Disable the cleaner from running.
         *
         * @hide
         */
        public void disableCleaner() {
            mIsCleanerEnabled = false;
        }

        /** @hide */
        @Override
        public void run() {
            if (!isCleanerEnabled()) {
                return;
            }
            try {
                mCallback.forceJobFinished(mJobId);
            } catch (Exception e) {
                Log.wtf(TAG, "Could not destroy running job", e);
            }
        }
    }

    public static final @android.annotation.NonNull Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
    public static final @android.annotation.NonNull Creator<JobParameters> CREATOR = new Creator<JobParameters>() {
        @Override
        @Override
        public JobParameters createFromParcel(Parcel in) {
        public JobParameters createFromParcel(Parcel in) {
+9 −0
Original line number Original line Diff line number Diff line
@@ -165,7 +165,13 @@ public abstract class JobServiceEngine {
                case MSG_EXECUTE_JOB: {
                case MSG_EXECUTE_JOB: {
                    final JobParameters params = (JobParameters) msg.obj;
                    final JobParameters params = (JobParameters) msg.obj;
                    try {
                    try {
                        if (Flags.cleanupEmptyJobs()) {
                            params.enableCleaner();
                        }
                        boolean workOngoing = JobServiceEngine.this.onStartJob(params);
                        boolean workOngoing = JobServiceEngine.this.onStartJob(params);
                        if (Flags.cleanupEmptyJobs() && !workOngoing) {
                            params.disableCleaner();
                        }
                        ackStartMessage(params, workOngoing);
                        ackStartMessage(params, workOngoing);
                    } catch (Exception e) {
                    } catch (Exception e) {
                        Log.e(TAG, "Error while executing job: " + params.getJobId());
                        Log.e(TAG, "Error while executing job: " + params.getJobId());
@@ -190,6 +196,9 @@ public abstract class JobServiceEngine {
                    IJobCallback callback = params.getCallback();
                    IJobCallback callback = params.getCallback();
                    if (callback != null) {
                    if (callback != null) {
                        try {
                        try {
                            if (Flags.cleanupEmptyJobs()) {
                                params.disableCleaner();
                            }
                            callback.jobFinished(params.getJobId(), needsReschedule);
                            callback.jobFinished(params.getJobId(), needsReschedule);
                        } catch (RemoteException e) {
                        } catch (RemoteException e) {
                            Log.e(TAG, "Error reporting job finish to system: binder has gone" +
                            Log.e(TAG, "Error reporting job finish to system: binder has gone" +
+36 −0
Original line number Original line Diff line number Diff line
@@ -129,6 +129,8 @@ public final class JobServiceContext implements ServiceConnection {
    private static final String[] VERB_STRINGS = {
    private static final String[] VERB_STRINGS = {
            "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
            "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
    };
    };
    private static final String TRACE_JOB_FORCE_FINISHED_PREFIX = "forceJobFinished:";
    private static final String TRACE_JOB_FORCE_FINISHED_DELIMITER = "#";


    // States that a job occupies while interacting with the client.
    // States that a job occupies while interacting with the client.
    static final int VERB_BINDING = 0;
    static final int VERB_BINDING = 0;
@@ -291,6 +293,11 @@ public final class JobServiceContext implements ServiceConnection {
            doJobFinished(this, jobId, reschedule);
            doJobFinished(this, jobId, reschedule);
        }
        }


        @Override
        public void forceJobFinished(int jobId) {
            doForceJobFinished(this, jobId);
        }

        @Override
        @Override
        public void updateEstimatedNetworkBytes(int jobId, JobWorkItem item,
        public void updateEstimatedNetworkBytes(int jobId, JobWorkItem item,
                long downloadBytes, long uploadBytes) {
                long downloadBytes, long uploadBytes) {
@@ -762,6 +769,35 @@ public final class JobServiceContext implements ServiceConnection {
        }
        }
    }
    }


    /**
     * This method just adds traces to evaluate jobs that leak jobparameters at the client.
     * It does not stop the job.
     */
    void doForceJobFinished(JobCallback cb, int jobId) {
        final long ident = Binder.clearCallingIdentity();
        try {
            final JobStatus executing;
            synchronized (mLock) {
                // not the current job, presumably it has finished in some way already
                if (!verifyCallerLocked(cb)) {
                    return;
                }

                executing = getRunningJobLocked();
            }
            if (executing != null && jobId == executing.getJobId()) {
                final StringBuilder stateSuffix = new StringBuilder();
                stateSuffix.append(TRACE_JOB_FORCE_FINISHED_PREFIX);
                stateSuffix.append(executing.getBatteryName());
                stateSuffix.append(TRACE_JOB_FORCE_FINISHED_DELIMITER);
                stateSuffix.append(executing.getJobId());
                Trace.instant(Trace.TRACE_TAG_POWER, stateSuffix.toString());
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

    private void doAcknowledgeGetTransferredDownloadBytesMessage(JobCallback cb, int jobId,
    private void doAcknowledgeGetTransferredDownloadBytesMessage(JobCallback cb, int jobId,
            int workId, @BytesLong long transferredBytes) {
            int workId, @BytesLong long transferredBytes) {
        // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
        // TODO(255393346): Make sure apps call this appropriately and monitor for abuse
Loading