blob: 021a6ec6aeff8b708edbf3fc0af3631e4b0cf621 [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"
Alex Chau67605b92019-06-05 10:48:2916#include "components/gcm_driver/crypto/gcm_encryption_result.h"
[email protected]446f73c22014-05-14 20:47:1817#include "components/gcm_driver/gcm_app_handler.h"
[email protected]df84c532014-04-25 15:36:5418
19namespace gcm {
20
Rayan Kanso9e5adf212019-05-09 16:07:5321InstanceIDHandler::InstanceIDHandler() = default;
jianli10018b2d2015-05-11 21:14:1322
Rayan Kanso9e5adf212019-05-09 16:07:5323InstanceIDHandler::~InstanceIDHandler() = default;
jianli10018b2d2015-05-11 21:14:1324
danakjb534bf72019-05-02 17:10:1425void InstanceIDHandler::DeleteAllTokensForApp(const std::string& app_id,
26 DeleteTokenCallback callback) {
27 DeleteToken(app_id, "*", "*", std::move(callback));
jianlic5f6bd92015-05-28 02:23:4528}
29
peterbfa736e2015-07-28 16:19:5530GCMDriver::GCMDriver(
31 const base::FilePath& store_path,
Alex Chaua76a6e32019-06-26 16:20:0132 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
33 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
Jeremy Roman5c341f6d2019-07-15 15:56:1034 : web_push_sender_(std::move(url_loader_factory)) {
Alex Chaua76a6e32019-06-26 16:20:0135 // The |blocking_task_runner| can be nullptr for tests that do not need the
peterbfa736e2015-07-28 16:19:5536 // 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
Rayan Kanso9e5adf212019-05-09 16:07:5341GCMDriver::~GCMDriver() = default;
[email protected]9d7e5c02014-05-21 03:09:0342
[email protected]c27c10792014-06-05 15:27:2343void GCMDriver::Register(const std::string& app_id,
44 const std::vector<std::string>& sender_ids,
danakjb534bf72019-05-02 17:10:1445 RegisterCallback callback) {
[email protected]c27c10792014-06-05 15:27:2346 DCHECK(!app_id.empty());
jianli7a0c9b62015-05-26 23:24:4747 DCHECK(!sender_ids.empty() && sender_ids.size() <= kMaxSenders);
[email protected]c27c10792014-06-05 15:27:2348 DCHECK(!callback.is_null());
49
jianlif3e52af42015-01-21 23:18:4750 GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
[email protected]c27c10792014-06-05 15:27:2351 if (result != GCMClient::SUCCESS) {
danakjb534bf72019-05-02 17:10:1452 std::move(callback).Run(std::string(), result);
[email protected]c27c10792014-06-05 15:27:2353 return;
54 }
55
jianlif17b85a2014-10-29 21:42:3056 // If previous register operation is still in progress, bail out.
57 if (register_callbacks_.find(app_id) != register_callbacks_.end()) {
danakjb534bf72019-05-02 17:10:1458 std::move(callback).Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
[email protected]c27c10792014-06-05 15:27:2359 return;
60 }
61
62 // Normalize the sender IDs by making them sorted.
63 std::vector<std::string> normalized_sender_ids = sender_ids;
64 std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
65
danakjb534bf72019-05-02 17:10:1466 register_callbacks_[app_id] = std::move(callback);
[email protected]c27c10792014-06-05 15:27:2367
jianlif17b85a2014-10-29 21:42:3068 // If previous unregister operation is still in progress, wait until it
69 // finishes. We don't want to throw ASYNC_OPERATION_PENDING when the user
70 // uninstalls an app (ungistering) and then reinstalls the app again
71 // (registering).
jdoerrie3feb1852018-10-05 12:16:4472 auto unregister_iter = unregister_callbacks_.find(app_id);
jianlif17b85a2014-10-29 21:42:3073 if (unregister_iter != unregister_callbacks_.end()) {
74 // Replace the original unregister callback with an intermediate callback
75 // that will invoke the original unregister callback and trigger the pending
76 // registration after the unregistration finishes.
77 // Note that some parameters to RegisterAfterUnregister are specified here
78 // when the callback is created (base::Bind supports the partial binding
79 // of parameters).
danakjb534bf72019-05-02 17:10:1480 unregister_iter->second = base::BindOnce(
81 &GCMDriver::RegisterAfterUnregister, weak_ptr_factory_.GetWeakPtr(),
82 app_id, normalized_sender_ids, std::move(unregister_iter->second));
jianlif17b85a2014-10-29 21:42:3083 return;
84 }
85
[email protected]c27c10792014-06-05 15:27:2386 RegisterImpl(app_id, normalized_sender_ids);
87}
88
89void GCMDriver::Unregister(const std::string& app_id,
danakjb534bf72019-05-02 17:10:1490 UnregisterCallback callback) {
91 UnregisterInternal(app_id, nullptr /* sender_id */, std::move(callback));
johnme07b355a2015-02-19 17:00:5192}
93
danakjb534bf72019-05-02 17:10:1494void GCMDriver::UnregisterWithSenderId(const std::string& app_id,
95 const std::string& sender_id,
96 UnregisterCallback callback) {
johnme07b355a2015-02-19 17:00:5197 DCHECK(!sender_id.empty());
danakjb534bf72019-05-02 17:10:1498 UnregisterInternal(app_id, &sender_id, std::move(callback));
johnme07b355a2015-02-19 17:00:5199}
100
101void GCMDriver::UnregisterInternal(const std::string& app_id,
102 const std::string* sender_id,
danakjb534bf72019-05-02 17:10:14103 UnregisterCallback callback) {
[email protected]c27c10792014-06-05 15:27:23104 DCHECK(!app_id.empty());
105 DCHECK(!callback.is_null());
106
jianlif3e52af42015-01-21 23:18:47107 GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
[email protected]c27c10792014-06-05 15:27:23108 if (result != GCMClient::SUCCESS) {
danakjb534bf72019-05-02 17:10:14109 std::move(callback).Run(result);
[email protected]c27c10792014-06-05 15:27:23110 return;
111 }
112
113 // If previous un/register operation is still in progress, bail out.
jianlif17b85a2014-10-29 21:42:30114 if (register_callbacks_.find(app_id) != register_callbacks_.end() ||
115 unregister_callbacks_.find(app_id) != unregister_callbacks_.end()) {
danakjb534bf72019-05-02 17:10:14116 std::move(callback).Run(GCMClient::ASYNC_OPERATION_PENDING);
[email protected]c27c10792014-06-05 15:27:23117 return;
118 }
119
danakjb534bf72019-05-02 17:10:14120 unregister_callbacks_[app_id] = std::move(callback);
[email protected]c27c10792014-06-05 15:27:23121
johnme07b355a2015-02-19 17:00:51122 if (sender_id)
123 UnregisterWithSenderIdImpl(app_id, *sender_id);
124 else
125 UnregisterImpl(app_id);
[email protected]c27c10792014-06-05 15:27:23126}
127
128void GCMDriver::Send(const std::string& app_id,
129 const std::string& receiver_id,
mvanouwerkerkf8633deb2015-07-13 11:04:06130 const OutgoingMessage& message,
[email protected]c27c10792014-06-05 15:27:23131 const SendCallback& callback) {
132 DCHECK(!app_id.empty());
133 DCHECK(!receiver_id.empty());
134 DCHECK(!callback.is_null());
135
jianlif3e52af42015-01-21 23:18:47136 GCMClient::Result result = EnsureStarted(GCMClient::IMMEDIATE_START);
[email protected]c27c10792014-06-05 15:27:23137 if (result != GCMClient::SUCCESS) {
138 callback.Run(std::string(), result);
139 return;
140 }
141
142 // If the message with send ID is still in progress, bail out.
143 std::pair<std::string, std::string> key(app_id, message.id);
144 if (send_callbacks_.find(key) != send_callbacks_.end()) {
145 callback.Run(message.id, GCMClient::INVALID_PARAMETER);
146 return;
147 }
148
149 send_callbacks_[key] = callback;
150
151 SendImpl(app_id, receiver_id, message);
152}
153
Peter Beverloo9635b662019-07-10 19:05:18154void GCMDriver::GetEncryptionInfo(const std::string& app_id,
155 GetEncryptionInfoCallback callback) {
johnme36ae5802016-05-10 15:46:24156 encryption_provider_.GetEncryptionInfo(app_id, "" /* authorized_entity */,
Peter Beverloo9635b662019-07-10 19:05:18157 std::move(callback));
peterbfa736e2015-07-28 16:19:55158}
159
johnme07b355a2015-02-19 17:00:51160void GCMDriver::UnregisterWithSenderIdImpl(const std::string& app_id,
161 const std::string& sender_id) {
162 NOTREACHED();
163}
164
[email protected]c27c10792014-06-05 15:27:23165void GCMDriver::RegisterFinished(const std::string& app_id,
166 const std::string& registration_id,
167 GCMClient::Result result) {
jdoerrie3feb1852018-10-05 12:16:44168 auto callback_iter = register_callbacks_.find(app_id);
[email protected]c27c10792014-06-05 15:27:23169 if (callback_iter == register_callbacks_.end()) {
170 // The callback could have been removed when the app is uninstalled.
171 return;
172 }
173
danakjb534bf72019-05-02 17:10:14174 RegisterCallback callback = std::move(callback_iter->second);
[email protected]c27c10792014-06-05 15:27:23175 register_callbacks_.erase(callback_iter);
danakjb534bf72019-05-02 17:10:14176 std::move(callback).Run(registration_id, result);
[email protected]c27c10792014-06-05 15:27:23177}
178
petera4795ae832016-02-17 11:35:27179void GCMDriver::RemoveEncryptionInfoAfterUnregister(const std::string& app_id,
180 GCMClient::Result result) {
181 encryption_provider_.RemoveEncryptionInfo(
johnme36ae5802016-05-10 15:46:24182 app_id, "" /* authorized_entity */,
183 base::Bind(&GCMDriver::UnregisterFinished, weak_ptr_factory_.GetWeakPtr(),
184 app_id, result));
petera4795ae832016-02-17 11:35:27185}
186
[email protected]c27c10792014-06-05 15:27:23187void GCMDriver::UnregisterFinished(const std::string& app_id,
188 GCMClient::Result result) {
jdoerrie3feb1852018-10-05 12:16:44189 auto callback_iter = unregister_callbacks_.find(app_id);
[email protected]c27c10792014-06-05 15:27:23190 if (callback_iter == unregister_callbacks_.end())
191 return;
192
danakjb534bf72019-05-02 17:10:14193 UnregisterCallback callback = std::move(callback_iter->second);
[email protected]c27c10792014-06-05 15:27:23194 unregister_callbacks_.erase(callback_iter);
danakjb534bf72019-05-02 17:10:14195 std::move(callback).Run(result);
[email protected]c27c10792014-06-05 15:27:23196}
197
198void GCMDriver::SendFinished(const std::string& app_id,
199 const std::string& message_id,
200 GCMClient::Result result) {
jdoerrie3feb1852018-10-05 12:16:44201 auto callback_iter = send_callbacks_.find(
202 std::pair<std::string, std::string>(app_id, message_id));
[email protected]c27c10792014-06-05 15:27:23203 if (callback_iter == send_callbacks_.end()) {
204 // The callback could have been removed when the app is uninstalled.
205 return;
206 }
207
208 SendCallback callback = callback_iter->second;
209 send_callbacks_.erase(callback_iter);
210 callback.Run(message_id, result);
211}
212
[email protected]9d7e5c02014-05-21 03:09:03213void GCMDriver::Shutdown() {
[email protected]df84c532014-04-25 15:36:54214 for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
215 iter != app_handlers_.end(); ++iter) {
fgorski83afd872014-10-16 01:11:52216 DVLOG(1) << "Calling ShutdownHandler for: " << iter->first;
[email protected]df84c532014-04-25 15:36:54217 iter->second->ShutdownHandler();
218 }
219 app_handlers_.clear();
[email protected]df84c532014-04-25 15:36:54220}
221
[email protected]530f2162014-05-15 01:05:04222void GCMDriver::AddAppHandler(const std::string& app_id,
223 GCMAppHandler* handler) {
[email protected]df84c532014-04-25 15:36:54224 DCHECK(!app_id.empty());
225 DCHECK(handler);
[email protected]b7176262014-06-18 15:26:29226 DCHECK_EQ(app_handlers_.count(app_id), 0u);
[email protected]df84c532014-04-25 15:36:54227 app_handlers_[app_id] = handler;
fgorski83afd872014-10-16 01:11:52228 DVLOG(1) << "App handler added for: " << app_id;
[email protected]df84c532014-04-25 15:36:54229}
230
[email protected]530f2162014-05-15 01:05:04231void GCMDriver::RemoveAppHandler(const std::string& app_id) {
[email protected]df84c532014-04-25 15:36:54232 DCHECK(!app_id.empty());
[email protected]df84c532014-04-25 15:36:54233 app_handlers_.erase(app_id);
fgorski83afd872014-10-16 01:11:52234 DVLOG(1) << "App handler removed for: " << app_id;
[email protected]df84c532014-04-25 15:36:54235}
236
[email protected]530f2162014-05-15 01:05:04237GCMAppHandler* GCMDriver::GetAppHandler(const std::string& app_id) {
[email protected]b7176262014-06-18 15:26:29238 // Look for exact match.
[email protected]21b77652014-05-31 01:21:09239 GCMAppHandlerMap::const_iterator iter = app_handlers_.find(app_id);
[email protected]b7176262014-06-18 15:26:29240 if (iter != app_handlers_.end())
241 return iter->second;
242
243 // Ask the handlers whether they know how to handle it.
244 for (iter = app_handlers_.begin(); iter != app_handlers_.end(); ++iter) {
245 if (iter->second->CanHandle(app_id))
246 return iter->second;
247 }
248
peterbef2a022017-02-23 14:41:15249 return nullptr;
[email protected]df84c532014-04-25 15:36:54250}
251
johnmea00cc8ae2016-06-02 13:58:04252GCMEncryptionProvider* GCMDriver::GetEncryptionProviderInternal() {
253 return &encryption_provider_;
254}
255
[email protected]c27c10792014-06-05 15:27:23256bool GCMDriver::HasRegisterCallback(const std::string& app_id) {
257 return register_callbacks_.find(app_id) != register_callbacks_.end();
258}
259
260void GCMDriver::ClearCallbacks() {
261 register_callbacks_.clear();
262 unregister_callbacks_.clear();
263 send_callbacks_.clear();
264}
265
peteradd31f62015-10-09 10:18:52266void GCMDriver::DispatchMessage(const std::string& app_id,
267 const IncomingMessage& message) {
peter266a2aa42016-02-19 18:51:39268 encryption_provider_.DecryptMessage(
269 app_id, message, base::Bind(&GCMDriver::DispatchMessageInternal,
270 weak_ptr_factory_.GetWeakPtr(), app_id));
271}
272
Peter Beverlooa376e98c2017-06-27 15:55:37273void GCMDriver::DispatchMessageInternal(const std::string& app_id,
274 GCMDecryptionResult result,
275 const IncomingMessage& message) {
peter266a2aa42016-02-19 18:51:39276 UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.DecryptMessageResult", result,
Peter Beverlooa376e98c2017-06-27 15:55:37277 GCMDecryptionResult::ENUM_SIZE);
peter266a2aa42016-02-19 18:51:39278
279 switch (result) {
Peter Beverlooa376e98c2017-06-27 15:55:37280 case GCMDecryptionResult::UNENCRYPTED:
281 case GCMDecryptionResult::DECRYPTED_DRAFT_03:
282 case GCMDecryptionResult::DECRYPTED_DRAFT_08: {
peterbef2a022017-02-23 14:41:15283 GCMAppHandler* handler = GetAppHandler(app_id);
Peter Beverloof6403f32019-09-05 12:33:10284 UMA_HISTOGRAM_BOOLEAN("GCM.DeliveredToAppHandler", !!handler);
285
peterbef2a022017-02-23 14:41:15286 if (handler)
287 handler->OnMessage(app_id, message);
288
289 // TODO(peter/harkness): Surface unavailable app handlers on
290 // chrome://gcm-internals and send a delivery receipt.
peter266a2aa42016-02-19 18:51:39291 return;
peterbef2a022017-02-23 14:41:15292 }
Peter Beverlooa376e98c2017-06-27 15:55:37293 case GCMDecryptionResult::INVALID_ENCRYPTION_HEADER:
294 case GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER:
295 case GCMDecryptionResult::NO_KEYS:
296 case GCMDecryptionResult::INVALID_SHARED_SECRET:
297 case GCMDecryptionResult::INVALID_PAYLOAD:
Peter Beverlooe5eef71732017-06-27 15:51:29298 case GCMDecryptionResult::INVALID_BINARY_HEADER_PAYLOAD_LENGTH:
299 case GCMDecryptionResult::INVALID_BINARY_HEADER_RECORD_SIZE:
300 case GCMDecryptionResult::INVALID_BINARY_HEADER_PUBLIC_KEY_LENGTH:
Rayan Kanso9e5adf212019-05-09 16:07:53301 case GCMDecryptionResult::INVALID_BINARY_HEADER_PUBLIC_KEY_FORMAT: {
peter266a2aa42016-02-19 18:51:39302 RecordDecryptionFailure(app_id, result);
Rayan Kanso9e5adf212019-05-09 16:07:53303 GCMAppHandler* handler = GetAppHandler(app_id);
304 if (handler) {
305 handler->OnMessageDecryptionFailed(
Rayan Kanso7bde07992019-05-09 17:59:22306 app_id, message.message_id,
307 ToGCMDecryptionResultDetailsString(result));
Rayan Kanso9e5adf212019-05-09 16:07:53308 }
peter266a2aa42016-02-19 18:51:39309 return;
Rayan Kanso9e5adf212019-05-09 16:07:53310 }
Peter Beverlooa376e98c2017-06-27 15:55:37311 case GCMDecryptionResult::ENUM_SIZE:
312 break; // deliberate fall-through
peteradd31f62015-10-09 10:18:52313 }
314
peter266a2aa42016-02-19 18:51:39315 NOTREACHED();
peteradd31f62015-10-09 10:18:52316}
317
jianlif17b85a2014-10-29 21:42:30318void GCMDriver::RegisterAfterUnregister(
319 const std::string& app_id,
320 const std::vector<std::string>& normalized_sender_ids,
danakjb534bf72019-05-02 17:10:14321 UnregisterCallback unregister_callback,
jianlif17b85a2014-10-29 21:42:30322 GCMClient::Result result) {
323 // Invoke the original unregister callback.
danakjb534bf72019-05-02 17:10:14324 std::move(unregister_callback).Run(result);
jianlif17b85a2014-10-29 21:42:30325
326 // Trigger the pending registration.
327 DCHECK(register_callbacks_.find(app_id) != register_callbacks_.end());
328 RegisterImpl(app_id, normalized_sender_ids);
[email protected]c27c10792014-06-05 15:27:23329}
330
Alex Chau67605b92019-06-05 10:48:29331void GCMDriver::SendWebPushMessage(const std::string& app_id,
332 const std::string& authorized_entity,
333 const std::string& p256dh,
334 const std::string& auth_secret,
335 const std::string& fcm_token,
336 crypto::ECPrivateKey* vapid_key,
Alex Chaua76a6e32019-06-26 16:20:01337 WebPushMessage message,
Alex Chaud6eff3c2019-08-20 15:57:09338 WebPushCallback callback) {
Alex Chau1fe6bab92019-06-28 12:33:02339 std::string payload_copy = message.payload;
Alex Chau67605b92019-06-05 10:48:29340 encryption_provider_.EncryptMessage(
Alex Chau1fe6bab92019-06-28 12:33:02341 app_id, authorized_entity, p256dh, auth_secret, payload_copy,
Alex Chaua76a6e32019-06-26 16:20:01342 base::BindOnce(&GCMDriver::OnMessageEncrypted,
343 weak_ptr_factory_.GetWeakPtr(), fcm_token, vapid_key,
344 std::move(message), std::move(callback)));
Alex Chau67605b92019-06-05 10:48:29345}
346
347void GCMDriver::OnMessageEncrypted(const std::string& fcm_token,
348 crypto::ECPrivateKey* vapid_key,
Alex Chaua76a6e32019-06-26 16:20:01349 WebPushMessage message,
Alex Chaud6eff3c2019-08-20 15:57:09350 WebPushCallback callback,
Alex Chau67605b92019-06-05 10:48:29351 GCMEncryptionResult result,
Alex Chaua76a6e32019-06-26 16:20:01352 std::string payload) {
Alex Chau67605b92019-06-05 10:48:29353 UMA_HISTOGRAM_ENUMERATION("GCM.Crypto.EncryptMessageResult", result,
354 GCMEncryptionResult::ENUM_SIZE);
355
356 switch (result) {
Alex Chaua76a6e32019-06-26 16:20:01357 case GCMEncryptionResult::ENCRYPTED_DRAFT_08: {
358 message.payload = std::move(payload);
Alex Chaud6eff3c2019-08-20 15:57:09359 web_push_sender_.SendMessage(fcm_token, vapid_key, std::move(message),
Alex Chaua76a6e32019-06-26 16:20:01360 std::move(callback));
Alex Chau67605b92019-06-05 10:48:29361 return;
Alex Chaua76a6e32019-06-26 16:20:01362 }
Alex Chau67605b92019-06-05 10:48:29363 case GCMEncryptionResult::NO_KEYS:
364 case GCMEncryptionResult::INVALID_SHARED_SECRET:
365 case GCMEncryptionResult::ENCRYPTION_FAILED: {
Alex Chaud6eff3c2019-08-20 15:57:09366 InvokeWebPushCallback(std::move(callback),
367 SendWebPushMessageResult::kEncryptionFailed);
Alex Chau67605b92019-06-05 10:48:29368 return;
369 }
370 case GCMEncryptionResult::ENUM_SIZE:
371 break; // deliberate fall-through
372 }
373
374 NOTREACHED();
375}
376
[email protected]df84c532014-04-25 15:36:54377} // namespace gcm