Chromium Code Reviews| Index: components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc |
| diff --git a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc |
| index 7387410d8cc7b624f27103882229b4c152fd2094..86ffb023f706fdaa4eba8d15fd4d1021223961b6 100644 |
| --- a/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc |
| +++ b/components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc |
| @@ -4,6 +4,7 @@ |
| #include "components/ntp_snippets/remote/scheduling_remote_suggestions_provider.h" |
| +#include <random> |
| #include <string> |
| #include <utility> |
| @@ -21,28 +22,57 @@ namespace ntp_snippets { |
| namespace { |
| +enum class FetchingInterval { |
| + PERSISTENT_FALLBACK, |
| + PERSISTENT_WIFI, |
| + SOFT_ON_USAGE_EVENT, |
| + COUNT |
| +}; |
| + |
| // Default values for fetching intervals, fallback and wifi. |
| -const double kDefaultFetchingIntervalRareNtpUser[] = {48.0, 24.0}; |
| -const double kDefaultFetchingIntervalActiveNtpUser[] = {24.0, 6.0}; |
| -const double kDefaultFetchingIntervalActiveSuggestionsConsumer[] = {24.0, 6.0}; |
| +const double kDefaultFetchingIntervalRareNtpUser[] = {48.0, 24.0, 12.0}; |
| +const double kDefaultFetchingIntervalActiveNtpUser[] = {24.0, 6.0, 2.0}; |
| +const double kDefaultFetchingIntervalActiveSuggestionsConsumer[] = {24.0, 6.0, |
| + 2.0}; |
| // Variation parameters than can the default fetching intervals. |
| const char* kFetchingIntervalParamNameRareNtpUser[] = { |
| "fetching_interval_hours-fallback-rare_ntp_user", |
| - "fetching_interval_hours-wifi-rare_ntp_user"}; |
| + "fetching_interval_hours-wifi-rare_ntp_user", |
| + "soft_fetching_interval_hours-active-rare_ntp_user"}; |
| const char* kFetchingIntervalParamNameActiveNtpUser[] = { |
| "fetching_interval_hours-fallback-active_ntp_user", |
| - "fetching_interval_hours-wifi-active_ntp_user"}; |
| + "fetching_interval_hours-wifi-active_ntp_user", |
| + "soft_fetching_interval_hours-active-active_ntp_user"}; |
| const char* kFetchingIntervalParamNameActiveSuggestionsConsumer[] = { |
| "fetching_interval_hours-fallback-active_suggestions_consumer", |
| - "fetching_interval_hours-wifi-active_suggestions_consumer"}; |
| - |
| -base::TimeDelta GetDesiredUpdateInterval( |
| - bool is_wifi, |
| + "fetching_interval_hours-wifi-active_suggestions_consumer", |
| + "soft_fetching_interval_hours-active-active_suggestions_consumer"}; |
| + |
| +static_assert( |
| + static_cast<unsigned int>(FetchingInterval::COUNT) == |
| + arraysize(kDefaultFetchingIntervalRareNtpUser) && |
| + static_cast<unsigned int>(FetchingInterval::COUNT) == |
| + arraysize(kDefaultFetchingIntervalActiveNtpUser) && |
| + static_cast<unsigned int>(FetchingInterval::COUNT) == |
| + arraysize(kDefaultFetchingIntervalActiveSuggestionsConsumer) && |
| + static_cast<unsigned int>(FetchingInterval::COUNT) == |
| + arraysize(kFetchingIntervalParamNameRareNtpUser) && |
| + static_cast<unsigned int>(FetchingInterval::COUNT) == |
| + arraysize(kFetchingIntervalParamNameActiveNtpUser) && |
| + static_cast<unsigned int>(FetchingInterval::COUNT) == |
| + arraysize(kFetchingIntervalParamNameActiveSuggestionsConsumer), |
| + "Fill in all the info for fetching intervals."); |
| + |
| +base::TimeDelta GetDesiredFetchingInterval( |
| + FetchingInterval interval, |
| UserClassifier::UserClass user_class) { |
| double default_value_hours = 0.0; |
| - const int index = is_wifi ? 1 : 0; |
| + DCHECK(interval != FetchingInterval::COUNT); |
| + const unsigned int index = static_cast<unsigned int>(interval); |
| + DCHECK(index < arraysize(kDefaultFetchingIntervalRareNtpUser)); |
| + |
| const char* param_name = nullptr; |
| switch (user_class) { |
| case UserClassifier::UserClass::RARE_NTP_USER: |
| @@ -70,17 +100,19 @@ base::TimeDelta GetDesiredUpdateInterval( |
| } // namespace |
| struct SchedulingRemoteSuggestionsProvider::FetchingSchedule { |
| - base::TimeDelta interval_wifi; |
| - base::TimeDelta interval_fallback; |
| + base::TimeDelta interval_persistent_wifi; |
| + base::TimeDelta interval_persistent_fallback; |
| + base::TimeDelta interval_soft_on_usage_event; |
| static FetchingSchedule Empty() { |
| - return FetchingSchedule{base::TimeDelta(), |
| + return FetchingSchedule{base::TimeDelta(), base::TimeDelta(), |
| base::TimeDelta()}; |
| } |
| bool operator==(const FetchingSchedule& other) const { |
| - return interval_wifi == other.interval_wifi && |
| - interval_fallback == other.interval_fallback; |
| + return interval_persistent_wifi == other.interval_persistent_wifi && |
| + interval_persistent_fallback == other.interval_persistent_fallback && |
| + interval_soft_on_usage_event == other.interval_soft_on_usage_event; |
| } |
| bool operator!=(const FetchingSchedule& other) const { |
| @@ -88,7 +120,9 @@ struct SchedulingRemoteSuggestionsProvider::FetchingSchedule { |
| } |
| bool is_empty() const { |
| - return interval_wifi.is_zero() && interval_fallback.is_zero(); |
| + return interval_persistent_wifi.is_zero() && |
| + interval_persistent_fallback.is_zero() && |
| + interval_soft_on_usage_event.is_zero(); |
| } |
| }; |
| @@ -120,9 +154,12 @@ SchedulingRemoteSuggestionsProvider::~SchedulingRemoteSuggestionsProvider() = |
| // static |
| void SchedulingRemoteSuggestionsProvider::RegisterProfilePrefs( |
| PrefRegistrySimple* registry) { |
| - registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalWifi, 0); |
| - registry->RegisterInt64Pref(prefs::kSnippetBackgroundFetchingIntervalFallback, |
| + registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalWifi, 0); |
| + registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalFallback, |
| + 0); |
| + registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalOnUsageEvent, |
| 0); |
| + registry->RegisterInt64Pref(prefs::kSnippetLastBackgroundFetchAttempt, 0); |
| } |
| void SchedulingRemoteSuggestionsProvider::RescheduleFetching() { |
| @@ -132,10 +169,7 @@ void SchedulingRemoteSuggestionsProvider::RescheduleFetching() { |
| } |
| void SchedulingRemoteSuggestionsProvider::OnPersistentSchedulerWakeUp() { |
| - provider_->RefetchInTheBackground( |
| - base::MakeUnique<RemoteSuggestionsProvider::FetchStatusCallback>( |
| - base::Bind(&SchedulingRemoteSuggestionsProvider::OnFetchCompleted, |
| - base::Unretained(this)))); |
| + RefetchInTheBackground(/*callback=*/nullptr); |
| } |
| void SchedulingRemoteSuggestionsProvider::OnBrowserStartup() { |
| @@ -143,17 +177,33 @@ void SchedulingRemoteSuggestionsProvider::OnBrowserStartup() { |
| } |
| void SchedulingRemoteSuggestionsProvider::OnNTPOpened() { |
| - // TODO(jkrcal): Implement. |
| + if (!ShouldRefetchInTheBackgroundNow()) { |
| + return; |
| + } |
| + RefetchInTheBackground(/*callback=*/nullptr); |
| } |
| void SchedulingRemoteSuggestionsProvider::SetProviderStatusCallback( |
| - std::unique_ptr<ProviderStatusCallback> callback) { |
| + std::unique_ptr<ProviderStatusCallback> callback) { |
| provider_->SetProviderStatusCallback(std::move(callback)); |
| } |
| void SchedulingRemoteSuggestionsProvider::RefetchInTheBackground( |
| std::unique_ptr<FetchStatusCallback> callback) { |
| - provider_->RefetchInTheBackground(std::move(callback)); |
| + if (background_fetch_in_progress_) { |
| + if (callback) { |
| + callback->Run( |
| + Status(StatusCode::TEMPORARY_ERROR, "Background fetch in progress")); |
| + } |
| + return; |
| + } |
| + |
| + RemoteSuggestionsProvider::FetchStatusCallback wrapper_callback = base::Bind( |
| + &SchedulingRemoteSuggestionsProvider::RefetchInTheBackgroundFinished, |
| + base::Unretained(this), base::Passed(&callback)); |
| + provider_->RefetchInTheBackground( |
| + base::MakeUnique<RemoteSuggestionsProvider::FetchStatusCallback>( |
| + std::move(wrapper_callback))); |
| } |
| const NTPSnippetsFetcher* SchedulingRemoteSuggestionsProvider:: |
| @@ -237,41 +287,43 @@ void SchedulingRemoteSuggestionsProvider::OnProviderStatusChanged( |
| } |
| void SchedulingRemoteSuggestionsProvider::StartScheduling() { |
| - // The scheduler only exists on Android so far, it's null on other platforms. |
| - if (!persistent_scheduler_) { |
| - return; |
| - } |
| - |
| FetchingSchedule last_schedule = GetLastFetchingSchedule(); |
| FetchingSchedule schedule = GetDesiredFetchingSchedule(); |
| // Reset the schedule only if the parameters have changed. |
| if (last_schedule != schedule) { |
| - ApplyFetchingSchedule(schedule); |
| + ApplyFetchingSchedule(schedule, /*also_apply_persistent_schedule=*/true); |
| } |
| } |
| void SchedulingRemoteSuggestionsProvider::StopScheduling() { |
| - // The scheduler only exists on Android so far, it's null on other platforms. |
| - if (!persistent_scheduler_) { |
| - return; |
| - } |
| - |
| // Do not unschedule if already switched off |
| FetchingSchedule last_schedule = GetLastFetchingSchedule(); |
| if (last_schedule.is_empty()) { |
| return; |
| } |
| - persistent_scheduler_->Unschedule(); |
| + pref_service_->ClearPref(prefs::kSnippetLastBackgroundFetchAttempt); |
| + |
| + // The scheduler only exists on Android so far, it's null on other platforms. |
| + if (persistent_scheduler_) { |
| + persistent_scheduler_->Unschedule(); |
| + } |
| StoreLastFetchingSchedule(FetchingSchedule::Empty()); |
| } |
| void SchedulingRemoteSuggestionsProvider::ApplyFetchingSchedule( |
| - const FetchingSchedule& schedule) { |
| - persistent_scheduler_->Schedule(schedule.interval_wifi, |
| - schedule.interval_fallback); |
| + const FetchingSchedule& schedule, |
| + bool also_apply_persistent_schedule) { |
| + pref_service_->SetInt64(prefs::kSnippetLastBackgroundFetchAttempt, |
|
tschumann
2017/01/03 18:54:25
from an offline discussion: We also call this on S
jkrcal
2017/01/04 10:06:30
Fixed, in the end.
|
| + base::Time::Now().ToInternalValue()); |
| + |
| + // The scheduler only exists on Android so far, it's null on other platforms. |
| + if (persistent_scheduler_ && also_apply_persistent_schedule) { |
| + persistent_scheduler_->Schedule(schedule.interval_persistent_wifi, |
| + schedule.interval_persistent_fallback); |
| + } |
| StoreLastFetchingSchedule(schedule); |
| } |
| @@ -281,58 +333,88 @@ SchedulingRemoteSuggestionsProvider::GetDesiredFetchingSchedule() const { |
| UserClassifier::UserClass user_class = user_classifier_->GetUserClass(); |
| FetchingSchedule schedule; |
| - schedule.interval_wifi = |
| - GetDesiredUpdateInterval(/*is_wifi=*/true, user_class); |
| - schedule.interval_fallback = |
| - GetDesiredUpdateInterval(/*is_wifi=*/false, user_class); |
| + schedule.interval_persistent_wifi = |
| + GetDesiredFetchingInterval(FetchingInterval::PERSISTENT_WIFI, user_class); |
| + schedule.interval_persistent_fallback = GetDesiredFetchingInterval( |
| + FetchingInterval::PERSISTENT_FALLBACK, user_class); |
| + schedule.interval_soft_on_usage_event = GetDesiredFetchingInterval( |
| + FetchingInterval::SOFT_ON_USAGE_EVENT, user_class); |
| + |
| return schedule; |
| } |
| SchedulingRemoteSuggestionsProvider::FetchingSchedule |
| SchedulingRemoteSuggestionsProvider::GetLastFetchingSchedule() const { |
| FetchingSchedule schedule; |
| - schedule.interval_wifi = base::TimeDelta::FromInternalValue( |
| - pref_service_->GetInt64(prefs::kSnippetBackgroundFetchingIntervalWifi)); |
| - schedule.interval_fallback = |
| + schedule.interval_persistent_wifi = base::TimeDelta::FromInternalValue( |
| + pref_service_->GetInt64(prefs::kSnippetPersistentFetchingIntervalWifi)); |
| + schedule.interval_persistent_fallback = |
| base::TimeDelta::FromInternalValue(pref_service_->GetInt64( |
| - prefs::kSnippetBackgroundFetchingIntervalFallback)); |
| + prefs::kSnippetPersistentFetchingIntervalFallback)); |
| + schedule.interval_soft_on_usage_event = base::TimeDelta::FromInternalValue( |
| + pref_service_->GetInt64(prefs::kSnippetSoftFetchingIntervalOnUsageEvent)); |
| + |
| return schedule; |
| } |
| void SchedulingRemoteSuggestionsProvider::StoreLastFetchingSchedule( |
| const FetchingSchedule& schedule) { |
| + pref_service_->SetInt64(prefs::kSnippetPersistentFetchingIntervalWifi, |
| + schedule.interval_persistent_wifi.ToInternalValue()); |
| pref_service_->SetInt64( |
| - prefs::kSnippetBackgroundFetchingIntervalWifi, |
| - schedule.interval_wifi.ToInternalValue()); |
| + prefs::kSnippetPersistentFetchingIntervalFallback, |
| + schedule.interval_persistent_fallback.ToInternalValue()); |
| pref_service_->SetInt64( |
| - prefs::kSnippetBackgroundFetchingIntervalFallback, |
| - schedule.interval_fallback.ToInternalValue()); |
| + prefs::kSnippetSoftFetchingIntervalOnUsageEvent, |
| + schedule.interval_soft_on_usage_event.ToInternalValue()); |
| +} |
| + |
| +bool SchedulingRemoteSuggestionsProvider::ShouldRefetchInTheBackgroundNow() { |
| + if (!pref_service_->HasPrefPath(prefs::kSnippetLastBackgroundFetchAttempt)) { |
| + // Soft background fetches are switched off. |
| + return false; |
| + } |
| + |
| + FetchingSchedule schedule = GetLastFetchingSchedule(); |
| + |
| + base::Time first_allowed_fetch_time = |
| + base::Time::FromInternalValue( |
| + pref_service_->GetInt64(prefs::kSnippetLastBackgroundFetchAttempt)) + |
| + schedule.interval_soft_on_usage_event; |
| + return first_allowed_fetch_time <= base::Time::Now(); |
| } |
| void SchedulingRemoteSuggestionsProvider::FetchFinished( |
| const FetchDoneCallback& callback, |
| - Status status_code, |
| + Status fetch_status, |
| std::vector<ContentSuggestion> suggestions) { |
| - OnFetchCompleted(status_code); |
| - callback.Run(status_code, std::move(suggestions)); |
| + // Reschedule after a fetch. The persistent schedule is applied only after a |
| + // successful fetch. After a failed fetch, we want to keep the previous |
| + // persistent schedule intact so that we eventually get a persistent fallback |
| + // fetch (if the wifi persistent fetches keep failing). |
| + ApplyFetchingSchedule(GetLastFetchingSchedule(), |
| + /*also_apply_persistent_schedule=*/fetch_status.code == |
| + StatusCode::SUCCESS); |
| + |
| + callback.Run(fetch_status, std::move(suggestions)); |
| } |
| -void SchedulingRemoteSuggestionsProvider::OnFetchCompleted( |
| +void SchedulingRemoteSuggestionsProvider::RefetchInTheBackgroundFinished( |
| + std::unique_ptr<FetchStatusCallback> callback, |
| Status fetch_status) { |
| - // The scheduler only exists on Android so far, it's null on other platforms. |
| - if (!persistent_scheduler_) { |
| - return; |
| + background_fetch_in_progress_ = false; |
| + |
| + // Reschedule after a fetch. The persistent schedule is applied only after a |
| + // successful fetch. After a failed fetch, we want to keep the previous |
| + // persistent schedule intact so that we eventually get a persistent fallback |
| + // fetch (if the wifi persistent fetches keep failing). |
| + ApplyFetchingSchedule(GetLastFetchingSchedule(), |
| + /*also_apply_persistent_schedule=*/fetch_status.code == |
| + StatusCode::SUCCESS); |
| + |
| + if (callback) { |
| + callback->Run(fetch_status); |
| } |
| - |
| - if (fetch_status.code != StatusCode::SUCCESS) { |
| - return; |
| - } |
| - |
| - // Reschedule after a successful fetch. This resets all currently scheduled |
| - // fetches, to make sure the fallback interval triggers only if no wifi fetch |
| - // succeeded, and also that we don't do a background fetch immediately after |
| - // a user-initiated one. |
| - ApplyFetchingSchedule(GetLastFetchingSchedule()); |
| } |
| } // namespace ntp_snippets |