Music Hub ..
A session-wide music playback service
state_controller.cpp
Go to the documentation of this file.
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU 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 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
20
22
23#include <core/dbus/macros.h>
24#include <core/dbus/object.h>
25
26namespace media = core::ubuntu::media;
27
28namespace com { namespace canonical {
29struct Unity
30{
31 struct Screen
32 {
33 static const std::string& name()
34 {
35 static std::string s = "com.canonical.Unity.Screen";
36 return s;
37 }
38
39 static const core::dbus::types::ObjectPath& path()
40 {
41 static core::dbus::types::ObjectPath p{"/com/canonical/Unity/Screen"};
42 return p;
43 }
44
45 DBUS_CPP_METHOD_DEF(keepDisplayOn, Screen)
46 DBUS_CPP_METHOD_DEF(removeDisplayOnRequest, Screen)
47 };
48};
49namespace powerd {
51{
52 static std::string& name()
53 {
54 static std::string s = "com.canonical.powerd";
55 return s;
56 }
57
58 static const core::dbus::types::ObjectPath& path()
59 {
60 static core::dbus::types::ObjectPath p{"/com/canonical/powerd"};
61 return p;
62 }
63
64 DBUS_CPP_METHOD_DEF(requestSysState, com::canonical::powerd::Interface)
65 DBUS_CPP_METHOD_DEF(clearSysState, com::canonical::powerd::Interface)
66};
67}}}
68
69namespace
70{
71namespace impl
72{
73struct DisplayStateLock : public media::power::StateController::Lock<media::power::DisplayState>,
74 public std::enable_shared_from_this<DisplayStateLock>
75{
76 // To safe us some typing
77 typedef std::shared_ptr<DisplayStateLock> Ptr;
78
79 // We postpone releasing the display for this amount of time.
80 static boost::posix_time::seconds timeout_for_release()
81 {
82 return boost::posix_time::seconds{4};
83 }
84
85 // The invalid cookie marker.
86 static constexpr const std::int32_t the_invalid_cookie{-1};
87
88 DisplayStateLock(const media::power::StateController::Ptr& parent,
89 boost::asio::io_context& io_context,
90 const core::dbus::Object::Ptr& object)
91 : parent{parent},
92 timeout{io_context},
93 object{object},
94 cookie{the_invalid_cookie}
95 {
96 }
97
98 // From core::ubuntu::media::power::StateController::Lock<DisplayState>
99 void request_acquire(media::power::DisplayState state) override
100 {
101 MH_TRACE("");
102
103 if (state == media::power::DisplayState::off)
104 return;
105
106 std::weak_ptr<DisplayStateLock> wp{shared_from_this()};
107
108 object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::keepDisplayOn, std::int32_t>(
109 [wp, state](const core::dbus::Result<std::int32_t>& result)
110 {
111 if (result.is_error())
112 {
113 MH_ERROR("%s", result.error().print());
114 return;
115 }
116
117 if (auto sp = wp.lock())
118 {
119 sp->cookie = result.value();
120 sp->signals.acquired(state);
121 }
122 });
123 }
124
125 void request_release(media::power::DisplayState state) override
126 {
127 if (state == media::power::DisplayState::off)
128 return;
129
130 if (cookie == the_invalid_cookie)
131 return;
132
133 // We make sure that we keep ourselves alive to make sure
134 // that release requests are always correctly issued.
135 auto sp = shared_from_this();
136
137 auto current_cookie(cookie);
138
139 timeout.expires_from_now(timeout_for_release());
140 timeout.async_wait([sp, state, current_cookie](const boost::system::error_code& ec)
141 {
142 // We only return early from the timeout handler if the operation has been
143 // explicitly aborted before.
144 if (ec == boost::asio::error::operation_aborted)
145 return;
146
147 sp->object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::removeDisplayOnRequest, void>(
148 [sp, state, current_cookie](const core::dbus::Result<void>& result)
149 {
150 if (result.is_error())
151 {
152 MH_ERROR("%s", result.error().print());
153 return;
154 }
155
156 sp->signals.released(state);
157
158 // We might have issued a different request before and
159 // only call the display state done if the original cookie
160 // corresponds to the one we just gave up.
161 if (sp->cookie == current_cookie)
162 sp->cookie = the_invalid_cookie;
163
164 }, current_cookie);
165 });
166 }
167
168 // Emitted whenever the acquire request completes.
169 const core::Signal<media::power::DisplayState>& acquired() const override
170 {
171 return signals.acquired;
172 }
173
174 // Emitted whenever the release request completes.
175 const core::Signal<media::power::DisplayState>& released() const override
176 {
177 return signals.released;
178 }
179
180 media::power::StateController::Ptr parent;
181 boost::asio::deadline_timer timeout;
182 core::dbus::Object::Ptr object;
183 std::int32_t cookie;
184
185 struct
186 {
187 core::Signal<media::power::DisplayState> acquired;
188 core::Signal<media::power::DisplayState> released;
189 } signals;
190};
191
192struct SystemStateLock : public media::power::StateController::Lock<media::power::SystemState>,
193 public std::enable_shared_from_this<SystemStateLock>
194{
195 static constexpr const char* wake_lock_name
196 {
197 "media-hub-playback_lock"
198 };
199
200 SystemStateLock(const media::power::StateController::Ptr& parent, const core::dbus::Object::Ptr& object)
201 : parent{parent},
202 object{object}
203 {
204 }
205
206 // Informs the system that the caller would like
207 // the system to stay active.
208 void request_acquire(media::power::SystemState state) override
209 {
210 MH_TRACE("");
211
212 if (state == media::power::SystemState::suspend)
213 return;
214
215 // Tighten the scope on the unique_lock
216 {
217 // Using a unique_lock instead of lock_guard to avoid deadlocks and instead, gracefully fail
218 std::unique_lock<std::mutex> ul{system_state_cookie_store_guard, std::try_to_lock};
219 if (ul.owns_lock())
220 {
221 if (system_state_cookie_store.count(state) > 0)
222 return;
223 }
224 else
225 {
226 MH_WARNING("Failed to lock system_state_cookie_store_guard and check system lock state");
227 // Prevent system_state_cookie_store.count(state) and the actual call to requestSysState below from
228 // getting out of sync.
229 return;
230 }
231 }
232
233 std::weak_ptr<SystemStateLock> wp{shared_from_this()};
234
235 object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::requestSysState, std::string>([wp, state, this](const core::dbus::Result<std::string>& result)
236 {
237 if (result.is_error())
238 {
239 MH_ERROR("%s", result.error().print());
240 return;
241 }
242
243 if (auto sp = wp.lock())
244 {
245 // Tighten the scope on the unique_lock
246 {
247 // Using a unique_lock instead of lock_guard to avoid deadlocks and instead, gracefully fail
248 std::unique_lock<std::mutex> ul{system_state_cookie_store_guard, std::try_to_lock};
249 if (ul.owns_lock())
250 sp->system_state_cookie_store[state] = result.value();
251 else
252 MH_WARNING("Failed to lock system_state_cookie_store_guard and update system lock state");
253 }
254
255 sp->signals.acquired(state);
256 }
257 }, std::string{wake_lock_name}, static_cast<std::int32_t>(state));
258 }
259
260 // Informs the system that the caller does not
261 // require the system to stay active anymore.
262 void request_release(media::power::SystemState state) override
263 {
264 if (state == media::power::SystemState::suspend)
265 return;
266
267 // Tighten the scope on the unique_lock
268 {
269 // Using a unique_lock instead of lock_guard to avoid deadlocks and instead, gracefully fail
270 std::unique_lock<std::mutex> ul{system_state_cookie_store_guard, std::try_to_lock};
271 if (ul.owns_lock())
272 {
273 if (system_state_cookie_store.count(state) == 0)
274 return;
275 }
276 else
277 {
278 MH_WARNING("Failed to lock system_state_cookie_store_guard and check system lock state");
279 // Prevent system_state_cookie_store.count(state) and the actual call to clearSysState below from
280 // getting out of sync.
281 return;
282 }
283 }
284
285 std::weak_ptr<SystemStateLock> wp{shared_from_this()};
286
287 object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::clearSysState, void>([this, wp, state](const core::dbus::Result<void>& result)
288 {
289 if (result.is_error())
290 MH_ERROR("%s", result.error().print());
291
292 if (auto sp = wp.lock())
293 {
294 // Tighten the scope on the unique_lock
295 {
296 std::unique_lock<std::mutex> ul{system_state_cookie_store_guard, std::try_to_lock};
297 if (ul.owns_lock())
298 sp->system_state_cookie_store.erase(state);
299 else
300 MH_WARNING("Failed to lock system_state_cookie_store_guard and erase system lock state");
301 }
302
303 sp->signals.released(state);
304 }
305 }, system_state_cookie_store.at(state));
306 }
307
308 // Emitted whenever the acquire request completes.
309 const core::Signal<media::power::SystemState>& acquired() const override
310 {
311 return signals.acquired;
312 }
313
314 // Emitted whenever the release request completes.
315 const core::Signal<media::power::SystemState>& released() const override
316 {
317 return signals.released;
318 }
319
320 // Guards concurrent accesses to the cookie store.
321 std::mutex system_state_cookie_store_guard;
322 // Maps previously requested system states to the cookies returned
323 // by the remote end. Used for keeping track of acquired states and
324 // associated cookies to be able to release previously granted acquisitions.
325 std::map<media::power::SystemState, std::string> system_state_cookie_store;
326 media::power::StateController::Ptr parent;
327 core::dbus::Object::Ptr object;
328 struct
329 {
330 core::Signal<media::power::SystemState> acquired;
331 core::Signal<media::power::SystemState> released;
332 } signals;
333};
334
335struct StateController : public media::power::StateController,
336 public std::enable_shared_from_this<impl::StateController>
337{
338 StateController(media::helper::ExternalServices& es)
339 : external_services{es},
340 powerd
341 {
342 core::dbus::Service::use_service<com::canonical::powerd::Interface>(external_services.system)
343 ->object_for_path(com::canonical::powerd::Interface::path())
344 },
345 unity_screen
346 {
347 core::dbus::Service::use_service<com::canonical::Unity::Screen>(external_services.system)
348 ->object_for_path(com::canonical::Unity::Screen::path())
349 }
350 {
351 }
352
353 media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock() override
354 {
355 return std::make_shared<impl::SystemStateLock>(shared_from_this(), powerd);
356 }
357
358 media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock() override
359 {
360 return std::make_shared<impl::DisplayStateLock>(shared_from_this(), external_services.io_context, unity_screen);
361 }
362
363 media::helper::ExternalServices& external_services;
364 core::dbus::Object::Ptr powerd;
365 core::dbus::Object::Ptr unity_screen;
366};
367}
368}
369
371{
372 return std::make_shared<::impl::StateController>(external_services);
373}
374
375// operator<< pretty prints the given display state to the given output stream.
376std::ostream& media::power::operator<<(std::ostream& out, media::power::DisplayState state)
377{
378 switch (state)
379 {
380 case media::power::DisplayState::off:
381 return out << "DisplayState::off";
382 case media::power::DisplayState::on:
383 return out << "DisplayState::on";
384 }
385
386 return out;
387}
388
389// operator<< pretty prints the given system state to the given output stream.
390std::ostream& media::power::operator<<(std::ostream& out, media::power::SystemState state)
391{
392 switch (state)
393 {
394 case media::power::SystemState::active:
395 return out << "SystemState::active";
396 case media::power::SystemState::blank_on_proximity:
397 return out << "SystemState::blank_on_proximity";
398 case media::power::SystemState::suspend:
399 return out << "SystemState::suspend";
400 }
401
402 return out;
403}
#define MH_TRACE(...)
Definition: logger.h:121
#define MH_ERROR(...)
Definition: logger.h:128
#define MH_WARNING(...)
Definition: logger.h:127
StateController::Ptr make_platform_default_state_controller(core::ubuntu::media::helper::ExternalServices &)
std::ostream & operator<<(std::ostream &out, Player::PlaybackStatus status)
Definition: player.h:217
static const std::string & name()
static const core::dbus::types::ObjectPath & path()
static const core::dbus::types::ObjectPath & path()