chromium/components/background_task_scheduler/internal/android/java/src/org/chromium/components/background_task_scheduler/internal/BackgroundTaskSchedulerImpl.java

// Copyright 2017 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.background_task_scheduler.internal;

import android.content.Context;

import org.chromium.base.CommandLine;
import org.chromium.base.ThreadUtils;
import org.chromium.base.TraceEvent;
import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
import org.chromium.components.background_task_scheduler.TaskInfo;

/**
 * This {@link BackgroundTaskScheduler} is the only one used in production code, and it is used to
 * schedule jobs that run in the background.
 *
 * To get an instance of this class, use {@link BackgroundTaskSchedulerFactory#getScheduler()}.
 */
class BackgroundTaskSchedulerImpl implements BackgroundTaskScheduler {
    private static final String SWITCH_IGNORE_BACKGROUND_TASKS = "ignore-background-tasks";

    private final BackgroundTaskSchedulerDelegate mSchedulerDelegate;

    /** Constructor only for {@link BackgroundTaskSchedulerFactory} and internal component tests. */
    BackgroundTaskSchedulerImpl(BackgroundTaskSchedulerDelegate schedulerDelegate) {
        mSchedulerDelegate = schedulerDelegate;
    }

    @Override
    public boolean schedule(Context context, TaskInfo taskInfo) {
        if (CommandLine.getInstance().hasSwitch(SWITCH_IGNORE_BACKGROUND_TASKS)) {
            // When background tasks finish executing, they leave a cached process, which
            // artificially inflates startup metrics that are based on events near to process
            // creation.
            return true;
        }
        try (TraceEvent te =
                TraceEvent.scoped(
                        "BackgroundTaskScheduler.schedule",
                        Integer.toString(taskInfo.getTaskId()))) {
            ThreadUtils.assertOnUiThread();

            SchedulingVisitor schedulingVisitor = new SchedulingVisitor(context, taskInfo);
            taskInfo.getTimingInfo().accept(schedulingVisitor);
            boolean success = schedulingVisitor.getSuccess();
            BackgroundTaskSchedulerUma.getInstance()
                    .reportTaskScheduled(taskInfo.getTaskId(), success);

            // Retain expiration metrics
            MetricsVisitor metricsVisitor = new MetricsVisitor(taskInfo.getTaskId());
            taskInfo.getTimingInfo().accept(metricsVisitor);

            return success;
        }
    }

    private class SchedulingVisitor implements TaskInfo.TimingInfoVisitor {
        private Context mContext;
        private TaskInfo mTaskInfo;
        private boolean mSuccess;

        SchedulingVisitor(Context context, TaskInfo taskInfo) {
            mContext = context;
            mTaskInfo = taskInfo;
        }

        // Only valid after a TimingInfo object was visited.
        boolean getSuccess() {
            return mSuccess;
        }

        @Override
        public void visit(TaskInfo.OneOffInfo oneOffInfo) {
            mSuccess = mSchedulerDelegate.schedule(mContext, mTaskInfo);
        }

        @Override
        public void visit(TaskInfo.PeriodicInfo periodicInfo) {
            mSuccess = mSchedulerDelegate.schedule(mContext, mTaskInfo);
        }
    }

    // TODO(crbug.com/41477414): Update the documentation for the expiration feature.
    private class MetricsVisitor implements TaskInfo.TimingInfoVisitor {
        private final int mTaskId;

        MetricsVisitor(int taskId) {
            mTaskId = taskId;
        }

        @Override
        public void visit(TaskInfo.OneOffInfo oneOffInfo) {
            BackgroundTaskSchedulerUma.getInstance()
                    .reportTaskCreatedAndExpirationState(
                            mTaskId, oneOffInfo.expiresAfterWindowEndTime());
        }

        @Override
        public void visit(TaskInfo.PeriodicInfo periodicInfo) {
            BackgroundTaskSchedulerUma.getInstance()
                    .reportTaskCreatedAndExpirationState(
                            mTaskId, periodicInfo.expiresAfterWindowEndTime());
        }
    }

    @Override
    public void cancel(Context context, int taskId) {
        try (TraceEvent te =
                TraceEvent.scoped("BackgroundTaskScheduler.cancel", Integer.toString(taskId))) {
            ThreadUtils.assertOnUiThread();
            BackgroundTaskSchedulerUma.getInstance().reportTaskCanceled(taskId);

            mSchedulerDelegate.cancel(context, taskId);
        }
    }

    @Override
    public void doMaintenance() {
        try (TraceEvent te = TraceEvent.scoped("BackgroundTaskScheduler.checkForOSUpgrade")) {
            ThreadUtils.assertOnUiThread();

            BackgroundTaskSchedulerUma.getInstance().flushStats();
        }
    }
}