123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- // Copyright 2021 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/services/multidevice_setup/global_state_feature_manager_impl.h"
- #include <memory>
- #include <sstream>
- #include <string>
- #include <utility>
- #include "ash/components/multidevice/logging/logging.h"
- #include "ash/components/multidevice/remote_device_ref.h"
- #include "ash/components/multidevice/software_feature.h"
- #include "ash/components/multidevice/software_feature_state.h"
- #include "ash/constants/ash_features.h"
- #include "ash/services/device_sync/feature_status_change.h"
- #include "ash/services/device_sync/public/cpp/device_sync_client.h"
- #include "ash/services/device_sync/public/mojom/device_sync.mojom.h"
- #include "ash/services/multidevice_setup/host_status_provider.h"
- #include "ash/services/multidevice_setup/public/cpp/prefs.h"
- #include "ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
- #include "ash/services/multidevice_setup/wifi_sync_notification_controller.h"
- #include "base/bind.h"
- #include "base/location.h"
- #include "base/memory/ptr_util.h"
- #include "base/memory/weak_ptr.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/time/time.h"
- #include "base/timer/timer.h"
- #include "components/prefs/pref_registry_simple.h"
- #include "components/prefs/pref_service.h"
- namespace ash {
- namespace multidevice_setup {
- namespace {
- // This pref name is left in a legacy format to maintain compatibility.
- const char kWifiSyncPendingStatePrefName[] =
- "multidevice_setup.pending_set_wifi_sync_enabled_request";
- // The number of minutes to wait before retrying a failed attempt.
- const int kNumMinutesBetweenRetries = 5;
- } // namespace
- // static
- GlobalStateFeatureManagerImpl::Factory*
- GlobalStateFeatureManagerImpl::Factory::test_factory_ = nullptr;
- // static
- std::unique_ptr<GlobalStateFeatureManager>
- GlobalStateFeatureManagerImpl::Factory::Create(
- Option option,
- HostStatusProvider* host_status_provider,
- PrefService* pref_service,
- device_sync::DeviceSyncClient* device_sync_client,
- std::unique_ptr<base::OneShotTimer> timer) {
- if (test_factory_) {
- return test_factory_->CreateInstance(option, host_status_provider,
- pref_service, device_sync_client,
- std::move(timer));
- }
- mojom::Feature managed_feature;
- multidevice::SoftwareFeature managed_host_feature;
- std::string pending_state_pref_name;
- switch (option) {
- case Option::kWifiSync:
- managed_feature = mojom::Feature::kWifiSync;
- managed_host_feature = multidevice::SoftwareFeature::kWifiSyncHost;
- pending_state_pref_name = kWifiSyncPendingStatePrefName;
- break;
- }
- return base::WrapUnique(new GlobalStateFeatureManagerImpl(
- managed_feature, managed_host_feature, pending_state_pref_name,
- host_status_provider, pref_service, device_sync_client,
- std::move(timer)));
- }
- // static
- void GlobalStateFeatureManagerImpl::Factory::SetFactoryForTesting(
- Factory* test_factory) {
- test_factory_ = test_factory;
- }
- GlobalStateFeatureManagerImpl::Factory::~Factory() = default;
- void GlobalStateFeatureManagerImpl::RegisterPrefs(
- PrefRegistrySimple* registry) {
- registry->RegisterIntegerPref(kWifiSyncPendingStatePrefName,
- static_cast<int>(PendingState::kPendingNone));
- }
- GlobalStateFeatureManagerImpl::GlobalStateFeatureManagerImpl(
- mojom::Feature managed_feature,
- multidevice::SoftwareFeature managed_host_feature,
- const std::string& pending_state_pref_name,
- HostStatusProvider* host_status_provider,
- PrefService* pref_service,
- device_sync::DeviceSyncClient* device_sync_client,
- std::unique_ptr<base::OneShotTimer> timer)
- : GlobalStateFeatureManager(),
- managed_feature_(managed_feature),
- managed_host_feature_(managed_host_feature),
- pending_state_pref_name_(pending_state_pref_name),
- host_status_provider_(host_status_provider),
- pref_service_(pref_service),
- device_sync_client_(device_sync_client),
- timer_(std::move(timer)) {
- host_status_provider_->AddObserver(this);
- device_sync_client_->AddObserver(this);
- if (GetCurrentState() == CurrentState::kValidPendingRequest) {
- AttemptSetHostStateNetworkRequest(false /* is_retry */);
- }
- if (ShouldEnableOnVerify()) {
- ProcessEnableOnVerifyAttempt();
- }
- }
- GlobalStateFeatureManagerImpl::~GlobalStateFeatureManagerImpl() {
- host_status_provider_->RemoveObserver(this);
- device_sync_client_->RemoveObserver(this);
- }
- void GlobalStateFeatureManagerImpl::OnHostStatusChange(
- const HostStatusProvider::HostStatusWithDevice& host_status_with_device) {
- if (GetCurrentState() == CurrentState::kNoVerifiedHost &&
- !ShouldEnableOnVerify()) {
- ResetPendingNetworkRequest();
- }
- if (ShouldAttemptToEnableAfterHostVerified()) {
- SetPendingState(PendingState::kSetPendingEnableOnVerify);
- return;
- }
- if (ShouldEnableOnVerify()) {
- ProcessEnableOnVerifyAttempt();
- }
- }
- void GlobalStateFeatureManagerImpl::OnNewDevicesSynced() {
- if (GetCurrentState() != CurrentState::kValidPendingRequest &&
- !ShouldEnableOnVerify()) {
- ResetPendingNetworkRequest();
- }
- }
- void GlobalStateFeatureManagerImpl::SetIsFeatureEnabled(bool enabled) {
- if (GetCurrentState() == CurrentState::kNoVerifiedHost) {
- PA_LOG(ERROR) << "GlobalStateFeatureManagerImpl::SetIsFeatureEnabled: "
- "Network request "
- "not attempted because there is No Verified Host";
- ResetPendingNetworkRequest();
- return;
- }
- SetPendingState(enabled ? PendingState::kPendingEnable
- : PendingState::kPendingDisable);
- if (managed_feature_ == mojom::Feature::kWifiSync)
- pref_service_->SetBoolean(kCanShowWifiSyncAnnouncementPrefName, false);
- // Stop timer since new attempt is started.
- timer_->Stop();
- AttemptSetHostStateNetworkRequest(false /* is_retry */);
- }
- bool GlobalStateFeatureManagerImpl::IsFeatureEnabled() {
- CurrentState current_state = GetCurrentState();
- if (current_state == CurrentState::kNoVerifiedHost) {
- return false;
- }
- if (current_state == CurrentState::kValidPendingRequest) {
- return GetPendingState() == PendingState::kPendingEnable;
- }
- return host_status_provider_->GetHostWithStatus()
- .host_device()
- ->GetSoftwareFeatureState(managed_host_feature_) ==
- multidevice::SoftwareFeatureState::kEnabled;
- }
- void GlobalStateFeatureManagerImpl::ResetPendingNetworkRequest() {
- SetPendingState(PendingState::kPendingNone);
- timer_->Stop();
- }
- void GlobalStateFeatureManagerImpl::SetPendingState(
- PendingState pending_state) {
- pref_service_->SetInteger(pending_state_pref_name_,
- static_cast<int>(pending_state));
- }
- GlobalStateFeatureManagerImpl::PendingState
- GlobalStateFeatureManagerImpl::GetPendingState() {
- return static_cast<PendingState>(
- pref_service_->GetInteger(pending_state_pref_name_));
- }
- GlobalStateFeatureManagerImpl::CurrentState
- GlobalStateFeatureManagerImpl::GetCurrentState() {
- if (host_status_provider_->GetHostWithStatus().host_status() !=
- mojom::HostStatus::kHostVerified) {
- return CurrentState::kNoVerifiedHost;
- }
- PendingState pending_state = GetPendingState();
- // If the pending request is kSetPendingEnableOnVerify then there is no
- // actionable pending equest. The pending request will be changed from
- // kSetPendingEnableOnVerify when the host has been verified.
- if (pending_state == PendingState::kPendingNone ||
- pending_state == PendingState::kSetPendingEnableOnVerify) {
- return CurrentState::kNoPendingRequest;
- }
- bool enabled_on_host =
- (host_status_provider_->GetHostWithStatus()
- .host_device()
- ->GetSoftwareFeatureState(managed_host_feature_) ==
- multidevice::SoftwareFeatureState::kEnabled);
- bool pending_enabled = (pending_state == PendingState::kPendingEnable);
- if (pending_enabled == enabled_on_host) {
- return CurrentState::kPendingMatchesBackend;
- }
- return CurrentState::kValidPendingRequest;
- }
- void GlobalStateFeatureManagerImpl::AttemptSetHostStateNetworkRequest(
- bool is_retry) {
- if (network_request_in_flight_) {
- return;
- }
- bool pending_enabled = (GetPendingState() == PendingState::kPendingEnable);
- PA_LOG(INFO) << "GlobalStateFeatureManagerImpl::" << __func__ << ": "
- << (is_retry ? "Retrying attempt" : "Attempting") << " to "
- << (pending_enabled ? "enable" : "disable");
- network_request_in_flight_ = true;
- multidevice::RemoteDeviceRef host_device =
- *host_status_provider_->GetHostWithStatus().host_device();
- if (features::ShouldUseV1DeviceSync()) {
- // Even if the |device_to_set| has a non-trivial Instance ID, we still
- // invoke the v1 DeviceSync RPC to set the feature state. This ensures that
- // GmsCore will be notified of the change regardless of what version of
- // DeviceSync it is running. The v1 and v2 RPCs to change feature states
- // ultimately update the same backend database entry. Note: The
- // RemoteDeviceProvider guarantees that every device will have a public key
- // while v1 DeviceSync is enabled.
- device_sync_client_->SetSoftwareFeatureState(
- host_device.public_key(), managed_host_feature_,
- pending_enabled /* enabled */, pending_enabled /* is_exclusive */,
- base::BindOnce(&GlobalStateFeatureManagerImpl::
- OnSetHostStateNetworkRequestFinished,
- weak_ptr_factory_.GetWeakPtr(), pending_enabled));
- } else {
- device_sync_client_->SetFeatureStatus(
- host_device.instance_id(), managed_host_feature_,
- pending_enabled ? device_sync::FeatureStatusChange::kEnableExclusively
- : device_sync::FeatureStatusChange::kDisable,
- base::BindOnce(&GlobalStateFeatureManagerImpl::
- OnSetHostStateNetworkRequestFinished,
- weak_ptr_factory_.GetWeakPtr(), pending_enabled));
- }
- }
- void GlobalStateFeatureManagerImpl::OnSetHostStateNetworkRequestFinished(
- bool attempted_to_enable,
- device_sync::mojom::NetworkRequestResult result_code) {
- network_request_in_flight_ = false;
- bool success =
- (result_code == device_sync::mojom::NetworkRequestResult::kSuccess);
- std::stringstream ss;
- ss << "GlobalStateFeatureManagerImpl::" << __func__ << ": Completed with "
- << (success ? "success" : "failure")
- << ". Attempted to enable: " << (attempted_to_enable ? "true" : "false");
- if (success) {
- PA_LOG(VERBOSE) << ss.str();
- PendingState pending_state = GetPendingState();
- if (pending_state == PendingState::kPendingNone) {
- return;
- }
- bool pending_enabled = (pending_state == PendingState::kPendingEnable);
- // If the network request was successful but there is still a pending
- // network request then trigger a network request immediately. This could
- // happen if there was a second attempt to set the backend while the first
- // one was still in progress.
- if (attempted_to_enable != pending_enabled) {
- AttemptSetHostStateNetworkRequest(false /* is_retry */);
- }
- return;
- }
- ss << ", Error code: " << result_code;
- PA_LOG(WARNING) << ss.str();
- // If the network request failed and there is still a pending network request,
- // schedule a retry.
- if (GetCurrentState() == CurrentState::kValidPendingRequest) {
- timer_->Start(
- FROM_HERE, base::Minutes(kNumMinutesBetweenRetries),
- base::BindOnce(
- &GlobalStateFeatureManagerImpl::AttemptSetHostStateNetworkRequest,
- base::Unretained(this), true /* is_retry */));
- }
- }
- bool GlobalStateFeatureManagerImpl::ShouldEnableOnVerify() {
- return (GetPendingState() == PendingState::kSetPendingEnableOnVerify);
- }
- void GlobalStateFeatureManagerImpl::ProcessEnableOnVerifyAttempt() {
- mojom::HostStatus host_status =
- host_status_provider_->GetHostWithStatus().host_status();
- // If host is not set.
- if (host_status == mojom::HostStatus::kNoEligibleHosts ||
- host_status == mojom::HostStatus::kEligibleHostExistsButNoHostSet) {
- ResetPendingNetworkRequest();
- return;
- }
- if (host_status != mojom::HostStatus::kHostVerified) {
- return;
- }
- if (IsFeatureEnabled()) {
- ResetPendingNetworkRequest();
- return;
- }
- SetIsFeatureEnabled(true);
- if (managed_feature_ == mojom::Feature::kPhoneHubCameraRoll) {
- base::UmaHistogramEnumeration("PhoneHub.CameraRoll.OptInEntryPoint",
- mojom::CameraRollOptInEntryPoint::kSetupFlow);
- }
- }
- bool GlobalStateFeatureManagerImpl::ShouldAttemptToEnableAfterHostVerified() {
- HostStatusProvider::HostStatusWithDevice host_status_with_device =
- host_status_provider_->GetHostWithStatus();
- // kHostSetLocallyButWaitingForBackendConfirmation is only possible if the
- // setup flow has been completed on the local device.
- if (host_status_with_device.host_status() !=
- mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation) {
- return false;
- }
- // Check if the feature is prohibited by enterprise policy or if feature flag
- // is disabled.
- if (!IsFeatureAllowed(managed_feature_, pref_service_)) {
- return false;
- }
- // Check if the feature is supported by host device.
- if (host_status_with_device.host_device()->GetSoftwareFeatureState(
- managed_host_feature_) ==
- multidevice::SoftwareFeatureState::kNotSupported) {
- return false;
- }
- return true;
- }
- } // namespace multidevice_setup
- } // namespace ash
|