Music Hub  ..
A session-wide music playback service
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
player_skeleton.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2013-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  * Jim Hodapp <jim.hodapp@canonical.com>
18  */
19 
20 #include "apparmor.h"
21 #include "codec.h"
22 #include "player_skeleton.h"
23 #include "player_traits.h"
24 #include "property_stub.h"
25 #include "the_session_bus.h"
26 #include "xesam.h"
27 
28 #include "mpris/media_player2.h"
29 #include "mpris/metadata.h"
30 #include "mpris/player.h"
31 #include "mpris/playlists.h"
32 
33 #include <core/dbus/object.h>
34 #include <core/dbus/property.h>
35 #include <core/dbus/stub.h>
36 
37 #include <core/dbus/asio/executor.h>
38 #include <core/dbus/interfaces/properties.h>
39 
40 namespace dbus = core::dbus;
41 namespace media = core::ubuntu::media;
42 
43 struct media::PlayerSkeleton::Private : public std::enable_shared_from_this<media::PlayerSkeleton::Private>
44 {
46  const std::string& identity,
47  const std::shared_ptr<core::dbus::Bus>& bus,
48  const std::shared_ptr<core::dbus::Object>& session)
49  : impl(player),
50  identity(identity),
51  bus(bus),
52  object(session),
53  apparmor_session(nullptr),
54  dbus_stub{bus},
56  signals
57  {
61  }
62  {
63  }
64 
65  void handle_next(const core::dbus::Message::Ptr& msg)
66  {
67  impl->next();
68  auto reply = dbus::Message::make_method_return(msg);
69  bus->send(reply);
70  }
71 
72  void handle_previous(const core::dbus::Message::Ptr& msg)
73  {
74  impl->previous();
75  auto reply = dbus::Message::make_method_return(msg);
76  bus->send(reply);
77  }
78 
79  void handle_pause(const core::dbus::Message::Ptr& msg)
80  {
81  impl->pause();
82  auto reply = dbus::Message::make_method_return(msg);
83  bus->send(reply);
84  }
85 
86  void handle_stop(const core::dbus::Message::Ptr& msg)
87  {
88  impl->stop();
89  auto reply = dbus::Message::make_method_return(msg);
90  bus->send(reply);
91  }
92 
93  void handle_play(const core::dbus::Message::Ptr& msg)
94  {
95  impl->play();
96  auto reply = dbus::Message::make_method_return(msg);
97  bus->send(reply);
98  }
99 
100  void handle_play_pause(const core::dbus::Message::Ptr& msg)
101  {
102  switch(impl->playback_status().get())
103  {
104  case core::ubuntu::media::Player::PlaybackStatus::ready:
105  case core::ubuntu::media::Player::PlaybackStatus::paused:
106  case core::ubuntu::media::Player::PlaybackStatus::stopped:
107  impl->play();
108  break;
109  case core::ubuntu::media::Player::PlaybackStatus::playing:
110  impl->pause();
111  break;
112  default:
113  break;
114  }
115 
116  bus->send(dbus::Message::make_method_return(msg));
117  }
118 
119  void handle_seek(const core::dbus::Message::Ptr& in)
120  {
121  uint64_t ticks;
122  in->reader() >> ticks;
123  impl->seek_to(std::chrono::microseconds(ticks));
124 
125  auto reply = dbus::Message::make_method_return(in);
126  bus->send(reply);
127  }
128 
129  void handle_set_position(const core::dbus::Message::Ptr&)
130  {
131  }
132 
133  void handle_create_video_sink(const core::dbus::Message::Ptr& in)
134  {
135  uint32_t texture_id;
136  in->reader() >> texture_id;
137  impl->create_video_sink(texture_id);
138 
139  auto reply = dbus::Message::make_method_return(in);
140  bus->send(reply);
141  }
142 
143  bool does_client_have_access(const std::string& context, const std::string& uri)
144  {
145  if (context.empty() || uri.empty())
146  {
147  std::cout << "Client denied access since context or uri are empty" << std::endl;
148  return false;
149  }
150 
151  if (context == "unconfined")
152  {
153  std::cout << "Client allowed access since it's unconfined" << std::endl;
154  return true;
155  }
156 
157  size_t pos = context.find_first_of('_');
158  if (pos == std::string::npos)
159  {
160  std::cout << "Client denied access since it's an invalid apparmor security context" << std::endl;
161  return false;
162  }
163 
164  const std::string pkgname = context.substr(0, pos);
165  std::cout << "client pkgname: " << pkgname << std::endl;
166  std::cout << "uri: " << uri << std::endl;
167 
168  // All confined apps can access their own files
169  if (uri.find(std::string(".local/share/" + pkgname + "/")) != std::string::npos
170  || uri.find(std::string(".cache/" + pkgname + "/")) != std::string::npos)
171  {
172  std::cout << "Client can access content in ~/.local/share/" << pkgname << " or ~/.cache/" << pkgname << std::endl;
173  return true;
174  }
175  else if (uri.find(std::string("opt/click.ubuntu.com/")) != std::string::npos
176  && uri.find(pkgname) != std::string::npos)
177  {
178  std::cout << "Client can access content in own opt directory" << std::endl;
179  return true;
180  }
181  else if ((uri.find(std::string("/system/media/audio/ui/")) != std::string::npos
182  || uri.find(std::string("/android/system/media/audio/ui/")) != std::string::npos)
183  && pkgname == "com.ubuntu.camera")
184  {
185  std::cout << "Camera app can access ui sounds" << std::endl;
186  return true;
187  }
188  // TODO: Check if the trust store previously allowed direct access to uri
189 
190  // Check in ~/Music and ~/Videos
191  // TODO: when the trust store lands, check it to see if this app can access the dirs and
192  // then remove the explicit whitelist of the music-app, and gallery-app
193  else if ((pkgname == "com.ubuntu.music" || pkgname == "com.ubuntu.gallery") &&
194  (uri.find(std::string("Music/")) != std::string::npos
195  || uri.find(std::string("Videos/")) != std::string::npos
196  || uri.find(std::string("/media")) != std::string::npos))
197  {
198  std::cout << "Client can access content in ~/Music or ~/Videos" << std::endl;
199  return true;
200  }
201  else if (uri.find(std::string("/usr/share/sounds")) != std::string::npos)
202  {
203  std::cout << "Client can access content in /usr/share/sounds" << std::endl;
204  return true;
205  }
206  else if (uri.find(std::string("http://")) != std::string::npos
207  || uri.find(std::string("rtsp://")) != std::string::npos)
208  {
209  std::cout << "Client can access streaming content" << std::endl;
210  return true;
211  }
212  else
213  {
214  std::cout << "Client denied access to open_uri()" << std::endl;
215  return false;
216  }
217  }
218 
219  void handle_key(const core::dbus::Message::Ptr& in)
220  {
221  auto reply = dbus::Message::make_method_return(in);
222  reply->writer() << impl->key();
223  bus->send(reply);
224  }
225 
226  void handle_open_uri(const core::dbus::Message::Ptr& in)
227  {
228  dbus_stub.get_connection_app_armor_security_async(in->sender(), [this, in](const std::string& profile)
229  {
230  Track::UriType uri;
231  in->reader() >> uri;
232 
233  bool have_access = does_client_have_access(profile, uri);
234 
235  auto reply = dbus::Message::make_method_return(in);
236  reply->writer() << (have_access ? impl->open_uri(uri) : false);
237 
238  bus->send(reply);
239  });
240  }
241 
242  template<typename Property>
244  const typename Property::ValueType& value,
245  const dbus::Signal
246  <
247  core::dbus::interfaces::Properties::Signals::PropertiesChanged,
248  core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType
249  >::Ptr& signal)
250  {
251  typedef std::map<std::string, dbus::types::Variant> Dictionary;
252 
253  static const std::vector<std::string> the_empty_list_of_invalidated_properties;
254 
255  Dictionary dict; dict[Property::name()] = dbus::types::Variant::encode(value);
256 
257  signal->emit(std::make_tuple(
258  dbus::traits::Service<typename Property::Interface>::interface_name(),
259  dict,
260  the_empty_list_of_invalidated_properties));
261  }
262 
264  std::string identity;
265  dbus::Bus::Ptr bus;
266  dbus::Object::Ptr object;
267  dbus::Object::Ptr apparmor_session;
268 
270 
272 
273  struct Signals
274  {
275  typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;
276  typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
277  typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
278 
279  Signals(const std::shared_ptr<DBusSeekedToSignal>& remote_seeked,
280  const std::shared_ptr<DBusEndOfStreamSignal>& remote_eos,
281  const std::shared_ptr<DBusPlaybackStatusChangedSignal>& remote_playback_status_changed)
282  {
283  seeked_to.connect([remote_seeked](std::uint64_t value)
284  {
285  remote_seeked->emit(value);
286  });
287 
288  end_of_stream.connect([remote_eos]()
289  {
290  remote_eos->emit();
291  });
292 
293  playback_status_changed.connect([remote_playback_status_changed](const media::Player::PlaybackStatus& status)
294  {
295  remote_playback_status_changed->emit(status);
296  });
297  }
298 
299  core::Signal<int64_t> seeked_to;
300  core::Signal<void> end_of_stream;
301  core::Signal<media::Player::PlaybackStatus> playback_status_changed;
302  } signals;
303 
304 };
305 
306 media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config)
307  : d(new Private{this, config.identity, config.bus, config.session})
308 {
309  // Setup method handlers for mpris::Player methods.
310  auto next = std::bind(&Private::handle_next, d, std::placeholders::_1);
311  d->object->install_method_handler<mpris::Player::Next>(next);
312 
313  auto previous = std::bind(&Private::handle_previous, d, std::placeholders::_1);
314  d->object->install_method_handler<mpris::Player::Previous>(previous);
315 
316  auto pause = std::bind(&Private::handle_pause, d, std::placeholders::_1);
317  d->object->install_method_handler<mpris::Player::Pause>(pause);
318 
319  auto stop = std::bind(&Private::handle_stop, d, std::placeholders::_1);
320  d->object->install_method_handler<mpris::Player::Stop>(stop);
321 
322  auto play = std::bind(&Private::handle_play, d, std::placeholders::_1);
323  d->object->install_method_handler<mpris::Player::Play>(play);
324 
325  auto play_pause = std::bind(&Private::handle_play_pause, d, std::placeholders::_1);
326  d->object->install_method_handler<mpris::Player::PlayPause>(play_pause);
327 
328  auto seek = std::bind(&Private::handle_seek, d, std::placeholders::_1);
329  d->object->install_method_handler<mpris::Player::Seek>(seek);
330 
331  auto set_position = std::bind(&Private::handle_set_position, d, std::placeholders::_1);
332  d->object->install_method_handler<mpris::Player::SetPosition>(set_position);
333 
334  auto open_uri = std::bind(&Private::handle_open_uri, d, std::placeholders::_1);
335  d->object->install_method_handler<mpris::Player::OpenUri>(open_uri);
336 
337  // All the method handlers that exceed the mpris spec go here.
338  d->object->install_method_handler<mpris::Player::CreateVideoSink>(
340  d,
341  std::placeholders::_1));
342 
343  d->object->install_method_handler<mpris::Player::Key>(
344  std::bind(&Private::handle_key,
345  d,
346  std::placeholders::_1));
347 }
348 
350 {
351 }
352 
353 const core::Property<bool>& media::PlayerSkeleton::can_play() const
354 {
355  return *d->skeleton.properties.can_play;
356 }
357 
358 const core::Property<bool>& media::PlayerSkeleton::can_pause() const
359 {
360  return *d->skeleton.properties.can_pause;
361 }
362 
363 const core::Property<bool>& media::PlayerSkeleton::can_seek() const
364 {
365  return *d->skeleton.properties.can_seek;
366 }
367 
368 const core::Property<bool>& media::PlayerSkeleton::can_go_previous() const
369 {
370  return *d->skeleton.properties.can_go_previous;
371 }
372 
373 const core::Property<bool>& media::PlayerSkeleton::can_go_next() const
374 {
375  return *d->skeleton.properties.can_go_next;
376 }
377 
378 const core::Property<bool>& media::PlayerSkeleton::is_video_source() const
379 {
380  return *d->skeleton.properties.is_video_source;
381 }
382 
383 const core::Property<bool>& media::PlayerSkeleton::is_audio_source() const
384 {
385  return *d->skeleton.properties.is_audio_source;
386 }
387 
388 const core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status() const
389 {
390  return *d->skeleton.properties.typed_playback_status;
391 }
392 
393 const core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status() const
394 {
395  return *d->skeleton.properties.typed_loop_status;
396 }
397 
398 const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate() const
399 {
400  return *d->skeleton.properties.playback_rate;
401 }
402 
403 const core::Property<bool>& media::PlayerSkeleton::is_shuffle() const
404 {
405  return *d->skeleton.properties.is_shuffle;
406 }
407 
408 const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const
409 {
410  return *d->skeleton.properties.typed_meta_data_for_current_track;
411 }
412 
413 const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const
414 {
415  return *d->skeleton.properties.volume;
416 }
417 
418 const core::Property<int64_t>& media::PlayerSkeleton::position() const
419 {
420  return *d->skeleton.properties.position;
421 }
422 
423 const core::Property<int64_t>& media::PlayerSkeleton::duration() const
424 {
425  return *d->skeleton.properties.duration;
426 }
427 
428 const core::Property<media::Player::AudioStreamRole>& media::PlayerSkeleton::audio_stream_role() const
429 {
430  return *d->skeleton.properties.audio_stream_role;
431 }
432 
433 const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() const
434 {
435  return *d->skeleton.properties.minimum_playback_rate;
436 }
437 
438 const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate() const
439 {
440  return *d->skeleton.properties.maximum_playback_rate;
441 }
442 
443 core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status()
444 {
445  return *d->skeleton.properties.typed_loop_status;
446 }
447 
448 core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate()
449 {
450  return *d->skeleton.properties.playback_rate;
451 }
452 
453 core::Property<bool>& media::PlayerSkeleton::is_shuffle()
454 {
455  return *d->skeleton.properties.is_shuffle;
456 }
457 
458 core::Property<media::Player::Volume>& media::PlayerSkeleton::volume()
459 {
460  return *d->skeleton.properties.volume;
461 }
462 
463 core::Property<int64_t>& media::PlayerSkeleton::position()
464 {
465  return *d->skeleton.properties.position;
466 }
467 
468 core::Property<int64_t>& media::PlayerSkeleton::duration()
469 {
470  return *d->skeleton.properties.duration;
471 }
472 
473 core::Property<media::Player::AudioStreamRole>& media::PlayerSkeleton::audio_stream_role()
474 {
475  return *d->skeleton.properties.audio_stream_role;
476 }
477 
478 core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status()
479 {
480  return *d->skeleton.properties.typed_playback_status;
481 }
482 
483 core::Property<bool>& media::PlayerSkeleton::can_play()
484 {
485  return *d->skeleton.properties.can_play;
486 }
487 
488 core::Property<bool>& media::PlayerSkeleton::can_pause()
489 {
490  return *d->skeleton.properties.can_pause;
491 }
492 
493 core::Property<bool>& media::PlayerSkeleton::can_seek()
494 {
495  return *d->skeleton.properties.can_seek;
496 }
497 
498 core::Property<bool>& media::PlayerSkeleton::can_go_previous()
499 {
500  return *d->skeleton.properties.can_go_previous;
501 }
502 
503 core::Property<bool>& media::PlayerSkeleton::can_go_next()
504 {
505  return *d->skeleton.properties.can_go_next;
506 }
507 
508 core::Property<bool>& media::PlayerSkeleton::is_video_source()
509 {
510  return *d->skeleton.properties.is_video_source;
511 }
512 
513 core::Property<bool>& media::PlayerSkeleton::is_audio_source()
514 {
515  return *d->skeleton.properties.is_audio_source;
516 }
517 
518 
519 core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track()
520 {
521  return *d->skeleton.properties.typed_meta_data_for_current_track;
522 }
523 
524 core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate()
525 {
526  return *d->skeleton.properties.minimum_playback_rate;
527 }
528 
529 core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate()
530 {
531  return *d->skeleton.properties.maximum_playback_rate;
532 }
533 
534 const core::Signal<int64_t>& media::PlayerSkeleton::seeked_to() const
535 {
536  return d->signals.seeked_to;
537 }
538 
539 core::Signal<int64_t>& media::PlayerSkeleton::seeked_to()
540 {
541  return d->signals.seeked_to;
542 }
543 
544 const core::Signal<void>& media::PlayerSkeleton::end_of_stream() const
545 {
546  return d->signals.end_of_stream;
547 }
548 
549 core::Signal<void>& media::PlayerSkeleton::end_of_stream()
550 {
551  return d->signals.end_of_stream;
552 }
553 
554 core::Signal<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status_changed()
555 {
556  return d->signals.playback_status_changed;
557 }
void handle_previous(const core::dbus::Message::Ptr &msg)
mpris::Player::Skeleton skeleton
void handle_play(const core::dbus::Message::Ptr &msg)
virtual const core::Property< PlaybackStatus > & playback_status() const
virtual const core::Property< bool > & is_video_source() const
virtual const core::Signal< void > & end_of_stream() const
virtual const core::Property< int64_t > & duration() const
virtual const core::Property< bool > & can_seek() const
virtual const core::Property< PlaybackRate > & maximum_playback_rate() const
virtual const core::Property< bool > & is_shuffle() const
virtual const core::Property< bool > & can_go_next() const
virtual const core::Property< int64_t > & position() const
virtual const core::Property< bool > & can_go_previous() const
virtual void previous()=0
void handle_set_position(const core::dbus::Message::Ptr &)
bool does_client_have_access(const std::string &context, const std::string &uri)
Signals(const std::shared_ptr< DBusSeekedToSignal > &remote_seeked, const std::shared_ptr< DBusEndOfStreamSignal > &remote_eos, const std::shared_ptr< DBusPlaybackStatusChangedSignal > &remote_playback_status_changed)
virtual const core::Property< bool > & can_play() const
void handle_play_pause(const core::dbus::Message::Ptr &msg)
virtual const core::Property< Track::MetaData > & meta_data_for_current_track() const
virtual const core::Property< bool > & can_pause() const
virtual const core::Property< LoopStatus > & loop_status() const
void handle_stop(const core::dbus::Message::Ptr &msg)
Private(media::PlayerSkeleton *player, const std::string &identity, const std::shared_ptr< core::dbus::Bus > &bus, const std::shared_ptr< core::dbus::Object > &session)
virtual bool open_uri(const Track::UriType &uri)=0
virtual const core::Signal< int64_t > & seeked_to() const
virtual const core::Property< PlaybackRate > & playback_rate() const
PlayerSkeleton(const Configuration &configuration)
virtual void create_video_sink(uint32_t texture_id)=0
virtual const core::Property< AudioStreamRole > & audio_stream_role() const
core::dbus::Signal< mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType > DBusEndOfStreamSignal
media::PlayerSkeleton * impl
void handle_pause(const core::dbus::Message::Ptr &msg)
core::dbus::Signal< mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType > DBusPlaybackStatusChangedSignal
virtual const core::Property< PlaybackRate > & minimum_playback_rate() const
struct media::PlayerSkeleton::Private::Signals signals
void handle_seek(const core::dbus::Message::Ptr &in)
void get_connection_app_armor_security_async(const std::string &name, std::function< void(const std::string &)> handler)
Definition: apparmor.h:75
struct mpris::Player::Skeleton::@15 signals
org::freedesktop::dbus::DBus::Stub dbus_stub
void handle_open_uri(const core::dbus::Message::Ptr &in)
core::dbus::Signal< Signals::Seeked, Signals::Seeked::ArgumentType >::Ptr seeked_to
Definition: player.h:338
virtual const core::Property< Volume > & volume() const
std::string UriType
Definition: track.h:40
virtual PlayerKey key() const =0
void handle_next(const core::dbus::Message::Ptr &msg)
virtual core::Signal< PlaybackStatus > & playback_status_changed()
void on_property_value_changed(const typename Property::ValueType &value, const dbus::Signal< core::dbus::interfaces::Properties::Signals::PropertiesChanged, core::dbus::interfaces::Properties::Signals::PropertiesChanged::ArgumentType >::Ptr &signal)
virtual const core::Property< bool > & is_audio_source() const
core::dbus::Signal< mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType > DBusSeekedToSignal
core::dbus::Signal< Signals::EndOfStream, Signals::EndOfStream::ArgumentType >::Ptr end_of_stream
Definition: player.h:339
core::Signal< media::Player::PlaybackStatus > playback_status_changed
core::dbus::Signal< Signals::PlaybackStatusChanged, Signals::PlaybackStatusChanged::ArgumentType >::Ptr playback_status_changed
Definition: player.h:340
void handle_create_video_sink(const core::dbus::Message::Ptr &in)
void handle_key(const core::dbus::Message::Ptr &in)
virtual void seek_to(const std::chrono::microseconds &offset)=0