snooping_protection_controller.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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 "ash/system/human_presence/snooping_protection_controller.h"
  5. #include <memory>
  6. #include "ash/constants/ash_pref_names.h"
  7. #include "ash/public/cpp/session/session_observer.h"
  8. #include "ash/session/session_controller_impl.h"
  9. #include "ash/shell.h"
  10. #include "ash/system/human_presence/human_presence_metrics.h"
  11. #include "ash/system/human_presence/snooping_protection_notification_blocker.h"
  12. #include "base/bind.h"
  13. #include "base/callback.h"
  14. #include "base/logging.h"
  15. #include "base/metrics/histogram_functions.h"
  16. #include "base/time/time.h"
  17. #include "base/timer/timer.h"
  18. #include "chromeos/ash/components/dbus/hps/hps_service.pb.h"
  19. #include "chromeos/ash/components/human_presence/human_presence_configuration.h"
  20. #include "components/account_id/account_id.h"
  21. #include "components/pref_registry/pref_registry_syncable.h"
  22. #include "components/prefs/pref_change_registrar.h"
  23. #include "components/prefs/pref_registry_simple.h"
  24. #include "components/prefs/pref_service.h"
  25. #include "components/session_manager/session_manager_types.h"
  26. #include "third_party/abseil-cpp/absl/types/optional.h"
  27. #include "ui/message_center/message_center.h"
  28. namespace ash {
  29. namespace metrics = ash::snooping_protection_metrics;
  30. SnoopingProtectionController::SnoopingProtectionController()
  31. : notification_blocker_(
  32. std::make_unique<SnoopingProtectionNotificationBlocker>(
  33. message_center::MessageCenter::Get(),
  34. this)),
  35. pos_window_(hps::GetSnoopingProtectionPositiveWindow()) {
  36. // When the controller is initialized, we are never in an active user session
  37. // and we never have any user preferences active. Hence, our default state
  38. // values are correct.
  39. // Session controller is instantiated before us in the shell.
  40. SessionControllerImpl* session_controller =
  41. Shell::Get()->session_controller();
  42. DCHECK(session_controller);
  43. session_observation_.Observe(session_controller);
  44. // Wait for the service to be available before subscribing to its events. If
  45. // we directly subscribe here, we will attempt to configure the DBus service
  46. // twice (once via this callback and once via |OnRestart|) if it's slow to
  47. // start. Configuring snooping protection without first disabling it is an
  48. // error.
  49. //
  50. // Might not exist in unit tests.
  51. if (HumanPresenceDBusClient::Get()) {
  52. HumanPresenceDBusClient::Get()->WaitForServiceToBeAvailable(
  53. base::BindOnce(&SnoopingProtectionController::StartServiceObservation,
  54. weak_ptr_factory_.GetWeakPtr()));
  55. }
  56. // Orientation controller is instantiated before us in the shell.
  57. HumanPresenceOrientationController* orientation_controller =
  58. Shell::Get()->human_presence_orientation_controller();
  59. state_.orientation_suitable = orientation_controller->IsOrientationSuitable();
  60. orientation_observation_.Observe(orientation_controller);
  61. }
  62. SnoopingProtectionController::~SnoopingProtectionController() {
  63. // This is a no-op if the service isn't available or isn't enabled.
  64. // TODO(crbug.com/1241704): only disable if the service is enabled.
  65. //
  66. // Might not exist in unit tests.
  67. if (HumanPresenceDBusClient::Get())
  68. HumanPresenceDBusClient::Get()->DisableHpsNotify();
  69. for (auto& observer : observers_)
  70. observer.OnSnoopingProtectionControllerDestroyed();
  71. // We want to log current presence/absence duration since we'll not get
  72. // another event anymore.
  73. LogPresenceWindow(state_.present);
  74. }
  75. // static
  76. void SnoopingProtectionController::RegisterProfilePrefs(
  77. PrefRegistrySimple* registry) {
  78. registry->RegisterBooleanPref(
  79. prefs::kSnoopingProtectionEnabled,
  80. /*default_value=*/false,
  81. user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
  82. registry->RegisterBooleanPref(
  83. prefs::kSnoopingProtectionNotificationSuppressionEnabled,
  84. /*default_value=*/true,
  85. user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
  86. }
  87. void SnoopingProtectionController::OnSessionStateChanged(
  88. session_manager::SessionState session_state) {
  89. const bool session_active =
  90. session_state == session_manager::SessionState::ACTIVE;
  91. State new_state = state_;
  92. new_state.session_active = session_active;
  93. ReconfigureService(&new_state);
  94. UpdateSnooperStatus(new_state);
  95. }
  96. void SnoopingProtectionController::OnActiveUserPrefServiceChanged(
  97. PrefService* pref_service) {
  98. DCHECK(pref_service);
  99. const bool pref_enabled =
  100. pref_service->GetBoolean(prefs::kSnoopingProtectionEnabled);
  101. State new_state = state_;
  102. new_state.pref_enabled = pref_enabled;
  103. ReconfigureService(&new_state);
  104. UpdateSnooperStatus(new_state);
  105. // Re-subscribe to pref changes.
  106. pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
  107. pref_change_registrar_->Init(pref_service);
  108. pref_change_registrar_->Add(
  109. prefs::kSnoopingProtectionEnabled,
  110. base::BindRepeating(&SnoopingProtectionController::UpdatePrefState,
  111. weak_ptr_factory_.GetWeakPtr()));
  112. }
  113. void SnoopingProtectionController::OnOrientationChanged(
  114. bool suitable_for_human_presence) {
  115. State new_state = state_;
  116. new_state.orientation_suitable = suitable_for_human_presence;
  117. ReconfigureService(&new_state);
  118. UpdateSnooperStatus(new_state);
  119. }
  120. void SnoopingProtectionController::OnHpsSenseChanged(
  121. const hps::HpsResultProto&) {}
  122. void SnoopingProtectionController::OnHpsNotifyChanged(
  123. const hps::HpsResultProto& result) {
  124. const bool present = result.value() == hps::HpsResult::POSITIVE;
  125. State new_state = state_;
  126. new_state.present = present;
  127. // Prevent snooping status from becoming negative within a window of time.
  128. if (present) {
  129. new_state.within_pos_window = true;
  130. // Cancels previous task if it is already scheduled.
  131. pos_window_timer_.Start(FROM_HERE, pos_window_, this,
  132. &SnoopingProtectionController::OnMinWindowExpired);
  133. }
  134. UpdateSnooperStatus(new_state);
  135. }
  136. void SnoopingProtectionController::OnRestart() {
  137. DCHECK(!state_.present);
  138. State new_state = state_;
  139. new_state.service_available = true;
  140. ReconfigureService(&new_state);
  141. UpdateSnooperStatus(new_state);
  142. }
  143. void SnoopingProtectionController::OnShutdown() {
  144. // Log current presence window and reset the report time so that the next
  145. // present/absent duration will not be logged, because the duration will be
  146. // incorrect.
  147. // This has to be done before UpdateSnooperStatus below.
  148. LogPresenceWindow(state_.present);
  149. last_presence_report_time_ = base::TimeTicks();
  150. State new_state = state_;
  151. new_state.service_available = false;
  152. ReconfigureService(&new_state);
  153. UpdateSnooperStatus(new_state);
  154. // We will be notified of the service starting back up again via our ongoing
  155. // observation of the DBus client.
  156. }
  157. void SnoopingProtectionController::AddObserver(Observer* observer) {
  158. observers_.AddObserver(observer);
  159. }
  160. void SnoopingProtectionController::RemoveObserver(Observer* observer) {
  161. observers_.RemoveObserver(observer);
  162. }
  163. bool SnoopingProtectionController::SnooperPresent() const {
  164. return state_.within_pos_window ||
  165. (state_.session_active && state_.present && state_.pref_enabled &&
  166. state_.orientation_suitable);
  167. }
  168. void SnoopingProtectionController::UpdateSnooperStatus(const State& new_state) {
  169. // Clean up new state to be consistent.
  170. const bool detection_active =
  171. new_state.session_active && new_state.pref_enabled &&
  172. new_state.service_available && new_state.service_configured &&
  173. new_state.orientation_suitable;
  174. State clean_state = new_state;
  175. clean_state.present = new_state.present && detection_active;
  176. clean_state.within_pos_window =
  177. new_state.within_pos_window && detection_active;
  178. // If the present state changes to false while within_pos_window, we would
  179. // have got a flakey disappearing of the eyecon without pos_window.
  180. if (clean_state.within_pos_window && !clean_state.present) {
  181. base::UmaHistogramBoolean("ChromeOS.HPS.SnoopingProtection.FlakeyDetection",
  182. false);
  183. }
  184. const bool was_present = SnooperPresent();
  185. state_ = clean_state;
  186. const bool is_present = SnooperPresent();
  187. if (was_present == is_present)
  188. return;
  189. LogPresenceWindow(was_present);
  190. for (auto& observer : observers_)
  191. observer.OnSnoopingStatusChanged(is_present);
  192. }
  193. void SnoopingProtectionController::ReconfigureService(State* new_state) {
  194. // Can't configure or de-configure the service if it's unavailable.
  195. if (!new_state->service_available) {
  196. new_state->service_configured = false;
  197. return;
  198. }
  199. // We have correctly cached that the service is available; now handle
  200. // configuring its signal.
  201. const bool want_configured = new_state->pref_enabled &&
  202. new_state->session_active &&
  203. new_state->orientation_suitable;
  204. if (state_.service_configured == want_configured) {
  205. new_state->service_configured = want_configured;
  206. return;
  207. }
  208. if (want_configured) {
  209. // Configure the snooping started/stopped signals that the service will
  210. // emit.
  211. const absl::optional<hps::FeatureConfig> config =
  212. hps::GetEnableSnoopingProtectionConfig();
  213. if (!config.has_value()) {
  214. LOG(ERROR) << "SnoopingProtectionController: couldn't parse HpsNotify "
  215. "configuration.";
  216. return;
  217. }
  218. LOG(ERROR)
  219. << "SnoopingProtectionController: enabling HpsNotify from chrome.";
  220. HumanPresenceDBusClient::Get()->EnableHpsNotify(*config);
  221. // Populate our initial HPS state for consistency with the service.
  222. HumanPresenceDBusClient::Get()->GetResultHpsNotify(
  223. base::BindOnce(&SnoopingProtectionController::UpdateServiceState,
  224. weak_ptr_factory_.GetWeakPtr()));
  225. new_state->service_configured = true;
  226. return;
  227. }
  228. // No longer need signals to be emitted.
  229. HumanPresenceDBusClient::Get()->DisableHpsNotify();
  230. new_state->service_configured = false;
  231. }
  232. void SnoopingProtectionController::StartServiceObservation(
  233. bool service_is_available) {
  234. state_.service_available = service_is_available;
  235. state_.service_configured = false;
  236. if (!service_is_available) {
  237. LOG(ERROR) << "Could not make initial connection to HPS service";
  238. return;
  239. }
  240. // Special case: at this point, the service could have been left in an enabled
  241. // state by a previous session that crashed (and hence didn't clean up
  242. // properly). Disable it here, which is a no-op if it is already disabled.
  243. HumanPresenceDBusClient::Get()->DisableHpsNotify();
  244. // Start listening for state updates and restarts/shutdowns.
  245. human_presence_dbus_observation_.Observe(HumanPresenceDBusClient::Get());
  246. // Configure the service and poll its initial value if necessary.
  247. ReconfigureService(&state_);
  248. UpdateSnooperStatus(state_);
  249. }
  250. // This callback almost always runs as the service is starting up.
  251. // LogPresenceWindow is purposefully not called inside ths function, because
  252. // during startup the service reports an UNKNOWN state, so there's a risk of
  253. // logging a spurious window of absence.
  254. void SnoopingProtectionController::UpdateServiceState(
  255. absl::optional<hps::HpsResultProto> response) {
  256. LOG_IF(WARNING, !response.has_value())
  257. << "Polling the presence daemon failed";
  258. const bool present =
  259. response.has_value() && response->value() == hps::HpsResult::POSITIVE;
  260. State new_state = state_;
  261. new_state.present = present;
  262. // Prevent snooping status from becoming negative within a window of time.
  263. if (present) {
  264. new_state.within_pos_window = true;
  265. // Cancels previous task if it is already scheduled.
  266. pos_window_timer_.Start(FROM_HERE, pos_window_, this,
  267. &SnoopingProtectionController::OnMinWindowExpired);
  268. }
  269. UpdateSnooperStatus(new_state);
  270. }
  271. void SnoopingProtectionController::UpdatePrefState() {
  272. DCHECK(pref_change_registrar_);
  273. DCHECK(pref_change_registrar_->prefs());
  274. const bool pref_enabled = pref_change_registrar_->prefs()->GetBoolean(
  275. prefs::kSnoopingProtectionEnabled);
  276. State new_state = state_;
  277. new_state.pref_enabled = pref_enabled;
  278. ReconfigureService(&new_state);
  279. UpdateSnooperStatus(new_state);
  280. base::UmaHistogramBoolean(metrics::kEnabledHistogramName, pref_enabled);
  281. }
  282. void SnoopingProtectionController::OnMinWindowExpired() {
  283. State new_state = state_;
  284. new_state.within_pos_window = false;
  285. UpdateSnooperStatus(new_state);
  286. }
  287. void SnoopingProtectionController::LogPresenceWindow(bool was_present) {
  288. const auto now = base::TimeTicks::Now();
  289. // Set last_presence_report_time_ and return if it is the first time reported.
  290. if (last_presence_report_time_.is_null()) {
  291. last_presence_report_time_ = now;
  292. return;
  293. }
  294. const auto time_since_last_report = now - last_presence_report_time_;
  295. last_presence_report_time_ = now;
  296. if (was_present) {
  297. base::UmaHistogramCustomTimes(metrics::kPositiveDurationHistogramName,
  298. time_since_last_report, metrics::kDurationMin,
  299. metrics::kPositiveDurationMax,
  300. metrics::kDurationNumBuckets);
  301. } else {
  302. base::UmaHistogramCustomTimes(metrics::kNegativeDurationHistogramName,
  303. time_since_last_report, metrics::kDurationMin,
  304. metrics::kNegativeDurationMax,
  305. metrics::kDurationNumBuckets);
  306. }
  307. }
  308. } // namespace ash