Commit a8bdfd02 authored by Kyle Horimoto's avatar Kyle Horimoto Committed by Commit Bot

[CrOS MultiDevice] Add FeatureStateManager.

This class determines the active state of each multi-device feature. A
future CL will integrate this class with the MultiDeviceSetup service
so that the settings page as well as each individual feature can query
the service for the correct state to show in the UI.

Bug: 824568
Change-Id: I11db1713e19a20f442e73a1f0e61c568ce340e49
Reviewed-on: https://chromium-review.googlesource.com/1171789Reviewed-by: default avatarRyan Hansberry <hansberry@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Commit-Queue: Kyle Horimoto <khorimoto@chromium.org>
Commit-Queue: Ryan Hansberry <hansberry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583359}
parent 8812d563
...@@ -17,6 +17,10 @@ static_library("multidevice_setup") { ...@@ -17,6 +17,10 @@ static_library("multidevice_setup") {
"eligible_host_devices_provider.h", "eligible_host_devices_provider.h",
"eligible_host_devices_provider_impl.cc", "eligible_host_devices_provider_impl.cc",
"eligible_host_devices_provider_impl.h", "eligible_host_devices_provider_impl.h",
"feature_state_manager.cc",
"feature_state_manager.h",
"feature_state_manager_impl.cc",
"feature_state_manager_impl.h",
"host_backend_delegate.cc", "host_backend_delegate.cc",
"host_backend_delegate.h", "host_backend_delegate.h",
"host_backend_delegate_impl.cc", "host_backend_delegate_impl.cc",
...@@ -73,6 +77,8 @@ static_library("test_support") { ...@@ -73,6 +77,8 @@ static_library("test_support") {
"fake_account_status_change_delegate_notifier.h", "fake_account_status_change_delegate_notifier.h",
"fake_eligible_host_devices_provider.cc", "fake_eligible_host_devices_provider.cc",
"fake_eligible_host_devices_provider.h", "fake_eligible_host_devices_provider.h",
"fake_feature_state_manager.cc",
"fake_feature_state_manager.h",
"fake_host_backend_delegate.cc", "fake_host_backend_delegate.cc",
"fake_host_backend_delegate.h", "fake_host_backend_delegate.h",
"fake_host_status_observer.cc", "fake_host_status_observer.cc",
...@@ -101,6 +107,7 @@ source_set("unit_tests") { ...@@ -101,6 +107,7 @@ source_set("unit_tests") {
sources = [ sources = [
"account_status_change_delegate_notifier_impl_unittest.cc", "account_status_change_delegate_notifier_impl_unittest.cc",
"eligible_host_devices_provider_impl_unittest.cc", "eligible_host_devices_provider_impl_unittest.cc",
"feature_state_manager_impl_unittest.cc",
"host_backend_delegate_impl_unittest.cc", "host_backend_delegate_impl_unittest.cc",
"host_status_provider_impl_unittest.cc", "host_status_provider_impl_unittest.cc",
"host_verifier_impl_unittest.cc", "host_verifier_impl_unittest.cc",
...@@ -115,6 +122,7 @@ source_set("unit_tests") { ...@@ -115,6 +122,7 @@ source_set("unit_tests") {
"//base", "//base",
"//base/test:test_support", "//base/test:test_support",
"//chromeos/services/device_sync/public/cpp:test_support", "//chromeos/services/device_sync/public/cpp:test_support",
"//chromeos/services/multidevice_setup/public/cpp:prefs",
"//chromeos/services/multidevice_setup/public/cpp:test_support", "//chromeos/services/multidevice_setup/public/cpp:test_support",
"//chromeos/services/multidevice_setup/public/cpp:unit_tests", "//chromeos/services/multidevice_setup/public/cpp:unit_tests",
"//chromeos/services/multidevice_setup/public/mojom", "//chromeos/services/multidevice_setup/public/mojom",
......
// 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/fake_feature_state_manager.h"
namespace chromeos {
namespace multidevice_setup {
namespace {
// Each feature's default value is kUnavailableNoVerifiedHost until proven
// otherwise.
FeatureStateManager::FeatureStatesMap GenerateInitialDefaultCachedStateMap() {
return FeatureStateManager::FeatureStatesMap{
{mojom::Feature::kBetterTogetherSuite,
mojom::FeatureState::kUnavailableNoVerifiedHost},
{mojom::Feature::kInstantTethering,
mojom::FeatureState::kUnavailableNoVerifiedHost},
{mojom::Feature::kMessages,
mojom::FeatureState::kUnavailableNoVerifiedHost},
{mojom::Feature::kSmartLock,
mojom::FeatureState::kUnavailableNoVerifiedHost}};
}
} // namespace
FakeFeatureStateManager::FakeFeatureStateManager()
: feature_states_map_(GenerateInitialDefaultCachedStateMap()) {}
FakeFeatureStateManager::~FakeFeatureStateManager() = default;
void FakeFeatureStateManager::SetFeatureState(mojom::Feature feature,
mojom::FeatureState state) {
if (feature_states_map_[feature] == state)
return;
feature_states_map_[feature] = state;
NotifyFeatureStatesChange(feature_states_map_);
}
void FakeFeatureStateManager::SetFeatureStates(
const FeatureStatesMap& feature_states_map) {
if (feature_states_map_ == feature_states_map)
return;
feature_states_map_ = feature_states_map;
NotifyFeatureStatesChange(feature_states_map_);
}
FeatureStateManager::FeatureStatesMap
FakeFeatureStateManager::GetFeatureStates() {
return feature_states_map_;
}
void FakeFeatureStateManager::PerformSetFeatureEnabledState(
mojom::Feature feature,
bool enabled) {
if (enabled)
SetFeatureState(feature, mojom::FeatureState::kEnabledByUser);
else
SetFeatureState(feature, mojom::FeatureState::kDisabledByUser);
}
FakeFeatureStateManagerObserver::FakeFeatureStateManagerObserver() = default;
FakeFeatureStateManagerObserver::~FakeFeatureStateManagerObserver() = default;
void FakeFeatureStateManagerObserver::OnFeatureStatesChange(
const FeatureStateManager::FeatureStatesMap& feature_states_map) {
feature_state_updates_.emplace_back(feature_states_map);
}
} // namespace multidevice_setup
} // namespace chromeos
// 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.
#ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_FEATURE_STATE_MANAGER_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_FEATURE_STATE_MANAGER_H_
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "chromeos/services/multidevice_setup/feature_state_manager.h"
#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
namespace chromeos {
namespace multidevice_setup {
// Test FeatureStateManager implementation. This class initializes all features
// to be state mojom::FeatureState::kUnavailableNoVerifiedHost.
class FakeFeatureStateManager : public FeatureStateManager {
public:
FakeFeatureStateManager();
~FakeFeatureStateManager() override;
void SetFeatureState(mojom::Feature feature, mojom::FeatureState state);
void SetFeatureStates(const FeatureStatesMap& feature_states_map);
using FeatureStateManager::NotifyFeatureStatesChange;
private:
// FeatureStateManager:
FeatureStatesMap GetFeatureStates() override;
void PerformSetFeatureEnabledState(mojom::Feature feature,
bool enabled) override;
FeatureStatesMap feature_states_map_;
DISALLOW_COPY_AND_ASSIGN(FakeFeatureStateManager);
};
// Test FeatureStateManager::Observer implementation.
class FakeFeatureStateManagerObserver : public FeatureStateManager::Observer {
public:
FakeFeatureStateManagerObserver();
~FakeFeatureStateManagerObserver() override;
const std::vector<FeatureStateManager::FeatureStatesMap>&
feature_state_updates() const {
return feature_state_updates_;
}
private:
// FeatureStateManager::Observer:
void OnFeatureStatesChange(
const FeatureStateManager::FeatureStatesMap& feature_states_map) override;
std::vector<FeatureStateManager::FeatureStatesMap> feature_state_updates_;
DISALLOW_COPY_AND_ASSIGN(FakeFeatureStateManagerObserver);
};
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FAKE_FEATURE_STATE_MANAGER_H_
...@@ -24,10 +24,10 @@ class FakeHostStatusProvider : public HostStatusProvider { ...@@ -24,10 +24,10 @@ class FakeHostStatusProvider : public HostStatusProvider {
mojom::HostStatus host_status, mojom::HostStatus host_status,
const base::Optional<cryptauth::RemoteDeviceRef>& host_device); const base::Optional<cryptauth::RemoteDeviceRef>& host_device);
private:
// HostStatusProvider: // HostStatusProvider:
HostStatusWithDevice GetHostWithStatus() const override; HostStatusWithDevice GetHostWithStatus() const override;
private:
mojom::HostStatus host_status_ = mojom::HostStatus::kNoEligibleHosts; mojom::HostStatus host_status_ = mojom::HostStatus::kNoEligibleHosts;
base::Optional<cryptauth::RemoteDeviceRef> host_device_; base::Optional<cryptauth::RemoteDeviceRef> host_device_;
......
// 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/feature_state_manager.h"
namespace chromeos {
namespace multidevice_setup {
FeatureStateManager::FeatureStateManager() = default;
FeatureStateManager::~FeatureStateManager() = default;
bool FeatureStateManager::SetFeatureEnabledState(mojom::Feature feature,
bool enabled) {
mojom::FeatureState state = GetFeatureStates()[feature];
// Changing the state is only allowed when changing from enabled to disabled
// or disabled to enabled.
if ((state == mojom::FeatureState::kEnabledByUser && !enabled) ||
(state == mojom::FeatureState::kDisabledByUser && enabled)) {
PerformSetFeatureEnabledState(feature, enabled);
return true;
}
return false;
}
void FeatureStateManager::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void FeatureStateManager::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void FeatureStateManager::NotifyFeatureStatesChange(
const FeatureStatesMap& feature_states_map) {
for (auto& observer : observer_list_)
observer.OnFeatureStatesChange(feature_states_map);
}
} // namespace multidevice_setup
} // namespace chromeos
// 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.
#ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FEATURE_STATE_MANAGER_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FEATURE_STATE_MANAGER_H_
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
namespace chromeos {
namespace multidevice_setup {
// Tracks the state of the multi-device features, providing a getter/observer
// interface as well as a way to toggle a feature on/off when appropriate.
class FeatureStateManager {
public:
using FeatureStatesMap = base::flat_map<mojom::Feature, mojom::FeatureState>;
class Observer {
public:
virtual ~Observer() = default;
// Invoked when one or more features have changed state.
virtual void OnFeatureStatesChange(
const FeatureStatesMap& feature_states_map) = 0;
};
virtual ~FeatureStateManager();
virtual FeatureStatesMap GetFeatureStates() = 0;
// Attempts to enable or disable the feature; returns whether this operation
// succeeded. A feature can only be changed via this function if the current
// state is mojom::FeatureState::kEnabledByUser or
// mojom::FeatureState::kDisabledByUser.
bool SetFeatureEnabledState(mojom::Feature feature, bool enabled);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
protected:
FeatureStateManager();
// Enables or disables the feature; by the time this function has been called,
// it has already been confirmed that the state is indeed able to be changed.
virtual void PerformSetFeatureEnabledState(mojom::Feature feature,
bool enabled) = 0;
void NotifyFeatureStatesChange(const FeatureStatesMap& feature_states_map);
private:
base::ObserverList<Observer> observer_list_;
DISALLOW_COPY_AND_ASSIGN(FeatureStateManager);
};
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FEATURE_STATE_MANAGER_H_
// 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.
#ifndef CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FEATURE_STATE_MANAGER_IMPL_H_
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FEATURE_STATE_MANAGER_IMPL_H_
#include "base/containers/flat_map.h"
#include "base/macros.h"
#include "chromeos/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/services/multidevice_setup/feature_state_manager.h"
#include "chromeos/services/multidevice_setup/host_status_provider.h"
#include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
#include "components/prefs/pref_change_registrar.h"
class PrefService;
namespace chromeos {
namespace multidevice_setup {
// Concrete FeatureStateManager implementation. This class relies on
// HostStatusProvider and DeviceSyncClient to determine if features are
// available at all (features are not available unless a verified host is set
// which has enabled the features). To track enabled/disabled/policy state, this
// class utilizes per-user preferences.
class FeatureStateManagerImpl : public FeatureStateManager,
public HostStatusProvider::Observer,
public device_sync::DeviceSyncClient::Observer {
public:
class Factory {
public:
static Factory* Get();
static void SetFactoryForTesting(Factory* test_factory);
virtual ~Factory();
virtual std::unique_ptr<FeatureStateManager> BuildInstance(
PrefService* pref_service,
HostStatusProvider* host_status_provider,
device_sync::DeviceSyncClient* device_sync_client);
private:
static Factory* test_factory_;
};
~FeatureStateManagerImpl() override;
private:
FeatureStateManagerImpl(PrefService* pref_service,
HostStatusProvider* host_status_provider,
device_sync::DeviceSyncClient* device_sync_client);
// FeatureStateManager:
FeatureStatesMap GetFeatureStates() override;
void PerformSetFeatureEnabledState(mojom::Feature feature,
bool enabled) override;
// HostStatusProvider::Observer,
void OnHostStatusChange(const HostStatusProvider::HostStatusWithDevice&
host_status_with_device) override;
// DeviceSyncClient::Observer:
void OnNewDevicesSynced() override;
void OnPrefValueChanged();
void UpdateFeatureStateCache(bool notify_observers_of_changes);
mojom::FeatureState ComputeFeatureState(mojom::Feature feature);
bool IsSupportedByChromebook(mojom::Feature feature);
bool HasSufficientSecurity(mojom::Feature feature,
const cryptauth::RemoteDeviceRef& host_device);
bool HasBeenActivatedByPhone(mojom::Feature feature,
const cryptauth::RemoteDeviceRef& host_device);
mojom::FeatureState GetEnabledOrDisabledState(mojom::Feature feature);
PrefService* pref_service_;
HostStatusProvider* host_status_provider_;
device_sync::DeviceSyncClient* device_sync_client_;
// Map from feature to the pref name which indicates the enabled/disabled
// boolean state for the feature.
base::flat_map<mojom::Feature, std::string> feature_to_enabled_pref_name_map_;
// Same as above, except that the pref names represent whether the feature is
// allowed by policy or not.
base::flat_map<mojom::Feature, std::string> feature_to_allowed_pref_name_map_;
// Map from feature to state, which is updated each time a feature's state
// changes. This cache is used to determine when a feature's state has changed
// so that observers can be notified.
FeatureStatesMap cached_feature_state_map_;
PrefChangeRegistrar registrar_;
DISALLOW_COPY_AND_ASSIGN(FeatureStateManagerImpl);
};
} // namespace multidevice_setup
} // namespace chromeos
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_FEATURE_STATE_MANAGER_IMPL_H_
...@@ -42,6 +42,41 @@ enum HostStatus { ...@@ -42,6 +42,41 @@ enum HostStatus {
kHostVerified kHostVerified
}; };
// Individual multi-device features types.
enum Feature {
kBetterTogetherSuite,
kInstantTethering,
kMessages,
kSmartLock
};
enum FeatureState {
// Feature was disabled by a device policy (e.g., EDU or Enterprise).
kDisabledByPolicy,
// Feature was disabled by the user (i.e., via settings).
kDisabledByUser,
// Feature was enabled by the user (i.e., via settings).
kEnabledByUser,
// Feature is not supported by this Chromebook.
kNotSupportedByChromebook,
// Feature is not supported by the current host phone device.
kNotSupportedByPhone,
// The feature is unavailable because there is no verified multi-device host.
// To set a host, use SetHostDevice(); to verify a host, use
// RetrySetHostNow().
kUnavailableNoVerifiedHost,
// The feature is unavailable because there are insufficient security
// mechanisms in place (e.g., Smart Lock returns this value when the host
// phone device does not have a lock screen set).
kUnavailableInsufficientSecurity
};
interface AccountStatusChangeDelegate { interface AccountStatusChangeDelegate {
// Callback which indicates that one or more MultiDevice host phones are // Callback which indicates that one or more MultiDevice host phones are
// available for setup with the MultiDevice setup flow. This function is only // available for setup with the MultiDevice setup flow. This function is only
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment