// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sync/notifier/p2p_notifier.h"
#include "base/message_loop_proxy.h"
#include "chrome/browser/sync/notifier/sync_notifier_observer.h"
#include "chrome/browser/sync/protocol/service_constants.h"
#include "chrome/browser/sync/syncable/model_type_payload_map.h"
#include "jingle/notifier/listener/mediator_thread_impl.h"
#include "jingle/notifier/listener/talk_mediator_impl.h"
namespace sync_notifier {
namespace {
const char kSyncNotificationChannel[] = "http://www.google.com/chrome/sync";
const char kSyncNotificationData[] = "sync-ping-p2p";
} // namespace
P2PNotifier::P2PNotifier(
const notifier::NotifierOptions& notifier_options)
: talk_mediator_(
new notifier::TalkMediatorImpl(
new notifier::MediatorThreadImpl(notifier_options),
notifier_options)),
logged_in_(false),
notifications_enabled_(false),
construction_message_loop_proxy_(
base::MessageLoopProxy::CreateForCurrentThread()) {
talk_mediator_->SetDelegate(this);
}
P2PNotifier::~P2PNotifier() {
DCHECK(construction_message_loop_proxy_->BelongsToCurrentThread());
}
void P2PNotifier::AddObserver(SyncNotifierObserver* observer) {
CheckOrSetValidThread();
observer_list_.AddObserver(observer);
}
// Note: Since we need to shutdown TalkMediator on the method_thread, we are
// calling Logout on TalkMediator when the last observer is removed.
// Users will need to call UpdateCredentials again to use the same object.
// TODO(akalin): Think of a better solution to fix this.
void P2PNotifier::RemoveObserver(SyncNotifierObserver* observer) {
CheckOrSetValidThread();
observer_list_.RemoveObserver(observer);
// Logout after the last observer is removed.
if (observer_list_.size() == 0) {
talk_mediator_->Logout();
}
}
void P2PNotifier::SetState(const std::string& state) {
CheckOrSetValidThread();
}
void P2PNotifier::UpdateCredentials(
const std::string& email, const std::string& token) {
CheckOrSetValidThread();
// If already logged in, the new credentials will take effect on the
// next reconnection.
talk_mediator_->SetAuthToken(email, token, SYNC_SERVICE_NAME);
if (!logged_in_) {
if (!talk_mediator_->Login()) {
LOG(DFATAL) << "Could not login for " << email;
return;
}
notifier::Subscription subscription;
subscription.channel = kSyncNotificationChannel;
// There may be some subtle issues around case sensitivity of the
// from field, but it doesn't matter too much since this is only
// used in p2p mode (which is only used in testing).
subscription.from = email;
talk_mediator_->AddSubscription(subscription);
logged_in_ = true;
}
}
void P2PNotifier::UpdateEnabledTypes(const syncable::ModelTypeSet& types) {
CheckOrSetValidThread();
enabled_types_ = types;
MaybeEmitNotification();
}
void P2PNotifier::SendNotification() {
CheckOrSetValidThread();
VLOG(1) << "Sending XMPP notification...";
notifier::Notification notification;
notification.channel = kSyncNotificationChannel;
notification.data = kSyncNotificationData;
talk_mediator_->SendNotification(notification);
}
void P2PNotifier::OnNotificationStateChange(bool notifications_enabled) {
CheckOrSetValidThread();
notifications_enabled_ = notifications_enabled;
FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_,
OnNotificationStateChange(notifications_enabled_));
MaybeEmitNotification();
}
void P2PNotifier::OnIncomingNotification(
const notifier::Notification& notification) {
CheckOrSetValidThread();
VLOG(1) << "Sync received P2P notification.";
if (notification.channel != kSyncNotificationChannel) {
LOG(WARNING) << "Notification from unexpected source: "
<< notification.channel;
}
MaybeEmitNotification();
}
void P2PNotifier::OnOutgoingNotification() {}
void P2PNotifier::MaybeEmitNotification() {
if (!logged_in_) {
VLOG(1) << "Not logged in yet -- not emitting notification";
return;
}
if (!notifications_enabled_) {
VLOG(1) << "Notifications not enabled -- not emitting notification";
return;
}
if (enabled_types_.empty()) {
VLOG(1) << "No enabled types -- not emitting notification";
return;
}
syncable::ModelTypePayloadMap type_payloads =
syncable::ModelTypePayloadMapFromBitSet(
syncable::ModelTypeBitSetFromSet(enabled_types_), std::string());
FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_,
OnIncomingNotification(type_payloads));
}
void P2PNotifier::CheckOrSetValidThread() {
if (method_message_loop_proxy_) {
DCHECK(method_message_loop_proxy_->BelongsToCurrentThread());
} else {
method_message_loop_proxy_ =
base::MessageLoopProxy::CreateForCurrentThread();
}
}
} // namespace sync_notifier