Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(177)

Side by Side Diff: components/ntp_snippets/remote/scheduling_remote_suggestions_provider.cc

Issue 2774663002: [Remote suggestions] Refactor the scheduler (Closed)
Patch Set: Marc's nits #2 Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 #include "components/ntp_snippets/remote/scheduling_remote_suggestions_provider. h"
6
7 #include <random>
8 #include <string>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/metrics/field_trial_params.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/strings/string_split.h"
16 #include "base/time/clock.h"
17 #include "components/ntp_snippets/features.h"
18 #include "components/ntp_snippets/pref_names.h"
19 #include "components/ntp_snippets/remote/persistent_scheduler.h"
20 #include "components/ntp_snippets/status.h"
21 #include "components/ntp_snippets/user_classifier.h"
22 #include "components/prefs/pref_registry_simple.h"
23 #include "components/prefs/pref_service.h"
24
25 namespace ntp_snippets {
26
27 namespace {
28
29 // The FetchingInterval enum specifies overlapping time intervals that are used
30 // for scheduling the next remote suggestion fetch. Therefore a timer is created
31 // for each interval. Initially all the timers are started at the same time.
32 // Fetches are
33 // only performed when certain conditions associated with the intervals are
34 // met. If a fetch failed, then only the corresponding timer is reset. The
35 // other timers are not touched.
36 // TODO(markusheintz): Describe the individual intervals.
37 enum class FetchingInterval {
38 PERSISTENT_FALLBACK,
39 PERSISTENT_WIFI,
40 SOFT_ON_USAGE_EVENT,
41 SOFT_ON_NTP_OPENED,
42 COUNT
43 };
44
45 // The following arrays specify default values for remote suggestions fetch
46 // intervals corresponding to individual user classes. The user classes are
47 // defined by the user classifier. There must be an array for each user class.
48 // The values of each array specify a default time interval for the intervals
49 // defined by the enum FetchingInterval. The default time intervals defined in
50 // the arrays can be overridden using different variation parameters.
51 const double kDefaultFetchingIntervalHoursRareNtpUser[] = {48.0, 24.0, 12.0,
52 6.0};
53 const double kDefaultFetchingIntervalHoursActiveNtpUser[] = {24.0, 6.0, 2.0,
54 2.0};
55 const double kDefaultFetchingIntervalHoursActiveSuggestionsConsumer[] = {
56 24.0, 6.0, 2.0, 1.0};
57
58 // Variation parameters than can be used to override the default fetching
59 // intervals.
60 const char* kFetchingIntervalParamNameRareNtpUser[] = {
61 "fetching_interval_hours-fallback-rare_ntp_user",
62 "fetching_interval_hours-wifi-rare_ntp_user",
63 "soft_fetching_interval_hours-active-rare_ntp_user",
64 "soft_on_ntp_opened_interval_hours-rare_ntp_user"};
65 const char* kFetchingIntervalParamNameActiveNtpUser[] = {
66 "fetching_interval_hours-fallback-active_ntp_user",
67 "fetching_interval_hours-wifi-active_ntp_user",
68 "soft_fetching_interval_hours-active-active_ntp_user",
69 "soft_on_ntp_opened_interval_hours-active_ntp_user"};
70 const char* kFetchingIntervalParamNameActiveSuggestionsConsumer[] = {
71 "fetching_interval_hours-fallback-active_suggestions_consumer",
72 "fetching_interval_hours-wifi-active_suggestions_consumer",
73 "soft_fetching_interval_hours-active-active_suggestions_consumer",
74 "soft_on_ntp_opened_interval_hours-active_suggestions_consumer"};
75
76 static_assert(
77 static_cast<unsigned int>(FetchingInterval::COUNT) ==
78 arraysize(kDefaultFetchingIntervalHoursRareNtpUser) &&
79 static_cast<unsigned int>(FetchingInterval::COUNT) ==
80 arraysize(kDefaultFetchingIntervalHoursActiveNtpUser) &&
81 static_cast<unsigned int>(FetchingInterval::COUNT) ==
82 arraysize(kDefaultFetchingIntervalHoursActiveSuggestionsConsumer) &&
83 static_cast<unsigned int>(FetchingInterval::COUNT) ==
84 arraysize(kFetchingIntervalParamNameRareNtpUser) &&
85 static_cast<unsigned int>(FetchingInterval::COUNT) ==
86 arraysize(kFetchingIntervalParamNameActiveNtpUser) &&
87 static_cast<unsigned int>(FetchingInterval::COUNT) ==
88 arraysize(kFetchingIntervalParamNameActiveSuggestionsConsumer),
89 "Fill in all the info for fetching intervals.");
90
91 const char* kTriggerTypeNames[] = {"persistent_scheduler_wake_up", "ntp_opened",
92 "browser_foregrounded",
93 "browser_cold_start"};
94
95 const char* kTriggerTypesParamName = "scheduler_trigger_types";
96 const char* kTriggerTypesParamValueForEmptyList = "-";
97
98 const int kBlockBackgroundFetchesMinutesAfterClearingHistory = 30;
99
100 // Returns the time interval to use for scheduling remote suggestion fetches for
101 // the given interval and user_class.
102 base::TimeDelta GetDesiredFetchingInterval(
103 FetchingInterval interval,
104 UserClassifier::UserClass user_class) {
105 DCHECK(interval != FetchingInterval::COUNT);
106 const unsigned int index = static_cast<unsigned int>(interval);
107 DCHECK(index < arraysize(kDefaultFetchingIntervalHoursRareNtpUser));
108
109 double default_value_hours = 0.0;
110 const char* param_name = nullptr;
111 switch (user_class) {
112 case UserClassifier::UserClass::RARE_NTP_USER:
113 default_value_hours = kDefaultFetchingIntervalHoursRareNtpUser[index];
114 param_name = kFetchingIntervalParamNameRareNtpUser[index];
115 break;
116 case UserClassifier::UserClass::ACTIVE_NTP_USER:
117 default_value_hours = kDefaultFetchingIntervalHoursActiveNtpUser[index];
118 param_name = kFetchingIntervalParamNameActiveNtpUser[index];
119 break;
120 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
121 default_value_hours =
122 kDefaultFetchingIntervalHoursActiveSuggestionsConsumer[index];
123 param_name = kFetchingIntervalParamNameActiveSuggestionsConsumer[index];
124 break;
125 }
126
127 double value_hours = base::GetFieldTrialParamByFeatureAsDouble(
128 ntp_snippets::kArticleSuggestionsFeature, param_name,
129 default_value_hours);
130
131 return base::TimeDelta::FromSecondsD(value_hours * 3600.0);
132 }
133
134 } // namespace
135
136 class EulaState : public web_resource::EulaAcceptedNotifier::Observer {
137 public:
138 EulaState(PrefService* local_state_prefs,
139 RemoteSuggestionsScheduler* scheduler)
140 : eula_notifier_(
141 web_resource::EulaAcceptedNotifier::Create(local_state_prefs)),
142 scheduler_(scheduler) {
143 // EulaNotifier is not constructed on some platforms (such as desktop).
144 if (!eula_notifier_) {
145 return;
146 }
147
148 // Register the observer.
149 eula_notifier_->Init(this);
150 }
151
152 ~EulaState() = default;
153
154 bool IsEulaAccepted() {
155 if (!eula_notifier_) {
156 return true;
157 }
158 return eula_notifier_->IsEulaAccepted();
159 }
160
161 // EulaAcceptedNotifier::Observer implementation.
162 void OnEulaAccepted() override {
163 // Emulate a browser foregrounded event.
164 scheduler_->OnBrowserForegrounded();
165 }
166
167 private:
168 std::unique_ptr<web_resource::EulaAcceptedNotifier> eula_notifier_;
169 RemoteSuggestionsScheduler* scheduler_;
170
171 DISALLOW_COPY_AND_ASSIGN(EulaState);
172 };
173
174 // static
175 SchedulingRemoteSuggestionsProvider::FetchingSchedule
176 SchedulingRemoteSuggestionsProvider::FetchingSchedule::Empty() {
177 return FetchingSchedule{base::TimeDelta(), base::TimeDelta(),
178 base::TimeDelta(), base::TimeDelta()};
179 }
180
181 bool SchedulingRemoteSuggestionsProvider::FetchingSchedule::operator==(
182 const FetchingSchedule& other) const {
183 return interval_persistent_wifi == other.interval_persistent_wifi &&
184 interval_persistent_fallback == other.interval_persistent_fallback &&
185 interval_soft_on_usage_event == other.interval_soft_on_usage_event &&
186 interval_soft_on_ntp_opened == other.interval_soft_on_ntp_opened;
187 }
188
189 bool SchedulingRemoteSuggestionsProvider::FetchingSchedule::operator!=(
190 const FetchingSchedule& other) const {
191 return !operator==(other);
192 }
193
194 bool SchedulingRemoteSuggestionsProvider::FetchingSchedule::is_empty() const {
195 return interval_persistent_wifi.is_zero() &&
196 interval_persistent_fallback.is_zero() &&
197 interval_soft_on_usage_event.is_zero() &&
198 interval_soft_on_ntp_opened.is_zero();
199 }
200
201 // The TriggerType enum specifies values for the events that can trigger
202 // fetching remote suggestions. These values are written to logs. New enum
203 // values can be added, but existing enums must never be renumbered or deleted
204 // and reused. When adding new entries, also update the array
205 // |kTriggerTypeNames| above.
206 enum class SchedulingRemoteSuggestionsProvider::TriggerType {
207 PERSISTENT_SCHEDULER_WAKE_UP = 0,
208 NTP_OPENED = 1,
209 BROWSER_FOREGROUNDED = 2,
210 BROWSER_COLD_START = 3,
211 COUNT
212 };
213
214 SchedulingRemoteSuggestionsProvider::SchedulingRemoteSuggestionsProvider(
215 Observer* observer,
216 std::unique_ptr<RemoteSuggestionsProvider> provider,
217 PersistentScheduler* persistent_scheduler,
218 const UserClassifier* user_classifier,
219 PrefService* profile_prefs,
220 PrefService* local_state_prefs,
221 std::unique_ptr<base::Clock> clock)
222 : RemoteSuggestionsProvider(observer),
223 RemoteSuggestionsScheduler(),
224 provider_(std::move(provider)),
225 persistent_scheduler_(persistent_scheduler),
226 background_fetch_in_progress_(false),
227 user_classifier_(user_classifier),
228 request_throttler_rare_ntp_user_(
229 profile_prefs,
230 RequestThrottler::RequestType::
231 CONTENT_SUGGESTION_FETCHER_RARE_NTP_USER),
232 request_throttler_active_ntp_user_(
233 profile_prefs,
234 RequestThrottler::RequestType::
235 CONTENT_SUGGESTION_FETCHER_ACTIVE_NTP_USER),
236 request_throttler_active_suggestions_consumer_(
237 profile_prefs,
238 RequestThrottler::RequestType::
239 CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER),
240 eula_state_(base::MakeUnique<EulaState>(local_state_prefs, this)),
241 profile_prefs_(profile_prefs),
242 clock_(std::move(clock)),
243 enabled_triggers_(GetEnabledTriggerTypes()) {
244 DCHECK(user_classifier);
245 DCHECK(profile_prefs);
246
247 LoadLastFetchingSchedule();
248 }
249
250 SchedulingRemoteSuggestionsProvider::~SchedulingRemoteSuggestionsProvider() =
251 default;
252
253 // static
254 void SchedulingRemoteSuggestionsProvider::RegisterProfilePrefs(
255 PrefRegistrySimple* registry) {
256 registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalWifi, 0);
257 registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalFallback,
258 0);
259 registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalOnUsageEvent,
260 0);
261 registry->RegisterInt64Pref(prefs::kSnippetLastFetchAttempt, 0);
262 registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalOnNtpOpened,
263 0);
264 }
265
266 void SchedulingRemoteSuggestionsProvider::OnProviderActivated() {
267 StartScheduling();
268 }
269
270 void SchedulingRemoteSuggestionsProvider::OnProviderDeactivated() {
271 StopScheduling();
272 }
273
274 void SchedulingRemoteSuggestionsProvider::OnSuggestionsCleared() {
275 // Some user action causes suggestions to be cleared, fetch now (as an
276 // interactive request).
277 ReloadSuggestions();
278 }
279
280 void SchedulingRemoteSuggestionsProvider::OnHistoryCleared() {
281 // Due to privacy, we should not fetch for a while (unless the user explicitly
282 // asks for new suggestions) to give sync the time to propagate the changes in
283 // history to the server.
284 background_fetches_allowed_after_ =
285 clock_->Now() + base::TimeDelta::FromMinutes(
286 kBlockBackgroundFetchesMinutesAfterClearingHistory);
287 // After that time elapses, we should fetch as soon as possible.
288 ClearLastFetchAttemptTime();
289 }
290
291 void SchedulingRemoteSuggestionsProvider::RescheduleFetching() {
292 // Force the reschedule by stopping and starting it again.
293 StopScheduling();
294 StartScheduling();
295 }
296
297 void SchedulingRemoteSuggestionsProvider::OnPersistentSchedulerWakeUp() {
298 RefetchInTheBackgroundIfEnabled(TriggerType::PERSISTENT_SCHEDULER_WAKE_UP);
299 }
300
301 void SchedulingRemoteSuggestionsProvider::OnBrowserForegrounded() {
302 // TODO(jkrcal): Consider that this is called whenever we open or return to an
303 // Activity. Therefore, keep work light for fast start up calls.
304 if (!ShouldRefetchInTheBackgroundNow(TriggerType::BROWSER_FOREGROUNDED)) {
305 return;
306 }
307
308 RefetchInTheBackgroundIfEnabled(TriggerType::BROWSER_FOREGROUNDED);
309 }
310
311 void SchedulingRemoteSuggestionsProvider::OnBrowserColdStart() {
312 // TODO(fhorschig|jkrcal): Consider that work here must be kept light for fast
313 // cold start ups.
314 if (!ShouldRefetchInTheBackgroundNow(TriggerType::BROWSER_COLD_START)) {
315 return;
316 }
317
318 RefetchInTheBackgroundIfEnabled(TriggerType::BROWSER_COLD_START);
319 }
320
321 void SchedulingRemoteSuggestionsProvider::OnNTPOpened() {
322 if (!ShouldRefetchInTheBackgroundNow(TriggerType::NTP_OPENED)) {
323 return;
324 }
325
326 RefetchInTheBackgroundIfEnabled(TriggerType::NTP_OPENED);
327 }
328
329 void SchedulingRemoteSuggestionsProvider::RefetchInTheBackground(
330 std::unique_ptr<FetchStatusCallback> callback) {
331 if (background_fetch_in_progress_) {
332 if (callback) {
333 callback->Run(
334 Status(StatusCode::TEMPORARY_ERROR, "Background fetch in progress"));
335 }
336 return;
337 }
338
339 if (!AcquireQuota(/*interactive_request=*/false)) {
340 if (callback) {
341 callback->Run(Status(StatusCode::TEMPORARY_ERROR,
342 "Non-interactive quota exceeded"));
343 }
344 return;
345 }
346
347 background_fetch_in_progress_ = true;
348 RemoteSuggestionsProvider::FetchStatusCallback wrapper_callback = base::Bind(
349 &SchedulingRemoteSuggestionsProvider::RefetchInTheBackgroundFinished,
350 base::Unretained(this), base::Passed(&callback));
351 provider_->RefetchInTheBackground(
352 base::MakeUnique<RemoteSuggestionsProvider::FetchStatusCallback>(
353 std::move(wrapper_callback)));
354 }
355
356 const RemoteSuggestionsFetcher*
357 SchedulingRemoteSuggestionsProvider::suggestions_fetcher_for_debugging() const {
358 return provider_->suggestions_fetcher_for_debugging();
359 }
360
361 CategoryStatus SchedulingRemoteSuggestionsProvider::GetCategoryStatus(
362 Category category) {
363 return provider_->GetCategoryStatus(category);
364 }
365
366 CategoryInfo SchedulingRemoteSuggestionsProvider::GetCategoryInfo(
367 Category category) {
368 return provider_->GetCategoryInfo(category);
369 }
370
371 void SchedulingRemoteSuggestionsProvider::DismissSuggestion(
372 const ContentSuggestion::ID& suggestion_id) {
373 provider_->DismissSuggestion(suggestion_id);
374 }
375
376 void SchedulingRemoteSuggestionsProvider::FetchSuggestionImage(
377 const ContentSuggestion::ID& suggestion_id,
378 const ImageFetchedCallback& callback) {
379 provider_->FetchSuggestionImage(suggestion_id, callback);
380 }
381
382 void SchedulingRemoteSuggestionsProvider::Fetch(
383 const Category& category,
384 const std::set<std::string>& known_suggestion_ids,
385 const FetchDoneCallback& callback) {
386 if (!AcquireQuota(/*interactive_request=*/true)) {
387 if (callback) {
388 callback.Run(
389 Status(StatusCode::TEMPORARY_ERROR, "Interactive quota exceeded"),
390 std::vector<ContentSuggestion>());
391 }
392 return;
393 }
394
395 provider_->Fetch(
396 category, known_suggestion_ids,
397 base::Bind(&SchedulingRemoteSuggestionsProvider::FetchFinished,
398 base::Unretained(this), callback));
399 }
400
401 void SchedulingRemoteSuggestionsProvider::ReloadSuggestions() {
402 if (!AcquireQuota(/*interactive_request=*/true)) {
403 return;
404 }
405
406 provider_->ReloadSuggestions();
407 }
408
409 void SchedulingRemoteSuggestionsProvider::ClearHistory(
410 base::Time begin,
411 base::Time end,
412 const base::Callback<bool(const GURL& url)>& filter) {
413 provider_->ClearHistory(begin, end, filter);
414 }
415
416 void SchedulingRemoteSuggestionsProvider::ClearCachedSuggestions(
417 Category category) {
418 provider_->ClearCachedSuggestions(category);
419 }
420
421 void SchedulingRemoteSuggestionsProvider::OnSignInStateChanged() {
422 provider_->OnSignInStateChanged();
423 }
424
425 void SchedulingRemoteSuggestionsProvider::GetDismissedSuggestionsForDebugging(
426 Category category,
427 const DismissedSuggestionsCallback& callback) {
428 provider_->GetDismissedSuggestionsForDebugging(category, callback);
429 }
430
431 void SchedulingRemoteSuggestionsProvider::ClearDismissedSuggestionsForDebugging(
432 Category category) {
433 provider_->ClearDismissedSuggestionsForDebugging(category);
434 }
435
436 void SchedulingRemoteSuggestionsProvider::StartScheduling() {
437 FetchingSchedule new_schedule = GetDesiredFetchingSchedule();
438
439 if (schedule_ == new_schedule) {
440 // Do not schedule if nothing has changed;
441 return;
442 }
443
444 schedule_ = new_schedule;
445 StoreFetchingSchedule();
446 ApplyPersistentFetchingSchedule();
447 }
448
449 void SchedulingRemoteSuggestionsProvider::StopScheduling() {
450 if (schedule_.is_empty()) {
451 // Do not unschedule if already switched off.
452 return;
453 }
454
455 schedule_ = FetchingSchedule::Empty();
456 StoreFetchingSchedule();
457 ApplyPersistentFetchingSchedule();
458 }
459
460 void SchedulingRemoteSuggestionsProvider::ApplyPersistentFetchingSchedule() {
461 // The scheduler only exists on Android so far, it's null on other platforms.
462 if (persistent_scheduler_) {
463 if (schedule_.is_empty()) {
464 persistent_scheduler_->Unschedule();
465 } else {
466 persistent_scheduler_->Schedule(schedule_.interval_persistent_wifi,
467 schedule_.interval_persistent_fallback);
468 }
469 }
470 }
471
472 SchedulingRemoteSuggestionsProvider::FetchingSchedule
473 SchedulingRemoteSuggestionsProvider::GetDesiredFetchingSchedule() const {
474 UserClassifier::UserClass user_class = user_classifier_->GetUserClass();
475
476 FetchingSchedule schedule;
477 schedule.interval_persistent_wifi =
478 GetDesiredFetchingInterval(FetchingInterval::PERSISTENT_WIFI, user_class);
479 schedule.interval_persistent_fallback = GetDesiredFetchingInterval(
480 FetchingInterval::PERSISTENT_FALLBACK, user_class);
481 schedule.interval_soft_on_usage_event = GetDesiredFetchingInterval(
482 FetchingInterval::SOFT_ON_USAGE_EVENT, user_class);
483 schedule.interval_soft_on_ntp_opened = GetDesiredFetchingInterval(
484 FetchingInterval::SOFT_ON_NTP_OPENED, user_class);
485
486 return schedule;
487 }
488
489 void SchedulingRemoteSuggestionsProvider::LoadLastFetchingSchedule() {
490 schedule_.interval_persistent_wifi = base::TimeDelta::FromInternalValue(
491 profile_prefs_->GetInt64(prefs::kSnippetPersistentFetchingIntervalWifi));
492 schedule_.interval_persistent_fallback =
493 base::TimeDelta::FromInternalValue(profile_prefs_->GetInt64(
494 prefs::kSnippetPersistentFetchingIntervalFallback));
495 schedule_.interval_soft_on_usage_event =
496 base::TimeDelta::FromInternalValue(profile_prefs_->GetInt64(
497 prefs::kSnippetSoftFetchingIntervalOnUsageEvent));
498 schedule_.interval_soft_on_ntp_opened = base::TimeDelta::FromInternalValue(
499 profile_prefs_->GetInt64(prefs::kSnippetSoftFetchingIntervalOnNtpOpened));
500 }
501
502 void SchedulingRemoteSuggestionsProvider::StoreFetchingSchedule() {
503 profile_prefs_->SetInt64(
504 prefs::kSnippetPersistentFetchingIntervalWifi,
505 schedule_.interval_persistent_wifi.ToInternalValue());
506 profile_prefs_->SetInt64(
507 prefs::kSnippetPersistentFetchingIntervalFallback,
508 schedule_.interval_persistent_fallback.ToInternalValue());
509 profile_prefs_->SetInt64(
510 prefs::kSnippetSoftFetchingIntervalOnUsageEvent,
511 schedule_.interval_soft_on_usage_event.ToInternalValue());
512 profile_prefs_->SetInt64(
513 prefs::kSnippetSoftFetchingIntervalOnNtpOpened,
514 schedule_.interval_soft_on_ntp_opened.ToInternalValue());
515 }
516
517 void SchedulingRemoteSuggestionsProvider::RefetchInTheBackgroundIfEnabled(
518 SchedulingRemoteSuggestionsProvider::TriggerType trigger) {
519 if (BackgroundFetchesDisabled(trigger)) {
520 return;
521 }
522
523 UMA_HISTOGRAM_ENUMERATION(
524 "NewTabPage.ContentSuggestions.BackgroundFetchTrigger",
525 static_cast<int>(trigger), static_cast<int>(TriggerType::COUNT));
526
527 RefetchInTheBackground(/*callback=*/nullptr);
528 }
529
530 bool SchedulingRemoteSuggestionsProvider::ShouldRefetchInTheBackgroundNow(
531 SchedulingRemoteSuggestionsProvider::TriggerType trigger) {
532 const base::Time last_fetch_attempt_time = base::Time::FromInternalValue(
533 profile_prefs_->GetInt64(prefs::kSnippetLastFetchAttempt));
534 base::Time first_allowed_fetch_time;
535 switch (trigger) {
536 case TriggerType::NTP_OPENED:
537 first_allowed_fetch_time =
538 last_fetch_attempt_time + schedule_.interval_soft_on_ntp_opened;
539 break;
540 case TriggerType::BROWSER_FOREGROUNDED:
541 case TriggerType::BROWSER_COLD_START:
542 first_allowed_fetch_time =
543 last_fetch_attempt_time + schedule_.interval_soft_on_usage_event;
544 break;
545 case TriggerType::PERSISTENT_SCHEDULER_WAKE_UP:
546 case TriggerType::COUNT:
547 NOTREACHED();
548 break;
549 }
550 base::Time now = clock_->Now();
551
552 return background_fetches_allowed_after_ <= now &&
553 first_allowed_fetch_time <= now;
554 }
555
556 bool SchedulingRemoteSuggestionsProvider::BackgroundFetchesDisabled(
557 SchedulingRemoteSuggestionsProvider::TriggerType trigger) const {
558 if (schedule_.is_empty()) {
559 return true; // Background fetches are disabled in general.
560 }
561
562 if (enabled_triggers_.count(trigger) == 0) {
563 return true; // Background fetches for |trigger| are not enabled.
564 }
565
566 if (!eula_state_->IsEulaAccepted()) {
567 return true; // No background fetches are allowed before EULA is accepted.
568 }
569
570 return false;
571 }
572
573 bool SchedulingRemoteSuggestionsProvider::AcquireQuota(
574 bool interactive_request) {
575 switch (user_classifier_->GetUserClass()) {
576 case UserClassifier::UserClass::RARE_NTP_USER:
577 return request_throttler_rare_ntp_user_.DemandQuotaForRequest(
578 interactive_request);
579 case UserClassifier::UserClass::ACTIVE_NTP_USER:
580 return request_throttler_active_ntp_user_.DemandQuotaForRequest(
581 interactive_request);
582 case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER:
583 return request_throttler_active_suggestions_consumer_
584 .DemandQuotaForRequest(interactive_request);
585 }
586 NOTREACHED();
587 return false;
588 }
589
590 void SchedulingRemoteSuggestionsProvider::FetchFinished(
591 const FetchDoneCallback& callback,
592 Status fetch_status,
593 std::vector<ContentSuggestion> suggestions) {
594 OnFetchCompleted(fetch_status);
595 if (callback) {
596 callback.Run(fetch_status, std::move(suggestions));
597 }
598 }
599
600 void SchedulingRemoteSuggestionsProvider::RefetchInTheBackgroundFinished(
601 std::unique_ptr<FetchStatusCallback> callback,
602 Status fetch_status) {
603 background_fetch_in_progress_ = false;
604 OnFetchCompleted(fetch_status);
605 if (callback) {
606 callback->Run(fetch_status);
607 }
608 }
609
610 void SchedulingRemoteSuggestionsProvider::OnFetchCompleted(
611 Status fetch_status) {
612 profile_prefs_->SetInt64(prefs::kSnippetLastFetchAttempt,
613 clock_->Now().ToInternalValue());
614
615 // Reschedule after a fetch. The persistent schedule is applied only after a
616 // successful fetch. After a failed fetch, we want to keep the previous
617 // persistent schedule intact so that we eventually get a persistent
618 // fallback fetch (if the wifi persistent fetches keep failing).
619 if (fetch_status.code != StatusCode::SUCCESS) {
620 return;
621 }
622 ApplyPersistentFetchingSchedule();
623 }
624
625 void SchedulingRemoteSuggestionsProvider::ClearLastFetchAttemptTime() {
626 profile_prefs_->ClearPref(prefs::kSnippetLastFetchAttempt);
627 }
628
629 std::set<SchedulingRemoteSuggestionsProvider::TriggerType>
630 SchedulingRemoteSuggestionsProvider::GetEnabledTriggerTypes() {
631 static_assert(static_cast<unsigned int>(TriggerType::COUNT) ==
632 arraysize(kTriggerTypeNames),
633 "Fill in names for trigger types.");
634
635 std::string param_value = base::GetFieldTrialParamValueByFeature(
636 ntp_snippets::kArticleSuggestionsFeature, kTriggerTypesParamName);
637 if (param_value == kTriggerTypesParamValueForEmptyList) {
638 return std::set<TriggerType>();
639 }
640
641 std::vector<std::string> tokens = base::SplitString(
642 param_value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
643 if (tokens.empty()) {
644 return GetDefaultEnabledTriggerTypes();
645 }
646
647 std::set<TriggerType> enabled_types;
648 for (const auto& token : tokens) {
649 auto** it = std::find(std::begin(kTriggerTypeNames),
650 std::end(kTriggerTypeNames), token);
651 if (it == std::end(kTriggerTypeNames)) {
652 DLOG(WARNING) << "Failed to parse variation param "
653 << kTriggerTypesParamName << " with string value "
654 << param_value
655 << " into a comma-separated list of keywords. "
656 << "Unknown token " << token
657 << " found. Falling back to default value.";
658 return GetDefaultEnabledTriggerTypes();
659 }
660
661 // Add the enabled type represented by |token| into the result set.
662 enabled_types.insert(
663 static_cast<TriggerType>(it - std::begin(kTriggerTypeNames)));
664 }
665 return enabled_types;
666 }
667
668 std::set<SchedulingRemoteSuggestionsProvider::TriggerType>
669 SchedulingRemoteSuggestionsProvider::GetDefaultEnabledTriggerTypes() {
670 return {TriggerType::PERSISTENT_SCHEDULER_WAKE_UP, TriggerType::NTP_OPENED,
671 TriggerType::BROWSER_FOREGROUNDED};
672 }
673
674 } // namespace ntp_snippets
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698