12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/gcm_driver/gcm_client_impl.h"
- #include <stddef.h>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/files/file_path.h"
- #include "base/location.h"
- #include "base/logging.h"
- #include "base/memory/ptr_util.h"
- #include "base/metrics/histogram_functions.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/time/default_clock.h"
- #include "base/timer/timer.h"
- #include "components/crx_file/id_util.h"
- #include "components/gcm_driver/crypto/gcm_decryption_result.h"
- #include "components/gcm_driver/features.h"
- #include "components/gcm_driver/gcm_account_mapper.h"
- #include "components/gcm_driver/gcm_backoff_policy.h"
- #include "google_apis/gcm/base/encryptor.h"
- #include "google_apis/gcm/base/mcs_message.h"
- #include "google_apis/gcm/base/mcs_util.h"
- #include "google_apis/gcm/engine/checkin_request.h"
- #include "google_apis/gcm/engine/connection_factory_impl.h"
- #include "google_apis/gcm/engine/gcm_registration_request_handler.h"
- #include "google_apis/gcm/engine/gcm_store_impl.h"
- #include "google_apis/gcm/engine/gcm_unregistration_request_handler.h"
- #include "google_apis/gcm/engine/instance_id_delete_token_request_handler.h"
- #include "google_apis/gcm/engine/instance_id_get_token_request_handler.h"
- #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
- #include "google_apis/gcm/protocol/checkin.pb.h"
- #include "google_apis/gcm/protocol/mcs.pb.h"
- #include "services/network/public/cpp/shared_url_loader_factory.h"
- #include "url/gurl.h"
- namespace gcm {
- // It is okay to append to the enum if these states grow. DO NOT reorder,
- // renumber or otherwise reuse existing values.
- // Do not assign an explicit value to REGISTRATION_CACHE_STATUS_COUNT, as
- // this lets the compiler keep it up to date.
- enum class RegistrationCacheStatus {
- REGISTRATION_NOT_FOUND = 0,
- REGISTRATION_FOUND_AND_FRESH = 1,
- REGISTRATION_FOUND_BUT_STALE = 2,
- REGISTRATION_FOUND_BUT_SENDERS_DONT_MATCH = 3,
- // NOTE: always keep this entry at the end. Add new value only immediately
- // above this line. Make sure to update the corresponding histogram enum
- // accordingly.
- REGISTRATION_CACHE_STATUS_COUNT
- };
- namespace {
- // Indicates a message type of the received message.
- enum MessageType {
- UNKNOWN, // Undetermined type.
- DATA_MESSAGE, // Regular data message.
- DELETED_MESSAGES, // Messages were deleted on the server.
- SEND_ERROR, // Error sending a message.
- };
- enum ResetStoreError {
- DESTROYING_STORE_FAILED,
- INFINITE_STORE_RESET,
- // NOTE: always keep this entry at the end. Add new value only immediately
- // above this line. Make sure to update the corresponding histogram enum
- // accordingly.
- RESET_STORE_ERROR_COUNT
- };
- const int kMaxRegistrationRetries = 5;
- const int kMaxUnregistrationRetries = 5;
- const char kDeletedCountKey[] = "total_deleted";
- const char kMessageTypeDataMessage[] = "gcm";
- const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
- const char kMessageTypeKey[] = "message_type";
- const char kMessageTypeSendErrorKey[] = "send_error";
- const char kSubtypeKey[] = "subtype";
- const char kSendMessageFromValue[] = "gcm@chrome.com";
- const int64_t kDefaultUserSerialNumber = 0LL;
- const int kDestroyGCMStoreDelayMS = 5 * 60 * 1000; // 5 minutes.
- GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
- switch (status) {
- case MCSClient::QUEUED:
- return GCMClient::SUCCESS;
- case MCSClient::MESSAGE_TOO_LARGE:
- return GCMClient::INVALID_PARAMETER;
- case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
- case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
- case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
- case MCSClient::TTL_EXCEEDED:
- return GCMClient::NETWORK_ERROR;
- case MCSClient::SENT:
- case MCSClient::SEND_STATUS_COUNT:
- NOTREACHED();
- break;
- }
- return GCMClientImpl::UNKNOWN_ERROR;
- }
- void ToCheckinProtoVersion(
- const GCMClient::ChromeBuildInfo& chrome_build_info,
- checkin_proto::ChromeBuildProto* android_build_info) {
- checkin_proto::ChromeBuildProto_Platform platform =
- checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
- switch (chrome_build_info.platform) {
- case GCMClient::PLATFORM_WIN:
- platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
- break;
- case GCMClient::PLATFORM_MAC:
- platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
- break;
- case GCMClient::PLATFORM_LINUX:
- platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
- break;
- case GCMClient::PLATFORM_IOS:
- platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
- break;
- case GCMClient::PLATFORM_ANDROID:
- platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
- break;
- case GCMClient::PLATFORM_CROS:
- platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
- break;
- case GCMClient::PLATFORM_UNSPECIFIED:
- // For unknown platform, return as LINUX.
- platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
- break;
- }
- android_build_info->set_platform(platform);
- checkin_proto::ChromeBuildProto_Channel channel =
- checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
- switch (chrome_build_info.channel) {
- case GCMClient::CHANNEL_STABLE:
- channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
- break;
- case GCMClient::CHANNEL_BETA:
- channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
- break;
- case GCMClient::CHANNEL_DEV:
- channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
- break;
- case GCMClient::CHANNEL_CANARY:
- channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
- break;
- case GCMClient::CHANNEL_UNKNOWN:
- channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
- break;
- }
- android_build_info->set_channel(channel);
- android_build_info->set_chrome_version(chrome_build_info.version);
- }
- MessageType DecodeMessageType(const std::string& value) {
- if (kMessageTypeDeletedMessagesKey == value)
- return DELETED_MESSAGES;
- if (kMessageTypeSendErrorKey == value)
- return SEND_ERROR;
- if (kMessageTypeDataMessage == value)
- return DATA_MESSAGE;
- return UNKNOWN;
- }
- int ConstructGCMVersion(const std::string& chrome_version) {
- // Major Chrome version is passed as GCM version.
- size_t pos = chrome_version.find('.');
- if (pos == std::string::npos) {
- NOTREACHED();
- return 0;
- }
- int gcm_version = 0;
- base::StringToInt(
- base::StringPiece(chrome_version.c_str(), pos), &gcm_version);
- return gcm_version;
- }
- std::string SerializeInstanceIDData(const std::string& instance_id,
- const std::string& extra_data) {
- DCHECK(!instance_id.empty() && !extra_data.empty());
- DCHECK(instance_id.find(',') == std::string::npos);
- return instance_id + "," + extra_data;
- }
- bool DeserializeInstanceIDData(const std::string& serialized_data,
- std::string* instance_id,
- std::string* extra_data) {
- DCHECK(instance_id && extra_data);
- std::size_t pos = serialized_data.find(',');
- if (pos == std::string::npos)
- return false;
- *instance_id = serialized_data.substr(0, pos);
- *extra_data = serialized_data.substr(pos + 1);
- return !instance_id->empty() && !extra_data->empty();
- }
- bool InstanceIDUsesSubtypeForAppId(const std::string& app_id) {
- // Always use subtypes with Instance ID, except for Chrome Apps/Extensions.
- return !crx_file::id_util::IdIsValid(app_id);
- }
- void RecordResetStoreErrorToUMA(ResetStoreError error) {
- UMA_HISTOGRAM_ENUMERATION("GCM.ResetStore", error, RESET_STORE_ERROR_COUNT);
- }
- } // namespace
- void RecordRegistrationRequestToUMA(gcm::RegistrationCacheStatus status) {
- UMA_HISTOGRAM_ENUMERATION(
- "GCM.RegistrationCacheStatus", status,
- RegistrationCacheStatus::REGISTRATION_CACHE_STATUS_COUNT);
- }
- GCMInternalsBuilder::GCMInternalsBuilder() {}
- GCMInternalsBuilder::~GCMInternalsBuilder() {}
- base::Clock* GCMInternalsBuilder::GetClock() {
- return base::DefaultClock::GetInstance();
- }
- std::unique_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
- const std::string& version,
- base::Clock* clock,
- ConnectionFactory* connection_factory,
- GCMStore* gcm_store,
- scoped_refptr<base::SequencedTaskRunner> io_task_runner,
- GCMStatsRecorder* recorder) {
- return std::make_unique<MCSClient>(version, clock, connection_factory,
- gcm_store, std::move(io_task_runner),
- recorder);
- }
- std::unique_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
- const std::vector<GURL>& endpoints,
- const net::BackoffEntry::Policy& backoff_policy,
- base::RepeatingCallback<void(
- mojo::PendingReceiver<network::mojom::ProxyResolvingSocketFactory>)>
- get_socket_factory_callback,
- scoped_refptr<base::SequencedTaskRunner> io_task_runner,
- GCMStatsRecorder* recorder,
- network::NetworkConnectionTracker* network_connection_tracker) {
- return std::make_unique<ConnectionFactoryImpl>(
- endpoints, backoff_policy, std::move(get_socket_factory_callback),
- std::move(io_task_runner), recorder, network_connection_tracker);
- }
- GCMClientImpl::CheckinInfo::CheckinInfo()
- : android_id(0), secret(0), accounts_set(false) {
- }
- GCMClientImpl::CheckinInfo::~CheckinInfo() {
- }
- void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
- last_checkin_accounts.clear();
- for (auto iter = account_tokens.begin(); iter != account_tokens.end();
- ++iter) {
- last_checkin_accounts.insert(iter->first);
- }
- }
- void GCMClientImpl::CheckinInfo::Reset() {
- android_id = 0;
- secret = 0;
- accounts_set = false;
- account_tokens.clear();
- last_checkin_accounts.clear();
- }
- GCMClientImpl::GCMClientImpl(
- std::unique_ptr<GCMInternalsBuilder> internals_builder)
- : internals_builder_(std::move(internals_builder)),
- state_(UNINITIALIZED),
- delegate_(nullptr),
- start_mode_(DELAYED_START),
- clock_(internals_builder_->GetClock()),
- gcm_store_reset_(false),
- network_connection_tracker_(nullptr) {}
- GCMClientImpl::~GCMClientImpl() {
- }
- void GCMClientImpl::Initialize(
- const ChromeBuildInfo& chrome_build_info,
- const base::FilePath& path,
- bool remove_account_mappings_with_email_key,
- const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
- scoped_refptr<base::SequencedTaskRunner> io_task_runner,
- base::RepeatingCallback<void(
- mojo::PendingReceiver<network::mojom::ProxyResolvingSocketFactory>)>
- get_socket_factory_callback,
- const scoped_refptr<network::SharedURLLoaderFactory>& url_loader_factory,
- network::NetworkConnectionTracker* network_connection_tracker,
- std::unique_ptr<Encryptor> encryptor,
- GCMClient::Delegate* delegate) {
- DCHECK_EQ(UNINITIALIZED, state_);
- DCHECK(delegate);
- DCHECK(io_task_runner);
- DCHECK(io_task_runner->RunsTasksInCurrentSequence());
- get_socket_factory_callback_ = std::move(get_socket_factory_callback);
- url_loader_factory_ = url_loader_factory;
- network_connection_tracker_ = network_connection_tracker;
- chrome_build_info_ = chrome_build_info;
- gcm_store_ = std::make_unique<GCMStoreImpl>(
- path, remove_account_mappings_with_email_key, blocking_task_runner,
- std::move(encryptor));
- delegate_ = delegate;
- io_task_runner_ = std::move(io_task_runner);
- recorder_.SetDelegate(this);
- state_ = INITIALIZED;
- }
- void GCMClientImpl::Start(StartMode start_mode) {
- DCHECK_NE(UNINITIALIZED, state_);
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- if (state_ == LOADED) {
- // Start the GCM if not yet.
- if (start_mode == IMMEDIATE_START) {
- // Give up the scheduling to wipe out the store since now some one starts
- // to use GCM.
- destroying_gcm_store_ptr_factory_.InvalidateWeakPtrs();
- StartGCM();
- }
- return;
- }
- // The delay start behavior will be abandoned when Start has been called
- // once with IMMEDIATE_START behavior.
- if (start_mode == IMMEDIATE_START)
- start_mode_ = IMMEDIATE_START;
- // Bail out if the loading is not started or completed.
- if (state_ != INITIALIZED)
- return;
- // Once the loading is completed, the check-in will be initiated.
- // If we're in lazy start mode, don't create a new store since none is really
- // using GCM functionality yet.
- gcm_store_->Load((start_mode == IMMEDIATE_START) ? GCMStore::CREATE_IF_MISSING
- : GCMStore::DO_NOT_CREATE,
- base::BindOnce(&GCMClientImpl::OnLoadCompleted,
- weak_ptr_factory_.GetWeakPtr()));
- state_ = LOADING;
- }
- void GCMClientImpl::OnLoadCompleted(
- std::unique_ptr<GCMStore::LoadResult> result) {
- DCHECK_EQ(LOADING, state_);
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- if (!result->success) {
- if (result->store_does_not_exist) {
- if (start_mode_ == IMMEDIATE_START) {
- // An immediate start was requested during the delayed start that just
- // completed. Perform it now.
- gcm_store_->Load(GCMStore::CREATE_IF_MISSING,
- base::BindOnce(&GCMClientImpl::OnLoadCompleted,
- weak_ptr_factory_.GetWeakPtr()));
- } else {
- // In the case that the store does not exist, set |state_| back to
- // INITIALIZED such that store loading could be triggered again when
- // Start() is called with IMMEDIATE_START.
- state_ = INITIALIZED;
- }
- } else {
- // Otherwise, destroy the store to try again.
- ResetStore();
- }
- return;
- }
- gcm_store_reset_ = false;
- device_checkin_info_.android_id = result->device_android_id;
- device_checkin_info_.secret = result->device_security_token;
- device_checkin_info_.last_checkin_accounts = result->last_checkin_accounts;
- // A case where there were previously no accounts reported with checkin is
- // considered to be the same as when the list of accounts is empty. It enables
- // scheduling a periodic checkin for devices with no signed in users
- // immediately after restart, while keeping |accounts_set == false| delays the
- // checkin until the list of accounts is set explicitly.
- if (result->last_checkin_accounts.size() == 0)
- device_checkin_info_.accounts_set = true;
- last_checkin_time_ = result->last_checkin_time;
- gservices_settings_.UpdateFromLoadResult(*result);
- for (auto iter = result->registrations.begin();
- iter != result->registrations.end();
- ++iter) {
- std::string registration_id;
- scoped_refptr<RegistrationInfo> registration =
- RegistrationInfo::BuildFromString(iter->first, iter->second,
- ®istration_id);
- // TODO(jianli): Add UMA to track the error case.
- if (registration)
- registrations_.emplace(std::move(registration), registration_id);
- }
- for (auto iter = result->instance_id_data.begin();
- iter != result->instance_id_data.end();
- ++iter) {
- std::string instance_id;
- std::string extra_data;
- if (DeserializeInstanceIDData(iter->second, &instance_id, &extra_data))
- instance_id_data_[iter->first] = std::make_pair(instance_id, extra_data);
- }
- load_result_ = std::move(result);
- state_ = LOADED;
- // Don't initiate the GCM connection when GCM is in delayed start mode and
- // not any standalone app has registered GCM yet.
- if (start_mode_ == DELAYED_START && !HasStandaloneRegisteredApp()) {
- // If no standalone app is using GCM and the device ID is present, schedule
- // to have the store wiped out.
- if (device_checkin_info_.android_id) {
- io_task_runner_->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&GCMClientImpl::DestroyStoreWhenNotNeeded,
- destroying_gcm_store_ptr_factory_.GetWeakPtr()),
- base::Milliseconds(kDestroyGCMStoreDelayMS));
- }
- return;
- }
- StartGCM();
- }
- void GCMClientImpl::StartGCM() {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- // Taking over the value of account_mappings before passing the ownership of
- // load result to InitializeMCSClient.
- std::vector<AccountMapping> account_mappings;
- account_mappings.swap(load_result_->account_mappings);
- base::Time last_token_fetch_time = load_result_->last_token_fetch_time;
- InitializeMCSClient();
- if (device_checkin_info_.IsValid()) {
- SchedulePeriodicCheckin();
- OnReady(account_mappings, last_token_fetch_time);
- return;
- }
- state_ = INITIAL_DEVICE_CHECKIN;
- device_checkin_info_.Reset();
- StartCheckin();
- }
- void GCMClientImpl::InitializeMCSClient() {
- DCHECK(network_connection_tracker_);
- std::vector<GURL> endpoints;
- endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
- GURL fallback_endpoint = gservices_settings_.GetMCSFallbackEndpoint();
- if (fallback_endpoint.is_valid())
- endpoints.push_back(fallback_endpoint);
- connection_factory_ = internals_builder_->BuildConnectionFactory(
- endpoints, GetGCMBackoffPolicy(), get_socket_factory_callback_,
- io_task_runner_, &recorder_, network_connection_tracker_);
- connection_factory_->SetConnectionListener(this);
- mcs_client_ = internals_builder_->BuildMCSClient(
- chrome_build_info_.version, clock_, connection_factory_.get(),
- gcm_store_.get(), io_task_runner_, &recorder_);
- mcs_client_->Initialize(
- base::BindRepeating(&GCMClientImpl::OnMCSError,
- weak_ptr_factory_.GetWeakPtr()),
- base::BindRepeating(&GCMClientImpl::OnMessageReceivedFromMCS,
- weak_ptr_factory_.GetWeakPtr()),
- base::BindRepeating(&GCMClientImpl::OnMessageSentToMCS,
- weak_ptr_factory_.GetWeakPtr()),
- std::move(load_result_));
- }
- void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
- const CheckinInfo& checkin_info) {
- DCHECK(!device_checkin_info_.IsValid());
- device_checkin_info_.android_id = checkin_info.android_id;
- device_checkin_info_.secret = checkin_info.secret;
- // If accounts were not set by now, we can consider them set (to empty list)
- // to make sure periodic checkins get scheduled after initial checkin.
- device_checkin_info_.accounts_set = true;
- gcm_store_->SetDeviceCredentials(
- checkin_info.android_id, checkin_info.secret,
- base::BindOnce(&GCMClientImpl::SetDeviceCredentialsCallback,
- weak_ptr_factory_.GetWeakPtr()));
- OnReady(std::vector<AccountMapping>(), base::Time());
- }
- void GCMClientImpl::OnReady(const std::vector<AccountMapping>& account_mappings,
- const base::Time& last_token_fetch_time) {
- state_ = READY;
- StartMCSLogin();
- delegate_->OnGCMReady(account_mappings, last_token_fetch_time);
- }
- void GCMClientImpl::StartMCSLogin() {
- DCHECK_EQ(READY, state_);
- DCHECK(device_checkin_info_.IsValid());
- mcs_client_->Login(device_checkin_info_.android_id,
- device_checkin_info_.secret);
- }
- void GCMClientImpl::DestroyStoreWhenNotNeeded() {
- if (state_ != LOADED || start_mode_ != DELAYED_START)
- return;
- gcm_store_->Destroy(base::BindOnce(&GCMClientImpl::DestroyStoreCallback,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void GCMClientImpl::ResetStore() {
- // If already being reset, don't do it again. We want to prevent from
- // resetting and loading from the store again and again.
- if (gcm_store_reset_) {
- RecordResetStoreErrorToUMA(INFINITE_STORE_RESET);
- state_ = UNINITIALIZED;
- return;
- }
- gcm_store_reset_ = true;
- // Destroy the GCM store to start over.
- gcm_store_->Destroy(base::BindOnce(&GCMClientImpl::ResetStoreCallback,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void GCMClientImpl::SetAccountTokens(
- const std::vector<AccountTokenInfo>& account_tokens) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- device_checkin_info_.account_tokens.clear();
- for (auto iter = account_tokens.begin(); iter != account_tokens.end();
- ++iter) {
- device_checkin_info_.account_tokens[iter->email] = iter->access_token;
- }
- bool accounts_set_before = device_checkin_info_.accounts_set;
- device_checkin_info_.accounts_set = true;
- DVLOG(1) << "Set account called with: " << account_tokens.size()
- << " accounts.";
- if (state_ != READY && state_ != INITIAL_DEVICE_CHECKIN)
- return;
- bool account_removed = false;
- for (auto iter = device_checkin_info_.last_checkin_accounts.begin();
- iter != device_checkin_info_.last_checkin_accounts.end(); ++iter) {
- if (device_checkin_info_.account_tokens.find(*iter) ==
- device_checkin_info_.account_tokens.end()) {
- account_removed = true;
- }
- }
- // Checkin will be forced when any of the accounts was removed during the
- // current Chrome session or if there has been an account removed between the
- // restarts of Chrome. If there is a checkin in progress, it will be canceled.
- // We only force checkin when user signs out. When there is a new account
- // signed in, the periodic checkin will take care of adding the association in
- // reasonable time.
- if (account_removed) {
- DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
- checkin_request_.reset();
- StartCheckin();
- } else if (!accounts_set_before) {
- SchedulePeriodicCheckin();
- DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
- }
- }
- void GCMClientImpl::UpdateAccountMapping(
- const AccountMapping& account_mapping) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- gcm_store_->AddAccountMapping(
- account_mapping, base::BindOnce(&GCMClientImpl::DefaultStoreCallback,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void GCMClientImpl::RemoveAccountMapping(const CoreAccountId& account_id) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- gcm_store_->RemoveAccountMapping(
- account_id, base::BindOnce(&GCMClientImpl::DefaultStoreCallback,
- weak_ptr_factory_.GetWeakPtr()));
- }
- void GCMClientImpl::SetLastTokenFetchTime(const base::Time& time) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- gcm_store_->SetLastTokenFetchTime(
- time,
- base::BindOnce(&GCMClientImpl::IgnoreWriteResultCallback,
- weak_ptr_factory_.GetWeakPtr(),
- /*operation_suffix_for_uma=*/"SetLastTokenFetchTime"));
- }
- void GCMClientImpl::UpdateHeartbeatTimer(
- std::unique_ptr<base::RetainingOneShotTimer> timer) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- DCHECK(mcs_client_);
- mcs_client_->UpdateHeartbeatTimer(std::move(timer));
- }
- void GCMClientImpl::AddInstanceIDData(const std::string& app_id,
- const std::string& instance_id,
- const std::string& extra_data) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- instance_id_data_[app_id] = std::make_pair(instance_id, extra_data);
- // TODO(crbug/1028761): If this call fails, we likely leak a registration
- // (the one stored in instance_id_data_ would be used for a registration but
- // not persisted).
- gcm_store_->AddInstanceIDData(
- app_id, SerializeInstanceIDData(instance_id, extra_data),
- base::BindOnce(&GCMClientImpl::IgnoreWriteResultCallback,
- weak_ptr_factory_.GetWeakPtr(),
- /*operation_suffix_for_uma=*/"AddInstanceIDData"));
- }
- void GCMClientImpl::RemoveInstanceIDData(const std::string& app_id) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- instance_id_data_.erase(app_id);
- gcm_store_->RemoveInstanceIDData(
- app_id,
- base::BindOnce(&GCMClientImpl::IgnoreWriteResultCallback,
- weak_ptr_factory_.GetWeakPtr(),
- /*operation_suffix_for_uma=*/"RemoveInstanceIDData"));
- }
- void GCMClientImpl::GetInstanceIDData(const std::string& app_id,
- std::string* instance_id,
- std::string* extra_data) {
- DCHECK(instance_id && extra_data);
- auto iter = instance_id_data_.find(app_id);
- if (iter == instance_id_data_.end())
- return;
- *instance_id = iter->second.first;
- *extra_data = iter->second.second;
- }
- void GCMClientImpl::AddHeartbeatInterval(const std::string& scope,
- int interval_ms) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- DCHECK(mcs_client_);
- mcs_client_->AddHeartbeatInterval(scope, interval_ms);
- }
- void GCMClientImpl::RemoveHeartbeatInterval(const std::string& scope) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- DCHECK(mcs_client_);
- mcs_client_->RemoveHeartbeatInterval(scope);
- }
- void GCMClientImpl::StartCheckin() {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- // Make sure no checkin is in progress.
- if (checkin_request_)
- return;
- checkin_proto::ChromeBuildProto chrome_build_proto;
- ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
- CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
- device_checkin_info_.secret,
- device_checkin_info_.account_tokens,
- gservices_settings_.digest(),
- chrome_build_proto);
- checkin_request_ = std::make_unique<CheckinRequest>(
- gservices_settings_.GetCheckinURL(), request_info, GetGCMBackoffPolicy(),
- base::BindOnce(&GCMClientImpl::OnCheckinCompleted,
- weak_ptr_factory_.GetWeakPtr()),
- url_loader_factory_, io_task_runner_, &recorder_);
- // Taking a snapshot of the accounts count here, as there might be an asynch
- // update of the account tokens while checkin is in progress.
- device_checkin_info_.SnapshotCheckinAccounts();
- checkin_request_->Start();
- }
- void GCMClientImpl::OnCheckinCompleted(
- net::HttpStatusCode response_code,
- const checkin_proto::AndroidCheckinResponse& checkin_response) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- checkin_request_.reset();
- if (response_code == net::HTTP_UNAUTHORIZED ||
- response_code == net::HTTP_BAD_REQUEST) {
- LOG(ERROR) << "Checkin rejected. Resetting GCM Store.";
- ResetStore();
- return;
- }
- DCHECK(checkin_response.has_android_id());
- DCHECK(checkin_response.has_security_token());
- CheckinInfo checkin_info;
- checkin_info.android_id = checkin_response.android_id();
- checkin_info.secret = checkin_response.security_token();
- if (state_ == INITIAL_DEVICE_CHECKIN) {
- OnFirstTimeDeviceCheckinCompleted(checkin_info);
- } else {
- // checkin_info is not expected to change after a periodic checkin as it
- // would invalidate the registration IDs.
- DCHECK_EQ(READY, state_);
- DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
- DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
- }
- if (device_checkin_info_.IsValid()) {
- // First update G-services settings, as something might have changed.
- if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
- gcm_store_->SetGServicesSettings(
- gservices_settings_.settings_map(), gservices_settings_.digest(),
- base::BindOnce(&GCMClientImpl::SetGServicesSettingsCallback,
- weak_ptr_factory_.GetWeakPtr()));
- }
- last_checkin_time_ = clock_->Now();
- gcm_store_->SetLastCheckinInfo(
- last_checkin_time_, device_checkin_info_.last_checkin_accounts,
- base::BindOnce(&GCMClientImpl::SetLastCheckinInfoCallback,
- weak_ptr_factory_.GetWeakPtr()));
- SchedulePeriodicCheckin();
- }
- }
- void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
- DCHECK(success);
- }
- void GCMClientImpl::SchedulePeriodicCheckin() {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- // Make sure no checkin is in progress.
- if (checkin_request_.get() || !device_checkin_info_.accounts_set)
- return;
- // There should be only one periodic checkin pending at a time. Removing
- // pending periodic checkin to schedule a new one.
- periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
- base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
- if (time_to_next_checkin.is_negative())
- time_to_next_checkin = base::TimeDelta();
- io_task_runner_->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&GCMClientImpl::StartCheckin,
- periodic_checkin_ptr_factory_.GetWeakPtr()),
- time_to_next_checkin);
- }
- base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
- return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
- clock_->Now();
- }
- void GCMClientImpl::SetLastCheckinInfoCallback(bool success) {
- // TODO(fgorski): This is one of the signals that store needs a rebuild.
- DCHECK(success);
- }
- void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
- // TODO(fgorski): This is one of the signals that store needs a rebuild.
- DCHECK(success);
- }
- void GCMClientImpl::UpdateRegistrationCallback(bool success) {
- // TODO(fgorski): This is one of the signals that store needs a rebuild.
- DCHECK(success);
- }
- void GCMClientImpl::DefaultStoreCallback(bool success) {
- DCHECK(success);
- }
- void GCMClientImpl::IgnoreWriteResultCallback(
- const std::string& operation_suffix_for_uma,
- bool success) {
- // TODO(crbug.com/1081149): Implement proper error handling.
- // TODO(fgorski): Ignoring the write result for now to make sure
- // sync_intergration_tests are not broken.
- }
- void GCMClientImpl::DestroyStoreCallback(bool success) {
- ResetCache();
- if (!success) {
- LOG(ERROR) << "Failed to destroy GCM store";
- RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED);
- state_ = UNINITIALIZED;
- return;
- }
- state_ = INITIALIZED;
- }
- void GCMClientImpl::ResetStoreCallback(bool success) {
- // Even an incomplete reset may invalidate registrations, and this might be
- // the only opportunity to notify the delegate. For example a partial reset
- // that deletes the "CURRENT" file will cause GCMStoreImpl to consider the DB
- // to no longer exist, in which case the next load will simply create a new
- // store rather than resetting it.
- delegate_->OnStoreReset();
- if (!success) {
- LOG(ERROR) << "Failed to reset GCM store";
- RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED);
- state_ = UNINITIALIZED;
- return;
- }
- state_ = INITIALIZED;
- Start(start_mode_);
- }
- void GCMClientImpl::Stop() {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
- // Shutdown.
- DVLOG(1) << "Stopping the GCM Client";
- ResetCache();
- state_ = INITIALIZED;
- gcm_store_->Close();
- }
- void GCMClientImpl::ResetCache() {
- weak_ptr_factory_.InvalidateWeakPtrs();
- periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
- device_checkin_info_.Reset();
- connection_factory_.reset();
- delegate_->OnDisconnected();
- mcs_client_.reset();
- checkin_request_.reset();
- // Delete all of the pending registration and unregistration requests.
- pending_registration_requests_.clear();
- pending_unregistration_requests_.clear();
- }
- void GCMClientImpl::Register(
- scoped_refptr<RegistrationInfo> registration_info) {
- DCHECK_EQ(state_, READY);
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- // Registrations should never pass as an app_id the special category used
- // internally when registering with a subtype. See security note in
- // GCMClientImpl::HandleIncomingMessage.
- CHECK_NE(registration_info->app_id,
- chrome_build_info_.product_category_for_subtypes);
- // Find and use the cached registration ID.
- RegistrationInfoMap::const_iterator registrations_iter =
- registrations_.find(registration_info);
- if (registrations_iter != registrations_.end()) {
- bool matched = true;
- // For GCM registration, we also match the sender IDs since multiple
- // registrations are not supported.
- const GCMRegistrationInfo* gcm_registration_info =
- GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
- if (gcm_registration_info) {
- const GCMRegistrationInfo* cached_gcm_registration_info =
- GCMRegistrationInfo::FromRegistrationInfo(
- registrations_iter->first.get());
- DCHECK(cached_gcm_registration_info);
- if (gcm_registration_info->sender_ids !=
- cached_gcm_registration_info->sender_ids) {
- matched = false;
- // Senders IDs don't match existing registration.
- RecordRegistrationRequestToUMA(
- RegistrationCacheStatus::REGISTRATION_FOUND_BUT_SENDERS_DONT_MATCH);
- }
- }
- if (matched) {
- // Skip registration if token is fresh.
- base::TimeDelta token_invalidation_period =
- features::GetTokenInvalidationInterval();
- base::TimeDelta time_since_last_validation =
- clock_->Now() - registrations_iter->first->last_validated;
- if (token_invalidation_period.is_zero() ||
- time_since_last_validation < token_invalidation_period) {
- // Token is fresh, or token invalidation is disabled.
- // Use cached registration.
- delegate_->OnRegisterFinished(registration_info,
- registrations_iter->second, SUCCESS);
- RecordRegistrationRequestToUMA(
- RegistrationCacheStatus::REGISTRATION_FOUND_AND_FRESH);
- return;
- } else {
- // Token is stale.
- RecordRegistrationRequestToUMA(
- RegistrationCacheStatus::REGISTRATION_FOUND_BUT_STALE);
- }
- }
- } else {
- // New Registration request (no existing registration)
- RecordRegistrationRequestToUMA(
- RegistrationCacheStatus::REGISTRATION_NOT_FOUND);
- }
- std::unique_ptr<RegistrationRequest::CustomRequestHandler> request_handler;
- std::string source_to_record;
- const GCMRegistrationInfo* gcm_registration_info =
- GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
- if (gcm_registration_info) {
- std::string senders;
- for (auto iter = gcm_registration_info->sender_ids.begin();
- iter != gcm_registration_info->sender_ids.end();
- ++iter) {
- if (!senders.empty())
- senders.append(",");
- senders.append(*iter);
- }
- request_handler = std::make_unique<GCMRegistrationRequestHandler>(senders);
- source_to_record = senders;
- }
- const InstanceIDTokenInfo* instance_id_token_info =
- InstanceIDTokenInfo::FromRegistrationInfo(registration_info.get());
- if (instance_id_token_info) {
- auto instance_id_iter = instance_id_data_.find(registration_info->app_id);
- DCHECK(instance_id_iter != instance_id_data_.end());
- request_handler = std::make_unique<InstanceIDGetTokenRequestHandler>(
- instance_id_iter->second.first,
- instance_id_token_info->authorized_entity,
- instance_id_token_info->scope,
- ConstructGCMVersion(chrome_build_info_.version),
- instance_id_token_info->time_to_live);
- source_to_record = instance_id_token_info->authorized_entity + "/" +
- instance_id_token_info->scope;
- }
- bool use_subtype = instance_id_token_info &&
- InstanceIDUsesSubtypeForAppId(registration_info->app_id);
- std::string category = use_subtype
- ? chrome_build_info_.product_category_for_subtypes
- : registration_info->app_id;
- std::string subtype = use_subtype ? registration_info->app_id : std::string();
- RegistrationRequest::RequestInfo request_info(device_checkin_info_.android_id,
- device_checkin_info_.secret,
- category, subtype);
- std::unique_ptr<RegistrationRequest> registration_request(
- new RegistrationRequest(
- gservices_settings_.GetRegistrationURL(), request_info,
- std::move(request_handler), GetGCMBackoffPolicy(),
- base::BindOnce(&GCMClientImpl::OnRegisterCompleted,
- weak_ptr_factory_.GetWeakPtr(), registration_info),
- kMaxRegistrationRetries, url_loader_factory_, io_task_runner_,
- &recorder_, source_to_record));
- registration_request->Start();
- pending_registration_requests_.insert(
- std::make_pair(registration_info, std::move(registration_request)));
- }
- void GCMClientImpl::OnRegisterCompleted(
- scoped_refptr<RegistrationInfo> registration_info,
- RegistrationRequest::Status status,
- const std::string& registration_id) {
- DCHECK(delegate_);
- Result result;
- PendingRegistrationRequests::const_iterator iter =
- pending_registration_requests_.find(registration_info);
- if (iter == pending_registration_requests_.end()) {
- result = UNKNOWN_ERROR;
- } else if (status == RegistrationRequest::INVALID_SENDER) {
- result = INVALID_PARAMETER;
- } else if (registration_id.empty()) {
- // All other errors are currently treated as SERVER_ERROR (including
- // REACHED_MAX_RETRIES due to the device being offline!).
- result = SERVER_ERROR;
- } else {
- result = SUCCESS;
- }
- if (result == SUCCESS) {
- // Cache it.
- // Note that the existing cached record has to be removed first because
- // otherwise the key value in registrations_ will not be updated. For GCM
- // registrations, the key consists of pair of app_id and sender_ids though
- // only app_id is used in the key comparison.
- registrations_.erase(registration_info);
- registration_info->last_validated = clock_->Now();
- registrations_[registration_info] = registration_id;
- // Save it in the persistent store.
- gcm_store_->AddRegistration(
- registration_info->GetSerializedKey(),
- registration_info->GetSerializedValue(registration_id),
- base::BindOnce(&GCMClientImpl::UpdateRegistrationCallback,
- weak_ptr_factory_.GetWeakPtr()));
- }
- delegate_->OnRegisterFinished(
- registration_info,
- result == SUCCESS ? registration_id : std::string(),
- result);
- if (iter != pending_registration_requests_.end())
- pending_registration_requests_.erase(iter);
- }
- bool GCMClientImpl::ValidateRegistration(
- scoped_refptr<RegistrationInfo> registration_info,
- const std::string& registration_id) {
- DCHECK_EQ(state_, READY);
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- // Must have a cached registration.
- RegistrationInfoMap::const_iterator registrations_iter =
- registrations_.find(registration_info);
- if (registrations_iter == registrations_.end())
- return false;
- // Cached registration ID must match.
- const std::string& cached_registration_id = registrations_iter->second;
- if (registration_id != cached_registration_id)
- return false;
- // For GCM registration, we also match the sender IDs since multiple
- // registrations are not supported.
- const GCMRegistrationInfo* gcm_registration_info =
- GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
- if (gcm_registration_info) {
- const GCMRegistrationInfo* cached_gcm_registration_info =
- GCMRegistrationInfo::FromRegistrationInfo(
- registrations_iter->first.get());
- DCHECK(cached_gcm_registration_info);
- if (gcm_registration_info->sender_ids !=
- cached_gcm_registration_info->sender_ids) {
- return false;
- }
- }
- return true;
- }
- void GCMClientImpl::Unregister(
- scoped_refptr<RegistrationInfo> registration_info) {
- DCHECK_EQ(state_, READY);
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- std::unique_ptr<UnregistrationRequest::CustomRequestHandler> request_handler;
- std::string source_to_record;
- const GCMRegistrationInfo* gcm_registration_info =
- GCMRegistrationInfo::FromRegistrationInfo(registration_info.get());
- if (gcm_registration_info) {
- request_handler = std::make_unique<GCMUnregistrationRequestHandler>(
- registration_info->app_id);
- }
- const InstanceIDTokenInfo* instance_id_token_info =
- InstanceIDTokenInfo::FromRegistrationInfo(registration_info.get());
- if (instance_id_token_info) {
- auto instance_id_iter = instance_id_data_.find(registration_info->app_id);
- if (instance_id_iter == instance_id_data_.end()) {
- // This should not be reached since we should not delete tokens when
- // an InstanceID has not been created yet.
- NOTREACHED();
- return;
- }
- request_handler = std::make_unique<InstanceIDDeleteTokenRequestHandler>(
- instance_id_iter->second.first,
- instance_id_token_info->authorized_entity,
- instance_id_token_info->scope,
- ConstructGCMVersion(chrome_build_info_.version));
- source_to_record = instance_id_token_info->authorized_entity + "/" +
- instance_id_token_info->scope;
- }
- // Remove the registration/token(s) from the cache and the store.
- // TODO(jianli): Remove it only when the request is successful.
- if (instance_id_token_info &&
- instance_id_token_info->authorized_entity == "*" &&
- instance_id_token_info->scope == "*") {
- // If authorized_entity and scope are '*', find and remove all associated
- // tokens.
- bool token_found = false;
- for (auto iter = registrations_.begin();
- iter != registrations_.end();) {
- InstanceIDTokenInfo* cached_instance_id_token_info =
- InstanceIDTokenInfo::FromRegistrationInfo(iter->first.get());
- if (cached_instance_id_token_info &&
- cached_instance_id_token_info->app_id == registration_info->app_id) {
- token_found = true;
- gcm_store_->RemoveRegistration(
- cached_instance_id_token_info->GetSerializedKey(),
- base::BindOnce(&GCMClientImpl::UpdateRegistrationCallback,
- weak_ptr_factory_.GetWeakPtr()));
- registrations_.erase(iter++);
- } else {
- ++iter;
- }
- }
- // If no token is found for the Instance ID, don't need to unregister
- // since the Instance ID is not sent to the server yet.
- if (!token_found) {
- OnUnregisterCompleted(registration_info,
- UnregistrationRequest::SUCCESS);
- return;
- }
- } else {
- auto iter = registrations_.find(registration_info);
- if (iter == registrations_.end()) {
- delegate_->OnUnregisterFinished(registration_info, INVALID_PARAMETER);
- return;
- }
- registrations_.erase(iter);
- gcm_store_->RemoveRegistration(
- registration_info->GetSerializedKey(),
- base::BindOnce(&GCMClientImpl::UpdateRegistrationCallback,
- weak_ptr_factory_.GetWeakPtr()));
- }
- bool use_subtype = instance_id_token_info &&
- InstanceIDUsesSubtypeForAppId(registration_info->app_id);
- std::string category = use_subtype
- ? chrome_build_info_.product_category_for_subtypes
- : registration_info->app_id;
- std::string subtype = use_subtype ? registration_info->app_id : std::string();
- UnregistrationRequest::RequestInfo request_info(
- device_checkin_info_.android_id, device_checkin_info_.secret, category,
- subtype);
- std::unique_ptr<UnregistrationRequest> unregistration_request(
- new UnregistrationRequest(
- gservices_settings_.GetRegistrationURL(), request_info,
- std::move(request_handler), GetGCMBackoffPolicy(),
- base::BindOnce(&GCMClientImpl::OnUnregisterCompleted,
- weak_ptr_factory_.GetWeakPtr(), registration_info),
- kMaxUnregistrationRetries, url_loader_factory_, io_task_runner_,
- &recorder_, source_to_record));
- unregistration_request->Start();
- pending_unregistration_requests_.insert(
- std::make_pair(registration_info, std::move(unregistration_request)));
- }
- void GCMClientImpl::OnUnregisterCompleted(
- scoped_refptr<RegistrationInfo> registration_info,
- UnregistrationRequest::Status status) {
- DVLOG(1) << "Unregister completed for app: " << registration_info->app_id
- << " with " << (status ? "success." : "failure.");
- Result result;
- switch (status) {
- case UnregistrationRequest::SUCCESS:
- result = SUCCESS;
- break;
- case UnregistrationRequest::INVALID_PARAMETERS:
- result = INVALID_PARAMETER;
- break;
- default:
- // All other errors are currently treated as SERVER_ERROR (including
- // REACHED_MAX_RETRIES due to the device being offline!).
- result = SERVER_ERROR;
- break;
- }
- delegate_->OnUnregisterFinished(registration_info, result);
- pending_unregistration_requests_.erase(registration_info);
- }
- void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
- DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
- UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
- }
- void GCMClientImpl::Send(const std::string& app_id,
- const std::string& receiver_id,
- const OutgoingMessage& message) {
- DCHECK_EQ(state_, READY);
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- mcs_proto::DataMessageStanza stanza;
- stanza.set_ttl(message.time_to_live);
- stanza.set_sent(clock_->Now().ToInternalValue() /
- base::Time::kMicrosecondsPerSecond);
- stanza.set_id(message.id);
- stanza.set_from(kSendMessageFromValue);
- stanza.set_to(receiver_id);
- stanza.set_category(app_id);
- for (auto iter = message.data.begin(); iter != message.data.end(); ++iter) {
- mcs_proto::AppData* app_data = stanza.add_app_data();
- app_data->set_key(iter->first);
- app_data->set_value(iter->second);
- }
- MCSMessage mcs_message(stanza);
- DVLOG(1) << "MCS message size: " << mcs_message.size();
- mcs_client_->SendMessage(mcs_message);
- }
- std::string GCMClientImpl::GetStateString() const {
- switch(state_) {
- case GCMClientImpl::UNINITIALIZED:
- return "UNINITIALIZED";
- case GCMClientImpl::INITIALIZED:
- return "INITIALIZED";
- case GCMClientImpl::LOADING:
- return "LOADING";
- case GCMClientImpl::LOADED:
- return "LOADED";
- case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
- return "INITIAL_DEVICE_CHECKIN";
- case GCMClientImpl::READY:
- return "READY";
- }
- NOTREACHED();
- return std::string();
- }
- void GCMClientImpl::RecordDecryptionFailure(const std::string& app_id,
- GCMDecryptionResult result) {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- recorder_.RecordDecryptionFailure(app_id, result);
- }
- void GCMClientImpl::SetRecording(bool recording) {
- recorder_.set_is_recording(recording);
- }
- void GCMClientImpl::ClearActivityLogs() {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- recorder_.Clear();
- }
- GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
- DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
- GCMClient::GCMStatistics stats;
- stats.gcm_client_created = true;
- stats.is_recording = recorder_.is_recording();
- stats.gcm_client_state = GetStateString();
- stats.connection_client_created = mcs_client_ != nullptr;
- stats.last_checkin = last_checkin_time_;
- stats.next_checkin =
- last_checkin_time_ + gservices_settings_.GetCheckinInterval();
- if (connection_factory_)
- stats.connection_state = connection_factory_->GetConnectionStateString();
- if (mcs_client_) {
- stats.send_queue_size = mcs_client_->GetSendQueueSize();
- stats.resend_queue_size = mcs_client_->GetResendQueueSize();
- }
- if (device_checkin_info_.android_id > 0)
- stats.android_id = device_checkin_info_.android_id;
- if (device_checkin_info_.secret > 0)
- stats.android_secret = device_checkin_info_.secret;
- recorder_.CollectActivities(&stats.recorded_activities);
- for (auto it = registrations_.begin(); it != registrations_.end(); ++it) {
- stats.registered_app_ids.push_back(it->first->app_id);
- }
- return stats;
- }
- void GCMClientImpl::OnActivityRecorded() {
- delegate_->OnActivityRecorded();
- }
- void GCMClientImpl::OnConnected(const GURL& current_server,
- const net::IPEndPoint& ip_endpoint) {
- // TODO(gcm): expose current server in debug page.
- delegate_->OnActivityRecorded();
- delegate_->OnConnected(ip_endpoint);
- }
- void GCMClientImpl::OnDisconnected() {
- delegate_->OnActivityRecorded();
- delegate_->OnDisconnected();
- }
- void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
- switch (message.tag()) {
- case kLoginResponseTag:
- DVLOG(1) << "Login response received by GCM Client. Ignoring.";
- return;
- case kDataMessageStanzaTag:
- DVLOG(1) << "A downstream message received. Processing...";
- HandleIncomingMessage(message);
- return;
- default:
- NOTREACHED() << "Message with unexpected tag received by GCMClient";
- return;
- }
- }
- void GCMClientImpl::OnMessageSentToMCS(int64_t user_serial_number,
- const std::string& app_id,
- const std::string& message_id,
- MCSClient::MessageSendStatus status) {
- DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
- DCHECK(delegate_);
- // TTL_EXCEEDED is singled out here, because it can happen long time after the
- // message was sent. That is why it comes as |OnMessageSendError| event rather
- // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
- // All other errors will be raised immediately, through asynchronous callback.
- // It is expected that TTL_EXCEEDED will be issued for a message that was
- // previously issued |OnSendFinished| with status SUCCESS.
- // TODO(jianli): Consider adding UMA for this status.
- if (status == MCSClient::TTL_EXCEEDED) {
- SendErrorDetails send_error_details;
- send_error_details.message_id = message_id;
- send_error_details.result = GCMClient::TTL_EXCEEDED;
- delegate_->OnMessageSendError(app_id, send_error_details);
- } else if (status == MCSClient::SENT) {
- delegate_->OnSendAcknowledged(app_id, message_id);
- } else {
- delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
- }
- }
- void GCMClientImpl::OnMCSError() {
- // TODO(fgorski): For now it replaces the initialization method. Long term it
- // should have an error or status passed in.
- }
- void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
- DCHECK(delegate_);
- const mcs_proto::DataMessageStanza& data_message_stanza =
- reinterpret_cast<const mcs_proto::DataMessageStanza&>(
- message.GetProtobuf());
- DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
- // Copy all the data from the stanza to a MessageData object. When present,
- // keys like kSubtypeKey or kMessageTypeKey will be filtered out later.
- MessageData message_data;
- for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
- std::string key = data_message_stanza.app_data(i).key();
- message_data[key] = data_message_stanza.app_data(i).value();
- }
- std::string subtype;
- auto subtype_iter = message_data.find(kSubtypeKey);
- if (subtype_iter != message_data.end()) {
- subtype = subtype_iter->second;
- message_data.erase(subtype_iter);
- }
- // SECURITY NOTE: Subtypes received from GCM *cannot* be trusted for
- // registrations without a subtype (as the sender can pass any subtype they
- // want). They can however be trusted for registrations that are known to have
- // a subtype (as GCM overwrites anything passed by the sender).
- //
- // So a given Chrome profile always passes a fixed string called
- // |product_category_for_subtypes| (of the form "com.chrome.macosx") as the
- // category when registering with a subtype, and incoming subtypes are only
- // trusted for that category.
- //
- // TODO(johnme): Remove this check if GCM starts sending the subtype in a
- // field that's guaranteed to be trusted (b/18198485).
- //
- // (On Android, all registrations made by Chrome on behalf of third-party
- // apps/extensions/websites have always had a subtype, so such a check is not
- // necessary - or possible, since category is fixed to the true package name).
- bool subtype_is_trusted = data_message_stanza.category() ==
- chrome_build_info_.product_category_for_subtypes;
- bool use_subtype = subtype_is_trusted && !subtype.empty();
- std::string app_id = use_subtype ? subtype : data_message_stanza.category();
- MessageType message_type = DATA_MESSAGE;
- auto type_iter = message_data.find(kMessageTypeKey);
- if (type_iter != message_data.end()) {
- message_type = DecodeMessageType(type_iter->second);
- message_data.erase(type_iter);
- }
- switch (message_type) {
- case DATA_MESSAGE:
- HandleIncomingDataMessage(app_id, use_subtype, data_message_stanza,
- message_data);
- break;
- case DELETED_MESSAGES:
- HandleIncomingDeletedMessages(app_id, data_message_stanza, message_data);
- break;
- case SEND_ERROR:
- HandleIncomingSendError(app_id, data_message_stanza, message_data);
- break;
- case UNKNOWN:
- DVLOG(1) << "Unknown message_type received. Message ignored. "
- << "App ID: " << app_id << ".";
- break;
- }
- }
- void GCMClientImpl::HandleIncomingDataMessage(
- const std::string& app_id,
- bool was_subtype,
- const mcs_proto::DataMessageStanza& data_message_stanza,
- MessageData& message_data) {
- UMA_HISTOGRAM_BOOLEAN("GCM.DataMessageReceived", true);
- bool has_collapse_key =
- data_message_stanza.has_token() && !data_message_stanza.token().empty();
- UMA_HISTOGRAM_BOOLEAN("GCM.DataMessageReceivedHasCollapseKey",
- has_collapse_key);
- recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
- data_message_stanza.ByteSize(),
- GCMStatsRecorder::DATA_MESSAGE);
- IncomingMessage incoming_message;
- incoming_message.sender_id = data_message_stanza.from();
- incoming_message.message_id = data_message_stanza.persistent_id();
- if (data_message_stanza.has_token())
- incoming_message.collapse_key = data_message_stanza.token();
- incoming_message.data = message_data;
- incoming_message.raw_data = data_message_stanza.raw_data();
- delegate_->OnMessageReceived(app_id, incoming_message);
- }
- void GCMClientImpl::HandleIncomingDeletedMessages(
- const std::string& app_id,
- const mcs_proto::DataMessageStanza& data_message_stanza,
- MessageData& message_data) {
- int deleted_count = 0;
- auto count_iter = message_data.find(kDeletedCountKey);
- if (count_iter != message_data.end()) {
- if (!base::StringToInt(count_iter->second, &deleted_count))
- deleted_count = 0;
- }
- UMA_HISTOGRAM_COUNTS_1000("GCM.DeletedMessagesReceived", deleted_count);
- recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
- data_message_stanza.ByteSize(),
- GCMStatsRecorder::DELETED_MESSAGES);
- delegate_->OnMessagesDeleted(app_id);
- }
- void GCMClientImpl::HandleIncomingSendError(
- const std::string& app_id,
- const mcs_proto::DataMessageStanza& data_message_stanza,
- MessageData& message_data) {
- SendErrorDetails send_error_details;
- send_error_details.additional_data = message_data;
- send_error_details.result = SERVER_ERROR;
- send_error_details.message_id = data_message_stanza.persistent_id();
- recorder_.RecordIncomingSendError(app_id, data_message_stanza.to(),
- data_message_stanza.id());
- delegate_->OnMessageSendError(app_id, send_error_details);
- }
- bool GCMClientImpl::HasStandaloneRegisteredApp() const {
- if (registrations_.empty())
- return false;
- // Note that account mapper is not counted as a standalone app since it is
- // automatically started when other app uses GCM.
- return registrations_.size() > 1 ||
- !ExistsGCMRegistrationInMap(registrations_, kGCMAccountMapperAppId);
- }
- } // namespace gcm
|