aboutsummaryrefslogtreecommitdiff
path: root/libcontextprovider/src/propertyadaptor.cpp
blob: f82b3055181dec9673514407d135b23f23b15cc6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*
 * Copyright (C) 2008, 2009 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "propertyadaptor.h"
#include "logging.h"
#include "sconnect.h"
#include "propertyprivate.h"
#include <QDBusConnection>

namespace ContextProvider {

/*!
    \class PropertyAdaptor
    \brief A DBus adaptor for implementing the org.maemo.contextkit.Property

    PropertyAdaptor represents the Property object on D-Bus. It also
    keeps track of its clients and sets the PropertyPrivate to
    subscribed or unsubscribed accordingly.

    PropertyAdaptor also listens to values sent by other providers on
    D-Bus and notifies the PropertyPrivate about them.
*/

/// Constructor. Creates new adaptor for the given manager with the given
/// dbus connection. The connection \a conn is not retained.
PropertyAdaptor::PropertyAdaptor(PropertyPrivate* propertyPrivate, QDBusConnection *conn)
    : QDBusAbstractAdaptor(propertyPrivate), propertyPrivate(propertyPrivate), connection(conn)
{
    // Start listening to the QDBusServiceWathcer, to know when our client has
    // exited.
    sconnect(&serviceWatcher, SIGNAL(serviceUnregistered(const QString&)),
             this, SLOT(onClientExited(const QString&)));
    serviceWatcher.setConnection(*conn);

    sconnect(propertyPrivate, SIGNAL(valueChanged(const QVariantList&, const quint64&)),
             this, SIGNAL(ValueChanged(const QVariantList&, const quint64&)));

    // Start listening to ValueChanged signals. We only listen to the
    // same bus we're on: that means if the same property is provided
    // both on session and on system bus, overhearing won't work.
    connection->connect("", objectPath(), DBUS_INTERFACE, "ValueChanged",
                        this, SLOT(onValueChanged(QVariantList, quint64)));
}

/// Implementation of the D-Bus method Subscribe
void PropertyAdaptor::Subscribe(const QDBusMessage &msg, QVariantList& values, quint64& timestamp)
{
    contextDebug() << "Subscribe called";

    // Store the information of the subscription. For each client, we
    // record how many times the client has subscribed.
    QString client = msg.service();

    if (clientServiceNames.contains(client) == false) {
        clientServiceNames.insert(client);
        if (clientServiceNames.size() == 1) {
            propertyPrivate->setSubscribed();
        }
        serviceWatcher.addWatchedService(client);
    }
    else {
        contextWarning() << "Client" << client << "subscribed to property" << propertyPrivate->key << "multiple times";
        QDBusMessage error = msg.createErrorReply("org.maemo.contextkit.Error.MultipleSubscribe",
                             "Subscribing to " + propertyPrivate->key + " multiple times");
        connection->send(error);
    }

    // Construct the return values
    Get(values, timestamp);
}

/// Implementation of the D-Bus method Unsubscribe
void PropertyAdaptor::Unsubscribe(const QDBusMessage &msg)
{
    contextDebug() << "Unsubscribe called";
    QString client = msg.service();

    if (clientServiceNames.remove(client)) {
        if (clientServiceNames.size() == 0) {
            propertyPrivate->setUnsubscribed();
        }
        serviceWatcher.removeWatchedService(client);
    }
    else {
        contextWarning() << "Client" << client << "unsubscribed from property" <<
            propertyPrivate->key << "without subscribing";
        QDBusMessage error = msg.createErrorReply("org.maemo.contextkit.Error.IllegalUnsubscribe",
                                                  "Unsubscribing from a non-subscribed property "
                                                  + propertyPrivate->key);
        connection->send(error);
    }
}

/// Implementation of the D-Bus method Get.
void PropertyAdaptor::Get(QVariantList& values, quint64& timestamp)
{
    // Construct the return values
    if (propertyPrivate->value.isNull() == false) {
        values << propertyPrivate->value;
    }
    timestamp = propertyPrivate->timestamp;
}

/// Called when a ValueChanged signal is overheard on D-Bus. Command
/// PropertyPrivate to update its overheard value.
void PropertyAdaptor::onValueChanged(QVariantList values, quint64 timestamp)
{
    propertyPrivate->updateOverheardValue(values, timestamp);
}

/// Called when the DBusServiceWatcher signals that one of our clients has
/// exited D-Bus.
void PropertyAdaptor::onClientExited(const QString& busName)
{
    if (clientServiceNames.remove(busName) && clientServiceNames.size() == 0) {
        propertyPrivate->setUnsubscribed();
    }
    // The client is expected to re-subscribe if it comes back. Then we will
    // start watching it again.
    serviceWatcher.removeWatchedService(busName);
}

/// Object path where the corresponding PropertyPrivate object should
/// be registered at. For a core propertiy Property.Name (not starting
/// with /), the path is /org/maemo/contextkit/Property/Name. For a
/// non-core property /com/my/property, the object path is
/// /com/my/property.
QString PropertyAdaptor::objectPath() const
{
    if (propertyPrivate->key.startsWith("/"))
        return QString(propertyPrivate->key);

    return QString("/org/maemo/contextkit/") +
            QString(propertyPrivate->key).replace(".", "/").replace(QRegExp("[^A-Za-z0-9_/]"), "_");

}

/// Called when the service is stopped and will disappear from
/// D-Bus. If it appears again, the clients will resubscribe, and it
/// shouldn't be a MultipleSubscribe error. Thus, we need to forget
/// the clients when the service is stopped.
void PropertyAdaptor::forgetClients()
{
    foreach(const QString& client, clientServiceNames)
        serviceWatcher.removeWatchedService(client);
    clientServiceNames.clear();
    propertyPrivate->setUnsubscribed();
}

} // namespace ContextProvider