blob: dfde1df7e74c0400912bf07fdf21a1cb2925aae6 [file] [log] [blame]
// Copyright 2018 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 "chromeos/services/multidevice_setup/grandfathered_easy_unlock_host_disabler.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "chromeos/components/proximity_auth/logging/logging.h"
#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
namespace chromeos {
namespace multidevice_setup {
namespace {
// Name of the pref that stores the ID of the device which still potentially
// needs to have kSmartLockHost disabled on it.
const char kEasyUnlockHostIdToDisablePrefName[] =
"multidevice_setup.easy_unlock_host_id_to_disable";
const char kNoDevice[] = "";
// The number of minutes to wait before retrying a failed attempt.
const int kNumMinutesBetweenRetries = 5;
bool IsEasyUnlockHost(const multidevice::RemoteDeviceRef& device) {
return device.GetSoftwareFeatureState(
multidevice::SoftwareFeature::kSmartLockHost) ==
multidevice::SoftwareFeatureState::kEnabled;
}
} // namespace
// static
GrandfatheredEasyUnlockHostDisabler::Factory*
GrandfatheredEasyUnlockHostDisabler::Factory::test_factory_ = nullptr;
// static
GrandfatheredEasyUnlockHostDisabler::Factory*
GrandfatheredEasyUnlockHostDisabler::Factory::Get() {
if (test_factory_)
return test_factory_;
static base::NoDestructor<Factory> factory;
return factory.get();
}
// static
void GrandfatheredEasyUnlockHostDisabler::Factory::SetFactoryForTesting(
Factory* test_factory) {
test_factory_ = test_factory;
}
GrandfatheredEasyUnlockHostDisabler::Factory::~Factory() = default;
std::unique_ptr<GrandfatheredEasyUnlockHostDisabler>
GrandfatheredEasyUnlockHostDisabler::Factory::BuildInstance(
HostBackendDelegate* host_backend_delegate,
device_sync::DeviceSyncClient* device_sync_client,
PrefService* pref_service,
std::unique_ptr<base::OneShotTimer> timer) {
return base::WrapUnique(new GrandfatheredEasyUnlockHostDisabler(
host_backend_delegate, device_sync_client, pref_service,
std::move(timer)));
}
// static
void GrandfatheredEasyUnlockHostDisabler::RegisterPrefs(
PrefRegistrySimple* registry) {
registry->RegisterStringPref(kEasyUnlockHostIdToDisablePrefName, kNoDevice);
}
GrandfatheredEasyUnlockHostDisabler::~GrandfatheredEasyUnlockHostDisabler() {
host_backend_delegate_->RemoveObserver(this);
}
GrandfatheredEasyUnlockHostDisabler::GrandfatheredEasyUnlockHostDisabler(
HostBackendDelegate* host_backend_delegate,
device_sync::DeviceSyncClient* device_sync_client,
PrefService* pref_service,
std::unique_ptr<base::OneShotTimer> timer)
: host_backend_delegate_(host_backend_delegate),
device_sync_client_(device_sync_client),
pref_service_(pref_service),
timer_(std::move(timer)),
current_better_together_host_(
host_backend_delegate_->GetMultiDeviceHostFromBackend()) {
host_backend_delegate_->AddObserver(this);
// There might be a device stored in the pref waiting for kSmartLockHost to
// be disabled.
DisableEasyUnlockHostIfNecessary();
}
void GrandfatheredEasyUnlockHostDisabler::OnHostChangedOnBackend() {
// kSmartLockHost possibly needs to be disabled on the previous
// BetterTogether host.
SetPotentialEasyUnlockHostToDisable(current_better_together_host_);
// Retrieve the new BetterTogether host.
current_better_together_host_ =
host_backend_delegate_->GetMultiDeviceHostFromBackend();
DisableEasyUnlockHostIfNecessary();
}
void GrandfatheredEasyUnlockHostDisabler::DisableEasyUnlockHostIfNecessary() {
timer_->Stop();
base::Optional<multidevice::RemoteDeviceRef> host_to_disable =
GetEasyUnlockHostToDisable();
if (!host_to_disable)
return;
PA_LOG(VERBOSE) << "Attempting to disable kSmartLockHost on device "
<< host_to_disable->GetTruncatedDeviceIdForLogs();
device_sync_client_->SetSoftwareFeatureState(
host_to_disable->public_key(),
multidevice::SoftwareFeature::kSmartLockHost, false /* enabled */,
false /* is_exclusive */,
base::BindOnce(
&GrandfatheredEasyUnlockHostDisabler::OnSetSoftwareFeatureStateResult,
base::Unretained(this), *host_to_disable));
}
void GrandfatheredEasyUnlockHostDisabler::OnSetSoftwareFeatureStateResult(
multidevice::RemoteDeviceRef device,
device_sync::mojom::NetworkRequestResult result_code) {
bool success =
result_code == device_sync::mojom::NetworkRequestResult::kSuccess;
if (success) {
PA_LOG(VERBOSE) << "Successfully disabled kSmartLockHost on device "
<< device.GetTruncatedDeviceIdForLogs();
} else {
PA_LOG(WARNING) << "Failed to disable kSmartLockHost on device "
<< device.GetTruncatedDeviceIdForLogs()
<< ", Error code: " << result_code;
}
// Return if the EasyUnlock host to disable changed between calls to
// SetSoftwareFeatureState() and OnSetSoftwareFeatureStateResult().
if (device != GetEasyUnlockHostToDisable())
return;
if (success) {
SetPotentialEasyUnlockHostToDisable(base::nullopt);
return;
}
PA_LOG(WARNING) << "Retrying in " << kNumMinutesBetweenRetries
<< " minutes if necessary.";
timer_->Start(FROM_HERE,
base::TimeDelta::FromMinutes(kNumMinutesBetweenRetries),
base::BindOnce(&GrandfatheredEasyUnlockHostDisabler::
DisableEasyUnlockHostIfNecessary,
base::Unretained(this)));
}
void GrandfatheredEasyUnlockHostDisabler::SetPotentialEasyUnlockHostToDisable(
base::Optional<multidevice::RemoteDeviceRef> device) {
pref_service_->SetString(kEasyUnlockHostIdToDisablePrefName,
device ? device->GetDeviceId() : kNoDevice);
}
base::Optional<multidevice::RemoteDeviceRef>
GrandfatheredEasyUnlockHostDisabler::GetEasyUnlockHostToDisable() {
std::string device_id =
pref_service_->GetString(kEasyUnlockHostIdToDisablePrefName);
if (device_id == kNoDevice)
return base::nullopt;
multidevice::RemoteDeviceRefList synced_devices =
device_sync_client_->GetSyncedDevices();
auto it = std::find_if(synced_devices.begin(), synced_devices.end(),
[&device_id](const auto& remote_device) {
return remote_device.GetDeviceId() == device_id;
});
// The device does not need to have kSmartLockHost disabled if any of the
// following are true:
// - the device is not in the list of synced devices anymore,
// - the device is not the current EasyUnlock host, or
// - the device is the BetterTogether host.
if (it == synced_devices.end() || !IsEasyUnlockHost(*it) ||
*it == current_better_together_host_) {
SetPotentialEasyUnlockHostToDisable(base::nullopt);
return base::nullopt;
}
return *it;
}
} // namespace multidevice_setup
} // namespace chromeos