OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 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. | |
4 | |
5 #include "chrome/browser/invalidation_service.h" | |
6 | |
7 #include "base/base64.h" | |
8 #include "base/command_line.h" | |
9 #include "base/rand_util.h" | |
10 #include "chrome/browser/profiles/profile.h" | |
11 #include "chrome/browser/signin/signin_manager.h" | |
12 #include "chrome/browser/signin/token_service.h" | |
13 #include "chrome/common/chrome_notification_types.h" | |
14 #include "chrome/common/chrome_switches.h" | |
15 #include "content/public/browser/notification_service.h" | |
16 #include "google_apis/gaia/gaia_constants.h" | |
17 #include "google_apis/gaia/google_service_auth_error.h" | |
18 #include "sync/notifier/invalidator.h" | |
19 #include "sync/notifier/invalidator_state.h" | |
20 #include "sync/notifier/non_blocking_invalidator.h" | |
21 | |
22 namespace { | |
23 // Parses the given command line for notifier options. | |
24 notifier::NotifierOptions ParseNotifierOptions( | |
25 const CommandLine& command_line, | |
26 const scoped_refptr<net::URLRequestContextGetter>& | |
27 request_context_getter) { | |
28 notifier::NotifierOptions notifier_options; | |
29 notifier_options.request_context_getter = request_context_getter; | |
30 | |
31 if (command_line.HasSwitch(switches::kSyncNotificationHostPort)) { | |
32 notifier_options.xmpp_host_port = | |
33 net::HostPortPair::FromString( | |
34 command_line.GetSwitchValueASCII( | |
35 switches::kSyncNotificationHostPort)); | |
36 DVLOG(1) << "Using " << notifier_options.xmpp_host_port.ToString() | |
37 << " for test sync notification server."; | |
38 } | |
39 | |
40 notifier_options.try_ssltcp_first = | |
41 command_line.HasSwitch(switches::kSyncTrySsltcpFirstForXmpp); | |
42 DVLOG_IF(1, notifier_options.try_ssltcp_first) | |
43 << "Trying SSL/TCP port before XMPP port for notifications."; | |
44 | |
45 notifier_options.invalidate_xmpp_login = | |
46 command_line.HasSwitch(switches::kSyncInvalidateXmppLogin); | |
47 DVLOG_IF(1, notifier_options.invalidate_xmpp_login) | |
48 << "Invalidating sync XMPP login."; | |
49 | |
50 notifier_options.allow_insecure_connection = | |
51 command_line.HasSwitch(switches::kSyncAllowInsecureXmppConnection); | |
52 DVLOG_IF(1, notifier_options.allow_insecure_connection) | |
53 << "Allowing insecure XMPP connections."; | |
54 | |
55 if (command_line.HasSwitch(switches::kSyncNotificationMethod)) { | |
56 const std::string notification_method_str( | |
57 command_line.GetSwitchValueASCII(switches::kSyncNotificationMethod)); | |
58 notifier_options.notification_method = | |
59 notifier::StringToNotificationMethod(notification_method_str); | |
60 } | |
61 | |
62 return notifier_options; | |
63 } | |
64 | |
65 std::string GenerateInvalidatorClientId() { | |
66 // Generate a GUID with 128 bits worth of base64-encoded randomness. | |
67 // This format is similar to that of sync's cache_guid. | |
68 const int kGuidBytes = 128 / 8; | |
69 std::string guid; | |
70 base::Base64Encode(base::RandBytesAsString(kGuidBytes), &guid); | |
71 return guid; | |
72 } | |
73 | |
74 } // namespace | |
75 | |
76 InvalidationService::InvalidationService(SigninManager* signin, | |
77 TokenService* token_service) | |
78 : signin_manager_(signin), | |
79 token_service_(token_service), | |
80 invalidator_registrar_(new syncer::InvalidatorRegistrar()) { } | |
81 | |
82 InvalidationService::~InvalidationService() { | |
83 DCHECK(CalledOnValidThread()); | |
84 } | |
85 | |
86 void InvalidationService::Init(Profile* profile) { | |
87 DCHECK(CalledOnValidThread()); | |
88 | |
89 profile_ = profile; | |
90 | |
91 invalidator_storage_.reset( | |
92 new browser_sync::InvalidatorStorage(profile_->GetPrefs())); | |
93 if (invalidator_storage_->GetInvalidatorClientId().empty()) { | |
94 // This also clears any existing state. We can't reuse old invalidator | |
95 // state with the new ID anyway. | |
96 invalidator_storage_->SetInvalidatorClientId( | |
97 GenerateInvalidatorClientId()); | |
98 } | |
99 | |
100 if (IsReadyToStart()) { | |
101 Start(); | |
102 } | |
103 | |
104 notification_registrar_.Add(this, | |
105 chrome::NOTIFICATION_TOKEN_AVAILABLE, | |
106 content::Source<TokenService>(token_service_)); | |
107 notification_registrar_.Add(this, | |
108 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, | |
109 content::NotificationService::AllSources()); | |
110 } | |
111 | |
112 void InvalidationService::RegisterInvalidationHandler( | |
113 syncer::InvalidationHandler* handler) { | |
114 DCHECK(CalledOnValidThread()); | |
115 DVLOG(2) << "Registering"; | |
116 invalidator_registrar_->RegisterHandler(handler); | |
117 } | |
118 | |
119 void InvalidationService::UpdateRegisteredInvalidationIds( | |
120 syncer::InvalidationHandler* handler, | |
121 const syncer::ObjectIdSet& ids) { | |
122 DCHECK(CalledOnValidThread()); | |
123 DVLOG(2) << "Registering ids: " << ids.size(); | |
124 invalidator_registrar_->UpdateRegisteredIds(handler, ids); | |
125 if (invalidator_) { | |
126 invalidator_->UpdateRegisteredIds( | |
127 this, | |
128 invalidator_registrar_->GetAllRegisteredIds()); | |
129 } | |
130 } | |
131 | |
132 void InvalidationService::UnregisterInvalidationHandler( | |
133 syncer::InvalidationHandler* handler) { | |
134 DCHECK(CalledOnValidThread()); | |
135 DVLOG(2) << "Unregistering"; | |
136 invalidator_registrar_->UnregisterHandler(handler); | |
137 // FIXME: Should unregistering the handler also unregister the IDs? | |
138 // I don't see why not... | |
139 if (invalidator_) { | |
140 invalidator_->UpdateRegisteredIds( | |
141 this, | |
142 invalidator_registrar_->GetAllRegisteredIds()); | |
143 } | |
144 } | |
145 | |
146 void InvalidationService::AcknowledgeInvalidation( | |
147 const invalidation::ObjectId& id, | |
148 const syncer::AckHandle& ack_handle) { | |
149 DCHECK(CalledOnValidThread()); | |
150 if (invalidator_) { | |
151 invalidator_->Acknowledge(id, ack_handle); | |
152 } | |
153 } | |
154 | |
155 syncer::InvalidatorState InvalidationService::GetInvalidatorState() const { | |
156 DCHECK(CalledOnValidThread()); | |
157 if (invalidator_) { | |
158 DVLOG(2) << "GetInvalidatorState returning " << GetInvalidatorState(); | |
159 return invalidator_->GetInvalidatorState(); | |
160 } else { | |
161 DVLOG(2) << "Invalidator currently stopped"; | |
162 return syncer::TRANSIENT_INVALIDATION_ERROR; | |
163 } | |
164 } | |
165 | |
166 std::string InvalidationService::GetInvalidatorClientId() const { | |
167 DCHECK(CalledOnValidThread()); | |
168 if (!invalidator_storage_) { // NULL in some unit tests. | |
169 return "TESTING_ONLY_ID"; | |
170 } | |
171 | |
172 return invalidator_storage_->GetInvalidatorClientId(); | |
173 } | |
174 | |
175 void InvalidationService::Observe( | |
176 int type, | |
177 const content::NotificationSource& source, | |
178 const content::NotificationDetails& details) { | |
179 DCHECK(CalledOnValidThread()); | |
180 | |
181 switch (type) { | |
182 case chrome::NOTIFICATION_TOKEN_AVAILABLE: { | |
tim (not reviewing)
2013/04/15 16:48:24
I forget, do we always send TOKEN_AVAILABLE from T
rlarocque
2013/04/22 21:47:15
I think that's always guaranteed. I was a bit sus
tim (not reviewing)
2013/04/22 22:13:56
More likely is at one time TOKEN_AVAILABLE was act
| |
183 const TokenService::TokenAvailableDetails& token_details = | |
184 *(content::Details<const TokenService::TokenAvailableDetails>( | |
185 details).ptr()); | |
186 if (token_details.service() == GaiaConstants::kSyncService) { | |
187 DCHECK(IsReadyToStart()); | |
188 if (!IsStarted()) { | |
189 Start(); | |
tim (not reviewing)
2013/04/15 16:48:24
You should check with atwilson@ and rogerta@ about
rlarocque
2013/04/22 21:47:15
I'm not sure that we want to go in that direction.
tim (not reviewing)
2013/04/22 22:13:56
As I said, just make sure you check with Drew (at
| |
190 } else { | |
191 UpdateToken(); | |
192 } | |
193 } | |
194 break; | |
195 } | |
196 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: | |
tim (not reviewing)
2013/04/15 16:48:24
This may get interesting when we have service acco
| |
197 Stop(); | |
198 break; | |
199 } | |
200 } | |
201 | |
202 void InvalidationService::OnInvalidatorStateChange( | |
203 syncer::InvalidatorState state) { | |
204 invalidator_registrar_->UpdateInvalidatorState(state); | |
205 } | |
206 | |
207 void InvalidationService::OnIncomingInvalidation( | |
208 const syncer::ObjectIdInvalidationMap& invalidation_map) { | |
209 invalidator_registrar_->DispatchInvalidationsToHandlers(invalidation_map); | |
210 } | |
211 | |
212 void InvalidationService::Shutdown() { | |
213 DCHECK(CalledOnValidThread()); | |
214 Stop(); | |
215 invalidator_registrar_.reset(); | |
216 } | |
217 | |
218 bool InvalidationService::IsReadyToStart() { | |
219 if (signin_manager_->GetAuthenticatedUsername().empty()) { | |
220 DVLOG(2) << "Not starting InvalidationService: user is not signed in."; | |
221 return false; | |
222 } | |
223 | |
224 if (!token_service_) { | |
225 DVLOG(2) << "Not starting InvalidationService: TokenService unavailable."; | |
226 return false; | |
227 } | |
228 | |
229 if (!token_service_->HasTokenForService(GaiaConstants::kSyncService)) { | |
230 DVLOG(2) << "Not starting InvalidationService: Sync token unavailable."; | |
231 return false; | |
232 } | |
233 | |
234 return true; | |
235 } | |
236 | |
237 bool InvalidationService::IsStarted() { | |
238 return !!invalidator_; | |
239 } | |
240 | |
241 void InvalidationService::Start() { | |
242 DCHECK(CalledOnValidThread()); | |
243 | |
244 DCHECK(!invalidator_); | |
245 | |
246 invalidator_.reset(new syncer::NonBlockingInvalidator( | |
247 ParseNotifierOptions(*CommandLine::ForCurrentProcess(), | |
248 profile_->GetRequestContext()), | |
249 invalidator_storage_->GetInvalidatorClientId(), | |
250 invalidator_storage_->GetAllInvalidationStates(), | |
251 invalidator_storage_->GetBootstrapData(), | |
252 syncer::WeakHandle<syncer::InvalidationStateTracker>( | |
253 invalidator_storage_->AsWeakPtr()), | |
254 content::GetUserAgent(GURL()))); | |
255 | |
256 UpdateToken(); | |
257 | |
258 invalidator_->RegisterHandler(this); | |
259 invalidator_->UpdateRegisteredIds( | |
260 this, | |
261 invalidator_registrar_->GetAllRegisteredIds()); | |
262 } | |
263 | |
264 void InvalidationService::UpdateToken() { | |
265 std::string email = signin_manager_->GetAuthenticatedUsername(); | |
266 DCHECK(!email.empty()) << "Expected user to be signed in."; | |
267 | |
268 // FIXME: Is it right to use the sync token here? | |
tim (not reviewing)
2013/04/15 16:48:24
Yes, this seems correct and equivalent to before t
rlarocque
2013/04/22 21:47:15
OK. Just to be clear, my intent with this patch i
tim (not reviewing)
2013/04/22 22:13:56
So, yes. I think we're in agreement? In the futur
| |
269 DCHECK(token_service_->HasTokenForService(GaiaConstants::kSyncService)); | |
270 std::string sync_token = token_service_->GetTokenForService( | |
271 GaiaConstants::kSyncService); | |
272 | |
273 DVLOG(2) << "UpdateCredentials: " << email; | |
274 invalidator_->UpdateCredentials(email, sync_token); | |
275 } | |
276 | |
277 void InvalidationService::Stop() { | |
278 if (invalidator_) { | |
279 invalidator_->UnregisterHandler(this); | |
280 invalidator_.reset(); | |
281 } | |
282 if (invalidator_storage_) { | |
283 invalidator_storage_->Clear(); | |
284 invalidator_storage_.reset(); | |
285 } | |
286 } | |
OLD | NEW |