Chromium Code Reviews| 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 |