Commit eb39fc23 authored by Mugdha Lakhani's avatar Mugdha Lakhani Committed by Commit Bot

[Background Sync] Move browser wakeup to scheduler.

Before this chance, we were calculating the soonest wakeup_delta across
all storage partitions every time there was a change in state of any
storage partition, to schedule a wakeup task with this delta.

This change builds on the fact that we have running timers representing
delays for each of the storage partitions with active registrations in
the scheduler. I've changed scheduling the wakeup task to a push system.
Each storage partition informs the scheduler of its soonest delay after
there's any change in state. If this requested delay is the smallest of
all the current delays of the timers the scheduler has, it schedules a
wakeup task.

Canceling delayed scheduling causes cancellation of the wakeup task if
none of the storage partitions need it.

The change also removes now unnecessary code.
Unit tests are also added, and integration tests updated.

Bug: 996166
Change-Id: I1e6216ecc7085535a8e0ca0075ddad05085df763
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1834086
Commit-Queue: Mugdha Lakhani <nator@chromium.org>
Reviewed-by: default avatarDavid Trainor <dtrainor@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarRayan Kanso <rayankans@chromium.org>
Auto-Submit: Mugdha Lakhani <nator@chromium.org>
Cr-Commit-Position: refs/heads/master@{#715262}
parent 7a18f0ba
......@@ -11,6 +11,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.ContextUtils;
import org.chromium.base.ObserverList;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.chrome.browser.background_task_scheduler.NativeBackgroundTask;
......@@ -26,6 +27,12 @@ import org.chromium.components.background_task_scheduler.TaskInfo;
* Thread model: This class is to be run on the UI thread only.
*/
public class BackgroundSyncBackgroundTaskScheduler {
/** An observer interface for BackgroundSyncBackgroundTaskScheduler. */
interface Observer {
void oneOffTaskScheduledFor(@BackgroundSyncTask int taskType, long delay);
void oneOffTaskCanceledFor(@BackgroundSyncTask int taskType);
}
/**
* Any tasks scheduled using GCMNetworkManager directly to wake up Chrome
* would use this TASK_TAG. We no longer use GCMNetworkManager directly, so
......@@ -59,6 +66,8 @@ public class BackgroundSyncBackgroundTaskScheduler {
private static BackgroundSyncBackgroundTaskScheduler sInstance;
private final ObserverList<Observer> mObservers = new ObserverList<>();
@CalledByNative
public static BackgroundSyncBackgroundTaskScheduler getInstance() {
if (sInstance == null) sInstance = new BackgroundSyncBackgroundTaskScheduler();
......@@ -109,6 +118,17 @@ public class BackgroundSyncBackgroundTaskScheduler {
}
}
/** @param observer The observer to add. */
@VisibleForTesting
public void addObserver(Observer observer) {
mObservers.addObserver(observer);
}
/** @param observer The observer to remove. */
public void removeObserver(Observer observer) {
mObservers.removeObserver(observer);
}
/**
* Cancels a Background Sync one-off task, if there's one scheduled.
*
......@@ -118,6 +138,10 @@ public class BackgroundSyncBackgroundTaskScheduler {
protected void cancelOneOffTask(@BackgroundSyncTask int taskType) {
BackgroundTaskSchedulerFactory.getScheduler().cancel(
ContextUtils.getApplicationContext(), getAppropriateTaskId(taskType));
for (Observer observer : mObservers) {
observer.oneOffTaskCanceledFor(taskType);
}
}
/**
......@@ -148,8 +172,14 @@ public class BackgroundSyncBackgroundTaskScheduler {
.setExtras(taskExtras)
.build();
// This will overwrite any existing task with this ID.
return BackgroundTaskSchedulerFactory.getScheduler().schedule(
boolean didSchedule = BackgroundTaskSchedulerFactory.getScheduler().schedule(
ContextUtils.getApplicationContext(), taskInfo);
for (Observer observer : mObservers) {
observer.oneOffTaskScheduledFor(taskType, minDelayMs);
}
return didSchedule;
}
/**
......
......@@ -4,14 +4,6 @@
package org.chromium.chrome.browser.background_sync;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.chromium.base.test.util.ScalableTimeout.scaleTimeout;
import android.support.test.InstrumentationRegistry;
......@@ -23,24 +15,18 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.background_task_scheduler.ChromeBackgroundTaskFactory;
import org.chromium.chrome.browser.background_sync.BackgroundSyncBackgroundTaskScheduler.BackgroundSyncTask;
import org.chromium.chrome.browser.externalauth.ExternalAuthUtils;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.util.browser.TabTitleObserver;
import org.chromium.chrome.test.util.browser.signin.SigninTestUtil;
import org.chromium.components.background_task_scheduler.BackgroundTaskScheduler;
import org.chromium.components.background_task_scheduler.BackgroundTaskSchedulerFactory;
import org.chromium.components.background_task_scheduler.TaskInfo;
import org.chromium.content_public.browser.test.NativeLibraryTestRule;
import org.chromium.content_public.browser.test.util.BackgroundSyncNetworkUtils;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
......@@ -48,6 +34,8 @@ import org.chromium.net.ConnectionType;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.net.test.ServerCertificate;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
......@@ -56,9 +44,6 @@ import java.util.concurrent.TimeoutException;
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
public final class BackgroundSyncTest {
@Mock
private BackgroundTaskScheduler mTaskScheduler;
@Rule
public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeActivity.class);
......@@ -72,17 +57,15 @@ public final class BackgroundSyncTest {
private static final int TITLE_UPDATE_TIMEOUT_SECONDS = (int) scaleTimeout(10);
private static final long WAIT_TIME_MS = scaleTimeout(5000);
private CountDownLatch mScheduleLatch, mCancelLatch;
private BackgroundSyncBackgroundTaskScheduler.Observer mSchedulerObserver;
@Before
public void setUp() throws InterruptedException {
MockitoAnnotations.initMocks(this);
addSchedulerObserver();
BackgroundTaskSchedulerFactory.setSchedulerForTesting(mTaskScheduler);
ChromeBackgroundTaskFactory.setAsDefault();
doReturn(true)
.when(mTaskScheduler)
.schedule(eq(ContextUtils.getApplicationContext()), any(TaskInfo.class));
// loadNativeLibraryAndInitBrowserProcess will access AccountManagerFacade, so it should
// loadNativeLibraryNoBrowserProcess will access AccountManagerFacade, so it should
// be initialized beforehand.
SigninTestUtil.setUpAuthForTest();
......@@ -108,6 +91,7 @@ public final class BackgroundSyncTest {
public void tearDown() {
if (mTestServer != null) mTestServer.stopAndDestroyServer();
SigninTestUtil.tearDownAuthForTest();
BackgroundSyncBackgroundTaskScheduler.getInstance().removeObserver(mSchedulerObserver);
}
@Test
......@@ -122,25 +106,13 @@ public final class BackgroundSyncTest {
// Register Sync.
runJavaScript("RegisterSyncForTag('tagSucceedsSync');");
assertTitleBecomes("registered sync");
verify(mTaskScheduler, timeout(WAIT_TIME_MS))
.schedule(eq(ContextUtils.getApplicationContext()),
argThat(taskInfo
-> taskInfo.getBackgroundTaskClass()
== BackgroundSyncBackgroundTask.class));
Assert.assertTrue(mScheduleLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
forceConnectionType(ConnectionType.CONNECTION_WIFI);
assertTitleBecomes("onsync: tagSucceedsSync");
// Another invocation when all events are firing but haven't completed, to cover the case
// when the browser is closed mid-event. This one races with the completion of the sync
// event, and might not happen.
// The wait is to ensure we wait for this invocation to happen, since it happens in
// parallel with dispatching the sync event.
verify(mTaskScheduler, after(WAIT_TIME_MS).atMost(2))
.schedule(eq(ContextUtils.getApplicationContext()),
argThat(taskInfo
-> taskInfo.getBackgroundTaskClass()
== BackgroundSyncBackgroundTask.class));
// Now that sync has completed, browser wakeup should get canceled.
Assert.assertTrue(mCancelLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
@Test
......@@ -155,25 +127,11 @@ public final class BackgroundSyncTest {
// Register Sync.
runJavaScript("RegisterSyncForTag('tagFailsSync');");
assertTitleBecomes("registered sync");
verify(mTaskScheduler, timeout(WAIT_TIME_MS))
.schedule(eq(ContextUtils.getApplicationContext()),
argThat(taskInfo
-> taskInfo.getBackgroundTaskClass()
== BackgroundSyncBackgroundTask.class));
Assert.assertTrue(mScheduleLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
forceConnectionType(ConnectionType.CONNECTION_WIFI);
// Wait for some time that is less than the retry time period (default 5 minutes).
// One call to schedule wake-up a few minutes after firing the first sync event, to cover
// our bases if the browser is closed mid-sync. This one races with the completion of the
// sync event, and might not happen.
// Another call to waking up the browser for attempt two after completion of the first
// event. This will always happen.
verify(mTaskScheduler, after(WAIT_TIME_MS).atMost(3))
.schedule(eq(ContextUtils.getApplicationContext()),
argThat(taskInfo
-> taskInfo.getBackgroundTaskClass()
== BackgroundSyncBackgroundTask.class));
// Browser wakeup must not be canceled.
Assert.assertFalse(mCancelLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
}
/**
......@@ -207,4 +165,26 @@ public final class BackgroundSyncTest {
/* disabled= */ true);
});
}
private void addSchedulerObserver() {
mScheduleLatch = new CountDownLatch(1);
mCancelLatch = new CountDownLatch(1);
mSchedulerObserver = new BackgroundSyncBackgroundTaskScheduler.Observer() {
@Override
public void oneOffTaskScheduledFor(@BackgroundSyncTask int taskType, long delay) {
if (taskType == BackgroundSyncTask.ONE_SHOT_SYNC_CHROME_WAKE_UP) {
mScheduleLatch.countDown();
}
}
@Override
public void oneOffTaskCanceledFor(@BackgroundSyncTask int taskType) {
if (taskType == BackgroundSyncTask.ONE_SHOT_SYNC_CHROME_WAKE_UP) {
mCancelLatch.countDown();
}
}
};
BackgroundSyncBackgroundTaskScheduler.getInstance().addObserver(mSchedulerObserver);
}
}
......@@ -72,17 +72,25 @@ BackgroundSyncLauncherAndroid* BackgroundSyncLauncherAndroid::Get() {
}
// static
void BackgroundSyncLauncherAndroid::ScheduleBrowserWakeUp(
blink::mojom::BackgroundSyncType sync_type) {
void BackgroundSyncLauncherAndroid::SetPlayServicesVersionCheckDisabledForTests(
bool disabled) {
disable_play_services_version_check_for_tests = disabled;
}
// static
void BackgroundSyncLauncherAndroid::ScheduleBrowserWakeUpWithDelay(
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Get()->ScheduleBrowserWakeUpImpl(sync_type);
Get()->ScheduleBrowserWakeUpWithDelayImpl(sync_type, delay);
}
// static
void BackgroundSyncLauncherAndroid::SetPlayServicesVersionCheckDisabledForTests(
bool disabled) {
disable_play_services_version_check_for_tests = disabled;
void BackgroundSyncLauncherAndroid::CancelBrowserWakeup(
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Get()->CancelBrowserWakeupImpl(sync_type);
}
// static
......@@ -94,21 +102,7 @@ bool BackgroundSyncLauncherAndroid::ShouldDisableBackgroundSync() {
base::android::AttachCurrentThread());
}
void BackgroundSyncLauncherAndroid::ScheduleBrowserWakeUpImpl(
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto* profile = ProfileManager::GetLastUsedProfile();
DCHECK(profile);
content::BackgroundSyncContext::GetSoonestWakeupDeltaAcrossPartitions(
sync_type, profile,
base::BindOnce(&BackgroundSyncLauncherAndroid::
ScheduleBrowserWakeUpWithWakeUpDeltaImpl,
base::Unretained(this), sync_type));
}
void BackgroundSyncLauncherAndroid::ScheduleBrowserWakeUpWithWakeUpDeltaImpl(
void BackgroundSyncLauncherAndroid::ScheduleBrowserWakeUpWithDelayImpl(
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta soonest_wakeup_delta) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
......@@ -122,6 +116,19 @@ void BackgroundSyncLauncherAndroid::ScheduleBrowserWakeUpWithWakeUpDeltaImpl(
min_delay_ms);
}
void BackgroundSyncLauncherAndroid::CancelBrowserWakeupImpl(
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
JNIEnv* env = base::android::AttachCurrentThread();
// TODO(crbug.com/996166): Add a new method to cancel browser launch.
Java_BackgroundSyncBackgroundTaskScheduler_launchBrowserIfStopped(
env, java_background_sync_background_task_scheduler_launcher_,
GetBackgroundTaskType(sync_type), /* shouldLaunch= */ false,
/* minDelayMs= */ 0);
}
void BackgroundSyncLauncherAndroid::FireBackgroundSyncEvents(
blink::mojom::BackgroundSyncType sync_type,
const base::android::JavaParamRef<jobject>& j_runnable) {
......
......@@ -23,12 +23,17 @@ class BackgroundSyncLauncherAndroid {
public:
static BackgroundSyncLauncherAndroid* Get();
// Calculates the soonest wakeup time across all the storage
// partitions for the non-incognito profile and ensures that the browser
// is running when the device next goes online after that time has passed.
// If this time is set to base::TimeDelta::Max() across all storage
// partitions, the wake-up task is cancelled.
static void ScheduleBrowserWakeUp(blink::mojom::BackgroundSyncType sync_type);
// Schedules a BackgroundTaskScheduler task for |sync_type| with delay |delay|
// to ensure that the browser is running when the device next goes online
// after that time has passed. If |delay| is base::TimeDelta::Max(), the
// wake-up task is cancelled.
static void ScheduleBrowserWakeUpWithDelay(
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay);
// Cancels the BackgroundTaskScheduler task that wakes up the browser to
// process Background Sync registrations of type |sync_type|.
static void CancelBrowserWakeup(blink::mojom::BackgroundSyncType sync_type);
static bool ShouldDisableBackgroundSync();
......@@ -50,10 +55,10 @@ class BackgroundSyncLauncherAndroid {
BackgroundSyncLauncherAndroid();
~BackgroundSyncLauncherAndroid();
void ScheduleBrowserWakeUpImpl(blink::mojom::BackgroundSyncType sync_type);
void ScheduleBrowserWakeUpWithWakeUpDeltaImpl(
void ScheduleBrowserWakeUpWithDelayImpl(
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta soonest_wakeup_delta);
void CancelBrowserWakeupImpl(blink::mojom::BackgroundSyncType sync_type);
base::android::ScopedJavaGlobalRef<jobject>
java_background_sync_background_task_scheduler_launcher_;
......
......@@ -226,7 +226,21 @@ void BackgroundSyncControllerImpl::NotifyPeriodicBackgroundSyncCompleted(
origin, status_code, num_attempts, max_attempts);
}
void BackgroundSyncControllerImpl::ScheduleBrowserWakeUp(
void BackgroundSyncControllerImpl::ScheduleBrowserWakeUpWithDelay(
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (profile_->IsOffTheRecord())
return;
#if defined(OS_ANDROID)
BackgroundSyncLauncherAndroid::ScheduleBrowserWakeUpWithDelay(sync_type,
delay);
#endif
}
void BackgroundSyncControllerImpl::CancelBrowserWakeup(
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
......@@ -234,7 +248,7 @@ void BackgroundSyncControllerImpl::ScheduleBrowserWakeUp(
return;
#if defined(OS_ANDROID)
BackgroundSyncLauncherAndroid::ScheduleBrowserWakeUp(sync_type);
BackgroundSyncLauncherAndroid::CancelBrowserWakeup(sync_type);
#endif
}
......
......@@ -91,8 +91,10 @@ class BackgroundSyncControllerImpl : public content::BackgroundSyncController,
blink::ServiceWorkerStatusCode status_code,
int num_attempts,
int max_attempts) override;
void ScheduleBrowserWakeUp(
blink::mojom::BackgroundSyncType sync_type) override;
void ScheduleBrowserWakeUpWithDelay(
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay) override;
void CancelBrowserWakeup(blink::mojom::BackgroundSyncType sync_type) override;
base::TimeDelta GetNextEventDelay(
const content::BackgroundSyncRegistration& registration,
......
......@@ -76,12 +76,6 @@ class BackgroundSyncProxy::Core {
},
std::move(delayed_task)));
}
// TODO(crbug.com/996166): Remove this call once the logic to schedule
// browser wakeup has moved to BackgroundTaskScheduler.
auto* controller = browser_context()->GetBackgroundSyncController();
DCHECK(controller);
controller->ScheduleBrowserWakeUp(sync_type);
}
void SendSuspendedPeriodicSyncOrigins(
......
......@@ -4,11 +4,14 @@
#include "content/browser/background_sync/background_sync_scheduler.h"
#include <algorithm>
#include "base/memory/scoped_refptr.h"
#include "base/supports_user_data.h"
#include "content/browser/storage_partition_impl.h"
#include "content/public/browser/background_sync_controller.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
namespace {
......@@ -18,8 +21,8 @@ const char kBackgroundSyncSchedulerKey[] = "background-sync-scheduler";
namespace content {
using DelayedProcessingInfo =
std::map<StoragePartition*, std::unique_ptr<base::OneShotTimer>>;
using DelayedProcessingInfoMap =
std::map<StoragePartitionImpl*, std::unique_ptr<base::OneShotTimer>>;
// static
BackgroundSyncScheduler* BackgroundSyncScheduler::GetFor(
......@@ -51,47 +54,103 @@ BackgroundSyncScheduler::~BackgroundSyncScheduler() {
}
void BackgroundSyncScheduler::ScheduleDelayedProcessing(
StoragePartition* storage_partition,
StoragePartitionImpl* storage_partition,
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay,
base::OnceClosure delayed_task) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(storage_partition);
auto& delayed_processing_info = GetDelayedProcessingInfo(sync_type);
// CancelDelayedProcessing should be called in this case.
DCHECK(!delay.is_max());
auto& delayed_processing_info = GetDelayedProcessingInfoMap(sync_type);
delayed_processing_info.emplace(storage_partition,
std::make_unique<base::OneShotTimer>());
if (!delay.is_zero() && !delay.is_max()) {
delayed_processing_info[storage_partition]->Start(FROM_HERE, delay,
std::move(delayed_task));
if (!delay.is_zero()) {
delayed_processing_info[storage_partition]->Start(
FROM_HERE, delay,
base::BindOnce(&BackgroundSyncScheduler::RunDelayedTaskAndPruneInfoMap,
weak_ptr_factory_.GetWeakPtr(), sync_type,
storage_partition, std::move(delayed_task)));
}
// TODO(crbug.com/996166) Move logic to schedule a browser wakeup task on
// Android from BackgroundSycnProxy to here.
#if defined(OS_ANDROID)
ScheduleOrCancelBrowserWakeupForSyncType(sync_type, storage_partition);
#endif
}
void BackgroundSyncScheduler::CancelDelayedProcessing(
StoragePartition* storage_partition,
StoragePartitionImpl* storage_partition,
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(storage_partition);
auto& delayed_processing_info = GetDelayedProcessingInfo(sync_type);
auto& delayed_processing_info = GetDelayedProcessingInfoMap(sync_type);
delayed_processing_info.erase(storage_partition);
// TODO(crbug.com/996166) Move logic to cancel a browser wakeup task on
// Android from BackgroundSycnProxy to here.
#if defined(OS_ANDROID)
ScheduleOrCancelBrowserWakeupForSyncType(sync_type, storage_partition);
#endif
}
DelayedProcessingInfo& BackgroundSyncScheduler::GetDelayedProcessingInfo(
DelayedProcessingInfoMap& BackgroundSyncScheduler::GetDelayedProcessingInfoMap(
blink::mojom::BackgroundSyncType sync_type) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT)
return delayed_processing_info_one_shot_;
else
return delayed_processing_info_periodic_;
}
void BackgroundSyncScheduler::RunDelayedTaskAndPruneInfoMap(
blink::mojom::BackgroundSyncType sync_type,
StoragePartitionImpl* storage_partition,
base::OnceClosure delayed_task) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::move(delayed_task).Run();
auto& delayed_processing_info = GetDelayedProcessingInfoMap(sync_type);
delayed_processing_info.erase(storage_partition);
#if defined(OS_ANDROID)
ScheduleOrCancelBrowserWakeupForSyncType(sync_type, storage_partition);
#endif
}
#if defined(OS_ANDROID)
void BackgroundSyncScheduler::ScheduleOrCancelBrowserWakeupForSyncType(
blink::mojom::BackgroundSyncType sync_type,
StoragePartitionImpl* storage_partition) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto* browser_context = storage_partition->browser_context();
DCHECK(browser_context);
auto* controller = browser_context->GetBackgroundSyncController();
DCHECK(controller);
auto& delayed_processing_info = GetDelayedProcessingInfoMap(sync_type);
// If no more scheduled tasks remain, cancel browser wakeup.
// Canceling when there's no task scheduled is a no-op.
if (delayed_processing_info.empty()) {
controller->CancelBrowserWakeup(sync_type);
return;
}
// Schedule browser wakeup with the smallest delay required.
auto& min_info = *std::min_element(
delayed_processing_info.begin(), delayed_processing_info.end(),
[](auto& lhs, auto& rhs) {
return (lhs.second->desired_run_time() - base::TimeTicks::Now()) <
(rhs.second->desired_run_time() - base::TimeTicks::Now());
});
controller->ScheduleBrowserWakeUpWithDelay(
sync_type, min_info.second->desired_run_time() - base::TimeTicks::Now());
}
#endif
} // namespace content
......@@ -9,7 +9,9 @@
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/common/content_export.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
......@@ -17,7 +19,7 @@
namespace content {
class StoragePartition;
class StoragePartitionImpl;
// This contains the logic to schedule delayed processing of (periodic)
// Background Sync registrations.
......@@ -36,7 +38,7 @@ class CONTENT_EXPORT BackgroundSyncScheduler
// TODO(crbug.com/996166): Add logic to schedule browser wakeup on Android.
// Must be called on the UI thread.
virtual void ScheduleDelayedProcessing(
StoragePartition* storage_partition,
StoragePartitionImpl* storage_partition,
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay,
base::OnceClosure delayed_task);
......@@ -44,7 +46,7 @@ class CONTENT_EXPORT BackgroundSyncScheduler
// Cancels delayed_processing for |sync_type| for |storage_partition|.
// Must be called on the UI thread.
virtual void CancelDelayedProcessing(
StoragePartition* storage_partition,
StoragePartitionImpl* storage_partition,
blink::mojom::BackgroundSyncType sync_type);
private:
......@@ -53,14 +55,24 @@ class CONTENT_EXPORT BackgroundSyncScheduler
friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
friend class base::DeleteHelper<BackgroundSyncScheduler>;
std::map<StoragePartition*, std::unique_ptr<base::OneShotTimer>>&
GetDelayedProcessingInfo(blink::mojom::BackgroundSyncType sync_type);
std::map<StoragePartitionImpl*, std::unique_ptr<base::OneShotTimer>>&
GetDelayedProcessingInfoMap(blink::mojom::BackgroundSyncType sync_type);
void RunDelayedTaskAndPruneInfoMap(blink::mojom::BackgroundSyncType sync_type,
StoragePartitionImpl* storage_partition,
base::OnceClosure delayed_task);
#if defined(OS_ANDROID)
void ScheduleOrCancelBrowserWakeupForSyncType(
blink::mojom::BackgroundSyncType sync_type,
StoragePartitionImpl* storage_partition);
#endif
std::map<StoragePartition*, std::unique_ptr<base::OneShotTimer>>
std::map<StoragePartitionImpl*, std::unique_ptr<base::OneShotTimer>>
delayed_processing_info_one_shot_;
std::map<StoragePartition*, std::unique_ptr<base::OneShotTimer>>
std::map<StoragePartitionImpl*, std::unique_ptr<base::OneShotTimer>>
delayed_processing_info_periodic_;
base::WeakPtrFactory<BackgroundSyncScheduler> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(BackgroundSyncScheduler);
};
......
......@@ -13,6 +13,7 @@
#include "base/strings/string_number_conversions.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/browser/background_sync/background_sync_scheduler.h"
#include "content/browser/storage_partition_impl.h"
#include "content/common/content_export.h"
......@@ -20,6 +21,7 @@
#include "content/public/common/content_client.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "content/test/mock_background_sync_controller.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
......@@ -60,8 +62,9 @@ class BackgroundSyncSchedulerTest : public testing::Test {
base::OnceClosure delayed_task) {
auto* scheduler = BackgroundSyncScheduler::GetFor(&test_browser_context_);
DCHECK(scheduler);
auto* storage_partition =
BrowserContext::GetStoragePartitionForSite(&test_browser_context_, url);
auto* storage_partition = static_cast<StoragePartitionImpl*>(
BrowserContext::GetStoragePartitionForSite(&test_browser_context_,
url));
DCHECK(storage_partition);
scheduler->ScheduleDelayedProcessing(storage_partition, sync_type, delay,
......@@ -72,13 +75,24 @@ class BackgroundSyncSchedulerTest : public testing::Test {
blink::mojom::BackgroundSyncType sync_type) {
auto* scheduler = BackgroundSyncScheduler::GetFor(&test_browser_context_);
DCHECK(scheduler);
auto* storage_partition =
BrowserContext::GetStoragePartitionForSite(&test_browser_context_, url);
auto* storage_partition = static_cast<StoragePartitionImpl*>(
BrowserContext::GetStoragePartitionForSite(&test_browser_context_,
url));
DCHECK(storage_partition);
scheduler->CancelDelayedProcessing(storage_partition, sync_type);
}
MockBackgroundSyncController* GetController() {
return static_cast<MockBackgroundSyncController*>(
test_browser_context_.GetBackgroundSyncController());
}
base::TimeDelta GetBrowserWakeupDelay(
blink::mojom::BackgroundSyncType sync_type) {
return GetController()->GetBrowserWakeupDelay(sync_type);
}
void SetUp() override {
original_client_ = SetBrowserClientForTesting(&browser_client_);
}
......@@ -111,17 +125,6 @@ TEST_F(BackgroundSyncSchedulerTest, ZeroDelayScheduleDoesNotInvokeCallback) {
EXPECT_FALSE(was_called);
}
TEST_F(BackgroundSyncSchedulerTest, MaxDelayScheduleDoesNotInvokeCallback) {
bool was_called = false;
ScheduleDelayedProcessing(
GURL(kUrl_1), blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta::Max(),
base::BindOnce([](bool* was_called) { *was_called = true; },
&was_called));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(was_called);
}
TEST_F(BackgroundSyncSchedulerTest, CancelDoesNotInvokeCallback) {
bool was_called = false;
ScheduleDelayedProcessing(
......@@ -183,4 +186,112 @@ TEST_F(BackgroundSyncSchedulerTest, ScheduleBothTypesOfSync) {
run_loop_2.Run();
}
#if defined(OS_ANDROID)
TEST_F(BackgroundSyncSchedulerTest, BrowserWakeupScheduled) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta::FromSeconds(1), base::DoNothing());
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromSeconds(1));
ScheduleDelayedProcessing(
GURL(kUrl_2), blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta::FromMilliseconds(1), base::DoNothing());
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromMilliseconds(1));
}
TEST_F(BackgroundSyncSchedulerTest,
BrowserWakeupScheduleSecondAfterFirstFinishes) {
base::RunLoop run_loop_1;
ScheduleDelayedProcessing(
GURL(kUrl_1), blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta::FromMilliseconds(1), run_loop_1.QuitClosure());
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromMilliseconds(1));
run_loop_1.Run();
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta::FromMinutes(1), base::DoNothing());
EXPECT_GT(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromMilliseconds(1));
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromMinutes(1));
}
TEST_F(BackgroundSyncSchedulerTest, BrowserWakeupScheduleOneOfEachType) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC,
base::TimeDelta::FromSeconds(1), base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::PERIODIC),
base::TimeDelta::FromSeconds(1));
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta::FromMinutes(1), base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromMinutes(1));
}
TEST_F(BackgroundSyncSchedulerTest, BrowserWakeupScheduleThenCancel) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC,
base::TimeDelta::FromMinutes(1), base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::PERIODIC),
base::TimeDelta::FromMinutes(1));
CancelDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::PERIODIC),
base::TimeDelta::Max());
}
TEST_F(BackgroundSyncSchedulerTest, CancelingOneTypeDoesNotAffectAnother) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC,
base::TimeDelta::FromMinutes(1), base::DoNothing());
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta::FromSeconds(1), base::DoNothing());
CancelDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::PERIODIC);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::PERIODIC),
base::TimeDelta::Max());
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromSeconds(1));
}
TEST_F(BackgroundSyncSchedulerTest,
CancelingProcessingForOneStorageParitionUpdatesBrowserWakeup) {
ScheduleDelayedProcessing(GURL(kUrl_1),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta::FromMinutes(1), base::DoNothing());
ScheduleDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT,
base::TimeDelta::FromSeconds(1), base::DoNothing());
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromSeconds(1));
CancelDelayedProcessing(GURL(kUrl_2),
blink::mojom::BackgroundSyncType::ONE_SHOT);
base::RunLoop().RunUntilIdle();
EXPECT_LE(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromMinutes(1));
EXPECT_GT(GetBrowserWakeupDelay(blink::mojom::BackgroundSyncType::ONE_SHOT),
base::TimeDelta::FromSeconds(1));
}
#endif
} // namespace content
......@@ -76,11 +76,16 @@ class CONTENT_EXPORT BackgroundSyncController {
int num_attempts,
int max_attempts) {}
// Calculates the soonest wakeup delta across all storage partitions and
// schedules a background task to wake up the browser to process
// Background Sync registrations.
virtual void ScheduleBrowserWakeUp(
blink::mojom::BackgroundSyncType sync_type) {}
// Schedules a background task with delay |delay| to wake up the browser to
// process Background Sync registrations of type |sync_type|.
virtual void ScheduleBrowserWakeUpWithDelay(
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay) {}
// Cancel the background task that wakes the browser up to process Background
// Sync registrations of type |sync_type|.
virtual void CancelBrowserWakeup(blink::mojom::BackgroundSyncType sync_type) {
}
// Calculates the delay after which the next sync event should be fired
// for a BackgroundSync registration. The delay is based on the sync_type of
......
......@@ -30,13 +30,25 @@ void MockBackgroundSyncController::NotifyOneShotBackgroundSyncRegistered(
registration_origin_ = origin;
}
void MockBackgroundSyncController::ScheduleBrowserWakeUp(
blink::mojom::BackgroundSyncType sync_type) {
void MockBackgroundSyncController::ScheduleBrowserWakeUpWithDelay(
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay) {
if (sync_type == blink::mojom::BackgroundSyncType::PERIODIC) {
run_in_background_for_periodic_sync_count_ += 1;
periodic_sync_browser_wakeup_delay_ = delay;
return;
}
run_in_background_for_one_shot_sync_count_ += 1;
one_shot_sync_browser_wakeup_delay_ = delay;
}
void MockBackgroundSyncController::CancelBrowserWakeup(
blink::mojom::BackgroundSyncType sync_type) {
if (sync_type == blink::mojom::BackgroundSyncType::PERIODIC) {
periodic_sync_browser_wakeup_delay_ = base::TimeDelta::Max();
} else {
one_shot_sync_browser_wakeup_delay_ = base::TimeDelta::Max();
}
}
void MockBackgroundSyncController::ApplyFieldTrialParamsOverrides() {
......
......@@ -27,8 +27,10 @@ class MockBackgroundSyncController : public BackgroundSyncController {
void NotifyOneShotBackgroundSyncRegistered(const url::Origin& origin,
bool can_fire,
bool is_reregistered) override;
void ScheduleBrowserWakeUp(
blink::mojom::BackgroundSyncType sync_type) override;
void ScheduleBrowserWakeUpWithDelay(
blink::mojom::BackgroundSyncType sync_type,
base::TimeDelta delay) override;
void CancelBrowserWakeup(blink::mojom::BackgroundSyncType sync_type) override;
void GetParameterOverrides(BackgroundSyncParameters* parameters) override;
base::TimeDelta GetNextEventDelay(
const BackgroundSyncRegistration& registration,
......@@ -49,6 +51,15 @@ class MockBackgroundSyncController : public BackgroundSyncController {
int run_in_background_periodic_sync_count() const {
return run_in_background_for_periodic_sync_count_;
}
base::TimeDelta GetBrowserWakeupDelay(
blink::mojom::BackgroundSyncType sync_type) const {
if (sync_type == blink::mojom::BackgroundSyncType::ONE_SHOT)
return one_shot_sync_browser_wakeup_delay_;
else
return periodic_sync_browser_wakeup_delay_;
}
BackgroundSyncParameters* background_sync_parameters() {
return &background_sync_parameters_;
}
......@@ -64,6 +75,8 @@ class MockBackgroundSyncController : public BackgroundSyncController {
int run_in_background_for_one_shot_sync_count_ = 0;
int run_in_background_for_periodic_sync_count_ = 0;
base::TimeDelta periodic_sync_browser_wakeup_delay_;
base::TimeDelta one_shot_sync_browser_wakeup_delay_;
BackgroundSyncParameters background_sync_parameters_;
std::set<url::Origin> suspended_periodic_sync_origins_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment