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 "components/invalidation/impl/p2p_invalidator.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/values.h"
14 #include "components/invalidation/impl/notifier_reason_util.h"
15 #include "components/invalidation/public/invalidation_handler.h"
16 #include "components/invalidation/public/invalidation_util.h"
17 #include "components/invalidation/public/object_id_invalidation_map.h"
18 #include "jingle/notifier/listener/push_client.h"
22 const char kSyncP2PNotificationChannel
[] = "http://www.google.com/chrome/sync";
26 const char kNotifySelf
[] = "notifySelf";
27 const char kNotifyOthers
[] = "notifyOthers";
28 const char kNotifyAll
[] = "notifyAll";
30 const char kSenderIdKey
[] = "senderId";
31 const char kNotificationTypeKey
[] = "notificationType";
32 const char kInvalidationsKey
[] = "invalidations";
36 std::string
P2PNotificationTargetToString(P2PNotificationTarget target
) {
50 P2PNotificationTarget
P2PNotificationTargetFromString(
51 const std::string
& target_str
) {
52 if (target_str
== kNotifySelf
) {
55 if (target_str
== kNotifyOthers
) {
58 if (target_str
== kNotifyAll
) {
61 LOG(WARNING
) << "Could not parse " << target_str
;
65 P2PNotificationData::P2PNotificationData()
66 : target_(NOTIFY_SELF
) {}
68 P2PNotificationData::P2PNotificationData(
69 const std::string
& sender_id
,
70 P2PNotificationTarget target
,
71 const ObjectIdInvalidationMap
& invalidation_map
)
72 : sender_id_(sender_id
),
74 invalidation_map_(invalidation_map
) {}
76 P2PNotificationData::~P2PNotificationData() {}
78 bool P2PNotificationData::IsTargeted(const std::string
& id
) const {
81 return sender_id_
== id
;
83 return sender_id_
!= id
;
92 const ObjectIdInvalidationMap
&
93 P2PNotificationData::GetIdInvalidationMap() const {
94 return invalidation_map_
;
97 bool P2PNotificationData::Equals(const P2PNotificationData
& other
) const {
99 (sender_id_
== other
.sender_id_
) &&
100 (target_
== other
.target_
) &&
101 (invalidation_map_
== other
.invalidation_map_
);
104 std::string
P2PNotificationData::ToString() const {
105 base::DictionaryValue dict
;
106 dict
.SetString(kSenderIdKey
, sender_id_
);
107 dict
.SetString(kNotificationTypeKey
, P2PNotificationTargetToString(target_
));
108 dict
.Set(kInvalidationsKey
, invalidation_map_
.ToValue().release());
110 base::JSONWriter::Write(dict
, &json
);
114 bool P2PNotificationData::ResetFromString(const std::string
& str
) {
115 scoped_ptr
<base::Value
> data_value
= base::JSONReader::Read(str
);
116 const base::DictionaryValue
* data_dict
= NULL
;
117 if (!data_value
.get() || !data_value
->GetAsDictionary(&data_dict
)) {
118 LOG(WARNING
) << "Could not parse " << str
<< " as a dictionary";
121 if (!data_dict
->GetString(kSenderIdKey
, &sender_id_
)) {
122 LOG(WARNING
) << "Could not find string value for " << kSenderIdKey
;
124 std::string target_str
;
125 if (!data_dict
->GetString(kNotificationTypeKey
, &target_str
)) {
126 LOG(WARNING
) << "Could not find string value for "
127 << kNotificationTypeKey
;
129 target_
= P2PNotificationTargetFromString(target_str
);
130 const base::ListValue
* invalidation_map_list
= NULL
;
131 if (!data_dict
->GetList(kInvalidationsKey
, &invalidation_map_list
) ||
132 !invalidation_map_
.ResetFromValue(*invalidation_map_list
)) {
133 LOG(WARNING
) << "Could not parse " << kInvalidationsKey
;
138 P2PInvalidator::P2PInvalidator(scoped_ptr
<notifier::PushClient
> push_client
,
139 const std::string
& invalidator_client_id
,
140 P2PNotificationTarget send_notification_target
)
141 : push_client_(push_client
.Pass()),
142 invalidator_client_id_(invalidator_client_id
),
144 notifications_enabled_(false),
145 send_notification_target_(send_notification_target
) {
146 DCHECK(send_notification_target_
== NOTIFY_OTHERS
||
147 send_notification_target_
== NOTIFY_ALL
);
148 push_client_
->AddObserver(this);
151 P2PInvalidator::~P2PInvalidator() {
152 DCHECK(thread_checker_
.CalledOnValidThread());
153 push_client_
->RemoveObserver(this);
156 void P2PInvalidator::RegisterHandler(InvalidationHandler
* handler
) {
157 DCHECK(thread_checker_
.CalledOnValidThread());
158 registrar_
.RegisterHandler(handler
);
161 bool P2PInvalidator::UpdateRegisteredIds(InvalidationHandler
* handler
,
162 const ObjectIdSet
& ids
) {
163 DCHECK(thread_checker_
.CalledOnValidThread());
165 const ObjectIdSet
& old_ids
= registrar_
.GetRegisteredIds(handler
);
166 std::set_difference(ids
.begin(), ids
.end(),
167 old_ids
.begin(), old_ids
.end(),
168 std::inserter(new_ids
, new_ids
.end()),
170 if (!registrar_
.UpdateRegisteredIds(handler
, ids
))
172 const P2PNotificationData
notification_data(
173 invalidator_client_id_
,
174 send_notification_target_
,
175 ObjectIdInvalidationMap::InvalidateAll(ids
));
176 SendNotificationData(notification_data
);
180 void P2PInvalidator::UnregisterHandler(InvalidationHandler
* handler
) {
181 DCHECK(thread_checker_
.CalledOnValidThread());
182 registrar_
.UnregisterHandler(handler
);
185 InvalidatorState
P2PInvalidator::GetInvalidatorState() const {
186 DCHECK(thread_checker_
.CalledOnValidThread());
187 return registrar_
.GetInvalidatorState();
190 void P2PInvalidator::UpdateCredentials(
191 const std::string
& email
, const std::string
& token
) {
192 DCHECK(thread_checker_
.CalledOnValidThread());
193 notifier::Subscription subscription
;
194 subscription
.channel
= kSyncP2PNotificationChannel
;
195 // There may be some subtle issues around case sensitivity of the
196 // from field, but it doesn't matter too much since this is only
197 // used in p2p mode (which is only used in testing).
198 subscription
.from
= email
;
199 push_client_
->UpdateSubscriptions(
200 notifier::SubscriptionList(1, subscription
));
201 // If already logged in, the new credentials will take effect on the
202 // next reconnection.
203 push_client_
->UpdateCredentials(email
, token
);
207 void P2PInvalidator::RequestDetailedStatus(
208 base::Callback
<void(const base::DictionaryValue
&)> callback
) const {
209 DCHECK(thread_checker_
.CalledOnValidThread());
210 // TODO(mferreria): Make the P2P Invalidator work.
211 scoped_ptr
<base::DictionaryValue
> value(new base::DictionaryValue());
212 callback
.Run(*value
);
215 void P2PInvalidator::SendInvalidation(const ObjectIdSet
& ids
) {
216 DCHECK(thread_checker_
.CalledOnValidThread());
217 ObjectIdInvalidationMap invalidation_map
=
218 ObjectIdInvalidationMap::InvalidateAll(ids
);
219 const P2PNotificationData
notification_data(
220 invalidator_client_id_
, send_notification_target_
, invalidation_map
);
221 SendNotificationData(notification_data
);
224 void P2PInvalidator::OnNotificationsEnabled() {
225 DCHECK(thread_checker_
.CalledOnValidThread());
226 bool just_turned_on
= (notifications_enabled_
== false);
227 notifications_enabled_
= true;
228 registrar_
.UpdateInvalidatorState(INVALIDATIONS_ENABLED
);
229 if (just_turned_on
) {
230 const P2PNotificationData
notification_data(
231 invalidator_client_id_
,
233 ObjectIdInvalidationMap::InvalidateAll(
234 registrar_
.GetAllRegisteredIds()));
235 SendNotificationData(notification_data
);
239 void P2PInvalidator::OnNotificationsDisabled(
240 notifier::NotificationsDisabledReason reason
) {
241 DCHECK(thread_checker_
.CalledOnValidThread());
242 registrar_
.UpdateInvalidatorState(FromNotifierReason(reason
));
245 void P2PInvalidator::OnIncomingNotification(
246 const notifier::Notification
& notification
) {
247 DCHECK(thread_checker_
.CalledOnValidThread());
248 DVLOG(1) << "Received notification " << notification
.ToString();
250 DVLOG(1) << "Not logged in yet -- not emitting notification";
253 if (!notifications_enabled_
) {
254 DVLOG(1) << "Notifications not on -- not emitting notification";
257 if (notification
.channel
!= kSyncP2PNotificationChannel
) {
258 LOG(WARNING
) << "Notification from unexpected source "
259 << notification
.channel
;
261 P2PNotificationData notification_data
;
262 if (!notification_data
.ResetFromString(notification
.data
)) {
263 LOG(WARNING
) << "Could not parse notification data from "
264 << notification
.data
;
265 notification_data
= P2PNotificationData(
266 invalidator_client_id_
,
268 ObjectIdInvalidationMap::InvalidateAll(
269 registrar_
.GetAllRegisteredIds()));
271 if (!notification_data
.IsTargeted(invalidator_client_id_
)) {
272 DVLOG(1) << "Not a target of the notification -- "
273 << "not emitting notification";
276 registrar_
.DispatchInvalidationsToHandlers(
277 notification_data
.GetIdInvalidationMap());
280 void P2PInvalidator::SendNotificationDataForTest(
281 const P2PNotificationData
& notification_data
) {
282 DCHECK(thread_checker_
.CalledOnValidThread());
283 SendNotificationData(notification_data
);
286 void P2PInvalidator::SendNotificationData(
287 const P2PNotificationData
& notification_data
) {
288 DCHECK(thread_checker_
.CalledOnValidThread());
289 if (notification_data
.GetIdInvalidationMap().Empty()) {
290 DVLOG(1) << "Not sending XMPP notification with empty state map: "
291 << notification_data
.ToString();
294 notifier::Notification notification
;
295 notification
.channel
= kSyncP2PNotificationChannel
;
296 notification
.data
= notification_data
.ToString();
297 DVLOG(1) << "Sending XMPP notification: " << notification
.ToString();
298 push_client_
->SendNotification(notification
);
301 } // namespace syncer