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.
5 #include "google_apis/gcm/engine/gcm_store_impl.h"
7 #include "base/basictypes.h"
9 #include "base/callback.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/string_tokenizer.h"
20 #include "base/time/time.h"
21 #include "base/tracked_objects.h"
22 #include "google_apis/gcm/base/encryptor.h"
23 #include "google_apis/gcm/base/mcs_message.h"
24 #include "google_apis/gcm/base/mcs_util.h"
25 #include "google_apis/gcm/protocol/mcs.pb.h"
26 #include "third_party/leveldatabase/src/include/leveldb/db.h"
27 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
33 // This enum is used in an UMA histogram (GCMLoadStatus enum defined in
34 // tools/metrics/histograms/histogram.xml). Hence the entries here shouldn't
35 // be deleted or re-ordered and new ones should be added to the end.
40 LOADING_DEVICE_CREDENTIALS_FAILED
,
41 LOADING_REGISTRATION_FAILED
,
42 LOADING_INCOMING_MESSAGES_FAILED
,
43 LOADING_OUTGOING_MESSAGES_FAILED
,
44 LOADING_LAST_CHECKIN_INFO_FAILED
,
45 LOADING_GSERVICE_SETTINGS_FAILED
,
46 LOADING_ACCOUNT_MAPPING_FAILED
,
47 LOADING_LAST_TOKEN_TIME_FAILED
,
49 // NOTE: always keep this entry at the end. Add new status types only
50 // immediately above this line. Make sure to update the corresponding
51 // histogram enum accordingly.
55 // Limit to the number of outstanding messages per app.
56 const int kMessagesPerAppLimit
= 20;
58 // ---- LevelDB keys. ----
59 // Key for this device's android id.
60 const char kDeviceAIDKey
[] = "device_aid_key";
61 // Key for this device's android security token.
62 const char kDeviceTokenKey
[] = "device_token_key";
63 // Lowest lexicographically ordered app ids.
64 // Used for prefixing app id.
65 const char kRegistrationKeyStart
[] = "reg1-";
66 // Key guaranteed to be higher than all app ids.
67 // Used for limiting iteration.
68 const char kRegistrationKeyEnd
[] = "reg2-";
69 // Lowest lexicographically ordered incoming message key.
70 // Used for prefixing messages.
71 const char kIncomingMsgKeyStart
[] = "incoming1-";
72 // Key guaranteed to be higher than all incoming message keys.
73 // Used for limiting iteration.
74 const char kIncomingMsgKeyEnd
[] = "incoming2-";
75 // Lowest lexicographically ordered outgoing message key.
76 // Used for prefixing outgoing messages.
77 const char kOutgoingMsgKeyStart
[] = "outgoing1-";
78 // Key guaranteed to be higher than all outgoing message keys.
79 // Used for limiting iteration.
80 const char kOutgoingMsgKeyEnd
[] = "outgoing2-";
81 // Lowest lexicographically ordered G-service settings key.
82 // Used for prefixing G-services settings.
83 const char kGServiceSettingKeyStart
[] = "gservice1-";
84 // Key guaranteed to be higher than all G-services settings keys.
85 // Used for limiting iteration.
86 const char kGServiceSettingKeyEnd
[] = "gservice2-";
87 // Key for digest of the last G-services settings update.
88 const char kGServiceSettingsDigestKey
[] = "gservices_digest";
89 // Key used to indicate how many accounts were last checked in with this device.
90 const char kLastCheckinAccountsKey
[] = "last_checkin_accounts_count";
91 // Key used to timestamp last checkin (marked with G services settings update).
92 const char kLastCheckinTimeKey
[] = "last_checkin_time";
93 // Lowest lexicographically ordered account key.
94 // Used for prefixing account information.
95 const char kAccountKeyStart
[] = "account1-";
96 // Key guaranteed to be higher than all account keys.
97 // Used for limiting iteration.
98 const char kAccountKeyEnd
[] = "account2-";
99 // Key used for last token fetch time.
100 const char kLastTokenFetchTimeKey
[] = "last_token_fetch_time";
102 std::string
MakeRegistrationKey(const std::string
& app_id
) {
103 return kRegistrationKeyStart
+ app_id
;
106 std::string
ParseRegistrationKey(const std::string
& key
) {
107 return key
.substr(arraysize(kRegistrationKeyStart
) - 1);
110 std::string
MakeIncomingKey(const std::string
& persistent_id
) {
111 return kIncomingMsgKeyStart
+ persistent_id
;
114 std::string
MakeOutgoingKey(const std::string
& persistent_id
) {
115 return kOutgoingMsgKeyStart
+ persistent_id
;
118 std::string
ParseOutgoingKey(const std::string
& key
) {
119 return key
.substr(arraysize(kOutgoingMsgKeyStart
) - 1);
122 std::string
MakeGServiceSettingKey(const std::string
& setting_name
) {
123 return kGServiceSettingKeyStart
+ setting_name
;
126 std::string
ParseGServiceSettingKey(const std::string
& key
) {
127 return key
.substr(arraysize(kGServiceSettingKeyStart
) - 1);
130 std::string
MakeAccountKey(const std::string
& account_id
) {
131 return kAccountKeyStart
+ account_id
;
134 std::string
ParseAccountKey(const std::string
& key
) {
135 return key
.substr(arraysize(kAccountKeyStart
) - 1);
138 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
139 // outlive the slice.
140 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
141 leveldb::Slice
MakeSlice(const base::StringPiece
& s
) {
142 return leveldb::Slice(s
.begin(), s
.size());
147 class GCMStoreImpl::Backend
148 : public base::RefCountedThreadSafe
<GCMStoreImpl::Backend
> {
150 Backend(const base::FilePath
& path
,
151 scoped_refptr
<base::SequencedTaskRunner
> foreground_runner
,
152 scoped_ptr
<Encryptor
> encryptor
);
154 // Blocking implementations of GCMStoreImpl methods.
155 void Load(const LoadCallback
& callback
);
157 void Destroy(const UpdateCallback
& callback
);
158 void SetDeviceCredentials(uint64 device_android_id
,
159 uint64 device_security_token
,
160 const UpdateCallback
& callback
);
161 void AddRegistration(const std::string
& app_id
,
162 const std::string
& serialized_registration
,
163 const UpdateCallback
& callback
);
164 void RemoveRegistration(const std::string
& app_id
,
165 const UpdateCallback
& callback
);
166 void AddIncomingMessage(const std::string
& persistent_id
,
167 const UpdateCallback
& callback
);
168 void RemoveIncomingMessages(const PersistentIdList
& persistent_ids
,
169 const UpdateCallback
& callback
);
170 void AddOutgoingMessage(const std::string
& persistent_id
,
171 const MCSMessage
& message
,
172 const UpdateCallback
& callback
);
173 void RemoveOutgoingMessages(
174 const PersistentIdList
& persistent_ids
,
175 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
177 void AddUserSerialNumber(const std::string
& username
,
179 const UpdateCallback
& callback
);
180 void RemoveUserSerialNumber(const std::string
& username
,
181 const UpdateCallback
& callback
);
182 void SetLastCheckinInfo(const base::Time
& time
,
183 const std::set
<std::string
>& accounts
,
184 const UpdateCallback
& callback
);
185 void SetGServicesSettings(
186 const std::map
<std::string
, std::string
>& settings
,
187 const std::string
& digest
,
188 const UpdateCallback
& callback
);
189 void AddAccountMapping(const AccountMapping
& account_mapping
,
190 const UpdateCallback
& callback
);
191 void RemoveAccountMapping(const std::string
& account_id
,
192 const UpdateCallback
& callback
);
193 void SetLastTokenFetchTime(const base::Time
& time
,
194 const UpdateCallback
& callback
);
195 void SetValue(const std::string
& key
,
196 const std::string
& value
,
197 const UpdateCallback
& callback
);
200 friend class base::RefCountedThreadSafe
<Backend
>;
203 LoadStatus
OpenStoreAndLoadData(LoadResult
* result
);
204 bool LoadDeviceCredentials(uint64
* android_id
, uint64
* security_token
);
205 bool LoadRegistrations(RegistrationInfoMap
* registrations
);
206 bool LoadIncomingMessages(std::vector
<std::string
>* incoming_messages
);
207 bool LoadOutgoingMessages(OutgoingMessageMap
* outgoing_messages
);
208 bool LoadLastCheckinInfo(base::Time
* last_checkin_time
,
209 std::set
<std::string
>* accounts
);
210 bool LoadGServicesSettings(std::map
<std::string
, std::string
>* settings
,
211 std::string
* digest
);
212 bool LoadAccountMappingInfo(AccountMappings
* account_mappings
);
213 bool LoadLastTokenFetchTime(base::Time
* last_token_fetch_time
);
215 const base::FilePath path_
;
216 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner_
;
217 scoped_ptr
<Encryptor
> encryptor_
;
219 scoped_ptr
<leveldb::DB
> db_
;
222 GCMStoreImpl::Backend::Backend(
223 const base::FilePath
& path
,
224 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner
,
225 scoped_ptr
<Encryptor
> encryptor
)
227 foreground_task_runner_(foreground_task_runner
),
228 encryptor_(encryptor
.Pass()) {
231 GCMStoreImpl::Backend::~Backend() {}
233 LoadStatus
GCMStoreImpl::Backend::OpenStoreAndLoadData(LoadResult
* result
) {
234 LoadStatus load_status
;
236 LOG(ERROR
) << "Attempting to reload open database.";
237 return RELOADING_OPEN_STORE
;
240 leveldb::Options options
;
241 options
.create_if_missing
= true;
243 leveldb::Status status
=
244 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
);
246 LOG(ERROR
) << "Failed to open database " << path_
.value() << ": "
247 << status
.ToString();
248 return OPENING_STORE_FAILED
;
252 if (!LoadDeviceCredentials(&result
->device_android_id
,
253 &result
->device_security_token
)) {
254 return LOADING_DEVICE_CREDENTIALS_FAILED
;
256 if (!LoadRegistrations(&result
->registrations
))
257 return LOADING_REGISTRATION_FAILED
;
258 if (!LoadIncomingMessages(&result
->incoming_messages
))
259 return LOADING_INCOMING_MESSAGES_FAILED
;
260 if (!LoadOutgoingMessages(&result
->outgoing_messages
))
261 return LOADING_OUTGOING_MESSAGES_FAILED
;
262 if (!LoadLastCheckinInfo(&result
->last_checkin_time
,
263 &result
->last_checkin_accounts
)) {
264 return LOADING_LAST_CHECKIN_INFO_FAILED
;
266 if (!LoadGServicesSettings(&result
->gservices_settings
,
267 &result
->gservices_digest
)) {
268 return load_status
= LOADING_GSERVICE_SETTINGS_FAILED
;
270 if (!LoadAccountMappingInfo(&result
->account_mappings
))
271 return LOADING_ACCOUNT_MAPPING_FAILED
;
272 if (!LoadLastTokenFetchTime(&result
->last_token_fetch_time
))
273 return LOADING_LAST_TOKEN_TIME_FAILED
;
275 return LOADING_SUCCEEDED
;
278 void GCMStoreImpl::Backend::Load(const LoadCallback
& callback
) {
279 scoped_ptr
<LoadResult
> result(new LoadResult());
280 LoadStatus load_status
= OpenStoreAndLoadData(result
.get());
281 UMA_HISTOGRAM_ENUMERATION("GCM.LoadStatus", load_status
, LOAD_STATUS_COUNT
);
282 if (load_status
!= LOADING_SUCCEEDED
) {
284 foreground_task_runner_
->PostTask(FROM_HERE
,
286 base::Passed(&result
)));
290 // Only record histograms if GCM had already been set up for this device.
291 if (result
->device_android_id
!= 0 && result
->device_security_token
!= 0) {
293 if (base::GetFileSize(path_
, &file_size
)) {
294 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
295 static_cast<int>(file_size
/ 1024));
297 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
298 result
->registrations
.size());
299 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
300 result
->outgoing_messages
.size());
301 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
302 result
->incoming_messages
.size());
305 DVLOG(1) << "Succeeded in loading " << result
->registrations
.size()
306 << " registrations, "
307 << result
->incoming_messages
.size()
308 << " unacknowledged incoming messages and "
309 << result
->outgoing_messages
.size()
310 << " unacknowledged outgoing messages.";
311 result
->success
= true;
312 foreground_task_runner_
->PostTask(FROM_HERE
,
314 base::Passed(&result
)));
318 void GCMStoreImpl::Backend::Close() {
319 DVLOG(1) << "Closing GCM store.";
323 void GCMStoreImpl::Backend::Destroy(const UpdateCallback
& callback
) {
324 DVLOG(1) << "Destroying GCM store.";
326 const leveldb::Status s
=
327 leveldb::DestroyDB(path_
.AsUTF8Unsafe(), leveldb::Options());
329 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
332 LOG(ERROR
) << "Destroy failed: " << s
.ToString();
333 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
336 void GCMStoreImpl::Backend::SetDeviceCredentials(
337 uint64 device_android_id
,
338 uint64 device_security_token
,
339 const UpdateCallback
& callback
) {
340 DVLOG(1) << "Saving device credentials with AID " << device_android_id
;
342 LOG(ERROR
) << "GCMStore db doesn't exist.";
343 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
347 leveldb::WriteOptions write_options
;
348 write_options
.sync
= true;
350 std::string encrypted_token
;
351 encryptor_
->EncryptString(base::Uint64ToString(device_security_token
),
353 std::string android_id_str
= base::Uint64ToString(device_android_id
);
355 db_
->Put(write_options
,
356 MakeSlice(kDeviceAIDKey
),
357 MakeSlice(android_id_str
));
360 write_options
, MakeSlice(kDeviceTokenKey
), MakeSlice(encrypted_token
));
363 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
366 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
367 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
370 void GCMStoreImpl::Backend::AddRegistration(
371 const std::string
& app_id
,
372 const std::string
& serialized_registration
,
373 const UpdateCallback
& callback
) {
374 DVLOG(1) << "Saving registration info for app: " << app_id
;
376 LOG(ERROR
) << "GCMStore db doesn't exist.";
377 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
380 leveldb::WriteOptions write_options
;
381 write_options
.sync
= true;
383 std::string key
= MakeRegistrationKey(app_id
);
384 const leveldb::Status status
= db_
->Put(write_options
,
386 MakeSlice(serialized_registration
));
388 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
391 LOG(ERROR
) << "LevelDB put failed: " << status
.ToString();
392 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
395 void GCMStoreImpl::Backend::RemoveRegistration(const std::string
& app_id
,
396 const UpdateCallback
& callback
) {
398 LOG(ERROR
) << "GCMStore db doesn't exist.";
399 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
402 leveldb::WriteOptions write_options
;
403 write_options
.sync
= true;
405 leveldb::Status status
=
406 db_
->Delete(write_options
, MakeSlice(MakeRegistrationKey(app_id
)));
408 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
411 LOG(ERROR
) << "LevelDB remove failed: " << status
.ToString();
412 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
415 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string
& persistent_id
,
416 const UpdateCallback
& callback
) {
417 DVLOG(1) << "Saving incoming message with id " << persistent_id
;
419 LOG(ERROR
) << "GCMStore db doesn't exist.";
420 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
424 leveldb::WriteOptions write_options
;
425 write_options
.sync
= true;
427 std::string key
= MakeIncomingKey(persistent_id
);
428 const leveldb::Status s
= db_
->Put(write_options
,
430 MakeSlice(persistent_id
));
432 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
435 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
436 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
439 void GCMStoreImpl::Backend::RemoveIncomingMessages(
440 const PersistentIdList
& persistent_ids
,
441 const UpdateCallback
& callback
) {
443 LOG(ERROR
) << "GCMStore db doesn't exist.";
444 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
447 leveldb::WriteOptions write_options
;
448 write_options
.sync
= true;
451 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
452 iter
!= persistent_ids
.end();
454 DVLOG(1) << "Removing incoming message with id " << *iter
;
455 std::string key
= MakeIncomingKey(*iter
);
456 s
= db_
->Delete(write_options
, MakeSlice(key
));
461 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
464 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
465 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
468 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string
& persistent_id
,
469 const MCSMessage
& message
,
470 const UpdateCallback
& callback
) {
471 DVLOG(1) << "Saving outgoing message with id " << persistent_id
;
473 LOG(ERROR
) << "GCMStore db doesn't exist.";
474 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
477 leveldb::WriteOptions write_options
;
478 write_options
.sync
= true;
481 static_cast<char>(message
.tag()) + message
.SerializeAsString();
482 std::string key
= MakeOutgoingKey(persistent_id
);
483 const leveldb::Status s
= db_
->Put(write_options
,
487 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
490 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
491 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
494 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
495 const PersistentIdList
& persistent_ids
,
496 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
499 LOG(ERROR
) << "GCMStore db doesn't exist.";
500 foreground_task_runner_
->PostTask(FROM_HERE
,
503 AppIdToMessageCountMap()));
506 leveldb::ReadOptions read_options
;
507 leveldb::WriteOptions write_options
;
508 write_options
.sync
= true;
510 AppIdToMessageCountMap removed_message_counts
;
513 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
514 iter
!= persistent_ids
.end();
516 DVLOG(1) << "Removing outgoing message with id " << *iter
;
517 std::string outgoing_message
;
518 std::string key
= MakeOutgoingKey(*iter
);
519 s
= db_
->Get(read_options
,
524 mcs_proto::DataMessageStanza data_message
;
525 // Skip the initial tag byte and parse the rest to extract the message.
526 if (data_message
.ParseFromString(outgoing_message
.substr(1))) {
527 DCHECK(!data_message
.category().empty());
528 if (removed_message_counts
.count(data_message
.category()) != 0)
529 removed_message_counts
[data_message
.category()]++;
531 removed_message_counts
[data_message
.category()] = 1;
533 DVLOG(1) << "Removing outgoing message with id " << *iter
;
534 s
= db_
->Delete(write_options
, MakeSlice(key
));
539 foreground_task_runner_
->PostTask(FROM_HERE
,
542 removed_message_counts
));
545 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
546 foreground_task_runner_
->PostTask(FROM_HERE
,
549 AppIdToMessageCountMap()));
552 void GCMStoreImpl::Backend::SetLastCheckinInfo(
553 const base::Time
& time
,
554 const std::set
<std::string
>& accounts
,
555 const UpdateCallback
& callback
) {
556 leveldb::WriteBatch write_batch
;
558 int64 last_checkin_time_internal
= time
.ToInternalValue();
559 write_batch
.Put(MakeSlice(kLastCheckinTimeKey
),
560 MakeSlice(base::Int64ToString(last_checkin_time_internal
)));
562 std::string serialized_accounts
;
563 for (std::set
<std::string
>::iterator iter
= accounts
.begin();
564 iter
!= accounts
.end();
566 serialized_accounts
+= *iter
;
567 serialized_accounts
+= ",";
569 if (!serialized_accounts
.empty())
570 serialized_accounts
.erase(serialized_accounts
.length() - 1);
572 write_batch
.Put(MakeSlice(kLastCheckinAccountsKey
),
573 MakeSlice(serialized_accounts
));
575 leveldb::WriteOptions write_options
;
576 write_options
.sync
= true;
577 const leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
580 LOG(ERROR
) << "LevelDB set last checkin info failed: " << s
.ToString();
581 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
584 void GCMStoreImpl::Backend::SetGServicesSettings(
585 const std::map
<std::string
, std::string
>& settings
,
586 const std::string
& settings_digest
,
587 const UpdateCallback
& callback
) {
588 leveldb::WriteBatch write_batch
;
590 // Remove all existing settings.
591 leveldb::ReadOptions read_options
;
592 read_options
.verify_checksums
= true;
593 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
594 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
595 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
597 write_batch
.Delete(iter
->key());
600 // Add the new settings.
601 for (std::map
<std::string
, std::string
>::const_iterator iter
=
603 iter
!= settings
.end(); ++iter
) {
604 write_batch
.Put(MakeSlice(MakeGServiceSettingKey(iter
->first
)),
605 MakeSlice(iter
->second
));
608 // Update the settings digest.
609 write_batch
.Put(MakeSlice(kGServiceSettingsDigestKey
),
610 MakeSlice(settings_digest
));
612 // Write it all in a batch.
613 leveldb::WriteOptions write_options
;
614 write_options
.sync
= true;
616 leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
618 LOG(ERROR
) << "LevelDB GService Settings update failed: " << s
.ToString();
619 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
622 void GCMStoreImpl::Backend::AddAccountMapping(
623 const AccountMapping
& account_mapping
,
624 const UpdateCallback
& callback
) {
625 DVLOG(1) << "Saving account info for account with email: "
626 << account_mapping
.email
;
628 LOG(ERROR
) << "GCMStore db doesn't exist.";
629 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
633 leveldb::WriteOptions write_options
;
634 write_options
.sync
= true;
636 std::string data
= account_mapping
.SerializeAsString();
637 std::string key
= MakeAccountKey(account_mapping
.account_id
);
638 const leveldb::Status s
=
639 db_
->Put(write_options
, MakeSlice(key
), MakeSlice(data
));
641 LOG(ERROR
) << "LevelDB adding account mapping failed: " << s
.ToString();
642 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
645 void GCMStoreImpl::Backend::RemoveAccountMapping(
646 const std::string
& account_id
,
647 const UpdateCallback
& callback
) {
649 LOG(ERROR
) << "GCMStore db doesn't exist.";
650 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
654 leveldb::WriteOptions write_options
;
655 write_options
.sync
= true;
658 db_
->Delete(write_options
, MakeSlice(MakeAccountKey(account_id
)));
661 LOG(ERROR
) << "LevelDB removal of account mapping failed: " << s
.ToString();
662 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
665 void GCMStoreImpl::Backend::SetLastTokenFetchTime(
666 const base::Time
& time
,
667 const UpdateCallback
& callback
) {
668 DVLOG(1) << "Setting last token fetching time.";
670 LOG(ERROR
) << "GCMStore db doesn't exist.";
671 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
675 leveldb::WriteOptions write_options
;
676 write_options
.sync
= true;
678 const leveldb::Status s
=
679 db_
->Put(write_options
,
680 MakeSlice(kLastTokenFetchTimeKey
),
681 MakeSlice(base::Int64ToString(time
.ToInternalValue())));
684 LOG(ERROR
) << "LevelDB setting last token fetching time: " << s
.ToString();
685 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
688 void GCMStoreImpl::Backend::SetValue(const std::string
& key
,
689 const std::string
& value
,
690 const UpdateCallback
& callback
) {
691 DVLOG(1) << "Injecting a value to GCM Store for testing. Key: "
692 << key
<< ", Value: " << value
;
694 LOG(ERROR
) << "GCMStore db doesn't exist.";
695 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
699 leveldb::WriteOptions write_options
;
700 write_options
.sync
= true;
702 const leveldb::Status s
=
703 db_
->Put(write_options
, MakeSlice(key
), MakeSlice(value
));
706 LOG(ERROR
) << "LevelDB had problems injecting a value: " << s
.ToString();
707 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
710 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64
* android_id
,
711 uint64
* security_token
) {
712 leveldb::ReadOptions read_options
;
713 read_options
.verify_checksums
= true;
716 leveldb::Status s
= db_
->Get(read_options
, MakeSlice(kDeviceAIDKey
), &result
);
718 if (!base::StringToUint64(result
, android_id
)) {
719 LOG(ERROR
) << "Failed to restore device id.";
723 s
= db_
->Get(read_options
, MakeSlice(kDeviceTokenKey
), &result
);
726 std::string decrypted_token
;
727 encryptor_
->DecryptString(result
, &decrypted_token
);
728 if (!base::StringToUint64(decrypted_token
, security_token
)) {
729 LOG(ERROR
) << "Failed to restore security token.";
735 if (s
.IsNotFound()) {
736 DVLOG(1) << "No credentials found.";
740 LOG(ERROR
) << "Error reading credentials from store.";
744 bool GCMStoreImpl::Backend::LoadRegistrations(
745 RegistrationInfoMap
* registrations
) {
746 leveldb::ReadOptions read_options
;
747 read_options
.verify_checksums
= true;
749 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
750 for (iter
->Seek(MakeSlice(kRegistrationKeyStart
));
751 iter
->Valid() && iter
->key().ToString() < kRegistrationKeyEnd
;
753 leveldb::Slice s
= iter
->value();
755 LOG(ERROR
) << "Error reading registration with key " << s
.ToString();
758 std::string app_id
= ParseRegistrationKey(iter
->key().ToString());
759 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
760 if (!registration
->ParseFromString(iter
->value().ToString())) {
761 LOG(ERROR
) << "Failed to parse registration with app id " << app_id
;
764 DVLOG(1) << "Found registration with app id " << app_id
;
765 (*registrations
)[app_id
] = registration
;
771 bool GCMStoreImpl::Backend::LoadIncomingMessages(
772 std::vector
<std::string
>* incoming_messages
) {
773 leveldb::ReadOptions read_options
;
774 read_options
.verify_checksums
= true;
776 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
777 for (iter
->Seek(MakeSlice(kIncomingMsgKeyStart
));
778 iter
->Valid() && iter
->key().ToString() < kIncomingMsgKeyEnd
;
780 leveldb::Slice s
= iter
->value();
782 LOG(ERROR
) << "Error reading incoming message with key "
783 << iter
->key().ToString();
786 DVLOG(1) << "Found incoming message with id " << s
.ToString();
787 incoming_messages
->push_back(s
.ToString());
793 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
794 OutgoingMessageMap
* outgoing_messages
) {
795 leveldb::ReadOptions read_options
;
796 read_options
.verify_checksums
= true;
798 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
799 for (iter
->Seek(MakeSlice(kOutgoingMsgKeyStart
));
800 iter
->Valid() && iter
->key().ToString() < kOutgoingMsgKeyEnd
;
802 leveldb::Slice s
= iter
->value();
804 LOG(ERROR
) << "Error reading incoming message with key " << s
.ToString();
807 uint8 tag
= iter
->value().data()[0];
808 std::string id
= ParseOutgoingKey(iter
->key().ToString());
809 scoped_ptr
<google::protobuf::MessageLite
> message(
810 BuildProtobufFromTag(tag
));
811 if (!message
.get() ||
812 !message
->ParseFromString(iter
->value().ToString().substr(1))) {
813 LOG(ERROR
) << "Failed to parse outgoing message with id " << id
814 << " and tag " << tag
;
817 DVLOG(1) << "Found outgoing message with id " << id
<< " of type "
818 << base::IntToString(tag
);
819 (*outgoing_messages
)[id
] = make_linked_ptr(message
.release());
825 bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
826 base::Time
* last_checkin_time
,
827 std::set
<std::string
>* accounts
) {
828 leveldb::ReadOptions read_options
;
829 read_options
.verify_checksums
= true;
832 leveldb::Status s
= db_
->Get(read_options
,
833 MakeSlice(kLastCheckinTimeKey
),
835 int64 time_internal
= 0LL;
836 if (s
.ok() && !base::StringToInt64(result
, &time_internal
)) {
837 LOG(ERROR
) << "Failed to restore last checkin time. Using default = 0.";
841 // In case we cannot read last checkin time, we default it to 0, as we don't
842 // want that situation to cause the whole load to fail.
843 *last_checkin_time
= base::Time::FromInternalValue(time_internal
);
846 s
= db_
->Get(read_options
, MakeSlice(kLastCheckinAccountsKey
), &result
);
848 DVLOG(1) << "No accounts where stored during last run.";
850 base::StringTokenizer
t(result
, ",");
852 accounts
->insert(t
.token());
857 bool GCMStoreImpl::Backend::LoadGServicesSettings(
858 std::map
<std::string
, std::string
>* settings
,
859 std::string
* digest
) {
860 leveldb::ReadOptions read_options
;
861 read_options
.verify_checksums
= true;
863 // Load all of the GServices settings.
864 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
865 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
866 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
868 std::string value
= iter
->value().ToString();
870 LOG(ERROR
) << "Error reading GService Settings " << value
;
873 std::string id
= ParseGServiceSettingKey(iter
->key().ToString());
874 (*settings
)[id
] = value
;
875 DVLOG(1) << "Found G Service setting with key: " << id
876 << ", and value: " << value
;
879 // Load the settings digest. It's ok if it is empty.
880 db_
->Get(read_options
, MakeSlice(kGServiceSettingsDigestKey
), digest
);
885 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
886 AccountMappings
* account_mappings
) {
887 leveldb::ReadOptions read_options
;
888 read_options
.verify_checksums
= true;
890 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
891 for (iter
->Seek(MakeSlice(kAccountKeyStart
));
892 iter
->Valid() && iter
->key().ToString() < kAccountKeyEnd
;
894 AccountMapping account_mapping
;
895 account_mapping
.account_id
= ParseAccountKey(iter
->key().ToString());
896 if (!account_mapping
.ParseFromString(iter
->value().ToString())) {
897 DVLOG(1) << "Failed to parse account info with ID: "
898 << account_mapping
.account_id
;
901 DVLOG(1) << "Found account mapping with ID: " << account_mapping
.account_id
;
902 account_mappings
->push_back(account_mapping
);
908 bool GCMStoreImpl::Backend::LoadLastTokenFetchTime(
909 base::Time
* last_token_fetch_time
) {
910 leveldb::ReadOptions read_options
;
911 read_options
.verify_checksums
= true;
915 db_
->Get(read_options
, MakeSlice(kLastTokenFetchTimeKey
), &result
);
916 int64 time_internal
= 0LL;
917 if (s
.ok() && !base::StringToInt64(result
, &time_internal
)) {
919 "Failed to restore last token fetching time. Using default = 0.";
923 // In case we cannot read last token fetching time, we default it to 0.
924 *last_token_fetch_time
= base::Time::FromInternalValue(time_internal
);
929 GCMStoreImpl::GCMStoreImpl(
930 const base::FilePath
& path
,
931 scoped_refptr
<base::SequencedTaskRunner
> blocking_task_runner
,
932 scoped_ptr
<Encryptor
> encryptor
)
933 : backend_(new Backend(path
,
934 base::MessageLoopProxy::current(),
936 blocking_task_runner_(blocking_task_runner
),
937 weak_ptr_factory_(this) {
940 GCMStoreImpl::~GCMStoreImpl() {}
942 void GCMStoreImpl::Load(const LoadCallback
& callback
) {
943 blocking_task_runner_
->PostTask(
945 base::Bind(&GCMStoreImpl::Backend::Load
,
947 base::Bind(&GCMStoreImpl::LoadContinuation
,
948 weak_ptr_factory_
.GetWeakPtr(),
952 void GCMStoreImpl::Close() {
953 weak_ptr_factory_
.InvalidateWeakPtrs();
954 app_message_counts_
.clear();
955 blocking_task_runner_
->PostTask(
957 base::Bind(&GCMStoreImpl::Backend::Close
, backend_
));
960 void GCMStoreImpl::Destroy(const UpdateCallback
& callback
) {
961 blocking_task_runner_
->PostTask(
963 base::Bind(&GCMStoreImpl::Backend::Destroy
, backend_
, callback
));
966 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id
,
967 uint64 device_security_token
,
968 const UpdateCallback
& callback
) {
969 blocking_task_runner_
->PostTask(
971 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials
,
974 device_security_token
,
978 void GCMStoreImpl::AddRegistration(
979 const std::string
& app_id
,
980 const linked_ptr
<RegistrationInfo
>& registration
,
981 const UpdateCallback
& callback
) {
982 std::string serialized_registration
= registration
->SerializeAsString();
983 blocking_task_runner_
->PostTask(
985 base::Bind(&GCMStoreImpl::Backend::AddRegistration
,
988 serialized_registration
,
992 void GCMStoreImpl::RemoveRegistration(const std::string
& app_id
,
993 const UpdateCallback
& callback
) {
994 blocking_task_runner_
->PostTask(
996 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration
,
1002 void GCMStoreImpl::AddIncomingMessage(const std::string
& persistent_id
,
1003 const UpdateCallback
& callback
) {
1004 blocking_task_runner_
->PostTask(
1006 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage
,
1012 void GCMStoreImpl::RemoveIncomingMessage(const std::string
& persistent_id
,
1013 const UpdateCallback
& callback
) {
1014 blocking_task_runner_
->PostTask(
1016 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
1018 PersistentIdList(1, persistent_id
),
1022 void GCMStoreImpl::RemoveIncomingMessages(
1023 const PersistentIdList
& persistent_ids
,
1024 const UpdateCallback
& callback
) {
1025 blocking_task_runner_
->PostTask(
1027 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
1033 bool GCMStoreImpl::AddOutgoingMessage(const std::string
& persistent_id
,
1034 const MCSMessage
& message
,
1035 const UpdateCallback
& callback
) {
1036 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
1037 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
1038 &message
.GetProtobuf())->category();
1039 DCHECK(!app_id
.empty());
1040 if (app_message_counts_
.count(app_id
) == 0)
1041 app_message_counts_
[app_id
] = 0;
1042 if (app_message_counts_
[app_id
] < kMessagesPerAppLimit
) {
1043 app_message_counts_
[app_id
]++;
1045 blocking_task_runner_
->PostTask(
1047 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
1051 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation
,
1052 weak_ptr_factory_
.GetWeakPtr(),
1060 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string
& persistent_id
,
1061 const MCSMessage
& message
,
1062 const UpdateCallback
& callback
) {
1063 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
1064 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
1065 &message
.GetProtobuf())->category();
1066 DCHECK(!app_id
.empty());
1067 // There should already be pending messages for this app.
1068 DCHECK(app_message_counts_
.count(app_id
));
1069 // TODO(zea): consider verifying the specific message already exists.
1070 blocking_task_runner_
->PostTask(
1072 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
1079 void GCMStoreImpl::RemoveOutgoingMessage(const std::string
& persistent_id
,
1080 const UpdateCallback
& callback
) {
1081 blocking_task_runner_
->PostTask(
1083 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
1085 PersistentIdList(1, persistent_id
),
1086 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
1087 weak_ptr_factory_
.GetWeakPtr(),
1091 void GCMStoreImpl::RemoveOutgoingMessages(
1092 const PersistentIdList
& persistent_ids
,
1093 const UpdateCallback
& callback
) {
1094 blocking_task_runner_
->PostTask(
1096 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
1099 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
1100 weak_ptr_factory_
.GetWeakPtr(),
1104 void GCMStoreImpl::SetLastCheckinInfo(const base::Time
& time
,
1105 const std::set
<std::string
>& accounts
,
1106 const UpdateCallback
& callback
) {
1107 blocking_task_runner_
->PostTask(
1109 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo
,
1116 void GCMStoreImpl::SetGServicesSettings(
1117 const std::map
<std::string
, std::string
>& settings
,
1118 const std::string
& digest
,
1119 const UpdateCallback
& callback
) {
1120 blocking_task_runner_
->PostTask(
1122 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings
,
1129 void GCMStoreImpl::AddAccountMapping(const AccountMapping
& account_mapping
,
1130 const UpdateCallback
& callback
) {
1131 blocking_task_runner_
->PostTask(
1133 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping
,
1139 void GCMStoreImpl::RemoveAccountMapping(const std::string
& account_id
,
1140 const UpdateCallback
& callback
) {
1141 blocking_task_runner_
->PostTask(
1143 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping
,
1149 void GCMStoreImpl::SetLastTokenFetchTime(const base::Time
& time
,
1150 const UpdateCallback
& callback
) {
1151 blocking_task_runner_
->PostTask(
1153 base::Bind(&GCMStoreImpl::Backend::SetLastTokenFetchTime
,
1159 void GCMStoreImpl::SetValueForTesting(const std::string
& key
,
1160 const std::string
& value
,
1161 const UpdateCallback
& callback
) {
1162 blocking_task_runner_
->PostTask(
1164 base::Bind(&GCMStoreImpl::Backend::SetValue
,
1171 void GCMStoreImpl::LoadContinuation(const LoadCallback
& callback
,
1172 scoped_ptr
<LoadResult
> result
) {
1173 if (!result
->success
) {
1174 callback
.Run(result
.Pass());
1177 int num_throttled_apps
= 0;
1178 for (OutgoingMessageMap::const_iterator
1179 iter
= result
->outgoing_messages
.begin();
1180 iter
!= result
->outgoing_messages
.end(); ++iter
) {
1181 const mcs_proto::DataMessageStanza
* data_message
=
1182 reinterpret_cast<mcs_proto::DataMessageStanza
*>(iter
->second
.get());
1183 DCHECK(!data_message
->category().empty());
1184 if (app_message_counts_
.count(data_message
->category()) == 0)
1185 app_message_counts_
[data_message
->category()] = 1;
1187 app_message_counts_
[data_message
->category()]++;
1188 if (app_message_counts_
[data_message
->category()] == kMessagesPerAppLimit
)
1189 num_throttled_apps
++;
1191 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps
);
1192 callback
.Run(result
.Pass());
1195 void GCMStoreImpl::AddOutgoingMessageContinuation(
1196 const UpdateCallback
& callback
,
1197 const std::string
& app_id
,
1200 DCHECK(app_message_counts_
[app_id
] > 0);
1201 app_message_counts_
[app_id
]--;
1203 callback
.Run(success
);
1206 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1207 const UpdateCallback
& callback
,
1209 const AppIdToMessageCountMap
& removed_message_counts
) {
1211 callback
.Run(false);
1214 for (AppIdToMessageCountMap::const_iterator iter
=
1215 removed_message_counts
.begin();
1216 iter
!= removed_message_counts
.end(); ++iter
) {
1217 DCHECK_NE(app_message_counts_
.count(iter
->first
), 0U);
1218 app_message_counts_
[iter
->first
] -= iter
->second
;
1219 DCHECK_GE(app_message_counts_
[iter
->first
], 0);