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

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

Merge "Persist JobWorkItems."

parents 9882b54f fcb65e5d
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -924,7 +924,8 @@ public class JobInfo implements Parcelable {
    @SuppressWarnings("UnsafeParcelApi")
    private JobInfo(Parcel in) {
        jobId = in.readInt();
        extras = in.readPersistableBundle();
        final PersistableBundle persistableExtras = in.readPersistableBundle();
        extras = persistableExtras != null ? persistableExtras : PersistableBundle.EMPTY;
        transientExtras = in.readBundle();
        if (in.readInt() != 0) {
            clipData = ClipData.CREATOR.createFromParcel(in);
+11 −1
Original line number Diff line number Diff line
@@ -337,7 +337,7 @@ public abstract class JobScheduler {
     * but there are situations where it may get this wrong and count the JobInfo as changing.
     * (That said, you should be relatively safe with a simple set of consistent data in these
     * fields.)  You should never use {@link JobInfo.Builder#setClipData(ClipData, int)} with
     * work you are enqueue, since currently this will always be treated as a different JobInfo,
     * work you are enqueuing, since currently this will always be treated as a different JobInfo,
     * even if the ClipData contents are exactly the same.</p>
     *
     * <p class="caution"><strong>Note:</strong> Scheduling a job can have a high cost, even if it's
@@ -345,6 +345,16 @@ public abstract class JobScheduler {
     * version {@link android.os.Build.VERSION_CODES#Q}. As such, the system may throttle calls to
     * this API if calls are made too frequently in a short amount of time.
     *
     * <p class="caution"><strong>Note:</strong> Prior to Android version
     * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, JobWorkItems could not be persisted.
     * Apps were not allowed to enqueue JobWorkItems with persisted jobs and the system would throw
     * an {@link IllegalArgumentException} if they attempted to do so. Starting with
     * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE},
     * JobWorkItems can be persisted alongside the hosting job.
     * However, Intents cannot be persisted. Set a {@link PersistableBundle} using
     * {@link JobWorkItem.Builder#setExtras(PersistableBundle)} for any information that needs
     * to be persisted.
     *
     * <p>Note: The JobService component needs to be enabled in order to successfully schedule a
     * job.
     *
+185 −0
Original line number Diff line number Diff line
@@ -19,20 +19,33 @@ package android.app.job;
import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN;

import android.annotation.BytesLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.compat.Compatibility;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Intent;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PersistableBundle;

/**
 * A unit of work that can be enqueued for a job using
 * {@link JobScheduler#enqueue JobScheduler.enqueue}.  See
 * {@link JobParameters#dequeueWork() JobParameters.dequeueWork} for more details.
 *
 * <p class="caution"><strong>Note:</strong> Prior to Android version
 * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, JobWorkItems could not be persisted.
 * Apps were not allowed to enqueue JobWorkItems with persisted jobs and the system would throw
 * an {@link IllegalArgumentException} if they attempted to do so. Starting with
 * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, JobWorkItems can be persisted alongside
 * the hosting job. However, Intents cannot be persisted. Set a {@link PersistableBundle} using
 * {@link Builder#setExtras(PersistableBundle)} for any information that needs to be persisted.
 */
final public class JobWorkItem implements Parcelable {
    @NonNull
    private final PersistableBundle mExtras;
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
    final Intent mIntent;
    private final long mNetworkDownloadBytes;
@@ -49,6 +62,10 @@ final public class JobWorkItem implements Parcelable {
     * Create a new piece of work, which can be submitted to
     * {@link JobScheduler#enqueue JobScheduler.enqueue}.
     *
     * <p>
     * Intents cannot be used for persisted JobWorkItems.
     * Use {@link Builder#setExtras(PersistableBundle)} instead for persisted JobWorkItems.
     *
     * @param intent The general Intent describing this work.
     */
    public JobWorkItem(Intent intent) {
@@ -62,6 +79,10 @@ final public class JobWorkItem implements Parcelable {
     * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
     * details about how to estimate network traffic.
     *
     * <p>
     * Intents cannot be used for persisted JobWorkItems.
     * Use {@link Builder#setExtras(PersistableBundle)} instead for persisted JobWorkItems.
     *
     * @param intent The general Intent describing this work.
     * @param downloadBytes The estimated size of network traffic that will be
     *            downloaded by this job work item, in bytes.
@@ -79,6 +100,10 @@ final public class JobWorkItem implements Parcelable {
     * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
     * details about how to estimate network traffic.
     *
     * <p>
     * Intents cannot be used for persisted JobWorkItems.
     * Use {@link Builder#setExtras(PersistableBundle)} instead for persisted JobWorkItems.
     *
     * @param intent            The general Intent describing this work.
     * @param downloadBytes     The estimated size of network traffic that will be
     *                          downloaded by this job work item, in bytes.
@@ -89,6 +114,7 @@ final public class JobWorkItem implements Parcelable {
     */
    public JobWorkItem(@Nullable Intent intent, @BytesLong long downloadBytes,
            @BytesLong long uploadBytes, @BytesLong long minimumChunkBytes) {
        mExtras = PersistableBundle.EMPTY;
        mIntent = intent;
        mNetworkDownloadBytes = downloadBytes;
        mNetworkUploadBytes = uploadBytes;
@@ -96,6 +122,25 @@ final public class JobWorkItem implements Parcelable {
        enforceValidity(Compatibility.isChangeEnabled(JobInfo.REJECT_NEGATIVE_NETWORK_ESTIMATES));
    }

    private JobWorkItem(@NonNull Builder builder) {
        mDeliveryCount = builder.mDeliveryCount;
        mExtras = builder.mExtras.deepCopy();
        mIntent = builder.mIntent;
        mNetworkDownloadBytes = builder.mNetworkDownloadBytes;
        mNetworkUploadBytes = builder.mNetworkUploadBytes;
        mMinimumChunkBytes = builder.mMinimumNetworkChunkBytes;
    }

    /**
     * Return the extras associated with this work.
     *
     * @see Builder#setExtras(PersistableBundle)
     */
    @NonNull
    public PersistableBundle getExtras() {
        return mExtras;
    }

    /**
     * Return the Intent associated with this work.
     */
@@ -176,6 +221,7 @@ final public class JobWorkItem implements Parcelable {
    /**
     * @hide
     */
    @Nullable
    public Object getGrants() {
        return mGrants;
    }
@@ -186,6 +232,8 @@ final public class JobWorkItem implements Parcelable {
        sb.append(mWorkId);
        sb.append(" intent=");
        sb.append(mIntent);
        sb.append(" extras=");
        sb.append(mExtras);
        if (mNetworkDownloadBytes != NETWORK_BYTES_UNKNOWN) {
            sb.append(" downloadBytes=");
            sb.append(mNetworkDownloadBytes);
@@ -206,6 +254,140 @@ final public class JobWorkItem implements Parcelable {
        return sb.toString();
    }

    /**
     * Builder class for constructing {@link JobWorkItem} objects.
     */
    public static final class Builder {
        private int mDeliveryCount;
        private PersistableBundle mExtras = PersistableBundle.EMPTY;
        private Intent mIntent;
        private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
        private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
        private long mMinimumNetworkChunkBytes = NETWORK_BYTES_UNKNOWN;

        /**
         * Initialize a new Builder to construct a {@link JobWorkItem} object.
         */
        public Builder() {
        }

        /**
         * @see JobWorkItem#getDeliveryCount()
         * @return This object for method chaining
         * @hide
         */
        @NonNull
        public Builder setDeliveryCount(int deliveryCount) {
            mDeliveryCount = deliveryCount;
            return this;
        }

        /**
         * Set optional extras. This can be persisted, so we only allow primitive types.
         * @param extras Bundle containing extras you want the scheduler to hold on to for you.
         * @return This object for method chaining
         * @see JobWorkItem#getExtras()
         */
        @NonNull
        public Builder setExtras(@NonNull PersistableBundle extras) {
            if (extras == null) {
                throw new IllegalArgumentException("extras cannot be null");
            }
            mExtras = extras;
            return this;
        }

        /**
         * Set an intent with information relevant to this work item.
         *
         * <p>
         * Intents cannot be used for persisted JobWorkItems.
         * Use {@link #setExtras(PersistableBundle)} instead for persisted JobWorkItems.
         *
         * @return This object for method chaining
         * @see JobWorkItem#getIntent()
         */
        @NonNull
        public Builder setIntent(@NonNull Intent intent) {
            mIntent = intent;
            return this;
        }

        /**
         * Set the estimated size of network traffic that will be performed for this work item,
         * in bytes.
         *
         * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
         * details about how to estimate network traffic.
         *
         * @param downloadBytes The estimated size of network traffic that will be
         *                      downloaded for this work item, in bytes.
         * @param uploadBytes   The estimated size of network traffic that will be
         *                      uploaded for this work item, in bytes.
         * @return This object for method chaining
         * @see JobInfo.Builder#setEstimatedNetworkBytes(long, long)
         * @see JobWorkItem#getEstimatedNetworkDownloadBytes()
         * @see JobWorkItem#getEstimatedNetworkUploadBytes()
         */
        @NonNull
        @SuppressLint("MissingGetterMatchingBuilder")
        public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
                @BytesLong long uploadBytes) {
            if (downloadBytes != NETWORK_BYTES_UNKNOWN && downloadBytes < 0) {
                throw new IllegalArgumentException(
                        "Invalid network download bytes: " + downloadBytes);
            }
            if (uploadBytes != NETWORK_BYTES_UNKNOWN && uploadBytes < 0) {
                throw new IllegalArgumentException("Invalid network upload bytes: " + uploadBytes);
            }
            mNetworkDownloadBytes = downloadBytes;
            mNetworkUploadBytes = uploadBytes;
            return this;
        }

        /**
         * Set the minimum size of non-resumable network traffic this work item requires, in bytes.
         * When the upload or download can be easily paused and resumed, use this to set the
         * smallest size that must be transmitted between start and stop events to be considered
         * successful. If the transfer cannot be paused and resumed, then this should be the sum
         * of the values provided to {@link #setEstimatedNetworkBytes(long, long)}.
         *
         * See {@link JobInfo.Builder#setMinimumNetworkChunkBytes(long)} for
         * details about how to set the minimum chunk.
         *
         * @param chunkSizeBytes The smallest piece of data that cannot be easily paused and
         *                       resumed, in bytes.
         * @return This object for method chaining
         * @see JobInfo.Builder#setMinimumNetworkChunkBytes(long)
         * @see JobWorkItem#getMinimumNetworkChunkBytes()
         * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long, long)
         */
        @NonNull
        public Builder setMinimumNetworkChunkBytes(@BytesLong long chunkSizeBytes) {
            if (chunkSizeBytes != NETWORK_BYTES_UNKNOWN && chunkSizeBytes <= 0) {
                throw new IllegalArgumentException("Minimum chunk size must be positive");
            }
            mMinimumNetworkChunkBytes = chunkSizeBytes;
            return this;
        }

        /**
         * @return The JobWorkItem object to hand to the JobScheduler. This object is immutable.
         */
        @NonNull
        public JobWorkItem build() {
            return build(Compatibility.isChangeEnabled(JobInfo.REJECT_NEGATIVE_NETWORK_ESTIMATES));
        }

        /** @hide */
        @NonNull
        public JobWorkItem build(boolean rejectNegativeNetworkEstimates) {
            JobWorkItem jobWorkItem = new JobWorkItem(this);
            jobWorkItem.enforceValidity(rejectNegativeNetworkEstimates);
            return jobWorkItem;
        }
    }

    /**
     * @hide
     */
@@ -249,6 +431,7 @@ final public class JobWorkItem implements Parcelable {
        } else {
            out.writeInt(0);
        }
        out.writePersistableBundle(mExtras);
        out.writeLong(mNetworkDownloadBytes);
        out.writeLong(mNetworkUploadBytes);
        out.writeLong(mMinimumChunkBytes);
@@ -274,6 +457,8 @@ final public class JobWorkItem implements Parcelable {
        } else {
            mIntent = null;
        }
        final PersistableBundle extras = in.readPersistableBundle();
        mExtras = extras != null ? extras : PersistableBundle.EMPTY;
        mNetworkDownloadBytes = in.readLong();
        mNetworkUploadBytes = in.readLong();
        mMinimumChunkBytes = in.readLong();
+9 −3
Original line number Diff line number Diff line
@@ -1406,6 +1406,7 @@ public class JobSchedulerService extends com.android.server.SystemService
                if (toCancel.getJob().equals(job)) {

                    toCancel.enqueueWorkLocked(work);
                    mJobs.touchJob(toCancel);

                    // If any of work item is enqueued when the source is in the foreground,
                    // exempt the entire job.
@@ -3775,6 +3776,14 @@ public class JobSchedulerService extends com.android.server.SystemService
                        }
                    }
                }
                if (job.isPersisted()) {
                    // Intent.saveToXml() doesn't persist everything, so just reject all
                    // JobWorkItems with Intents to be safe/predictable.
                    if (jobWorkItem.getIntent() != null) {
                        throw new IllegalArgumentException(
                                "Cannot persist JobWorkItems with Intents");
                    }
                }
            }
            return JobScheduler.RESULT_SUCCESS;
        }
@@ -3837,9 +3846,6 @@ public class JobSchedulerService extends com.android.server.SystemService
            final int userId = UserHandle.getUserId(uid);

            enforceValidJobRequest(uid, job);
            if (job.isPersisted()) {
                throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
            }
            if (work == null) {
                throw new NullPointerException("work is null");
            }
+4 −0
Original line number Diff line number Diff line
@@ -615,6 +615,9 @@ public final class JobServiceContext implements ServiceConnection {
                            "last work dequeued");
                    // This will finish the job.
                    doCallbackLocked(false, "last work dequeued");
                } else {
                    // Delivery count has been updated, so persist JobWorkItem change.
                    mService.mJobs.touchJob(mRunningJob);
                }
                return work;
            }
@@ -632,6 +635,7 @@ public final class JobServiceContext implements ServiceConnection {
                    // Exception-throwing-can down the road to JobParameters.completeWork >:(
                    return true;
                }
                mService.mJobs.touchJob(mRunningJob);
                return mRunningJob.completeWorkLocked(workId);
            }
        } finally {
Loading