1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/proximity_auth/cryptauth/cryptauth_device_manager.h"
7 #include "base/prefs/pref_registry_simple.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "components/proximity_auth/cryptauth/cryptauth_client.h"
11 #include "components/proximity_auth/cryptauth/pref_names.h"
12 #include "components/proximity_auth/cryptauth/sync_scheduler_impl.h"
13 #include "components/proximity_auth/logging/logging.h"
15 namespace proximity_auth
{
19 // The normal period between successful syncs, in hours.
20 const int kRefreshPeriodHours
= 24;
22 // A more aggressive period between sync attempts to recover when the last
23 // sync attempt fails, in minutes. This is a base time that increases for each
24 // subsequent failure.
25 const int kDeviceSyncBaseRecoveryPeriodMinutes
= 10;
27 // The bound on the amount to jitter the period between syncs.
28 const double kDeviceSyncMaxJitterRatio
= 0.2;
30 // Keys for ExternalDeviceInfo dictionaries that are stored in the user's prefs.
31 const char kExternalDeviceKeyPublicKey
[] = "public_key";
32 const char kExternalDeviceKeyDeviceName
[] = "device_name";
33 const char kExternalDeviceKeyBluetoothAddress
[] = "bluetooth_address";
35 // Converts an unlock key proto to a dictionary that can be stored in user
37 scoped_ptr
<base::DictionaryValue
> UnlockKeyToDictionary(
38 const cryptauth::ExternalDeviceInfo
& device
) {
39 scoped_ptr
<base::DictionaryValue
> dictionary(new base::DictionaryValue());
40 dictionary
->SetString(kExternalDeviceKeyPublicKey
, device
.public_key());
41 dictionary
->SetString(kExternalDeviceKeyDeviceName
,
42 device
.friendly_device_name());
43 dictionary
->SetString(kExternalDeviceKeyBluetoothAddress
,
44 device
.bluetooth_address());
45 return dictionary
.Pass();
48 // Converts an unlock key dictionary stored in user prefs to an
49 // ExternalDeviceInfo proto. Returns true if the dictionary is valid, and the
50 // parsed proto is written to |external_device|.
51 bool DictionaryToUnlockKey(const base::DictionaryValue
& dictionary
,
52 cryptauth::ExternalDeviceInfo
* external_device
) {
53 std::string public_key
, device_name
, bluetooth_address
;
54 if (!dictionary
.GetString(kExternalDeviceKeyPublicKey
, &public_key
) ||
55 !dictionary
.GetString(kExternalDeviceKeyDeviceName
, &device_name
) ||
56 !dictionary
.GetString(kExternalDeviceKeyBluetoothAddress
,
57 &bluetooth_address
)) {
61 external_device
->set_public_key(public_key
);
62 external_device
->set_friendly_device_name(device_name
);
63 external_device
->set_bluetooth_address(bluetooth_address
);
64 external_device
->set_unlock_key(true);
65 external_device
->set_unlockable(false);
71 CryptAuthDeviceManager::CryptAuthDeviceManager(
72 scoped_ptr
<base::Clock
> clock
,
73 scoped_ptr
<CryptAuthClientFactory
> client_factory
,
74 PrefService
* pref_service
)
75 : clock_(clock
.Pass()),
76 client_factory_(client_factory
.Pass()),
77 pref_service_(pref_service
),
78 weak_ptr_factory_(this) {
81 CryptAuthDeviceManager::~CryptAuthDeviceManager() {
85 void CryptAuthDeviceManager::RegisterPrefs(PrefRegistrySimple
* registry
) {
86 registry
->RegisterDoublePref(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds
,
88 registry
->RegisterBooleanPref(
89 prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure
, false);
90 registry
->RegisterIntegerPref(prefs::kCryptAuthDeviceSyncReason
,
91 cryptauth::INVOCATION_REASON_UNKNOWN
);
92 registry
->RegisterListPref(prefs::kCryptAuthDeviceSyncUnlockKeys
);
95 void CryptAuthDeviceManager::Start() {
96 UpdateUnlockKeysFromPrefs();
98 base::Time last_successful_sync
= GetLastSyncTime();
99 base::TimeDelta elapsed_time_since_last_sync
=
100 clock_
->Now() - last_successful_sync
;
102 bool is_recovering_from_failure
=
103 pref_service_
->GetBoolean(
104 prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure
) ||
105 last_successful_sync
.is_null();
107 scheduler_
= CreateSyncScheduler();
108 scheduler_
->Start(elapsed_time_since_last_sync
,
109 is_recovering_from_failure
110 ? SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
111 : SyncScheduler::Strategy::PERIODIC_REFRESH
);
114 void CryptAuthDeviceManager::AddObserver(Observer
* observer
) {
115 observers_
.AddObserver(observer
);
118 void CryptAuthDeviceManager::RemoveObserver(Observer
* observer
) {
119 observers_
.RemoveObserver(observer
);
122 void CryptAuthDeviceManager::ForceSyncNow(
123 cryptauth::InvocationReason invocation_reason
) {
124 pref_service_
->SetInteger(prefs::kCryptAuthDeviceSyncReason
,
126 scheduler_
->ForceSync();
129 base::Time
CryptAuthDeviceManager::GetLastSyncTime() const {
130 return base::Time::FromDoubleT(
131 pref_service_
->GetDouble(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds
));
134 base::TimeDelta
CryptAuthDeviceManager::GetTimeToNextAttempt() const {
135 return scheduler_
->GetTimeToNextSync();
138 bool CryptAuthDeviceManager::IsSyncInProgress() const {
139 return scheduler_
->GetSyncState() ==
140 SyncScheduler::SyncState::SYNC_IN_PROGRESS
;
143 bool CryptAuthDeviceManager::IsRecoveringFromFailure() const {
144 return scheduler_
->GetStrategy() ==
145 SyncScheduler::Strategy::AGGRESSIVE_RECOVERY
;
148 void CryptAuthDeviceManager::OnGetMyDevicesSuccess(
149 const cryptauth::GetMyDevicesResponse
& response
) {
150 // Update the unlock keys stored in the user's prefs.
151 scoped_ptr
<base::ListValue
> unlock_keys_pref(new base::ListValue());
152 for (const auto& device
: response
.devices()) {
153 if (device
.unlock_key())
154 unlock_keys_pref
->Append(UnlockKeyToDictionary(device
));
157 bool unlock_keys_changed
= !unlock_keys_pref
->Equals(
158 pref_service_
->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys
));
160 ListPrefUpdate
update(pref_service_
, prefs::kCryptAuthDeviceSyncUnlockKeys
);
161 update
.Get()->Swap(unlock_keys_pref
.get());
163 UpdateUnlockKeysFromPrefs();
165 // Reset metadata used for scheduling syncing.
166 pref_service_
->SetBoolean(prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure
,
168 pref_service_
->SetDouble(prefs::kCryptAuthDeviceSyncLastSyncTimeSeconds
,
169 clock_
->Now().ToDoubleT());
170 pref_service_
->SetInteger(prefs::kCryptAuthDeviceSyncReason
,
171 cryptauth::INVOCATION_REASON_UNKNOWN
);
173 sync_request_
->OnDidComplete(true);
174 cryptauth_client_
.reset();
175 sync_request_
.reset();
177 Observer
, observers_
,
178 OnSyncFinished(SyncResult::SUCCESS
, unlock_keys_changed
179 ? DeviceChangeResult::CHANGED
180 : DeviceChangeResult::UNCHANGED
));
183 void CryptAuthDeviceManager::OnGetMyDevicesFailure(const std::string
& error
) {
184 PA_LOG(ERROR
) << "GetMyDevices API failed: " << error
;
185 pref_service_
->SetBoolean(prefs::kCryptAuthDeviceSyncIsRecoveringFromFailure
,
187 sync_request_
->OnDidComplete(false);
188 cryptauth_client_
.reset();
189 sync_request_
.reset();
191 Observer
, observers_
,
192 OnSyncFinished(SyncResult::FAILURE
, DeviceChangeResult::UNCHANGED
));
195 scoped_ptr
<SyncScheduler
> CryptAuthDeviceManager::CreateSyncScheduler() {
196 return make_scoped_ptr(new SyncSchedulerImpl(
197 this, base::TimeDelta::FromHours(kRefreshPeriodHours
),
198 base::TimeDelta::FromMinutes(kDeviceSyncBaseRecoveryPeriodMinutes
),
199 kDeviceSyncMaxJitterRatio
, "CryptAuth DeviceSync"));
202 void CryptAuthDeviceManager::UpdateUnlockKeysFromPrefs() {
203 const base::ListValue
* unlock_key_list
=
204 pref_service_
->GetList(prefs::kCryptAuthDeviceSyncUnlockKeys
);
205 unlock_keys_
.clear();
206 for (size_t i
= 0; i
< unlock_key_list
->GetSize(); ++i
) {
207 const base::DictionaryValue
* unlock_key_dictionary
;
208 if (unlock_key_list
->GetDictionary(i
, &unlock_key_dictionary
)) {
209 cryptauth::ExternalDeviceInfo unlock_key
;
210 if (DictionaryToUnlockKey(*unlock_key_dictionary
, &unlock_key
)) {
211 unlock_keys_
.push_back(unlock_key
);
213 PA_LOG(ERROR
) << "Unable to deserialize unlock key dictionary "
214 << "(index=" << i
<< "):\n" << *unlock_key_dictionary
;
217 PA_LOG(ERROR
) << "Can not get dictionary in list of unlock keys "
218 << "(index=" << i
<< "):\n" << *unlock_key_list
;
223 void CryptAuthDeviceManager::OnSyncRequested(
224 scoped_ptr
<SyncScheduler::SyncRequest
> sync_request
) {
225 FOR_EACH_OBSERVER(Observer
, observers_
, OnSyncStarted());
227 sync_request_
= sync_request
.Pass();
228 cryptauth_client_
= client_factory_
->CreateInstance();
230 cryptauth::InvocationReason invocation_reason
=
231 cryptauth::INVOCATION_REASON_UNKNOWN
;
233 int reason_stored_in_prefs
=
234 pref_service_
->GetInteger(prefs::kCryptAuthDeviceSyncReason
);
236 if (cryptauth::InvocationReason_IsValid(reason_stored_in_prefs
) &&
237 reason_stored_in_prefs
!= cryptauth::INVOCATION_REASON_UNKNOWN
) {
239 static_cast<cryptauth::InvocationReason
>(reason_stored_in_prefs
);
240 } else if (GetLastSyncTime().is_null()) {
241 invocation_reason
= cryptauth::INVOCATION_REASON_INITIALIZATION
;
242 } else if (IsRecoveringFromFailure()) {
243 invocation_reason
= cryptauth::INVOCATION_REASON_FAILURE_RECOVERY
;
245 invocation_reason
= cryptauth::INVOCATION_REASON_PERIODIC
;
248 cryptauth::GetMyDevicesRequest request
;
249 request
.set_invocation_reason(invocation_reason
);
250 cryptauth_client_
->GetMyDevices(
251 request
, base::Bind(&CryptAuthDeviceManager::OnGetMyDevicesSuccess
,
252 weak_ptr_factory_
.GetWeakPtr()),
253 base::Bind(&CryptAuthDeviceManager::OnGetMyDevicesFailure
,
254 weak_ptr_factory_
.GetWeakPtr()));
257 } // namespace proximity_auth