[Telepathy-commits] [telepathy-qt4/master] Connection: Use ReadinessHelper class (sanitize code and fix some bugs).
Andre Moreira Magalhaes (andrunko)
andre.magalhaes at collabora.co.uk
Wed Feb 18 14:43:31 PST 2009
P.s: Tests broken (will be fixed in a later patch)
---
TelepathyQt4/Client/connection-internal.h | 6 +-
TelepathyQt4/Client/connection.cpp | 638 +++++++++--------------------
TelepathyQt4/Client/connection.h | 19 +-
3 files changed, 213 insertions(+), 450 deletions(-)
diff --git a/TelepathyQt4/Client/connection-internal.h b/TelepathyQt4/Client/connection-internal.h
index 9dff267..dead1f0 100644
--- a/TelepathyQt4/Client/connection-internal.h
+++ b/TelepathyQt4/Client/connection-internal.h
@@ -26,6 +26,8 @@
#include <TelepathyQt4/Client/PendingOperation>
+#include <QSet>
+
namespace Telepathy
{
namespace Client
@@ -36,14 +38,14 @@ class Connection::PendingConnect : public PendingOperation
Q_OBJECT
public:
- PendingConnect(Connection *parent, Connection::Features features);
+ PendingConnect(Connection *parent, const QSet<uint> &requestedFeatures);
private Q_SLOTS:
void onConnectReply(QDBusPendingCallWatcher *);
void onBecomeReadyReply(Telepathy::Client::PendingOperation *);
private:
- Connection::Features features;
+ QSet<uint> requestedFeatures;
};
} // Telepathy::Client
diff --git a/TelepathyQt4/Client/connection.cpp b/TelepathyQt4/Client/connection.cpp
index 32945b7..387b6cd 100644
--- a/TelepathyQt4/Client/connection.cpp
+++ b/TelepathyQt4/Client/connection.cpp
@@ -35,8 +35,9 @@
#include <TelepathyQt4/Client/PendingContacts>
#include <TelepathyQt4/Client/PendingFailure>
#include <TelepathyQt4/Client/PendingHandles>
-#include <TelepathyQt4/Client/PendingReadyConnection>
+#include <TelepathyQt4/Client/PendingReady>
#include <TelepathyQt4/Client/PendingVoidMethodCall>
+#include <TelepathyQt4/Client/ReadinessHelper>
#include <QMap>
#include <QMetaObject>
@@ -74,57 +75,16 @@ namespace Client
struct Connection::Private
{
- /*
- * \enum Connection::Private::Readiness
- *
- * Describes readiness of the Connection for usage. The readiness depends
- * on the state of the remote object. In suitable states, an asynchronous
- * introspection process is started, and the Connection becomes more ready
- * when that process is completed.
- *
- * \value ReadinessJustCreated, The object has just been created and introspection
- * is still in progress. No functionality is available.
- * The readiness can change to any other state depending
- * on the result of the initial state query to the remote
- * object.
- * \value ReadinessNotYetConnected The remote object is in the Disconnected state and
- * introspection relevant to that state has been completed.
- * This state is useful for being able to set your presence status
- * (through the SimplePresence interface) before connecting. Most other
- * functionality is unavailable, though.
- * The readiness can change to ReadinessConnecting and ReadinessDead.
- * \value ReadinessConnecting The remote object is in the Connecting state. Most functionality is
- * unavailable.
- * The readiness can change to ReadinessFull and ReadinessDead.
- * \value ReadinessFull The connection is in the Connected state and all introspection
- * has been completed. Most functionality is available.
- * The readiness can change to ReadinessDead.
- * \value ReadinessDead The remote object has gone into a state where it can no longer be
- * used. No functionality is available.
- * No further readiness changes are possible.
- */
- enum Readiness {
- ReadinessJustCreated = 0,
- ReadinessNotYetConnected = 5,
- ReadinessConnecting = 10,
- ReadinessFull = 15,
- ReadinessDead = 20,
- _ReadinessInvalid = 0xffff
- };
-
Private(Connection *parent);
~Private();
- void startIntrospection();
- void introspectMain();
- void introspectContacts();
- void introspectSimplePresence();
- void introspectSelfContact();
- void introspectSelfHandle();
-
- void changeReadiness(Readiness newReadiness);
+ void init();
- void updatePendingOperations();
+ static void introspectMain(Private *self);
+ void introspectSelfHandle();
+ void introspectContacts();
+ static void introspectSelfContact(Private *self);
+ static void introspectSimplePresence(Private *self);
struct HandleContext;
@@ -138,18 +98,10 @@ struct Connection::Private
DBus::PropertiesInterface *properties;
ConnectionInterfaceSimplePresenceInterface *simplePresence;
- bool ready;
- QList<PendingReadyConnection *> pendingOperations;
+ ReadinessHelper *readinessHelper;
// Introspection
- bool initialIntrospection;
- Readiness readiness;
QStringList interfaces;
- QQueue<void (Private::*)()> introspectQueue;
-
- Connection::Features features;
- Connection::Features pendingFeatures;
- Connection::Features missingFeatures;
// Introspected properties
// keep pendingStatus and pendingStatusReason until we emit statusChanged
@@ -158,11 +110,10 @@ struct Connection::Private
uint pendingStatusReason;
uint status;
uint statusReason;
- bool haveInitialStatus;
+
SimpleStatusSpecMap simplePresenceStatuses;
QSharedPointer<Contact> selfContact;
QStringList contactAttributeInterfaces;
-
uint selfHandle;
// (Bus connection name, service name) -> HandleContext
@@ -206,18 +157,47 @@ Connection::Private::Private(Connection *parent)
parent->busName(), parent->objectPath(), parent)),
properties(0),
simplePresence(0),
- ready(false),
- initialIntrospection(false),
- readiness(ReadinessJustCreated),
pendingStatus(Connection::StatusUnknown),
pendingStatusReason(ConnectionStatusReasonNoneSpecified),
status(Connection::StatusUnknown),
statusReason(ConnectionStatusReasonNoneSpecified),
- haveInitialStatus(false),
selfHandle(0),
handleContext(0),
contactManager(new ContactManager(parent))
{
+ QMap<uint, ReadinessHelper::Introspectable> introspectables;
+
+ ReadinessHelper::Introspectable introspectableCore(
+ QSet<uint>() << Connection::StatusDisconnected << Connection::StatusConnected, // makesSenseForStatuses
+ QSet<uint>(), // dependsOnFeatures
+ QStringList(), // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectMain,
+ this);
+ introspectables[0] = introspectableCore;
+
+ ReadinessHelper::Introspectable introspectableSelfContact(
+ QSet<uint>() << Connection::StatusConnected, // makesSenseForStatuses
+ QSet<uint>() << 0, // dependsOnFeatures (core)
+ QStringList() << TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS, // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectSelfContact,
+ this);
+ introspectables[FeatureSelfContact] = introspectableSelfContact;
+
+ ReadinessHelper::Introspectable introspectableSimplePresence(
+ QSet<uint>() << Connection::StatusConnected, // makesSenseForStatuses
+ QSet<uint>() << 0, // dependsOnFeatures (core)
+ QStringList() << TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE, // dependsOnInterfaces
+ (ReadinessHelper::IntrospectFunc) &Private::introspectSimplePresence,
+ this);
+ introspectables[FeatureSimplePresence] = introspectableSimplePresence;
+
+ readinessHelper = new ReadinessHelper(status,
+ introspectables, parent);
+ parent->connect(readinessHelper,
+ SIGNAL(statusReady(uint)),
+ SLOT(onStatusReady(uint)));
+
+ init();
}
Connection::Private::~Private()
@@ -262,16 +242,14 @@ Connection::Private::~Private()
}
}
-void Connection::Private::startIntrospection()
+void Connection::Private::init()
{
debug() << "Connecting to StatusChanged()";
-
parent->connect(baseInterface,
SIGNAL(StatusChanged(uint, uint)),
SLOT(onStatusChanged(uint, uint)));
debug() << "Calling GetStatus()";
-
QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(baseInterface->GetStatus(), parent);
parent->connect(watcher,
@@ -296,17 +274,18 @@ void Connection::Private::startIntrospection()
++handleContext->refcount;
}
-void Connection::Private::introspectMain()
+void Connection::Private::introspectMain(Connection::Private *self)
{
// Introspecting the main interface is currently just calling
// GetInterfaces(), but it might include other stuff in the future if we
// gain GetAll-able properties on the connection
debug() << "Calling GetInterfaces()";
QDBusPendingCallWatcher *watcher =
- new QDBusPendingCallWatcher(baseInterface->GetInterfaces(), parent);
- parent->connect(watcher,
- SIGNAL(finished(QDBusPendingCallWatcher *)),
- SLOT(gotInterfaces(QDBusPendingCallWatcher *)));
+ new QDBusPendingCallWatcher(self->baseInterface->GetInterfaces(),
+ self->parent);
+ self->parent->connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher *)),
+ SLOT(gotInterfaces(QDBusPendingCallWatcher *)));
}
void Connection::Private::introspectContacts()
@@ -319,7 +298,7 @@ void Connection::Private::introspectContacts()
debug() << "Getting available interfaces for GetContactAttributes";
QDBusPendingCall call =
properties->Get(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS,
- "ContactAttributeInterfaces");
+ "ContactAttributeInterfaces");
QDBusPendingCallWatcher *watcher =
new QDBusPendingCallWatcher(call, parent);
parent->connect(watcher,
@@ -327,34 +306,34 @@ void Connection::Private::introspectContacts()
SLOT(gotContactAttributeInterfaces(QDBusPendingCallWatcher *)));
}
-void Connection::Private::introspectSimplePresence()
+void Connection::Private::introspectSimplePresence(Connection::Private *self)
{
- if (!properties) {
- properties = parent->propertiesInterface();
- Q_ASSERT(properties != 0);
+ if (!self->properties) {
+ self->properties = self->parent->propertiesInterface();
+ Q_ASSERT(self->properties != 0);
}
debug() << "Getting available SimplePresence statuses";
QDBusPendingCall call =
- properties->Get(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
+ self->properties->Get(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE,
"Statuses");
QDBusPendingCallWatcher *watcher =
- new QDBusPendingCallWatcher(call, parent);
- parent->connect(watcher,
+ new QDBusPendingCallWatcher(call, self->parent);
+ self->parent->connect(watcher,
SIGNAL(finished(QDBusPendingCallWatcher *)),
SLOT(gotSimpleStatuses(QDBusPendingCallWatcher *)));
}
-void Connection::Private::introspectSelfContact()
+void Connection::Private::introspectSelfContact(Connection::Private *self)
{
debug() << "Building self contact";
// FIXME: these should be features when Connection is sanitized
- PendingContacts *contacts = contactManager->contactsForHandles(
- UIntList() << selfHandle,
+ PendingContacts *contacts = self->contactManager->contactsForHandles(
+ UIntList() << self->selfHandle,
QSet<Contact::Feature>() << Contact::FeatureAlias
<< Contact::FeatureAvatarToken
<< Contact::FeatureSimplePresence);
- parent->connect(contacts,
+ self->parent->connect(contacts,
SIGNAL(finished(Telepathy::Client::PendingOperation *)),
SLOT(gotSelfContact(Telepathy::Client::PendingOperation *)));
}
@@ -374,62 +353,9 @@ void Connection::Private::introspectSelfHandle()
SLOT(gotSelfHandle(QDBusPendingCallWatcher *)));
}
-void Connection::Private::changeReadiness(Readiness newReadiness)
-{
- debug() << "changing readiness from" << readiness <<
- "to" << newReadiness;
- Q_ASSERT(newReadiness != readiness);
-
- switch (readiness) {
- case ReadinessJustCreated:
- break;
- case ReadinessNotYetConnected:
- Q_ASSERT(newReadiness == ReadinessConnecting
- || newReadiness == ReadinessDead);
- break;
- case ReadinessConnecting:
- Q_ASSERT(newReadiness == ReadinessFull
- || newReadiness == ReadinessDead);
- break;
- case ReadinessFull:
- Q_ASSERT(newReadiness == ReadinessDead);
- break;
- case ReadinessDead:
- // clear up introspection queue, no need for that as the connection
- // is dead
- introspectQueue.clear();
- default:
- Q_ASSERT(false);
- }
-
- debug() << "Readiness changed from" << readiness << "to" << newReadiness;
- readiness = newReadiness;
-
- if (status != pendingStatus ||
- statusReason != pendingStatusReason) {
- status = pendingStatus;
- statusReason = pendingStatusReason;
- emit parent->statusChanged(status, statusReason);
- }
-}
-
-void Connection::Private::updatePendingOperations()
-{
- foreach (PendingReadyConnection *operation, pendingOperations) {
- if (ready &&
- ((operation->requestedFeatures() &
- (features | missingFeatures)) == operation->requestedFeatures())) {
- operation->setFinished();
- }
- if (operation->isFinished()) {
- pendingOperations.removeOne(operation);
- }
- }
-}
-
-Connection::PendingConnect::PendingConnect(Connection *parent, Connection::Features features)
+Connection::PendingConnect::PendingConnect(Connection *parent, const QSet<uint> &requestedFeatures)
: PendingOperation(parent),
- features(features)
+ requestedFeatures(requestedFeatures)
{
QDBusPendingCall call = parent->baseInterface()->Connect();
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, parent);
@@ -445,7 +371,7 @@ void Connection::PendingConnect::onConnectReply(QDBusPendingCallWatcher *watcher
setFinishedWithError(watcher->error());
}
else {
- connect(qobject_cast<Connection*>(parent())->becomeReady(features),
+ connect(qobject_cast<Connection*>(parent())->becomeReady(requestedFeatures),
SIGNAL(finished(Telepathy::Client::PendingOperation*)),
SLOT(onBecomeReadyReply(Telepathy::Client::PendingOperation*)));
}
@@ -506,10 +432,6 @@ Connection::Connection(const QString &serviceName,
OptionalInterfaceFactory<Connection>(this),
mPriv(new Private(this))
{
- if (isValid()) {
- mPriv->introspectQueue.enqueue(&Private::startIntrospection);
- QTimer::singleShot(0, this, SLOT(continueIntrospection()));
- }
}
/**
@@ -528,10 +450,6 @@ Connection::Connection(const QDBusConnection &bus,
OptionalInterfaceFactory<Connection>(this),
mPriv(new Private(this))
{
- if (isValid()) {
- mPriv->introspectQueue.enqueue(&Private::startIntrospection);
- QTimer::singleShot(0, this, SLOT(continueIntrospection()));
- }
}
/**
@@ -552,10 +470,6 @@ Connection::~Connection()
*/
uint Connection::status() const
{
- if (mPriv->readiness == Private::ReadinessJustCreated) {
- warning() << "Connection::status() used with readiness ReadinessJustCreated";
- }
-
return mPriv->status;
}
@@ -567,10 +481,6 @@ uint Connection::status() const
*/
uint Connection::statusReason() const
{
- if (mPriv->readiness == Private::ReadinessJustCreated) {
- warning() << "Connection::statusReason() used with readiness ReadinessJustCreated";
- }
-
return mPriv->statusReason;
}
@@ -586,16 +496,8 @@ uint Connection::statusReason() const
*/
QStringList Connection::interfaces() const
{
- // Different check than the others, because the optional interface getters
- // may be used internally with the knowledge about getting the interfaces
- // list, so we don't want this to cause warnings.
- if (mPriv->readiness != Private::ReadinessNotYetConnected &&
- mPriv->readiness != Private::ReadinessFull &&
- mPriv->interfaces.empty()) {
- warning() << "Connection::interfaces() used possibly before the list of interfaces has been received";
- }
- else if (mPriv->readiness == Private::ReadinessDead) {
- warning() << "Connection::interfaces() used with readiness ReadinessDead";
+ if (!isReady()) {
+ warning() << "Connection::interfaces() used while connection is not ready";
}
return mPriv->interfaces;
@@ -630,17 +532,10 @@ uint Connection::selfHandle() const
*/
SimpleStatusSpecMap Connection::allowedPresenceStatuses() const
{
- if (mPriv->missingFeatures & FeatureSimplePresence) {
+ if (!isReady(QSet<uint>() << FeatureSimplePresence)) {
warning() << "Trying to retrieve simple presence from connection, but "
- "simple presence is not supported";
- }
- else if (!(mPriv->features & FeatureSimplePresence)) {
- warning() << "Trying to retrieve simple presence from connection without "
- "calling Connection::becomeReady(FeatureSimplePresence)";
- }
- else if (mPriv->pendingFeatures & FeatureSimplePresence) {
- warning() << "Trying to retrieve simple presence from connection, but "
- "simple presence is still being retrieved";
+ "simple presence is not supported or was not requested. "
+ "Use becomeReady(FeatureSimplePresence)";
}
return mPriv->simplePresenceStatuses;
@@ -772,95 +667,83 @@ QSharedPointer<Contact> Connection::selfContact() const
* \return <code>optionalInterface<DBus::PropertiesInterface>(BypassInterfaceCheck)</code>
*/
+void Connection::onStatusReady(uint status)
+{
+ Q_ASSERT(status == mPriv->pendingStatus);
+
+ mPriv->status = status;
+ mPriv->statusReason = mPriv->pendingStatusReason;
+ emit statusChanged(mPriv->status, mPriv->statusReason);
+}
+
void Connection::onStatusChanged(uint status, uint reason)
{
- debug() << "StatusChanged from" << mPriv->status
+ debug() << "StatusChanged from" << mPriv->pendingStatus
<< "to" << status << "with reason" << reason;
- if (!mPriv->haveInitialStatus) {
- debug() << "Still haven't got the GetStatus reply, ignoring StatusChanged until we have (but saving reason)";
- mPriv->pendingStatusReason = reason;
- return;
- }
-
if (mPriv->pendingStatus == status) {
warning() << "New status was the same as the old status! Ignoring redundant StatusChanged";
return;
}
- if (status == ConnectionStatusConnected &&
- mPriv->pendingStatus != ConnectionStatusConnecting) {
- // CMs aren't meant to go straight from Disconnected to
- // Connected; recover by faking Connecting
- warning() << " Non-compliant CM - went straight to Connected! Faking a transition through Connecting";
- onStatusChanged(ConnectionStatusConnecting, reason);
- }
-
mPriv->pendingStatus = status;
mPriv->pendingStatusReason = reason;
switch (status) {
case ConnectionStatusConnected:
- debug() << " Performing introspection for the Connected status";
- mPriv->introspectQueue.enqueue(&Private::introspectMain);
- continueIntrospection();
+ debug() << "Performing introspection for the Connected status";
+ mPriv->readinessHelper->setCurrentStatus(status);
break;
case ConnectionStatusConnecting:
- if (mPriv->readiness < Private::ReadinessConnecting) {
- mPriv->changeReadiness(Private::ReadinessConnecting);
- }
- else {
- warning() << " Got unexpected status change to Connecting";
- }
+ mPriv->readinessHelper->setCurrentStatus(status);
break;
case ConnectionStatusDisconnected:
- if (mPriv->readiness != Private::ReadinessDead) {
- const char *errorName;
-
- // This is the best we can do right now: in an imminent
- // spec version we should define a different D-Bus error name
- // for each ConnectionStatusReason
-
- switch (reason) {
- case ConnectionStatusReasonNoneSpecified:
- case ConnectionStatusReasonRequested:
- errorName = TELEPATHY_ERROR_DISCONNECTED;
- break;
-
- case ConnectionStatusReasonNetworkError:
- case ConnectionStatusReasonAuthenticationFailed:
- case ConnectionStatusReasonEncryptionError:
- errorName = TELEPATHY_ERROR_NETWORK_ERROR;
- break;
-
- case ConnectionStatusReasonNameInUse:
- errorName = TELEPATHY_ERROR_NOT_YOURS;
- break;
-
- case ConnectionStatusReasonCertNotProvided:
- case ConnectionStatusReasonCertUntrusted:
- case ConnectionStatusReasonCertExpired:
- case ConnectionStatusReasonCertNotActivated:
- case ConnectionStatusReasonCertHostnameMismatch:
- case ConnectionStatusReasonCertFingerprintMismatch:
- case ConnectionStatusReasonCertSelfSigned:
- case ConnectionStatusReasonCertOtherError:
- errorName = TELEPATHY_ERROR_NETWORK_ERROR;
-
- default:
- errorName = TELEPATHY_ERROR_DISCONNECTED;
- }
-
- invalidate(QLatin1String(errorName),
- QString("ConnectionStatusReason = %1").arg(uint(reason)));
-
- mPriv->changeReadiness(Private::ReadinessDead);
- }
- else {
- warning() << " Got unexpected status change to Disconnected";
+ const char *errorName;
+
+ // This is the best we can do right now: in an imminent
+ // spec version we should define a different D-Bus error name
+ // for each ConnectionStatusReason
+
+ switch (reason) {
+ case ConnectionStatusReasonNoneSpecified:
+ case ConnectionStatusReasonRequested:
+ errorName = TELEPATHY_ERROR_DISCONNECTED;
+ break;
+
+ case ConnectionStatusReasonNetworkError:
+ case ConnectionStatusReasonAuthenticationFailed:
+ case ConnectionStatusReasonEncryptionError:
+ errorName = TELEPATHY_ERROR_NETWORK_ERROR;
+ break;
+
+ case ConnectionStatusReasonNameInUse:
+ errorName = TELEPATHY_ERROR_NOT_YOURS;
+ break;
+
+ case ConnectionStatusReasonCertNotProvided:
+ case ConnectionStatusReasonCertUntrusted:
+ case ConnectionStatusReasonCertExpired:
+ case ConnectionStatusReasonCertNotActivated:
+ case ConnectionStatusReasonCertHostnameMismatch:
+ case ConnectionStatusReasonCertFingerprintMismatch:
+ case ConnectionStatusReasonCertSelfSigned:
+ case ConnectionStatusReasonCertOtherError:
+ errorName = TELEPATHY_ERROR_NETWORK_ERROR;
+
+ default:
+ errorName = TELEPATHY_ERROR_DISCONNECTED;
}
+
+ // TODO should we signal statusChanged to Disconnected here or just
+ // invalidate?
+ // Also none of the pendingOperations will finish. The
+ // user should just consider them to fail as the connection
+ // is invalid
+ onStatusReady(StatusDisconnected);
+ invalidate(QLatin1String(errorName),
+ QString("ConnectionStatusReason = %1").arg(uint(reason)));
break;
default:
@@ -873,47 +756,28 @@ void Connection::gotStatus(QDBusPendingCallWatcher *watcher)
{
QDBusPendingReply<uint> reply = *watcher;
+ if (mPriv->pendingStatus != StatusUnknown) {
+ // we already got the status from StatusChanged, ignoring here
+ return;
+ }
+
if (reply.isError()) {
warning().nospace() << "GetStatus() failed with " <<
reply.error().name() << ":" << reply.error().message();
+
invalidate(QLatin1String(TELEPATHY_ERROR_DISCONNECTED),
QString("ConnectionStatusReason = %1").arg(uint(mPriv->pendingStatusReason)));
- mPriv->changeReadiness(Private::ReadinessDead);
+
+ // introspect core failed
+ mPriv->readinessHelper->setIntrospectCompleted(0, false);
return;
}
uint status = reply.value();
-
debug() << "Got connection status" << status;
- mPriv->pendingStatus = status;
- mPriv->haveInitialStatus = true;
-
- // Don't do any introspection yet if the connection is in the Connecting
- // state; the StatusChanged handler will take care of doing that, if the
- // connection ever gets to the Connected state.
- if (status == ConnectionStatusConnecting) {
- debug() << "Not introspecting yet because the connection is currently Connecting";
- mPriv->changeReadiness(Private::ReadinessConnecting);
- return;
- }
-
- if (status == ConnectionStatusDisconnected) {
- debug() << "Performing introspection for the Disconnected status";
- mPriv->initialIntrospection = true;
- }
- else {
- if (status != ConnectionStatusConnected) {
- warning() << "Not performing introspection for unknown status" << status;
- return;
- }
- else {
- debug() << "Performing introspection for the Connected status";
- }
- }
- mPriv->introspectQueue.enqueue(&Private::introspectMain);
-
- continueIntrospection();
+ mPriv->pendingStatus = status;
+ mPriv->readinessHelper->setCurrentStatus(status);
watcher->deleteLater();
}
@@ -923,31 +787,22 @@ void Connection::gotInterfaces(QDBusPendingCallWatcher *watcher)
QDBusPendingReply<QStringList> reply = *watcher;
if (!reply.isError()) {
- debug() << "Got reply to GetInterfaces():" << mPriv->interfaces;
mPriv->interfaces = reply.value();
-
- if (mPriv->pendingStatus == ConnectionStatusConnected) {
- mPriv->introspectQueue.enqueue(&Private::introspectSelfHandle);
- } else {
- debug() << "Connection basic functionality is ready";
- mPriv->ready = true;
- }
-
- // if FeatureSimplePresence was requested and the interface exists and
- // the introspect func is not already enqueued, enqueue it.
- if (mPriv->pendingFeatures & FeatureSimplePresence &&
- mPriv->interfaces.contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE) &&
- !mPriv->introspectQueue.contains(&Private::introspectSimplePresence)) {
- mPriv->introspectQueue.enqueue(&Private::introspectSimplePresence);
- }
+ debug() << "Got reply to GetInterfaces():" << mPriv->interfaces;
+ mPriv->readinessHelper->setInterfaces(mPriv->interfaces);
}
else {
warning().nospace() << "GetInterfaces() failed with " <<
reply.error().name() << ":" << reply.error().message() <<
" - assuming no new interfaces";
+ // do not fail if GetInterfaces fail
}
- continueIntrospection();
+ if (mPriv->pendingStatus == StatusConnected) {
+ mPriv->introspectSelfHandle();
+ } else {
+ mPriv->readinessHelper->setIntrospectCompleted(0, true);
+ }
watcher->deleteLater();
}
@@ -956,22 +811,19 @@ void Connection::gotContactAttributeInterfaces(QDBusPendingCallWatcher *watcher)
{
QDBusPendingReply<QDBusVariant> reply = *watcher;
- // This is junk, but need to do this to make ContactManager behave - ideally, the self contact
- // wouldn't be core, but CAI would, so isReady(<nothing but core>) should return true already
- // at this point, and ContactManager should be happy
- debug() << "Connection basic functionality is ready (Got CAI)";
- mPriv->ready = true;
-
if (!reply.isError()) {
mPriv->contactAttributeInterfaces = qdbus_cast<QStringList>(reply.value().variant());
debug() << "Got" << mPriv->contactAttributeInterfaces.size() << "contact attribute interfaces";
- mPriv->introspectQueue.enqueue(&Private::introspectSelfContact);
} else {
warning().nospace() << "Getting contact attribute interfaces failed with " <<
reply.error().name() << ":" << reply.error().message();
+
+ // TODO should we remove Contacts interface from interfaces?
+ warning() << "Connection supports Contacts interface but contactAttributeInterfaces "
+ "can't be retrieved";
}
- continueIntrospection();
+ mPriv->readinessHelper->setIntrospectCompleted(0, true);
watcher->deleteLater();
}
@@ -980,47 +832,47 @@ void Connection::gotSelfContact(PendingOperation *op)
{
PendingContacts *pending = qobject_cast<PendingContacts *>(op);
- debug() << "Connection basic functionality is ready (Got SelfContact)";
- mPriv->ready = true;
-
if (pending->isValid()) {
Q_ASSERT(pending->contacts().size() == 1);
QSharedPointer<Contact> contact = pending->contacts()[0];
+
if (mPriv->selfContact != contact) {
mPriv->selfContact = contact;
+
+ // first time
+ if (!mPriv->readinessHelper->actualFeatures().contains(FeatureSelfContact)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureSelfContact, true);
+ }
+
emit selfContactChanged();
}
} else {
warning().nospace() << "Getting self contact failed with " <<
pending->errorName() << ":" << pending->errorMessage();
- }
- continueIntrospection();
+ // check if the feature is already there, and for some reason introspectSelfContact
+ // failed when called the second time
+ if (!mPriv->readinessHelper->actualFeatures().contains(FeatureSelfContact)) {
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureSelfContact, false);
+ }
+ }
}
void Connection::gotSimpleStatuses(QDBusPendingCallWatcher *watcher)
{
QDBusPendingReply<QDBusVariant> reply = *watcher;
- mPriv->pendingFeatures &= ~FeatureSimplePresence;
-
if (!reply.isError()) {
- mPriv->features |= FeatureSimplePresence;
- debug() << "Adding FeatureSimplePresence to features";
-
mPriv->simplePresenceStatuses = qdbus_cast<SimpleStatusSpecMap>(reply.value().variant());
debug() << "Got" << mPriv->simplePresenceStatuses.size() << "simple presence statuses";
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureSimplePresence, true);
}
else {
- mPriv->missingFeatures |= FeatureSimplePresence;
- debug() << "Adding FeatureSimplePresence to missing features";
-
warning().nospace() << "Getting simple presence statuses failed with " <<
reply.error().name() << ":" << reply.error().message();
+ mPriv->readinessHelper->setIntrospectCompleted(FeatureSimplePresence, false);
}
- continueIntrospection();
-
watcher->deleteLater();
}
@@ -1031,20 +883,18 @@ void Connection::gotSelfHandle(QDBusPendingCallWatcher *watcher)
if (!reply.isError()) {
mPriv->selfHandle = reply.value();
debug() << "Got self handle" << mPriv->selfHandle;
+
+ if (mPriv->interfaces.contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS)) {
+ mPriv->introspectContacts();
+ } else {
+ mPriv->readinessHelper->setIntrospectCompleted(0, true);
+ }
} else {
warning().nospace() << "Getting self handle failed with " <<
reply.error().name() << ":" << reply.error().message();
+ mPriv->readinessHelper->setIntrospectCompleted(0, false);
}
- if (mPriv->interfaces.contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS)) {
- mPriv->introspectQueue.enqueue(&Private::introspectContacts);
- } else {
- debug() << "Connection basic functionality is ready (Don't have Contacts)";
- mPriv->ready = true;
- }
-
- continueIntrospection();
-
watcher->deleteLater();
}
@@ -1090,7 +940,7 @@ ConnectionInterface *Connection::baseInterface() const
*/
PendingChannel *Connection::createChannel(const QVariantMap &request)
{
- if (mPriv->readiness != Private::ReadinessFull) {
+ if (mPriv->status != StatusConnected) {
warning() << "Calling createChannel with connection not yet connected";
return new PendingChannel(this, TELEPATHY_ERROR_NOT_AVAILABLE,
"Connection not yet connected");
@@ -1141,7 +991,7 @@ PendingChannel *Connection::createChannel(const QVariantMap &request)
*/
PendingChannel *Connection::ensureChannel(const QVariantMap &request)
{
- if (mPriv->readiness != Private::ReadinessFull) {
+ if (mPriv->status != StatusConnected) {
warning() << "Calling ensureChannel with connection not yet connected";
return new PendingChannel(this, TELEPATHY_ERROR_NOT_AVAILABLE,
"Connection not yet connected");
@@ -1294,10 +1144,9 @@ PendingHandles *Connection::referenceHandles(uint handleType, const UIntList &ha
* \param features Which features should be tested.
* \return \c true if the object has finished initial setup.
*/
-bool Connection::isReady(Features features) const
+bool Connection::isReady(const QSet<uint> &features) const
{
- return mPriv->ready
- && ((mPriv->features & features) == features);
+ return mPriv->readinessHelper->isReady(features);
}
/**
@@ -1305,78 +1154,28 @@ bool Connection::isReady(Features features) const
* its initial setup, or will fail if a fatal error occurs during this
* initial setup.
*
- * \param features Which features should be tested.
- * \return A PendingReadyConnection object which will emit finished
+ * \param requestedFeatures Which features should be tested.
+ * \return A PendingReady object which will emit finished
* when this object has finished or failed its initial setup.
*/
-PendingReadyConnection *Connection::becomeReady(Features requestedFeatures)
+PendingReady *Connection::becomeReady(const QSet<uint> &requestedFeatures)
{
- if (!isValid()) {
- PendingReadyConnection *operation =
- new PendingReadyConnection(requestedFeatures, this);
- operation->setFinishedWithError(TELEPATHY_ERROR_NOT_AVAILABLE,
- "Connection is invalid");
- return operation;
- }
-
- if (isReady(requestedFeatures)) {
- PendingReadyConnection *operation =
- new PendingReadyConnection(requestedFeatures, this);
- operation->setFinished();
- return operation;
- }
-
- debug() << "Calling becomeReady with requested features:"
- << requestedFeatures;
- foreach (PendingReadyConnection *operation, mPriv->pendingOperations) {
- if (operation->requestedFeatures() == requestedFeatures) {
- debug() << "Returning cached pending operation";
- return operation;
- }
- }
-
- if (requestedFeatures & FeatureSimplePresence) {
- // as the feature is optional, if it's know to not be supported,
- // just finish silently
- if (requestedFeatures == FeatureSimplePresence &&
- mPriv->missingFeatures & FeatureSimplePresence) {
- PendingReadyConnection *operation =
- new PendingReadyConnection(requestedFeatures, this);
- operation->setFinished();
- return operation;
- }
-
- // if we already have the interface simple presence enqueue the call to
- // introspect simple presence, otherwise it will be enqueued when/if the
- // interface is available
- if (!(mPriv->features & FeatureSimplePresence) &&
- !(mPriv->pendingFeatures & FeatureSimplePresence) &&
- !(mPriv->missingFeatures & FeatureSimplePresence) &&
- mPriv->interfaces.contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE)) {
- mPriv->introspectQueue.enqueue(&Private::introspectSimplePresence);
-
- // FIXME: Is not particularly good... this might introspect something completely
- // unrelated (the head of the queue) at the wrong time
- QTimer::singleShot(0, this, SLOT(continueIntrospection()));
- } else {
- if (mPriv->readiness == Private::ReadinessFull) {
- // we don't support simple presence but we are online, so
- // we should have all interfaces now, so if simple presence is not
- // present, add it to missing features.
- mPriv->missingFeatures |= FeatureSimplePresence;
- }
- }
- }
+ return mPriv->readinessHelper->becomeReady(requestedFeatures);
+}
- mPriv->pendingFeatures |= requestedFeatures;
+QSet<uint> Connection::requestedFeatures() const
+{
+ return mPriv->readinessHelper->requestedFeatures();
+}
- debug() << "Creating new pending operation";
- PendingReadyConnection *operation =
- new PendingReadyConnection(requestedFeatures, this);
- mPriv->pendingOperations.append(operation);
+QSet<uint> Connection::actualFeatures() const
+{
+ return mPriv->readinessHelper->actualFeatures();
+}
- mPriv->updatePendingOperations();
- return operation;
+QSet<uint> Connection::missingFeatures() const
+{
+ return mPriv->readinessHelper->missingFeatures();
}
/**
@@ -1386,12 +1185,13 @@ PendingReadyConnection *Connection::becomeReady(Features requestedFeatures)
* has reached StatusConnected and the requested \a features are all ready, or
* finish with an error if a fatal error occurs during that process.
*
+ * \param requestedFeatures Which features should be tested.
* \return A %PendingOperation, which will emit finished when the
* request finishes.
*/
-PendingOperation *Connection::requestConnect(Connection::Features features)
+PendingOperation *Connection::requestConnect(const QSet<uint> &requestedFeatures)
{
- return new PendingConnect(this, features);
+ return new PendingConnect(this, requestedFeatures);
}
/**
@@ -1577,57 +1377,13 @@ void Connection::handleRequestLanded(uint type)
}
}
-void Connection::continueIntrospection()
-{
- if (mPriv->introspectQueue.isEmpty()) {
- if (mPriv->initialIntrospection) {
- mPriv->initialIntrospection = false;
- if (mPriv->readiness < Private::ReadinessNotYetConnected) {
- mPriv->changeReadiness(Private::ReadinessNotYetConnected);
- }
- }
- else {
- if (mPriv->readiness != Private::ReadinessDead &&
- mPriv->readiness != Private::ReadinessFull) {
- mPriv->changeReadiness(Private::ReadinessFull);
-
- // we should have all interfaces now, so if an interface is not
- // present and we have a feature for it, add the feature to missing
- // features.
- if (!mPriv->interfaces.contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_SIMPLE_PRESENCE)) {
- debug() << "removing FeatureSimplePresence from pending features";
- mPriv->pendingFeatures &= ~FeatureSimplePresence;
- debug() << "adding FeatureSimplePresence to missing features";
- mPriv->missingFeatures |= FeatureSimplePresence;
- }
- else {
- // the user requested for FeatureSimplePresence so, now we are
- // able to get it
- if (mPriv->pendingFeatures == FeatureSimplePresence) {
- mPriv->introspectQueue.enqueue(&Private::introspectSimplePresence);
- }
- }
- }
- }
- }
- else {
- (mPriv->*(mPriv->introspectQueue.dequeue()))();
- }
-
- mPriv->updatePendingOperations();
-}
-
void Connection::onSelfHandleChanged(uint handle)
{
mPriv->selfHandle = handle;
emit selfHandleChanged(handle);
- // FIXME: not ideal - when SelfContact is a feature, should check
- // actualFeatures().contains(SelfContact) instead
- // Also, when we figure out how the Renaming interface should work and how that should map to
- // Contact objects, this might not need any special handling anymore.
- if (interfaces().contains(TELEPATHY_INTERFACE_CONNECTION_INTERFACE_CONTACTS)) {
- mPriv->introspectSelfContact();
+ if (mPriv->readinessHelper->actualFeatures().contains(FeatureSelfContact)) {
+ Private::introspectSelfContact(mPriv);
}
}
diff --git a/TelepathyQt4/Client/connection.h b/TelepathyQt4/Client/connection.h
index 153c47f..ec5feab 100644
--- a/TelepathyQt4/Client/connection.h
+++ b/TelepathyQt4/Client/connection.h
@@ -35,6 +35,7 @@
#include <TelepathyQt4/Constants>
#include <TelepathyQt4/Types>
+#include <QSet>
#include <QSharedPointer>
#include <QString>
#include <QStringList>
@@ -51,7 +52,7 @@ class PendingChannel;
class PendingContactAttributes;
class PendingHandles;
class PendingOperation;
-class PendingReadyConnection;
+class PendingReady;
class Connection : public StatefulDBusProxy,
private OptionalInterfaceFactory<Connection>
@@ -62,7 +63,8 @@ class Connection : public StatefulDBusProxy,
public:
enum Feature {
- FeatureSimplePresence = 1,
+ FeatureSelfContact = 1,
+ FeatureSimplePresence = 2,
_Padding = 0xFFFFFFFF
};
Q_DECLARE_FLAGS(Features, Feature)
@@ -156,7 +158,7 @@ public:
PendingChannel *ensureChannel(const QVariantMap &request);
- PendingOperation *requestConnect(Features features = 0);
+ PendingOperation *requestConnect(const QSet<uint> &requestedFeatures = QSet<uint>());
PendingOperation *requestDisconnect();
@@ -169,9 +171,13 @@ public:
QStringList contactAttributeInterfaces() const;
ContactManager *contactManager() const;
- bool isReady(Features features = 0) const;
+ bool isReady(const QSet<uint> &features = QSet<uint>()) const;
- PendingReadyConnection *becomeReady(Features features = 0);
+ PendingReady *becomeReady(const QSet<uint> &requestedFeatures = QSet<uint>());
+
+ QSet<uint> requestedFeatures() const;
+ QSet<uint> actualFeatures() const;
+ QSet<uint> missingFeatures() const;
Q_SIGNALS:
void statusChanged(uint newStatus, uint newStatusReason);
@@ -183,6 +189,7 @@ protected:
ConnectionInterface *baseInterface() const;
private Q_SLOTS:
+ void onStatusReady(uint);
void onStatusChanged(uint, uint);
void gotStatus(QDBusPendingCallWatcher *watcher);
void gotInterfaces(QDBusPendingCallWatcher *watcher);
@@ -193,8 +200,6 @@ private Q_SLOTS:
void doReleaseSweep(uint type);
- void continueIntrospection();
-
void onSelfHandleChanged(uint);
private:
--
1.5.6.5
More information about the telepathy-commits
mailing list