Commit f7ffd7d2 authored by James Cook's avatar James Cook Committed by Commit Bot

Sync updates for SYNCABLE_OS_PREFS back to old clients

We rely on sync to provide the user's OS settings when setting
up a new Chromebook. However, new devices are not guaranteed
to be running the latest version of Chrome before the user's
first login (it's a poor experience to have to wait to download
a large OS update before being allowed to sign in).

As part of go/split-settings-sync we are migrating OS prefs to
new sync ModelTypes OS_PREFERENCES and OS_PRIORITY_PREFERENCES.
We need to ensure that old clients still get synced updates for
these preferences.

When split sync is disabled, register OS pref names with the
browser pref ModelTypes. This makes sync work just like it did
before the split, and allows OS prefs to always be registered
as SYNCABLE_OS_PREF, without conditionals on the flag.

When split sync is enabled, register OS pref names with the
PrefModelAssociators for both the new ModelType and the old
ModelType. When a pref is changed locally, this sends updates
to the sync server under both ModelTypes.

However, when a sync change is received from the server we
only apply the update if the change is for the new ModelType.
We made a product decision not to sync updates from old clients
to new clients. This helps avoid ping-pong updates between old
and new clients.

Bug: 1013466
Test: added to components_unittests
Test: manually verified that new client with flag enabled
  sends updates to old client (M78), but old client changes
  are ignored on new client.
  updates
Test: manually verified that client with flag disabled
  syncs updates in both directions with old client (M78).

Change-Id: I04c494938d3526c57b8eb2f9c689acb0d4b0a3ed
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1865662
Commit-Queue: James Cook <jamescook@chromium.org>
Auto-Submit: James Cook <jamescook@chromium.org>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Cr-Commit-Position: refs/heads/master@{#708442}
parent e90092de
......@@ -26,6 +26,10 @@ static_library("sync_preferences") {
"//services/preferences/public/cpp:service_main",
]
if (is_chromeos) {
deps += [ "//chromeos/constants" ]
}
if (!is_ios) {
deps += [ "//components/policy/core/browser" ]
}
......@@ -67,4 +71,8 @@ source_set("unit_tests") {
"//components/sync:test_support_model",
"//testing/gtest",
]
if (is_chromeos) {
deps += [ "//chromeos/constants" ]
}
}
include_rules = [
"+chromeos/constants",
"+components/policy/core/browser",
"+components/policy/core/common",
"+components/pref_registry",
......
......@@ -229,6 +229,16 @@ syncer::SyncMergeResult PrefModelAssociator::MergeDataAndStartSyncing(
InitPrefAndAssociate(syncer::SyncData(), *pref_name_iter, &new_changes);
}
for (const std::string& legacy_pref_name : legacy_model_type_preferences_) {
// Track preferences for which we have a local user-controlled value. That
// could be a value from last run, or a value just set by the initial sync.
// We don't call InitPrefAndAssociate because we don't want the initial sync
// to trigger outgoing changes -- these prefs are only tracked to send
// updates back to older clients.
if (pref_service_->GetUserPrefValue(legacy_pref_name))
synced_preferences_.insert(legacy_pref_name);
}
// Push updates to sync.
merge_result.set_error(
sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes));
......@@ -474,17 +484,29 @@ void PrefModelAssociator::RegisterPref(const std::string& name) {
DCHECK(registered_preferences_.count(name) == 0);
registered_preferences_.insert(name);
// Make sure data in the local store matches the registered type.
// Make sure data in the local store matches the registered type (where "type"
// means base::Value data type like string, not ModelType like PREFERENCES).
// If this results in a modification of the local pref store, we don't want
// to tell ChromeSync about these -- it's a local anomaly,
base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true);
EnforceRegisteredTypeInStore(name);
}
void PrefModelAssociator::RegisterPrefWithLegacyModelType(
const std::string& name) {
DCHECK(legacy_model_type_preferences_.count(name) == 0);
DCHECK(registered_preferences_.count(name) == 0);
legacy_model_type_preferences_.insert(name);
}
bool PrefModelAssociator::IsPrefRegistered(const std::string& name) const {
return registered_preferences_.count(name) > 0;
}
bool PrefModelAssociator::IsLegacyModelTypePref(const std::string& name) const {
return legacy_model_type_preferences_.count(name) > 0;
}
void PrefModelAssociator::ProcessPrefChange(const std::string& name) {
if (processing_syncer_changes_)
return; // These are changes originating from us, ignore.
......@@ -500,10 +522,12 @@ void PrefModelAssociator::ProcessPrefChange(const std::string& name) {
if (!preference)
return;
if (!IsPrefRegistered(name)) {
if (!IsPrefRegistered(name) && !IsLegacyModelTypePref(name)) {
// We are not syncing this preference -- this also filters out synced
// preferences of the wrong type (priority preference are handled by a
// separate associator).
// preferences of the wrong type (e.g. priority preference are handled by a
// separate associator). Legacy model type preferences are allowed to
// continue because we want to push updates to old clients using the
// old ModelType.
return;
}
......@@ -559,6 +583,12 @@ void PrefModelAssociator::NotifySyncedPrefObservers(const std::string& path,
auto observer_iter = synced_pref_observers_.find(path);
if (observer_iter == synced_pref_observers_.end())
return;
// Don't notify for prefs we are only observing to support old clients.
// The PrefModelAssociator for the new ModelType will notify.
if (IsLegacyModelTypePref(path)) {
DCHECK(!from_sync);
return;
}
for (auto& observer : *observer_iter->second)
observer.OnSyncedPrefChanged(path, from_sync);
}
......
......@@ -81,6 +81,9 @@ class PrefModelAssociator : public syncer::SyncableService {
// begins).
void RegisterPref(const std::string& name);
// See |legacy_model_type_preferences_|.
void RegisterPrefWithLegacyModelType(const std::string& name);
// Process a local preference change. This can trigger new SyncChanges being
// sent to the syncer.
void ProcessPrefChange(const std::string& name);
......@@ -108,6 +111,10 @@ class PrefModelAssociator : public syncer::SyncableService {
// Returns true if the specified preference is registered for syncing.
bool IsPrefRegistered(const std::string& name) const;
// See |legacy_model_type_preferences_|.
// Exposed for testing.
bool IsLegacyModelTypePref(const std::string& name) const;
// Adds a SyncedPrefObserver to watch for changes to a specific pref.
void AddSyncedPrefObserver(const std::string& name,
SyncedPrefObserver* observer);
......@@ -188,6 +195,13 @@ class PrefModelAssociator : public syncer::SyncableService {
// a new sync node.
PreferenceSet synced_preferences_;
// Preferences that have migrated to a new ModelType. They are included here
// so updates can be sent back to older clients with this old ModelType.
// Updates received from older clients will be ignored. The common case is
// migration from PREFERENCES to OS_PREFERENCES. This field can be removed
// after 10/2020.
PreferenceSet legacy_model_type_preferences_;
// The PrefService we are syncing to.
PrefServiceSyncable* pref_service_ = nullptr;
......
......@@ -25,6 +25,10 @@
#include "services/preferences/public/mojom/preferences.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#if defined(OS_CHROMEOS)
#include "chromeos/constants/chromeos_features.h"
#endif
namespace sync_preferences {
// TODO(tschumann): Handing out pointers to this in the constructor is an
......@@ -236,11 +240,28 @@ void PrefServiceSyncable::AddRegisteredSyncablePreference(
}
#if defined(OS_CHROMEOS)
if (flags & user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF) {
os_pref_sync_associator_.RegisterPref(path);
if (chromeos::features::IsSplitSettingsSyncEnabled()) {
// Register the pref under the new ModelType::OS_PREFERENCES.
os_pref_sync_associator_.RegisterPref(path);
// Also register under the old ModelType::PREFERENCES. This ensures that
// local changes to OS prefs are also synced to old clients that have the
// pref registered as a browser SYNCABLE_PREF.
pref_sync_associator_.RegisterPrefWithLegacyModelType(path);
} else {
// Behave like an old client and treat this pref like it was registered
// as a SYNCABLE_PREF under ModelType::PREFERENCES.
pref_sync_associator_.RegisterPref(path);
}
return;
}
if (flags & user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PRIORITY_PREF) {
os_priority_pref_sync_associator_.RegisterPref(path);
// See comments for SYNCABLE_OS_PREF above.
if (chromeos::features::IsSplitSettingsSyncEnabled()) {
os_priority_pref_sync_associator_.RegisterPref(path);
priority_pref_sync_associator_.RegisterPrefWithLegacyModelType(path);
} else {
priority_pref_sync_associator_.RegisterPref(path);
}
return;
}
#endif
......
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