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  if (state == media::power::DisplayState::off)
102  return;
103 
104  std::weak_ptr<DisplayStateLock> wp{shared_from_this()};
105 
106  object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::keepDisplayOn, std::int32_t>(
107  [wp, state](const core::dbus::Result<std::int32_t>& result)
108  {
109  if (result.is_error())
110  {
111  std::cerr << result.error().print() << std::endl;
112  return;
113  }
114 
115  if (auto sp = wp.lock())
116  {
117  sp->cookie = result.value();
118  sp->signals.acquired(state);
119  }
120  });
121  }
122 
123  void request_release(media::power::DisplayState state) override
124  {
125  if (state == media::power::DisplayState::off)
126  return;
127 
128  if (cookie == the_invalid_cookie)
129  return;
130 
131  // We make sure that we keep ourselves alive to make sure
132  // that release requests are always correctly issued.
133  auto sp = shared_from_this();
134 
135  auto current_cookie(cookie);
136 
137  timeout.expires_from_now(timeout_for_release());
138  timeout.async_wait([sp, state, current_cookie](const boost::system::error_code& ec)
139  {
140  // We only return early from the timeout handler if the operation has been
141  // explicitly aborted before.
142  if (ec == boost::asio::error::operation_aborted)
143  return;
144 
145  sp->object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::removeDisplayOnRequest, void>(
146  [sp, state, current_cookie](const core::dbus::Result<void>& result)
147  {
148  if (result.is_error())
149  {
150  std::cerr << result.error().print() << std::endl;
151  return;
152  }
153 
154  sp->signals.released(state);
155 
156  // We might have issued a different request before and
157  // only call the display state done if the original cookie
158  // corresponds to the one we just gave up.
159  if (sp->cookie == current_cookie)
160  sp->cookie = the_invalid_cookie;
161 
162  }, current_cookie);
163  });
164  }
165 
166  // Emitted whenever the acquire request completes.
167  const core::Signal<media::power::DisplayState>& acquired() const override
168  {
169  return signals.acquired;
170  }
171 
172  // Emitted whenever the release request completes.
173  const core::Signal<media::power::DisplayState>& released() const override
174  {
175  return signals.released;
176  }
177 
178  media::power::StateController::Ptr parent;
179  boost::asio::deadline_timer timeout;
180  core::dbus::Object::Ptr object;
181  std::int32_t cookie;
182 
183  struct
184  {
185  core::Signal<media::power::DisplayState> acquired;
186  core::Signal<media::power::DisplayState> released;
187  } signals;
188 };
189 
190 struct SystemStateLock : public media::power::StateController::Lock<media::power::SystemState>,
191  public std::enable_shared_from_this<SystemStateLock>
192 {
193  static constexpr const char* wake_lock_name
194  {
195  "media-hub-playback_lock"
196  };
197 
198  SystemStateLock(const media::power::StateController::Ptr& parent, const core::dbus::Object::Ptr& object)
199  : parent{parent},
200  object{object}
201  {
202  }
203 
204  // Informs the system that the caller would like
205  // the system to stay active.
206  void request_acquire(media::power::SystemState state) override
207  {
208  if (state == media::power::SystemState::suspend)
209  return;
210 
211  // Keep scope of this lock tight as to avoid
212  // deadlocks on PlayerImplementation destruction
213  {
214  std::lock_guard<std::mutex> lg{system_state_cookie_store_guard};
215  if (system_state_cookie_store.count(state) > 0)
216  return;
217  }
218 
219  std::weak_ptr<SystemStateLock> wp{shared_from_this()};
220 
221  object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::requestSysState, std::string>([wp, state](const core::dbus::Result<std::string>& result)
222  {
223  if (result.is_error()) // TODO(tvoss): We should log the error condition here.
224  return;
225 
226  if (auto sp = wp.lock())
227  {
228  std::lock_guard<std::mutex> lg{sp->system_state_cookie_store_guard};
229 
230  sp->system_state_cookie_store[state] = result.value();
231  sp->signals.acquired(state);
232  }
233  }, std::string{wake_lock_name}, static_cast<std::int32_t>(state));
234  }
235 
236  // Informs the system that the caller does not
237  // require the system to stay active anymore.
238  void request_release(media::power::SystemState state) override
239  {
240  if (state == media::power::SystemState::suspend)
241  return;
242 
243  std::lock_guard<std::mutex> lg{system_state_cookie_store_guard};
244 
245  if (system_state_cookie_store.count(state) == 0)
246  return;
247 
248  std::weak_ptr<SystemStateLock> wp{shared_from_this()};
249 
250  object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::clearSysState, void>([wp, state](const core::dbus::Result<void>& result)
251  {
252  if (result.is_error())
253  std::cerr << result.error().print() << std::endl;
254 
255  if (auto sp = wp.lock())
256  {
257  std::lock_guard<std::mutex> lg{sp->system_state_cookie_store_guard};
258 
259  sp->system_state_cookie_store.erase(state);
260  sp->signals.released(state);
261  }
262  }, system_state_cookie_store.at(state));
263  }
264 
265  // Emitted whenever the acquire request completes.
266  const core::Signal<media::power::SystemState>& acquired() const override
267  {
268  return signals.acquired;
269  }
270 
271  // Emitted whenever the release request completes.
272  const core::Signal<media::power::SystemState>& released() const override
273  {
274  return signals.released;
275  }
276 
277  // Guards concurrent accesses to the cookie store.
278  std::mutex system_state_cookie_store_guard;
279  // Maps previously requested system states to the cookies returned
280  // by the remote end. Used for keeping track of acquired states and
281  // associated cookies to be able to release previously granted acquisitions.
282  std::map<media::power::SystemState, std::string> system_state_cookie_store;
283  media::power::StateController::Ptr parent;
284  core::dbus::Object::Ptr object;
285  struct
286  {
287  core::Signal<media::power::SystemState> acquired;
288  core::Signal<media::power::SystemState> released;
289  } signals;
290 };
291 
292 struct StateController : public media::power::StateController,
293  public std::enable_shared_from_this<impl::StateController>
294 {
295  StateController(media::helper::ExternalServices& es)
296  : external_services{es},
297  powerd
298  {
299  core::dbus::Service::use_service<com::canonical::powerd::Interface>(external_services.system)
300  ->object_for_path(com::canonical::powerd::Interface::path())
301  },
302  unity_screen
303  {
304  core::dbus::Service::use_service<com::canonical::Unity::Screen>(external_services.system)
305  ->object_for_path(com::canonical::Unity::Screen::path())
306  }
307  {
308  }
309 
310  media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock() override
311  {
312  return std::make_shared<impl::SystemStateLock>(shared_from_this(), powerd);
313  }
314 
315  media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock() override
316  {
317  return std::make_shared<impl::DisplayStateLock>(shared_from_this(), external_services.io_service, unity_screen);
318  }
319 
320  media::helper::ExternalServices& external_services;
321  core::dbus::Object::Ptr powerd;
322  core::dbus::Object::Ptr unity_screen;
323 };
324 }
325 }
326 
328 {
329  return std::make_shared<impl::StateController>(external_services);
330 }
331 
332 // operator<< pretty prints the given display state to the given output stream.
333 std::ostream& media::power::operator<<(std::ostream& out, media::power::DisplayState state)
334 {
335  switch (state)
336  {
337  case media::power::DisplayState::off:
338  return out << "DisplayState::off";
339  case media::power::DisplayState::on:
340  return out << "DisplayState::on";
341  }
342 
343  return out;
344 }
345 
346 // operator<< pretty prints the given system state to the given output stream.
347 std::ostream& media::power::operator<<(std::ostream& out, media::power::SystemState state)
348 {
349  switch (state)
350  {
351  case media::power::SystemState::active:
352  return out << "SystemState::active";
353  case media::power::SystemState::blank_on_proximity:
354  return out << "SystemState::blank_on_proximity";
355  case media::power::SystemState::suspend:
356  return out << "SystemState::suspend";
357  }
358 
359  return out;
360 }
StateController::Ptr make_platform_default_state_controller(core::ubuntu::media::helper::ExternalServices &)
static const std::string & name()
static const core::dbus::types::ObjectPath & path()
static const core::dbus::types::ObjectPath & path()
std::ostream & operator<<(std::ostream &out, IntWrapper< Tag, IntegerType > const &value)
Definition: dimensions.h:90