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 
21 #include <core/dbus/macros.h>
22 #include <core/dbus/object.h>
23 
24 #include <iostream>
25 
26 namespace media = core::ubuntu::media;
27 
28 namespace com { namespace canonical {
29 struct 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 };
49 namespace powerd {
50 struct Interface
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 
69 namespace
70 {
71 namespace impl
72 {
73 struct 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_service& io_service,
90  const core::dbus::Object::Ptr& object)
91  : parent{parent},
92  timeout{io_service},
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  std::cout << __PRETTY_FUNCTION__ << std::endl;
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  std::cerr << result.error().print() << std::endl;
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  std::cerr << result.error().print() << std::endl;
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 
192 struct 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  std::cout << __PRETTY_FUNCTION__ << std::endl;
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  std::cerr << "Failed to lock system_state_cookie_store_guard and check system lock state" << std::endl;
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  std::cerr << result.error().print() << std::endl;
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  std::cerr << "Failed to lock system_state_cookie_store_guard and update system lock state" << std::endl;
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  std::cerr << "Failed to lock system_state_cookie_store_guard and check system lock state" << std::endl;
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  std::cerr << result.error().print() << std::endl;
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  std::cerr << "Failed to lock system_state_cookie_store_guard and erase system lock state" << std::endl;
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 
335 struct 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_service, 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.
376 std::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.
390 std::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 }
StateController::Ptr make_platform_default_state_controller(core::ubuntu::media::helper::ExternalServices &)
static const std::string & name()
std::ostream & operator<<(std::ostream &out, Player::PlaybackStatus status)
Definition: player.h:198
static const core::dbus::types::ObjectPath & path()
static const core::dbus::types::ObjectPath & path()