| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 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/internal_api/public/sync_manager.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/base64.h" | |
| 10 #include "base/bind.h" | |
| 11 #include "base/callback.h" | |
| 12 #include "base/command_line.h" | |
| 13 #include "base/compiler_specific.h" | |
| 14 #include "base/json/json_writer.h" | |
| 15 #include "base/memory/ref_counted.h" | |
| 16 #include "base/metrics/histogram.h" | |
| 17 #include "base/observer_list.h" | |
| 18 #include "base/string_number_conversions.h" | |
| 19 #include "base/values.h" | |
| 20 #include "net/base/network_change_notifier.h" | |
| 21 #include "sync/engine/all_status.h" | |
| 22 #include "sync/engine/net/server_connection_manager.h" | |
| 23 #include "sync/engine/sync_scheduler.h" | |
| 24 #include "sync/engine/syncer_types.h" | |
| 25 #include "sync/engine/throttled_data_type_tracker.h" | |
| 26 #include "sync/internal_api/change_reorder_buffer.h" | |
| 27 #include "sync/internal_api/debug_info_event_listener.h" | |
| 28 #include "sync/internal_api/js_mutation_event_observer.h" | |
| 29 #include "sync/internal_api/js_sync_manager_observer.h" | |
| 30 #include "sync/internal_api/public/base/model_type.h" | |
| 31 #include "sync/internal_api/public/base/model_type_payload_map.h" | |
| 32 #include "sync/internal_api/public/base_node.h" | |
| 33 #include "sync/internal_api/public/configure_reason.h" | |
| 34 #include "sync/internal_api/public/engine/polling_constants.h" | |
| 35 #include "sync/internal_api/public/read_node.h" | |
| 36 #include "sync/internal_api/public/read_transaction.h" | |
| 37 #include "sync/internal_api/public/user_share.h" | |
| 38 #include "sync/internal_api/public/util/experiments.h" | |
| 39 #include "sync/internal_api/public/write_node.h" | |
| 40 #include "sync/internal_api/public/write_transaction.h" | |
| 41 #include "sync/internal_api/syncapi_internal.h" | |
| 42 #include "sync/internal_api/syncapi_server_connection_manager.h" | |
| 43 #include "sync/js/js_arg_list.h" | |
| 44 #include "sync/js/js_backend.h" | |
| 45 #include "sync/js/js_event_details.h" | |
| 46 #include "sync/js/js_event_handler.h" | |
| 47 #include "sync/js/js_reply_handler.h" | |
| 48 #include "sync/notifier/notifications_disabled_reason.h" | |
| 49 #include "sync/notifier/sync_notifier.h" | |
| 50 #include "sync/notifier/sync_notifier_observer.h" | |
| 51 #include "sync/protocol/encryption.pb.h" | |
| 52 #include "sync/protocol/proto_value_conversions.h" | |
| 53 #include "sync/protocol/sync.pb.h" | |
| 54 #include "sync/syncable/directory.h" | |
| 55 #include "sync/syncable/directory_change_delegate.h" | |
| 56 #include "sync/syncable/entry.h" | |
| 57 #include "sync/syncable/in_memory_directory_backing_store.h" | |
| 58 #include "sync/syncable/nigori_util.h" | |
| 59 #include "sync/syncable/on_disk_directory_backing_store.h" | |
| 60 #include "sync/util/cryptographer.h" | |
| 61 #include "sync/util/get_session_name.h" | |
| 62 #include "sync/util/time.h" | |
| 63 | |
| 64 using base::TimeDelta; | |
| 65 using sync_pb::GetUpdatesCallerInfo; | |
| 66 | |
| 67 namespace { | |
| 68 | |
| 69 // Delays for syncer nudges. | |
| 70 static const int kSyncRefreshDelayMsec = 500; | |
| 71 static const int kSyncSchedulerDelayMsec = 250; | |
| 72 | |
| 73 GetUpdatesCallerInfo::GetUpdatesSource GetSourceFromReason( | |
| 74 syncer::ConfigureReason reason) { | |
| 75 switch (reason) { | |
| 76 case syncer::CONFIGURE_REASON_RECONFIGURATION: | |
| 77 return GetUpdatesCallerInfo::RECONFIGURATION; | |
| 78 case syncer::CONFIGURE_REASON_MIGRATION: | |
| 79 return GetUpdatesCallerInfo::MIGRATION; | |
| 80 case syncer::CONFIGURE_REASON_NEW_CLIENT: | |
| 81 return GetUpdatesCallerInfo::NEW_CLIENT; | |
| 82 case syncer::CONFIGURE_REASON_NEWLY_ENABLED_DATA_TYPE: | |
| 83 return GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE; | |
| 84 default: | |
| 85 NOTREACHED(); | |
| 86 } | |
| 87 | |
| 88 return GetUpdatesCallerInfo::UNKNOWN; | |
| 89 } | |
| 90 | |
| 91 // The maximum number of times we will automatically overwrite the nigori node | |
| 92 // because the encryption keys don't match (per chrome instantiation). | |
| 93 static const int kNigoriOverwriteLimit = 10; | |
| 94 | |
| 95 } // namespace | |
| 96 | |
| 97 namespace syncer { | |
| 98 | |
| 99 using sessions::SyncSessionContext; | |
| 100 using syncable::ImmutableWriteTransactionInfo; | |
| 101 using syncable::SPECIFICS; | |
| 102 | |
| 103 const int SyncManager::kDefaultNudgeDelayMilliseconds = 200; | |
| 104 const int SyncManager::kPreferencesNudgeDelayMilliseconds = 2000; | |
| 105 | |
| 106 // Maximum count and size for traffic recorder. | |
| 107 const unsigned int kMaxMessagesToRecord = 10; | |
| 108 const unsigned int kMaxMessageSizeToRecord = 5 * 1024; | |
| 109 | |
| 110 ////////////////////////////////////////////////////////////////////////// | |
| 111 // SyncManager's implementation: SyncManager::SyncInternal | |
| 112 class SyncManager::SyncInternal | |
| 113 : public net::NetworkChangeNotifier::IPAddressObserver, | |
| 114 public syncer::Cryptographer::Observer, | |
| 115 public syncer::SyncNotifierObserver, | |
| 116 public JsBackend, | |
| 117 public SyncEngineEventListener, | |
| 118 public ServerConnectionEventListener, | |
| 119 public syncable::DirectoryChangeDelegate { | |
| 120 public: | |
| 121 explicit SyncInternal(const std::string& name) | |
| 122 : name_(name), | |
| 123 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), | |
| 124 change_delegate_(NULL), | |
| 125 initialized_(false), | |
| 126 testing_mode_(NON_TEST), | |
| 127 observing_ip_address_changes_(false), | |
| 128 throttled_data_type_tracker_(&allstatus_), | |
| 129 traffic_recorder_(kMaxMessagesToRecord, kMaxMessageSizeToRecord), | |
| 130 encryptor_(NULL), | |
| 131 unrecoverable_error_handler_(NULL), | |
| 132 report_unrecoverable_error_function_(NULL), | |
| 133 created_on_loop_(MessageLoop::current()), | |
| 134 nigori_overwrite_count_(0) { | |
| 135 // Pre-fill |notification_info_map_|. | |
| 136 for (int i = syncer::FIRST_REAL_MODEL_TYPE; | |
| 137 i < syncer::MODEL_TYPE_COUNT; ++i) { | |
| 138 notification_info_map_.insert( | |
| 139 std::make_pair(syncer::ModelTypeFromInt(i), NotificationInfo())); | |
| 140 } | |
| 141 | |
| 142 // Bind message handlers. | |
| 143 BindJsMessageHandler( | |
| 144 "getNotificationState", | |
| 145 &SyncManager::SyncInternal::GetNotificationState); | |
| 146 BindJsMessageHandler( | |
| 147 "getNotificationInfo", | |
| 148 &SyncManager::SyncInternal::GetNotificationInfo); | |
| 149 BindJsMessageHandler( | |
| 150 "getRootNodeDetails", | |
| 151 &SyncManager::SyncInternal::GetRootNodeDetails); | |
| 152 BindJsMessageHandler( | |
| 153 "getNodeSummariesById", | |
| 154 &SyncManager::SyncInternal::GetNodeSummariesById); | |
| 155 BindJsMessageHandler( | |
| 156 "getNodeDetailsById", | |
| 157 &SyncManager::SyncInternal::GetNodeDetailsById); | |
| 158 BindJsMessageHandler( | |
| 159 "getAllNodes", | |
| 160 &SyncManager::SyncInternal::GetAllNodes); | |
| 161 BindJsMessageHandler( | |
| 162 "getChildNodeIds", | |
| 163 &SyncManager::SyncInternal::GetChildNodeIds); | |
| 164 BindJsMessageHandler( | |
| 165 "getClientServerTraffic", | |
| 166 &SyncManager::SyncInternal::GetClientServerTraffic); | |
| 167 } | |
| 168 | |
| 169 virtual ~SyncInternal() { | |
| 170 CHECK(!initialized_); | |
| 171 } | |
| 172 | |
| 173 bool Init(const FilePath& database_location, | |
| 174 const WeakHandle<JsEventHandler>& event_handler, | |
| 175 const std::string& sync_server_and_path, | |
| 176 int port, | |
| 177 bool use_ssl, | |
| 178 const scoped_refptr<base::TaskRunner>& blocking_task_runner, | |
| 179 HttpPostProviderFactory* post_factory, | |
| 180 const syncer::ModelSafeRoutingInfo& model_safe_routing_info, | |
| 181 const std::vector<syncer::ModelSafeWorker*>& workers, | |
| 182 syncer::ExtensionsActivityMonitor* | |
| 183 extensions_activity_monitor, | |
| 184 ChangeDelegate* change_delegate, | |
| 185 const SyncCredentials& credentials, | |
| 186 syncer::SyncNotifier* sync_notifier, | |
| 187 const std::string& restored_key_for_bootstrapping, | |
| 188 TestingMode testing_mode, | |
| 189 Encryptor* encryptor, | |
| 190 UnrecoverableErrorHandler* unrecoverable_error_handler, | |
| 191 ReportUnrecoverableErrorFunction | |
| 192 report_unrecoverable_error_function); | |
| 193 | |
| 194 // Sign into sync with given credentials. | |
| 195 // We do not verify the tokens given. After this call, the tokens are set | |
| 196 // and the sync DB is open. True if successful, false if something | |
| 197 // went wrong. | |
| 198 bool SignIn(const SyncCredentials& credentials); | |
| 199 | |
| 200 // Purge from the directory those types with non-empty progress markers | |
| 201 // but without initial synced ended set. | |
| 202 // Returns false if an error occurred, true otherwise. | |
| 203 bool PurgePartiallySyncedTypes(); | |
| 204 | |
| 205 // Update tokens that we're using in Sync. Email must stay the same. | |
| 206 void UpdateCredentials(const SyncCredentials& credentials); | |
| 207 | |
| 208 // Called when the user disables or enables a sync type. | |
| 209 void UpdateEnabledTypes(const ModelTypeSet& enabled_types); | |
| 210 | |
| 211 // Tell the sync engine to start the syncing process. | |
| 212 void StartSyncingNormally( | |
| 213 const syncer::ModelSafeRoutingInfo& routing_info); | |
| 214 | |
| 215 // Whether or not the Nigori node is encrypted using an explicit passphrase. | |
| 216 bool IsUsingExplicitPassphrase(); | |
| 217 | |
| 218 // Update the Cryptographer from the current nigori node and write back any | |
| 219 // necessary changes to the nigori node. We also detect missing encryption | |
| 220 // keys and write them into the nigori node. | |
| 221 // Also updates or adds the device information into the nigori node. | |
| 222 // Note: opens a transaction and can trigger an ON_PASSPHRASE_REQUIRED, so | |
| 223 // should only be called after syncapi is fully initialized. | |
| 224 // Calls the callback argument with true if cryptographer is ready, false | |
| 225 // otherwise. | |
| 226 void UpdateCryptographerAndNigori( | |
| 227 const std::string& chrome_version, | |
| 228 const base::Closure& done_callback); | |
| 229 | |
| 230 // Stores the current set of encryption keys (if the cryptographer is ready) | |
| 231 // and encrypted types into the nigori node. | |
| 232 void UpdateNigoriEncryptionState(Cryptographer* cryptographer, | |
| 233 WriteNode* nigori_node); | |
| 234 | |
| 235 // Updates the nigori node with any new encrypted types and then | |
| 236 // encrypts the nodes for those new data types as well as other | |
| 237 // nodes that should be encrypted but aren't. Triggers | |
| 238 // OnPassphraseRequired if the cryptographer isn't ready. | |
| 239 void RefreshEncryption(); | |
| 240 | |
| 241 // Re-encrypts the encrypted data types using the passed passphrase, and sets | |
| 242 // a flag in the nigori node specifying whether the current passphrase is | |
| 243 // explicit (custom passphrase) or non-explicit (GAIA). If the existing | |
| 244 // encryption passphrase is "explicit", the data cannot be re-encrypted and | |
| 245 // SetEncryptionPassphrase will do nothing. | |
| 246 // If !is_explicit and there are pending keys, we will attempt to decrypt them | |
| 247 // using this passphrase. If this fails, we will save this encryption key to | |
| 248 // be applied later after the pending keys are resolved. | |
| 249 // Calls FinishSetPassphrase at the end, which notifies observers of the | |
| 250 // result of the set passphrase operation, updates the nigori node, and does | |
| 251 // re-encryption. | |
| 252 void SetEncryptionPassphrase(const std::string& passphrase, bool is_explicit); | |
| 253 | |
| 254 // Provides a passphrase for decrypting the user's existing sync data. Calls | |
| 255 // FinishSetPassphrase at the end, which notifies observers of the result of | |
| 256 // the set passphrase operation, updates the nigori node, and does | |
| 257 // re-encryption. | |
| 258 void SetDecryptionPassphrase(const std::string& passphrase); | |
| 259 | |
| 260 // The final step of SetEncryptionPassphrase and SetDecryptionPassphrase that | |
| 261 // notifies observers of the result of the set passphrase operation, updates | |
| 262 // the nigori node, and does re-encryption. | |
| 263 // |success|: true if the operation was successful and false otherwise. If | |
| 264 // success == false, we send an OnPassphraseRequired notification. | |
| 265 // |bootstrap_token|: used to inform observers if the cryptographer's | |
| 266 // bootstrap token was updated. | |
| 267 // |is_explicit|: used to differentiate between a custom passphrase (true) and | |
| 268 // a GAIA passphrase that is implicitly used for encryption | |
| 269 // (false). | |
| 270 // |trans| and |nigori_node|: used to access data in the cryptographer. | |
| 271 void FinishSetPassphrase( | |
| 272 bool success, | |
| 273 const std::string& bootstrap_token, | |
| 274 bool is_explicit, | |
| 275 WriteTransaction* trans, | |
| 276 WriteNode* nigori_node); | |
| 277 | |
| 278 // Call periodically from a database-safe thread to persist recent changes | |
| 279 // to the syncapi model. | |
| 280 void SaveChanges(); | |
| 281 | |
| 282 // DirectoryChangeDelegate implementation. | |
| 283 // This listener is called upon completion of a syncable transaction, and | |
| 284 // builds the list of sync-engine initiated changes that will be forwarded to | |
| 285 // the SyncManager's Observers. | |
| 286 virtual void HandleTransactionCompleteChangeEvent( | |
| 287 ModelTypeSet models_with_changes) OVERRIDE; | |
| 288 virtual ModelTypeSet HandleTransactionEndingChangeEvent( | |
| 289 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 290 syncable::BaseTransaction* trans) OVERRIDE; | |
| 291 virtual void HandleCalculateChangesChangeEventFromSyncApi( | |
| 292 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 293 syncable::BaseTransaction* trans) OVERRIDE; | |
| 294 virtual void HandleCalculateChangesChangeEventFromSyncer( | |
| 295 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 296 syncable::BaseTransaction* trans) OVERRIDE; | |
| 297 | |
| 298 // Open the directory named with username_for_share | |
| 299 bool OpenDirectory(); | |
| 300 | |
| 301 // Cryptographer::Observer implementation. | |
| 302 virtual void OnEncryptedTypesChanged( | |
| 303 syncer::ModelTypeSet encrypted_types, | |
| 304 bool encrypt_everything) OVERRIDE; | |
| 305 | |
| 306 // SyncNotifierObserver implementation. | |
| 307 virtual void OnNotificationsEnabled() OVERRIDE; | |
| 308 virtual void OnNotificationsDisabled( | |
| 309 syncer::NotificationsDisabledReason reason) OVERRIDE; | |
| 310 virtual void OnIncomingNotification( | |
| 311 const syncer::ModelTypePayloadMap& type_payloads, | |
| 312 syncer::IncomingNotificationSource source) OVERRIDE; | |
| 313 | |
| 314 void AddObserver(SyncManager::Observer* observer); | |
| 315 void RemoveObserver(SyncManager::Observer* observer); | |
| 316 | |
| 317 // Accessors for the private members. | |
| 318 syncable::Directory* directory() { return share_.directory.get(); } | |
| 319 SyncAPIServerConnectionManager* connection_manager() { | |
| 320 return connection_manager_.get(); | |
| 321 } | |
| 322 SyncSessionContext* session_context() { return session_context_.get(); } | |
| 323 SyncScheduler* scheduler() const { return scheduler_.get(); } | |
| 324 UserShare* GetUserShare() { | |
| 325 DCHECK(initialized_); | |
| 326 return &share_; | |
| 327 } | |
| 328 | |
| 329 // Return the currently active (validated) username for use with syncable | |
| 330 // types. | |
| 331 const std::string& username_for_share() const { | |
| 332 return share_.name; | |
| 333 } | |
| 334 | |
| 335 SyncStatus GetStatus(); | |
| 336 | |
| 337 void RequestNudge(const tracked_objects::Location& nudge_location); | |
| 338 | |
| 339 void RequestNudgeForDataTypes( | |
| 340 const tracked_objects::Location& nudge_location, | |
| 341 ModelTypeSet type); | |
| 342 | |
| 343 TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type); | |
| 344 | |
| 345 void NotifyCryptographerState(Cryptographer* cryptographer); | |
| 346 | |
| 347 // See SyncManager::Shutdown* for information. | |
| 348 void StopSyncingForShutdown(const base::Closure& callback); | |
| 349 void ShutdownOnSyncThread(); | |
| 350 | |
| 351 // If this is a deletion for a password, sets the legacy | |
| 352 // ExtraPasswordChangeRecordData field of |buffer|. Otherwise sets | |
| 353 // |buffer|'s specifics field to contain the unencrypted data. | |
| 354 void SetExtraChangeRecordData(int64 id, | |
| 355 syncer::ModelType type, | |
| 356 ChangeReorderBuffer* buffer, | |
| 357 Cryptographer* cryptographer, | |
| 358 const syncable::EntryKernel& original, | |
| 359 bool existed_before, | |
| 360 bool exists_now); | |
| 361 | |
| 362 // Called only by our NetworkChangeNotifier. | |
| 363 virtual void OnIPAddressChanged() OVERRIDE; | |
| 364 | |
| 365 ModelTypeSet GetTypesWithEmptyProgressMarkerToken(ModelTypeSet types) { | |
| 366 DCHECK(initialized_); | |
| 367 syncer::ModelTypeSet result; | |
| 368 for (syncer::ModelTypeSet::Iterator i = types.First(); | |
| 369 i.Good(); i.Inc()) { | |
| 370 sync_pb::DataTypeProgressMarker marker; | |
| 371 directory()->GetDownloadProgress(i.Get(), &marker); | |
| 372 | |
| 373 if (marker.token().empty()) | |
| 374 result.Put(i.Get()); | |
| 375 | |
| 376 } | |
| 377 return result; | |
| 378 } | |
| 379 | |
| 380 syncer::ModelTypeSet InitialSyncEndedTypes() { | |
| 381 DCHECK(initialized_); | |
| 382 return directory()->initial_sync_ended_types(); | |
| 383 } | |
| 384 | |
| 385 // SyncEngineEventListener implementation. | |
| 386 virtual void OnSyncEngineEvent(const SyncEngineEvent& event) OVERRIDE; | |
| 387 | |
| 388 // ServerConnectionEventListener implementation. | |
| 389 virtual void OnServerConnectionEvent( | |
| 390 const ServerConnectionEvent& event) OVERRIDE; | |
| 391 | |
| 392 // JsBackend implementation. | |
| 393 virtual void SetJsEventHandler( | |
| 394 const WeakHandle<JsEventHandler>& event_handler) OVERRIDE; | |
| 395 virtual void ProcessJsMessage( | |
| 396 const std::string& name, const JsArgList& args, | |
| 397 const WeakHandle<JsReplyHandler>& reply_handler) OVERRIDE; | |
| 398 | |
| 399 void SetSyncSchedulerForTest(scoped_ptr<SyncScheduler> scheduler); | |
| 400 | |
| 401 private: | |
| 402 struct NotificationInfo { | |
| 403 int total_count; | |
| 404 std::string payload; | |
| 405 | |
| 406 NotificationInfo() : total_count(0) {} | |
| 407 | |
| 408 ~NotificationInfo() {} | |
| 409 | |
| 410 // Returned pointer owned by the caller. | |
| 411 DictionaryValue* ToValue() const { | |
| 412 DictionaryValue* value = new DictionaryValue(); | |
| 413 value->SetInteger("totalCount", total_count); | |
| 414 value->SetString("payload", payload); | |
| 415 return value; | |
| 416 } | |
| 417 }; | |
| 418 | |
| 419 typedef std::map<syncer::ModelType, NotificationInfo> NotificationInfoMap; | |
| 420 typedef JsArgList | |
| 421 (SyncManager::SyncInternal::*UnboundJsMessageHandler)(const JsArgList&); | |
| 422 typedef base::Callback<JsArgList(const JsArgList&)> JsMessageHandler; | |
| 423 typedef std::map<std::string, JsMessageHandler> JsMessageHandlerMap; | |
| 424 | |
| 425 // Internal callback of UpdateCryptographerAndNigoriCallback. | |
| 426 void UpdateCryptographerAndNigoriCallback( | |
| 427 const std::string& chrome_version, | |
| 428 const base::Closure& done_callback, | |
| 429 const std::string& session_name); | |
| 430 | |
| 431 // Determine if the parents or predecessors differ between the old and new | |
| 432 // versions of an entry stored in |a| and |b|. Note that a node's index may | |
| 433 // change without its NEXT_ID changing if the node at NEXT_ID also moved (but | |
| 434 // the relative order is unchanged). To handle such cases, we rely on the | |
| 435 // caller to treat a position update on any sibling as updating the positions | |
| 436 // of all siblings. | |
| 437 static bool VisiblePositionsDiffer( | |
| 438 const syncable::EntryKernelMutation& mutation) { | |
| 439 const syncable::EntryKernel& a = mutation.original; | |
| 440 const syncable::EntryKernel& b = mutation.mutated; | |
| 441 // If the datatype isn't one where the browser model cares about position, | |
| 442 // don't bother notifying that data model of position-only changes. | |
| 443 if (!ShouldMaintainPosition( | |
| 444 syncer::GetModelTypeFromSpecifics(b.ref(SPECIFICS)))) | |
| 445 return false; | |
| 446 if (a.ref(syncable::NEXT_ID) != b.ref(syncable::NEXT_ID)) | |
| 447 return true; | |
| 448 if (a.ref(syncable::PARENT_ID) != b.ref(syncable::PARENT_ID)) | |
| 449 return true; | |
| 450 return false; | |
| 451 } | |
| 452 | |
| 453 // Determine if any of the fields made visible to clients of the Sync API | |
| 454 // differ between the versions of an entry stored in |a| and |b|. A return | |
| 455 // value of false means that it should be OK to ignore this change. | |
| 456 static bool VisiblePropertiesDiffer( | |
| 457 const syncable::EntryKernelMutation& mutation, | |
| 458 Cryptographer* cryptographer) { | |
| 459 const syncable::EntryKernel& a = mutation.original; | |
| 460 const syncable::EntryKernel& b = mutation.mutated; | |
| 461 const sync_pb::EntitySpecifics& a_specifics = a.ref(SPECIFICS); | |
| 462 const sync_pb::EntitySpecifics& b_specifics = b.ref(SPECIFICS); | |
| 463 DCHECK_EQ(syncer::GetModelTypeFromSpecifics(a_specifics), | |
| 464 syncer::GetModelTypeFromSpecifics(b_specifics)); | |
| 465 syncer::ModelType model_type = | |
| 466 syncer::GetModelTypeFromSpecifics(b_specifics); | |
| 467 // Suppress updates to items that aren't tracked by any browser model. | |
| 468 if (model_type < syncer::FIRST_REAL_MODEL_TYPE || | |
| 469 !a.ref(syncable::UNIQUE_SERVER_TAG).empty()) { | |
| 470 return false; | |
| 471 } | |
| 472 if (a.ref(syncable::IS_DIR) != b.ref(syncable::IS_DIR)) | |
| 473 return true; | |
| 474 if (!AreSpecificsEqual(cryptographer, | |
| 475 a.ref(syncable::SPECIFICS), | |
| 476 b.ref(syncable::SPECIFICS))) { | |
| 477 return true; | |
| 478 } | |
| 479 // We only care if the name has changed if neither specifics is encrypted | |
| 480 // (encrypted nodes blow away the NON_UNIQUE_NAME). | |
| 481 if (!a_specifics.has_encrypted() && !b_specifics.has_encrypted() && | |
| 482 a.ref(syncable::NON_UNIQUE_NAME) != b.ref(syncable::NON_UNIQUE_NAME)) | |
| 483 return true; | |
| 484 if (VisiblePositionsDiffer(mutation)) | |
| 485 return true; | |
| 486 return false; | |
| 487 } | |
| 488 | |
| 489 bool ChangeBuffersAreEmpty() { | |
| 490 for (int i = 0; i < syncer::MODEL_TYPE_COUNT; ++i) { | |
| 491 if (!change_buffers_[i].IsEmpty()) | |
| 492 return false; | |
| 493 } | |
| 494 return true; | |
| 495 } | |
| 496 | |
| 497 void ReEncryptEverything(WriteTransaction* trans); | |
| 498 | |
| 499 // Called for every notification. This updates the notification statistics | |
| 500 // to be displayed in about:sync. | |
| 501 void UpdateNotificationInfo( | |
| 502 const syncer::ModelTypePayloadMap& type_payloads); | |
| 503 | |
| 504 // Checks for server reachabilty and requests a nudge. | |
| 505 void OnIPAddressChangedImpl(); | |
| 506 | |
| 507 // Helper function used only by the constructor. | |
| 508 void BindJsMessageHandler( | |
| 509 const std::string& name, UnboundJsMessageHandler unbound_message_handler); | |
| 510 | |
| 511 // Returned pointer is owned by the caller. | |
| 512 static DictionaryValue* NotificationInfoToValue( | |
| 513 const NotificationInfoMap& notification_info); | |
| 514 | |
| 515 // JS message handlers. | |
| 516 JsArgList GetNotificationState(const JsArgList& args); | |
| 517 JsArgList GetNotificationInfo(const JsArgList& args); | |
| 518 JsArgList GetRootNodeDetails(const JsArgList& args); | |
| 519 JsArgList GetAllNodes(const JsArgList& args); | |
| 520 JsArgList GetNodeSummariesById(const JsArgList& args); | |
| 521 JsArgList GetNodeDetailsById(const JsArgList& args); | |
| 522 JsArgList GetChildNodeIds(const JsArgList& args); | |
| 523 JsArgList GetClientServerTraffic(const JsArgList& args); | |
| 524 | |
| 525 FilePath database_path_; | |
| 526 | |
| 527 const std::string name_; | |
| 528 | |
| 529 base::ThreadChecker thread_checker_; | |
| 530 | |
| 531 base::WeakPtrFactory<SyncInternal> weak_ptr_factory_; | |
| 532 | |
| 533 // Thread-safe handle used by | |
| 534 // HandleCalculateChangesChangeEventFromSyncApi(), which can be | |
| 535 // called from any thread. Valid only between between calls to | |
| 536 // Init() and Shutdown(). | |
| 537 // | |
| 538 // TODO(akalin): Ideally, we wouldn't need to store this; instead, | |
| 539 // we'd have another worker class which implements | |
| 540 // HandleCalculateChangesChangeEventFromSyncApi() and we'd pass it a | |
| 541 // WeakHandle when we construct it. | |
| 542 WeakHandle<SyncInternal> weak_handle_this_; | |
| 543 | |
| 544 // |blocking_task_runner| is a TaskRunner to be used for tasks that | |
| 545 // may block on disk I/O. | |
| 546 scoped_refptr<base::TaskRunner> blocking_task_runner_; | |
| 547 | |
| 548 // We give a handle to share_ to clients of the API for use when constructing | |
| 549 // any transaction type. | |
| 550 UserShare share_; | |
| 551 | |
| 552 // This can be called from any thread, but only between calls to | |
| 553 // OpenDirectory() and ShutdownOnSyncThread(). | |
| 554 syncer::WeakHandle<SyncManager::ChangeObserver> change_observer_; | |
| 555 | |
| 556 ObserverList<SyncManager::Observer> observers_; | |
| 557 | |
| 558 // The ServerConnectionManager used to abstract communication between the | |
| 559 // client (the Syncer) and the sync server. | |
| 560 scoped_ptr<SyncAPIServerConnectionManager> connection_manager_; | |
| 561 | |
| 562 // A container of various bits of information used by the SyncScheduler to | |
| 563 // create SyncSessions. Must outlive the SyncScheduler. | |
| 564 scoped_ptr<SyncSessionContext> session_context_; | |
| 565 | |
| 566 // The scheduler that runs the Syncer. Needs to be explicitly | |
| 567 // Start()ed. | |
| 568 scoped_ptr<SyncScheduler> scheduler_; | |
| 569 | |
| 570 // The SyncNotifier which notifies us when updates need to be downloaded. | |
| 571 scoped_ptr<syncer::SyncNotifier> sync_notifier_; | |
| 572 | |
| 573 // A multi-purpose status watch object that aggregates stats from various | |
| 574 // sync components. | |
| 575 AllStatus allstatus_; | |
| 576 | |
| 577 // Each element of this array is a store of change records produced by | |
| 578 // HandleChangeEvent during the CALCULATE_CHANGES step. The changes are | |
| 579 // segregated by model type, and are stored here to be processed and | |
| 580 // forwarded to the observer slightly later, at the TRANSACTION_ENDING | |
| 581 // step by HandleTransactionEndingChangeEvent. The list is cleared in the | |
| 582 // TRANSACTION_COMPLETE step by HandleTransactionCompleteChangeEvent. | |
| 583 ChangeReorderBuffer change_buffers_[syncer::MODEL_TYPE_COUNT]; | |
| 584 | |
| 585 SyncManager::ChangeDelegate* change_delegate_; | |
| 586 | |
| 587 // Set to true once Init has been called. | |
| 588 bool initialized_; | |
| 589 | |
| 590 // Controls the disabling of certain SyncManager features. | |
| 591 // Can be used to disable communication with the server and the use of an | |
| 592 // on-disk file for maintaining syncer state. | |
| 593 // TODO(117836): Clean up implementation of SyncManager unit tests. | |
| 594 TestingMode testing_mode_; | |
| 595 | |
| 596 bool observing_ip_address_changes_; | |
| 597 | |
| 598 // Map used to store the notification info to be displayed in | |
| 599 // about:sync page. | |
| 600 NotificationInfoMap notification_info_map_; | |
| 601 | |
| 602 // These are for interacting with chrome://sync-internals. | |
| 603 JsMessageHandlerMap js_message_handlers_; | |
| 604 WeakHandle<JsEventHandler> js_event_handler_; | |
| 605 JsSyncManagerObserver js_sync_manager_observer_; | |
| 606 JsMutationEventObserver js_mutation_event_observer_; | |
| 607 | |
| 608 syncer::ThrottledDataTypeTracker throttled_data_type_tracker_; | |
| 609 | |
| 610 // This is for keeping track of client events to send to the server. | |
| 611 DebugInfoEventListener debug_info_event_listener_; | |
| 612 | |
| 613 syncer::TrafficRecorder traffic_recorder_; | |
| 614 | |
| 615 Encryptor* encryptor_; | |
| 616 UnrecoverableErrorHandler* unrecoverable_error_handler_; | |
| 617 ReportUnrecoverableErrorFunction report_unrecoverable_error_function_; | |
| 618 | |
| 619 MessageLoop* const created_on_loop_; | |
| 620 | |
| 621 // The number of times we've automatically (i.e. not via SetPassphrase or | |
| 622 // conflict resolver) updated the nigori's encryption keys in this chrome | |
| 623 // instantiation. | |
| 624 int nigori_overwrite_count_; | |
| 625 }; | |
| 626 | |
| 627 // A class to calculate nudge delays for types. | |
| 628 class NudgeStrategy { | |
| 629 public: | |
| 630 static TimeDelta GetNudgeDelayTimeDelta(const ModelType& model_type, | |
| 631 SyncManager::SyncInternal* core) { | |
| 632 NudgeDelayStrategy delay_type = GetNudgeDelayStrategy(model_type); | |
| 633 return GetNudgeDelayTimeDeltaFromType(delay_type, | |
| 634 model_type, | |
| 635 core); | |
| 636 } | |
| 637 | |
| 638 private: | |
| 639 // Possible types of nudge delay for datatypes. | |
| 640 // Note: These are just hints. If a sync happens then all dirty entries | |
| 641 // would be committed as part of the sync. | |
| 642 enum NudgeDelayStrategy { | |
| 643 // Sync right away. | |
| 644 IMMEDIATE, | |
| 645 | |
| 646 // Sync this change while syncing another change. | |
| 647 ACCOMPANY_ONLY, | |
| 648 | |
| 649 // The datatype does not use one of the predefined wait times but defines | |
| 650 // its own wait time logic for nudge. | |
| 651 CUSTOM, | |
| 652 }; | |
| 653 | |
| 654 static NudgeDelayStrategy GetNudgeDelayStrategy(const ModelType& type) { | |
| 655 switch (type) { | |
| 656 case syncer::AUTOFILL: | |
| 657 return ACCOMPANY_ONLY; | |
| 658 case syncer::PREFERENCES: | |
| 659 case syncer::SESSIONS: | |
| 660 return CUSTOM; | |
| 661 default: | |
| 662 return IMMEDIATE; | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 static TimeDelta GetNudgeDelayTimeDeltaFromType( | |
| 667 const NudgeDelayStrategy& delay_type, const ModelType& model_type, | |
| 668 const SyncManager::SyncInternal* core) { | |
| 669 CHECK(core); | |
| 670 TimeDelta delay = TimeDelta::FromMilliseconds( | |
| 671 SyncManager::kDefaultNudgeDelayMilliseconds); | |
| 672 switch (delay_type) { | |
| 673 case IMMEDIATE: | |
| 674 delay = TimeDelta::FromMilliseconds( | |
| 675 SyncManager::kDefaultNudgeDelayMilliseconds); | |
| 676 break; | |
| 677 case ACCOMPANY_ONLY: | |
| 678 delay = TimeDelta::FromSeconds( | |
| 679 syncer::kDefaultShortPollIntervalSeconds); | |
| 680 break; | |
| 681 case CUSTOM: | |
| 682 switch (model_type) { | |
| 683 case syncer::PREFERENCES: | |
| 684 delay = TimeDelta::FromMilliseconds( | |
| 685 SyncManager::kPreferencesNudgeDelayMilliseconds); | |
| 686 break; | |
| 687 case syncer::SESSIONS: | |
| 688 delay = core->scheduler()->sessions_commit_delay(); | |
| 689 break; | |
| 690 default: | |
| 691 NOTREACHED(); | |
| 692 } | |
| 693 break; | |
| 694 default: | |
| 695 NOTREACHED(); | |
| 696 } | |
| 697 return delay; | |
| 698 } | |
| 699 }; | |
| 700 | |
| 701 SyncManager::ChangeDelegate::~ChangeDelegate() {} | |
| 702 | |
| 703 SyncManager::ChangeObserver::~ChangeObserver() {} | |
| 704 | |
| 705 SyncManager::Observer::~Observer() {} | |
| 706 | |
| 707 SyncManager::SyncManager(const std::string& name) | |
| 708 : data_(new SyncInternal(name)) {} | |
| 709 | |
| 710 bool SyncManager::Init( | |
| 711 const FilePath& database_location, | |
| 712 const WeakHandle<JsEventHandler>& event_handler, | |
| 713 const std::string& sync_server_and_path, | |
| 714 int sync_server_port, | |
| 715 bool use_ssl, | |
| 716 const scoped_refptr<base::TaskRunner>& blocking_task_runner, | |
| 717 HttpPostProviderFactory* post_factory, | |
| 718 const syncer::ModelSafeRoutingInfo& model_safe_routing_info, | |
| 719 const std::vector<syncer::ModelSafeWorker*>& workers, | |
| 720 syncer::ExtensionsActivityMonitor* extensions_activity_monitor, | |
| 721 ChangeDelegate* change_delegate, | |
| 722 const SyncCredentials& credentials, | |
| 723 syncer::SyncNotifier* sync_notifier, | |
| 724 const std::string& restored_key_for_bootstrapping, | |
| 725 TestingMode testing_mode, | |
| 726 Encryptor* encryptor, | |
| 727 UnrecoverableErrorHandler* unrecoverable_error_handler, | |
| 728 ReportUnrecoverableErrorFunction report_unrecoverable_error_function) { | |
| 729 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 730 DCHECK(post_factory); | |
| 731 DVLOG(1) << "SyncManager starting Init..."; | |
| 732 std::string server_string(sync_server_and_path); | |
| 733 return data_->Init(database_location, | |
| 734 event_handler, | |
| 735 server_string, | |
| 736 sync_server_port, | |
| 737 use_ssl, | |
| 738 blocking_task_runner, | |
| 739 post_factory, | |
| 740 model_safe_routing_info, | |
| 741 workers, | |
| 742 extensions_activity_monitor, | |
| 743 change_delegate, | |
| 744 credentials, | |
| 745 sync_notifier, | |
| 746 restored_key_for_bootstrapping, | |
| 747 testing_mode, | |
| 748 encryptor, | |
| 749 unrecoverable_error_handler, | |
| 750 report_unrecoverable_error_function); | |
| 751 } | |
| 752 | |
| 753 void SyncManager::UpdateCredentials(const SyncCredentials& credentials) { | |
| 754 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 755 data_->UpdateCredentials(credentials); | |
| 756 } | |
| 757 | |
| 758 void SyncManager::UpdateEnabledTypes(const ModelTypeSet& enabled_types) { | |
| 759 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 760 data_->UpdateEnabledTypes(enabled_types); | |
| 761 } | |
| 762 | |
| 763 void SyncManager::ThrowUnrecoverableError() { | |
| 764 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 765 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 766 trans.GetWrappedTrans()->OnUnrecoverableError( | |
| 767 FROM_HERE, "Simulating unrecoverable error for testing purposes."); | |
| 768 } | |
| 769 | |
| 770 syncer::ModelTypeSet SyncManager::InitialSyncEndedTypes() { | |
| 771 return data_->InitialSyncEndedTypes(); | |
| 772 } | |
| 773 | |
| 774 syncer::ModelTypeSet SyncManager::GetTypesWithEmptyProgressMarkerToken( | |
| 775 syncer::ModelTypeSet types) { | |
| 776 return data_->GetTypesWithEmptyProgressMarkerToken(types); | |
| 777 } | |
| 778 | |
| 779 bool SyncManager::PurgePartiallySyncedTypes() { | |
| 780 return data_->PurgePartiallySyncedTypes(); | |
| 781 } | |
| 782 | |
| 783 void SyncManager::StartSyncingNormally( | |
| 784 const syncer::ModelSafeRoutingInfo& routing_info) { | |
| 785 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 786 data_->StartSyncingNormally(routing_info); | |
| 787 } | |
| 788 | |
| 789 void SyncManager::SetEncryptionPassphrase(const std::string& passphrase, | |
| 790 bool is_explicit) { | |
| 791 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 792 data_->SetEncryptionPassphrase(passphrase, is_explicit); | |
| 793 } | |
| 794 | |
| 795 void SyncManager::SetDecryptionPassphrase(const std::string& passphrase) { | |
| 796 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 797 data_->SetDecryptionPassphrase(passphrase); | |
| 798 } | |
| 799 | |
| 800 void SyncManager::EnableEncryptEverything() { | |
| 801 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 802 { | |
| 803 // Update the cryptographer to know we're now encrypting everything. | |
| 804 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 805 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 806 // Only set encrypt everything if we know we can encrypt. This allows the | |
| 807 // user to cancel encryption if they have forgotten their passphrase. | |
| 808 if (cryptographer->is_ready()) | |
| 809 cryptographer->set_encrypt_everything(); | |
| 810 } | |
| 811 | |
| 812 // Reads from cryptographer so will automatically encrypt all | |
| 813 // datatypes and update the nigori node as necessary. Will trigger | |
| 814 // OnPassphraseRequired if necessary. | |
| 815 data_->RefreshEncryption(); | |
| 816 } | |
| 817 | |
| 818 bool SyncManager::EncryptEverythingEnabledForTest() const { | |
| 819 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 820 return trans.GetCryptographer()->encrypt_everything(); | |
| 821 } | |
| 822 | |
| 823 bool SyncManager::IsUsingExplicitPassphrase() { | |
| 824 return data_ && data_->IsUsingExplicitPassphrase(); | |
| 825 } | |
| 826 | |
| 827 void SyncManager::ConfigureSyncer( | |
| 828 ConfigureReason reason, | |
| 829 const syncer::ModelTypeSet& types_to_config, | |
| 830 const syncer::ModelSafeRoutingInfo& new_routing_info, | |
| 831 const base::Closure& ready_task, | |
| 832 const base::Closure& retry_task) { | |
| 833 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 834 DCHECK(!ready_task.is_null()); | |
| 835 DCHECK(!retry_task.is_null()); | |
| 836 | |
| 837 // TODO(zea): set this based on whether cryptographer has keystore | |
| 838 // encryption key or not (requires opening a transaction). crbug.com/129665. | |
| 839 ConfigurationParams::KeystoreKeyStatus keystore_key_status = | |
| 840 ConfigurationParams::KEYSTORE_KEY_UNNECESSARY; | |
| 841 | |
| 842 ConfigurationParams params(GetSourceFromReason(reason), | |
| 843 types_to_config, | |
| 844 new_routing_info, | |
| 845 keystore_key_status, | |
| 846 ready_task); | |
| 847 | |
| 848 if (!data_->scheduler()) { | |
| 849 LOG(INFO) | |
| 850 << "SyncManager::ConfigureSyncer: could not configure because " | |
| 851 << "scheduler is null"; | |
| 852 params.ready_task.Run(); | |
| 853 return; | |
| 854 } | |
| 855 | |
| 856 data_->scheduler()->Start(syncer::SyncScheduler::CONFIGURATION_MODE); | |
| 857 if (!data_->scheduler()->ScheduleConfiguration(params)) | |
| 858 retry_task.Run(); | |
| 859 | |
| 860 } | |
| 861 | |
| 862 bool SyncManager::SyncInternal::Init( | |
| 863 const FilePath& database_location, | |
| 864 const WeakHandle<JsEventHandler>& event_handler, | |
| 865 const std::string& sync_server_and_path, | |
| 866 int port, | |
| 867 bool use_ssl, | |
| 868 const scoped_refptr<base::TaskRunner>& blocking_task_runner, | |
| 869 HttpPostProviderFactory* post_factory, | |
| 870 const syncer::ModelSafeRoutingInfo& model_safe_routing_info, | |
| 871 const std::vector<syncer::ModelSafeWorker*>& workers, | |
| 872 syncer::ExtensionsActivityMonitor* extensions_activity_monitor, | |
| 873 ChangeDelegate* change_delegate, | |
| 874 const SyncCredentials& credentials, | |
| 875 syncer::SyncNotifier* sync_notifier, | |
| 876 const std::string& restored_key_for_bootstrapping, | |
| 877 TestingMode testing_mode, | |
| 878 Encryptor* encryptor, | |
| 879 UnrecoverableErrorHandler* unrecoverable_error_handler, | |
| 880 ReportUnrecoverableErrorFunction report_unrecoverable_error_function) { | |
| 881 CHECK(!initialized_); | |
| 882 | |
| 883 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 884 | |
| 885 DVLOG(1) << "Starting SyncInternal initialization."; | |
| 886 | |
| 887 weak_handle_this_ = MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()); | |
| 888 | |
| 889 blocking_task_runner_ = blocking_task_runner; | |
| 890 | |
| 891 change_delegate_ = change_delegate; | |
| 892 testing_mode_ = testing_mode; | |
| 893 | |
| 894 sync_notifier_.reset(sync_notifier); | |
| 895 | |
| 896 AddObserver(&js_sync_manager_observer_); | |
| 897 SetJsEventHandler(event_handler); | |
| 898 | |
| 899 AddObserver(&debug_info_event_listener_); | |
| 900 | |
| 901 database_path_ = database_location.Append( | |
| 902 syncable::Directory::kSyncDatabaseFilename); | |
| 903 encryptor_ = encryptor; | |
| 904 unrecoverable_error_handler_ = unrecoverable_error_handler; | |
| 905 report_unrecoverable_error_function_ = report_unrecoverable_error_function; | |
| 906 | |
| 907 syncable::DirectoryBackingStore* backing_store = NULL; | |
| 908 if (testing_mode_ == TEST_IN_MEMORY) { | |
| 909 // TODO(tim): 117836. Use a factory or delegate to create this and don't | |
| 910 // depend on TEST_IN_MEMORY here. | |
| 911 backing_store = | |
| 912 new syncable::InMemoryDirectoryBackingStore(credentials.email); | |
| 913 } else { | |
| 914 FilePath absolute_db_path(database_path_); | |
| 915 file_util::AbsolutePath(&absolute_db_path); | |
| 916 backing_store = new syncable::OnDiskDirectoryBackingStore( | |
| 917 credentials.email, absolute_db_path); | |
| 918 } | |
| 919 | |
| 920 DCHECK(backing_store); | |
| 921 share_.directory.reset( | |
| 922 new syncable::Directory(encryptor_, | |
| 923 unrecoverable_error_handler_, | |
| 924 report_unrecoverable_error_function_, | |
| 925 backing_store)); | |
| 926 | |
| 927 connection_manager_.reset(new SyncAPIServerConnectionManager( | |
| 928 sync_server_and_path, port, use_ssl, post_factory)); | |
| 929 | |
| 930 net::NetworkChangeNotifier::AddIPAddressObserver(this); | |
| 931 observing_ip_address_changes_ = true; | |
| 932 | |
| 933 connection_manager()->AddListener(this); | |
| 934 | |
| 935 // Test mode does not use a syncer context or syncer thread. | |
| 936 if (testing_mode_ == NON_TEST) { | |
| 937 // Build a SyncSessionContext and store the worker in it. | |
| 938 DVLOG(1) << "Sync is bringing up SyncSessionContext."; | |
| 939 std::vector<SyncEngineEventListener*> listeners; | |
| 940 listeners.push_back(&allstatus_); | |
| 941 listeners.push_back(this); | |
| 942 session_context_.reset(new SyncSessionContext( | |
| 943 connection_manager_.get(), | |
| 944 directory(), | |
| 945 model_safe_routing_info, | |
| 946 workers, | |
| 947 extensions_activity_monitor, | |
| 948 &throttled_data_type_tracker_, | |
| 949 listeners, | |
| 950 &debug_info_event_listener_, | |
| 951 &traffic_recorder_)); | |
| 952 session_context()->set_account_name(credentials.email); | |
| 953 scheduler_.reset(new SyncScheduler(name_, session_context(), new Syncer())); | |
| 954 } | |
| 955 | |
| 956 bool success = SignIn(credentials); | |
| 957 | |
| 958 if (success) { | |
| 959 if (scheduler()) { | |
| 960 scheduler()->Start(syncer::SyncScheduler::CONFIGURATION_MODE); | |
| 961 } | |
| 962 | |
| 963 initialized_ = true; | |
| 964 | |
| 965 // Unapplied datatypes (those that do not have initial sync ended set) get | |
| 966 // re-downloaded during any configuration. But, it's possible for a datatype | |
| 967 // to have a progress marker but not have initial sync ended yet, making | |
| 968 // it a candidate for migration. This is a problem, as the DataTypeManager | |
| 969 // does not support a migration while it's already in the middle of a | |
| 970 // configuration. As a result, any partially synced datatype can stall the | |
| 971 // DTM, waiting for the configuration to complete, which it never will due | |
| 972 // to the migration error. In addition, a partially synced nigori will | |
| 973 // trigger the migration logic before the backend is initialized, resulting | |
| 974 // in crashes. We therefore detect and purge any partially synced types as | |
| 975 // part of initialization. | |
| 976 if (!PurgePartiallySyncedTypes()) | |
| 977 success = false; | |
| 978 | |
| 979 // Cryptographer should only be accessed while holding a | |
| 980 // transaction. Grabbing the user share for the transaction | |
| 981 // checks the initialization state, so this must come after | |
| 982 // |initialized_| is set to true. | |
| 983 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 984 trans.GetCryptographer()->Bootstrap(restored_key_for_bootstrapping); | |
| 985 trans.GetCryptographer()->AddObserver(this); | |
| 986 } | |
| 987 | |
| 988 // Notify that initialization is complete. Note: This should be the last to | |
| 989 // execute if |signed_in| is false. Reason being in that case we would | |
| 990 // post a task to shutdown sync. But if this function posts any other tasks | |
| 991 // on the UI thread and if shutdown wins then that tasks would execute on | |
| 992 // a freed pointer. This is because UI thread is not shut down. | |
| 993 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 994 OnInitializationComplete( | |
| 995 MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()), | |
| 996 success)); | |
| 997 | |
| 998 if (!success && testing_mode_ == NON_TEST) | |
| 999 return false; | |
| 1000 | |
| 1001 sync_notifier_->AddObserver(this); | |
| 1002 | |
| 1003 return success; | |
| 1004 } | |
| 1005 | |
| 1006 void SyncManager::SyncInternal::UpdateCryptographerAndNigori( | |
| 1007 const std::string& chrome_version, | |
| 1008 const base::Closure& done_callback) { | |
| 1009 DCHECK(initialized_); | |
| 1010 syncer::GetSessionName( | |
| 1011 blocking_task_runner_, | |
| 1012 base::Bind( | |
| 1013 &SyncManager::SyncInternal::UpdateCryptographerAndNigoriCallback, | |
| 1014 weak_ptr_factory_.GetWeakPtr(), | |
| 1015 chrome_version, | |
| 1016 done_callback)); | |
| 1017 } | |
| 1018 | |
| 1019 void SyncManager::SyncInternal::UpdateNigoriEncryptionState( | |
| 1020 Cryptographer* cryptographer, | |
| 1021 WriteNode* nigori_node) { | |
| 1022 DCHECK(nigori_node); | |
| 1023 sync_pb::NigoriSpecifics nigori = nigori_node->GetNigoriSpecifics(); | |
| 1024 | |
| 1025 if (cryptographer->is_ready() && | |
| 1026 nigori_overwrite_count_ < kNigoriOverwriteLimit) { | |
| 1027 // Does not modify the encrypted blob if the unencrypted data already | |
| 1028 // matches what is about to be written. | |
| 1029 sync_pb::EncryptedData original_keys = nigori.encrypted(); | |
| 1030 if (!cryptographer->GetKeys(nigori.mutable_encrypted())) | |
| 1031 NOTREACHED(); | |
| 1032 | |
| 1033 if (nigori.encrypted().SerializeAsString() != | |
| 1034 original_keys.SerializeAsString()) { | |
| 1035 // We've updated the nigori node's encryption keys. In order to prevent | |
| 1036 // a possible looping of two clients constantly overwriting each other, | |
| 1037 // we limit the absolute number of overwrites per client instantiation. | |
| 1038 nigori_overwrite_count_++; | |
| 1039 UMA_HISTOGRAM_COUNTS("Sync.AutoNigoriOverwrites", | |
| 1040 nigori_overwrite_count_); | |
| 1041 } | |
| 1042 | |
| 1043 // Note: we don't try to set using_explicit_passphrase here since if that | |
| 1044 // is lost the user can always set it again. The main point is to preserve | |
| 1045 // the encryption keys so all data remains decryptable. | |
| 1046 } | |
| 1047 cryptographer->UpdateNigoriFromEncryptedTypes(&nigori); | |
| 1048 | |
| 1049 // If nothing has changed, this is a no-op. | |
| 1050 nigori_node->SetNigoriSpecifics(nigori); | |
| 1051 } | |
| 1052 | |
| 1053 void SyncManager::SyncInternal::UpdateCryptographerAndNigoriCallback( | |
| 1054 const std::string& chrome_version, | |
| 1055 const base::Closure& done_callback, | |
| 1056 const std::string& session_name) { | |
| 1057 if (!directory()->initial_sync_ended_for_type(syncer::NIGORI)) { | |
| 1058 done_callback.Run(); // Should only happen during first time sync. | |
| 1059 return; | |
| 1060 } | |
| 1061 | |
| 1062 bool success = false; | |
| 1063 { | |
| 1064 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 1065 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 1066 WriteNode node(&trans); | |
| 1067 | |
| 1068 if (node.InitByTagLookup(kNigoriTag) == syncer::BaseNode::INIT_OK) { | |
| 1069 sync_pb::NigoriSpecifics nigori(node.GetNigoriSpecifics()); | |
| 1070 Cryptographer::UpdateResult result = cryptographer->Update(nigori); | |
| 1071 if (result == Cryptographer::NEEDS_PASSPHRASE) { | |
| 1072 sync_pb::EncryptedData pending_keys; | |
| 1073 if (cryptographer->has_pending_keys()) | |
| 1074 pending_keys = cryptographer->GetPendingKeys(); | |
| 1075 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1076 OnPassphraseRequired(syncer::REASON_DECRYPTION, | |
| 1077 pending_keys)); | |
| 1078 } | |
| 1079 | |
| 1080 | |
| 1081 // Add or update device information. | |
| 1082 bool contains_this_device = false; | |
| 1083 for (int i = 0; i < nigori.device_information_size(); ++i) { | |
| 1084 const sync_pb::DeviceInformation& device_information = | |
| 1085 nigori.device_information(i); | |
| 1086 if (device_information.cache_guid() == directory()->cache_guid()) { | |
| 1087 // Update the version number in case it changed due to an update. | |
| 1088 if (device_information.chrome_version() != chrome_version) { | |
| 1089 sync_pb::DeviceInformation* mutable_device_information = | |
| 1090 nigori.mutable_device_information(i); | |
| 1091 mutable_device_information->set_chrome_version( | |
| 1092 chrome_version); | |
| 1093 } | |
| 1094 contains_this_device = true; | |
| 1095 } | |
| 1096 } | |
| 1097 | |
| 1098 if (!contains_this_device) { | |
| 1099 sync_pb::DeviceInformation* device_information = | |
| 1100 nigori.add_device_information(); | |
| 1101 device_information->set_cache_guid(directory()->cache_guid()); | |
| 1102 #if defined(OS_CHROMEOS) | |
| 1103 device_information->set_platform("ChromeOS"); | |
| 1104 #elif defined(OS_LINUX) | |
| 1105 device_information->set_platform("Linux"); | |
| 1106 #elif defined(OS_MACOSX) | |
| 1107 device_information->set_platform("Mac"); | |
| 1108 #elif defined(OS_WIN) | |
| 1109 device_information->set_platform("Windows"); | |
| 1110 #endif | |
| 1111 device_information->set_name(session_name); | |
| 1112 device_information->set_chrome_version(chrome_version); | |
| 1113 } | |
| 1114 // Disabled to avoid nigori races. TODO(zea): re-enable. crbug.com/122837 | |
| 1115 // node.SetNigoriSpecifics(nigori); | |
| 1116 | |
| 1117 // Make sure the nigori node has the up to date encryption info. | |
| 1118 UpdateNigoriEncryptionState(cryptographer, &node); | |
| 1119 | |
| 1120 NotifyCryptographerState(cryptographer); | |
| 1121 allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); | |
| 1122 | |
| 1123 success = cryptographer->is_ready(); | |
| 1124 } else { | |
| 1125 NOTREACHED(); | |
| 1126 } | |
| 1127 } | |
| 1128 | |
| 1129 if (success) | |
| 1130 RefreshEncryption(); | |
| 1131 done_callback.Run(); | |
| 1132 } | |
| 1133 | |
| 1134 void SyncManager::SyncInternal::NotifyCryptographerState( | |
| 1135 Cryptographer * cryptographer) { | |
| 1136 // TODO(lipalani): Explore the possibility of hooking this up to | |
| 1137 // SyncManager::Observer and making |AllStatus| a listener for that. | |
| 1138 allstatus_.SetCryptographerReady(cryptographer->is_ready()); | |
| 1139 allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys()); | |
| 1140 debug_info_event_listener_.SetCryptographerReady(cryptographer->is_ready()); | |
| 1141 debug_info_event_listener_.SetCrytographerHasPendingKeys( | |
| 1142 cryptographer->has_pending_keys()); | |
| 1143 } | |
| 1144 | |
| 1145 void SyncManager::SyncInternal::StartSyncingNormally( | |
| 1146 const syncer::ModelSafeRoutingInfo& routing_info) { | |
| 1147 // Start the sync scheduler. | |
| 1148 if (scheduler()) { // NULL during certain unittests. | |
| 1149 // TODO(sync): We always want the newest set of routes when we switch back | |
| 1150 // to normal mode. Figure out how to enforce set_routing_info is always | |
| 1151 // appropriately set and that it's only modified when switching to normal | |
| 1152 // mode. | |
| 1153 session_context()->set_routing_info(routing_info); | |
| 1154 scheduler()->Start(SyncScheduler::NORMAL_MODE); | |
| 1155 } | |
| 1156 } | |
| 1157 | |
| 1158 bool SyncManager::SyncInternal::OpenDirectory() { | |
| 1159 DCHECK(!initialized_) << "Should only happen once"; | |
| 1160 | |
| 1161 // Set before Open(). | |
| 1162 change_observer_ = | |
| 1163 syncer::MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()); | |
| 1164 WeakHandle<syncable::TransactionObserver> transaction_observer( | |
| 1165 syncer::MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr())); | |
| 1166 | |
| 1167 syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED; | |
| 1168 open_result = directory()->Open(username_for_share(), this, | |
| 1169 transaction_observer); | |
| 1170 if (open_result != syncable::OPENED) { | |
| 1171 LOG(ERROR) << "Could not open share for:" << username_for_share(); | |
| 1172 return false; | |
| 1173 } | |
| 1174 | |
| 1175 connection_manager()->set_client_id(directory()->cache_guid()); | |
| 1176 return true; | |
| 1177 } | |
| 1178 | |
| 1179 bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { | |
| 1180 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1181 DCHECK(share_.name.empty()); | |
| 1182 share_.name = credentials.email; | |
| 1183 | |
| 1184 DVLOG(1) << "Signing in user: " << username_for_share(); | |
| 1185 if (!OpenDirectory()) | |
| 1186 return false; | |
| 1187 | |
| 1188 // Retrieve and set the sync notifier state. This should be done | |
| 1189 // only after OpenDirectory is called. | |
| 1190 std::string unique_id = directory()->cache_guid(); | |
| 1191 std::string state = directory()->GetNotificationState(); | |
| 1192 DVLOG(1) << "Read notification unique ID: " << unique_id; | |
| 1193 if (VLOG_IS_ON(1)) { | |
| 1194 std::string encoded_state; | |
| 1195 base::Base64Encode(state, &encoded_state); | |
| 1196 DVLOG(1) << "Read notification state: " << encoded_state; | |
| 1197 } | |
| 1198 allstatus_.SetUniqueId(unique_id); | |
| 1199 sync_notifier_->SetUniqueId(unique_id); | |
| 1200 // TODO(tim): Remove once invalidation state has been migrated to new | |
| 1201 // InvalidationStateTracker store. Bug 124140. | |
| 1202 sync_notifier_->SetStateDeprecated(state); | |
| 1203 | |
| 1204 UpdateCredentials(credentials); | |
| 1205 return true; | |
| 1206 } | |
| 1207 | |
| 1208 bool SyncManager::SyncInternal::PurgePartiallySyncedTypes() { | |
| 1209 syncer::ModelTypeSet partially_synced_types = | |
| 1210 syncer::ModelTypeSet::All(); | |
| 1211 partially_synced_types.RemoveAll(InitialSyncEndedTypes()); | |
| 1212 partially_synced_types.RemoveAll(GetTypesWithEmptyProgressMarkerToken( | |
| 1213 syncer::ModelTypeSet::All())); | |
| 1214 | |
| 1215 UMA_HISTOGRAM_COUNTS("Sync.PartiallySyncedTypes", | |
| 1216 partially_synced_types.Size()); | |
| 1217 if (partially_synced_types.Empty()) | |
| 1218 return true; | |
| 1219 return directory()->PurgeEntriesWithTypeIn(partially_synced_types); | |
| 1220 } | |
| 1221 | |
| 1222 void SyncManager::SyncInternal::UpdateCredentials( | |
| 1223 const SyncCredentials& credentials) { | |
| 1224 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1225 DCHECK_EQ(credentials.email, share_.name); | |
| 1226 DCHECK(!credentials.email.empty()); | |
| 1227 DCHECK(!credentials.sync_token.empty()); | |
| 1228 | |
| 1229 observing_ip_address_changes_ = true; | |
| 1230 if (connection_manager()->set_auth_token(credentials.sync_token)) { | |
| 1231 sync_notifier_->UpdateCredentials( | |
| 1232 credentials.email, credentials.sync_token); | |
| 1233 if (initialized_ && scheduler()) { | |
| 1234 scheduler()->OnCredentialsUpdated(); | |
| 1235 } | |
| 1236 } | |
| 1237 } | |
| 1238 | |
| 1239 void SyncManager::SyncInternal::UpdateEnabledTypes( | |
| 1240 const ModelTypeSet& enabled_types) { | |
| 1241 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1242 sync_notifier_->UpdateEnabledTypes(enabled_types); | |
| 1243 } | |
| 1244 | |
| 1245 void SyncManager::SyncInternal::SetEncryptionPassphrase( | |
| 1246 const std::string& passphrase, | |
| 1247 bool is_explicit) { | |
| 1248 // We do not accept empty passphrases. | |
| 1249 if (passphrase.empty()) { | |
| 1250 NOTREACHED() << "Cannot encrypt with an empty passphrase."; | |
| 1251 return; | |
| 1252 } | |
| 1253 | |
| 1254 // All accesses to the cryptographer are protected by a transaction. | |
| 1255 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 1256 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 1257 KeyParams key_params = {"localhost", "dummy", passphrase}; | |
| 1258 WriteNode node(&trans); | |
| 1259 if (node.InitByTagLookup(kNigoriTag) != syncer::BaseNode::INIT_OK) { | |
| 1260 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. | |
| 1261 NOTREACHED(); | |
| 1262 return; | |
| 1263 } | |
| 1264 | |
| 1265 bool nigori_has_explicit_passphrase = | |
| 1266 node.GetNigoriSpecifics().using_explicit_passphrase(); | |
| 1267 std::string bootstrap_token; | |
| 1268 sync_pb::EncryptedData pending_keys; | |
| 1269 if (cryptographer->has_pending_keys()) | |
| 1270 pending_keys = cryptographer->GetPendingKeys(); | |
| 1271 bool success = false; | |
| 1272 | |
| 1273 | |
| 1274 // There are six cases to handle here: | |
| 1275 // 1. The user has no pending keys and is setting their current GAIA password | |
| 1276 // as the encryption passphrase. This happens either during first time sync | |
| 1277 // with a clean profile, or after re-authenticating on a profile that was | |
| 1278 // already signed in with the cryptographer ready. | |
| 1279 // 2. The user has no pending keys, and is overwriting an (already provided) | |
| 1280 // implicit passphrase with an explicit (custom) passphrase. | |
| 1281 // 3. The user has pending keys for an explicit passphrase that is somehow set | |
| 1282 // to their current GAIA passphrase. | |
| 1283 // 4. The user has pending keys encrypted with their current GAIA passphrase | |
| 1284 // and the caller passes in the current GAIA passphrase. | |
| 1285 // 5. The user has pending keys encrypted with an older GAIA passphrase | |
| 1286 // and the caller passes in the current GAIA passphrase. | |
| 1287 // 6. The user has previously done encryption with an explicit passphrase. | |
| 1288 // Furthermore, we enforce the fact that the bootstrap encryption token will | |
| 1289 // always be derived from the newest GAIA password if the account is using | |
| 1290 // an implicit passphrase (even if the data is encrypted with an old GAIA | |
| 1291 // password). If the account is using an explicit (custom) passphrase, the | |
| 1292 // bootstrap token will be derived from the most recently provided explicit | |
| 1293 // passphrase (that was able to decrypt the data). | |
| 1294 if (!nigori_has_explicit_passphrase) { | |
| 1295 if (!cryptographer->has_pending_keys()) { | |
| 1296 if (cryptographer->AddKey(key_params)) { | |
| 1297 // Case 1 and 2. We set a new GAIA passphrase when there are no pending | |
| 1298 // keys (1), or overwriting an implicit passphrase with a new explicit | |
| 1299 // one (2) when there are no pending keys. | |
| 1300 DVLOG(1) << "Setting " << (is_explicit ? "explicit" : "implicit" ) | |
| 1301 << " passphrase for encryption."; | |
| 1302 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1303 success = true; | |
| 1304 } else { | |
| 1305 NOTREACHED() << "Failed to add key to cryptographer."; | |
| 1306 success = false; | |
| 1307 } | |
| 1308 } else { // cryptographer->has_pending_keys() == true | |
| 1309 if (is_explicit) { | |
| 1310 // This can only happen if the nigori node is updated with a new | |
| 1311 // implicit passphrase while a client is attempting to set a new custom | |
| 1312 // passphrase (race condition). | |
| 1313 DVLOG(1) << "Failing because an implicit passphrase is already set."; | |
| 1314 success = false; | |
| 1315 } else { // is_explicit == false | |
| 1316 if (cryptographer->DecryptPendingKeys(key_params)) { | |
| 1317 // Case 4. We successfully decrypted with the implicit GAIA passphrase | |
| 1318 // passed in. | |
| 1319 DVLOG(1) << "Implicit internal passphrase accepted for decryption."; | |
| 1320 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1321 success = true; | |
| 1322 } else { | |
| 1323 // Case 5. Encryption was done with an old GAIA password, but we were | |
| 1324 // provided with the current GAIA password. We need to generate a new | |
| 1325 // bootstrap token to preserve it. We build a temporary cryptographer | |
| 1326 // to allow us to extract these params without polluting our current | |
| 1327 // cryptographer. | |
| 1328 DVLOG(1) << "Implicit internal passphrase failed to decrypt, adding " | |
| 1329 << "anyways as default passphrase and persisting via " | |
| 1330 << "bootstrap token."; | |
| 1331 Cryptographer temp_cryptographer(encryptor_); | |
| 1332 temp_cryptographer.AddKey(key_params); | |
| 1333 temp_cryptographer.GetBootstrapToken(&bootstrap_token); | |
| 1334 // We then set the new passphrase as the default passphrase of the | |
| 1335 // real cryptographer, even though we have pending keys. This is safe, | |
| 1336 // as although Cryptographer::is_initialized() will now be true, | |
| 1337 // is_ready() will remain false due to having pending keys. | |
| 1338 cryptographer->AddKey(key_params); | |
| 1339 success = false; | |
| 1340 } | |
| 1341 } // is_explicit | |
| 1342 } // cryptographer->has_pending_keys() | |
| 1343 } else { // nigori_has_explicit_passphrase == true | |
| 1344 // Case 6. We do not want to override a previously set explicit passphrase, | |
| 1345 // so we return a failure. | |
| 1346 DVLOG(1) << "Failing because an explicit passphrase is already set."; | |
| 1347 success = false; | |
| 1348 } | |
| 1349 | |
| 1350 DVLOG_IF(1, !success) | |
| 1351 << "Failure in SetEncryptionPassphrase; notifying and returning."; | |
| 1352 DVLOG_IF(1, success) | |
| 1353 << "Successfully set encryption passphrase; updating nigori and " | |
| 1354 "reencrypting."; | |
| 1355 | |
| 1356 FinishSetPassphrase( | |
| 1357 success, bootstrap_token, is_explicit, &trans, &node); | |
| 1358 } | |
| 1359 | |
| 1360 void SyncManager::SyncInternal::SetDecryptionPassphrase( | |
| 1361 const std::string& passphrase) { | |
| 1362 // We do not accept empty passphrases. | |
| 1363 if (passphrase.empty()) { | |
| 1364 NOTREACHED() << "Cannot decrypt with an empty passphrase."; | |
| 1365 return; | |
| 1366 } | |
| 1367 | |
| 1368 // All accesses to the cryptographer are protected by a transaction. | |
| 1369 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 1370 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 1371 KeyParams key_params = {"localhost", "dummy", passphrase}; | |
| 1372 WriteNode node(&trans); | |
| 1373 if (node.InitByTagLookup(kNigoriTag) != syncer::BaseNode::INIT_OK) { | |
| 1374 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. | |
| 1375 NOTREACHED(); | |
| 1376 return; | |
| 1377 } | |
| 1378 | |
| 1379 if (!cryptographer->has_pending_keys()) { | |
| 1380 // Note that this *can* happen in a rare situation where data is | |
| 1381 // re-encrypted on another client while a SetDecryptionPassphrase() call is | |
| 1382 // in-flight on this client. It is rare enough that we choose to do nothing. | |
| 1383 NOTREACHED() << "Attempt to set decryption passphrase failed because there " | |
| 1384 << "were no pending keys."; | |
| 1385 return; | |
| 1386 } | |
| 1387 | |
| 1388 bool nigori_has_explicit_passphrase = | |
| 1389 node.GetNigoriSpecifics().using_explicit_passphrase(); | |
| 1390 std::string bootstrap_token; | |
| 1391 sync_pb::EncryptedData pending_keys; | |
| 1392 pending_keys = cryptographer->GetPendingKeys(); | |
| 1393 bool success = false; | |
| 1394 | |
| 1395 // There are three cases to handle here: | |
| 1396 // 7. We're using the current GAIA password to decrypt the pending keys. This | |
| 1397 // happens when signing in to an account with a previously set implicit | |
| 1398 // passphrase, where the data is already encrypted with the newest GAIA | |
| 1399 // password. | |
| 1400 // 8. The user is providing an old GAIA password to decrypt the pending keys. | |
| 1401 // In this case, the user is using an implicit passphrase, but has changed | |
| 1402 // their password since they last encrypted their data, and therefore | |
| 1403 // their current GAIA password was unable to decrypt the data. This will | |
| 1404 // happen when the user is setting up a new profile with a previously | |
| 1405 // encrypted account (after changing passwords). | |
| 1406 // 9. The user is providing a previously set explicit passphrase to decrypt | |
| 1407 // the pending keys. | |
| 1408 if (!nigori_has_explicit_passphrase) { | |
| 1409 if (cryptographer->is_initialized()) { | |
| 1410 // We only want to change the default encryption key to the pending | |
| 1411 // one if the pending keybag already contains the current default. | |
| 1412 // This covers the case where a different client re-encrypted | |
| 1413 // everything with a newer gaia passphrase (and hence the keybag | |
| 1414 // contains keys from all previously used gaia passphrases). | |
| 1415 // Otherwise, we're in a situation where the pending keys are | |
| 1416 // encrypted with an old gaia passphrase, while the default is the | |
| 1417 // current gaia passphrase. In that case, we preserve the default. | |
| 1418 Cryptographer temp_cryptographer(encryptor_); | |
| 1419 temp_cryptographer.SetPendingKeys(cryptographer->GetPendingKeys()); | |
| 1420 if (temp_cryptographer.DecryptPendingKeys(key_params)) { | |
| 1421 // Check to see if the pending bag of keys contains the current | |
| 1422 // default key. | |
| 1423 sync_pb::EncryptedData encrypted; | |
| 1424 cryptographer->GetKeys(&encrypted); | |
| 1425 if (temp_cryptographer.CanDecrypt(encrypted)) { | |
| 1426 DVLOG(1) << "Implicit user provided passphrase accepted for " | |
| 1427 << "decryption, overwriting default."; | |
| 1428 // Case 7. The pending keybag contains the current default. Go ahead | |
| 1429 // and update the cryptographer, letting the default change. | |
| 1430 cryptographer->DecryptPendingKeys(key_params); | |
| 1431 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1432 success = true; | |
| 1433 } else { | |
| 1434 // Case 8. The pending keybag does not contain the current default | |
| 1435 // encryption key. We decrypt the pending keys here, and in | |
| 1436 // FinishSetPassphrase, re-encrypt everything with the current GAIA | |
| 1437 // passphrase instead of the passphrase just provided by the user. | |
| 1438 DVLOG(1) << "Implicit user provided passphrase accepted for " | |
| 1439 << "decryption, restoring implicit internal passphrase " | |
| 1440 << "as default."; | |
| 1441 std::string bootstrap_token_from_current_key; | |
| 1442 cryptographer->GetBootstrapToken( | |
| 1443 &bootstrap_token_from_current_key); | |
| 1444 cryptographer->DecryptPendingKeys(key_params); | |
| 1445 // Overwrite the default from the pending keys. | |
| 1446 cryptographer->AddKeyFromBootstrapToken( | |
| 1447 bootstrap_token_from_current_key); | |
| 1448 success = true; | |
| 1449 } | |
| 1450 } else { // !temp_cryptographer.DecryptPendingKeys(..) | |
| 1451 DVLOG(1) << "Implicit user provided passphrase failed to decrypt."; | |
| 1452 success = false; | |
| 1453 } // temp_cryptographer.DecryptPendingKeys(...) | |
| 1454 } else { // cryptographer->is_initialized() == false | |
| 1455 if (cryptographer->DecryptPendingKeys(key_params)) { | |
| 1456 // This can happpen in two cases: | |
| 1457 // - First time sync on android, where we'll never have a | |
| 1458 // !user_provided passphrase. | |
| 1459 // - This is a restart for a client that lost their bootstrap token. | |
| 1460 // In both cases, we should go ahead and initialize the cryptographer | |
| 1461 // and persist the new bootstrap token. | |
| 1462 // | |
| 1463 // Note: at this point, we cannot distinguish between cases 7 and 8 | |
| 1464 // above. This user provided passphrase could be the current or the | |
| 1465 // old. But, as long as we persist the token, there's nothing more | |
| 1466 // we can do. | |
| 1467 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1468 DVLOG(1) << "Implicit user provided passphrase accepted, initializing" | |
| 1469 << " cryptographer."; | |
| 1470 success = true; | |
| 1471 } else { | |
| 1472 DVLOG(1) << "Implicit user provided passphrase failed to decrypt."; | |
| 1473 success = false; | |
| 1474 } | |
| 1475 } // cryptographer->is_initialized() | |
| 1476 } else { // nigori_has_explicit_passphrase == true | |
| 1477 // Case 9. Encryption was done with an explicit passphrase, and we decrypt | |
| 1478 // with the passphrase provided by the user. | |
| 1479 if (cryptographer->DecryptPendingKeys(key_params)) { | |
| 1480 DVLOG(1) << "Explicit passphrase accepted for decryption."; | |
| 1481 cryptographer->GetBootstrapToken(&bootstrap_token); | |
| 1482 success = true; | |
| 1483 } else { | |
| 1484 DVLOG(1) << "Explicit passphrase failed to decrypt."; | |
| 1485 success = false; | |
| 1486 } | |
| 1487 } // nigori_has_explicit_passphrase | |
| 1488 | |
| 1489 DVLOG_IF(1, !success) | |
| 1490 << "Failure in SetDecryptionPassphrase; notifying and returning."; | |
| 1491 DVLOG_IF(1, success) | |
| 1492 << "Successfully set decryption passphrase; updating nigori and " | |
| 1493 "reencrypting."; | |
| 1494 | |
| 1495 FinishSetPassphrase(success, | |
| 1496 bootstrap_token, | |
| 1497 nigori_has_explicit_passphrase, | |
| 1498 &trans, | |
| 1499 &node); | |
| 1500 } | |
| 1501 | |
| 1502 void SyncManager::SyncInternal::FinishSetPassphrase( | |
| 1503 bool success, | |
| 1504 const std::string& bootstrap_token, | |
| 1505 bool is_explicit, | |
| 1506 WriteTransaction* trans, | |
| 1507 WriteNode* nigori_node) { | |
| 1508 Cryptographer* cryptographer = trans->GetCryptographer(); | |
| 1509 NotifyCryptographerState(cryptographer); | |
| 1510 | |
| 1511 // It's possible we need to change the bootstrap token even if we failed to | |
| 1512 // set the passphrase (for example if we need to preserve the new GAIA | |
| 1513 // passphrase). | |
| 1514 if (!bootstrap_token.empty()) { | |
| 1515 DVLOG(1) << "Bootstrap token updated."; | |
| 1516 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1517 OnBootstrapTokenUpdated(bootstrap_token)); | |
| 1518 } | |
| 1519 | |
| 1520 if (!success) { | |
| 1521 if (cryptographer->is_ready()) { | |
| 1522 LOG(ERROR) << "Attempt to change passphrase failed while cryptographer " | |
| 1523 << "was ready."; | |
| 1524 } else if (cryptographer->has_pending_keys()) { | |
| 1525 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1526 OnPassphraseRequired(syncer::REASON_DECRYPTION, | |
| 1527 cryptographer->GetPendingKeys())); | |
| 1528 } else { | |
| 1529 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1530 OnPassphraseRequired(syncer::REASON_ENCRYPTION, | |
| 1531 sync_pb::EncryptedData())); | |
| 1532 } | |
| 1533 return; | |
| 1534 } | |
| 1535 | |
| 1536 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1537 OnPassphraseAccepted()); | |
| 1538 DCHECK(cryptographer->is_ready()); | |
| 1539 | |
| 1540 // TODO(tim): Bug 58231. It would be nice if setting a passphrase didn't | |
| 1541 // require messing with the Nigori node, because we can't set a passphrase | |
| 1542 // until download conditions are met vs Cryptographer init. It seems like | |
| 1543 // it's safe to defer this work. | |
| 1544 sync_pb::NigoriSpecifics specifics(nigori_node->GetNigoriSpecifics()); | |
| 1545 // Does not modify specifics.encrypted() if the original decrypted data was | |
| 1546 // the same. | |
| 1547 if (!cryptographer->GetKeys(specifics.mutable_encrypted())) { | |
| 1548 NOTREACHED(); | |
| 1549 return; | |
| 1550 } | |
| 1551 specifics.set_using_explicit_passphrase(is_explicit); | |
| 1552 nigori_node->SetNigoriSpecifics(specifics); | |
| 1553 | |
| 1554 // Does nothing if everything is already encrypted or the cryptographer has | |
| 1555 // pending keys. | |
| 1556 ReEncryptEverything(trans); | |
| 1557 } | |
| 1558 | |
| 1559 bool SyncManager::SyncInternal::IsUsingExplicitPassphrase() { | |
| 1560 ReadTransaction trans(FROM_HERE, &share_); | |
| 1561 ReadNode node(&trans); | |
| 1562 if (node.InitByTagLookup(kNigoriTag) != syncer::BaseNode::INIT_OK) { | |
| 1563 // TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS. | |
| 1564 NOTREACHED(); | |
| 1565 return false; | |
| 1566 } | |
| 1567 | |
| 1568 return node.GetNigoriSpecifics().using_explicit_passphrase(); | |
| 1569 } | |
| 1570 | |
| 1571 void SyncManager::SyncInternal::RefreshEncryption() { | |
| 1572 DCHECK(initialized_); | |
| 1573 | |
| 1574 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 1575 WriteNode node(&trans); | |
| 1576 if (node.InitByTagLookup(kNigoriTag) != syncer::BaseNode::INIT_OK) { | |
| 1577 NOTREACHED() << "Unable to set encrypted datatypes because Nigori node not " | |
| 1578 << "found."; | |
| 1579 return; | |
| 1580 } | |
| 1581 | |
| 1582 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 1583 | |
| 1584 if (!cryptographer->is_ready()) { | |
| 1585 DVLOG(1) << "Attempting to encrypt datatypes when cryptographer not " | |
| 1586 << "initialized, prompting for passphrase."; | |
| 1587 // TODO(zea): this isn't really decryption, but that's the only way we have | |
| 1588 // to prompt the user for a passsphrase. See http://crbug.com/91379. | |
| 1589 sync_pb::EncryptedData pending_keys; | |
| 1590 if (cryptographer->has_pending_keys()) | |
| 1591 pending_keys = cryptographer->GetPendingKeys(); | |
| 1592 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1593 OnPassphraseRequired(syncer::REASON_DECRYPTION, | |
| 1594 pending_keys)); | |
| 1595 return; | |
| 1596 } | |
| 1597 | |
| 1598 UpdateNigoriEncryptionState(cryptographer, &node); | |
| 1599 | |
| 1600 allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); | |
| 1601 | |
| 1602 // We reencrypt everything regardless of whether the set of encrypted | |
| 1603 // types changed to ensure that any stray unencrypted entries are overwritten. | |
| 1604 ReEncryptEverything(&trans); | |
| 1605 } | |
| 1606 | |
| 1607 // This function iterates over all encrypted types. There are many scenarios in | |
| 1608 // which data for some or all types is not currently available. In that case, | |
| 1609 // the lookup of the root node will fail and we will skip encryption for that | |
| 1610 // type. | |
| 1611 void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) { | |
| 1612 Cryptographer* cryptographer = trans->GetCryptographer(); | |
| 1613 if (!cryptographer || !cryptographer->is_ready()) | |
| 1614 return; | |
| 1615 syncer::ModelTypeSet encrypted_types = GetEncryptedTypes(trans); | |
| 1616 for (syncer::ModelTypeSet::Iterator iter = encrypted_types.First(); | |
| 1617 iter.Good(); iter.Inc()) { | |
| 1618 if (iter.Get() == syncer::PASSWORDS || iter.Get() == syncer::NIGORI) | |
| 1619 continue; // These types handle encryption differently. | |
| 1620 | |
| 1621 ReadNode type_root(trans); | |
| 1622 std::string tag = syncer::ModelTypeToRootTag(iter.Get()); | |
| 1623 if (type_root.InitByTagLookup(tag) != syncer::BaseNode::INIT_OK) | |
| 1624 continue; // Don't try to reencrypt if the type's data is unavailable. | |
| 1625 | |
| 1626 // Iterate through all children of this datatype. | |
| 1627 std::queue<int64> to_visit; | |
| 1628 int64 child_id = type_root.GetFirstChildId(); | |
| 1629 to_visit.push(child_id); | |
| 1630 while (!to_visit.empty()) { | |
| 1631 child_id = to_visit.front(); | |
| 1632 to_visit.pop(); | |
| 1633 if (child_id == kInvalidId) | |
| 1634 continue; | |
| 1635 | |
| 1636 WriteNode child(trans); | |
| 1637 if (child.InitByIdLookup(child_id) != syncer::BaseNode::INIT_OK) { | |
| 1638 NOTREACHED(); | |
| 1639 continue; | |
| 1640 } | |
| 1641 if (child.GetIsFolder()) { | |
| 1642 to_visit.push(child.GetFirstChildId()); | |
| 1643 } | |
| 1644 if (child.GetEntry()->Get(syncable::UNIQUE_SERVER_TAG).empty()) { | |
| 1645 // Rewrite the specifics of the node with encrypted data if necessary | |
| 1646 // (only rewrite the non-unique folders). | |
| 1647 child.ResetFromSpecifics(); | |
| 1648 } | |
| 1649 to_visit.push(child.GetSuccessorId()); | |
| 1650 } | |
| 1651 } | |
| 1652 | |
| 1653 // Passwords are encrypted with their own legacy scheme. Passwords are always | |
| 1654 // encrypted so we don't need to check GetEncryptedTypes() here. | |
| 1655 ReadNode passwords_root(trans); | |
| 1656 std::string passwords_tag = syncer::ModelTypeToRootTag(syncer::PASSWORDS); | |
| 1657 if (passwords_root.InitByTagLookup(passwords_tag) == | |
| 1658 syncer::BaseNode::INIT_OK) { | |
| 1659 int64 child_id = passwords_root.GetFirstChildId(); | |
| 1660 while (child_id != kInvalidId) { | |
| 1661 WriteNode child(trans); | |
| 1662 if (child.InitByIdLookup(child_id) != syncer::BaseNode::INIT_OK) { | |
| 1663 NOTREACHED(); | |
| 1664 return; | |
| 1665 } | |
| 1666 child.SetPasswordSpecifics(child.GetPasswordSpecifics()); | |
| 1667 child_id = child.GetSuccessorId(); | |
| 1668 } | |
| 1669 } | |
| 1670 | |
| 1671 // NOTE: We notify from within a transaction. | |
| 1672 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, OnEncryptionComplete()); | |
| 1673 } | |
| 1674 | |
| 1675 SyncManager::~SyncManager() { | |
| 1676 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1677 delete data_; | |
| 1678 } | |
| 1679 | |
| 1680 void SyncManager::AddObserver(Observer* observer) { | |
| 1681 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1682 data_->AddObserver(observer); | |
| 1683 } | |
| 1684 | |
| 1685 void SyncManager::RemoveObserver(Observer* observer) { | |
| 1686 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1687 data_->RemoveObserver(observer); | |
| 1688 } | |
| 1689 | |
| 1690 void SyncManager::StopSyncingForShutdown(const base::Closure& callback) { | |
| 1691 data_->StopSyncingForShutdown(callback); | |
| 1692 } | |
| 1693 | |
| 1694 void SyncManager::SyncInternal::StopSyncingForShutdown( | |
| 1695 const base::Closure& callback) { | |
| 1696 DVLOG(2) << "StopSyncingForShutdown"; | |
| 1697 if (scheduler()) // May be null in tests. | |
| 1698 scheduler()->RequestStop(callback); | |
| 1699 else | |
| 1700 created_on_loop_->PostTask(FROM_HERE, callback); | |
| 1701 | |
| 1702 if (connection_manager_.get()) | |
| 1703 connection_manager_->TerminateAllIO(); | |
| 1704 } | |
| 1705 | |
| 1706 void SyncManager::ShutdownOnSyncThread() { | |
| 1707 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1708 data_->ShutdownOnSyncThread(); | |
| 1709 } | |
| 1710 | |
| 1711 void SyncManager::SyncInternal::ShutdownOnSyncThread() { | |
| 1712 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1713 | |
| 1714 // Prevent any in-flight method calls from running. Also | |
| 1715 // invalidates |weak_handle_this_| and |change_observer_|. | |
| 1716 weak_ptr_factory_.InvalidateWeakPtrs(); | |
| 1717 js_mutation_event_observer_.InvalidateWeakPtrs(); | |
| 1718 | |
| 1719 scheduler_.reset(); | |
| 1720 session_context_.reset(); | |
| 1721 | |
| 1722 SetJsEventHandler(WeakHandle<JsEventHandler>()); | |
| 1723 RemoveObserver(&js_sync_manager_observer_); | |
| 1724 | |
| 1725 RemoveObserver(&debug_info_event_listener_); | |
| 1726 | |
| 1727 if (sync_notifier_.get()) { | |
| 1728 sync_notifier_->RemoveObserver(this); | |
| 1729 } | |
| 1730 sync_notifier_.reset(); | |
| 1731 | |
| 1732 if (connection_manager_.get()) { | |
| 1733 connection_manager_->RemoveListener(this); | |
| 1734 } | |
| 1735 connection_manager_.reset(); | |
| 1736 | |
| 1737 net::NetworkChangeNotifier::RemoveIPAddressObserver(this); | |
| 1738 observing_ip_address_changes_ = false; | |
| 1739 | |
| 1740 if (initialized_ && directory()) { | |
| 1741 { | |
| 1742 // Cryptographer should only be accessed while holding a | |
| 1743 // transaction. | |
| 1744 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 1745 trans.GetCryptographer()->RemoveObserver(this); | |
| 1746 } | |
| 1747 directory()->SaveChanges(); | |
| 1748 } | |
| 1749 | |
| 1750 share_.directory.reset(); | |
| 1751 | |
| 1752 change_delegate_ = NULL; | |
| 1753 | |
| 1754 initialized_ = false; | |
| 1755 | |
| 1756 // We reset these here, since only now we know they will not be | |
| 1757 // accessed from other threads (since we shut down everything). | |
| 1758 change_observer_.Reset(); | |
| 1759 weak_handle_this_.Reset(); | |
| 1760 } | |
| 1761 | |
| 1762 void SyncManager::SyncInternal::OnIPAddressChanged() { | |
| 1763 DVLOG(1) << "IP address change detected"; | |
| 1764 if (!observing_ip_address_changes_) { | |
| 1765 DVLOG(1) << "IP address change dropped."; | |
| 1766 return; | |
| 1767 } | |
| 1768 | |
| 1769 OnIPAddressChangedImpl(); | |
| 1770 } | |
| 1771 | |
| 1772 void SyncManager::SyncInternal::OnIPAddressChangedImpl() { | |
| 1773 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1774 if (scheduler()) | |
| 1775 scheduler()->OnConnectionStatusChange(); | |
| 1776 } | |
| 1777 | |
| 1778 void SyncManager::SyncInternal::OnServerConnectionEvent( | |
| 1779 const ServerConnectionEvent& event) { | |
| 1780 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1781 if (event.connection_code == | |
| 1782 syncer::HttpResponse::SERVER_CONNECTION_OK) { | |
| 1783 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1784 OnConnectionStatusChange(CONNECTION_OK)); | |
| 1785 } | |
| 1786 | |
| 1787 if (event.connection_code == syncer::HttpResponse::SYNC_AUTH_ERROR) { | |
| 1788 observing_ip_address_changes_ = false; | |
| 1789 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1790 OnConnectionStatusChange(CONNECTION_AUTH_ERROR)); | |
| 1791 } | |
| 1792 | |
| 1793 if (event.connection_code == | |
| 1794 syncer::HttpResponse::SYNC_SERVER_ERROR) { | |
| 1795 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 1796 OnConnectionStatusChange(CONNECTION_SERVER_ERROR)); | |
| 1797 } | |
| 1798 } | |
| 1799 | |
| 1800 void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent( | |
| 1801 ModelTypeSet models_with_changes) { | |
| 1802 // This notification happens immediately after the transaction mutex is | |
| 1803 // released. This allows work to be performed without blocking other threads | |
| 1804 // from acquiring a transaction. | |
| 1805 if (!change_delegate_) | |
| 1806 return; | |
| 1807 | |
| 1808 // Call commit. | |
| 1809 for (ModelTypeSet::Iterator it = models_with_changes.First(); | |
| 1810 it.Good(); it.Inc()) { | |
| 1811 change_delegate_->OnChangesComplete(it.Get()); | |
| 1812 change_observer_.Call( | |
| 1813 FROM_HERE, &SyncManager::ChangeObserver::OnChangesComplete, it.Get()); | |
| 1814 } | |
| 1815 } | |
| 1816 | |
| 1817 ModelTypeSet | |
| 1818 SyncManager::SyncInternal::HandleTransactionEndingChangeEvent( | |
| 1819 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 1820 syncable::BaseTransaction* trans) { | |
| 1821 // This notification happens immediately before a syncable WriteTransaction | |
| 1822 // falls out of scope. It happens while the channel mutex is still held, | |
| 1823 // and while the transaction mutex is held, so it cannot be re-entrant. | |
| 1824 if (!change_delegate_ || ChangeBuffersAreEmpty()) | |
| 1825 return ModelTypeSet(); | |
| 1826 | |
| 1827 // This will continue the WriteTransaction using a read only wrapper. | |
| 1828 // This is the last chance for read to occur in the WriteTransaction | |
| 1829 // that's closing. This special ReadTransaction will not close the | |
| 1830 // underlying transaction. | |
| 1831 ReadTransaction read_trans(GetUserShare(), trans); | |
| 1832 | |
| 1833 ModelTypeSet models_with_changes; | |
| 1834 for (int i = syncer::FIRST_REAL_MODEL_TYPE; | |
| 1835 i < syncer::MODEL_TYPE_COUNT; ++i) { | |
| 1836 const syncer::ModelType type = syncer::ModelTypeFromInt(i); | |
| 1837 if (change_buffers_[type].IsEmpty()) | |
| 1838 continue; | |
| 1839 | |
| 1840 ImmutableChangeRecordList ordered_changes; | |
| 1841 // TODO(akalin): Propagate up the error further (see | |
| 1842 // http://crbug.com/100907). | |
| 1843 CHECK(change_buffers_[type].GetAllChangesInTreeOrder(&read_trans, | |
| 1844 &ordered_changes)); | |
| 1845 if (!ordered_changes.Get().empty()) { | |
| 1846 change_delegate_-> | |
| 1847 OnChangesApplied(type, &read_trans, ordered_changes); | |
| 1848 change_observer_.Call(FROM_HERE, | |
| 1849 &SyncManager::ChangeObserver::OnChangesApplied, | |
| 1850 type, write_transaction_info.Get().id, ordered_changes); | |
| 1851 models_with_changes.Put(type); | |
| 1852 } | |
| 1853 change_buffers_[i].Clear(); | |
| 1854 } | |
| 1855 return models_with_changes; | |
| 1856 } | |
| 1857 | |
| 1858 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi( | |
| 1859 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 1860 syncable::BaseTransaction* trans) { | |
| 1861 if (!scheduler()) { | |
| 1862 return; | |
| 1863 } | |
| 1864 | |
| 1865 // We have been notified about a user action changing a sync model. | |
| 1866 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << | |
| 1867 "CALCULATE_CHANGES called with unapplied old changes."; | |
| 1868 | |
| 1869 // The mutated model type, or UNSPECIFIED if nothing was mutated. | |
| 1870 syncer::ModelTypeSet mutated_model_types; | |
| 1871 | |
| 1872 const syncable::ImmutableEntryKernelMutationMap& mutations = | |
| 1873 write_transaction_info.Get().mutations; | |
| 1874 for (syncable::EntryKernelMutationMap::const_iterator it = | |
| 1875 mutations.Get().begin(); it != mutations.Get().end(); ++it) { | |
| 1876 if (!it->second.mutated.ref(syncable::IS_UNSYNCED)) { | |
| 1877 continue; | |
| 1878 } | |
| 1879 | |
| 1880 syncer::ModelType model_type = | |
| 1881 syncer::GetModelTypeFromSpecifics( | |
| 1882 it->second.mutated.ref(SPECIFICS)); | |
| 1883 if (model_type < syncer::FIRST_REAL_MODEL_TYPE) { | |
| 1884 NOTREACHED() << "Permanent or underspecified item changed via syncapi."; | |
| 1885 continue; | |
| 1886 } | |
| 1887 | |
| 1888 // Found real mutation. | |
| 1889 if (model_type != syncer::UNSPECIFIED) { | |
| 1890 mutated_model_types.Put(model_type); | |
| 1891 } | |
| 1892 } | |
| 1893 | |
| 1894 // Nudge if necessary. | |
| 1895 if (!mutated_model_types.Empty()) { | |
| 1896 if (weak_handle_this_.IsInitialized()) { | |
| 1897 weak_handle_this_.Call(FROM_HERE, | |
| 1898 &SyncInternal::RequestNudgeForDataTypes, | |
| 1899 FROM_HERE, | |
| 1900 mutated_model_types); | |
| 1901 } else { | |
| 1902 NOTREACHED(); | |
| 1903 } | |
| 1904 } | |
| 1905 } | |
| 1906 | |
| 1907 void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id, | |
| 1908 syncer::ModelType type, ChangeReorderBuffer* buffer, | |
| 1909 Cryptographer* cryptographer, const syncable::EntryKernel& original, | |
| 1910 bool existed_before, bool exists_now) { | |
| 1911 // If this is a deletion and the datatype was encrypted, we need to decrypt it | |
| 1912 // and attach it to the buffer. | |
| 1913 if (!exists_now && existed_before) { | |
| 1914 sync_pb::EntitySpecifics original_specifics(original.ref(SPECIFICS)); | |
| 1915 if (type == syncer::PASSWORDS) { | |
| 1916 // Passwords must use their own legacy ExtraPasswordChangeRecordData. | |
| 1917 scoped_ptr<sync_pb::PasswordSpecificsData> data( | |
| 1918 DecryptPasswordSpecifics(original_specifics, cryptographer)); | |
| 1919 if (!data.get()) { | |
| 1920 NOTREACHED(); | |
| 1921 return; | |
| 1922 } | |
| 1923 buffer->SetExtraDataForId(id, new ExtraPasswordChangeRecordData(*data)); | |
| 1924 } else if (original_specifics.has_encrypted()) { | |
| 1925 // All other datatypes can just create a new unencrypted specifics and | |
| 1926 // attach it. | |
| 1927 const sync_pb::EncryptedData& encrypted = original_specifics.encrypted(); | |
| 1928 if (!cryptographer->Decrypt(encrypted, &original_specifics)) { | |
| 1929 NOTREACHED(); | |
| 1930 return; | |
| 1931 } | |
| 1932 } | |
| 1933 buffer->SetSpecificsForId(id, original_specifics); | |
| 1934 } | |
| 1935 } | |
| 1936 | |
| 1937 void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer( | |
| 1938 const ImmutableWriteTransactionInfo& write_transaction_info, | |
| 1939 syncable::BaseTransaction* trans) { | |
| 1940 // We only expect one notification per sync step, so change_buffers_ should | |
| 1941 // contain no pending entries. | |
| 1942 LOG_IF(WARNING, !ChangeBuffersAreEmpty()) << | |
| 1943 "CALCULATE_CHANGES called with unapplied old changes."; | |
| 1944 | |
| 1945 Cryptographer* crypto = directory()->GetCryptographer(trans); | |
| 1946 const syncable::ImmutableEntryKernelMutationMap& mutations = | |
| 1947 write_transaction_info.Get().mutations; | |
| 1948 for (syncable::EntryKernelMutationMap::const_iterator it = | |
| 1949 mutations.Get().begin(); it != mutations.Get().end(); ++it) { | |
| 1950 bool existed_before = !it->second.original.ref(syncable::IS_DEL); | |
| 1951 bool exists_now = !it->second.mutated.ref(syncable::IS_DEL); | |
| 1952 | |
| 1953 // Omit items that aren't associated with a model. | |
| 1954 syncer::ModelType type = | |
| 1955 syncer::GetModelTypeFromSpecifics( | |
| 1956 it->second.mutated.ref(SPECIFICS)); | |
| 1957 if (type < syncer::FIRST_REAL_MODEL_TYPE) | |
| 1958 continue; | |
| 1959 | |
| 1960 int64 handle = it->first; | |
| 1961 if (exists_now && !existed_before) | |
| 1962 change_buffers_[type].PushAddedItem(handle); | |
| 1963 else if (!exists_now && existed_before) | |
| 1964 change_buffers_[type].PushDeletedItem(handle); | |
| 1965 else if (exists_now && existed_before && | |
| 1966 VisiblePropertiesDiffer(it->second, crypto)) { | |
| 1967 change_buffers_[type].PushUpdatedItem( | |
| 1968 handle, VisiblePositionsDiffer(it->second)); | |
| 1969 } | |
| 1970 | |
| 1971 SetExtraChangeRecordData(handle, type, &change_buffers_[type], crypto, | |
| 1972 it->second.original, existed_before, exists_now); | |
| 1973 } | |
| 1974 } | |
| 1975 | |
| 1976 SyncStatus SyncManager::SyncInternal::GetStatus() { | |
| 1977 return allstatus_.status(); | |
| 1978 } | |
| 1979 | |
| 1980 void SyncManager::SyncInternal::RequestNudge( | |
| 1981 const tracked_objects::Location& location) { | |
| 1982 if (scheduler()) { | |
| 1983 scheduler()->ScheduleNudgeAsync( | |
| 1984 TimeDelta::FromMilliseconds(0), syncer::NUDGE_SOURCE_LOCAL, | |
| 1985 ModelTypeSet(), location); | |
| 1986 } | |
| 1987 } | |
| 1988 | |
| 1989 TimeDelta SyncManager::SyncInternal::GetNudgeDelayTimeDelta( | |
| 1990 const ModelType& model_type) { | |
| 1991 return NudgeStrategy::GetNudgeDelayTimeDelta(model_type, this); | |
| 1992 } | |
| 1993 | |
| 1994 void SyncManager::SyncInternal::RequestNudgeForDataTypes( | |
| 1995 const tracked_objects::Location& nudge_location, | |
| 1996 ModelTypeSet types) { | |
| 1997 if (!scheduler()) { | |
| 1998 NOTREACHED(); | |
| 1999 return; | |
| 2000 } | |
| 2001 | |
| 2002 debug_info_event_listener_.OnNudgeFromDatatype(types.First().Get()); | |
| 2003 | |
| 2004 // TODO(lipalani) : Calculate the nudge delay based on all types. | |
| 2005 base::TimeDelta nudge_delay = NudgeStrategy::GetNudgeDelayTimeDelta( | |
| 2006 types.First().Get(), | |
| 2007 this); | |
| 2008 scheduler()->ScheduleNudgeAsync(nudge_delay, | |
| 2009 syncer::NUDGE_SOURCE_LOCAL, | |
| 2010 types, | |
| 2011 nudge_location); | |
| 2012 } | |
| 2013 | |
| 2014 void SyncManager::SyncInternal::OnSyncEngineEvent( | |
| 2015 const SyncEngineEvent& event) { | |
| 2016 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2017 // Only send an event if this is due to a cycle ending and this cycle | |
| 2018 // concludes a canonical "sync" process; that is, based on what is known | |
| 2019 // locally we are "all happy" and up-to-date. There may be new changes on | |
| 2020 // the server, but we'll get them on a subsequent sync. | |
| 2021 // | |
| 2022 // Notifications are sent at the end of every sync cycle, regardless of | |
| 2023 // whether we should sync again. | |
| 2024 if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) { | |
| 2025 { | |
| 2026 // Check to see if we need to notify the frontend that we have newly | |
| 2027 // encrypted types or that we require a passphrase. | |
| 2028 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2029 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 2030 // If we've completed a sync cycle and the cryptographer isn't ready | |
| 2031 // yet, prompt the user for a passphrase. | |
| 2032 if (cryptographer->has_pending_keys()) { | |
| 2033 DVLOG(1) << "OnPassPhraseRequired Sent"; | |
| 2034 sync_pb::EncryptedData pending_keys = cryptographer->GetPendingKeys(); | |
| 2035 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2036 OnPassphraseRequired(syncer::REASON_DECRYPTION, | |
| 2037 pending_keys)); | |
| 2038 } else if (!cryptographer->is_ready() && | |
| 2039 event.snapshot.initial_sync_ended().Has(syncer::NIGORI)) { | |
| 2040 DVLOG(1) << "OnPassphraseRequired sent because cryptographer is not " | |
| 2041 << "ready"; | |
| 2042 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2043 OnPassphraseRequired(syncer::REASON_ENCRYPTION, | |
| 2044 sync_pb::EncryptedData())); | |
| 2045 } | |
| 2046 | |
| 2047 NotifyCryptographerState(cryptographer); | |
| 2048 allstatus_.SetEncryptedTypes(cryptographer->GetEncryptedTypes()); | |
| 2049 } | |
| 2050 | |
| 2051 if (!initialized_) { | |
| 2052 LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not " | |
| 2053 << "initialized"; | |
| 2054 return; | |
| 2055 } | |
| 2056 | |
| 2057 if (!event.snapshot.has_more_to_sync()) { | |
| 2058 // To account for a nigori node arriving with stale/bad data, we ensure | |
| 2059 // that the nigori node is up to date at the end of each cycle. | |
| 2060 WriteTransaction trans(FROM_HERE, GetUserShare()); | |
| 2061 WriteNode nigori_node(&trans); | |
| 2062 if (nigori_node.InitByTagLookup(kNigoriTag) == | |
| 2063 syncer::BaseNode::INIT_OK) { | |
| 2064 Cryptographer* cryptographer = trans.GetCryptographer(); | |
| 2065 UpdateNigoriEncryptionState(cryptographer, &nigori_node); | |
| 2066 } | |
| 2067 | |
| 2068 DVLOG(1) << "Sending OnSyncCycleCompleted"; | |
| 2069 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2070 OnSyncCycleCompleted(event.snapshot)); | |
| 2071 } | |
| 2072 | |
| 2073 // This is here for tests, which are still using p2p notifications. | |
| 2074 // | |
| 2075 // TODO(chron): Consider changing this back to track has_more_to_sync | |
| 2076 // only notify peers if a successful commit has occurred. | |
| 2077 bool is_notifiable_commit = | |
| 2078 (event.snapshot.model_neutral_state().num_successful_commits > 0); | |
| 2079 if (is_notifiable_commit) { | |
| 2080 if (sync_notifier_.get()) { | |
| 2081 const ModelTypeSet changed_types = | |
| 2082 syncer::ModelTypePayloadMapToEnumSet( | |
| 2083 event.snapshot.source().types); | |
| 2084 sync_notifier_->SendNotification(changed_types); | |
| 2085 } else { | |
| 2086 DVLOG(1) << "Not sending notification: sync_notifier_ is NULL"; | |
| 2087 } | |
| 2088 } | |
| 2089 } | |
| 2090 | |
| 2091 if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) { | |
| 2092 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2093 OnStopSyncingPermanently()); | |
| 2094 return; | |
| 2095 } | |
| 2096 | |
| 2097 if (event.what_happened == SyncEngineEvent::UPDATED_TOKEN) { | |
| 2098 FOR_EACH_OBSERVER(SyncManager::Observer, observers_, | |
| 2099 OnUpdatedToken(event.updated_token)); | |
| 2100 return; | |
| 2101 } | |
| 2102 | |
| 2103 if (event.what_happened == SyncEngineEvent::ACTIONABLE_ERROR) { | |
| 2104 FOR_EACH_OBSERVER( | |
| 2105 SyncManager::Observer, observers_, | |
| 2106 OnActionableError( | |
| 2107 event.snapshot.model_neutral_state().sync_protocol_error)); | |
| 2108 return; | |
| 2109 } | |
| 2110 | |
| 2111 } | |
| 2112 | |
| 2113 void SyncManager::SyncInternal::SetJsEventHandler( | |
| 2114 const WeakHandle<JsEventHandler>& event_handler) { | |
| 2115 js_event_handler_ = event_handler; | |
| 2116 js_sync_manager_observer_.SetJsEventHandler(js_event_handler_); | |
| 2117 js_mutation_event_observer_.SetJsEventHandler(js_event_handler_); | |
| 2118 } | |
| 2119 | |
| 2120 void SyncManager::SyncInternal::ProcessJsMessage( | |
| 2121 const std::string& name, const JsArgList& args, | |
| 2122 const WeakHandle<JsReplyHandler>& reply_handler) { | |
| 2123 if (!initialized_) { | |
| 2124 NOTREACHED(); | |
| 2125 return; | |
| 2126 } | |
| 2127 | |
| 2128 if (!reply_handler.IsInitialized()) { | |
| 2129 DVLOG(1) << "Uninitialized reply handler; dropping unknown message " | |
| 2130 << name << " with args " << args.ToString(); | |
| 2131 return; | |
| 2132 } | |
| 2133 | |
| 2134 JsMessageHandler js_message_handler = js_message_handlers_[name]; | |
| 2135 if (js_message_handler.is_null()) { | |
| 2136 DVLOG(1) << "Dropping unknown message " << name | |
| 2137 << " with args " << args.ToString(); | |
| 2138 return; | |
| 2139 } | |
| 2140 | |
| 2141 reply_handler.Call(FROM_HERE, | |
| 2142 &JsReplyHandler::HandleJsReply, | |
| 2143 name, js_message_handler.Run(args)); | |
| 2144 } | |
| 2145 | |
| 2146 void SyncManager::SyncInternal::BindJsMessageHandler( | |
| 2147 const std::string& name, | |
| 2148 UnboundJsMessageHandler unbound_message_handler) { | |
| 2149 js_message_handlers_[name] = | |
| 2150 base::Bind(unbound_message_handler, base::Unretained(this)); | |
| 2151 } | |
| 2152 | |
| 2153 DictionaryValue* SyncManager::SyncInternal::NotificationInfoToValue( | |
| 2154 const NotificationInfoMap& notification_info) { | |
| 2155 DictionaryValue* value = new DictionaryValue(); | |
| 2156 | |
| 2157 for (NotificationInfoMap::const_iterator it = notification_info.begin(); | |
| 2158 it != notification_info.end(); ++it) { | |
| 2159 const std::string& model_type_str = | |
| 2160 syncer::ModelTypeToString(it->first); | |
| 2161 value->Set(model_type_str, it->second.ToValue()); | |
| 2162 } | |
| 2163 | |
| 2164 return value; | |
| 2165 } | |
| 2166 | |
| 2167 JsArgList SyncManager::SyncInternal::GetNotificationState( | |
| 2168 const JsArgList& args) { | |
| 2169 bool notifications_enabled = allstatus_.status().notifications_enabled; | |
| 2170 ListValue return_args; | |
| 2171 return_args.Append(Value::CreateBooleanValue(notifications_enabled)); | |
| 2172 return JsArgList(&return_args); | |
| 2173 } | |
| 2174 | |
| 2175 JsArgList SyncManager::SyncInternal::GetNotificationInfo( | |
| 2176 const JsArgList& args) { | |
| 2177 ListValue return_args; | |
| 2178 return_args.Append(NotificationInfoToValue(notification_info_map_)); | |
| 2179 return JsArgList(&return_args); | |
| 2180 } | |
| 2181 | |
| 2182 JsArgList SyncManager::SyncInternal::GetRootNodeDetails( | |
| 2183 const JsArgList& args) { | |
| 2184 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2185 ReadNode root(&trans); | |
| 2186 root.InitByRootLookup(); | |
| 2187 ListValue return_args; | |
| 2188 return_args.Append(root.GetDetailsAsValue()); | |
| 2189 return JsArgList(&return_args); | |
| 2190 } | |
| 2191 | |
| 2192 JsArgList SyncManager::SyncInternal::GetClientServerTraffic( | |
| 2193 const JsArgList& args) { | |
| 2194 ListValue return_args; | |
| 2195 ListValue* value = traffic_recorder_.ToValue(); | |
| 2196 if (value != NULL) | |
| 2197 return_args.Append(value); | |
| 2198 return JsArgList(&return_args); | |
| 2199 } | |
| 2200 | |
| 2201 namespace { | |
| 2202 | |
| 2203 int64 GetId(const ListValue& ids, int i) { | |
| 2204 std::string id_str; | |
| 2205 if (!ids.GetString(i, &id_str)) { | |
| 2206 return kInvalidId; | |
| 2207 } | |
| 2208 int64 id = kInvalidId; | |
| 2209 if (!base::StringToInt64(id_str, &id)) { | |
| 2210 return kInvalidId; | |
| 2211 } | |
| 2212 return id; | |
| 2213 } | |
| 2214 | |
| 2215 JsArgList GetNodeInfoById(const JsArgList& args, | |
| 2216 UserShare* user_share, | |
| 2217 DictionaryValue* (BaseNode::*info_getter)() const) { | |
| 2218 CHECK(info_getter); | |
| 2219 ListValue return_args; | |
| 2220 ListValue* node_summaries = new ListValue(); | |
| 2221 return_args.Append(node_summaries); | |
| 2222 ListValue* id_list = NULL; | |
| 2223 ReadTransaction trans(FROM_HERE, user_share); | |
| 2224 if (args.Get().GetList(0, &id_list)) { | |
| 2225 CHECK(id_list); | |
| 2226 for (size_t i = 0; i < id_list->GetSize(); ++i) { | |
| 2227 int64 id = GetId(*id_list, i); | |
| 2228 if (id == kInvalidId) { | |
| 2229 continue; | |
| 2230 } | |
| 2231 ReadNode node(&trans); | |
| 2232 if (node.InitByIdLookup(id) != syncer::BaseNode::INIT_OK) { | |
| 2233 continue; | |
| 2234 } | |
| 2235 node_summaries->Append((node.*info_getter)()); | |
| 2236 } | |
| 2237 } | |
| 2238 return JsArgList(&return_args); | |
| 2239 } | |
| 2240 | |
| 2241 } // namespace | |
| 2242 | |
| 2243 JsArgList SyncManager::SyncInternal::GetNodeSummariesById( | |
| 2244 const JsArgList& args) { | |
| 2245 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetSummaryAsValue); | |
| 2246 } | |
| 2247 | |
| 2248 JsArgList SyncManager::SyncInternal::GetNodeDetailsById( | |
| 2249 const JsArgList& args) { | |
| 2250 return GetNodeInfoById(args, GetUserShare(), &BaseNode::GetDetailsAsValue); | |
| 2251 } | |
| 2252 | |
| 2253 JsArgList SyncManager::SyncInternal::GetAllNodes( | |
| 2254 const JsArgList& args) { | |
| 2255 ListValue return_args; | |
| 2256 ListValue* result = new ListValue(); | |
| 2257 return_args.Append(result); | |
| 2258 | |
| 2259 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2260 std::vector<const syncable::EntryKernel*> entry_kernels; | |
| 2261 trans.GetDirectory()->GetAllEntryKernels(trans.GetWrappedTrans(), | |
| 2262 &entry_kernels); | |
| 2263 | |
| 2264 for (std::vector<const syncable::EntryKernel*>::const_iterator it = | |
| 2265 entry_kernels.begin(); it != entry_kernels.end(); ++it) { | |
| 2266 result->Append((*it)->ToValue()); | |
| 2267 } | |
| 2268 | |
| 2269 return JsArgList(&return_args); | |
| 2270 } | |
| 2271 | |
| 2272 JsArgList SyncManager::SyncInternal::GetChildNodeIds( | |
| 2273 const JsArgList& args) { | |
| 2274 ListValue return_args; | |
| 2275 ListValue* child_ids = new ListValue(); | |
| 2276 return_args.Append(child_ids); | |
| 2277 int64 id = GetId(args.Get(), 0); | |
| 2278 if (id != kInvalidId) { | |
| 2279 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2280 syncable::Directory::ChildHandles child_handles; | |
| 2281 trans.GetDirectory()->GetChildHandlesByHandle(trans.GetWrappedTrans(), | |
| 2282 id, &child_handles); | |
| 2283 for (syncable::Directory::ChildHandles::const_iterator it = | |
| 2284 child_handles.begin(); it != child_handles.end(); ++it) { | |
| 2285 child_ids->Append(Value::CreateStringValue( | |
| 2286 base::Int64ToString(*it))); | |
| 2287 } | |
| 2288 } | |
| 2289 return JsArgList(&return_args); | |
| 2290 } | |
| 2291 | |
| 2292 void SyncManager::SyncInternal::OnEncryptedTypesChanged( | |
| 2293 syncer::ModelTypeSet encrypted_types, | |
| 2294 bool encrypt_everything) { | |
| 2295 // NOTE: We're in a transaction. | |
| 2296 FOR_EACH_OBSERVER( | |
| 2297 SyncManager::Observer, observers_, | |
| 2298 OnEncryptedTypesChanged(encrypted_types, encrypt_everything)); | |
| 2299 } | |
| 2300 | |
| 2301 void SyncManager::SyncInternal::UpdateNotificationInfo( | |
| 2302 const syncer::ModelTypePayloadMap& type_payloads) { | |
| 2303 for (syncer::ModelTypePayloadMap::const_iterator it = type_payloads.begin(); | |
| 2304 it != type_payloads.end(); ++it) { | |
| 2305 NotificationInfo* info = ¬ification_info_map_[it->first]; | |
| 2306 info->total_count++; | |
| 2307 info->payload = it->second; | |
| 2308 } | |
| 2309 } | |
| 2310 | |
| 2311 void SyncManager::SyncInternal::OnNotificationsEnabled() { | |
| 2312 DVLOG(1) << "Notifications enabled"; | |
| 2313 allstatus_.SetNotificationsEnabled(true); | |
| 2314 if (scheduler()) { | |
| 2315 scheduler()->set_notifications_enabled(true); | |
| 2316 } | |
| 2317 // TODO(akalin): Separate onNotificationStateChange into | |
| 2318 // enabled/disabled events. | |
| 2319 if (js_event_handler_.IsInitialized()) { | |
| 2320 DictionaryValue details; | |
| 2321 details.Set("enabled", Value::CreateBooleanValue(true)); | |
| 2322 js_event_handler_.Call(FROM_HERE, | |
| 2323 &JsEventHandler::HandleJsEvent, | |
| 2324 "onNotificationStateChange", | |
| 2325 JsEventDetails(&details)); | |
| 2326 } | |
| 2327 } | |
| 2328 | |
| 2329 void SyncManager::SyncInternal::OnNotificationsDisabled( | |
| 2330 syncer::NotificationsDisabledReason reason) { | |
| 2331 DVLOG(1) << "Notifications disabled with reason " | |
| 2332 << syncer::NotificationsDisabledReasonToString(reason); | |
| 2333 allstatus_.SetNotificationsEnabled(false); | |
| 2334 if (scheduler()) { | |
| 2335 scheduler()->set_notifications_enabled(false); | |
| 2336 } | |
| 2337 if (js_event_handler_.IsInitialized()) { | |
| 2338 DictionaryValue details; | |
| 2339 details.Set("enabled", Value::CreateBooleanValue(false)); | |
| 2340 js_event_handler_.Call(FROM_HERE, | |
| 2341 &JsEventHandler::HandleJsEvent, | |
| 2342 "onNotificationStateChange", | |
| 2343 JsEventDetails(&details)); | |
| 2344 } | |
| 2345 // TODO(akalin): Treat a CREDENTIALS_REJECTED state as an auth | |
| 2346 // error. | |
| 2347 } | |
| 2348 | |
| 2349 void SyncManager::SyncInternal::OnIncomingNotification( | |
| 2350 const syncer::ModelTypePayloadMap& type_payloads, | |
| 2351 syncer::IncomingNotificationSource source) { | |
| 2352 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2353 if (source == syncer::LOCAL_NOTIFICATION) { | |
| 2354 if (scheduler()) { | |
| 2355 scheduler()->ScheduleNudgeWithPayloadsAsync( | |
| 2356 TimeDelta::FromMilliseconds(kSyncRefreshDelayMsec), | |
| 2357 syncer::NUDGE_SOURCE_LOCAL_REFRESH, | |
| 2358 type_payloads, FROM_HERE); | |
| 2359 } | |
| 2360 } else if (!type_payloads.empty()) { | |
| 2361 if (scheduler()) { | |
| 2362 scheduler()->ScheduleNudgeWithPayloadsAsync( | |
| 2363 TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec), | |
| 2364 syncer::NUDGE_SOURCE_NOTIFICATION, | |
| 2365 type_payloads, FROM_HERE); | |
| 2366 } | |
| 2367 allstatus_.IncrementNotificationsReceived(); | |
| 2368 UpdateNotificationInfo(type_payloads); | |
| 2369 debug_info_event_listener_.OnIncomingNotification(type_payloads); | |
| 2370 } else { | |
| 2371 LOG(WARNING) << "Sync received notification without any type information."; | |
| 2372 } | |
| 2373 | |
| 2374 if (js_event_handler_.IsInitialized()) { | |
| 2375 DictionaryValue details; | |
| 2376 ListValue* changed_types = new ListValue(); | |
| 2377 details.Set("changedTypes", changed_types); | |
| 2378 for (syncer::ModelTypePayloadMap::const_iterator | |
| 2379 it = type_payloads.begin(); | |
| 2380 it != type_payloads.end(); ++it) { | |
| 2381 const std::string& model_type_str = | |
| 2382 syncer::ModelTypeToString(it->first); | |
| 2383 changed_types->Append(Value::CreateStringValue(model_type_str)); | |
| 2384 } | |
| 2385 details.SetString("source", (source == syncer::LOCAL_NOTIFICATION) ? | |
| 2386 "LOCAL_NOTIFICATION" : "REMOTE_NOTIFICATION"); | |
| 2387 js_event_handler_.Call(FROM_HERE, | |
| 2388 &JsEventHandler::HandleJsEvent, | |
| 2389 "onIncomingNotification", | |
| 2390 JsEventDetails(&details)); | |
| 2391 } | |
| 2392 } | |
| 2393 | |
| 2394 void SyncManager::SyncInternal::AddObserver( | |
| 2395 SyncManager::Observer* observer) { | |
| 2396 observers_.AddObserver(observer); | |
| 2397 } | |
| 2398 | |
| 2399 void SyncManager::SyncInternal::RemoveObserver( | |
| 2400 SyncManager::Observer* observer) { | |
| 2401 observers_.RemoveObserver(observer); | |
| 2402 } | |
| 2403 | |
| 2404 void SyncManager::SyncInternal::SetSyncSchedulerForTest( | |
| 2405 scoped_ptr<SyncScheduler> sync_scheduler) { | |
| 2406 scheduler_ = sync_scheduler.Pass(); | |
| 2407 } | |
| 2408 | |
| 2409 SyncStatus SyncManager::GetDetailedStatus() const { | |
| 2410 return data_->GetStatus(); | |
| 2411 } | |
| 2412 | |
| 2413 void SyncManager::SaveChanges() { | |
| 2414 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2415 data_->SaveChanges(); | |
| 2416 } | |
| 2417 | |
| 2418 void SyncManager::SyncInternal::SaveChanges() { | |
| 2419 directory()->SaveChanges(); | |
| 2420 } | |
| 2421 | |
| 2422 UserShare* SyncManager::GetUserShare() const { | |
| 2423 return data_->GetUserShare(); | |
| 2424 } | |
| 2425 | |
| 2426 void SyncManager::RefreshNigori(const std::string& chrome_version, | |
| 2427 const base::Closure& done_callback) { | |
| 2428 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2429 data_->UpdateCryptographerAndNigori( | |
| 2430 chrome_version, | |
| 2431 done_callback); | |
| 2432 } | |
| 2433 | |
| 2434 TimeDelta SyncManager::GetNudgeDelayTimeDelta( | |
| 2435 const ModelType& model_type) { | |
| 2436 return data_->GetNudgeDelayTimeDelta(model_type); | |
| 2437 } | |
| 2438 | |
| 2439 void SyncManager::SetSyncSchedulerForTest(scoped_ptr<SyncScheduler> scheduler) { | |
| 2440 data_->SetSyncSchedulerForTest(scheduler.Pass()); | |
| 2441 } | |
| 2442 | |
| 2443 syncer::ModelTypeSet SyncManager::GetEncryptedDataTypesForTest() const { | |
| 2444 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2445 return GetEncryptedTypes(&trans); | |
| 2446 } | |
| 2447 | |
| 2448 bool SyncManager::ReceivedExperiment(syncer::Experiments* experiments) | |
| 2449 const { | |
| 2450 ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2451 ReadNode node(&trans); | |
| 2452 if (node.InitByTagLookup(kNigoriTag) != syncer::BaseNode::INIT_OK) { | |
| 2453 DVLOG(1) << "Couldn't find Nigori node."; | |
| 2454 return false; | |
| 2455 } | |
| 2456 bool found_experiment = false; | |
| 2457 if (node.GetNigoriSpecifics().sync_tab_favicons()) { | |
| 2458 experiments->sync_tab_favicons = true; | |
| 2459 found_experiment = true; | |
| 2460 } | |
| 2461 return found_experiment; | |
| 2462 } | |
| 2463 | |
| 2464 bool SyncManager::HasUnsyncedItems() const { | |
| 2465 syncer::ReadTransaction trans(FROM_HERE, GetUserShare()); | |
| 2466 return (trans.GetWrappedTrans()->directory()->unsynced_entity_count() != 0); | |
| 2467 } | |
| 2468 | |
| 2469 void SyncManager::SimulateEnableNotificationsForTest() { | |
| 2470 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2471 data_->OnNotificationsEnabled(); | |
| 2472 } | |
| 2473 | |
| 2474 void SyncManager::SimulateDisableNotificationsForTest(int reason) { | |
| 2475 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2476 data_->OnNotificationsDisabled( | |
| 2477 static_cast<syncer::NotificationsDisabledReason>(reason)); | |
| 2478 } | |
| 2479 | |
| 2480 void SyncManager::TriggerOnIncomingNotificationForTest( | |
| 2481 ModelTypeSet model_types) { | |
| 2482 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 2483 syncer::ModelTypePayloadMap model_types_with_payloads = | |
| 2484 syncer::ModelTypePayloadMapFromEnumSet(model_types, | |
| 2485 std::string()); | |
| 2486 | |
| 2487 data_->OnIncomingNotification(model_types_with_payloads, | |
| 2488 syncer::REMOTE_NOTIFICATION); | |
| 2489 } | |
| 2490 | |
| 2491 const char* ConnectionStatusToString(ConnectionStatus status) { | |
| 2492 switch (status) { | |
| 2493 case CONNECTION_OK: | |
| 2494 return "CONNECTION_OK"; | |
| 2495 case CONNECTION_AUTH_ERROR: | |
| 2496 return "CONNECTION_AUTH_ERROR"; | |
| 2497 case CONNECTION_SERVER_ERROR: | |
| 2498 return "CONNECTION_SERVER_ERROR"; | |
| 2499 default: | |
| 2500 NOTREACHED(); | |
| 2501 return "INVALID_CONNECTION_STATUS"; | |
| 2502 } | |
| 2503 } | |
| 2504 | |
| 2505 // Helper function that converts a PassphraseRequiredReason value to a string. | |
| 2506 const char* PassphraseRequiredReasonToString( | |
| 2507 PassphraseRequiredReason reason) { | |
| 2508 switch (reason) { | |
| 2509 case REASON_PASSPHRASE_NOT_REQUIRED: | |
| 2510 return "REASON_PASSPHRASE_NOT_REQUIRED"; | |
| 2511 case REASON_ENCRYPTION: | |
| 2512 return "REASON_ENCRYPTION"; | |
| 2513 case REASON_DECRYPTION: | |
| 2514 return "REASON_DECRYPTION"; | |
| 2515 default: | |
| 2516 NOTREACHED(); | |
| 2517 return "INVALID_REASON"; | |
| 2518 } | |
| 2519 } | |
| 2520 | |
| 2521 // Helper function to determine if initial sync had ended for types. | |
| 2522 bool InitialSyncEndedForTypes(syncer::ModelTypeSet types, | |
| 2523 syncer::UserShare* share) { | |
| 2524 for (syncer::ModelTypeSet::Iterator i = types.First(); | |
| 2525 i.Good(); i.Inc()) { | |
| 2526 if (!share->directory->initial_sync_ended_for_type(i.Get())) | |
| 2527 return false; | |
| 2528 } | |
| 2529 return true; | |
| 2530 } | |
| 2531 | |
| 2532 } // namespace syncer | |
| OLD | NEW |