1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.chrome.browser.component_updater;
6 
7 import android.os.Build;
8 
9 import org.chromium.base.ContextUtils;
10 import org.chromium.base.annotations.CalledByNative;
11 import org.chromium.base.annotations.JNINamespace;
12 import org.chromium.base.annotations.NativeMethods;
13 import org.chromium.components.background_task_scheduler.BackgroundTask.TaskFinishedCallback;
14 import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
15 import org.chromium.components.background_task_scheduler.TaskIds;
16 import org.chromium.components.background_task_scheduler.TaskInfo;
17 import org.chromium.gms.ChromiumPlayServicesAvailability;
18 
19 /** Java-side implementation of the component update scheduler using the BackgroundTaskScheduler. */
20 @JNINamespace("component_updater")
21 public class UpdateScheduler {
22     private static UpdateScheduler sInstance;
23     private TaskFinishedCallback mTaskFinishedCallback;
24     private long mNativeScheduler;
25     private long mDelayMs;
26 
27     @CalledByNative
getInstance()28     /* package */ static UpdateScheduler getInstance() {
29         if (sInstance == null) {
30             sInstance = new UpdateScheduler();
31         }
32         return sInstance;
33     }
34 
35     @CalledByNative
isAvailable()36     /* package */ static boolean isAvailable() {
37         return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
38                 || ChromiumPlayServicesAvailability.isGooglePlayServicesAvailable(
39                         ContextUtils.getApplicationContext());
40     }
41 
onStartTaskBeforeNativeLoaded(TaskFinishedCallback callback)42     /* package */ void onStartTaskBeforeNativeLoaded(TaskFinishedCallback callback) {
43         mTaskFinishedCallback = callback;
44     }
45 
onStartTaskWithNative()46     /* package */ void onStartTaskWithNative() {
47         assert mNativeScheduler != 0;
48         UpdateSchedulerJni.get().onStartTask(mNativeScheduler, UpdateScheduler.this);
49     }
50 
onStopTask()51     /* package */ void onStopTask() {
52         if (mNativeScheduler != 0) {
53             UpdateSchedulerJni.get().onStopTask(mNativeScheduler, UpdateScheduler.this);
54         }
55         mTaskFinishedCallback = null;
56         scheduleInternal(mDelayMs);
57     }
58 
reschedule()59     /* package */ void reschedule() {
60         scheduleInternal(mDelayMs);
61     }
62 
UpdateScheduler()63     private UpdateScheduler() {}
64 
scheduleInternal(long delayMs)65     private void scheduleInternal(long delayMs) {
66         // Skip re-scheduling if we are currently running the update task. Otherwise, the current
67         // update tasks would be cancelled.
68         if (mTaskFinishedCallback != null) return;
69 
70         TaskInfo taskInfo = TaskInfo.createOneOffTask(TaskIds.COMPONENT_UPDATE_JOB_ID, delayMs,
71                                             Integer.MAX_VALUE)
72                                     .setUpdateCurrent(true)
73                                     .setRequiredNetworkType(TaskInfo.NetworkType.UNMETERED)
74                                     .setIsPersisted(true)
75                                     .build();
76         BackgroundTaskSchedulerFactory.getScheduler().schedule(
77                 ContextUtils.getApplicationContext(), taskInfo);
78     }
79 
80     @CalledByNative
schedule(long initialDelayMs, long delayMs)81     private void schedule(long initialDelayMs, long delayMs) {
82         mDelayMs = delayMs;
83         scheduleInternal(initialDelayMs);
84     }
85 
86     @CalledByNative
finishTask(boolean reschedule)87     private void finishTask(boolean reschedule) {
88         assert mTaskFinishedCallback != null;
89         mTaskFinishedCallback.taskFinished(false);
90         mTaskFinishedCallback = null;
91         if (reschedule) {
92             scheduleInternal(mDelayMs);
93         }
94     }
95 
96     @CalledByNative
setNativeScheduler(long nativeScheduler)97     private void setNativeScheduler(long nativeScheduler) {
98         mNativeScheduler = nativeScheduler;
99     }
100 
101     @CalledByNative
cancelTask()102     private void cancelTask() {
103         BackgroundTaskSchedulerFactory.getScheduler().cancel(
104                 ContextUtils.getApplicationContext(), TaskIds.COMPONENT_UPDATE_JOB_ID);
105     }
106 
107     @NativeMethods
108     interface Natives {
onStartTask(long nativeBackgroundTaskUpdateScheduler, UpdateScheduler caller)109         void onStartTask(long nativeBackgroundTaskUpdateScheduler, UpdateScheduler caller);
onStopTask(long nativeBackgroundTaskUpdateScheduler, UpdateScheduler caller)110         void onStopTask(long nativeBackgroundTaskUpdateScheduler, UpdateScheduler caller);
111     }
112 }
113