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/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/notifier_reason_util.h"
15 #include "jingle/notifier/listener/push_client.h"
16 #include "sync/notifier/invalidation_handler.h"
17 #include "sync/notifier/invalidation_util.h"
18 #include "sync/notifier/object_id_invalidation_map.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 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
106 dict
->SetString(kSenderIdKey
, sender_id_
);
107 dict
->SetString(kNotificationTypeKey
,
108 P2PNotificationTargetToString(target_
));
109 dict
->Set(kInvalidationsKey
, invalidation_map_
.ToValue().release());
111 base::JSONWriter::Write(dict
.get(), &json
);
115 bool P2PNotificationData::ResetFromString(const std::string
& str
) {
116 scoped_ptr
<base::Value
> data_value(base::JSONReader::Read(str
));
117 const base::DictionaryValue
* data_dict
= NULL
;
118 if (!data_value
.get() || !data_value
->GetAsDictionary(&data_dict
)) {
119 LOG(WARNING
) << "Could not parse " << str
<< " as a dictionary";
122 if (!data_dict
->GetString(kSenderIdKey
, &sender_id_
)) {
123 LOG(WARNING
) << "Could not find string value for " << kSenderIdKey
;
125 std::string target_str
;
126 if (!data_dict
->GetString(kNotificationTypeKey
, &target_str
)) {
127 LOG(WARNING
) << "Could not find string value for "
128 << kNotificationTypeKey
;
130 target_
= P2PNotificationTargetFromString(target_str
);
131 const base::ListValue
* invalidation_map_list
= NULL
;
132 if (!data_dict
->GetList(kInvalidationsKey
, &invalidation_map_list
) ||
133 !invalidation_map_
.ResetFromValue(*invalidation_map_list
)) {
134 LOG(WARNING
) << "Could not parse " << kInvalidationsKey
;
139 P2PInvalidator::P2PInvalidator(scoped_ptr
<notifier::PushClient
> push_client
,
140 const std::string
& invalidator_client_id
,
141 P2PNotificationTarget send_notification_target
)
142 : push_client_(push_client
.Pass()),
143 invalidator_client_id_(invalidator_client_id
),
145 notifications_enabled_(false),
146 send_notification_target_(send_notification_target
) {
147 DCHECK(send_notification_target_
== NOTIFY_OTHERS
||
148 send_notification_target_
== NOTIFY_ALL
);
149 push_client_
->AddObserver(this);
152 P2PInvalidator::~P2PInvalidator() {
153 DCHECK(thread_checker_
.CalledOnValidThread());
154 push_client_
->RemoveObserver(this);
157 void P2PInvalidator::RegisterHandler(InvalidationHandler
* handler
) {
158 DCHECK(thread_checker_
.CalledOnValidThread());
159 registrar_
.RegisterHandler(handler
);
162 void P2PInvalidator::UpdateRegisteredIds(InvalidationHandler
* handler
,
163 const ObjectIdSet
& ids
) {
164 DCHECK(thread_checker_
.CalledOnValidThread());
166 const ObjectIdSet
& old_ids
= registrar_
.GetRegisteredIds(handler
);
167 std::set_difference(ids
.begin(), ids
.end(),
168 old_ids
.begin(), old_ids
.end(),
169 std::inserter(new_ids
, new_ids
.end()),
171 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
);
179 void P2PInvalidator::UnregisterHandler(InvalidationHandler
* handler
) {
180 DCHECK(thread_checker_
.CalledOnValidThread());
181 registrar_
.UnregisterHandler(handler
);
184 InvalidatorState
P2PInvalidator::GetInvalidatorState() const {
185 DCHECK(thread_checker_
.CalledOnValidThread());
186 return registrar_
.GetInvalidatorState();
189 void P2PInvalidator::UpdateCredentials(
190 const std::string
& email
, const std::string
& token
) {
191 DCHECK(thread_checker_
.CalledOnValidThread());
192 notifier::Subscription subscription
;
193 subscription
.channel
= kSyncP2PNotificationChannel
;
194 // There may be some subtle issues around case sensitivity of the
195 // from field, but it doesn't matter too much since this is only
196 // used in p2p mode (which is only used in testing).
197 subscription
.from
= email
;
198 push_client_
->UpdateSubscriptions(
199 notifier::SubscriptionList(1, subscription
));
200 // If already logged in, the new credentials will take effect on the
201 // next reconnection.
202 push_client_
->UpdateCredentials(email
, token
);
206 void P2PInvalidator::RequestDetailedStatus(
207 base::Callback
<void(const base::DictionaryValue
&)> callback
) const {
208 DCHECK(thread_checker_
.CalledOnValidThread());
209 // TODO(mferreria): Make the P2P Invalidator work.
210 scoped_ptr
<base::DictionaryValue
> value(new base::DictionaryValue());
211 callback
.Run(*value
);
214 void P2PInvalidator::SendInvalidation(const ObjectIdSet
& ids
) {
215 DCHECK(thread_checker_
.CalledOnValidThread());
216 ObjectIdInvalidationMap invalidation_map
=
217 ObjectIdInvalidationMap::InvalidateAll(ids
);
218 const P2PNotificationData
notification_data(
219 invalidator_client_id_
, send_notification_target_
, invalidation_map
);
220 SendNotificationData(notification_data
);
223 void P2PInvalidator::OnNotificationsEnabled() {
224 DCHECK(thread_checker_
.CalledOnValidThread());
225 bool just_turned_on
= (notifications_enabled_
== false);
226 notifications_enabled_
= true;
227 registrar_
.UpdateInvalidatorState(INVALIDATIONS_ENABLED
);
228 if (just_turned_on
) {
229 const P2PNotificationData
notification_data(
230 invalidator_client_id_
,
232 ObjectIdInvalidationMap::InvalidateAll(
233 registrar_
.GetAllRegisteredIds()));
234 SendNotificationData(notification_data
);
238 void P2PInvalidator::OnNotificationsDisabled(
239 notifier::NotificationsDisabledReason reason
) {
240 DCHECK(thread_checker_
.CalledOnValidThread());
241 registrar_
.UpdateInvalidatorState(FromNotifierReason(reason
));
244 void P2PInvalidator::OnIncomingNotification(
245 const notifier::Notification
& notification
) {
246 DCHECK(thread_checker_
.CalledOnValidThread());
247 DVLOG(1) << "Received notification " << notification
.ToString();
249 DVLOG(1) << "Not logged in yet -- not emitting notification";
252 if (!notifications_enabled_
) {
253 DVLOG(1) << "Notifications not on -- not emitting notification";
256 if (notification
.channel
!= kSyncP2PNotificationChannel
) {
257 LOG(WARNING
) << "Notification from unexpected source "
258 << notification
.channel
;
260 P2PNotificationData notification_data
;
261 if (!notification_data
.ResetFromString(notification
.data
)) {
262 LOG(WARNING
) << "Could not parse notification data from "
263 << notification
.data
;
264 notification_data
= P2PNotificationData(
265 invalidator_client_id_
,
267 ObjectIdInvalidationMap::InvalidateAll(
268 registrar_
.GetAllRegisteredIds()));
270 if (!notification_data
.IsTargeted(invalidator_client_id_
)) {
271 DVLOG(1) << "Not a target of the notification -- "
272 << "not emitting notification";
275 registrar_
.DispatchInvalidationsToHandlers(
276 notification_data
.GetIdInvalidationMap());
279 void P2PInvalidator::SendNotificationDataForTest(
280 const P2PNotificationData
& notification_data
) {
281 DCHECK(thread_checker_
.CalledOnValidThread());
282 SendNotificationData(notification_data
);
285 void P2PInvalidator::SendNotificationData(
286 const P2PNotificationData
& notification_data
) {
287 DCHECK(thread_checker_
.CalledOnValidThread());
288 if (notification_data
.GetIdInvalidationMap().Empty()) {
289 DVLOG(1) << "Not sending XMPP notification with empty state map: "
290 << notification_data
.ToString();
293 notifier::Notification notification
;
294 notification
.channel
= kSyncP2PNotificationChannel
;
295 notification
.data
= notification_data
.ToString();
296 DVLOG(1) << "Sending XMPP notification: " << notification
.ToString();
297 push_client_
->SendNotification(notification
);
300 } // namespace syncer