Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(90)

Side by Side Diff: sync/engine/apply_control_data_updates_unittest.cc

Issue 2130453004: [Sync] Move //sync to //components/sync. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sync/engine/apply_control_data_updates.cc ('k') | sync/engine/backoff_delay_provider.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2012 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.
4
5 #include "sync/engine/apply_control_data_updates.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include <memory>
11 #include <string>
12
13 #include "base/format_macros.h"
14 #include "base/location.h"
15 #include "base/macros.h"
16 #include "base/message_loop/message_loop.h"
17 #include "base/strings/stringprintf.h"
18 #include "sync/engine/syncer.h"
19 #include "sync/engine/syncer_util.h"
20 #include "sync/internal_api/public/test/test_entry_factory.h"
21 #include "sync/protocol/nigori_specifics.pb.h"
22 #include "sync/syncable/directory.h"
23 #include "sync/syncable/mutable_entry.h"
24 #include "sync/syncable/nigori_util.h"
25 #include "sync/syncable/syncable_read_transaction.h"
26 #include "sync/syncable/syncable_util.h"
27 #include "sync/syncable/syncable_write_transaction.h"
28 #include "sync/test/engine/fake_model_worker.h"
29 #include "sync/test/engine/test_directory_setter_upper.h"
30 #include "sync/test/engine/test_id_factory.h"
31 #include "sync/test/fake_sync_encryption_handler.h"
32 #include "sync/util/cryptographer.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34
35 namespace syncer {
36
37 using syncable::MutableEntry;
38 using syncable::UNITTEST;
39 using syncable::Id;
40
41 class ApplyControlDataUpdatesTest : public ::testing::Test {
42 public:
43 protected:
44 ApplyControlDataUpdatesTest() {}
45 ~ApplyControlDataUpdatesTest() override {}
46
47 void SetUp() override {
48 dir_maker_.SetUp();
49 entry_factory_.reset(new TestEntryFactory(directory()));
50 }
51
52 void TearDown() override { dir_maker_.TearDown(); }
53
54 syncable::Directory* directory() {
55 return dir_maker_.directory();
56 }
57
58 TestIdFactory id_factory_;
59 std::unique_ptr<TestEntryFactory> entry_factory_;
60
61 private:
62 base::MessageLoop loop_; // Needed for directory init.
63 TestDirectorySetterUpper dir_maker_;
64
65 DISALLOW_COPY_AND_ASSIGN(ApplyControlDataUpdatesTest);
66 };
67
68 // Verify that applying a nigori node sets initial sync ended properly,
69 // updates the set of encrypted types, and updates the cryptographer.
70 TEST_F(ApplyControlDataUpdatesTest, NigoriUpdate) {
71 // Storing the cryptographer separately is bad, but for this test we
72 // know it's safe.
73 Cryptographer* cryptographer;
74 ModelTypeSet encrypted_types;
75 encrypted_types.PutAll(SyncEncryptionHandler::SensitiveTypes());
76
77 {
78 syncable::ReadTransaction trans(FROM_HERE, directory());
79 cryptographer = directory()->GetCryptographer(&trans);
80 EXPECT_EQ(encrypted_types,
81 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
82 }
83
84 // Nigori node updates should update the Cryptographer.
85 Cryptographer other_cryptographer(cryptographer->encryptor());
86 KeyParams params = {"localhost", "dummy", "foobar"};
87 other_cryptographer.AddKey(params);
88
89 sync_pb::EntitySpecifics specifics;
90 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
91 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
92 nigori->set_encrypt_everything(true);
93 entry_factory_->CreateUnappliedNewItem(
94 ModelTypeToRootTag(NIGORI), specifics, true);
95 EXPECT_FALSE(cryptographer->has_pending_keys());
96
97 ApplyControlDataUpdates(directory());
98
99 EXPECT_FALSE(cryptographer->is_ready());
100 EXPECT_TRUE(cryptographer->has_pending_keys());
101 {
102 syncable::ReadTransaction trans(FROM_HERE, directory());
103 EXPECT_EQ(ModelTypeSet::All(),
104 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
105 }
106 }
107
108 // Create some local unsynced and unencrypted data. Apply a nigori update that
109 // turns on encryption for the unsynced data. Ensure we properly encrypt the
110 // data as part of the nigori update. Apply another nigori update with no
111 // changes. Ensure we ignore already-encrypted unsynced data and that nothing
112 // breaks.
113 TEST_F(ApplyControlDataUpdatesTest, EncryptUnsyncedChanges) {
114 // Storing the cryptographer separately is bad, but for this test we
115 // know it's safe.
116 Cryptographer* cryptographer;
117 ModelTypeSet encrypted_types;
118 encrypted_types.PutAll(SyncEncryptionHandler::SensitiveTypes());
119 {
120 syncable::ReadTransaction trans(FROM_HERE, directory());
121 cryptographer = directory()->GetCryptographer(&trans);
122 EXPECT_EQ(encrypted_types,
123 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
124
125 // With default encrypted_types, this should be true.
126 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
127
128 Syncer::UnsyncedMetaHandles handles;
129 GetUnsyncedEntries(&trans, &handles);
130 EXPECT_TRUE(handles.empty());
131 }
132
133 // Create unsynced bookmarks without encryption.
134 // First item is a folder
135 Id folder_id = id_factory_.NewLocalId();
136 entry_factory_->CreateUnsyncedItem(folder_id, id_factory_.root(), "folder",
137 true, BOOKMARKS, NULL);
138 // Next five items are children of the folder
139 size_t i;
140 size_t batch_s = 5;
141 for (i = 0; i < batch_s; ++i) {
142 entry_factory_->CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
143 base::StringPrintf("Item %" PRIuS "", i),
144 false, BOOKMARKS, NULL);
145 }
146 // Next five items are children of the root.
147 for (; i < 2*batch_s; ++i) {
148 entry_factory_->CreateUnsyncedItem(
149 id_factory_.NewLocalId(), id_factory_.root(),
150 base::StringPrintf("Item %" PRIuS "", i), false,
151 BOOKMARKS, NULL);
152 }
153
154 KeyParams params = {"localhost", "dummy", "foobar"};
155 cryptographer->AddKey(params);
156 sync_pb::EntitySpecifics specifics;
157 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
158 cryptographer->GetKeys(nigori->mutable_encryption_keybag());
159 nigori->set_encrypt_everything(true);
160 encrypted_types.Put(BOOKMARKS);
161 entry_factory_->CreateUnappliedNewItem(
162 ModelTypeToRootTag(NIGORI), specifics, true);
163 EXPECT_FALSE(cryptographer->has_pending_keys());
164 EXPECT_TRUE(cryptographer->is_ready());
165
166 {
167 // Ensure we have unsynced nodes that aren't properly encrypted.
168 syncable::ReadTransaction trans(FROM_HERE, directory());
169 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
170
171 Syncer::UnsyncedMetaHandles handles;
172 GetUnsyncedEntries(&trans, &handles);
173 EXPECT_EQ(2*batch_s+1, handles.size());
174 }
175
176 ApplyControlDataUpdates(directory());
177
178 EXPECT_FALSE(cryptographer->has_pending_keys());
179 EXPECT_TRUE(cryptographer->is_ready());
180 {
181 syncable::ReadTransaction trans(FROM_HERE, directory());
182
183 // If ProcessUnsyncedChangesForEncryption worked, all our unsynced changes
184 // should be encrypted now.
185 EXPECT_EQ(ModelTypeSet::All(),
186 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
187 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
188
189 Syncer::UnsyncedMetaHandles handles;
190 GetUnsyncedEntries(&trans, &handles);
191 EXPECT_EQ(2*batch_s+1, handles.size());
192 }
193
194 // Simulate another nigori update that doesn't change anything.
195 {
196 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
197 MutableEntry entry(&trans, syncable::GET_TYPE_ROOT, NIGORI);
198 ASSERT_TRUE(entry.good());
199 entry.PutServerVersion(entry_factory_->GetNextRevision());
200 entry.PutIsUnappliedUpdate(true);
201 }
202
203 ApplyControlDataUpdates(directory());
204
205 EXPECT_FALSE(cryptographer->has_pending_keys());
206 EXPECT_TRUE(cryptographer->is_ready());
207 {
208 syncable::ReadTransaction trans(FROM_HERE, directory());
209
210 // All our changes should still be encrypted.
211 EXPECT_EQ(ModelTypeSet::All(),
212 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
213 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
214
215 Syncer::UnsyncedMetaHandles handles;
216 GetUnsyncedEntries(&trans, &handles);
217 EXPECT_EQ(2*batch_s+1, handles.size());
218 }
219 }
220
221 // Create some local unsynced and unencrypted changes. Receive a new nigori
222 // node enabling their encryption but also introducing pending keys. Ensure
223 // we apply the update properly without encrypting the unsynced changes or
224 // breaking.
225 TEST_F(ApplyControlDataUpdatesTest, CannotEncryptUnsyncedChanges) {
226 // Storing the cryptographer separately is bad, but for this test we
227 // know it's safe.
228 Cryptographer* cryptographer;
229 ModelTypeSet encrypted_types;
230 encrypted_types.PutAll(SyncEncryptionHandler::SensitiveTypes());
231 {
232 syncable::ReadTransaction trans(FROM_HERE, directory());
233 cryptographer = directory()->GetCryptographer(&trans);
234 EXPECT_EQ(encrypted_types,
235 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
236
237 // With default encrypted_types, this should be true.
238 EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
239
240 Syncer::UnsyncedMetaHandles handles;
241 GetUnsyncedEntries(&trans, &handles);
242 EXPECT_TRUE(handles.empty());
243 }
244
245 // Create unsynced bookmarks without encryption.
246 // First item is a folder
247 Id folder_id = id_factory_.NewLocalId();
248 entry_factory_->CreateUnsyncedItem(
249 folder_id, id_factory_.root(), "folder", true,
250 BOOKMARKS, NULL);
251 // Next five items are children of the folder
252 size_t i;
253 size_t batch_s = 5;
254 for (i = 0; i < batch_s; ++i) {
255 entry_factory_->CreateUnsyncedItem(id_factory_.NewLocalId(), folder_id,
256 base::StringPrintf("Item %" PRIuS "", i),
257 false, BOOKMARKS, NULL);
258 }
259 // Next five items are children of the root.
260 for (; i < 2*batch_s; ++i) {
261 entry_factory_->CreateUnsyncedItem(
262 id_factory_.NewLocalId(), id_factory_.root(),
263 base::StringPrintf("Item %" PRIuS "", i), false,
264 BOOKMARKS, NULL);
265 }
266
267 // We encrypt with new keys, triggering the local cryptographer to be unready
268 // and unable to decrypt data (once updated).
269 Cryptographer other_cryptographer(cryptographer->encryptor());
270 KeyParams params = {"localhost", "dummy", "foobar"};
271 other_cryptographer.AddKey(params);
272 sync_pb::EntitySpecifics specifics;
273 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
274 other_cryptographer.GetKeys(nigori->mutable_encryption_keybag());
275 nigori->set_encrypt_everything(true);
276 encrypted_types.Put(BOOKMARKS);
277 entry_factory_->CreateUnappliedNewItem(
278 ModelTypeToRootTag(NIGORI), specifics, true);
279 EXPECT_FALSE(cryptographer->has_pending_keys());
280
281 {
282 // Ensure we have unsynced nodes that aren't properly encrypted.
283 syncable::ReadTransaction trans(FROM_HERE, directory());
284 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
285 Syncer::UnsyncedMetaHandles handles;
286 GetUnsyncedEntries(&trans, &handles);
287 EXPECT_EQ(2*batch_s+1, handles.size());
288 }
289
290 ApplyControlDataUpdates(directory());
291
292 EXPECT_FALSE(cryptographer->is_ready());
293 EXPECT_TRUE(cryptographer->has_pending_keys());
294 {
295 syncable::ReadTransaction trans(FROM_HERE, directory());
296
297 // Since we have pending keys, we would have failed to encrypt, but the
298 // cryptographer should be updated.
299 EXPECT_FALSE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
300 EXPECT_EQ(ModelTypeSet::All(),
301 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
302 EXPECT_FALSE(cryptographer->is_ready());
303 EXPECT_TRUE(cryptographer->has_pending_keys());
304
305 Syncer::UnsyncedMetaHandles handles;
306 GetUnsyncedEntries(&trans, &handles);
307 EXPECT_EQ(2*batch_s+1, handles.size());
308 }
309 }
310
311 // Verify we handle a nigori node conflict by merging encryption keys and
312 // types, but preserve the custom passphrase state of the server.
313 // Initial sync ended should be set.
314 TEST_F(ApplyControlDataUpdatesTest,
315 NigoriConflictPendingKeysServerEncryptEverythingCustom) {
316 Cryptographer* cryptographer;
317 ModelTypeSet encrypted_types(SyncEncryptionHandler::SensitiveTypes());
318 KeyParams other_params = {"localhost", "dummy", "foobar"};
319 KeyParams local_params = {"localhost", "dummy", "local"};
320 {
321 syncable::ReadTransaction trans(FROM_HERE, directory());
322 cryptographer = directory()->GetCryptographer(&trans);
323 EXPECT_EQ(directory()->GetNigoriHandler()->GetEncryptedTypes(&trans),
324 encrypted_types);
325 }
326
327 // Set up a temporary cryptographer to generate new keys with.
328 Cryptographer other_cryptographer(cryptographer->encryptor());
329 other_cryptographer.AddKey(other_params);
330
331 // Create server specifics with pending keys, new encrypted types,
332 // and a custom passphrase (unmigrated).
333 sync_pb::EntitySpecifics server_specifics;
334 sync_pb::NigoriSpecifics* server_nigori = server_specifics.mutable_nigori();
335 other_cryptographer.GetKeys(server_nigori->mutable_encryption_keybag());
336 server_nigori->set_encrypt_everything(true);
337 server_nigori->set_keybag_is_frozen(true);
338 int64_t nigori_handle = entry_factory_->CreateUnappliedNewItem(
339 kNigoriTag, server_specifics, true);
340
341 // Initialize the local cryptographer with the local keys.
342 cryptographer->AddKey(local_params);
343 EXPECT_TRUE(cryptographer->is_ready());
344
345 // Set up a local nigori with the local encryption keys and default encrypted
346 // types.
347 sync_pb::EntitySpecifics local_specifics;
348 sync_pb::NigoriSpecifics* local_nigori = local_specifics.mutable_nigori();
349 cryptographer->GetKeys(local_nigori->mutable_encryption_keybag());
350 local_nigori->set_encrypt_everything(false);
351 local_nigori->set_keybag_is_frozen(true);
352 ASSERT_TRUE(entry_factory_->SetLocalSpecificsForItem(
353 nigori_handle, local_specifics));
354 // Apply the update locally so that UpdateFromEncryptedTypes knows what state
355 // to use.
356 {
357 syncable::ReadTransaction trans(FROM_HERE, directory());
358 cryptographer = directory()->GetCryptographer(&trans);
359 directory()->GetNigoriHandler()->ApplyNigoriUpdate(
360 *local_nigori,
361 &trans);
362 }
363
364 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
365 EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
366 ApplyControlDataUpdates(directory());
367 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
368 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
369
370 EXPECT_FALSE(cryptographer->is_ready());
371 EXPECT_TRUE(cryptographer->is_initialized());
372 EXPECT_TRUE(cryptographer->has_pending_keys());
373 EXPECT_TRUE(other_cryptographer.CanDecryptUsingDefaultKey(
374 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
375 nigori().encryption_keybag()));
376 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
377 nigori().keybag_is_frozen());
378 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
379 nigori().encrypt_everything());
380 {
381 syncable::ReadTransaction trans(FROM_HERE, directory());
382 EXPECT_EQ(ModelTypeSet::All(),
383 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
384 }
385 }
386
387 // Verify we handle a nigori node conflict by merging encryption keys and
388 // types, but preserve the custom passphrase state of the server.
389 // Initial sync ended should be set.
390 TEST_F(ApplyControlDataUpdatesTest,
391 NigoriConflictPendingKeysLocalEncryptEverythingCustom) {
392 Cryptographer* cryptographer;
393 ModelTypeSet encrypted_types(SyncEncryptionHandler::SensitiveTypes());
394 KeyParams other_params = {"localhost", "dummy", "foobar"};
395 KeyParams local_params = {"localhost", "dummy", "local"};
396 {
397 syncable::ReadTransaction trans(FROM_HERE, directory());
398 cryptographer = directory()->GetCryptographer(&trans);
399 EXPECT_EQ(encrypted_types,
400 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
401 }
402
403 // Set up a temporary cryptographer to generate new keys with.
404 Cryptographer other_cryptographer(cryptographer->encryptor());
405 other_cryptographer.AddKey(other_params);
406
407 // Create server specifics with pending keys, new encrypted types,
408 // and a custom passphrase (unmigrated).
409 sync_pb::EntitySpecifics server_specifics;
410 sync_pb::NigoriSpecifics* server_nigori = server_specifics.mutable_nigori();
411 other_cryptographer.GetKeys(server_nigori->mutable_encryption_keybag());
412 server_nigori->set_encrypt_everything(false);
413 server_nigori->set_keybag_is_frozen(false);
414 int64_t nigori_handle = entry_factory_->CreateUnappliedNewItem(
415 kNigoriTag, server_specifics, true);
416
417 // Initialize the local cryptographer with the local keys.
418 cryptographer->AddKey(local_params);
419 EXPECT_TRUE(cryptographer->is_ready());
420
421 // Set up a local nigori with the local encryption keys and default encrypted
422 // types.
423 sync_pb::EntitySpecifics local_specifics;
424 sync_pb::NigoriSpecifics* local_nigori = local_specifics.mutable_nigori();
425 cryptographer->GetKeys(local_nigori->mutable_encryption_keybag());
426 local_nigori->set_encrypt_everything(true);
427 local_nigori->set_keybag_is_frozen(true);
428 ASSERT_TRUE(entry_factory_->SetLocalSpecificsForItem(
429 nigori_handle, local_specifics));
430 // Apply the update locally so that UpdateFromEncryptedTypes knows what state
431 // to use.
432 {
433 syncable::ReadTransaction trans(FROM_HERE, directory());
434 cryptographer = directory()->GetCryptographer(&trans);
435 directory()->GetNigoriHandler()->ApplyNigoriUpdate(
436 *local_nigori,
437 &trans);
438 }
439
440 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
441 EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
442 ApplyControlDataUpdates(directory());
443 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
444 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
445
446 EXPECT_FALSE(cryptographer->is_ready());
447 EXPECT_TRUE(cryptographer->is_initialized());
448 EXPECT_TRUE(cryptographer->has_pending_keys());
449 EXPECT_TRUE(other_cryptographer.CanDecryptUsingDefaultKey(
450 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
451 nigori().encryption_keybag()));
452 EXPECT_FALSE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
453 nigori().keybag_is_frozen());
454 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
455 nigori().encrypt_everything());
456 {
457 syncable::ReadTransaction trans(FROM_HERE, directory());
458 EXPECT_EQ(ModelTypeSet::All(),
459 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
460 }
461 }
462
463 // If the conflicting nigori has a subset of the local keys, the conflict
464 // resolution should preserve the full local keys. Initial sync ended should be
465 // set.
466 TEST_F(ApplyControlDataUpdatesTest,
467 NigoriConflictOldKeys) {
468 Cryptographer* cryptographer;
469 ModelTypeSet encrypted_types(SyncEncryptionHandler::SensitiveTypes());
470 KeyParams old_params = {"localhost", "dummy", "old"};
471 KeyParams new_params = {"localhost", "dummy", "new"};
472 {
473 syncable::ReadTransaction trans(FROM_HERE, directory());
474 cryptographer = directory()->GetCryptographer(&trans);
475 EXPECT_EQ(encrypted_types,
476 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
477 }
478
479 // Set up the cryptographer with old keys
480 cryptographer->AddKey(old_params);
481
482 // Create server specifics with old keys and new encrypted types.
483 sync_pb::EntitySpecifics server_specifics;
484 sync_pb::NigoriSpecifics* server_nigori = server_specifics.mutable_nigori();
485 cryptographer->GetKeys(server_nigori->mutable_encryption_keybag());
486 server_nigori->set_encrypt_everything(true);
487 int64_t nigori_handle = entry_factory_->CreateUnappliedNewItem(
488 kNigoriTag, server_specifics, true);
489
490 // Add the new keys to the cryptogrpaher
491 cryptographer->AddKey(new_params);
492 EXPECT_TRUE(cryptographer->is_ready());
493
494 // Set up a local nigori with the superset of keys.
495 sync_pb::EntitySpecifics local_specifics;
496 sync_pb::NigoriSpecifics* local_nigori = local_specifics.mutable_nigori();
497 cryptographer->GetKeys(local_nigori->mutable_encryption_keybag());
498 local_nigori->set_encrypt_everything(false);
499 ASSERT_TRUE(entry_factory_->SetLocalSpecificsForItem(
500 nigori_handle, local_specifics));
501 // Apply the update locally so that UpdateFromEncryptedTypes knows what state
502 // to use.
503 {
504 syncable::ReadTransaction trans(FROM_HERE, directory());
505 cryptographer = directory()->GetCryptographer(&trans);
506 directory()->GetNigoriHandler()->ApplyNigoriUpdate(
507 *local_nigori,
508 &trans);
509 }
510
511 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
512 EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
513 ApplyControlDataUpdates(directory());
514 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
515 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
516
517 EXPECT_TRUE(cryptographer->is_ready());
518 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
519 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
520 nigori().encryption_keybag()));
521 EXPECT_FALSE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
522 nigori().keybag_is_frozen());
523 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
524 nigori().encrypt_everything());
525 {
526 syncable::ReadTransaction trans(FROM_HERE, directory());
527 EXPECT_EQ(ModelTypeSet::All(),
528 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
529 }
530 }
531
532 // If both nigoris are migrated, but we also set a custom passphrase locally,
533 // the local nigori should be preserved.
534 TEST_F(ApplyControlDataUpdatesTest,
535 NigoriConflictBothMigratedLocalCustom) {
536 Cryptographer* cryptographer;
537 ModelTypeSet encrypted_types(SyncEncryptionHandler::SensitiveTypes());
538 KeyParams old_params = {"localhost", "dummy", "old"};
539 KeyParams new_params = {"localhost", "dummy", "new"};
540 {
541 syncable::ReadTransaction trans(FROM_HERE, directory());
542 cryptographer = directory()->GetCryptographer(&trans);
543 EXPECT_EQ(encrypted_types,
544 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
545 }
546
547 // Set up the cryptographer with new keys
548 Cryptographer other_cryptographer(cryptographer->encryptor());
549 other_cryptographer.AddKey(old_params);
550
551 // Create server specifics with a migrated keystore passphrase type.
552 sync_pb::EntitySpecifics server_specifics;
553 sync_pb::NigoriSpecifics* server_nigori = server_specifics.mutable_nigori();
554 other_cryptographer.GetKeys(server_nigori->mutable_encryption_keybag());
555 server_nigori->set_encrypt_everything(false);
556 server_nigori->set_keybag_is_frozen(true);
557 server_nigori->set_passphrase_type(
558 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
559 server_nigori->mutable_keystore_decryptor_token();
560 int64_t nigori_handle = entry_factory_->CreateUnappliedNewItem(
561 kNigoriTag, server_specifics, true);
562
563 // Add the new keys to the cryptographer.
564 cryptographer->AddKey(old_params);
565 cryptographer->AddKey(new_params);
566 EXPECT_TRUE(cryptographer->is_ready());
567
568 // Set up a local nigori with a migrated custom passphrase type
569 sync_pb::EntitySpecifics local_specifics;
570 sync_pb::NigoriSpecifics* local_nigori = local_specifics.mutable_nigori();
571 cryptographer->GetKeys(local_nigori->mutable_encryption_keybag());
572 local_nigori->set_encrypt_everything(true);
573 local_nigori->set_keybag_is_frozen(true);
574 local_nigori->set_passphrase_type(
575 sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE);
576 ASSERT_TRUE(entry_factory_->SetLocalSpecificsForItem(
577 nigori_handle, local_specifics));
578 // Apply the update locally so that UpdateFromEncryptedTypes knows what state
579 // to use.
580 {
581 syncable::ReadTransaction trans(FROM_HERE, directory());
582 cryptographer = directory()->GetCryptographer(&trans);
583 directory()->GetNigoriHandler()->ApplyNigoriUpdate(
584 *local_nigori,
585 &trans);
586 }
587
588 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
589 EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
590 ApplyControlDataUpdates(directory());
591 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
592 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
593
594 EXPECT_TRUE(cryptographer->is_ready());
595 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
596 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
597 nigori().encryption_keybag()));
598 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
599 nigori().keybag_is_frozen());
600 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
601 nigori().encrypt_everything());
602 EXPECT_EQ(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE,
603 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
604 nigori().passphrase_type());
605 {
606 syncable::ReadTransaction trans(FROM_HERE, directory());
607 EXPECT_EQ(ModelTypeSet::All(),
608 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
609 }
610 }
611
612 // If both nigoris are migrated, but a custom passphrase with a new key was
613 // set remotely, the remote nigori should be preserved.
614 TEST_F(ApplyControlDataUpdatesTest,
615 NigoriConflictBothMigratedServerCustom) {
616 Cryptographer* cryptographer;
617 ModelTypeSet encrypted_types(SyncEncryptionHandler::SensitiveTypes());
618 KeyParams old_params = {"localhost", "dummy", "old"};
619 KeyParams new_params = {"localhost", "dummy", "new"};
620 {
621 syncable::ReadTransaction trans(FROM_HERE, directory());
622 cryptographer = directory()->GetCryptographer(&trans);
623 EXPECT_EQ(encrypted_types,
624 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
625 }
626
627 // Set up the cryptographer with both new keys and old keys.
628 Cryptographer other_cryptographer(cryptographer->encryptor());
629 other_cryptographer.AddKey(old_params);
630 other_cryptographer.AddKey(new_params);
631
632 // Create server specifics with a migrated custom passphrase type.
633 sync_pb::EntitySpecifics server_specifics;
634 sync_pb::NigoriSpecifics* server_nigori = server_specifics.mutable_nigori();
635 other_cryptographer.GetKeys(server_nigori->mutable_encryption_keybag());
636 server_nigori->set_encrypt_everything(true);
637 server_nigori->set_keybag_is_frozen(true);
638 server_nigori->set_passphrase_type(
639 sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE);
640 int64_t nigori_handle = entry_factory_->CreateUnappliedNewItem(
641 kNigoriTag, server_specifics, true);
642
643 // Add the old keys to the cryptographer.
644 cryptographer->AddKey(old_params);
645 EXPECT_TRUE(cryptographer->is_ready());
646
647 // Set up a local nigori with a migrated keystore passphrase type
648 sync_pb::EntitySpecifics local_specifics;
649 sync_pb::NigoriSpecifics* local_nigori = local_specifics.mutable_nigori();
650 cryptographer->GetKeys(local_nigori->mutable_encryption_keybag());
651 local_nigori->set_encrypt_everything(false);
652 local_nigori->set_keybag_is_frozen(true);
653 local_nigori->set_passphrase_type(
654 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
655 server_nigori->mutable_keystore_decryptor_token();
656 ASSERT_TRUE(entry_factory_->SetLocalSpecificsForItem(
657 nigori_handle, local_specifics));
658 // Apply the update locally so that UpdateFromEncryptedTypes knows what state
659 // to use.
660 {
661 syncable::ReadTransaction trans(FROM_HERE, directory());
662 cryptographer = directory()->GetCryptographer(&trans);
663 directory()->GetNigoriHandler()->ApplyNigoriUpdate(
664 *local_nigori,
665 &trans);
666 }
667
668 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
669 EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
670 ApplyControlDataUpdates(directory());
671 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
672 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
673
674 EXPECT_TRUE(cryptographer->is_initialized());
675 EXPECT_TRUE(cryptographer->has_pending_keys());
676 EXPECT_TRUE(other_cryptographer.CanDecryptUsingDefaultKey(
677 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
678 nigori().encryption_keybag()));
679 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
680 nigori().keybag_is_frozen());
681 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
682 nigori().encrypt_everything());
683 EXPECT_EQ(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE,
684 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
685 nigori().passphrase_type());
686 {
687 syncable::ReadTransaction trans(FROM_HERE, directory());
688 EXPECT_EQ(ModelTypeSet::All(),
689 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
690 }
691 }
692
693 // If the local nigori is migrated but the server is not, preserve the local
694 // nigori.
695 TEST_F(ApplyControlDataUpdatesTest,
696 NigoriConflictLocalMigrated) {
697 Cryptographer* cryptographer;
698 ModelTypeSet encrypted_types(SyncEncryptionHandler::SensitiveTypes());
699 KeyParams old_params = {"localhost", "dummy", "old"};
700 KeyParams new_params = {"localhost", "dummy", "new"};
701 {
702 syncable::ReadTransaction trans(FROM_HERE, directory());
703 cryptographer = directory()->GetCryptographer(&trans);
704 EXPECT_EQ(encrypted_types,
705 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
706 }
707
708 // Set up the cryptographer with both new keys and old keys.
709 Cryptographer other_cryptographer(cryptographer->encryptor());
710 other_cryptographer.AddKey(old_params);
711
712 // Create server specifics with an unmigrated implicit passphrase type.
713 sync_pb::EntitySpecifics server_specifics;
714 sync_pb::NigoriSpecifics* server_nigori = server_specifics.mutable_nigori();
715 other_cryptographer.GetKeys(server_nigori->mutable_encryption_keybag());
716 server_nigori->set_encrypt_everything(true);
717 server_nigori->set_keybag_is_frozen(false);
718 int64_t nigori_handle = entry_factory_->CreateUnappliedNewItem(
719 kNigoriTag, server_specifics, true);
720
721 // Add the old keys to the cryptographer.
722 cryptographer->AddKey(old_params);
723 cryptographer->AddKey(new_params);
724 EXPECT_TRUE(cryptographer->is_ready());
725
726 // Set up a local nigori with a migrated custom passphrase type
727 sync_pb::EntitySpecifics local_specifics;
728 sync_pb::NigoriSpecifics* local_nigori = local_specifics.mutable_nigori();
729 cryptographer->GetKeys(local_nigori->mutable_encryption_keybag());
730 local_nigori->set_encrypt_everything(true);
731 local_nigori->set_keybag_is_frozen(true);
732 local_nigori->set_passphrase_type(
733 sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE);
734 ASSERT_TRUE(entry_factory_->SetLocalSpecificsForItem(
735 nigori_handle, local_specifics));
736 // Apply the update locally so that UpdateFromEncryptedTypes knows what state
737 // to use.
738 {
739 syncable::ReadTransaction trans(FROM_HERE, directory());
740 cryptographer = directory()->GetCryptographer(&trans);
741 directory()->GetNigoriHandler()->ApplyNigoriUpdate(
742 *local_nigori,
743 &trans);
744 }
745
746 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
747 EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
748 ApplyControlDataUpdates(directory());
749 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
750 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
751
752 EXPECT_TRUE(cryptographer->is_ready());
753 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey(
754 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
755 nigori().encryption_keybag()));
756 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
757 nigori().keybag_is_frozen());
758 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
759 nigori().encrypt_everything());
760 EXPECT_EQ(sync_pb::NigoriSpecifics::CUSTOM_PASSPHRASE,
761 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
762 nigori().passphrase_type());
763 {
764 syncable::ReadTransaction trans(FROM_HERE, directory());
765 EXPECT_EQ(ModelTypeSet::All(),
766 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
767 }
768 }
769
770 // If the server nigori is migrated but the local is not, preserve the server
771 // nigori.
772 TEST_F(ApplyControlDataUpdatesTest,
773 NigoriConflictServerMigrated) {
774 Cryptographer* cryptographer;
775 ModelTypeSet encrypted_types(SyncEncryptionHandler::SensitiveTypes());
776 KeyParams old_params = {"localhost", "dummy", "old"};
777 KeyParams new_params = {"localhost", "dummy", "new"};
778 {
779 syncable::ReadTransaction trans(FROM_HERE, directory());
780 cryptographer = directory()->GetCryptographer(&trans);
781 EXPECT_EQ(encrypted_types,
782 directory()->GetNigoriHandler()->GetEncryptedTypes(&trans));
783 }
784
785 // Set up the cryptographer with both new keys and old keys.
786 Cryptographer other_cryptographer(cryptographer->encryptor());
787 other_cryptographer.AddKey(old_params);
788
789 // Create server specifics with an migrated keystore passphrase type.
790 sync_pb::EntitySpecifics server_specifics;
791 sync_pb::NigoriSpecifics* server_nigori = server_specifics.mutable_nigori();
792 other_cryptographer.GetKeys(server_nigori->mutable_encryption_keybag());
793 server_nigori->set_encrypt_everything(false);
794 server_nigori->set_keybag_is_frozen(true);
795 server_nigori->set_passphrase_type(
796 sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE);
797 server_nigori->mutable_keystore_decryptor_token();
798 int64_t nigori_handle = entry_factory_->CreateUnappliedNewItem(
799 kNigoriTag, server_specifics, true);
800
801 // Add the old keys to the cryptographer.
802 cryptographer->AddKey(old_params);
803 cryptographer->AddKey(new_params);
804 EXPECT_TRUE(cryptographer->is_ready());
805
806 // Set up a local nigori with a migrated custom passphrase type
807 sync_pb::EntitySpecifics local_specifics;
808 sync_pb::NigoriSpecifics* local_nigori = local_specifics.mutable_nigori();
809 cryptographer->GetKeys(local_nigori->mutable_encryption_keybag());
810 local_nigori->set_encrypt_everything(false);
811 local_nigori->set_keybag_is_frozen(false);
812 ASSERT_TRUE(entry_factory_->SetLocalSpecificsForItem(
813 nigori_handle, local_specifics));
814 // Apply the update locally so that UpdateFromEncryptedTypes knows what state
815 // to use.
816 {
817 syncable::ReadTransaction trans(FROM_HERE, directory());
818 cryptographer = directory()->GetCryptographer(&trans);
819 directory()->GetNigoriHandler()->ApplyNigoriUpdate(
820 *local_nigori,
821 &trans);
822 }
823
824 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
825 EXPECT_TRUE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
826 ApplyControlDataUpdates(directory());
827 EXPECT_TRUE(entry_factory_->GetIsUnsyncedForItem(nigori_handle));
828 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(nigori_handle));
829
830 EXPECT_TRUE(cryptographer->is_ready());
831 // Note: we didn't overwrite the encryption keybag with the local keys. The
832 // sync encryption handler will do that when it detects that the new
833 // keybag is out of date (and update the keystore bootstrap if necessary).
834 EXPECT_FALSE(cryptographer->CanDecryptUsingDefaultKey(
835 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
836 nigori().encryption_keybag()));
837 EXPECT_TRUE(cryptographer->CanDecrypt(
838 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
839 nigori().encryption_keybag()));
840 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
841 nigori().keybag_is_frozen());
842 EXPECT_TRUE(entry_factory_->GetLocalSpecificsForItem(nigori_handle).
843 nigori().has_keystore_decryptor_token());
844 EXPECT_EQ(sync_pb::NigoriSpecifics::KEYSTORE_PASSPHRASE,
845 entry_factory_->GetLocalSpecificsForItem(nigori_handle).
846 nigori().passphrase_type());
847 {
848 syncable::ReadTransaction trans(FROM_HERE, directory());
849 }
850 }
851
852 // Check that we can apply a simple control datatype node successfully.
853 TEST_F(ApplyControlDataUpdatesTest, ControlApply) {
854 std::string experiment_id = "experiment";
855 sync_pb::EntitySpecifics specifics;
856 specifics.mutable_experiments()->mutable_keystore_encryption()->
857 set_enabled(true);
858 int64_t experiment_handle =
859 entry_factory_->CreateUnappliedNewItem(experiment_id, specifics, false);
860 ApplyControlDataUpdates(directory());
861
862 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(experiment_handle));
863 EXPECT_TRUE(
864 entry_factory_->GetLocalSpecificsForItem(experiment_handle).
865 experiments().keystore_encryption().enabled());
866 }
867
868 // Verify that we apply top level folders before their children.
869 TEST_F(ApplyControlDataUpdatesTest, ControlApplyParentBeforeChild) {
870 std::string parent_id = "parent";
871 std::string experiment_id = "experiment";
872 sync_pb::EntitySpecifics specifics;
873 specifics.mutable_experiments()->mutable_keystore_encryption()->
874 set_enabled(true);
875 int64_t experiment_handle = entry_factory_->CreateUnappliedNewItemWithParent(
876 experiment_id, specifics, parent_id);
877 int64_t parent_handle =
878 entry_factory_->CreateUnappliedNewItem(parent_id, specifics, true);
879 ApplyControlDataUpdates(directory());
880
881 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(parent_handle));
882 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(experiment_handle));
883 EXPECT_TRUE(
884 entry_factory_->GetLocalSpecificsForItem(experiment_handle).
885 experiments().keystore_encryption().enabled());
886 }
887
888 // Verify that we handle control datatype conflicts by preserving the server
889 // data.
890 TEST_F(ApplyControlDataUpdatesTest, ControlConflict) {
891 std::string experiment_id = "experiment";
892 sync_pb::EntitySpecifics local_specifics, server_specifics;
893 server_specifics.mutable_experiments()->mutable_keystore_encryption()->
894 set_enabled(true);
895 local_specifics.mutable_experiments()->mutable_keystore_encryption()->
896 set_enabled(false);
897 int64_t experiment_handle =
898 entry_factory_->CreateSyncedItem(experiment_id, EXPERIMENTS, false);
899 entry_factory_->SetServerSpecificsForItem(experiment_handle,
900 server_specifics);
901 entry_factory_->SetLocalSpecificsForItem(experiment_handle,
902 local_specifics);
903 ApplyControlDataUpdates(directory());
904
905 EXPECT_FALSE(entry_factory_->GetIsUnappliedForItem(experiment_handle));
906 EXPECT_TRUE(
907 entry_factory_->GetLocalSpecificsForItem(experiment_handle).
908 experiments().keystore_encryption().enabled());
909 }
910
911 // Check that applying a EXPERIMENTS update marks the datatype as downloaded.
912 TEST_F(ApplyControlDataUpdatesTest, ExperimentsApplyMarksDownloadCompleted) {
913 EXPECT_FALSE(directory()->InitialSyncEndedForType(EXPERIMENTS));
914
915 // Create root node for EXPERIMENTS datatype
916 {
917 syncable::WriteTransaction trans(FROM_HERE, UNITTEST, directory());
918 syncable::ModelNeutralMutableEntry entry(
919 &trans, syncable::CREATE_NEW_TYPE_ROOT, EXPERIMENTS);
920 ASSERT_TRUE(entry.good());
921 entry.PutServerIsDir(true);
922 entry.PutUniqueServerTag(ModelTypeToRootTag(EXPERIMENTS));
923 }
924
925 // Initial sync isn't marked as ended for EXPERIMENTS even though the
926 // root folder exists.
927 EXPECT_FALSE(directory()->InitialSyncEndedForType(EXPERIMENTS));
928
929 std::string experiment_id = "experiment";
930 sync_pb::EntitySpecifics specifics;
931 specifics.mutable_experiments()->mutable_keystore_encryption()->set_enabled(
932 true);
933 entry_factory_->CreateUnappliedNewItem(experiment_id, specifics, false);
934
935 ApplyControlDataUpdates(directory());
936
937 // After applying the updates EXPERIMENTS should be marked as having its
938 // initial sync completed.
939 EXPECT_TRUE(directory()->InitialSyncEndedForType(EXPERIMENTS));
940 // Verify that there is no side effect on another control type.
941 EXPECT_FALSE(directory()->InitialSyncEndedForType(NIGORI));
942 }
943
944 // Check that applying a NIGORI update marks the datatype as downloaded.
945 TEST_F(ApplyControlDataUpdatesTest, NigoriApplyMarksDownloadCompleted) {
946 EXPECT_FALSE(directory()->InitialSyncEndedForType(NIGORI));
947
948 Cryptographer* cryptographer;
949
950 {
951 syncable::ReadTransaction trans(FROM_HERE, directory());
952 cryptographer = directory()->GetCryptographer(&trans);
953 }
954
955 KeyParams params = {"localhost", "dummy", "foobar"};
956 cryptographer->AddKey(params);
957 sync_pb::EntitySpecifics specifics;
958 sync_pb::NigoriSpecifics* nigori = specifics.mutable_nigori();
959 cryptographer->GetKeys(nigori->mutable_encryption_keybag());
960 nigori->set_encrypt_everything(true);
961
962 entry_factory_->CreateUnappliedNewItem(ModelTypeToRootTag(NIGORI), specifics,
963 true);
964
965 ApplyControlDataUpdates(directory());
966
967 // After applying the updates NIGORI should be marked as having its
968 // initial sync completed.
969 EXPECT_TRUE(directory()->InitialSyncEndedForType(NIGORI));
970 // Verify that there is no side effect on another control type.
971 EXPECT_FALSE(directory()->InitialSyncEndedForType(EXPERIMENTS));
972 }
973
974 } // namespace syncer
OLDNEW
« no previous file with comments | « sync/engine/apply_control_data_updates.cc ('k') | sync/engine/backoff_delay_provider.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698