Home | History | Annotate | Download | only in drive
      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 "chrome/browser/profiles/profile.h"
     12 #include "google/cacheinvalidation/types.pb.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(Profile* profile)
     31     : profile_(profile),
     32       push_notification_registered_(false),
     33       push_notification_enabled_(false),
     34       observers_notified_(false),
     35       polling_timer_(true /* retain_user_task */, false /* is_repeating */),
     36       weak_ptr_factory_(this) {
     37   RegisterDriveNotifications();
     38   RestartPollingTimer();
     39 }
     40 
     41 DriveNotificationManager::~DriveNotificationManager() {}
     42 
     43 void DriveNotificationManager::Shutdown() {
     44   // Unregister for Drive notifications.
     45   invalidation::InvalidationService* invalidation_service =
     46       invalidation::InvalidationServiceFactory::GetForProfile(profile_);
     47   if (!invalidation_service || !push_notification_registered_) {
     48     return;
     49   }
     50 
     51   // We unregister the handler without updating unregistering our IDs on
     52   // purpose.  See the class comment on the InvalidationService interface for
     53   // more information.
     54   invalidation_service->UnregisterInvalidationHandler(this);
     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   DCHECK_EQ(1U, invalidation_map.size());
     73   const invalidation::ObjectId object_id(
     74       ipc::invalidation::ObjectSource::COSMO_CHANGELOG,
     75       kDriveInvalidationObjectId);
     76   DCHECK_EQ(1U, invalidation_map.count(object_id));
     77 
     78   // TODO(dcheng): Only acknowledge the invalidation once the fetch has
     79   // completed. http://crbug.com/156843
     80   invalidation::InvalidationService* invalidation_service =
     81       invalidation::InvalidationServiceFactory::GetForProfile(profile_);
     82   DCHECK(invalidation_service);
     83   invalidation_service->AcknowledgeInvalidation(
     84       invalidation_map.begin()->first,
     85       invalidation_map.begin()->second.ack_handle);
     86 
     87   NotifyObserversToUpdate(NOTIFICATION_XMPP);
     88 }
     89 
     90 void DriveNotificationManager::AddObserver(
     91     DriveNotificationObserver* observer) {
     92   observers_.AddObserver(observer);
     93 }
     94 
     95 void DriveNotificationManager::RemoveObserver(
     96     DriveNotificationObserver* observer) {
     97   observers_.RemoveObserver(observer);
     98 }
     99 
    100 void DriveNotificationManager::RestartPollingTimer() {
    101   const int interval_secs = (push_notification_enabled_ ?
    102                              kSlowPollingIntervalInSecs :
    103                              kFastPollingIntervalInSecs);
    104   polling_timer_.Stop();
    105   polling_timer_.Start(
    106       FROM_HERE,
    107       base::TimeDelta::FromSeconds(interval_secs),
    108       base::Bind(&DriveNotificationManager::NotifyObserversToUpdate,
    109                  weak_ptr_factory_.GetWeakPtr(),
    110                  NOTIFICATION_POLLING));
    111 }
    112 
    113 void DriveNotificationManager::NotifyObserversToUpdate(
    114     NotificationSource source) {
    115   DVLOG(1) << "Notifying observers: " << NotificationSourceToString(source);
    116   FOR_EACH_OBSERVER(DriveNotificationObserver, observers_,
    117                     OnNotificationReceived());
    118   if (!observers_notified_) {
    119     UMA_HISTOGRAM_BOOLEAN("Drive.PushNotificationInitiallyEnabled",
    120                           push_notification_enabled_);
    121   }
    122   observers_notified_ = true;
    123 
    124   // Note that polling_timer_ is not a repeating timer. Restarting manually
    125   // here is better as XMPP may be received right before the polling timer is
    126   // fired (i.e. we don't notify observers twice in a row).
    127   RestartPollingTimer();
    128 }
    129 
    130 void DriveNotificationManager::RegisterDriveNotifications() {
    131   DCHECK(!push_notification_enabled_);
    132 
    133   invalidation::InvalidationService* invalidation_service =
    134       invalidation::InvalidationServiceFactory::GetForProfile(profile_);
    135   if (!invalidation_service)
    136     return;
    137 
    138   invalidation_service->RegisterInvalidationHandler(this);
    139   syncer::ObjectIdSet ids;
    140   ids.insert(invalidation::ObjectId(
    141       ipc::invalidation::ObjectSource::COSMO_CHANGELOG,
    142       kDriveInvalidationObjectId));
    143   invalidation_service->UpdateRegisteredInvalidationIds(this, ids);
    144   push_notification_registered_ = true;
    145   OnInvalidatorStateChange(invalidation_service->GetInvalidatorState());
    146 
    147   UMA_HISTOGRAM_BOOLEAN("Drive.PushNotificationRegistered",
    148                         push_notification_registered_);
    149 }
    150 
    151 // static
    152 std::string DriveNotificationManager::NotificationSourceToString(
    153     NotificationSource source) {
    154   switch (source) {
    155     case NOTIFICATION_XMPP:
    156       return "NOTIFICATION_XMPP";
    157     case NOTIFICATION_POLLING:
    158       return "NOTIFICATION_POLLING";
    159   }
    160 
    161   NOTREACHED();
    162   return "";
    163 }
    164 
    165 }  // namespace drive
    166