Snap for 4829593 from 4e496f7ea9bc3d2535219ce0861b4b2fc4397545 to pi-release

Change-Id: I1abd50e0ebb6db38bd732947c01dc4964c97f8dd
diff --git a/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java b/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java
index c633c46..323757a 100644
--- a/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java
+++ b/work/workmanager/src/androidTest/java/androidx/work/impl/utils/ForceStopRunnableTest.java
@@ -18,11 +18,11 @@
 
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.content.ComponentName;
@@ -48,6 +48,7 @@
     private WorkManagerImpl mWorkManager;
     private WorkDatabase mWorkDatabase;
     private WorkSpecDao mWorkSpecDao;
+    private Preferences mPreferences;
     private ForceStopRunnable mRunnable;
 
     @Before
@@ -56,8 +57,10 @@
         mWorkManager = mock(WorkManagerImpl.class);
         mWorkDatabase = mock(WorkDatabase.class);
         mWorkSpecDao = mock(WorkSpecDao.class);
+        mPreferences = mock(Preferences.class);
         when(mWorkManager.getWorkDatabase()).thenReturn(mWorkDatabase);
         when(mWorkDatabase.workSpecDao()).thenReturn(mWorkSpecDao);
+        when(mWorkManager.getPreferences()).thenReturn(mPreferences);
         mRunnable = new ForceStopRunnable(mContext, mWorkManager);
     }
 
@@ -73,6 +76,7 @@
     @Test
     public void testReschedulesOnForceStop() {
         ForceStopRunnable runnable = spy(mRunnable);
+        when(runnable.shouldCancelPersistedJobs()).thenReturn(false);
         when(runnable.isForceStopped()).thenReturn(true);
         runnable.run();
         verify(mWorkManager, times(1)).rescheduleEligibleWork();
@@ -81,8 +85,28 @@
     @Test
     public void test_doNothingWhenNotForceStopped() {
         ForceStopRunnable runnable = spy(mRunnable);
+        when(runnable.shouldCancelPersistedJobs()).thenReturn(false);
         when(runnable.isForceStopped()).thenReturn(false);
         runnable.run();
-        verifyNoMoreInteractions(mWorkManager);
+        verify(mWorkManager, times(0)).rescheduleEligibleWork();
+    }
+
+    @Test
+    public void test_cancelAllJobSchedulerJobs() {
+        ForceStopRunnable runnable = spy(mRunnable);
+        doNothing().when(runnable).cancelAllInJobScheduler();
+        when(runnable.shouldCancelPersistedJobs()).thenReturn(true);
+        runnable.run();
+        verify(runnable, times(1)).cancelAllInJobScheduler();
+        verify(mPreferences, times(1)).setMigratedPersistedJobs();
+    }
+
+    @Test
+    public void test_doNothingWhenThereIsNothingToCancel() {
+        ForceStopRunnable runnable = spy(mRunnable);
+        doNothing().when(runnable).cancelAllInJobScheduler();
+        when(runnable.shouldCancelPersistedJobs()).thenReturn(false);
+        runnable.run();
+        verify(runnable, times(0)).cancelAllInJobScheduler();
     }
 }
diff --git a/work/workmanager/src/main/java/androidx/work/OneTimeWorkRequest.java b/work/workmanager/src/main/java/androidx/work/OneTimeWorkRequest.java
index 333335f..57849be 100644
--- a/work/workmanager/src/main/java/androidx/work/OneTimeWorkRequest.java
+++ b/work/workmanager/src/main/java/androidx/work/OneTimeWorkRequest.java
@@ -62,7 +62,6 @@
         return workList;
     }
 
-
     OneTimeWorkRequest(Builder builder) {
         super(builder.mId, builder.mWorkSpec, builder.mTags);
     }
diff --git a/work/workmanager/src/main/java/androidx/work/WorkRequest.java b/work/workmanager/src/main/java/androidx/work/WorkRequest.java
index 2232444..c1eae64 100644
--- a/work/workmanager/src/main/java/androidx/work/WorkRequest.java
+++ b/work/workmanager/src/main/java/androidx/work/WorkRequest.java
@@ -53,6 +53,10 @@
     private @NonNull WorkSpec mWorkSpec;
     private @NonNull Set<String> mTags;
 
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
     protected WorkRequest(@NonNull UUID id, @NonNull WorkSpec workSpec, @NonNull Set<String> tags) {
         mId = id;
         mWorkSpec = workSpec;
diff --git a/work/workmanager/src/main/java/androidx/work/Worker.java b/work/workmanager/src/main/java/androidx/work/Worker.java
index 58a5c22..87e4ca4 100644
--- a/work/workmanager/src/main/java/androidx/work/Worker.java
+++ b/work/workmanager/src/main/java/androidx/work/Worker.java
@@ -168,30 +168,30 @@
     public abstract @NonNull Result doWork();
 
     /**
-     * Call this method to pass an {@link Data} object to {@link Worker} that is
-     * dependent on this one.
+     * Call this method to pass a {@link Data} object as the output of this {@link Worker}.  This
+     * result can be observed and passed to Workers that are dependent on this one.
      *
-     * Note that if there are multiple {@link Worker}s that contribute to the target, the
-     * Data will be merged together, so it is up to the developer to make sure that keys are
-     * unique.  New values and types will clobber old values and types, and if there are multiple
-     * parent Workers of a child Worker, the order of clobbering may not be deterministic.
-     *
-     * This method is invoked after {@link #doWork()} returns {@link Result#SUCCESS}
-     * and there are chained jobs available.
-     *
+     * In cases like where two or more {@link OneTimeWorkRequest}s share a dependent WorkRequest,
+     * their Data will be merged together using an {@link InputMerger}.  The default InputMerger is
+     * {@link OverwritingInputMerger}, unless otherwise specified using the
+     * {@link OneTimeWorkRequest.Builder#setInputMerger(Class)} method.
+     * <p>
+     * This method is invoked after {@link #doWork()} returns {@link Result#SUCCESS} or
+     * {@link Result#FAILURE}.
+     * <p>
      * For example, if you had this structure:
-     *
+     * <pre>
      * {@code WorkManager.getInstance(context)
-     *             .enqueueWithDefaults(WorkerA.class, WorkerB.class)
-     *             .then(WorkerC.class)
-     *             .enqueue()}
+     *             .beginWith(workRequestA, workRequestB)
+     *             .then(workRequestC)
+     *             .enqueue()}</pre>
      *
-     * This method would be called for both WorkerA and WorkerB after their successful completion,
-     * modifying the input Data for WorkerC.
+     * This method would be called for both {@code workRequestA} and {@code workRequestB} after
+     * their completion, modifying the input Data for {@code workRequestC}.
      *
      * @param outputData An {@link Data} object that will be merged into the input Data of any
-     *                   OneTimeWorkRequest that is dependent on this one, or {@code null} if there
-     *                   is nothing to contribute
+     *                   OneTimeWorkRequest that is dependent on this one, or {@link Data#EMPTY} if
+     *                   there is nothing to contribute
      */
     public final void setOutputData(@NonNull Data outputData) {
         mOutputData = outputData;
@@ -220,7 +220,7 @@
      * <p>
      * Note that it is almost never sufficient to check only this method; its value is only
      * meaningful when {@link #isStopped()} returns {@code true}.
-     * <p>
+     *
      * @return {@code true} if this work operation has been cancelled
      */
     public final boolean isCancelled() {
diff --git a/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java b/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
index 5b659d1..119325c 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/WorkManagerImpl.java
@@ -237,6 +237,15 @@
         return mTaskExecutor;
     }
 
+    /**
+     * @return the {@link Preferences} used by the instance of {@link WorkManager}.
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+    public @NonNull Preferences getPreferences() {
+        return mPreferences;
+    }
+
     @Override
     public void enqueue(@NonNull List<? extends WorkRequest> workRequests) {
         new WorkContinuationImpl(this, workRequests).enqueue();
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
index 5915d66..22c5559 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobInfoConverter.java
@@ -25,6 +25,7 @@
 import android.os.PersistableBundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.RequiresApi;
+import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 
@@ -37,7 +38,10 @@
 
 /**
  * Converts a {@link WorkSpec} into a JobInfo.
+ *
+ * @hide
  */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 @RequiresApi(api = WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL)
 class SystemJobInfoConverter {
     private static final String TAG = "SystemJobInfoConverter";
diff --git a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
index 98aff4e..933e0d8 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobScheduler.java
@@ -20,6 +20,7 @@
 import android.app.job.JobScheduler;
 import android.content.Context;
 import android.os.Build;
+import android.os.PersistableBundle;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
@@ -151,4 +152,26 @@
             }
         }
     }
+
+    /**
+     * Cancels all the jobs owned by {@link androidx.work.WorkManager} in {@link JobScheduler}.
+     */
+    public static void jobSchedulerCancelAll(@NonNull Context context) {
+        JobScheduler jobScheduler = (JobScheduler)
+                context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+        if (jobScheduler != null) {
+            List<JobInfo> jobInfos = jobScheduler.getAllPendingJobs();
+            // Apparently this can be null on API 23?
+            if (jobInfos != null) {
+                for (JobInfo jobInfo : jobInfos) {
+                    PersistableBundle extras = jobInfo.getExtras();
+                    // This is a job scheduled by WorkManager.
+                    if (extras.containsKey(SystemJobInfoConverter.EXTRA_WORK_SPEC_ID)) {
+                        jobScheduler.cancel(jobInfo.getId());
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java b/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
index e9712b3..87de105 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/ForceStopRunnable.java
@@ -20,8 +20,10 @@
 import static android.app.PendingIntent.FLAG_NO_CREATE;
 import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 
+import android.annotation.TargetApi;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
+import android.app.job.JobScheduler;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -32,6 +34,7 @@
 import android.util.Log;
 
 import androidx.work.impl.WorkManagerImpl;
+import androidx.work.impl.background.systemjob.SystemJobScheduler;
 
 import java.util.concurrent.TimeUnit;
 
@@ -64,7 +67,13 @@
 
     @Override
     public void run() {
-        if (isForceStopped()) {
+        if (shouldCancelPersistedJobs()) {
+            cancelAllInJobScheduler();
+            Log.d(TAG, "Migrating persisted jobs.");
+            mWorkManager.rescheduleEligibleWork();
+            // Mark the jobs as migrated.
+            mWorkManager.getPreferences().setMigratedPersistedJobs();
+        } else if (isForceStopped()) {
             Log.d(TAG, "Application was force-stopped, rescheduling.");
             mWorkManager.rescheduleEligibleWork();
         }
@@ -89,6 +98,15 @@
     }
 
     /**
+     * @return {@code true} If persisted jobs in JobScheduler need to be cancelled.
+     */
+    @VisibleForTesting
+    public boolean shouldCancelPersistedJobs() {
+        return Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL
+                && mWorkManager.getPreferences().shouldMigratePersistedJobs();
+    }
+
+    /**
      * @param alarmId The stable alarm id to be used.
      * @param flags   The {@link PendingIntent} flags.
      * @return an instance of the {@link PendingIntent}.
@@ -110,6 +128,15 @@
         return intent;
     }
 
+    /**
+     * Cancels all the persisted jobs in {@link JobScheduler}.
+     */
+    @VisibleForTesting
+    @TargetApi(WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL)
+    public void cancelAllInJobScheduler() {
+        SystemJobScheduler.jobSchedulerCancelAll(mContext);
+    }
+
     private void setAlarm(int alarmId) {
         AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         // Using FLAG_UPDATE_CURRENT, because we only ever want once instance of this alarm.
diff --git a/work/workmanager/src/main/java/androidx/work/impl/utils/Preferences.java b/work/workmanager/src/main/java/androidx/work/impl/utils/Preferences.java
index 762f6b0..49cd262 100644
--- a/work/workmanager/src/main/java/androidx/work/impl/utils/Preferences.java
+++ b/work/workmanager/src/main/java/androidx/work/impl/utils/Preferences.java
@@ -35,6 +35,7 @@
     private static final String PREFERENCES_FILE_NAME = "androidx.work.util.preferences";
 
     private static final String KEY_LAST_CANCEL_ALL_TIME_MS = "last_cancel_all_time_ms";
+    private static final String KEY_MIGRATE_PERSISTED_JOBS = "migrate_persisted_jobs";
 
     private SharedPreferences mSharedPreferences;
 
@@ -68,6 +69,23 @@
     }
 
     /**
+     * @return {@code true} When we should migrate from persisted jobs to non-persisted jobs in
+     * {@link android.app.job.JobScheduler}
+     */
+    public boolean shouldMigratePersistedJobs() {
+        // TODO Remove this before WorkManager 1.0 beta.
+        return mSharedPreferences.getBoolean(KEY_MIGRATE_PERSISTED_JOBS, true);
+    }
+
+    /**
+     * Updates the key which indicates that we have migrated all our persisted jobs in
+     * {@link android.app.job.JobScheduler}.
+     */
+    public void setMigratedPersistedJobs() {
+        mSharedPreferences.edit().putBoolean(KEY_MIGRATE_PERSISTED_JOBS, true).apply();
+    }
+
+    /**
      * A {@link android.arch.lifecycle.LiveData} that responds to changes in
      * {@link SharedPreferences} for the {@code lastCancelAllTime} value.
      */