chromium/components/component_updater/android/java/src/org/chromium/components/component_updater/UpdateScheduler.java

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.component_updater;

import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;

import org.chromium.base.ContextUtils;
import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback;
import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
import org.chromium.components.background_task_scheduler.TaskIds;
import org.chromium.components.background_task_scheduler.TaskInfo;

/** Java-side implementation of the component update scheduler using the BackgroundTaskScheduler. */
@JNINamespace("component_updater")
public class UpdateScheduler {
    private static UpdateScheduler sInstance;
    private TaskFinishedCallback mTaskFinishedCallback;
    private long mNativeScheduler;
    private long mDelayMs;

    @CalledByNative
    /* package */ static UpdateScheduler getInstance() {
        if (sInstance == null) {
            sInstance = new UpdateScheduler();
        }
        return sInstance;
    }

    /* package */ void onStartTaskBeforeNativeLoaded(TaskFinishedCallback callback) {
        mTaskFinishedCallback = callback;
    }

    /* package */ void onStartTaskWithNative() {
        assert mNativeScheduler != 0;
        UpdateSchedulerJni.get().onStartTask(mNativeScheduler, UpdateScheduler.this);
    }

    /* package */ void onStopTask() {
        if (mNativeScheduler != 0) {
            UpdateSchedulerJni.get().onStopTask(mNativeScheduler, UpdateScheduler.this);
        }
        mTaskFinishedCallback = null;
        scheduleInternal(mDelayMs);
    }

    private UpdateScheduler() {}

    private void scheduleInternal(long delayMs) {
        // Skip re-scheduling if we are currently running the update task. Otherwise, the current
        // update tasks would be cancelled.
        if (mTaskFinishedCallback != null) return;

        TaskInfo taskInfo =
                TaskInfo.createOneOffTask(
                                TaskIds.COMPONENT_UPDATE_JOB_ID, delayMs, Integer.MAX_VALUE)
                        .setUpdateCurrent(true)
                        .setRequiredNetworkType(TaskInfo.NetworkType.UNMETERED)
                        .setIsPersisted(true)
                        .build();
        BackgroundTaskSchedulerFactory.getScheduler()
                .schedule(ContextUtils.getApplicationContext(), taskInfo);
    }

    @CalledByNative
    private void schedule(long initialDelayMs, long delayMs) {
        mDelayMs = delayMs;
        scheduleInternal(initialDelayMs);
    }

    @CalledByNative
    private void finishTask(boolean reschedule) {
        assert mTaskFinishedCallback != null;
        mTaskFinishedCallback.taskFinished(false);
        mTaskFinishedCallback = null;
        if (reschedule) {
            scheduleInternal(mDelayMs);
        }
    }

    @CalledByNative
    private void setNativeScheduler(long nativeScheduler) {
        mNativeScheduler = nativeScheduler;
    }

    @CalledByNative
    private void cancelTask() {
        BackgroundTaskSchedulerFactory.getScheduler()
                .cancel(ContextUtils.getApplicationContext(), TaskIds.COMPONENT_UPDATE_JOB_ID);
    }

    @NativeMethods
    interface Natives {
        void onStartTask(long nativeBackgroundTaskUpdateScheduler, UpdateScheduler caller);

        void onStopTask(long nativeBackgroundTaskUpdateScheduler, UpdateScheduler caller);
    }
}