global_state_feature_manager_impl.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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/services/multidevice_setup/global_state_feature_manager_impl.h"
  5. #include <memory>
  6. #include <sstream>
  7. #include <string>
  8. #include <utility>
  9. #include "ash/components/multidevice/logging/logging.h"
  10. #include "ash/components/multidevice/remote_device_ref.h"
  11. #include "ash/components/multidevice/software_feature.h"
  12. #include "ash/components/multidevice/software_feature_state.h"
  13. #include "ash/constants/ash_features.h"
  14. #include "ash/services/device_sync/feature_status_change.h"
  15. #include "ash/services/device_sync/public/cpp/device_sync_client.h"
  16. #include "ash/services/device_sync/public/mojom/device_sync.mojom.h"
  17. #include "ash/services/multidevice_setup/host_status_provider.h"
  18. #include "ash/services/multidevice_setup/public/cpp/prefs.h"
  19. #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
  20. #include "ash/services/multidevice_setup/wifi_sync_notification_controller.h"
  21. #include "base/bind.h"
  22. #include "base/location.h"
  23. #include "base/memory/ptr_util.h"
  24. #include "base/memory/weak_ptr.h"
  25. #include "base/metrics/histogram_functions.h"
  26. #include "base/time/time.h"
  27. #include "base/timer/timer.h"
  28. #include "components/prefs/pref_registry_simple.h"
  29. #include "components/prefs/pref_service.h"
  30. namespace ash {
  31. namespace multidevice_setup {
  32. namespace {
  33. // This pref name is left in a legacy format to maintain compatibility.
  34. const char kWifiSyncPendingStatePrefName[] =
  35. "multidevice_setup.pending_set_wifi_sync_enabled_request";
  36. // The number of minutes to wait before retrying a failed attempt.
  37. const int kNumMinutesBetweenRetries = 5;
  38. } // namespace
  39. // static
  40. GlobalStateFeatureManagerImpl::Factory*
  41. GlobalStateFeatureManagerImpl::Factory::test_factory_ = nullptr;
  42. // static
  43. std::unique_ptr<GlobalStateFeatureManager>
  44. GlobalStateFeatureManagerImpl::Factory::Create(
  45. Option option,
  46. HostStatusProvider* host_status_provider,
  47. PrefService* pref_service,
  48. device_sync::DeviceSyncClient* device_sync_client,
  49. std::unique_ptr<base::OneShotTimer> timer) {
  50. if (test_factory_) {
  51. return test_factory_->CreateInstance(option, host_status_provider,
  52. pref_service, device_sync_client,
  53. std::move(timer));
  54. }
  55. mojom::Feature managed_feature;
  56. multidevice::SoftwareFeature managed_host_feature;
  57. std::string pending_state_pref_name;
  58. switch (option) {
  59. case Option::kWifiSync:
  60. managed_feature = mojom::Feature::kWifiSync;
  61. managed_host_feature = multidevice::SoftwareFeature::kWifiSyncHost;
  62. pending_state_pref_name = kWifiSyncPendingStatePrefName;
  63. break;
  64. }
  65. return base::WrapUnique(new GlobalStateFeatureManagerImpl(
  66. managed_feature, managed_host_feature, pending_state_pref_name,
  67. host_status_provider, pref_service, device_sync_client,
  68. std::move(timer)));
  69. }
  70. // static
  71. void GlobalStateFeatureManagerImpl::Factory::SetFactoryForTesting(
  72. Factory* test_factory) {
  73. test_factory_ = test_factory;
  74. }
  75. GlobalStateFeatureManagerImpl::Factory::~Factory() = default;
  76. void GlobalStateFeatureManagerImpl::RegisterPrefs(
  77. PrefRegistrySimple* registry) {
  78. registry->RegisterIntegerPref(kWifiSyncPendingStatePrefName,
  79. static_cast<int>(PendingState::kPendingNone));
  80. }
  81. GlobalStateFeatureManagerImpl::GlobalStateFeatureManagerImpl(
  82. mojom::Feature managed_feature,
  83. multidevice::SoftwareFeature managed_host_feature,
  84. const std::string& pending_state_pref_name,
  85. HostStatusProvider* host_status_provider,
  86. PrefService* pref_service,
  87. device_sync::DeviceSyncClient* device_sync_client,
  88. std::unique_ptr<base::OneShotTimer> timer)
  89. : GlobalStateFeatureManager(),
  90. managed_feature_(managed_feature),
  91. managed_host_feature_(managed_host_feature),
  92. pending_state_pref_name_(pending_state_pref_name),
  93. host_status_provider_(host_status_provider),
  94. pref_service_(pref_service),
  95. device_sync_client_(device_sync_client),
  96. timer_(std::move(timer)) {
  97. host_status_provider_->AddObserver(this);
  98. device_sync_client_->AddObserver(this);
  99. if (GetCurrentState() == CurrentState::kValidPendingRequest) {
  100. AttemptSetHostStateNetworkRequest(false /* is_retry */);
  101. }
  102. if (ShouldEnableOnVerify()) {
  103. ProcessEnableOnVerifyAttempt();
  104. }
  105. }
  106. GlobalStateFeatureManagerImpl::~GlobalStateFeatureManagerImpl() {
  107. host_status_provider_->RemoveObserver(this);
  108. device_sync_client_->RemoveObserver(this);
  109. }
  110. void GlobalStateFeatureManagerImpl::OnHostStatusChange(
  111. const HostStatusProvider::HostStatusWithDevice& host_status_with_device) {
  112. if (GetCurrentState() == CurrentState::kNoVerifiedHost &&
  113. !ShouldEnableOnVerify()) {
  114. ResetPendingNetworkRequest();
  115. }
  116. if (ShouldAttemptToEnableAfterHostVerified()) {
  117. SetPendingState(PendingState::kSetPendingEnableOnVerify);
  118. return;
  119. }
  120. if (ShouldEnableOnVerify()) {
  121. ProcessEnableOnVerifyAttempt();
  122. }
  123. }
  124. void GlobalStateFeatureManagerImpl::OnNewDevicesSynced() {
  125. if (GetCurrentState() != CurrentState::kValidPendingRequest &&
  126. !ShouldEnableOnVerify()) {
  127. ResetPendingNetworkRequest();
  128. }
  129. }
  130. void GlobalStateFeatureManagerImpl::SetIsFeatureEnabled(bool enabled) {
  131. if (GetCurrentState() == CurrentState::kNoVerifiedHost) {
  132. PA_LOG(ERROR) << "GlobalStateFeatureManagerImpl::SetIsFeatureEnabled: "
  133. "Network request "
  134. "not attempted because there is No Verified Host";
  135. ResetPendingNetworkRequest();
  136. return;
  137. }
  138. SetPendingState(enabled ? PendingState::kPendingEnable
  139. : PendingState::kPendingDisable);
  140. if (managed_feature_ == mojom::Feature::kWifiSync)
  141. pref_service_->SetBoolean(kCanShowWifiSyncAnnouncementPrefName, false);
  142. // Stop timer since new attempt is started.
  143. timer_->Stop();
  144. AttemptSetHostStateNetworkRequest(false /* is_retry */);
  145. }
  146. bool GlobalStateFeatureManagerImpl::IsFeatureEnabled() {
  147. CurrentState current_state = GetCurrentState();
  148. if (current_state == CurrentState::kNoVerifiedHost) {
  149. return false;
  150. }
  151. if (current_state == CurrentState::kValidPendingRequest) {
  152. return GetPendingState() == PendingState::kPendingEnable;
  153. }
  154. return host_status_provider_->GetHostWithStatus()
  155. .host_device()
  156. ->GetSoftwareFeatureState(managed_host_feature_) ==
  157. multidevice::SoftwareFeatureState::kEnabled;
  158. }
  159. void GlobalStateFeatureManagerImpl::ResetPendingNetworkRequest() {
  160. SetPendingState(PendingState::kPendingNone);
  161. timer_->Stop();
  162. }
  163. void GlobalStateFeatureManagerImpl::SetPendingState(
  164. PendingState pending_state) {
  165. pref_service_->SetInteger(pending_state_pref_name_,
  166. static_cast<int>(pending_state));
  167. }
  168. GlobalStateFeatureManagerImpl::PendingState
  169. GlobalStateFeatureManagerImpl::GetPendingState() {
  170. return static_cast<PendingState>(
  171. pref_service_->GetInteger(pending_state_pref_name_));
  172. }
  173. GlobalStateFeatureManagerImpl::CurrentState
  174. GlobalStateFeatureManagerImpl::GetCurrentState() {
  175. if (host_status_provider_->GetHostWithStatus().host_status() !=
  176. mojom::HostStatus::kHostVerified) {
  177. return CurrentState::kNoVerifiedHost;
  178. }
  179. PendingState pending_state = GetPendingState();
  180. // If the pending request is kSetPendingEnableOnVerify then there is no
  181. // actionable pending equest. The pending request will be changed from
  182. // kSetPendingEnableOnVerify when the host has been verified.
  183. if (pending_state == PendingState::kPendingNone ||
  184. pending_state == PendingState::kSetPendingEnableOnVerify) {
  185. return CurrentState::kNoPendingRequest;
  186. }
  187. bool enabled_on_host =
  188. (host_status_provider_->GetHostWithStatus()
  189. .host_device()
  190. ->GetSoftwareFeatureState(managed_host_feature_) ==
  191. multidevice::SoftwareFeatureState::kEnabled);
  192. bool pending_enabled = (pending_state == PendingState::kPendingEnable);
  193. if (pending_enabled == enabled_on_host) {
  194. return CurrentState::kPendingMatchesBackend;
  195. }
  196. return CurrentState::kValidPendingRequest;
  197. }
  198. void GlobalStateFeatureManagerImpl::AttemptSetHostStateNetworkRequest(
  199. bool is_retry) {
  200. if (network_request_in_flight_) {
  201. return;
  202. }
  203. bool pending_enabled = (GetPendingState() == PendingState::kPendingEnable);
  204. PA_LOG(INFO) << "GlobalStateFeatureManagerImpl::" << __func__ << ": "
  205. << (is_retry ? "Retrying attempt" : "Attempting") << " to "
  206. << (pending_enabled ? "enable" : "disable");
  207. network_request_in_flight_ = true;
  208. multidevice::RemoteDeviceRef host_device =
  209. *host_status_provider_->GetHostWithStatus().host_device();
  210. if (features::ShouldUseV1DeviceSync()) {
  211. // Even if the |device_to_set| has a non-trivial Instance ID, we still
  212. // invoke the v1 DeviceSync RPC to set the feature state. This ensures that
  213. // GmsCore will be notified of the change regardless of what version of
  214. // DeviceSync it is running. The v1 and v2 RPCs to change feature states
  215. // ultimately update the same backend database entry. Note: The
  216. // RemoteDeviceProvider guarantees that every device will have a public key
  217. // while v1 DeviceSync is enabled.
  218. device_sync_client_->SetSoftwareFeatureState(
  219. host_device.public_key(), managed_host_feature_,
  220. pending_enabled /* enabled */, pending_enabled /* is_exclusive */,
  221. base::BindOnce(&GlobalStateFeatureManagerImpl::
  222. OnSetHostStateNetworkRequestFinished,
  223. weak_ptr_factory_.GetWeakPtr(), pending_enabled));
  224. } else {
  225. device_sync_client_->SetFeatureStatus(
  226. host_device.instance_id(), managed_host_feature_,
  227. pending_enabled ? device_sync::FeatureStatusChange::kEnableExclusively
  228. : device_sync::FeatureStatusChange::kDisable,
  229. base::BindOnce(&GlobalStateFeatureManagerImpl::
  230. OnSetHostStateNetworkRequestFinished,
  231. weak_ptr_factory_.GetWeakPtr(), pending_enabled));
  232. }
  233. }
  234. void GlobalStateFeatureManagerImpl::OnSetHostStateNetworkRequestFinished(
  235. bool attempted_to_enable,
  236. device_sync::mojom::NetworkRequestResult result_code) {
  237. network_request_in_flight_ = false;
  238. bool success =
  239. (result_code == device_sync::mojom::NetworkRequestResult::kSuccess);
  240. std::stringstream ss;
  241. ss << "GlobalStateFeatureManagerImpl::" << __func__ << ": Completed with "
  242. << (success ? "success" : "failure")
  243. << ". Attempted to enable: " << (attempted_to_enable ? "true" : "false");
  244. if (success) {
  245. PA_LOG(VERBOSE) << ss.str();
  246. PendingState pending_state = GetPendingState();
  247. if (pending_state == PendingState::kPendingNone) {
  248. return;
  249. }
  250. bool pending_enabled = (pending_state == PendingState::kPendingEnable);
  251. // If the network request was successful but there is still a pending
  252. // network request then trigger a network request immediately. This could
  253. // happen if there was a second attempt to set the backend while the first
  254. // one was still in progress.
  255. if (attempted_to_enable != pending_enabled) {
  256. AttemptSetHostStateNetworkRequest(false /* is_retry */);
  257. }
  258. return;
  259. }
  260. ss << ", Error code: " << result_code;
  261. PA_LOG(WARNING) << ss.str();
  262. // If the network request failed and there is still a pending network request,
  263. // schedule a retry.
  264. if (GetCurrentState() == CurrentState::kValidPendingRequest) {
  265. timer_->Start(
  266. FROM_HERE, base::Minutes(kNumMinutesBetweenRetries),
  267. base::BindOnce(
  268. &GlobalStateFeatureManagerImpl::AttemptSetHostStateNetworkRequest,
  269. base::Unretained(this), true /* is_retry */));
  270. }
  271. }
  272. bool GlobalStateFeatureManagerImpl::ShouldEnableOnVerify() {
  273. return (GetPendingState() == PendingState::kSetPendingEnableOnVerify);
  274. }
  275. void GlobalStateFeatureManagerImpl::ProcessEnableOnVerifyAttempt() {
  276. mojom::HostStatus host_status =
  277. host_status_provider_->GetHostWithStatus().host_status();
  278. // If host is not set.
  279. if (host_status == mojom::HostStatus::kNoEligibleHosts ||
  280. host_status == mojom::HostStatus::kEligibleHostExistsButNoHostSet) {
  281. ResetPendingNetworkRequest();
  282. return;
  283. }
  284. if (host_status != mojom::HostStatus::kHostVerified) {
  285. return;
  286. }
  287. if (IsFeatureEnabled()) {
  288. ResetPendingNetworkRequest();
  289. return;
  290. }
  291. SetIsFeatureEnabled(true);
  292. if (managed_feature_ == mojom::Feature::kPhoneHubCameraRoll) {
  293. base::UmaHistogramEnumeration("PhoneHub.CameraRoll.OptInEntryPoint",
  294. mojom::CameraRollOptInEntryPoint::kSetupFlow);
  295. }
  296. }
  297. bool GlobalStateFeatureManagerImpl::ShouldAttemptToEnableAfterHostVerified() {
  298. HostStatusProvider::HostStatusWithDevice host_status_with_device =
  299. host_status_provider_->GetHostWithStatus();
  300. // kHostSetLocallyButWaitingForBackendConfirmation is only possible if the
  301. // setup flow has been completed on the local device.
  302. if (host_status_with_device.host_status() !=
  303. mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation) {
  304. return false;
  305. }
  306. // Check if the feature is prohibited by enterprise policy or if feature flag
  307. // is disabled.
  308. if (!IsFeatureAllowed(managed_feature_, pref_service_)) {
  309. return false;
  310. }
  311. // Check if the feature is supported by host device.
  312. if (host_status_with_device.host_device()->GetSoftwareFeatureState(
  313. managed_host_feature_) ==
  314. multidevice::SoftwareFeatureState::kNotSupported) {
  315. return false;
  316. }
  317. return true;
  318. }
  319. } // namespace multidevice_setup
  320. } // namespace ash