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

Side by Side Diff: chrome/browser/chromeos/contacts/google_contact_store.cc

Issue 190063004: chromeos: Delete old, unused contacts code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: merge again Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 "chrome/browser/chromeos/contacts/google_contact_store.h"
6
7 #include <algorithm>
8
9 #include "base/bind.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/chromeos/contacts/contact.pb.h"
14 #include "chrome/browser/chromeos/contacts/contact_database.h"
15 #include "chrome/browser/chromeos/contacts/contact_store_observer.h"
16 #include "chrome/browser/chromeos/contacts/gdata_contacts_service.h"
17 #include "chrome/browser/chromeos/profiles/profile_util.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/signin/profile_oauth2_token_service.h"
20 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
21 #include "chrome/browser/signin/signin_manager.h"
22 #include "chrome/browser/signin/signin_manager_factory.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "google_apis/drive/auth_service.h"
25 #include "google_apis/drive/time_util.h"
26
27 using content::BrowserThread;
28
29 namespace contacts {
30
31 namespace {
32
33 // Name of the directory within the profile directory where the contact database
34 // is stored.
35 const base::FilePath::CharType kDatabaseDirectoryName[] =
36 FILE_PATH_LITERAL("Google Contacts");
37
38 // We wait this long after the last update has completed successfully before
39 // updating again.
40 // TODO(derat): Decide what this should be.
41 const int kUpdateIntervalSec = 600;
42
43 // https://developers.google.com/google-apps/contacts/v3/index says that deleted
44 // contact (groups?) will only be returned for 30 days after deletion when the
45 // "showdeleted" parameter is set. If it's been longer than that since the last
46 // successful update, we do a full refresh to make sure that we haven't missed
47 // any deletions. Use 29 instead to make sure that we don't run afoul of
48 // daylight saving time shenanigans or minor skew in the system clock.
49 const int kForceFullUpdateDays = 29;
50
51 // When an update fails, we initially wait this many seconds before retrying.
52 // The delay increases exponentially in response to repeated failures.
53 const int kUpdateFailureInitialRetrySec = 5;
54
55 // Amount by which |update_delay_on_next_failure_| is multiplied on failure.
56 const int kUpdateFailureBackoffFactor = 2;
57
58 // OAuth2 scope for the Contacts API.
59 const char kContactsScope[] = "https://www.google.com/m8/feeds/";
60
61 } // namespace
62
63 GoogleContactStore::TestAPI::TestAPI(GoogleContactStore* store)
64 : store_(store) {
65 DCHECK(store);
66 }
67
68 GoogleContactStore::TestAPI::~TestAPI() {
69 store_ = NULL;
70 }
71
72 void GoogleContactStore::TestAPI::SetDatabase(ContactDatabaseInterface* db) {
73 store_->DestroyDatabase();
74 store_->db_ = db;
75 }
76
77 void GoogleContactStore::TestAPI::SetGDataService(
78 GDataContactsServiceInterface* service) {
79 store_->gdata_service_.reset(service);
80 }
81
82 void GoogleContactStore::TestAPI::DoUpdate() {
83 store_->UpdateContacts();
84 }
85
86 void GoogleContactStore::TestAPI::NotifyAboutNetworkStateChange(bool online) {
87 net::NetworkChangeNotifier::ConnectionType type =
88 online ?
89 net::NetworkChangeNotifier::CONNECTION_UNKNOWN :
90 net::NetworkChangeNotifier::CONNECTION_NONE;
91 store_->OnConnectionTypeChanged(type);
92 }
93
94 scoped_ptr<ContactPointers> GoogleContactStore::TestAPI::GetLoadedContacts() {
95 scoped_ptr<ContactPointers> contacts(new ContactPointers);
96 for (ContactMap::const_iterator it = store_->contacts_.begin();
97 it != store_->contacts_.end(); ++it) {
98 contacts->push_back(it->second);
99 }
100 return contacts.Pass();
101 }
102
103 GoogleContactStore::GoogleContactStore(
104 net::URLRequestContextGetter* url_request_context_getter,
105 Profile* profile)
106 : url_request_context_getter_(url_request_context_getter),
107 profile_(profile),
108 db_(new ContactDatabase),
109 update_delay_on_next_failure_(
110 base::TimeDelta::FromSeconds(kUpdateFailureInitialRetrySec)),
111 is_online_(true),
112 should_update_when_online_(false),
113 weak_ptr_factory_(this) {
114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
115 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
116 is_online_ = !net::NetworkChangeNotifier::IsOffline();
117 }
118
119 GoogleContactStore::~GoogleContactStore() {
120 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
121 weak_ptr_factory_.InvalidateWeakPtrs();
122 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
123 DestroyDatabase();
124 }
125
126 void GoogleContactStore::Init() {
127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
128
129 // Create a GData service if one hasn't already been assigned for testing.
130 if (!gdata_service_.get()) {
131 std::vector<std::string> scopes;
132 scopes.push_back(kContactsScope);
133
134 ProfileOAuth2TokenService* oauth2_service =
135 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
136 SigninManagerBase* signin_manager =
137 SigninManagerFactory::GetForProfile(profile_);
138 gdata_service_.reset(new GDataContactsService(
139 url_request_context_getter_,
140 new google_apis::AuthService(
141 oauth2_service,
142 signin_manager->GetAuthenticatedAccountId(),
143 url_request_context_getter_, scopes)));
144 }
145
146 base::FilePath db_path = profile_->GetPath().Append(kDatabaseDirectoryName);
147 VLOG(1) << "Initializing contact database \"" << db_path.value() << "\" for "
148 << profile_->GetProfileName();
149 db_->Init(db_path,
150 base::Bind(&GoogleContactStore::OnDatabaseInitialized,
151 weak_ptr_factory_.GetWeakPtr()));
152 }
153
154 void GoogleContactStore::AppendContacts(ContactPointers* contacts_out) {
155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
156 DCHECK(contacts_out);
157 for (ContactMap::const_iterator it = contacts_.begin();
158 it != contacts_.end(); ++it) {
159 if (!it->second->deleted())
160 contacts_out->push_back(it->second);
161 }
162 }
163
164 const Contact* GoogleContactStore::GetContactById(
165 const std::string& contact_id) {
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
167 return contacts_.Find(contact_id);
168 }
169
170 void GoogleContactStore::AddObserver(ContactStoreObserver* observer) {
171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
172 DCHECK(observer);
173 observers_.AddObserver(observer);
174 }
175
176 void GoogleContactStore::RemoveObserver(ContactStoreObserver* observer) {
177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
178 DCHECK(observer);
179 observers_.RemoveObserver(observer);
180 }
181
182 void GoogleContactStore::OnConnectionTypeChanged(
183 net::NetworkChangeNotifier::ConnectionType type) {
184 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
185 bool was_online = is_online_;
186 is_online_ = (type != net::NetworkChangeNotifier::CONNECTION_NONE);
187 if (!was_online && is_online_ && should_update_when_online_) {
188 should_update_when_online_ = false;
189 UpdateContacts();
190 }
191 }
192
193 base::Time GoogleContactStore::GetCurrentTime() const {
194 return !current_time_for_testing_.is_null() ?
195 current_time_for_testing_ :
196 base::Time::Now();
197 }
198
199 void GoogleContactStore::DestroyDatabase() {
200 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
201 if (db_) {
202 db_->DestroyOnUIThread();
203 db_ = NULL;
204 }
205 }
206
207 void GoogleContactStore::UpdateContacts() {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
209
210 // If we're offline, defer the update.
211 if (!is_online_) {
212 VLOG(1) << "Deferring contact update due to offline state";
213 should_update_when_online_ = true;
214 return;
215 }
216
217 base::Time min_update_time;
218 base::TimeDelta time_since_last_update =
219 last_successful_update_start_time_.is_null() ?
220 base::TimeDelta() :
221 GetCurrentTime() - last_successful_update_start_time_;
222
223 if (!last_contact_update_time_.is_null() &&
224 time_since_last_update <
225 base::TimeDelta::FromDays(kForceFullUpdateDays)) {
226 // TODO(derat): I'm adding one millisecond to the last update time here as I
227 // don't want to re-download the same most-recently-updated contact each
228 // time, but what happens if within the same millisecond, contact A is
229 // updated, we do a sync, and then contact B is updated? I'm probably being
230 // overly paranoid about this.
231 min_update_time =
232 last_contact_update_time_ + base::TimeDelta::FromMilliseconds(1);
233 }
234 if (min_update_time.is_null()) {
235 VLOG(1) << "Downloading all contacts for " << profile_->GetProfileName();
236 } else {
237 VLOG(1) << "Downloading contacts updated since "
238 << google_apis::util::FormatTimeAsString(min_update_time) << " for "
239 << profile_->GetProfileName();
240 }
241
242 gdata_service_->DownloadContacts(
243 base::Bind(&GoogleContactStore::OnDownloadSuccess,
244 weak_ptr_factory_.GetWeakPtr(),
245 min_update_time.is_null(),
246 GetCurrentTime()),
247 base::Bind(&GoogleContactStore::OnDownloadFailure,
248 weak_ptr_factory_.GetWeakPtr()),
249 min_update_time);
250 }
251
252 void GoogleContactStore::ScheduleUpdate(bool last_update_was_successful) {
253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
254 base::TimeDelta delay;
255 if (last_update_was_successful) {
256 delay = base::TimeDelta::FromSeconds(kUpdateIntervalSec);
257 update_delay_on_next_failure_ =
258 base::TimeDelta::FromSeconds(kUpdateFailureInitialRetrySec);
259 } else {
260 delay = update_delay_on_next_failure_;
261 update_delay_on_next_failure_ = std::min(
262 update_delay_on_next_failure_ * kUpdateFailureBackoffFactor,
263 base::TimeDelta::FromSeconds(kUpdateIntervalSec));
264 }
265 VLOG(1) << "Scheduling update of " << profile_->GetProfileName()
266 << " in " << delay.InSeconds() << " second(s)";
267 update_timer_.Start(
268 FROM_HERE, delay, this, &GoogleContactStore::UpdateContacts);
269 }
270
271 void GoogleContactStore::MergeContacts(
272 bool is_full_update,
273 scoped_ptr<ScopedVector<Contact> > updated_contacts) {
274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
275
276 if (is_full_update) {
277 contacts_.Clear();
278 last_contact_update_time_ = base::Time();
279 }
280
281 // Find the maximum update time from |updated_contacts| since contacts whose
282 // |deleted| flags are set won't be saved to |contacts_|.
283 for (ScopedVector<Contact>::iterator it = updated_contacts->begin();
284 it != updated_contacts->end(); ++it) {
285 last_contact_update_time_ =
286 std::max(last_contact_update_time_,
287 base::Time::FromInternalValue((*it)->update_time()));
288 }
289 VLOG(1) << "Last contact update time is "
290 << google_apis::util::FormatTimeAsString(last_contact_update_time_);
291
292 contacts_.Merge(updated_contacts.Pass(), ContactMap::DROP_DELETED_CONTACTS);
293 }
294
295 void GoogleContactStore::OnDownloadSuccess(
296 bool is_full_update,
297 const base::Time& update_start_time,
298 scoped_ptr<ScopedVector<Contact> > updated_contacts) {
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
300 VLOG(1) << "Got " << updated_contacts->size() << " contact(s) for "
301 << profile_->GetProfileName();
302
303 // Copy the pointers so we can update just these contacts in the database.
304 scoped_ptr<ContactPointers> contacts_to_save_to_db(new ContactPointers);
305 scoped_ptr<ContactDatabaseInterface::ContactIds>
306 contact_ids_to_delete_from_db(new ContactDatabaseInterface::ContactIds);
307 if (db_) {
308 for (size_t i = 0; i < updated_contacts->size(); ++i) {
309 Contact* contact = (*updated_contacts)[i];
310 if (contact->deleted())
311 contact_ids_to_delete_from_db->push_back(contact->contact_id());
312 else
313 contacts_to_save_to_db->push_back(contact);
314 }
315 }
316 bool got_updates = !updated_contacts->empty();
317
318 MergeContacts(is_full_update, updated_contacts.Pass());
319 last_successful_update_start_time_ = update_start_time;
320
321 if (is_full_update || got_updates) {
322 FOR_EACH_OBSERVER(ContactStoreObserver,
323 observers_,
324 OnContactsUpdated(this));
325 }
326
327 if (db_) {
328 // Even if this was an incremental update and we didn't get any updated
329 // contacts, we still want to write updated metadata containing
330 // |update_start_time|.
331 VLOG(1) << "Saving " << contacts_to_save_to_db->size() << " contact(s) to "
332 << "database and deleting " << contact_ids_to_delete_from_db->size()
333 << " as " << (is_full_update ? "full" : "incremental") << " update";
334
335 scoped_ptr<UpdateMetadata> metadata(new UpdateMetadata);
336 metadata->set_last_update_start_time(update_start_time.ToInternalValue());
337 metadata->set_last_contact_update_time(
338 last_contact_update_time_.ToInternalValue());
339
340 db_->SaveContacts(
341 contacts_to_save_to_db.Pass(),
342 contact_ids_to_delete_from_db.Pass(),
343 metadata.Pass(),
344 is_full_update,
345 base::Bind(&GoogleContactStore::OnDatabaseContactsSaved,
346 weak_ptr_factory_.GetWeakPtr()));
347
348 // We'll schedule an update from OnDatabaseContactsSaved() after we're done
349 // writing to the database -- we don't want to modify the contacts while
350 // they're being used by the database.
351 } else {
352 ScheduleUpdate(true);
353 }
354 }
355
356 void GoogleContactStore::OnDownloadFailure() {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358 LOG(WARNING) << "Contacts download failed for " << profile_->GetProfileName();
359 ScheduleUpdate(false);
360 }
361
362 void GoogleContactStore::OnDatabaseInitialized(bool success) {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
364 if (success) {
365 VLOG(1) << "Contact database initialized for "
366 << profile_->GetProfileName();
367 db_->LoadContacts(base::Bind(&GoogleContactStore::OnDatabaseContactsLoaded,
368 weak_ptr_factory_.GetWeakPtr()));
369 } else {
370 LOG(WARNING) << "Failed to initialize contact database for "
371 << profile_->GetProfileName();
372 // Limp along as best as we can: throw away the database and do an update,
373 // which will schedule further updates.
374 DestroyDatabase();
375 UpdateContacts();
376 }
377 }
378
379 void GoogleContactStore::OnDatabaseContactsLoaded(
380 bool success,
381 scoped_ptr<ScopedVector<Contact> > contacts,
382 scoped_ptr<UpdateMetadata> metadata) {
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
384 if (success) {
385 VLOG(1) << "Loaded " << contacts->size() << " contact(s) from database";
386 MergeContacts(true, contacts.Pass());
387 last_successful_update_start_time_ =
388 base::Time::FromInternalValue(metadata->last_update_start_time());
389 last_contact_update_time_ = std::max(
390 last_contact_update_time_,
391 base::Time::FromInternalValue(metadata->last_contact_update_time()));
392
393 if (!contacts_.empty()) {
394 FOR_EACH_OBSERVER(ContactStoreObserver,
395 observers_,
396 OnContactsUpdated(this));
397 }
398 } else {
399 LOG(WARNING) << "Failed to load contacts from database";
400 }
401 UpdateContacts();
402 }
403
404 void GoogleContactStore::OnDatabaseContactsSaved(bool success) {
405 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
406 if (!success)
407 LOG(WARNING) << "Failed to save contacts to database";
408
409 // We only update the database when we've successfully downloaded contacts, so
410 // report success to ScheduleUpdate() even if the database update failed.
411 ScheduleUpdate(true);
412 }
413
414 GoogleContactStoreFactory::GoogleContactStoreFactory() {
415 }
416
417 GoogleContactStoreFactory::~GoogleContactStoreFactory() {
418 }
419
420 bool GoogleContactStoreFactory::CanCreateContactStoreForProfile(
421 Profile* profile) {
422 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
423 DCHECK(profile);
424 return chromeos::IsProfileAssociatedWithGaiaAccount(profile);
425 }
426
427 ContactStore* GoogleContactStoreFactory::CreateContactStore(Profile* profile) {
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
429 DCHECK(CanCreateContactStoreForProfile(profile));
430 return new GoogleContactStore(
431 g_browser_process->system_request_context(), profile);
432 }
433
434 } // namespace contacts
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698