blob: c13ae9e72efdfa5de065a2c859adb67112e9c9be [file] [log] [blame]
[email protected]df84c532014-04-25 15:36:541// 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
[email protected]8d41e5a2014-05-28 03:18:495#include "components/gcm_driver/gcm_driver.h"
[email protected]df84c532014-04-25 15:36:546
avi26062922015-12-26 00:14:187#include <stddef.h>
8
[email protected]c27c10792014-06-05 15:27:239#include <algorithm>
10
jianli753282a32015-01-09 02:07:4011#include "base/bind.h"
peterbfa736e2015-07-28 16:19:5512#include "base/files/file_path.h"
[email protected]df84c532014-04-25 15:36:5413#include "base/logging.h"
peter266a2aa42016-02-19 18:51:3914#include "base/metrics/histogram_macros.h"
Peter Beverlooa376e98c2017-06-27 15:55:3715#include "components/gcm_driver/crypto/gcm_decryption_result.h"
[email protected]446f73c22014-05-14 20:47:1816#include "components/gcm_driver/gcm_app_handler.h"
[email protected]df84c532014-04-25 15:36:5417
18namespace gcm {
19
jianli7a0c9b62015-05-26 23:24:4720InstanceIDHandler::InstanceIDHandler() {
jianli10018b2d2015-05-11 21:14:1321}
22
jianli7a0c9b62015-05-26 23:24:4723InstanceIDHandler::~InstanceIDHandler() {
jianli10018b2d2015-05-11 21:14:1324}
25
jianlic5f6bd92015-05-28 02:23:4526void InstanceIDHandler::DeleteAllTokensForApp(
27 const std::string& app_id, const DeleteTokenCallback& callback) {
28 DeleteToken(app_id, "*", "*", callback);
29}
30
peterbfa736e2015-07-28 16:19:5531GCMDriver::GCMDriver(
32 const base::FilePath& store_path,
33 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner)
34 : weak_ptr_factory_(this) {
35 // The |blocking_task_runner| can be NULL for tests that do not need the
36 // encryption capabilities of the GCMDriver class.
37 if (blocking_task_runner)
38 encryption_provider_.Init(store_path, blocking_task_runner);
[email protected]9d7e5c02014-05-21 03:09:0339}
40
41GCMDriver::~GCMDriver() {
42}
43
[email protected]c27c10792014-06-05 15:27:2344void GCMDriver::Register(const std::string& app_id,
45 const std::vector<std::string>& sender_ids,
46 const RegisterCallback& callback) {
47 DCHECK(!app_id.empty());
jianli7a0c9b62015-05-26 23:24:4748 DCHECK(!sender_ids.empty() && sender_ids.size() <= kMaxSenders);
[email protected]c27c10792014-06-05 15:27:2349 DCHECK(!callback.is_null());
50
jianlif3e52af42015-01-21 23:18:4751 GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
[email protected]c27c10792014-06-05 15:27:2352 if (result != GCMClient::SUCCESS) {
53 callback.Run(std::string(), result);
54 return;
55 }
56
jianlif17b85a2014-10-29 21:42:3057 // If previous register operation is still in progress, bail out.
58 if (register_callbacks_.find(app_id) != register_callbacks_.end()) {
[email protected]c27c10792014-06-05 15:27:2359 callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
60 return;
61 }
62
63 // Normalize the sender IDs by making them sorted.
64 std::vector<std::string> normalized_sender_ids = sender_ids;
65 std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
66
67 register_callbacks_[app_id] = callback;
68
jianlif17b85a2014-10-29 21:42:3069 // If previous unregister operation is still in progress, wait until it
70 // finishes. We don't want to throw ASYNC_OPERATION_PENDING when the user
71 // uninstalls an app (ungistering) and then reinstalls the app again
72 // (registering).
73 std::map<std::string, UnregisterCallback>::iterator unregister_iter =
74 unregister_callbacks_.find(app_id);
75 if (unregister_iter != unregister_callbacks_.end()) {
76 // Replace the original unregister callback with an intermediate callback
77 // that will invoke the original unregister callback and trigger the pending
78 // registration after the unregistration finishes.
79 // Note that some parameters to RegisterAfterUnregister are specified here
80 // when the callback is created (base::Bind supports the partial binding
81 // of parameters).
82 unregister_iter->second = base::Bind(
83 &GCMDriver::RegisterAfterUnregister,
84 weak_ptr_factory_.GetWeakPtr(),
85 app_id,
86 normalized_sender_ids,
87 unregister_iter->second);
88 return;
89 }
90
[email protected]c27c10792014-06-05 15:27:2391 RegisterImpl(app_id, normalized_sender_ids);
92}
93
94void GCMDriver::Unregister(const std::string& app_id,
95 const UnregisterCallback& callback) {
johnme07b355a2015-02-19 17:00:5196 UnregisterInternal(app_id, nullptr /* sender_id */, callback);
97}
98
99void GCMDriver::UnregisterWithSenderId(
100 const std::string& app_id,
101 const std::string& sender_id,
102 const UnregisterCallback& callback) {
103 DCHECK(!sender_id.empty());
104 UnregisterInternal(app_id, &sender_id, callback);
105}
106
107void GCMDriver::UnregisterInternal(const std::string& app_id,
108 const std::string* sender_id,
109 const UnregisterCallback& callback) {
[email protected]c27c10792014-06-05 15:27:23110 DCHECK(!app_id.empty());
111 DCHECK(!callback.is_null());
112
jianlif3e52af42015-01-21 23:18:47113 GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
[email protected]c27c10792014-06-05 15:27:23114 if (result != GCMClient::SUCCESS) {
115 callback.Run(result);
116 return;
117 }
118
119 // If previous un/register operation is still in progress, bail out.
jianlif17b85a2014-10-29 21:42:30120 if (register_callbacks_.find(app_id) != register_callbacks_.end() ||
121 unregister_callbacks_.find(app_id) != unregister_callbacks_.end()) {
[email protected]c27c10792014-06-05 15:27:23122 callback.Run(GCMClient::ASYNC_OPERATION_PENDING);
123 return;
124 }
125
126 unregister_callbacks_[app_id] = callback;
127
johnme07b355a2015-02-19 17:00:51128 if (sender_id)
129 UnregisterWithSenderIdImpl(app_id, *sender_id);
130 else
131 UnregisterImpl(app_id);
[email protected]c27c10792014-06-05 15:27:23132}
133
134void GCMDriver::Send(const std::string& app_id,
135 const std::string& receiver_id,
mvanouwerkerkf8633deb2015-07-13 11:04:06136 const OutgoingMessage& message,
[email protected]c27c10792014-06-05 15:27:23137 const SendCallback& callback) {
138 DCHECK(!app_id.empty());
139 DCHECK(!receiver_id.empty());
140 DCHECK(!callback.is_null());
141
jianlif3e52af42015-01-21 23:18:47142 GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
[email protected]c27c10792014-06-05 15:27:23143 if (result != GCMClient::SUCCESS) {
144 callback.Run(std::string(), result);
145 return;
146 }
147
148 // If the message with send ID is still in progress, bail out.
149 std::pair<std::string, std::string> key(app_id, message.id);
150 if (send_callbacks_.find(key) != send_callbacks_.end()) {
151 callback.Run(message.id, GCMClient::INVALID_PARAMETER);
152 return;
153 }
154
155 send_callbacks_[key] = callback;
156
157 SendImpl(app_id, receiver_id, message);
158}
159
peter903cde432015-12-21 15:39:50160void GCMDriver::GetEncryptionInfo(
peterbfa736e2015-07-28 16:19:55161 const std::string& app_id,
peter903cde432015-12-21 15:39:50162 const GetEncryptionInfoCallback& callback) {
johnme36ae5802016-05-10 15:46:24163 encryption_provider_.GetEncryptionInfo(app_id, "" /* authorized_entity */,
164 callback);
peterbfa736e2015-07-28 16:19:55165}
166
johnme07b355a2015-02-19 17:00:51167void GCMDriver::UnregisterWithSenderIdImpl(const std::string& app_id,
168 const std::string& sender_id) {
169 NOTREACHED();
170}
171
[email protected]c27c10792014-06-05 15:27:23172void GCMDriver::RegisterFinished(const std::string& app_id,
173 const std::string& registration_id,
174 GCMClient::Result result) {
175 std::map<std::string, RegisterCallback>::iterator callback_iter =
176 register_callbacks_.find(app_id);
177 if (callback_iter == register_callbacks_.end()) {
178 // The callback could have been removed when the app is uninstalled.
179 return;
180 }
181
182 RegisterCallback callback = callback_iter->second;
183 register_callbacks_.erase(callback_iter);
184 callback.Run(registration_id, result);
185}
186
petera4795ae832016-02-17 11:35:27187void GCMDriver::RemoveEncryptionInfoAfterUnregister(const std::string& app_id,
188 GCMClient::Result result) {
189 encryption_provider_.RemoveEncryptionInfo(
johnme36ae5802016-05-10 15:46:24190 app_id, "" /* authorized_entity */,
191 base::Bind(&GCMDriver::UnregisterFinished, weak_ptr_factory_.GetWeakPtr(),
192 app_id, result));
petera4795ae832016-02-17 11:35:27193}
194
[email protected]c27c10792014-06-05 15:27:23195void GCMDriver::UnregisterFinished(const std::string& app_id,
196 GCMClient::Result result) {
197 std::map<std::string, UnregisterCallback>::iterator callback_iter =
198 unregister_callbacks_.find(app_id);
199 if (callback_iter == unregister_callbacks_.end())
200 return;
201
202 UnregisterCallback callback = callback_iter->second;
203 unregister_callbacks_.erase(callback_iter);
204 callback.Run(result);
205}
206
207void GCMDriver::SendFinished(const std::string& app_id,
208 const std::string& message_id,
209 GCMClient::Result result) {
210 std::map<std::pair<std::string, std::string>, SendCallback>::iterator
211 callback_iter = send_callbacks_.find(
212 std::pair<std::string, std::string>(app_id, message_id));
213 if (callback_iter == send_callbacks_.end()) {
214 // The callback could have been removed when the app is uninstalled.
215 return;
216 }
217
218 SendCallback callback = callback_iter->second;
219 send_callbacks_.erase(callback_iter);
220 callback.Run(message_id, result);
221}
222
[email protected]9d7e5c02014-05-21 03:09:03223void GCMDriver::Shutdown() {
[email protected]df84c532014-04-25 15:36:54224 for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
225 iter != app_handlers_.end(); ++iter) {
fgorski83afd872014-10-16 01:11:52226 DVLOG(1) << "Calling ShutdownHandler for: " << iter->first;
[email protected]df84c532014-04-25 15:36:54227 iter->second->ShutdownHandler();
228 }
229 app_handlers_.clear();
[email protected]df84c532014-04-25 15:36:54230}
231
[email protected]530f2162014-05-15 01:05:04232void GCMDriver::AddAppHandler(const std::string& app_id,
233 GCMAppHandler* handler) {
[email protected]df84c532014-04-25 15:36:54234 DCHECK(!app_id.empty());
235 DCHECK(handler);
[email protected]b7176262014-06-18 15:26:29236 DCHECK_EQ(app_handlers_.count(app_id), 0u);
[email protected]df84c532014-04-25 15:36:54237 app_handlers_[app_id] = handler;
fgorski83afd872014-10-16 01:11:52238 DVLOG(1) << "App handler added for: " << app_id;
[email protected]df84c532014-04-25 15:36:54239}
240
[email protected]530f2162014-05-15 01:05:04241void GCMDriver::RemoveAppHandler(const std::string& app_id) {
[email protected]df84c532014-04-25 15:36:54242 DCHECK(!app_id.empty());
[email protected]df84c532014-04-25 15:36:54243 app_handlers_.erase(app_id);
fgorski83afd872014-10-16 01:11:52244 DVLOG(1) << "App handler removed for: " << app_id;
[email protected]df84c532014-04-25 15:36:54245}
246
[email protected]530f2162014-05-15 01:05:04247GCMAppHandler* GCMDriver::GetAppHandler(const std::string& app_id) {
[email protected]b7176262014-06-18 15:26:29248 // Look for exact match.
[email protected]21b77652014-05-31 01:21:09249 GCMAppHandlerMap::const_iterator iter = app_handlers_.find(app_id);
[email protected]b7176262014-06-18 15:26:29250 if (iter != app_handlers_.end())
251 return iter->second;
252
253 // Ask the handlers whether they know how to handle it.
254 for (iter = app_handlers_.begin(); iter != app_handlers_.end(); ++iter) {
255 if (iter->second->CanHandle(app_id))
256 return iter->second;
257 }
258
peterbef2a022017-02-23 14:41:15259 return nullptr;
[email protected]df84c532014-04-25 15:36:54260}
261
johnmea00cc8ae2016-06-02 13:58:04262GCMEncryptionProvider* GCMDriver::GetEncryptionProviderInternal() {
263 return &encryption_provider_;
264}
265
[email protected]c27c10792014-06-05 15:27:23266bool GCMDriver::HasRegisterCallback(const std::string& app_id) {
267 return register_callbacks_.find(app_id) != register_callbacks_.end();
268}
269
270void GCMDriver::ClearCallbacks() {
271 register_callbacks_.clear();
272 unregister_callbacks_.clear();
273 send_callbacks_.clear();
274}
275
peteradd31f62015-10-09 10:18:52276void GCMDriver::DispatchMessage(const std::string& app_id,
277 const IncomingMessage& message) {
peter266a2aa42016-02-19 18:51:39278 encryption_provider_.DecryptMessage(
279 app_id, message, base::Bind(&GCMDriver::DispatchMessageInternal,
280 weak_ptr_factory_.GetWeakPtr(), app_id));
281}
282
Peter Beverlooa376e98c2017-06-27 15:55:37283void GCMDriver::DispatchMessageInternal(const std::string& app_id,
284 GCMDecryptionResult result,
285 const IncomingMessage& message) {
peter266a2aa42016-02-19 18:51:39286 UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.DecryptMessageResult", result,
Peter Beverlooa376e98c2017-06-27 15:55:37287 GCMDecryptionResult::ENUM_SIZE);
peter266a2aa42016-02-19 18:51:39288
289 switch (result) {
Peter Beverlooa376e98c2017-06-27 15:55:37290 case GCMDecryptionResult::UNENCRYPTED:
291 case GCMDecryptionResult::DECRYPTED_DRAFT_03:
292 case GCMDecryptionResult::DECRYPTED_DRAFT_08: {
peterbef2a022017-02-23 14:41:15293 GCMAppHandler* handler = GetAppHandler(app_id);
294 if (handler)
295 handler->OnMessage(app_id, message);
296
297 // TODO(peter/harkness): Surface unavailable app handlers on
298 // chrome://gcm-internals and send a delivery receipt.
peter266a2aa42016-02-19 18:51:39299 return;
peterbef2a022017-02-23 14:41:15300 }
Peter Beverlooa376e98c2017-06-27 15:55:37301 case GCMDecryptionResult::INVALID_ENCRYPTION_HEADER:
302 case GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER:
303 case GCMDecryptionResult::NO_KEYS:
304 case GCMDecryptionResult::INVALID_SHARED_SECRET:
305 case GCMDecryptionResult::INVALID_PAYLOAD:
306 case GCMDecryptionResult::INVALID_BINARY_HEADER:
peter266a2aa42016-02-19 18:51:39307 RecordDecryptionFailure(app_id, result);
308 return;
Peter Beverlooa376e98c2017-06-27 15:55:37309 case GCMDecryptionResult::ENUM_SIZE:
310 break; // deliberate fall-through
peteradd31f62015-10-09 10:18:52311 }
312
peter266a2aa42016-02-19 18:51:39313 NOTREACHED();
peteradd31f62015-10-09 10:18:52314}
315
jianlif17b85a2014-10-29 21:42:30316void GCMDriver::RegisterAfterUnregister(
317 const std::string& app_id,
318 const std::vector<std::string>& normalized_sender_ids,
319 const UnregisterCallback& unregister_callback,
320 GCMClient::Result result) {
321 // Invoke the original unregister callback.
322 unregister_callback.Run(result);
323
324 // Trigger the pending registration.
325 DCHECK(register_callbacks_.find(app_id) != register_callbacks_.end());
326 RegisterImpl(app_id, normalized_sender_ids);
[email protected]c27c10792014-06-05 15:27:23327}
328
[email protected]df84c532014-04-25 15:36:54329} // namespace gcm