Reschedule works or alarms if needed when device boot

Test: atest DeviceLockControllerRoboTests
Bug: 288937639

Change-Id: I30a60501913ea2f043e93efcb03741a5cb6ca140
(cherry picked from commit adfa5aff29f084a07014228595a201f9729d7317)
diff --git a/DeviceLockController/src/com/android/devicelockcontroller/AbstractDeviceLockControllerScheduler.java b/DeviceLockController/src/com/android/devicelockcontroller/AbstractDeviceLockControllerScheduler.java
index 70c3509..8a0ce85 100644
--- a/DeviceLockController/src/com/android/devicelockcontroller/AbstractDeviceLockControllerScheduler.java
+++ b/DeviceLockController/src/com/android/devicelockcontroller/AbstractDeviceLockControllerScheduler.java
@@ -30,16 +30,16 @@
     public abstract void correctExpectedToRunTime(Duration delta);
 
     /**
+     * Reschedule any works / alarms that were scheduled previously but got pushed back or canceled.
+     */
+    public abstract void rescheduleIfNeeded();
+
+    /**
      * Schedule an alarm to resume the provision flow.
      */
     public abstract void scheduleResumeProvisionAlarm();
 
     /**
-     * Reschedule the alarm to resume provision based on the stored expected to run time.
-     */
-    public abstract void rescheduleResumeProvisionAlarm();
-
-    /**
      * Schedule the initial check-in work when device first boot.
      */
     public abstract void scheduleInitialCheckInWork();
@@ -52,27 +52,12 @@
     public abstract void scheduleRetryCheckInWork(Duration delay);
 
     /**
-     * Reschedule retry check-in work based on the stored expected to run time.
-     */
-    public abstract void rescheduleRetryCheckInWork();
-
-    /**
      * Schedule an alarm to perform next provision failed step with the default delay.
      */
     public abstract void scheduleNextProvisionFailedStepAlarm();
 
     /**
-     * Reschedule the alarm to perform next provision failed step based on the stored time.
-     */
-    public abstract void rescheduleNextProvisionFailedStepAlarm();
-
-    /**
      * Schedule an alarm to factory reset the device in case of provision is failed.
      */
     public abstract void scheduleResetDeviceAlarm();
-
-    /**
-     * Reschedule the alarm to factory reset device based on stored timestamp.
-     */
-    public abstract void rescheduleResetDeviceAlarm();
 }
diff --git a/DeviceLockController/src/com/android/devicelockcontroller/DeviceLockControllerScheduler.java b/DeviceLockController/src/com/android/devicelockcontroller/DeviceLockControllerScheduler.java
index 6c98247..fc97a66 100644
--- a/DeviceLockController/src/com/android/devicelockcontroller/DeviceLockControllerScheduler.java
+++ b/DeviceLockController/src/com/android/devicelockcontroller/DeviceLockControllerScheduler.java
@@ -16,6 +16,7 @@
 
 package com.android.devicelockcontroller;
 
+import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.PROVISION_FAILED;
 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.PROVISION_PAUSED;
 import static com.android.devicelockcontroller.policy.DeviceStateController.DeviceState.UNPROVISIONED;
 import static com.android.devicelockcontroller.storage.GlobalParametersClient.getInstance;
@@ -123,6 +124,30 @@
     }
 
     @Override
+    public void rescheduleIfNeeded() {
+        DeviceStateController stateController =
+                ((PolicyObjectsInterface) mContext.getApplicationContext()).getStateController();
+
+        switch (stateController.getState()) {
+            case UNPROVISIONED:
+                rescheduleRetryCheckInWork();
+                break;
+            case PROVISION_PAUSED:
+                rescheduleResumeProvisionAlarm();
+                break;
+            case PROVISION_FAILED:
+                rescheduleNextProvisionFailedStepAlarm();
+                // Reset device is the last step in provision failed flow. We call this API
+                // regardless, because the alarm will only be rescheduled if it has been
+                // scheduled previously.
+                rescheduleResetDeviceAlarm();
+                break;
+            default:
+                // no-op for other states.
+        }
+    }
+
+    @Override
     public void scheduleResumeProvisionAlarm() {
         Duration delay = Duration.ofMinutes(PROVISION_PAUSED_MINUTES_DEFAULT);
         if (Build.isDebuggable()) {
@@ -148,13 +173,14 @@
                 }, MoreExecutors.directExecutor());
     }
 
-    @Override
-    public void rescheduleResumeProvisionAlarm() {
+    @VisibleForTesting
+    void rescheduleResumeProvisionAlarm() {
         Futures.addCallback(
                 getInstance().getResumeProvisionTimeMillis(),
                 new FutureCallback<>() {
                     @Override
                     public void onSuccess(Long resumeProvisionTimeMillis) {
+                        if (resumeProvisionTimeMillis <= 0) return;
                         Duration delay = Duration.between(
                                 Instant.now(mClock),
                                 Instant.ofEpochMilli(resumeProvisionTimeMillis));
@@ -198,13 +224,14 @@
                 }, MoreExecutors.directExecutor());
     }
 
-    @Override
-    public void rescheduleRetryCheckInWork() {
+    @VisibleForTesting
+    void rescheduleRetryCheckInWork() {
         Futures.addCallback(
                 getInstance().getNextCheckInTimeMillis(),
                 new FutureCallback<>() {
                     @Override
                     public void onSuccess(Long nextCheckInTimeMillis) {
+                        if (nextCheckInTimeMillis <= 0) return;
                         Duration delay = Duration.between(
                                 Instant.now(mClock),
                                 Instant.ofEpochMilli(nextCheckInTimeMillis));
@@ -247,12 +274,13 @@
                 }, MoreExecutors.directExecutor());
     }
 
-    @Override
-    public void rescheduleNextProvisionFailedStepAlarm() {
+    @VisibleForTesting
+    void rescheduleNextProvisionFailedStepAlarm() {
         Futures.addCallback(getInstance().getNextProvisionFailedStepTimeMills(),
                 new FutureCallback<>() {
                     @Override
                     public void onSuccess(Long timestamp) {
+                        if (timestamp <= 0) return;
                         Duration delay = Duration.between(
                                 Instant.now(mClock),
                                 Instant.ofEpochMilli(timestamp));
@@ -297,12 +325,13 @@
                 }, MoreExecutors.directExecutor());
     }
 
-    @Override
-    public void rescheduleResetDeviceAlarm() {
+    @VisibleForTesting
+    void rescheduleResetDeviceAlarm() {
         Futures.addCallback(getInstance().getResetDeviceTimeMillis(),
                 new FutureCallback<>() {
                     @Override
                     public void onSuccess(Long timestamp) {
+                        if (timestamp <= 0) return;
                         Duration delay = Duration.between(
                                 Instant.now(mClock),
                                 Instant.ofEpochMilli(timestamp));
diff --git a/DeviceLockController/src/com/android/devicelockcontroller/debug/DeviceLockCommandReceiver.java b/DeviceLockController/src/com/android/devicelockcontroller/debug/DeviceLockCommandReceiver.java
index 95d51a8..49dce60 100644
--- a/DeviceLockController/src/com/android/devicelockcontroller/debug/DeviceLockCommandReceiver.java
+++ b/DeviceLockController/src/com/android/devicelockcontroller/debug/DeviceLockCommandReceiver.java
@@ -204,7 +204,10 @@
         AlarmManager alarmManager = Objects.requireNonNull(
                 context.getSystemService(AlarmManager.class));
         alarmManager.cancel(
-                ResetDeviceReceiver.getResetDevicePendingIntent(context));
+                PendingIntent.getBroadcast(
+                        context, /* ignored */ 0,
+                        new Intent(context, ResetDeviceReceiver.class),
+                        PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE));
         alarmManager.cancel(PendingIntent.getBroadcast(
                 context, /* ignored */ 0,
                 new Intent(context, NextProvisionFailedStepReceiver.class),
diff --git a/DeviceLockController/src/com/android/devicelockcontroller/provision/worker/PauseProvisioningWorker.java b/DeviceLockController/src/com/android/devicelockcontroller/provision/worker/PauseProvisioningWorker.java
index 011fc09..9e3f697 100644
--- a/DeviceLockController/src/com/android/devicelockcontroller/provision/worker/PauseProvisioningWorker.java
+++ b/DeviceLockController/src/com/android/devicelockcontroller/provision/worker/PauseProvisioningWorker.java
@@ -41,7 +41,8 @@
 import com.google.common.util.concurrent.MoreExecutors;
 
 /**
- * A worker class dedicated to request pause of provisioning for device lock program.
+ * Despite the naming, this worker class is only to report provision has been paused by user to
+ * backend server.
  */
 public final class PauseProvisioningWorker extends AbstractCheckInWorker {
     private static final String KEY_PAUSE_DEVICE_PROVISIONING_REASON =
diff --git a/DeviceLockController/src/com/android/devicelockcontroller/receivers/ResetDeviceReceiver.java b/DeviceLockController/src/com/android/devicelockcontroller/receivers/ResetDeviceReceiver.java
index d1b4394..de8c3e6 100644
--- a/DeviceLockController/src/com/android/devicelockcontroller/receivers/ResetDeviceReceiver.java
+++ b/DeviceLockController/src/com/android/devicelockcontroller/receivers/ResetDeviceReceiver.java
@@ -16,7 +16,6 @@
 
 package com.android.devicelockcontroller.receivers;
 
-import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -28,16 +27,6 @@
  */
 public final class ResetDeviceReceiver extends BroadcastReceiver {
 
-    /**
-     * Get a {@link PendingIntent} for resetting the device.
-     */
-    public static PendingIntent getResetDevicePendingIntent(Context context) {
-        return PendingIntent.getBroadcast(
-                context, /* ignored */ 0,
-                new Intent(context, ResetDeviceReceiver.class),
-                PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE);
-    }
-
     @Override
     public void onReceive(Context context, Intent intent) {
         if (!ResetDeviceReceiver.class.getName().equals(intent.getComponent().getClassName())) {
diff --git a/DeviceLockController/src/com/android/devicelockcontroller/receivers/SingleUserBootCompletedReceiver.java b/DeviceLockController/src/com/android/devicelockcontroller/receivers/SingleUserBootCompletedReceiver.java
index e529562..9fd8dda 100644
--- a/DeviceLockController/src/com/android/devicelockcontroller/receivers/SingleUserBootCompletedReceiver.java
+++ b/DeviceLockController/src/com/android/devicelockcontroller/receivers/SingleUserBootCompletedReceiver.java
@@ -78,8 +78,10 @@
             return;
         }
 
+        DeviceLockControllerScheduler scheduler = new DeviceLockControllerScheduler(context);
         checkInIfNeeded(
                 ((PolicyObjectsInterface) context.getApplicationContext()).getStateController(),
-                new DeviceLockControllerScheduler(context));
+                scheduler);
+        scheduler.rescheduleIfNeeded();
     }
 }