Index: components/gcm_driver/gcm_client_impl.cc |
diff --git a/components/gcm_driver/gcm_client_impl.cc b/components/gcm_driver/gcm_client_impl.cc |
index 448e416f30d74c70a29c0ea8d6af532aedc5db60..32428ff4c58564b228ba89d6e42ee2cac54e0ee2 100644 |
--- a/components/gcm_driver/gcm_client_impl.cc |
+++ b/components/gcm_driver/gcm_client_impl.cc |
@@ -67,6 +67,7 @@ enum ResetStoreError { |
RESET_STORE_ERROR_COUNT |
}; |
+const char kGCMScope[] = "GCM"; |
const int kMaxRegistrationRetries = 5; |
const char kMessageTypeDataMessage[] = "gcm"; |
const char kMessageTypeDeletedMessagesKey[] = "deleted_messages"; |
@@ -169,6 +170,25 @@ MessageType DecodeMessageType(const std::string& value) { |
return UNKNOWN; |
} |
+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(); |
+} |
+ |
void RecordOutgoingMessageToUMA( |
const gcm::GCMClient::OutgoingMessage& message) { |
OutgoingMessageTTLCategory ttl_category; |
@@ -335,7 +355,6 @@ void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) { |
} |
gcm_store_reset_ = false; |
- registrations_ = result->registrations; |
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; |
@@ -348,7 +367,28 @@ void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) { |
device_checkin_info_.accounts_set = true; |
last_checkin_time_ = result->last_checkin_time; |
gservices_settings_.UpdateFromLoadResult(*result); |
- instance_id_data_ = result->instance_id_data; |
+ |
+ for (auto iter = result->registrations.begin(); |
+ iter != result->registrations.end(); |
+ ++iter) { |
+ std::string registration_id; |
+ scoped_ptr<RegistrationInfo> registration = |
+ RegistrationInfo::BuildFromString( |
+ iter->first, iter->second, ®istration_id); |
+ // TODO(jianli): Add UMA to track the error case. |
+ if (registration.get()) |
+ registrations_[make_linked_ptr(registration.release())] = 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_ = result.Pass(); |
state_ = LOADED; |
@@ -532,11 +572,12 @@ void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr<base::Timer> timer) { |
} |
void GCMClientImpl::AddInstanceIDData(const std::string& app_id, |
- const std::string& instance_id_data) { |
- instance_id_data_[app_id] = instance_id_data; |
+ const std::string& instance_id, |
+ const std::string& extra_data) { |
+ instance_id_data_[app_id] = std::make_pair(instance_id, extra_data); |
gcm_store_->AddInstanceIDData( |
app_id, |
- instance_id_data, |
+ SerializeInstanceIDData(instance_id, extra_data), |
base::Bind(&GCMClientImpl::IgnoreWriteResultCallback, |
weak_ptr_factory_.GetWeakPtr())); |
} |
@@ -549,11 +590,16 @@ void GCMClientImpl::RemoveInstanceIDData(const std::string& app_id) { |
weak_ptr_factory_.GetWeakPtr())); |
} |
-std::string GCMClientImpl::GetInstanceIDData(const std::string& app_id) { |
+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 std::string(); |
- return iter->second; |
+ return; |
+ *instance_id = iter->second.first; |
+ *extra_data = iter->second.second; |
} |
void GCMClientImpl::AddHeartbeatInterval(const std::string& scope, |
@@ -721,53 +767,63 @@ void GCMClientImpl::Stop() { |
gcm_store_->Close(); |
} |
-void GCMClientImpl::Register(const std::string& app_id, |
- const std::vector<std::string>& sender_ids) { |
+void GCMClientImpl::Register( |
+ const linked_ptr<RegistrationInfo>& registration_info) { |
DCHECK_EQ(state_, READY); |
- // If the same sender ids is provided, return the cached registration ID |
- // directly. |
+ // Find and use the cached registration ID. |
RegistrationInfoMap::const_iterator registrations_iter = |
- registrations_.find(app_id); |
- if (registrations_iter != registrations_.end() && |
- registrations_iter->second->sender_ids == sender_ids) { |
- delegate_->OnRegisterFinished( |
- app_id, registrations_iter->second->registration_id, SUCCESS); |
- return; |
- } |
+ 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 (cached_gcm_registration_info && |
+ gcm_registration_info->sender_ids != |
+ cached_gcm_registration_info->sender_ids) { |
+ matched = false; |
+ } |
+ } |
- RegistrationRequest::RequestInfo request_info( |
- device_checkin_info_.android_id, |
- device_checkin_info_.secret, |
- app_id, |
- sender_ids); |
- DCHECK_EQ(0u, pending_registration_requests_.count(app_id)); |
+ if (matched) { |
+ delegate_->OnRegisterFinished( |
+ registration_info, registrations_iter->second, SUCCESS); |
+ return; |
+ } |
+ } |
RegistrationRequest* registration_request = |
- new RegistrationRequest(gservices_settings_.GetRegistrationURL(), |
- request_info, |
- GetGCMBackoffPolicy(), |
- base::Bind(&GCMClientImpl::OnRegisterCompleted, |
- weak_ptr_factory_.GetWeakPtr(), |
- app_id, |
- sender_ids), |
- kMaxRegistrationRetries, |
- url_request_context_getter_, |
- &recorder_); |
- pending_registration_requests_[app_id] = registration_request; |
+ new RegistrationRequest( |
+ gservices_settings_.GetRegistrationURL(), |
+ BuildRegistrationRequestInfo(*registration_info).Pass(), |
+ GetGCMBackoffPolicy(), |
+ base::Bind(&GCMClientImpl::OnRegisterCompleted, |
+ weak_ptr_factory_.GetWeakPtr(), |
+ registration_info), |
+ kMaxRegistrationRetries, |
+ url_request_context_getter_, |
+ &recorder_); |
+ pending_registration_requests_[registration_info] = registration_request; |
registration_request->Start(); |
} |
void GCMClientImpl::OnRegisterCompleted( |
- const std::string& app_id, |
- const std::vector<std::string>& sender_ids, |
+ const linked_ptr<RegistrationInfo>& registration_info, |
RegistrationRequest::Status status, |
const std::string& registration_id) { |
DCHECK(delegate_); |
Result result; |
PendingRegistrationRequests::iterator iter = |
- pending_registration_requests_.find(app_id); |
+ pending_registration_requests_.find(registration_info); |
if (iter == pending_registration_requests_.end()) |
result = UNKNOWN_ERROR; |
else if (status == RegistrationRequest::INVALID_SENDER) |
@@ -779,21 +835,20 @@ void GCMClientImpl::OnRegisterCompleted( |
if (result == SUCCESS) { |
// Cache it. |
- linked_ptr<RegistrationInfo> registration(new RegistrationInfo); |
- registration->sender_ids = sender_ids; |
- registration->registration_id = registration_id; |
- registrations_[app_id] = registration; |
+ registrations_[registration_info] = registration_id; |
// Save it in the persistent store. |
gcm_store_->AddRegistration( |
- app_id, |
- registration, |
+ registration_info->GetSerializedKey(), |
+ registration_info->GetSerializedValue(registration_id), |
base::Bind(&GCMClientImpl::UpdateRegistrationCallback, |
weak_ptr_factory_.GetWeakPtr())); |
} |
delegate_->OnRegisterFinished( |
- app_id, result == SUCCESS ? registration_id : std::string(), result); |
+ registration_info, |
+ result == SUCCESS ? registration_id : std::string(), |
+ result); |
if (iter != pending_registration_requests_.end()) { |
delete iter->second; |
@@ -801,47 +856,43 @@ void GCMClientImpl::OnRegisterCompleted( |
} |
} |
-void GCMClientImpl::Unregister(const std::string& app_id) { |
+void GCMClientImpl::Unregister( |
+ const linked_ptr<RegistrationInfo>& registration_info) { |
DCHECK_EQ(state_, READY); |
- if (pending_unregistration_requests_.count(app_id) == 1) |
+ if (pending_unregistration_requests_.count(registration_info) == 1) |
return; |
// Remove from the cache and persistent store. |
- registrations_.erase(app_id); |
+ registrations_.erase(registration_info); |
gcm_store_->RemoveRegistration( |
- app_id, |
+ registration_info->GetSerializedKey(), |
base::Bind(&GCMClientImpl::UpdateRegistrationCallback, |
weak_ptr_factory_.GetWeakPtr())); |
- UnregistrationRequest::RequestInfo request_info( |
- device_checkin_info_.android_id, |
- device_checkin_info_.secret, |
- app_id); |
- |
UnregistrationRequest* unregistration_request = new UnregistrationRequest( |
gservices_settings_.GetRegistrationURL(), |
- request_info, |
+ BuildUnregistrationRequestInfo(*registration_info).Pass(), |
GetGCMBackoffPolicy(), |
base::Bind(&GCMClientImpl::OnUnregisterCompleted, |
weak_ptr_factory_.GetWeakPtr(), |
- app_id), |
+ registration_info), |
url_request_context_getter_, |
&recorder_); |
- pending_unregistration_requests_[app_id] = unregistration_request; |
+ pending_unregistration_requests_[registration_info] = unregistration_request; |
unregistration_request->Start(); |
} |
void GCMClientImpl::OnUnregisterCompleted( |
- const std::string& app_id, |
+ const linked_ptr<RegistrationInfo>& registration_info, |
UnregistrationRequest::Status status) { |
- DVLOG(1) << "Unregister completed for app: " << app_id |
+ DVLOG(1) << "Unregister completed for app: " << registration_info->app_id |
<< " with " << (status ? "success." : "failure."); |
delegate_->OnUnregisterFinished( |
- app_id, |
+ registration_info, |
status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR); |
PendingUnregistrationRequests::iterator iter = |
- pending_unregistration_requests_.find(app_id); |
+ pending_unregistration_requests_.find(registration_info); |
if (iter == pending_unregistration_requests_.end()) |
return; |
@@ -903,6 +954,82 @@ std::string GCMClientImpl::GetStateString() const { |
} |
} |
+scoped_ptr<RegistrationRequest::RequestInfo> |
+GCMClientImpl::BuildRegistrationRequestInfo( |
+ const RegistrationInfo& registration_info) const { |
+ scoped_ptr<RegistrationRequest::RequestInfo> request_info; |
+ |
+ const GCMRegistrationInfo* gcm_registration_info = |
+ GCMRegistrationInfo::FromRegistrationInfo(®istration_info); |
+ if (gcm_registration_info) { |
+ scoped_ptr<RegistrationRequest::GCMRequestInfo> gcm_request_info( |
+ new RegistrationRequest::GCMRequestInfo); |
+ gcm_request_info->set_sender_ids(gcm_registration_info->sender_ids); |
+ request_info = gcm_request_info.Pass(); |
+ } |
+ |
+ const InstanceIDTokenInfo* instance_id_token_info = |
+ InstanceIDTokenInfo::FromRegistrationInfo(®istration_info); |
+ 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()); |
+ |
+ scoped_ptr<RegistrationRequest::InstanceIDRequestInfo> |
+ instance_id_token_request_info( |
+ new RegistrationRequest::InstanceIDRequestInfo); |
+ instance_id_token_request_info->set_instance_id(instance_id_iter->first); |
+ instance_id_token_request_info->set_authorized_entity( |
+ instance_id_token_info->authorized_entity); |
+ instance_id_token_request_info->set_scope(instance_id_token_info->scope); |
+ instance_id_token_request_info->set_options( |
+ instance_id_token_info->options); |
+ request_info = instance_id_token_request_info.Pass(); |
+ } |
+ |
+ DCHECK(request_info.get()); |
+ |
+ request_info->set_chrome_version(chrome_build_info_.version); |
+ request_info->set_android_id(device_checkin_info_.android_id); |
+ request_info->set_security_token(device_checkin_info_.secret); |
+ request_info->set_app_id(registration_info.app_id); |
+ return request_info.Pass(); |
+} |
+ |
+scoped_ptr<UnregistrationRequest::RequestInfo> |
+GCMClientImpl::BuildUnregistrationRequestInfo( |
+ const RegistrationInfo& registration_info) const { |
+ scoped_ptr<UnregistrationRequest::RequestInfo> request_info; |
+ |
+ const GCMRegistrationInfo* gcm_registration_info = |
+ GCMRegistrationInfo::FromRegistrationInfo(®istration_info); |
+ if (gcm_registration_info) |
+ request_info.reset(new UnregistrationRequest::GCMRequestInfo); |
+ |
+ const InstanceIDTokenInfo* instance_id_token_info = |
+ InstanceIDTokenInfo::FromRegistrationInfo(®istration_info); |
+ 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()); |
+ |
+ scoped_ptr<UnregistrationRequest::InstanceIDRequestInfo> |
+ instance_id_token_request_info( |
+ new UnregistrationRequest::InstanceIDRequestInfo); |
+ instance_id_token_request_info->set_instance_id(instance_id_iter->first); |
+ instance_id_token_request_info->set_authorized_entity( |
+ instance_id_token_info->authorized_entity); |
+ instance_id_token_request_info->set_scope(instance_id_token_info->scope); |
+ request_info = instance_id_token_request_info.Pass(); |
+ } |
+ |
+ DCHECK(request_info.get()); |
+ |
+ request_info->set_chrome_version(chrome_build_info_.version); |
+ request_info->set_android_id(device_checkin_info_.android_id); |
+ request_info->set_security_token(device_checkin_info_.secret); |
+ request_info->set_app_id(registration_info.app_id); |
+ return request_info.Pass(); |
+} |
+ |
void GCMClientImpl::SetRecording(bool recording) { |
recorder_.SetRecording(recording); |
} |
@@ -929,7 +1056,7 @@ GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const { |
for (RegistrationInfoMap::const_iterator it = registrations_.begin(); |
it != registrations_.end(); ++it) { |
- stats.registered_app_ids.push_back(it->first); |
+ stats.registered_app_ids.push_back(it->first->app_id); |
} |
return stats; |
} |
@@ -1047,21 +1174,46 @@ void GCMClientImpl::HandleIncomingDataMessage( |
const mcs_proto::DataMessageStanza& data_message_stanza, |
MessageData& message_data) { |
std::string app_id = data_message_stanza.category(); |
+ std::string sender = data_message_stanza.from(); |
// Drop the message when the app is not registered for the sender of the |
// message. |
- RegistrationInfoMap::iterator iter = registrations_.find(app_id); |
- bool not_registered = |
- iter == registrations_.end() || |
- std::find(iter->second->sender_ids.begin(), |
- iter->second->sender_ids.end(), |
- data_message_stanza.from()) == iter->second->sender_ids.end(); |
- recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(), |
- data_message_stanza.ByteSize(), !not_registered, |
+ bool registered = false; |
+ |
+ // First, find among all GCM registrations. |
+ scoped_ptr<GCMRegistrationInfo> gcm_registration(new GCMRegistrationInfo); |
+ gcm_registration->app_id = app_id; |
+ auto gcm_registration_iter = registrations_.find( |
+ make_linked_ptr<RegistrationInfo>(gcm_registration.release())); |
+ if (gcm_registration_iter != registrations_.end()) { |
+ GCMRegistrationInfo* cached_gcm_registration = |
+ GCMRegistrationInfo::FromRegistrationInfo( |
+ gcm_registration_iter->first.get()); |
+ if (cached_gcm_registration && |
+ std::find(cached_gcm_registration->sender_ids.begin(), |
+ cached_gcm_registration->sender_ids.end(), |
+ sender) != cached_gcm_registration->sender_ids.end()) { |
+ registered = true; |
+ } |
+ } |
+ |
+ // Then, find among all InstanceID registrations. |
+ if (!registered) { |
+ scoped_ptr<InstanceIDTokenInfo> instance_id_token(new InstanceIDTokenInfo); |
+ instance_id_token->app_id = app_id; |
+ instance_id_token->authorized_entity = sender; |
+ instance_id_token->scope = kGCMScope; |
+ auto instance_id_token_iter = registrations_.find( |
+ make_linked_ptr<RegistrationInfo>(instance_id_token.release())); |
+ if (instance_id_token_iter != registrations_.end()) |
+ registered = true; |
+ } |
+ |
+ recorder_.RecordDataMessageReceived(app_id, sender, |
+ data_message_stanza.ByteSize(), registered, |
GCMStatsRecorder::DATA_MESSAGE); |
- if (not_registered) { |
+ if (!registered) |
return; |
- } |
IncomingMessage incoming_message; |
incoming_message.sender_id = data_message_stanza.from(); |
@@ -1099,7 +1251,7 @@ bool GCMClientImpl::HasStandaloneRegisteredApp() const { |
// 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 || |
- !registrations_.count(kGCMAccountMapperAppId); |
+ !ExistsGCMRegistrationInMap(registrations_, kGCMAccountMapperAppId); |
} |
} // namespace gcm |