Home | History | Annotate | Download | only in gcm
      1 // Copyright 2014 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/services/gcm/push_messaging_service_impl.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/bind.h"
     10 #include "base/command_line.h"
     11 #include "base/prefs/pref_service.h"
     12 #include "base/strings/string_util.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/services/gcm/gcm_profile_service.h"
     15 #include "chrome/browser/services/gcm/gcm_profile_service_factory.h"
     16 #include "chrome/common/chrome_switches.h"
     17 #include "chrome/common/pref_names.h"
     18 #include "components/gcm_driver/gcm_driver.h"
     19 #include "components/pref_registry/pref_registry_syncable.h"
     20 
     21 namespace gcm {
     22 
     23 namespace {
     24 const char kAppIdPrefix[] = "push:";
     25 const int kMaxRegistrations = 1000000;
     26 }  // namespace
     27 
     28 // static
     29 void PushMessagingServiceImpl::RegisterProfilePrefs(
     30     user_prefs::PrefRegistrySyncable* registry) {
     31   registry->RegisterIntegerPref(
     32       prefs::kPushMessagingRegistrationCount,
     33       0,
     34       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
     35 }
     36 
     37 // static
     38 void PushMessagingServiceImpl::InitializeForProfile(Profile* profile) {
     39   // TODO(mvanouwerkerk): Make sure to remove this check at the same time as
     40   // push graduates from experimental in Blink.
     41   if (!CommandLine::ForCurrentProcess()->HasSwitch(
     42           switches::kEnableExperimentalWebPlatformFeatures)) {
     43     return;
     44   }
     45   // TODO(johnme): Consider whether push should be enabled in incognito. If it
     46   // does get enabled, then be careful that you're reading the pref from the
     47   // right profile, as prefs defined in a regular profile are visible in the
     48   // corresponding incognito profile unless overrriden.
     49   if (!profile || profile->IsOffTheRecord() ||
     50       profile->GetPrefs()->GetInteger(prefs::kPushMessagingRegistrationCount) <=
     51           0) {
     52     return;
     53   }
     54   // Create the GCMProfileService, and hence instantiate this class.
     55   GCMProfileService* gcm_service =
     56       gcm::GCMProfileServiceFactory::GetForProfile(profile);
     57   PushMessagingServiceImpl* push_service =
     58       static_cast<PushMessagingServiceImpl*>(
     59           gcm_service->push_messaging_service());
     60   // Register ourselves as an app handler.
     61   gcm_service->driver()->AddAppHandler(kAppIdPrefix, push_service);
     62 }
     63 
     64 PushMessagingServiceImpl::PushMessagingServiceImpl(
     65     GCMProfileService* gcm_profile_service,
     66     Profile* profile)
     67     : gcm_profile_service_(gcm_profile_service),
     68       profile_(profile),
     69       weak_factory_(this) {
     70 }
     71 
     72 PushMessagingServiceImpl::~PushMessagingServiceImpl() {
     73   // TODO(johnme): If it's possible for this to be destroyed before GCMDriver,
     74   // then we should call RemoveAppHandler.
     75 }
     76 
     77 bool PushMessagingServiceImpl::CanHandle(const std::string& app_id) const {
     78   // TODO(mvanouwerkerk): Finalize and centralize format of Push API app_id.
     79   return StartsWithASCII(app_id, kAppIdPrefix, true);
     80 }
     81 
     82 void PushMessagingServiceImpl::ShutdownHandler() {
     83   // TODO(johnme): Do any necessary cleanup.
     84 }
     85 
     86 void PushMessagingServiceImpl::OnMessage(
     87     const std::string& app_id,
     88     const GCMClient::IncomingMessage& message) {
     89   // The Push API only exposes a single string of data in the push event fired
     90   // on the Service Worker. When developers send messages using GCM to the Push
     91   // API, they must pass a single key-value pair, where the key is "data" and
     92   // the value is the string they want to be passed to their Service Worker.
     93   // For example, they could send the following JSON using the HTTPS GCM API:
     94   // {
     95   //     "registration_ids": ["FOO", "BAR"],
     96   //     "data": {
     97   //         "data": "BAZ",
     98   //     },
     99   //     "delay_while_idle": true,
    100   // }
    101   // TODO(johnme): Make sure this is clearly documented for developers.
    102   GCMClient::MessageData::const_iterator it = message.data.find("data");
    103   if (it != message.data.end()) {
    104     const std::string& data ALLOW_UNUSED = it->second;
    105     // TODO(mvanouwerkerk): Fire push event with data on the Service Worker
    106     // corresponding to app_id (and remove ALLOW_UNUSED above).
    107   } else {
    108     // Drop the message, as it is invalid.
    109     // TODO(mvanouwerkerk): Show a warning in the developer console of the
    110     // Service Worker corresponding to app_id.
    111     // TODO(johnme): Add diagnostic observers (e.g. UMA and an internals page)
    112     // to know when bad things happen.
    113   }
    114 }
    115 
    116 void PushMessagingServiceImpl::OnMessagesDeleted(const std::string& app_id) {
    117   // TODO(mvanouwerkerk): Fire push error event on the Service Worker
    118   // corresponding to app_id.
    119 }
    120 
    121 void PushMessagingServiceImpl::OnSendError(
    122     const std::string& app_id,
    123     const GCMClient::SendErrorDetails& send_error_details) {
    124   NOTREACHED() << "The Push API shouldn't have sent messages upstream";
    125 }
    126 
    127 void PushMessagingServiceImpl::Register(
    128     const std::string& app_id,
    129     const std::string& sender_id,
    130     const content::PushMessagingService::RegisterCallback& callback) {
    131   if (!gcm_profile_service_->driver()) {
    132     NOTREACHED() << "There is no GCMDriver. Has GCMProfileService shut down?";
    133   }
    134 
    135   if (profile_->GetPrefs()->GetInteger(
    136           prefs::kPushMessagingRegistrationCount) >= kMaxRegistrations) {
    137     DidRegister(app_id, callback, "", GCMClient::UNKNOWN_ERROR);
    138     return;
    139   }
    140 
    141   // If this is registering for the first time then the driver does not have
    142   // this as an app handler and registration would fail.
    143   if (gcm_profile_service_->driver()->GetAppHandler(kAppIdPrefix) != this)
    144     gcm_profile_service_->driver()->AddAppHandler(kAppIdPrefix, this);
    145 
    146   std::vector<std::string> sender_ids(1, sender_id);
    147   gcm_profile_service_->driver()->Register(
    148       app_id,
    149       sender_ids,
    150       base::Bind(&PushMessagingServiceImpl::DidRegister,
    151                  weak_factory_.GetWeakPtr(),
    152                  app_id,
    153                  callback));
    154 }
    155 
    156 void PushMessagingServiceImpl::DidRegister(
    157     const std::string& app_id,
    158     const content::PushMessagingService::RegisterCallback& callback,
    159     const std::string& registration_id,
    160     GCMClient::Result result) {
    161   GURL endpoint = GURL("https://android.googleapis.com/gcm/send");
    162   bool success = (result == GCMClient::SUCCESS);
    163   callback.Run(endpoint, registration_id, success);
    164   if (success) {
    165     // TODO(johnme): Make sure the pref doesn't get out of sync after crashes.
    166     int registration_count = profile_->GetPrefs()->GetInteger(
    167         prefs::kPushMessagingRegistrationCount);
    168     profile_->GetPrefs()->SetInteger(prefs::kPushMessagingRegistrationCount,
    169                                      registration_count + 1);
    170   }
    171 }
    172 
    173 // TODO(johnme): Unregister should decrement the pref, and call
    174 // RemoveAppHandler if the count drops to zero.
    175 
    176 }  // namespace gcm
    177