forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cros MultiDevice] Re-enroll device if SoftwareFeature supported state…
… changes. Create a class, DeviceReenroller, that compares GcmDeviceInfo's list of supported SoftwareFeatures--eg., BETTER_TOGETHER_CLIENT or MAGIC_TETHER_CLIENT--with those stored in the local device metadata on the CryptAuth server. If a SoftwareFeature state has changed from supported to unsupported, or vice versa, then re-enroll the device with CryptAuth. Finally, after a successful re-enrollment, re-sync with CryptAuth and verify that the local device metadata was updated correctly. The flow is as follows: 1) The supported states of CrOS-relevant SoftwareFeatures are set in //src/chrome/browser/chromeos/cryptauth/gcm_device_info_provider_impl.cc, based on FeatureList flags. 2) This information is stored in GcmDeviceInfo, which is passed along to the MultiDeviceSetupService via a ctor argument. 3) On creation, MultiDeviceSetupImpl creates a DeviceReenroller, providing it - a DeviceSyncClient, for communicating with CryptAuth to retrieve local device metadata, to re-enroll the device, and to sync the device; - and access to GcmDeviceInfo, in order to retrieve the latest list of supported SoftwareFeatures. 4) In the DeviceReenroller constructor, the supported software features from the local device metadata are compared with the latest supported-state given by GcmDeviceInfo's supported_software_features list. 5) If any supported-state changed, a re-enrollment is attempted via the DeviceSyncClient. 6) If the re-enrollment is successful, a device sync is attempted via the DeviceSyncClient. - If the re-enrollment is unsuccessful, another attempt is scheduled to run after 5 minutes. 7) If the device sync is unsuccessful, another attempt is scheduled to run after 5 minutes. Note that DeviceReenroller logic only runs when the MultiDeviceSetupService is created, except for possibly retrying failed enrollments or syncs after some delay. It does NOT act as an observer, checking for changes to GcmDeviceInfo's supported features list, because we only expect this to change when Chrome flags change, i.e., when the Chrome binary restarts. Bug: 870770 Change-Id: I5f26a68448f3ee0e5dd8759aac12d05d1e4760a6 Reviewed-on: https://chromium-review.googlesource.com/1182419 Reviewed-by: Ryan Hansberry <[email protected]> Reviewed-by: Ryan Hamilton <[email protected]> Reviewed-by: Jeremy Klein <[email protected]> Commit-Queue: Josh Nohle <[email protected]> Cr-Commit-Position: refs/heads/master@{#589387}
- Loading branch information
Showing
16 changed files
with
787 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
166 changes: 166 additions & 0 deletions
166
chromeos/services/multidevice_setup/device_reenroller.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
// 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/device_reenroller.h" | ||
|
||
#include "base/containers/flat_set.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/cryptauth/gcm_device_info_provider.h" | ||
|
||
namespace chromeos { | ||
|
||
namespace multidevice_setup { | ||
|
||
namespace { | ||
|
||
// The number of minutes to wait before retrying a failed re-enrollment or | ||
// device sync attempt. | ||
const int kNumMinutesBetweenRetries = 5; | ||
|
||
std::vector<cryptauth::SoftwareFeature> | ||
ComputeSupportedSoftwareFeaturesSortedDedupedListFromGcmDeviceInfo( | ||
const cryptauth::GcmDeviceInfo& gcm_device_info) { | ||
base::flat_set<cryptauth::SoftwareFeature> sorted_and_deduped_set; | ||
for (int i = 0; i < gcm_device_info.supported_software_features_size(); ++i) { | ||
sorted_and_deduped_set.insert( | ||
gcm_device_info.supported_software_features(i)); | ||
} | ||
return std::vector<cryptauth::SoftwareFeature>(sorted_and_deduped_set.begin(), | ||
sorted_and_deduped_set.end()); | ||
} | ||
|
||
std::vector<cryptauth::SoftwareFeature> | ||
ComputeSupportedSoftwareFeaturesSortedDedupedListFromLocalDeviceMetadata( | ||
const cryptauth::RemoteDeviceRef& local_device_metadata) { | ||
base::flat_set<cryptauth::SoftwareFeature> sorted_and_deduped_set; | ||
for (int i = cryptauth::SoftwareFeature_MIN; | ||
i <= cryptauth::SoftwareFeature_MAX; ++i) { | ||
cryptauth::SoftwareFeature feature = | ||
static_cast<cryptauth::SoftwareFeature>(i); | ||
if (local_device_metadata.GetSoftwareFeatureState(feature) != | ||
cryptauth::SoftwareFeatureState::kNotSupported) { | ||
sorted_and_deduped_set.insert(feature); | ||
} | ||
} | ||
return std::vector<cryptauth::SoftwareFeature>(sorted_and_deduped_set.begin(), | ||
sorted_and_deduped_set.end()); | ||
} | ||
|
||
} // namespace | ||
|
||
// static | ||
DeviceReenroller::Factory* DeviceReenroller::Factory::test_factory_ = nullptr; | ||
|
||
// static | ||
DeviceReenroller::Factory* DeviceReenroller::Factory::Get() { | ||
if (test_factory_) | ||
return test_factory_; | ||
|
||
static base::NoDestructor<Factory> factory; | ||
return factory.get(); | ||
} | ||
|
||
// static | ||
void DeviceReenroller::Factory::SetFactoryForTesting(Factory* test_factory) { | ||
test_factory_ = test_factory; | ||
} | ||
|
||
DeviceReenroller::Factory::~Factory() = default; | ||
|
||
std::unique_ptr<DeviceReenroller> DeviceReenroller::Factory::BuildInstance( | ||
device_sync::DeviceSyncClient* device_sync_client, | ||
const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider, | ||
std::unique_ptr<base::OneShotTimer> timer) { | ||
return base::WrapUnique(new DeviceReenroller( | ||
device_sync_client, gcm_device_info_provider, std::move(timer))); | ||
} | ||
|
||
DeviceReenroller::~DeviceReenroller() = default; | ||
|
||
DeviceReenroller::DeviceReenroller( | ||
device_sync::DeviceSyncClient* device_sync_client, | ||
const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider, | ||
std::unique_ptr<base::OneShotTimer> timer) | ||
: device_sync_client_(device_sync_client), | ||
gcm_supported_software_features_( | ||
ComputeSupportedSoftwareFeaturesSortedDedupedListFromGcmDeviceInfo( | ||
gcm_device_info_provider->GetGcmDeviceInfo())), | ||
timer_(std::move(timer)) { | ||
// If the current set of supported software features from GcmDeviceInfo | ||
// differs from that of the local device metadata on the CryptAuth server, | ||
// attempt re-enrollment. Note: Both lists here are sorted and duplicate-free. | ||
if (gcm_supported_software_features_ != | ||
ComputeSupportedSoftwareFeaturesSortedDedupedListFromLocalDeviceMetadata( | ||
*device_sync_client_->GetLocalDeviceMetadata())) { | ||
AttemptReenrollment(); | ||
} | ||
} | ||
|
||
void DeviceReenroller::AttemptReenrollment() { | ||
DCHECK(!timer_->IsRunning()); | ||
device_sync_client_->ForceEnrollmentNow(base::BindOnce( | ||
&DeviceReenroller::OnForceEnrollmentNowComplete, base::Unretained(this))); | ||
} | ||
|
||
void DeviceReenroller::AttemptDeviceSync() { | ||
DCHECK(!timer_->IsRunning()); | ||
device_sync_client_->ForceSyncNow(base::BindOnce( | ||
&DeviceReenroller::OnForceSyncNowComplete, base::Unretained(this))); | ||
} | ||
|
||
void DeviceReenroller::OnForceEnrollmentNowComplete(bool success) { | ||
if (success) { | ||
PA_LOG(INFO) << "DeviceReenroller::OnForceEnrollmentNowComplete(): " | ||
<< "Forced enrollment attempt was successful. " | ||
<< "Syncing devices now."; | ||
AttemptDeviceSync(); | ||
return; | ||
} | ||
PA_LOG(WARNING) << "DeviceReenroller::OnForceEnrollmentNowComplete(): " | ||
<< "Forced enrollment attempt was unsuccessful. Retrying in " | ||
<< kNumMinutesBetweenRetries << " minutes."; | ||
timer_->Start(FROM_HERE, | ||
base::TimeDelta::FromMinutes(kNumMinutesBetweenRetries), | ||
base::BindOnce(&DeviceReenroller::AttemptReenrollment, | ||
base::Unretained(this))); | ||
} | ||
|
||
void DeviceReenroller::OnForceSyncNowComplete(bool success) { | ||
// This is used to track if the device sync properly updated the local device | ||
// metadata to reflect the supported software features from GcmDeviceInfo. | ||
bool local_device_metadata_agrees = | ||
device_sync_client_->GetLocalDeviceMetadata() && | ||
ComputeSupportedSoftwareFeaturesSortedDedupedListFromLocalDeviceMetadata( | ||
*device_sync_client_->GetLocalDeviceMetadata()) == | ||
gcm_supported_software_features_; | ||
|
||
if (success && local_device_metadata_agrees) { | ||
PA_LOG(INFO) << "DeviceReenroller::OnForceSyncNowComplete(): " | ||
<< "Forced device sync attempt was successful."; | ||
return; | ||
} | ||
if (!success) { | ||
PA_LOG(WARNING) << "DeviceReenroller::OnForceSyncNowComplete(): " | ||
<< "Forced device sync attempt was unsuccessful. " | ||
<< "Retrying in " << kNumMinutesBetweenRetries | ||
<< " minutes."; | ||
} else { | ||
DCHECK(!local_device_metadata_agrees); | ||
PA_LOG(WARNING) << "DeviceReenroller::OnForceSyncNowComplete(): " | ||
<< "The local device metadata's supported software " | ||
<< "features do not agree with the set extracted from GCM " | ||
<< "device info. Retrying in " << kNumMinutesBetweenRetries | ||
<< " minutes."; | ||
} | ||
timer_->Start(FROM_HERE, | ||
base::TimeDelta::FromMinutes(kNumMinutesBetweenRetries), | ||
base::BindOnce(&DeviceReenroller::AttemptDeviceSync, | ||
base::Unretained(this))); | ||
} | ||
|
||
} // namespace multidevice_setup | ||
|
||
} // namespace chromeos |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// 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_DEVICE_REENROLLER_H_ | ||
#define CHROMEOS_SERVICES_MULTIDEVICE_SETUP_DEVICE_REENROLLER_H_ | ||
|
||
#include <memory> | ||
|
||
#include "base/macros.h" | ||
#include "components/cryptauth/proto/cryptauth_api.pb.h" | ||
|
||
namespace base { | ||
class OneShotTimer; | ||
} // namespace base | ||
|
||
namespace cryptauth { | ||
class GcmDeviceInfoProvider; | ||
} // namespace cryptauth | ||
|
||
namespace chromeos { | ||
|
||
namespace device_sync { | ||
class DeviceSyncClient; | ||
} // namespace device_sync | ||
|
||
namespace multidevice_setup { | ||
|
||
// The DeviceReenroller constructor re-enrolls and syncs the device if the set | ||
// of supported SoftwareFeatures in the current GCM device info differs from | ||
// that of the local device metadata on the CryptAuth server. | ||
class DeviceReenroller { | ||
public: | ||
class Factory { | ||
public: | ||
static Factory* Get(); | ||
static void SetFactoryForTesting(Factory* test_factory); | ||
virtual ~Factory(); | ||
virtual std::unique_ptr<DeviceReenroller> BuildInstance( | ||
device_sync::DeviceSyncClient* device_sync_client, | ||
const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider, | ||
std::unique_ptr<base::OneShotTimer> timer = | ||
std::make_unique<base::OneShotTimer>()); | ||
|
||
private: | ||
static Factory* test_factory_; | ||
}; | ||
|
||
virtual ~DeviceReenroller(); | ||
|
||
private: | ||
DeviceReenroller( | ||
device_sync::DeviceSyncClient* device_sync_client, | ||
const cryptauth::GcmDeviceInfoProvider* gcm_device_info_provider, | ||
std::unique_ptr<base::OneShotTimer> timer); | ||
|
||
void AttemptReenrollment(); | ||
void AttemptDeviceSync(); | ||
|
||
// If the re-enrollment was successful, force a device sync; otherwise, retry | ||
// re-enrollment every 5 minutes or until success. | ||
void OnForceEnrollmentNowComplete(bool success); | ||
// If the device sync was successful and the list of supported software | ||
// features on the CryptAuth server now agrees with the list of supported | ||
// software features in GcmDeviceInfo, log the success; otherwise, retry | ||
// device sync every 5 minutes or until success. | ||
void OnForceSyncNowComplete(bool success); | ||
|
||
device_sync::DeviceSyncClient* device_sync_client_; | ||
// The sorted and deduped list of supported software features extracted from | ||
// GcmDeviceInfo. | ||
std::vector<cryptauth::SoftwareFeature> gcm_supported_software_features_; | ||
std::unique_ptr<base::OneShotTimer> timer_; | ||
|
||
DISALLOW_COPY_AND_ASSIGN(DeviceReenroller); | ||
}; | ||
|
||
} // namespace multidevice_setup | ||
|
||
} // namespace chromeos | ||
|
||
#endif // CHROMEOS_SERVICES_MULTIDEVICE_SETUP_DEVICE_REENROLLER_H_ |
Oops, something went wrong.