blob: 9c12bd844f85609e39daa0fdc4d925118e5660e6 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/notifications/scheduler/background_task_coordinator.h"
#include <algorithm>
#include <utility>
#include "base/numerics/ranges.h"
#include "base/optional.h"
#include "base/time/clock.h"
#include "chrome/browser/notifications/scheduler/impression_types.h"
#include "chrome/browser/notifications/scheduler/notification_background_task_scheduler.h"
#include "chrome/browser/notifications/scheduler/scheduler_config.h"
#include "chrome/browser/notifications/scheduler/scheduler_utils.h"
namespace notifications {
namespace {
class BackgroundTaskCoordinatorHelper {
public:
BackgroundTaskCoordinatorHelper(
NotificationBackgroundTaskScheduler* background_task,
const SchedulerConfig* config,
base::Clock* clock)
: background_task_(background_task), config_(config), clock_(clock) {}
~BackgroundTaskCoordinatorHelper() = default;
void ScheduleBackgroundTask(
BackgroundTaskCoordinator::Notifications notifications,
BackgroundTaskCoordinator::ClientStates client_states,
SchedulerTaskTime task_start_time) {
if (notifications.empty()) {
background_task_->Cancel();
return;
}
std::map<SchedulerClientType, int> shown_per_type;
int shown_total = 0;
SchedulerClientType last_shown_type = SchedulerClientType::kUnknown;
NotificationsShownToday(client_states, &shown_per_type, &shown_total,
&last_shown_type);
bool reach_max_today_all_type =
shown_total >= config_->max_daily_shown_all_type;
base::Time next_morning = NextMorning();
base::Time this_evening = ThisEvening();
for (const auto& pair : notifications) {
auto type = pair.first;
auto it = client_states.find(type);
if (pair.second.empty() || (it == client_states.end()))
continue;
const ClientState* client_state = it->second;
// Try to schedule on the day that suppression expires.
if (client_state->suppression_info.has_value()) {
const auto& suppression = client_state->suppression_info.value();
base::Time suppression_expire;
ToLocalHour(config_->morning_task_hour, suppression.ReleaseTime(),
0 /*day_delta*/, &suppression_expire);
MaybeUpdateBackgroundTaskTime(
std::max(suppression_expire, next_morning));
continue;
}
// Has met the quota for this notification type or for all types, only can
// send more on next day.
bool reach_max_today =
shown_per_type[type] >= client_state->current_max_daily_show;
if (reach_max_today || reach_max_today_all_type) {
MaybeUpdateBackgroundTaskTime(next_morning);
continue;
}
switch (task_start_time) {
case SchedulerTaskTime::kMorning:
// Still can send more in the evening.
MaybeUpdateBackgroundTaskTime(this_evening);
break;
case SchedulerTaskTime::kEvening:
// Wait until the next calendar day.
MaybeUpdateBackgroundTaskTime(next_morning);
break;
case SchedulerTaskTime::kUnknown:
// TODO(xingliu): Support arbitrary time background task.
NOTIMPLEMENTED();
break;
}
}
ScheduleBackgroundTaskInternal();
}
private:
// Returns the morning background task time on the next day.
base::Time NextMorning() {
base::Time next_morning;
bool success = ToLocalHour(config_->morning_task_hour, clock_->Now(),
1 /*day_delta*/, &next_morning);
DCHECK(success);
return next_morning;
}
// Returns the evening background task time on today.
base::Time ThisEvening() {
base::Time this_evening;
bool success = ToLocalHour(config_->evening_task_hour, clock_->Now(),
0 /*day_delta*/, &this_evening);
DCHECK(success);
return this_evening;
}
void MaybeUpdateBackgroundTaskTime(const base::Time& time) {
if (!background_task_time_.has_value() ||
time < background_task_time_.value())
background_task_time_ = time;
}
void ScheduleBackgroundTaskInternal() {
if (!background_task_time_.has_value())
return;
base::TimeDelta window_start_time =
background_task_time_.value() - clock_->Now();
window_start_time = base::ClampToRange(window_start_time, base::TimeDelta(),
base::TimeDelta::Max());
// TODO(xingliu): Pass the SchedulerTaskTime tag to background task to
// support arbitrary time background task.
background_task_->Schedule(
window_start_time,
window_start_time + config_->background_task_window_duration);
}
NotificationBackgroundTaskScheduler* background_task_;
const SchedulerConfig* config_;
base::Clock* clock_;
base::Optional<base::Time> background_task_time_;
DISALLOW_COPY_AND_ASSIGN(BackgroundTaskCoordinatorHelper);
};
} // namespace
BackgroundTaskCoordinator::BackgroundTaskCoordinator(
std::unique_ptr<NotificationBackgroundTaskScheduler> background_task,
const SchedulerConfig* config,
base::Clock* clock)
: background_task_(std::move(background_task)),
config_(config),
clock_(clock) {}
BackgroundTaskCoordinator::~BackgroundTaskCoordinator() = default;
void BackgroundTaskCoordinator::ScheduleBackgroundTask(
Notifications notifications,
ClientStates client_states,
SchedulerTaskTime task_start_time) {
auto helper = std::make_unique<BackgroundTaskCoordinatorHelper>(
background_task_.get(), config_, clock_);
helper->ScheduleBackgroundTask(std::move(notifications),
std::move(client_states), task_start_time);
}
} // namespace notifications