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. 4 5 #include "chrome/browser/drive/drive_notification_manager.h" 6 7 #include "base/metrics/histogram.h" 8 #include "chrome/browser/drive/drive_notification_observer.h" 9 #include "chrome/browser/invalidation/invalidation_service.h" 10 #include "chrome/browser/invalidation/invalidation_service_factory.h" 11 #include "google/cacheinvalidation/types.pb.h" 12 #include "sync/notifier/object_id_invalidation_map.h" 13 14 namespace drive { 15 16 namespace { 17 18 // The polling interval time is used when XMPP is disabled. 19 const int kFastPollingIntervalInSecs = 60; 20 21 // The polling interval time is used when XMPP is enabled. Theoretically 22 // polling should be unnecessary if XMPP is enabled, but just in case. 23 const int kSlowPollingIntervalInSecs = 300; 24 25 // The sync invalidation object ID for Google Drive. 26 const char kDriveInvalidationObjectId[] = "CHANGELOG"; 27 28 } // namespace 29 30 DriveNotificationManager::DriveNotificationManager( 31 invalidation::InvalidationService* invalidation_service) 32 : invalidation_service_(invalidation_service), 33 push_notification_registered_(false), 34 push_notification_enabled_(false), 35 observers_notified_(false), 36 polling_timer_(true /* retain_user_task */, false /* is_repeating */), 37 weak_ptr_factory_(this) { 38 DCHECK(invalidation_service_); 39 RegisterDriveNotifications(); 40 RestartPollingTimer(); 41 } 42 43 DriveNotificationManager::~DriveNotificationManager() {} 44 45 void DriveNotificationManager::Shutdown() { 46 // Unregister for Drive notifications. 47 if (!invalidation_service_ || !push_notification_registered_) 48 return; 49 50 // We unregister the handler without updating unregistering our IDs on 51 // purpose. See the class comment on the InvalidationService interface for 52 // more information. 53 invalidation_service_->UnregisterInvalidationHandler(this); 54 invalidation_service_ = NULL; 55 } 56 57 void DriveNotificationManager::OnInvalidatorStateChange( 58 syncer::InvalidatorState state) { 59 push_notification_enabled_ = (state == syncer::INVALIDATIONS_ENABLED); 60 if (push_notification_enabled_) { 61 DVLOG(1) << "XMPP Notifications enabled"; 62 } else { 63 DVLOG(1) << "XMPP Notifications disabled (state=" << state << ")"; 64 } 65 FOR_EACH_OBSERVER(DriveNotificationObserver, observers_, 66 OnPushNotificationEnabled(push_notification_enabled_)); 67 } 68 69 void DriveNotificationManager::OnIncomingInvalidation( 70 const syncer::ObjectIdInvalidationMap& invalidation_map) { 71 DVLOG(2) << "XMPP Drive Notification Received"; 72 syncer::ObjectIdSet ids = invalidation_map.GetObjectIds(); 73 DCHECK_EQ(1U, ids.size()); 74 const invalidation::ObjectId object_id( 75 ipc::invalidation::ObjectSource::COSMO_CHANGELOG, 76 kDriveInvalidationObjectId); 77 DCHECK_EQ(1U, ids.count(object_id)); 78 79 // This effectively disables 'local acks'. It tells the invalidations system 80 // to not bother saving invalidations across restarts for us. 81 // See crbug.com/320878. 82 invalidation_map.AcknowledgeAll(); 83 NotifyObserversToUpdate(NOTIFICATION_XMPP); 84 } 85 86 void DriveNotificationManager::AddObserver( 87 DriveNotificationObserver* observer) { 88 observers_.AddObserver(observer); 89 } 90 91 void DriveNotificationManager::RemoveObserver( 92 DriveNotificationObserver* observer) { 93 observers_.RemoveObserver(observer); 94 } 95 96 void DriveNotificationManager::RestartPollingTimer() { 97 const int interval_secs = (push_notification_enabled_ ? 98 kSlowPollingIntervalInSecs : 99 kFastPollingIntervalInSecs); 100 polling_timer_.Stop(); 101 polling_timer_.Start( 102 FROM_HERE, 103 base::TimeDelta::FromSeconds(interval_secs), 104 base::Bind(&DriveNotificationManager::NotifyObserversToUpdate, 105 weak_ptr_factory_.GetWeakPtr(), 106 NOTIFICATION_POLLING)); 107 } 108 109 void DriveNotificationManager::NotifyObserversToUpdate( 110 NotificationSource source) { 111 DVLOG(1) << "Notifying observers: " << NotificationSourceToString(source); 112 FOR_EACH_OBSERVER(DriveNotificationObserver, observers_, 113 OnNotificationReceived()); 114 if (!observers_notified_) { 115 UMA_HISTOGRAM_BOOLEAN("Drive.PushNotificationInitiallyEnabled", 116 push_notification_enabled_); 117 } 118 observers_notified_ = true; 119 120 // Note that polling_timer_ is not a repeating timer. Restarting manually 121 // here is better as XMPP may be received right before the polling timer is 122 // fired (i.e. we don't notify observers twice in a row). 123 RestartPollingTimer(); 124 } 125 126 void DriveNotificationManager::RegisterDriveNotifications() { 127 DCHECK(!push_notification_enabled_); 128 129 if (!invalidation_service_) 130 return; 131 132 invalidation_service_->RegisterInvalidationHandler(this); 133 syncer::ObjectIdSet ids; 134 ids.insert(invalidation::ObjectId( 135 ipc::invalidation::ObjectSource::COSMO_CHANGELOG, 136 kDriveInvalidationObjectId)); 137 invalidation_service_->UpdateRegisteredInvalidationIds(this, ids); 138 push_notification_registered_ = true; 139 OnInvalidatorStateChange(invalidation_service_->GetInvalidatorState()); 140 141 UMA_HISTOGRAM_BOOLEAN("Drive.PushNotificationRegistered", 142 push_notification_registered_); 143 } 144 145 // static 146 std::string DriveNotificationManager::NotificationSourceToString( 147 NotificationSource source) { 148 switch (source) { 149 case NOTIFICATION_XMPP: 150 return "NOTIFICATION_XMPP"; 151 case NOTIFICATION_POLLING: 152 return "NOTIFICATION_POLLING"; 153 } 154 155 NOTREACHED(); 156 return ""; 157 } 158 159 } // namespace drive 160