1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file.
4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "components/gcm_driver/gcm_driver.h"
6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <algorithm>
8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/logging.h"
10116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch#include "base/metrics/field_trial.h"
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "components/gcm_driver/gcm_app_handler.h"
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)namespace gcm {
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
15116680a4aac90f2aa7413d9095a592090648e557Ben Murdochnamespace {
16116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconst char kGCMFieldTrialName[] = "GCM";
17116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconst char kGCMFieldTrialEnabledGroupName[] = "Enabled";
18116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}  // namespace
19116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
20116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// static
21116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool GCMDriver::IsAllowedForAllUsers() {
22116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  std::string group_name =
23116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      base::FieldTrialList::FindFullName(kGCMFieldTrialName);
24116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return group_name == kGCMFieldTrialEnabledGroupName;
25116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
26116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
2746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)GCMDriver::GCMDriver() {
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)GCMDriver::~GCMDriver() {
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GCMDriver::Register(const std::string& app_id,
34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                         const std::vector<std::string>& sender_ids,
35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                         const RegisterCallback& callback) {
36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!app_id.empty());
37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!sender_ids.empty());
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!callback.is_null());
39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  GCMClient::Result result = EnsureStarted();
41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (result != GCMClient::SUCCESS) {
42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    callback.Run(std::string(), result);
43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // If previous un/register operation is still in progress, bail out.
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (IsAsyncOperationPending(app_id)) {
48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Normalize the sender IDs by making them sorted.
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  std::vector<std::string> normalized_sender_ids = sender_ids;
54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
5646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  register_callbacks_[app_id] = callback;
5746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
5846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  RegisterImpl(app_id, normalized_sender_ids);
59cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
60cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
61cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GCMDriver::Unregister(const std::string& app_id,
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                           const UnregisterCallback& callback) {
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!app_id.empty());
64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!callback.is_null());
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  GCMClient::Result result = EnsureStarted();
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (result != GCMClient::SUCCESS) {
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    callback.Run(result);
69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // If previous un/register operation is still in progress, bail out.
73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (IsAsyncOperationPending(app_id)) {
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    callback.Run(GCMClient::ASYNC_OPERATION_PENDING);
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  unregister_callbacks_[app_id] = callback;
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
8046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  UnregisterImpl(app_id);
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GCMDriver::Send(const std::string& app_id,
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                     const std::string& receiver_id,
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                     const GCMClient::OutgoingMessage& message,
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                     const SendCallback& callback) {
87cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!app_id.empty());
88cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!receiver_id.empty());
89cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  DCHECK(!callback.is_null());
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  GCMClient::Result result = EnsureStarted();
92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (result != GCMClient::SUCCESS) {
93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    callback.Run(std::string(), result);
94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // If the message with send ID is still in progress, bail out.
98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  std::pair<std::string, std::string> key(app_id, message.id);
99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (send_callbacks_.find(key) != send_callbacks_.end()) {
100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    callback.Run(message.id, GCMClient::INVALID_PARAMETER);
101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  send_callbacks_[key] = callback;
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
10646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  SendImpl(app_id, receiver_id, message);
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
109cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GCMDriver::RegisterFinished(const std::string& app_id,
110cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                 const std::string& registration_id,
111cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                 GCMClient::Result result) {
112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  std::map<std::string, RegisterCallback>::iterator callback_iter =
113cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      register_callbacks_.find(app_id);
114cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (callback_iter == register_callbacks_.end()) {
115cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // The callback could have been removed when the app is uninstalled.
116cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
117cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
118cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
119cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  RegisterCallback callback = callback_iter->second;
120cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  register_callbacks_.erase(callback_iter);
121cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  callback.Run(registration_id, result);
122cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
123cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
124cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GCMDriver::UnregisterFinished(const std::string& app_id,
125cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                                   GCMClient::Result result) {
126cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  std::map<std::string, UnregisterCallback>::iterator callback_iter =
127cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      unregister_callbacks_.find(app_id);
128cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (callback_iter == unregister_callbacks_.end())
129cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
130cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
131cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  UnregisterCallback callback = callback_iter->second;
132cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  unregister_callbacks_.erase(callback_iter);
133cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  callback.Run(result);
134cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
135cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
136cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void GCMDriver::SendFinished(const std::string& app_id,
137cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                             const std::string& message_id,
138cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                             GCMClient::Result result) {
139cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  std::map<std::pair<std::string, std::string>, SendCallback>::iterator
140cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      callback_iter = send_callbacks_.find(
141cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)          std::pair<std::string, std::string>(app_id, message_id));
142cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (callback_iter == send_callbacks_.end()) {
143cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    // The callback could have been removed when the app is uninstalled.
144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
145cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
146cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
147cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  SendCallback callback = callback_iter->second;
148cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  send_callbacks_.erase(callback_iter);
149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  callback.Run(message_id, result);
150cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
151cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
15246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void GCMDriver::Shutdown() {
15346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
15446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)       iter != app_handlers_.end(); ++iter) {
15546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)    iter->second->ShutdownHandler();
15646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
15746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  app_handlers_.clear();
158cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
159cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
16046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void GCMDriver::AddAppHandler(const std::string& app_id,
16146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)                              GCMAppHandler* handler) {
16246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  DCHECK(!app_id.empty());
16346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  DCHECK(handler);
164f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK_EQ(app_handlers_.count(app_id), 0u);
16546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  app_handlers_[app_id] = handler;
166cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
167cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
16846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void GCMDriver::RemoveAppHandler(const std::string& app_id) {
16946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  DCHECK(!app_id.empty());
17046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  app_handlers_.erase(app_id);
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)GCMAppHandler* GCMDriver::GetAppHandler(const std::string& app_id) {
174f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Look for exact match.
17546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  GCMAppHandlerMap::const_iterator iter = app_handlers_.find(app_id);
176f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (iter != app_handlers_.end())
177f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return iter->second;
178f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
179f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Ask the handlers whether they know how to handle it.
180f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (iter = app_handlers_.begin(); iter != app_handlers_.end(); ++iter) {
181f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (iter->second->CanHandle(app_id))
182f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      return iter->second;
183f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
184f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
185f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return &default_app_handler_;
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
18846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)bool GCMDriver::HasRegisterCallback(const std::string& app_id) {
18946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return register_callbacks_.find(app_id) != register_callbacks_.end();
19046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)}
191cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
19246d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)void GCMDriver::ClearCallbacks() {
19346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  register_callbacks_.clear();
19446d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  unregister_callbacks_.clear();
19546d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  send_callbacks_.clear();
196cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
197cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
19846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)bool GCMDriver::IsAsyncOperationPending(const std::string& app_id) const {
19946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  return register_callbacks_.find(app_id) != register_callbacks_.end() ||
20046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)         unregister_callbacks_.find(app_id) != unregister_callbacks_.end();
201cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
202cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
203cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}  // namespace gcm
204