1 // Copyright (c) 2012 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 "sync/notifier/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 "jingle/notifier/listener/push_client.h"
15 #include "sync/notifier/invalidation_handler.h"
16 #include "sync/notifier/invalidation_util.h"
17 #include "sync/notifier/object_id_invalidation_map.h"
21 const char kSyncP2PNotificationChannel
[] = "http://www.google.com/chrome/sync";
25 const char kNotifySelf
[] = "notifySelf";
26 const char kNotifyOthers
[] = "notifyOthers";
27 const char kNotifyAll
[] = "notifyAll";
29 const char kSenderIdKey
[] = "senderId";
30 const char kNotificationTypeKey
[] = "notificationType";
31 const char kInvalidationsKey
[] = "invalidations";
35 std::string
P2PNotificationTargetToString(P2PNotificationTarget target
) {
49 P2PNotificationTarget
P2PNotificationTargetFromString(
50 const std::string
& target_str
) {
51 if (target_str
== kNotifySelf
) {
54 if (target_str
== kNotifyOthers
) {
57 if (target_str
== kNotifyAll
) {
60 LOG(WARNING
) << "Could not parse " << target_str
;
64 P2PNotificationData::P2PNotificationData()
65 : target_(NOTIFY_SELF
) {}
67 P2PNotificationData::P2PNotificationData(
68 const std::string
& sender_id
,
69 P2PNotificationTarget target
,
70 const ObjectIdInvalidationMap
& invalidation_map
)
71 : sender_id_(sender_id
),
73 invalidation_map_(invalidation_map
) {}
75 P2PNotificationData::~P2PNotificationData() {}
77 bool P2PNotificationData::IsTargeted(const std::string
& id
) const {
80 return sender_id_
== id
;
82 return sender_id_
!= id
;
91 const ObjectIdInvalidationMap
&
92 P2PNotificationData::GetIdInvalidationMap() const {
93 return invalidation_map_
;
96 bool P2PNotificationData::Equals(const P2PNotificationData
& other
) const {
98 (sender_id_
== other
.sender_id_
) &&
99 (target_
== other
.target_
) &&
100 (invalidation_map_
== other
.invalidation_map_
);
103 std::string
P2PNotificationData::ToString() const {
104 scoped_ptr
<base::DictionaryValue
> dict(new base::DictionaryValue());
105 dict
->SetString(kSenderIdKey
, sender_id_
);
106 dict
->SetString(kNotificationTypeKey
,
107 P2PNotificationTargetToString(target_
));
108 dict
->Set(kInvalidationsKey
, invalidation_map_
.ToValue().release());
110 base::JSONWriter::Write(dict
.get(), &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 void 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 registrar_
.UpdateRegisteredIds(handler
, ids
);
171 const P2PNotificationData
notification_data(
172 invalidator_client_id_
,
173 send_notification_target_
,
174 ObjectIdInvalidationMap::InvalidateAll(ids
));
175 SendNotificationData(notification_data
);
178 void P2PInvalidator::UnregisterHandler(InvalidationHandler
* handler
) {
179 DCHECK(thread_checker_
.CalledOnValidThread());
180 registrar_
.UnregisterHandler(handler
);
183 InvalidatorState
P2PInvalidator::GetInvalidatorState() const {
184 DCHECK(thread_checker_
.CalledOnValidThread());
185 return registrar_
.GetInvalidatorState();
188 void P2PInvalidator::UpdateCredentials(
189 const std::string
& email
, const std::string
& token
) {
190 DCHECK(thread_checker_
.CalledOnValidThread());
191 notifier::Subscription subscription
;
192 subscription
.channel
= kSyncP2PNotificationChannel
;
193 // There may be some subtle issues around case sensitivity of the
194 // from field, but it doesn't matter too much since this is only
195 // used in p2p mode (which is only used in testing).
196 subscription
.from
= email
;
197 push_client_
->UpdateSubscriptions(
198 notifier::SubscriptionList(1, subscription
));
199 // If already logged in, the new credentials will take effect on the
200 // next reconnection.
201 push_client_
->UpdateCredentials(email
, token
);
205 void P2PInvalidator::RequestDetailedStatus(
206 base::Callback
<void(const base::DictionaryValue
&)> callback
) const {
207 DCHECK(thread_checker_
.CalledOnValidThread());
208 // TODO(mferreria): Make the P2P Invalidator work.
209 scoped_ptr
<base::DictionaryValue
> value(new base::DictionaryValue());
210 callback
.Run(*value
);
213 void P2PInvalidator::SendInvalidation(const ObjectIdSet
& ids
) {
214 DCHECK(thread_checker_
.CalledOnValidThread());
215 ObjectIdInvalidationMap invalidation_map
=
216 ObjectIdInvalidationMap::InvalidateAll(ids
);
217 const P2PNotificationData
notification_data(
218 invalidator_client_id_
, send_notification_target_
, invalidation_map
);
219 SendNotificationData(notification_data
);
222 void P2PInvalidator::OnNotificationsEnabled() {
223 DCHECK(thread_checker_
.CalledOnValidThread());
224 bool just_turned_on
= (notifications_enabled_
== false);
225 notifications_enabled_
= true;
226 registrar_
.UpdateInvalidatorState(INVALIDATIONS_ENABLED
);
227 if (just_turned_on
) {
228 const P2PNotificationData
notification_data(
229 invalidator_client_id_
,
231 ObjectIdInvalidationMap::InvalidateAll(
232 registrar_
.GetAllRegisteredIds()));
233 SendNotificationData(notification_data
);
237 void P2PInvalidator::OnNotificationsDisabled(
238 notifier::NotificationsDisabledReason reason
) {
239 DCHECK(thread_checker_
.CalledOnValidThread());
240 registrar_
.UpdateInvalidatorState(FromNotifierReason(reason
));
243 void P2PInvalidator::OnIncomingNotification(
244 const notifier::Notification
& notification
) {
245 DCHECK(thread_checker_
.CalledOnValidThread());
246 DVLOG(1) << "Received notification " << notification
.ToString();
248 DVLOG(1) << "Not logged in yet -- not emitting notification";
251 if (!notifications_enabled_
) {
252 DVLOG(1) << "Notifications not on -- not emitting notification";
255 if (notification
.channel
!= kSyncP2PNotificationChannel
) {
256 LOG(WARNING
) << "Notification from unexpected source "
257 << notification
.channel
;
259 P2PNotificationData notification_data
;
260 if (!notification_data
.ResetFromString(notification
.data
)) {
261 LOG(WARNING
) << "Could not parse notification data from "
262 << notification
.data
;
263 notification_data
= P2PNotificationData(
264 invalidator_client_id_
,
266 ObjectIdInvalidationMap::InvalidateAll(
267 registrar_
.GetAllRegisteredIds()));
269 if (!notification_data
.IsTargeted(invalidator_client_id_
)) {
270 DVLOG(1) << "Not a target of the notification -- "
271 << "not emitting notification";
274 registrar_
.DispatchInvalidationsToHandlers(
275 notification_data
.GetIdInvalidationMap());
278 void P2PInvalidator::SendNotificationDataForTest(
279 const P2PNotificationData
& notification_data
) {
280 DCHECK(thread_checker_
.CalledOnValidThread());
281 SendNotificationData(notification_data
);
284 void P2PInvalidator::SendNotificationData(
285 const P2PNotificationData
& notification_data
) {
286 DCHECK(thread_checker_
.CalledOnValidThread());
287 if (notification_data
.GetIdInvalidationMap().Empty()) {
288 DVLOG(1) << "Not sending XMPP notification with empty state map: "
289 << notification_data
.ToString();
292 notifier::Notification notification
;
293 notification
.channel
= kSyncP2PNotificationChannel
;
294 notification
.data
= notification_data
.ToString();
295 DVLOG(1) << "Sending XMPP notification: " << notification
.ToString();
296 push_client_
->SendNotification(notification
);
299 } // namespace syncer