accuracy_service.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // Copyright 2021 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. #include "components/accuracy_tips/accuracy_service.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/callback.h"
  8. #include "base/callback_forward.h"
  9. #include "base/containers/contains.h"
  10. #include "base/memory/scoped_refptr.h"
  11. #include "base/metrics/histogram_functions.h"
  12. #include "base/observer_list.h"
  13. #include "base/time/time.h"
  14. #include "base/values.h"
  15. #include "components/accuracy_tips/accuracy_tip_interaction.h"
  16. #include "components/accuracy_tips/accuracy_tip_safe_browsing_client.h"
  17. #include "components/accuracy_tips/accuracy_tip_status.h"
  18. #include "components/accuracy_tips/features.h"
  19. #include "components/accuracy_tips/pref_names.h"
  20. #include "components/pref_registry/pref_registry_syncable.h"
  21. #include "components/prefs/pref_service.h"
  22. #include "components/prefs/scoped_user_pref_update.h"
  23. #include "components/unified_consent/pref_names.h"
  24. #include "content/public/browser/web_contents.h"
  25. #include "services/metrics/public/cpp/metrics_utils.h"
  26. #include "services/metrics/public/cpp/ukm_builders.h"
  27. #include "url/gurl.h"
  28. namespace accuracy_tips {
  29. // Returns a suffix for accuracy tips histograms.
  30. // Needs to match AccuracyTipInteractions from histogram_suffixes_list.xml.
  31. const std::string GetHistogramSuffix(AccuracyTipInteraction interaction) {
  32. switch (interaction) {
  33. case AccuracyTipInteraction::kNoAction:
  34. return "NoAction";
  35. case AccuracyTipInteraction::kLearnMore:
  36. return "LearnMore";
  37. case AccuracyTipInteraction::kOptOut:
  38. return "OptOut";
  39. case AccuracyTipInteraction::kClosed:
  40. return "Closed";
  41. case AccuracyTipInteraction::kIgnore:
  42. return "Ignore";
  43. case AccuracyTipInteraction::kPermissionRequested:
  44. return "PermissionRequested";
  45. case AccuracyTipInteraction::kDisabledByExperiment:
  46. NOTREACHED(); // We don't need specific histograms for this.
  47. return "";
  48. }
  49. }
  50. // Returns the preference to store feature state in. Uses a different pref if
  51. // the UI is disabled to avoid dark-launch experiments affecting real usage.
  52. const char* GetLastShownPrefName(bool disable_ui) {
  53. return disable_ui ? prefs::kLastAccuracyTipShownDisabledUi
  54. : prefs::kLastAccuracyTipShown;
  55. }
  56. const char* GetPreviousInteractionsPrefName(bool disable_ui) {
  57. return disable_ui ? prefs::kPreviousInteractionsDisabledUi
  58. : prefs::kPreviousInteractions;
  59. }
  60. using AccuracyCheckCallback = AccuracyService::AccuracyCheckCallback;
  61. // static
  62. void AccuracyService::RegisterProfilePrefs(
  63. user_prefs::PrefRegistrySyncable* registry) {
  64. const bool disable_ui = features::kDisableUi.Get();
  65. registry->RegisterTimePref(GetLastShownPrefName(disable_ui), base::Time());
  66. registry->RegisterListPref(GetPreviousInteractionsPrefName(disable_ui));
  67. }
  68. AccuracyService::AccuracyService(
  69. std::unique_ptr<Delegate> delegate,
  70. PrefService* pref_service,
  71. scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> sb_database,
  72. history::HistoryService* history_service,
  73. scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
  74. scoped_refptr<base::SequencedTaskRunner> io_task_runner)
  75. : delegate_(std::move(delegate)),
  76. pref_service_(pref_service),
  77. ui_task_runner_(ui_task_runner),
  78. sample_url_(GURL(features::kSampleUrl.Get())),
  79. time_between_prompts_(features::kTimeBetweenPrompts.Get()),
  80. disable_ui_(features::kDisableUi.Get()) {
  81. if (sb_database) {
  82. sb_client_ = base::MakeRefCounted<AccuracyTipSafeBrowsingClient>(
  83. std::move(sb_database), std::move(ui_task_runner),
  84. std::move(io_task_runner));
  85. }
  86. if (history_service) {
  87. history_service_observation_.Observe(history_service);
  88. }
  89. }
  90. AccuracyService::~AccuracyService() = default;
  91. void AccuracyService::Shutdown() {
  92. if (sb_client_) {
  93. sb_client_->Shutdown();
  94. sb_client_ = nullptr;
  95. }
  96. history_service_observation_.Reset();
  97. }
  98. void AccuracyService::CheckAccuracyStatus(const GURL& url,
  99. AccuracyCheckCallback callback) {
  100. DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
  101. const base::Value::List& last_interactions =
  102. pref_service_->GetValueList(GetPreviousInteractionsPrefName(disable_ui_));
  103. const base::Value opt_out_value(
  104. static_cast<int>(AccuracyTipInteraction::kOptOut));
  105. if (base::Contains(last_interactions, opt_out_value)) {
  106. return std::move(callback).Run(AccuracyTipStatus::kOptOut);
  107. }
  108. base::Time last_shown =
  109. pref_service_->GetTime(GetLastShownPrefName(disable_ui_));
  110. if (clock_->Now() - last_shown < time_between_prompts_) {
  111. return std::move(callback).Run(AccuracyTipStatus::kRateLimited);
  112. }
  113. if (sample_url_.is_valid() && url == sample_url_) {
  114. return OnAccuracyStatusReceived(url, std::move(callback),
  115. AccuracyTipStatus::kShowAccuracyTip);
  116. }
  117. if (!sb_client_) {
  118. return std::move(callback).Run(AccuracyTipStatus::kNone);
  119. }
  120. sb_client_->CheckAccuracyStatus(
  121. url,
  122. base::BindOnce(&AccuracyService::OnAccuracyStatusReceived,
  123. weak_factory_.GetWeakPtr(), url, std::move(callback)));
  124. }
  125. void AccuracyService::OnAccuracyStatusReceived(const GURL& url,
  126. AccuracyCheckCallback callback,
  127. AccuracyTipStatus status) {
  128. if (status == AccuracyTipStatus::kShowAccuracyTip &&
  129. delegate_->IsEngagementHigh(url)) {
  130. return std::move(callback).Run(AccuracyTipStatus::kHighEnagagement);
  131. }
  132. return std::move(callback).Run(status);
  133. }
  134. void AccuracyService::MaybeShowAccuracyTip(content::WebContents* web_contents) {
  135. DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
  136. pref_service_->SetTime(GetLastShownPrefName(disable_ui_), clock_->Now());
  137. if (disable_ui_) {
  138. return OnAccuracyTipClosed(
  139. base::TimeTicks(),
  140. web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId(),
  141. AccuracyTipInteraction::kDisabledByExperiment);
  142. }
  143. bool show_opt_out =
  144. pref_service_->GetValueList(GetPreviousInteractionsPrefName(disable_ui_))
  145. .size() >= static_cast<size_t>(features::kNumIgnorePrompts.Get());
  146. url_for_last_shown_tip_ = web_contents->GetLastCommittedURL();
  147. web_contents_showing_accuracy_tip_ = web_contents;
  148. delegate_->ShowAccuracyTip(
  149. web_contents, AccuracyTipStatus::kShowAccuracyTip,
  150. /*show_opt_out=*/show_opt_out,
  151. base::BindOnce(
  152. &AccuracyService::OnAccuracyTipClosed, weak_factory_.GetWeakPtr(),
  153. base::TimeTicks::Now(),
  154. web_contents->GetPrimaryMainFrame()->GetPageUkmSourceId()));
  155. for (Observer& observer : observers_)
  156. observer.OnAccuracyTipShown();
  157. }
  158. void AccuracyService::MaybeShowSurvey() {
  159. if (CanShowSurvey()) {
  160. const auto& interactions_list = pref_service_->GetValueList(
  161. GetPreviousInteractionsPrefName(disable_ui_));
  162. const int last_interaction = interactions_list.back().GetInt();
  163. const bool ukm_enabled = pref_service_->GetBoolean(
  164. unified_consent::prefs::kUrlKeyedAnonymizedDataCollectionEnabled);
  165. std::string url_parameter_for_hats =
  166. ukm_enabled ? url_for_last_shown_tip_.DeprecatedGetOriginAsURL().spec()
  167. : "";
  168. delegate_->ShowSurvey(
  169. {}, {{"Tip shown for URL", url_parameter_for_hats},
  170. {"UI interaction", base::NumberToString(last_interaction)}});
  171. }
  172. }
  173. bool AccuracyService::IsSecureConnection(content::WebContents* web_contents) {
  174. return delegate_->IsSecureConnection(web_contents);
  175. }
  176. void AccuracyService::OnURLsDeleted(
  177. history::HistoryService* history_service,
  178. const history::DeletionInfo& deletion_info) {
  179. if (deletion_info.time_range().IsValid()) {
  180. base::Time last_shown =
  181. pref_service_->GetTime(GetLastShownPrefName(disable_ui_));
  182. if (last_shown >= deletion_info.time_range().begin() &&
  183. last_shown <= deletion_info.time_range().end()) {
  184. url_for_last_shown_tip_ = GURL();
  185. }
  186. } else {
  187. if (deletion_info.deleted_urls_origin_map().count(
  188. url_for_last_shown_tip_.DeprecatedGetOriginAsURL())) {
  189. url_for_last_shown_tip_ = GURL();
  190. }
  191. }
  192. }
  193. void AccuracyService::AddObserver(Observer* observer) {
  194. observers_.AddObserver(observer);
  195. }
  196. void AccuracyService::RemoveObserver(Observer* observer) {
  197. observers_.RemoveObserver(observer);
  198. }
  199. bool AccuracyService::IsShowingAccuracyTip(content::WebContents* web_contents) {
  200. return web_contents_showing_accuracy_tip_ != nullptr &&
  201. web_contents_showing_accuracy_tip_ == web_contents;
  202. }
  203. void AccuracyService::OnAccuracyTipClosed(base::TimeTicks time_opened,
  204. ukm::SourceId ukm_source_id,
  205. AccuracyTipInteraction interaction) {
  206. DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
  207. ListPrefUpdate update(pref_service_,
  208. GetPreviousInteractionsPrefName(disable_ui_));
  209. base::Value* interaction_list = update.Get();
  210. interaction_list->Append(static_cast<int>(interaction));
  211. // Record metrics.
  212. base::UmaHistogramEnumeration("Privacy.AccuracyTip.AccuracyTipInteraction",
  213. interaction);
  214. base::UmaHistogramCounts100("Privacy.AccuracyTip.NumDialogsShown",
  215. interaction_list->GetListDeprecated().size());
  216. ukm::builders::AccuracyTipDialog ukm_builder(ukm_source_id);
  217. ukm_builder.SetInteraction(static_cast<int>(interaction));
  218. if (interaction != AccuracyTipInteraction::kDisabledByExperiment) {
  219. const base::TimeDelta time_open = base::TimeTicks::Now() - time_opened;
  220. base::UmaHistogramMediumTimes("Privacy.AccuracyTip.AccuracyTipTimeOpen",
  221. time_open);
  222. ukm_builder.SetTimeSpent(
  223. ukm::GetExponentialBucketMinForFineUserTiming(time_open.InSeconds()));
  224. const std::string suffix = GetHistogramSuffix(interaction);
  225. base::UmaHistogramCounts100("Privacy.AccuracyTip.NumDialogsShown." + suffix,
  226. interaction_list->GetListDeprecated().size());
  227. base::UmaHistogramMediumTimes(
  228. "Privacy.AccuracyTip.AccuracyTipTimeOpen." + suffix, time_open);
  229. }
  230. ukm_builder.Record(ukm::UkmRecorder::Get());
  231. web_contents_showing_accuracy_tip_ = nullptr;
  232. for (Observer& observer : observers_)
  233. observer.OnAccuracyTipClosed();
  234. }
  235. bool AccuracyService::CanShowSurvey() {
  236. if (!base::FeatureList::IsEnabled(
  237. accuracy_tips::features::kAccuracyTipsSurveyFeature) ||
  238. disable_ui_) {
  239. return false;
  240. }
  241. base::Time last_shown =
  242. pref_service_->GetTime(GetLastShownPrefName(disable_ui_));
  243. base::TimeDelta last_shown_delta = clock_->Now() - last_shown;
  244. const bool has_required_time_passed =
  245. last_shown_delta >= features::kMinTimeToShowSurvey.Get() &&
  246. last_shown_delta <= features::kMaxTimeToShowSurvey.Get();
  247. if (!has_required_time_passed)
  248. return false;
  249. if (url_for_last_shown_tip_.is_empty() || !url_for_last_shown_tip_.is_valid())
  250. return false;
  251. int interactions_count =
  252. pref_service_->GetValueList(GetPreviousInteractionsPrefName(disable_ui_))
  253. .size();
  254. return interactions_count >= features::kMinPromptCountRequiredForSurvey.Get();
  255. }
  256. } // namespace accuracy_tips