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 "components/gcm_driver/gcm_driver.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "base/metrics/field_trial.h"
11#include "components/gcm_driver/gcm_app_handler.h"
12
13namespace gcm {
14
15namespace {
16const char kGCMFieldTrialName[] = "GCM";
17const char kGCMFieldTrialEnabledGroupName[] = "Enabled";
18}  // namespace
19
20// static
21bool GCMDriver::IsAllowedForAllUsers() {
22  std::string group_name =
23      base::FieldTrialList::FindFullName(kGCMFieldTrialName);
24  return group_name == kGCMFieldTrialEnabledGroupName;
25}
26
27GCMDriver::GCMDriver() {
28}
29
30GCMDriver::~GCMDriver() {
31}
32
33void GCMDriver::Register(const std::string& app_id,
34                         const std::vector<std::string>& sender_ids,
35                         const RegisterCallback& callback) {
36  DCHECK(!app_id.empty());
37  DCHECK(!sender_ids.empty());
38  DCHECK(!callback.is_null());
39
40  GCMClient::Result result = EnsureStarted();
41  if (result != GCMClient::SUCCESS) {
42    callback.Run(std::string(), result);
43    return;
44  }
45
46  // If previous un/register operation is still in progress, bail out.
47  if (IsAsyncOperationPending(app_id)) {
48    callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
49    return;
50  }
51
52  // Normalize the sender IDs by making them sorted.
53  std::vector<std::string> normalized_sender_ids = sender_ids;
54  std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
55
56  register_callbacks_[app_id] = callback;
57
58  RegisterImpl(app_id, normalized_sender_ids);
59}
60
61void GCMDriver::Unregister(const std::string& app_id,
62                           const UnregisterCallback& callback) {
63  DCHECK(!app_id.empty());
64  DCHECK(!callback.is_null());
65
66  GCMClient::Result result = EnsureStarted();
67  if (result != GCMClient::SUCCESS) {
68    callback.Run(result);
69    return;
70  }
71
72  // If previous un/register operation is still in progress, bail out.
73  if (IsAsyncOperationPending(app_id)) {
74    callback.Run(GCMClient::ASYNC_OPERATION_PENDING);
75    return;
76  }
77
78  unregister_callbacks_[app_id] = callback;
79
80  UnregisterImpl(app_id);
81}
82
83void GCMDriver::Send(const std::string& app_id,
84                     const std::string& receiver_id,
85                     const GCMClient::OutgoingMessage& message,
86                     const SendCallback& callback) {
87  DCHECK(!app_id.empty());
88  DCHECK(!receiver_id.empty());
89  DCHECK(!callback.is_null());
90
91  GCMClient::Result result = EnsureStarted();
92  if (result != GCMClient::SUCCESS) {
93    callback.Run(std::string(), result);
94    return;
95  }
96
97  // If the message with send ID is still in progress, bail out.
98  std::pair<std::string, std::string> key(app_id, message.id);
99  if (send_callbacks_.find(key) != send_callbacks_.end()) {
100    callback.Run(message.id, GCMClient::INVALID_PARAMETER);
101    return;
102  }
103
104  send_callbacks_[key] = callback;
105
106  SendImpl(app_id, receiver_id, message);
107}
108
109void GCMDriver::RegisterFinished(const std::string& app_id,
110                                 const std::string& registration_id,
111                                 GCMClient::Result result) {
112  std::map<std::string, RegisterCallback>::iterator callback_iter =
113      register_callbacks_.find(app_id);
114  if (callback_iter == register_callbacks_.end()) {
115    // The callback could have been removed when the app is uninstalled.
116    return;
117  }
118
119  RegisterCallback callback = callback_iter->second;
120  register_callbacks_.erase(callback_iter);
121  callback.Run(registration_id, result);
122}
123
124void GCMDriver::UnregisterFinished(const std::string& app_id,
125                                   GCMClient::Result result) {
126  std::map<std::string, UnregisterCallback>::iterator callback_iter =
127      unregister_callbacks_.find(app_id);
128  if (callback_iter == unregister_callbacks_.end())
129    return;
130
131  UnregisterCallback callback = callback_iter->second;
132  unregister_callbacks_.erase(callback_iter);
133  callback.Run(result);
134}
135
136void GCMDriver::SendFinished(const std::string& app_id,
137                             const std::string& message_id,
138                             GCMClient::Result result) {
139  std::map<std::pair<std::string, std::string>, SendCallback>::iterator
140      callback_iter = send_callbacks_.find(
141          std::pair<std::string, std::string>(app_id, message_id));
142  if (callback_iter == send_callbacks_.end()) {
143    // The callback could have been removed when the app is uninstalled.
144    return;
145  }
146
147  SendCallback callback = callback_iter->second;
148  send_callbacks_.erase(callback_iter);
149  callback.Run(message_id, result);
150}
151
152void GCMDriver::Shutdown() {
153  for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
154       iter != app_handlers_.end(); ++iter) {
155    iter->second->ShutdownHandler();
156  }
157  app_handlers_.clear();
158}
159
160void GCMDriver::AddAppHandler(const std::string& app_id,
161                              GCMAppHandler* handler) {
162  DCHECK(!app_id.empty());
163  DCHECK(handler);
164  DCHECK_EQ(app_handlers_.count(app_id), 0u);
165  app_handlers_[app_id] = handler;
166}
167
168void GCMDriver::RemoveAppHandler(const std::string& app_id) {
169  DCHECK(!app_id.empty());
170  app_handlers_.erase(app_id);
171}
172
173GCMAppHandler* GCMDriver::GetAppHandler(const std::string& app_id) {
174  // Look for exact match.
175  GCMAppHandlerMap::const_iterator iter = app_handlers_.find(app_id);
176  if (iter != app_handlers_.end())
177    return iter->second;
178
179  // Ask the handlers whether they know how to handle it.
180  for (iter = app_handlers_.begin(); iter != app_handlers_.end(); ++iter) {
181    if (iter->second->CanHandle(app_id))
182      return iter->second;
183  }
184
185  return &default_app_handler_;
186}
187
188bool GCMDriver::HasRegisterCallback(const std::string& app_id) {
189  return register_callbacks_.find(app_id) != register_callbacks_.end();
190}
191
192void GCMDriver::ClearCallbacks() {
193  register_callbacks_.clear();
194  unregister_callbacks_.clear();
195  send_callbacks_.clear();
196}
197
198bool GCMDriver::IsAsyncOperationPending(const std::string& app_id) const {
199  return register_callbacks_.find(app_id) != register_callbacks_.end() ||
200         unregister_callbacks_.find(app_id) != unregister_callbacks_.end();
201}
202
203}  // namespace gcm
204