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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/sync/notifier/p2p_notifier.h"
6
7 #include "base/message_loop_proxy.h"
8 #include "chrome/browser/sync/notifier/sync_notifier_observer.h"
9 #include "chrome/browser/sync/protocol/service_constants.h"
10 #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
14 namespace sync_notifier {
15
16 namespace {
17 const char kSyncNotificationChannel[] = "http://www.google.com/chrome/sync";
18 const char kSyncNotificationData[] = "sync-ping-p2p";
19 } // namespace
20
P2PNotifier(const notifier::NotifierOptions & notifier_options)21 P2PNotifier::P2PNotifier(
22 const notifier::NotifierOptions& notifier_options)
23 : talk_mediator_(
24 new notifier::TalkMediatorImpl(
25 new notifier::MediatorThreadImpl(notifier_options),
26 notifier_options)),
27 logged_in_(false),
28 notifications_enabled_(false),
29 construction_message_loop_proxy_(
30 base::MessageLoopProxy::CreateForCurrentThread()) {
31 talk_mediator_->SetDelegate(this);
32 }
33
~P2PNotifier()34 P2PNotifier::~P2PNotifier() {
35 DCHECK(construction_message_loop_proxy_->BelongsToCurrentThread());
36 }
37
AddObserver(SyncNotifierObserver * observer)38 void P2PNotifier::AddObserver(SyncNotifierObserver* observer) {
39 CheckOrSetValidThread();
40 observer_list_.AddObserver(observer);
41 }
42
43 // Note: Since we need to shutdown TalkMediator on the method_thread, we are
44 // calling Logout on TalkMediator when the last observer is removed.
45 // Users will need to call UpdateCredentials again to use the same object.
46 // TODO(akalin): Think of a better solution to fix this.
RemoveObserver(SyncNotifierObserver * observer)47 void P2PNotifier::RemoveObserver(SyncNotifierObserver* observer) {
48 CheckOrSetValidThread();
49 observer_list_.RemoveObserver(observer);
50
51 // Logout after the last observer is removed.
52 if (observer_list_.size() == 0) {
53 talk_mediator_->Logout();
54 }
55 }
56
SetState(const std::string & state)57 void P2PNotifier::SetState(const std::string& state) {
58 CheckOrSetValidThread();
59 }
60
UpdateCredentials(const std::string & email,const std::string & token)61 void P2PNotifier::UpdateCredentials(
62 const std::string& email, const std::string& token) {
63 CheckOrSetValidThread();
64 // If already logged in, the new credentials will take effect on the
65 // next reconnection.
66 talk_mediator_->SetAuthToken(email, token, SYNC_SERVICE_NAME);
67 if (!logged_in_) {
68 if (!talk_mediator_->Login()) {
69 LOG(DFATAL) << "Could not login for " << email;
70 return;
71 }
72
73 notifier::Subscription subscription;
74 subscription.channel = kSyncNotificationChannel;
75 // There may be some subtle issues around case sensitivity of the
76 // from field, but it doesn't matter too much since this is only
77 // used in p2p mode (which is only used in testing).
78 subscription.from = email;
79 talk_mediator_->AddSubscription(subscription);
80
81 logged_in_ = true;
82 }
83 }
84
UpdateEnabledTypes(const syncable::ModelTypeSet & types)85 void P2PNotifier::UpdateEnabledTypes(const syncable::ModelTypeSet& types) {
86 CheckOrSetValidThread();
87 enabled_types_ = types;
88 MaybeEmitNotification();
89 }
90
SendNotification()91 void P2PNotifier::SendNotification() {
92 CheckOrSetValidThread();
93 VLOG(1) << "Sending XMPP notification...";
94 notifier::Notification notification;
95 notification.channel = kSyncNotificationChannel;
96 notification.data = kSyncNotificationData;
97 talk_mediator_->SendNotification(notification);
98 }
99
OnNotificationStateChange(bool notifications_enabled)100 void P2PNotifier::OnNotificationStateChange(bool notifications_enabled) {
101 CheckOrSetValidThread();
102 notifications_enabled_ = notifications_enabled;
103 FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_,
104 OnNotificationStateChange(notifications_enabled_));
105 MaybeEmitNotification();
106 }
107
OnIncomingNotification(const notifier::Notification & notification)108 void P2PNotifier::OnIncomingNotification(
109 const notifier::Notification& notification) {
110 CheckOrSetValidThread();
111 VLOG(1) << "Sync received P2P notification.";
112 if (notification.channel != kSyncNotificationChannel) {
113 LOG(WARNING) << "Notification from unexpected source: "
114 << notification.channel;
115 }
116 MaybeEmitNotification();
117 }
118
OnOutgoingNotification()119 void P2PNotifier::OnOutgoingNotification() {}
120
MaybeEmitNotification()121 void P2PNotifier::MaybeEmitNotification() {
122 if (!logged_in_) {
123 VLOG(1) << "Not logged in yet -- not emitting notification";
124 return;
125 }
126 if (!notifications_enabled_) {
127 VLOG(1) << "Notifications not enabled -- not emitting notification";
128 return;
129 }
130 if (enabled_types_.empty()) {
131 VLOG(1) << "No enabled types -- not emitting notification";
132 return;
133 }
134 syncable::ModelTypePayloadMap type_payloads =
135 syncable::ModelTypePayloadMapFromBitSet(
136 syncable::ModelTypeBitSetFromSet(enabled_types_), std::string());
137 FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_,
138 OnIncomingNotification(type_payloads));
139 }
140
CheckOrSetValidThread()141 void P2PNotifier::CheckOrSetValidThread() {
142 if (method_message_loop_proxy_) {
143 DCHECK(method_message_loop_proxy_->BelongsToCurrentThread());
144 } else {
145 method_message_loop_proxy_ =
146 base::MessageLoopProxy::CreateForCurrentThread();
147 }
148 }
149
150 } // namespace sync_notifier
151