1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sync/engine/download.h"
9 #include "base/command_line.h"
10 #include "sync/engine/syncer.h"
11 #include "sync/engine/syncer_proto_util.h"
12 #include "sync/sessions/nudge_tracker.h"
13 #include "sync/syncable/directory.h"
14 #include "sync/syncable/nigori_handler.h"
15 #include "sync/syncable/syncable_read_transaction.h"
19 using sessions::StatusController
;
20 using sessions::SyncSession
;
21 using sessions::SyncSessionContext
;
28 typedef std::map
<ModelType
, size_t> TypeToIndexMap
;
30 SyncerError
HandleGetEncryptionKeyResponse(
31 const sync_pb::ClientToServerResponse
& update_response
,
32 syncable::Directory
* dir
) {
34 if (update_response
.get_updates().encryption_keys_size() == 0) {
35 LOG(ERROR
) << "Failed to receive encryption key from server.";
36 return SERVER_RESPONSE_VALIDATION_FAILED
;
38 syncable::ReadTransaction
trans(FROM_HERE
, dir
);
39 syncable::NigoriHandler
* nigori_handler
= dir
->GetNigoriHandler();
40 success
= nigori_handler
->SetKeystoreKeys(
41 update_response
.get_updates().encryption_keys(),
44 DVLOG(1) << "GetUpdates returned "
45 << update_response
.get_updates().encryption_keys_size()
46 << "encryption keys. Nigori keystore key "
47 << (success
? "" : "not ") << "updated.";
48 return (success
? SYNCER_OK
: SERVER_RESPONSE_VALIDATION_FAILED
);
51 sync_pb::SyncEnums::GetUpdatesOrigin
ConvertConfigureSourceToOrigin(
52 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source
) {
55 case sync_pb::GetUpdatesCallerInfo::NEWLY_SUPPORTED_DATATYPE
:
56 return sync_pb::SyncEnums::NEWLY_SUPPORTED_DATATYPE
;
57 case sync_pb::GetUpdatesCallerInfo::MIGRATION
:
58 return sync_pb::SyncEnums::MIGRATION
;
59 case sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
:
60 return sync_pb::SyncEnums::RECONFIGURATION
;
61 case sync_pb::GetUpdatesCallerInfo::NEW_CLIENT
:
62 return sync_pb::SyncEnums::NEW_CLIENT
;
65 return sync_pb::SyncEnums::UNKNOWN_ORIGIN
;
69 bool ShouldRequestEncryptionKey(
70 SyncSessionContext
* context
) {
71 bool need_encryption_key
= false;
72 if (context
->keystore_encryption_enabled()) {
73 syncable::Directory
* dir
= context
->directory();
74 syncable::ReadTransaction
trans(FROM_HERE
, dir
);
75 syncable::NigoriHandler
* nigori_handler
= dir
->GetNigoriHandler();
76 need_encryption_key
= nigori_handler
->NeedKeystoreKey(&trans
);
78 return need_encryption_key
;
81 void InitDownloadUpdatesContext(
83 bool create_mobile_bookmarks_folder
,
84 sync_pb::ClientToServerMessage
* message
) {
85 message
->set_share(session
->context()->account_name());
86 message
->set_message_contents(sync_pb::ClientToServerMessage::GET_UPDATES
);
88 sync_pb::GetUpdatesMessage
* get_updates
= message
->mutable_get_updates();
90 // We want folders for our associated types, always. If we were to set
91 // this to false, the server would send just the non-container items
92 // (e.g. Bookmark URLs but not their containing folders).
93 get_updates
->set_fetch_folders(true);
95 get_updates
->set_create_mobile_bookmarks_folder(
96 create_mobile_bookmarks_folder
);
97 bool need_encryption_key
= ShouldRequestEncryptionKey(session
->context());
98 get_updates
->set_need_encryption_key(need_encryption_key
);
100 // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
101 get_updates
->mutable_caller_info()->set_notifications_enabled(
102 session
->context()->notifications_enabled());
107 void BuildNormalDownloadUpdates(
108 SyncSession
* session
,
109 GetUpdatesProcessor
* get_updates_processor
,
110 bool create_mobile_bookmarks_folder
,
111 ModelTypeSet request_types
,
112 const sessions::NudgeTracker
& nudge_tracker
,
113 sync_pb::ClientToServerMessage
* client_to_server_message
) {
114 // Request updates for all requested types.
115 DVLOG(1) << "Getting updates for types "
116 << ModelTypeSetToString(request_types
);
117 DCHECK(!request_types
.Empty());
119 InitDownloadUpdatesContext(
121 create_mobile_bookmarks_folder
,
122 client_to_server_message
);
124 BuildNormalDownloadUpdatesImpl(
125 Intersection(request_types
, ProtocolTypes()),
126 get_updates_processor
,
128 client_to_server_message
->mutable_get_updates());
131 void BuildNormalDownloadUpdatesImpl(
132 ModelTypeSet proto_request_types
,
133 GetUpdatesProcessor
* get_updates_processor
,
134 const sessions::NudgeTracker
& nudge_tracker
,
135 sync_pb::GetUpdatesMessage
* get_updates
) {
136 DCHECK(!proto_request_types
.Empty());
138 // Get progress markers and other data for requested types.
139 get_updates_processor
->PrepareGetUpdates(proto_request_types
, get_updates
);
141 // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
142 get_updates
->mutable_caller_info()->set_source(
143 nudge_tracker
.updates_source());
145 // Set the new and improved version of source, too.
146 get_updates
->set_get_updates_origin(sync_pb::SyncEnums::GU_TRIGGER
);
147 get_updates
->set_is_retry(
148 nudge_tracker
.IsRetryRequired(base::TimeTicks::Now()));
150 // Fill in the notification hints.
151 for (int i
= 0; i
< get_updates
->from_progress_marker_size(); ++i
) {
152 sync_pb::DataTypeProgressMarker
* progress_marker
=
153 get_updates
->mutable_from_progress_marker(i
);
154 ModelType type
= GetModelTypeFromSpecificsFieldNumber(
155 progress_marker
->data_type_id());
157 DCHECK(!nudge_tracker
.IsTypeThrottled(type
))
158 << "Throttled types should have been removed from the request_types.";
160 nudge_tracker
.SetLegacyNotificationHint(type
, progress_marker
);
161 nudge_tracker
.FillProtoMessage(
163 progress_marker
->mutable_get_update_triggers());
167 void BuildDownloadUpdatesForConfigure(
168 SyncSession
* session
,
169 GetUpdatesProcessor
* get_updates_processor
,
170 bool create_mobile_bookmarks_folder
,
171 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source
,
172 ModelTypeSet request_types
,
173 sync_pb::ClientToServerMessage
* client_to_server_message
) {
174 // Request updates for all enabled types.
175 DVLOG(1) << "Initial download for types "
176 << ModelTypeSetToString(request_types
);
178 InitDownloadUpdatesContext(
180 create_mobile_bookmarks_folder
,
181 client_to_server_message
);
182 BuildDownloadUpdatesForConfigureImpl(
183 Intersection(request_types
, ProtocolTypes()),
184 get_updates_processor
,
186 client_to_server_message
->mutable_get_updates());
189 void BuildDownloadUpdatesForConfigureImpl(
190 ModelTypeSet proto_request_types
,
191 GetUpdatesProcessor
* get_updates_processor
,
192 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source
,
193 sync_pb::GetUpdatesMessage
* get_updates
) {
194 DCHECK(!proto_request_types
.Empty());
196 // Get progress markers and other data for requested types.
197 get_updates_processor
->PrepareGetUpdates(proto_request_types
, get_updates
);
199 // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
200 get_updates
->mutable_caller_info()->set_source(source
);
202 // Set the new and improved version of source, too.
203 sync_pb::SyncEnums::GetUpdatesOrigin origin
=
204 ConvertConfigureSourceToOrigin(source
);
205 get_updates
->set_get_updates_origin(origin
);
208 void BuildDownloadUpdatesForPoll(
209 SyncSession
* session
,
210 GetUpdatesProcessor
* get_updates_processor
,
211 bool create_mobile_bookmarks_folder
,
212 ModelTypeSet request_types
,
213 sync_pb::ClientToServerMessage
* client_to_server_message
) {
214 DVLOG(1) << "Polling for types "
215 << ModelTypeSetToString(request_types
);
217 InitDownloadUpdatesContext(
219 create_mobile_bookmarks_folder
,
220 client_to_server_message
);
221 BuildDownloadUpdatesForPollImpl(
222 Intersection(request_types
, ProtocolTypes()),
223 get_updates_processor
,
224 client_to_server_message
->mutable_get_updates());
227 void BuildDownloadUpdatesForPollImpl(
228 ModelTypeSet proto_request_types
,
229 GetUpdatesProcessor
* get_updates_processor
,
230 sync_pb::GetUpdatesMessage
* get_updates
) {
231 DCHECK(!proto_request_types
.Empty());
233 // Get progress markers and other data for requested types.
234 get_updates_processor
->PrepareGetUpdates(proto_request_types
, get_updates
);
236 // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
237 get_updates
->mutable_caller_info()->set_source(
238 sync_pb::GetUpdatesCallerInfo::PERIODIC
);
240 // Set the new and improved version of source, too.
241 get_updates
->set_get_updates_origin(sync_pb::SyncEnums::PERIODIC
);
244 void BuildDownloadUpdatesForRetry(
245 SyncSession
* session
,
246 GetUpdatesProcessor
* get_updates_processor
,
247 bool create_mobile_bookmarks_folder
,
248 ModelTypeSet request_types
,
249 sync_pb::ClientToServerMessage
* client_to_server_message
) {
250 DVLOG(1) << "Retrying for types "
251 << ModelTypeSetToString(request_types
);
253 InitDownloadUpdatesContext(
255 create_mobile_bookmarks_folder
,
256 client_to_server_message
);
257 BuildDownloadUpdatesForRetryImpl(
258 Intersection(request_types
, ProtocolTypes()),
259 get_updates_processor
,
260 client_to_server_message
->mutable_get_updates());
263 void BuildDownloadUpdatesForRetryImpl(
264 ModelTypeSet proto_request_types
,
265 GetUpdatesProcessor
* get_updates_processor
,
266 sync_pb::GetUpdatesMessage
* get_updates
) {
267 DCHECK(!proto_request_types
.Empty());
269 get_updates_processor
->PrepareGetUpdates(proto_request_types
, get_updates
);
271 // Set legacy GetUpdatesMessage.GetUpdatesCallerInfo information.
272 get_updates
->mutable_caller_info()->set_source(
273 sync_pb::GetUpdatesCallerInfo::RETRY
);
275 // Set the new and improved version of source, too.
276 get_updates
->set_get_updates_origin(sync_pb::SyncEnums::RETRY
);
277 get_updates
->set_is_retry(true);
280 SyncerError
ExecuteDownloadUpdates(
281 ModelTypeSet request_types
,
282 SyncSession
* session
,
283 GetUpdatesProcessor
* get_updates_processor
,
284 sync_pb::ClientToServerMessage
* msg
) {
285 sync_pb::ClientToServerResponse update_response
;
286 StatusController
* status
= session
->mutable_status_controller();
287 bool need_encryption_key
= ShouldRequestEncryptionKey(session
->context());
289 if (session
->context()->debug_info_getter()) {
290 sync_pb::DebugInfo
* debug_info
= msg
->mutable_debug_info();
291 CopyClientDebugInfo(session
->context()->debug_info_getter(), debug_info
);
294 SyncerError result
= SyncerProtoUtil::PostClientToServerMessage(
299 DVLOG(2) << SyncerProtoUtil::ClientToServerResponseDebugString(
302 if (result
!= SYNCER_OK
) {
303 LOG(ERROR
) << "PostClientToServerMessage() failed during GetUpdates";
307 DVLOG(1) << "GetUpdates "
308 << " returned " << update_response
.get_updates().entries_size()
309 << " updates and indicated "
310 << update_response
.get_updates().changes_remaining()
311 << " updates left on server.";
313 if (session
->context()->debug_info_getter()) {
314 // Clear debug info now that we have successfully sent it to the server.
315 DVLOG(1) << "Clearing client debug info.";
316 session
->context()->debug_info_getter()->ClearDebugInfo();
319 if (need_encryption_key
||
320 update_response
.get_updates().encryption_keys_size() > 0) {
321 syncable::Directory
* dir
= session
->context()->directory();
322 status
->set_last_get_key_result(
323 HandleGetEncryptionKeyResponse(update_response
, dir
));
326 const ModelTypeSet proto_request_types
=
327 Intersection(request_types
, ProtocolTypes());
329 return ProcessResponse(update_response
.get_updates(),
331 get_updates_processor
,
335 SyncerError
ProcessResponse(
336 const sync_pb::GetUpdatesResponse
& gu_response
,
337 ModelTypeSet proto_request_types
,
338 GetUpdatesProcessor
* get_updates_processor
,
339 StatusController
* status
) {
340 status
->increment_num_updates_downloaded_by(gu_response
.entries_size());
342 // The changes remaining field is used to prevent the client from looping. If
343 // that field is being set incorrectly, we're in big trouble.
344 if (!gu_response
.has_changes_remaining()) {
345 return SERVER_RESPONSE_VALIDATION_FAILED
;
347 status
->set_num_server_changes_remaining(gu_response
.changes_remaining());
350 if (!get_updates_processor
->ProcessGetUpdatesResponse(proto_request_types
,
353 return SERVER_RESPONSE_VALIDATION_FAILED
;
356 if (gu_response
.changes_remaining() == 0) {
359 return SERVER_MORE_TO_DOWNLOAD
;
363 void CopyClientDebugInfo(
364 sessions::DebugInfoGetter
* debug_info_getter
,
365 sync_pb::DebugInfo
* debug_info
) {
366 DVLOG(1) << "Copying client debug info to send.";
367 debug_info_getter
->GetDebugInfo(debug_info
);
370 } // namespace download
372 } // namespace syncer