1 // Copyright 2017 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/feature_engagement/internal/feature_config_condition_validator.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10
11 #include "base/check.h"
12 #include "base/feature_list.h"
13 #include "base/notreached.h"
14 #include "base/stl_util.h"
15 #include "components/feature_engagement/internal/availability_model.h"
16 #include "components/feature_engagement/internal/display_lock_controller.h"
17 #include "components/feature_engagement/internal/event_model.h"
18 #include "components/feature_engagement/internal/proto/feature_event.pb.h"
19 #include "components/feature_engagement/public/configuration.h"
20 #include "components/feature_engagement/public/feature_list.h"
21
22 namespace feature_engagement {
23
FeatureConfigConditionValidator()24 FeatureConfigConditionValidator::FeatureConfigConditionValidator()
25 : currently_showing_(false) {}
26
27 FeatureConfigConditionValidator::~FeatureConfigConditionValidator() = default;
28
MeetsConditions(const base::Feature & feature,const FeatureConfig & config,const EventModel & event_model,const AvailabilityModel & availability_model,const DisplayLockController & display_lock_controller,uint32_t current_day) const29 ConditionValidator::Result FeatureConfigConditionValidator::MeetsConditions(
30 const base::Feature& feature,
31 const FeatureConfig& config,
32 const EventModel& event_model,
33 const AvailabilityModel& availability_model,
34 const DisplayLockController& display_lock_controller,
35 uint32_t current_day) const {
36 ConditionValidator::Result result(true);
37 result.event_model_ready_ok = event_model.IsReady();
38 result.currently_showing_ok = !currently_showing_;
39 result.feature_enabled_ok = base::FeatureList::IsEnabled(feature);
40 result.config_ok = config.valid;
41 result.used_ok =
42 EventConfigMeetsConditions(config.used, event_model, current_day);
43 result.trigger_ok =
44 EventConfigMeetsConditions(config.trigger, event_model, current_day);
45
46 for (const auto& event_config : config.event_configs) {
47 result.preconditions_ok &=
48 EventConfigMeetsConditions(event_config, event_model, current_day);
49 }
50
51 result.session_rate_ok =
52 SessionRateMeetsConditions(config.session_rate, feature);
53
54 result.availability_model_ready_ok = availability_model.IsReady();
55
56 result.availability_ok = AvailabilityMeetsConditions(
57 feature, config.availability, availability_model, current_day);
58
59 result.display_lock_ok = !display_lock_controller.IsDisplayLocked();
60
61 return result;
62 }
63
NotifyIsShowing(const base::Feature & feature,const FeatureConfig & config,const std::vector<std::string> & all_feature_names)64 void FeatureConfigConditionValidator::NotifyIsShowing(
65 const base::Feature& feature,
66 const FeatureConfig& config,
67 const std::vector<std::string>& all_feature_names) {
68 DCHECK(!currently_showing_);
69 DCHECK(base::FeatureList::IsEnabled(feature));
70
71 currently_showing_ = true;
72
73 switch (config.session_rate_impact.type) {
74 case SessionRateImpact::Type::ALL:
75 for (const std::string& feature_name : all_feature_names)
76 ++times_shown_for_feature_[feature_name];
77 break;
78 case SessionRateImpact::Type::NONE:
79 // Intentionally ignore, since no features should be impacted.
80 break;
81 case SessionRateImpact::Type::EXPLICIT:
82 DCHECK(config.session_rate_impact.affected_features.has_value());
83 for (const std::string& feature_name :
84 config.session_rate_impact.affected_features.value()) {
85 DCHECK(base::Contains(all_feature_names, feature_name));
86 ++times_shown_for_feature_[feature_name];
87 }
88 break;
89 default:
90 // All cases should be covered.
91 NOTREACHED();
92 }
93 }
94
NotifyDismissed(const base::Feature & feature)95 void FeatureConfigConditionValidator::NotifyDismissed(
96 const base::Feature& feature) {
97 currently_showing_ = false;
98 }
99
EventConfigMeetsConditions(const EventConfig & event_config,const EventModel & event_model,uint32_t current_day) const100 bool FeatureConfigConditionValidator::EventConfigMeetsConditions(
101 const EventConfig& event_config,
102 const EventModel& event_model,
103 uint32_t current_day) const {
104 uint32_t event_count = event_model.GetEventCount(
105 event_config.name, current_day, event_config.window);
106 return event_config.comparator.MeetsCriteria(event_count);
107 }
108
AvailabilityMeetsConditions(const base::Feature & feature,Comparator comparator,const AvailabilityModel & availability_model,uint32_t current_day) const109 bool FeatureConfigConditionValidator::AvailabilityMeetsConditions(
110 const base::Feature& feature,
111 Comparator comparator,
112 const AvailabilityModel& availability_model,
113 uint32_t current_day) const {
114 if (comparator.type == ANY)
115 return true;
116
117 base::Optional<uint32_t> availability_day =
118 availability_model.GetAvailability(feature);
119 if (!availability_day.has_value())
120 return false;
121
122 uint32_t days_available = current_day - availability_day.value();
123
124 // Ensure that availability days never wrap around.
125 if (availability_day.value() > current_day)
126 days_available = 0u;
127
128 return comparator.MeetsCriteria(days_available);
129 }
130
SessionRateMeetsConditions(const Comparator session_rate,const base::Feature & feature) const131 bool FeatureConfigConditionValidator::SessionRateMeetsConditions(
132 const Comparator session_rate,
133 const base::Feature& feature) const {
134 const auto it = times_shown_for_feature_.find(feature.name);
135 if (it == times_shown_for_feature_.end())
136 return session_rate.MeetsCriteria(0u);
137 return session_rate.MeetsCriteria(it->second);
138 }
139
140 } // namespace feature_engagement
141