chromium/chrome/android/java/src/org/chromium/chrome/browser/download/service/DownloadBackgroundTask.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.chrome.browser.download.service;

import android.content.Context;

import androidx.annotation.VisibleForTesting;

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

import org.chromium.base.Callback;
import org.chromium.chrome.browser.download.DownloadManagerService;
import org.chromium.chrome.browser.download.DownloadNotificationService;
import org.chromium.chrome.browser.download.DownloadUtils;
import org.chromium.chrome.browser.download.items.OfflineContentAggregatorNotificationBridgeUiFactory;
import org.chromium.chrome.browser.profiles.ProfileKey;
import org.chromium.chrome.browser.profiles.ProfileKeyUtil;
import org.chromium.components.background_task_scheduler.NativeBackgroundTask;
import org.chromium.components.background_task_scheduler.TaskParameters;
import org.chromium.components.download.DownloadTaskType;
import org.chromium.components.download.internal.BatteryStatusListenerAndroid;

/**
 * Entry point for the download service to perform desired action when the task is fired by the
 * scheduler.
 */
@JNINamespace("download::android")
public class DownloadBackgroundTask extends NativeBackgroundTask {
    @DownloadTaskType private int mCurrentTaskType;

    @Override
    protected @StartBeforeNativeResult int onStartTaskBeforeNativeLoaded(
            Context context, TaskParameters taskParameters, TaskFinishedCallback callback) {
        boolean requiresCharging =
                taskParameters
                        .getExtras()
                        .getBoolean(DownloadTaskScheduler.EXTRA_BATTERY_REQUIRES_CHARGING);
        int optimalBatteryPercentage =
                taskParameters
                        .getExtras()
                        .getInt(DownloadTaskScheduler.EXTRA_OPTIMAL_BATTERY_PERCENTAGE);
        mCurrentTaskType = taskParameters.getExtras().getInt(DownloadTaskScheduler.EXTRA_TASK_TYPE);
        // Reschedule if minimum battery level is not satisfied.
        if (!requiresCharging
                && BatteryStatusListenerAndroid.getBatteryPercentage() < optimalBatteryPercentage) {
            return StartBeforeNativeResult.RESCHEDULE;
        }

        return StartBeforeNativeResult.LOAD_NATIVE;
    }

    @Override
    protected void onStartTaskWithNative(
            Context context, TaskParameters taskParameters, final TaskFinishedCallback callback) {
        // In case of future upgrades, we would need to build an intent for the old version and
        // validate that this code still works. This would require decoupling this immediate class
        // from native as well.
        DownloadManagerService.getDownloadManagerService().initForBackgroundTask();
        if (DownloadUtils.isUserInitiatedJob(mTaskId)) {
            // In case of user-initiated jobs, we need to ensure that notifications are attached to
            // the job life cycle.
            ensureNotificationBridgeInitialized();
            DownloadNotificationService.getInstance()
                    .setBackgroundTaskNotificationCallback(taskParameters.getTaskId(), callback);
        }
        DownloadBackgroundTaskJni.get()
                .startBackgroundTask(
                        DownloadBackgroundTask.this,
                        getProfileKey(),
                        mCurrentTaskType,
                        needsReschedule -> {
                            finishTask(taskParameters, callback, needsReschedule);
                        });
    }

    @Override
    protected boolean supportsMinimalBrowser() {
        return true;
    }

    @Override
    protected boolean onStopTaskBeforeNativeLoaded(Context context, TaskParameters taskParameters) {
        return true;
    }

    @Override
    protected boolean onStopTaskWithNative(Context context, TaskParameters taskParameters) {
        if (DownloadUtils.isUserInitiatedJob(mTaskId)) {
            DownloadNotificationService.getInstance()
                    .setBackgroundTaskNotificationCallback(taskParameters.getTaskId(), null);
        }
        @DownloadTaskType
        int taskType = taskParameters.getExtras().getInt(DownloadTaskScheduler.EXTRA_TASK_TYPE);
        return DownloadBackgroundTaskJni.get()
                .stopBackgroundTask(DownloadBackgroundTask.this, getProfileKey(), taskType);
    }

    @VisibleForTesting
    protected void finishTask(
            TaskParameters taskParameters, TaskFinishedCallback callback, boolean needsReschedule) {
        if (DownloadUtils.isUserInitiatedJob(mTaskId)) {
            DownloadNotificationService.getInstance()
                    .setBackgroundTaskNotificationCallback(taskParameters.getTaskId(), null);
        }
        callback.taskFinished(needsReschedule);
    }

    @VisibleForTesting()
    protected ProfileKey getProfileKey() {
        return ProfileKeyUtil.getLastUsedRegularProfileKey();
    }

    @VisibleForTesting
    protected void ensureNotificationBridgeInitialized() {
        OfflineContentAggregatorNotificationBridgeUiFactory.instance();
    }

    @NativeMethods
    @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
    public interface Natives {
        void startBackgroundTask(
                DownloadBackgroundTask caller,
                ProfileKey key,
                int taskType,
                Callback<Boolean> callback);

        boolean stopBackgroundTask(DownloadBackgroundTask caller, ProfileKey key, int taskType);
    }
}