~cemil-azizoglu/miral/mir-0.26.1-compat

« back to all changes in this revision

Viewing changes to miral-qt/src/modules/Unity/Application/sharedwakelock.cpp

  • Committer: Larry Price
  • Date: 2016-09-13 16:19:29 UTC
  • mto: (330.4.1 miral)
  • mto: This revision was merged to the branch mainline in revision 352.
  • Revision ID: larry.price@canonical.com-20160913161929-vs9ka1capmljq1es
Removing miral-qt from release branch, updating copyright file, and adding GPL3 license to root dir

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2015 Canonical, Ltd.
3
 
 *
4
 
 * This program is free software: you can redistribute it and/or modify it under
5
 
 * the terms of the GNU Lesser General Public License version 3, as published by
6
 
 * the Free Software Foundation.
7
 
 *
8
 
 * This program is distributed in the hope that it will be useful, but WITHOUT
9
 
 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10
 
 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
 
 * Lesser General Public License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU Lesser General Public License
14
 
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 
 */
16
 
 
17
 
#include "sharedwakelock.h"
18
 
#include "abstractdbusservicemonitor.h"
19
 
#include "logging.h"
20
 
 
21
 
#include <QDBusAbstractInterface>
22
 
#include <QDBusPendingCallWatcher>
23
 
#include <QDBusPendingReply>
24
 
#include <QFile>
25
 
 
26
 
namespace qtmir {
27
 
 
28
 
const int POWERD_SYS_STATE_ACTIVE = 1; // copied from private header file powerd.h
29
 
const char cookieFile[] = "/tmp/qtmir_powerd_cookie";
30
 
 
31
 
/**
32
 
 * @brief The Wakelock class - wraps a single system wakelock
33
 
 * Should the PowerD service vanish from the bus, the wakelock will be re-acquired when it re-joins the bus.
34
 
 */
35
 
class Wakelock : public AbstractDBusServiceMonitor
36
 
{
37
 
    Q_OBJECT
38
 
public:
39
 
    Wakelock(const QDBusConnection &connection) noexcept
40
 
        : AbstractDBusServiceMonitor(QStringLiteral("com.canonical.powerd"), QStringLiteral("/com/canonical/powerd"), QStringLiteral("com.canonical.powerd"), connection)
41
 
        , m_wakelockEnabled(false)
42
 
    {
43
 
        // (re-)acquire wake lock when powerd (re-)appears on the bus
44
 
        QObject::connect(this, &Wakelock::serviceAvailableChanged,
45
 
                         this, &Wakelock::onServiceAvailableChanged);
46
 
 
47
 
        // WORKAROUND: if shell crashed while it held a wakelock, due to bug lp:1409722 powerd will not have released
48
 
        // the wakelock for it. As workaround, we save the cookie to file and restore it if possible.
49
 
        QFile cookieCache(cookieFile);
50
 
        if (cookieCache.exists() && cookieCache.open(QFile::ReadOnly | QFile::Text)) {
51
 
            m_wakelockEnabled = true;
52
 
            m_cookie = cookieCache.readAll();
53
 
        }
54
 
    }
55
 
 
56
 
    virtual ~Wakelock() noexcept
57
 
    {
58
 
        release();
59
 
    }
60
 
 
61
 
    Q_SIGNAL void enabledChanged(bool);
62
 
    bool enabled() const
63
 
    {
64
 
        return m_wakelockEnabled;
65
 
    }
66
 
 
67
 
    void acquire()
68
 
    {
69
 
        if (m_wakelockEnabled) { // wakelock already requested/set
70
 
            return;
71
 
        }
72
 
        m_wakelockEnabled = true;
73
 
 
74
 
        acquireWakelock();
75
 
    }
76
 
 
77
 
    void release()
78
 
    {
79
 
        QFile::remove(cookieFile);
80
 
 
81
 
        if (!m_wakelockEnabled) { // no wakelock already requested/set
82
 
            return;
83
 
        }
84
 
        m_wakelockEnabled = false;
85
 
        Q_EMIT enabledChanged(false);
86
 
 
87
 
        if (!serviceAvailable()) {
88
 
            qWarning() << "com.canonical.powerd DBus interface not available, presuming no wakelocks held";
89
 
            return;
90
 
        }
91
 
 
92
 
        if (!m_cookie.isEmpty()) {
93
 
            dbusInterface()->asyncCall(QStringLiteral("clearSysState"), QString(m_cookie));
94
 
            qCDebug(QTMIR_SESSIONS) << "Wakelock released" << m_cookie;
95
 
            m_cookie.clear();
96
 
        }
97
 
    }
98
 
 
99
 
private Q_SLOTS:
100
 
    void onServiceAvailableChanged(bool available)
101
 
    {
102
 
        // Assumption is if service vanishes & reappears on the bus, it has lost its wakelock state and
103
 
        // we must re-acquire if necessary
104
 
        if (!m_wakelockEnabled) {
105
 
            return;
106
 
        }
107
 
 
108
 
        if (available) {
109
 
            acquireWakelock();
110
 
        } else {
111
 
            m_cookie.clear();
112
 
            QFile::remove(cookieFile);
113
 
        }
114
 
    }
115
 
 
116
 
    void onWakeLockAcquired(QDBusPendingCallWatcher *call)
117
 
    {
118
 
        QDBusPendingReply<QString> reply = *call;
119
 
        if (reply.isError()) {
120
 
            qCDebug(QTMIR_SESSIONS) << "Wakelock was NOT acquired, error:"
121
 
                                    << QDBusError::errorString(reply.error().type());
122
 
            if (m_wakelockEnabled) {
123
 
                m_wakelockEnabled = false;
124
 
                Q_EMIT enabledChanged(false);
125
 
            }
126
 
 
127
 
            call->deleteLater();
128
 
            return;
129
 
        }
130
 
        QByteArray cookie = reply.argumentAt<0>().toLatin1();
131
 
        call->deleteLater();
132
 
 
133
 
        if (!m_wakelockEnabled || !m_cookie.isEmpty()) {
134
 
            // notified wakelock was created, but we either don't want it, or already have one - release it immediately
135
 
            dbusInterface()->asyncCall(QStringLiteral("clearSysState"), QString(cookie));
136
 
            return;
137
 
        }
138
 
 
139
 
        m_cookie = cookie;
140
 
 
141
 
        // see WORKAROUND above for why we save cookie to disk
142
 
        QFile cookieCache(cookieFile);
143
 
        cookieCache.open(QFile::WriteOnly | QFile::Text);
144
 
        cookieCache.write(m_cookie);
145
 
 
146
 
        qCDebug(QTMIR_SESSIONS) << "Wakelock acquired" << m_cookie;
147
 
        Q_EMIT enabledChanged(true);
148
 
    }
149
 
 
150
 
private:
151
 
    void acquireWakelock()
152
 
    {
153
 
        if (!serviceAvailable()) {
154
 
            qWarning() << "com.canonical.powerd DBus interface not available, waiting for it";
155
 
            return;
156
 
        }
157
 
 
158
 
        QDBusPendingCall pcall = dbusInterface()->asyncCall(QStringLiteral("requestSysState"), "active", POWERD_SYS_STATE_ACTIVE);
159
 
 
160
 
        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
161
 
        QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
162
 
                         this, &Wakelock::onWakeLockAcquired);
163
 
    }
164
 
 
165
 
    QByteArray m_cookie;
166
 
    bool m_wakelockEnabled;
167
 
 
168
 
    Q_DISABLE_COPY(Wakelock)
169
 
};
170
 
 
171
 
#include "sharedwakelock.moc"
172
 
 
173
 
/**
174
 
 * @brief SharedWakelock - allow a single wakelock instance to be shared between multiple owners
175
 
 *
176
 
 * QtMir has application management duties to perform even if display is off. To prevent device
177
 
 * going to deep sleep before QtMir is ready, have QtMir register a system wakelock when it needs to.
178
 
 *
179
 
 * This class allows multiple objects to own the wakelock simultaneously. The wakelock is first
180
 
 * registered when acquire has been called by one caller. Multiple callers may then share the
181
 
 * wakelock. The wakelock is only destroyed when all callers have called release.
182
 
 *
183
 
 * Note a caller cannot have multiple shares of the wakelock. Multiple calls to acquire are ignored.
184
 
 */
185
 
 
186
 
SharedWakelock::SharedWakelock(const QDBusConnection &connection)
187
 
    : m_wakelock(new Wakelock(connection))
188
 
{
189
 
    connect(m_wakelock.data(), &Wakelock::enabledChanged,
190
 
            this, &SharedWakelock::enabledChanged);
191
 
}
192
 
 
193
 
// Define empty deconstructor here, as QScopedPointer<Wakelock> requires the destructor of the Wakelock class
194
 
// to be defined first.
195
 
SharedWakelock::~SharedWakelock()
196
 
{
197
 
}
198
 
 
199
 
bool SharedWakelock::enabled() const
200
 
{
201
 
    return m_wakelock->enabled();
202
 
}
203
 
 
204
 
void SharedWakelock::acquire(const QObject *caller)
205
 
{
206
 
    if (caller == nullptr || m_owners.contains(caller)) {
207
 
        return;
208
 
    }
209
 
 
210
 
    // register a slot to remove itself from owners list if destroyed
211
 
    QObject::connect(caller, &QObject::destroyed, this, &SharedWakelock::release);
212
 
 
213
 
    m_wakelock->acquire();
214
 
 
215
 
    m_owners.insert(caller);
216
 
}
217
 
 
218
 
void SharedWakelock::release(const QObject *caller)
219
 
{
220
 
    if (caller == nullptr || !m_owners.remove(caller)) {
221
 
        return;
222
 
    }
223
 
 
224
 
    QObject::disconnect(caller, &QObject::destroyed, this, 0);
225
 
 
226
 
    if (m_owners.empty()) {
227
 
        m_wakelock->release();
228
 
    }
229
 
}
230
 
 
231
 
} // namespace qtmir