OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/sync/notifier/p2p_notifier.h" | 5 #include "chrome/browser/sync/notifier/p2p_notifier.h" |
6 | 6 |
| 7 #include <algorithm> |
| 8 |
| 9 #include "base/logging.h" |
7 #include "base/message_loop_proxy.h" | 10 #include "base/message_loop_proxy.h" |
| 11 #include "base/json/json_reader.h" |
| 12 #include "base/json/json_writer.h" |
| 13 #include "base/values.h" |
8 #include "chrome/browser/sync/notifier/sync_notifier_observer.h" | 14 #include "chrome/browser/sync/notifier/sync_notifier_observer.h" |
9 #include "chrome/browser/sync/protocol/service_constants.h" | 15 #include "chrome/browser/sync/protocol/service_constants.h" |
10 #include "chrome/browser/sync/syncable/model_type_payload_map.h" | 16 #include "chrome/browser/sync/syncable/model_type_payload_map.h" |
11 #include "jingle/notifier/listener/mediator_thread_impl.h" | |
12 #include "jingle/notifier/listener/talk_mediator_impl.h" | |
13 | 17 |
14 namespace sync_notifier { | 18 namespace sync_notifier { |
15 | 19 |
16 namespace { | 20 namespace { |
| 21 |
17 const char kSyncNotificationChannel[] = "http://www.google.com/chrome/sync"; | 22 const char kSyncNotificationChannel[] = "http://www.google.com/chrome/sync"; |
18 const char kSyncNotificationData[] = "sync-ping-p2p"; | 23 |
| 24 const char kNotifySelf[] = "notifySelf"; |
| 25 const char kNotifyOthers[] = "notifyOthers"; |
| 26 const char kNotifyAll[] = "notifyAll"; |
| 27 |
| 28 const char kSenderIdKey[] = "senderId"; |
| 29 const char kNotificationTypeKey[] = "notificationType"; |
| 30 const char kChangedTypesKey[] = "changedTypes"; |
| 31 |
19 } // namespace | 32 } // namespace |
20 | 33 |
21 P2PNotifier::P2PNotifier( | 34 std::string P2PNotificationTargetToString(P2PNotificationTarget target) { |
22 const notifier::NotifierOptions& notifier_options) | 35 switch (target) { |
23 : talk_mediator_( | 36 case NOTIFY_SELF: |
24 new notifier::TalkMediatorImpl( | 37 return kNotifySelf; |
25 new notifier::MediatorThreadImpl(notifier_options), | 38 case NOTIFY_OTHERS: |
26 notifier_options)), | 39 return kNotifyOthers; |
| 40 case NOTIFY_ALL: |
| 41 return kNotifyAll; |
| 42 default: |
| 43 NOTREACHED(); |
| 44 return ""; |
| 45 } |
| 46 } |
| 47 |
| 48 P2PNotificationTarget P2PNotificationTargetFromString( |
| 49 const std::string& target_str) { |
| 50 if (target_str == kNotifySelf) { |
| 51 return NOTIFY_SELF; |
| 52 } |
| 53 if (target_str == kNotifyOthers) { |
| 54 return NOTIFY_OTHERS; |
| 55 } |
| 56 if (target_str == kNotifyAll) { |
| 57 return NOTIFY_ALL; |
| 58 } |
| 59 LOG(WARNING) << "Could not parse " << target_str; |
| 60 return NOTIFY_SELF; |
| 61 } |
| 62 |
| 63 P2PNotificationData::P2PNotificationData() : target_(NOTIFY_SELF) {} |
| 64 |
| 65 P2PNotificationData::P2PNotificationData( |
| 66 const std::string& sender_id, |
| 67 P2PNotificationTarget target, |
| 68 const syncable::ModelTypeSet& changed_types) |
| 69 : sender_id_(sender_id), |
| 70 target_(target), |
| 71 changed_types_(changed_types) {} |
| 72 |
| 73 P2PNotificationData::~P2PNotificationData() {} |
| 74 |
| 75 bool P2PNotificationData::IsTargeted(const std::string& id) const { |
| 76 switch (target_) { |
| 77 case NOTIFY_SELF: |
| 78 return sender_id_ == id; |
| 79 case NOTIFY_OTHERS: |
| 80 return sender_id_ != id; |
| 81 case NOTIFY_ALL: |
| 82 return true; |
| 83 default: |
| 84 NOTREACHED(); |
| 85 return false; |
| 86 } |
| 87 } |
| 88 |
| 89 const syncable::ModelTypeSet& P2PNotificationData::GetChangedTypes() const { |
| 90 return changed_types_; |
| 91 } |
| 92 |
| 93 bool P2PNotificationData::Equals(const P2PNotificationData& other) const { |
| 94 return |
| 95 (sender_id_ == other.sender_id_) && |
| 96 (target_ == other.target_) && |
| 97 (changed_types_ == other.changed_types_); |
| 98 } |
| 99 |
| 100 std::string P2PNotificationData::ToString() const { |
| 101 scoped_ptr<DictionaryValue> dict(new DictionaryValue()); |
| 102 dict->SetString(kSenderIdKey, sender_id_); |
| 103 dict->SetString(kNotificationTypeKey, |
| 104 P2PNotificationTargetToString(target_)); |
| 105 dict->Set(kChangedTypesKey, syncable::ModelTypeSetToValue(changed_types_)); |
| 106 std::string json; |
| 107 base::JSONWriter::Write(dict.get(), false /* pretty_print */, &json); |
| 108 return json; |
| 109 } |
| 110 |
| 111 bool P2PNotificationData::ResetFromString(const std::string& str) { |
| 112 scoped_ptr<Value> data_value( |
| 113 base::JSONReader::Read(str, false /* allow_trailing_comma */)); |
| 114 if (!data_value.get()) { |
| 115 LOG(WARNING) << "Could not parse " << str; |
| 116 return false; |
| 117 } |
| 118 if (!data_value->IsType(Value::TYPE_DICTIONARY)) { |
| 119 LOG(WARNING) << "Could not parse " << str << " as a dictionary"; |
| 120 return false; |
| 121 } |
| 122 // TODO(akalin): Use Values::AsDictionary() when it becomes |
| 123 // available. |
| 124 DictionaryValue* data_dict = |
| 125 static_cast<DictionaryValue*>(data_value.get()); |
| 126 if (!data_dict->GetString(kSenderIdKey, &sender_id_)) { |
| 127 LOG(WARNING) << "Could not find string value for " << kSenderIdKey; |
| 128 } |
| 129 std::string target_str; |
| 130 if (!data_dict->GetString(kNotificationTypeKey, &target_str)) { |
| 131 LOG(WARNING) << "Could not find string value for " |
| 132 << kNotificationTypeKey; |
| 133 } |
| 134 target_ = P2PNotificationTargetFromString(target_str); |
| 135 ListValue* changed_types_list = NULL; |
| 136 if (!data_dict->GetList(kChangedTypesKey, &changed_types_list)) { |
| 137 LOG(WARNING) << "Could not find list value for " |
| 138 << kChangedTypesKey; |
| 139 return false; |
| 140 } |
| 141 changed_types_ = syncable::ModelTypeSetFromValue(*changed_types_list); |
| 142 return true; |
| 143 } |
| 144 |
| 145 P2PNotifier::P2PNotifier(notifier::TalkMediator* talk_mediator) |
| 146 : talk_mediator_(talk_mediator), |
27 logged_in_(false), | 147 logged_in_(false), |
28 notifications_enabled_(false), | 148 notifications_enabled_(false), |
29 parent_message_loop_proxy_( | 149 parent_message_loop_proxy_( |
30 base::MessageLoopProxy::current()) { | 150 base::MessageLoopProxy::current()) { |
31 talk_mediator_->SetDelegate(this); | 151 talk_mediator_->SetDelegate(this); |
32 } | 152 } |
33 | 153 |
34 P2PNotifier::~P2PNotifier() { | 154 P2PNotifier::~P2PNotifier() { |
35 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | 155 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); |
36 } | 156 } |
(...skipping 12 matching lines...) Expand all Loading... |
49 observer_list_.RemoveObserver(observer); | 169 observer_list_.RemoveObserver(observer); |
50 | 170 |
51 // Logout after the last observer is removed. | 171 // Logout after the last observer is removed. |
52 if (observer_list_.size() == 0) { | 172 if (observer_list_.size() == 0) { |
53 talk_mediator_->Logout(); | 173 talk_mediator_->Logout(); |
54 } | 174 } |
55 } | 175 } |
56 | 176 |
57 void P2PNotifier::SetUniqueId(const std::string& unique_id) { | 177 void P2PNotifier::SetUniqueId(const std::string& unique_id) { |
58 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | 178 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); |
| 179 unique_id_ = unique_id; |
59 } | 180 } |
60 | 181 |
61 void P2PNotifier::SetState(const std::string& state) { | 182 void P2PNotifier::SetState(const std::string& state) { |
62 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | 183 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); |
63 } | 184 } |
64 | 185 |
65 void P2PNotifier::UpdateCredentials( | 186 void P2PNotifier::UpdateCredentials( |
66 const std::string& email, const std::string& token) { | 187 const std::string& email, const std::string& token) { |
67 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | 188 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); |
68 // If already logged in, the new credentials will take effect on the | 189 // If already logged in, the new credentials will take effect on the |
(...skipping 10 matching lines...) Expand all Loading... |
79 // There may be some subtle issues around case sensitivity of the | 200 // There may be some subtle issues around case sensitivity of the |
80 // from field, but it doesn't matter too much since this is only | 201 // from field, but it doesn't matter too much since this is only |
81 // used in p2p mode (which is only used in testing). | 202 // used in p2p mode (which is only used in testing). |
82 subscription.from = email; | 203 subscription.from = email; |
83 talk_mediator_->AddSubscription(subscription); | 204 talk_mediator_->AddSubscription(subscription); |
84 | 205 |
85 logged_in_ = true; | 206 logged_in_ = true; |
86 } | 207 } |
87 } | 208 } |
88 | 209 |
89 void P2PNotifier::UpdateEnabledTypes(const syncable::ModelTypeSet& types) { | 210 void P2PNotifier::UpdateEnabledTypes( |
| 211 const syncable::ModelTypeSet& enabled_types) { |
90 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | 212 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); |
91 enabled_types_ = types; | 213 syncable::ModelTypeSet new_enabled_types; |
92 MaybeEmitNotification(); | 214 std::set_difference(enabled_types.begin(), enabled_types.end(), |
| 215 enabled_types_.begin(), enabled_types_.end(), |
| 216 std::inserter(new_enabled_types, |
| 217 new_enabled_types.end())); |
| 218 enabled_types_ = enabled_types; |
| 219 const P2PNotificationData notification_data( |
| 220 unique_id_, NOTIFY_SELF, new_enabled_types); |
| 221 SendNotificationData(notification_data); |
93 } | 222 } |
94 | 223 |
95 void P2PNotifier::SendNotification() { | 224 void P2PNotifier::SendNotification( |
| 225 const syncable::ModelTypeSet& changed_types) { |
96 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | 226 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); |
97 VLOG(1) << "Sending XMPP notification..."; | 227 const P2PNotificationData notification_data( |
98 notifier::Notification notification; | 228 unique_id_, NOTIFY_OTHERS, changed_types); |
99 notification.channel = kSyncNotificationChannel; | 229 SendNotificationData(notification_data); |
100 notification.data = kSyncNotificationData; | |
101 talk_mediator_->SendNotification(notification); | |
102 } | 230 } |
103 | 231 |
104 void P2PNotifier::OnNotificationStateChange(bool notifications_enabled) { | 232 void P2PNotifier::OnNotificationStateChange(bool notifications_enabled) { |
105 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | 233 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); |
| 234 bool disabled_to_enabled = notifications_enabled && !notifications_enabled_; |
106 notifications_enabled_ = notifications_enabled; | 235 notifications_enabled_ = notifications_enabled; |
107 FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_, | 236 FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_, |
108 OnNotificationStateChange(notifications_enabled_)); | 237 OnNotificationStateChange(notifications_enabled_)); |
109 MaybeEmitNotification(); | 238 if (disabled_to_enabled) { |
| 239 const P2PNotificationData notification_data( |
| 240 unique_id_, NOTIFY_SELF, enabled_types_); |
| 241 SendNotificationData(notification_data); |
| 242 } |
110 } | 243 } |
111 | 244 |
112 void P2PNotifier::OnIncomingNotification( | 245 void P2PNotifier::OnIncomingNotification( |
113 const notifier::Notification& notification) { | 246 const notifier::Notification& notification) { |
114 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | 247 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); |
115 VLOG(1) << "Sync received P2P notification."; | 248 VLOG(1) << "Received notification " << notification.ToString(); |
116 if (notification.channel != kSyncNotificationChannel) { | |
117 LOG(WARNING) << "Notification from unexpected source: " | |
118 << notification.channel; | |
119 } | |
120 MaybeEmitNotification(); | |
121 } | |
122 | |
123 void P2PNotifier::OnOutgoingNotification() {} | |
124 | |
125 void P2PNotifier::MaybeEmitNotification() { | |
126 if (!logged_in_) { | 249 if (!logged_in_) { |
127 VLOG(1) << "Not logged in yet -- not emitting notification"; | 250 VLOG(1) << "Not logged in yet -- not emitting notification"; |
128 return; | 251 return; |
129 } | 252 } |
130 if (!notifications_enabled_) { | 253 if (!notifications_enabled_) { |
131 VLOG(1) << "Notifications not enabled -- not emitting notification"; | 254 VLOG(1) << "Notifications not enabled -- not emitting notification"; |
132 return; | 255 return; |
133 } | 256 } |
134 if (enabled_types_.empty()) { | 257 if (notification.channel != kSyncNotificationChannel) { |
135 VLOG(1) << "No enabled types -- not emitting notification"; | 258 LOG(WARNING) << "Notification from unexpected source " |
| 259 << notification.channel; |
| 260 } |
| 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 = |
| 266 P2PNotificationData(unique_id_, NOTIFY_ALL, enabled_types_); |
| 267 } |
| 268 if (!notification_data.IsTargeted(unique_id_)) { |
| 269 VLOG(1) << "Not a target of the notification -- " |
| 270 << "not emitting notification"; |
136 return; | 271 return; |
137 } | 272 } |
138 syncable::ModelTypePayloadMap type_payloads = | 273 if (notification_data.GetChangedTypes().empty()) { |
| 274 VLOG(1) << "No changed types -- not emitting notification"; |
| 275 return; |
| 276 } |
| 277 const syncable::ModelTypePayloadMap& type_payloads = |
139 syncable::ModelTypePayloadMapFromBitSet( | 278 syncable::ModelTypePayloadMapFromBitSet( |
140 syncable::ModelTypeBitSetFromSet(enabled_types_), std::string()); | 279 syncable::ModelTypeBitSetFromSet( |
| 280 notification_data.GetChangedTypes()), std::string()); |
141 FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_, | 281 FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_, |
142 OnIncomingNotification(type_payloads)); | 282 OnIncomingNotification(type_payloads)); |
143 } | 283 } |
144 | 284 |
| 285 void P2PNotifier::OnOutgoingNotification() {} |
| 286 |
| 287 void P2PNotifier::SendNotificationDataForTest( |
| 288 const P2PNotificationData& notification_data) { |
| 289 SendNotificationData(notification_data); |
| 290 } |
| 291 |
| 292 void P2PNotifier::SendNotificationData( |
| 293 const P2PNotificationData& notification_data) { |
| 294 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); |
| 295 notifier::Notification notification; |
| 296 notification.channel = kSyncNotificationChannel; |
| 297 notification.data = notification_data.ToString(); |
| 298 VLOG(1) << "Sending XMPP notification: " << notification.ToString(); |
| 299 talk_mediator_->SendNotification(notification); |
| 300 } |
| 301 |
145 } // namespace sync_notifier | 302 } // namespace sync_notifier |
OLD | NEW |