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 
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  // The session object may outlive the private instance
352  // so uninstall all method handlers.
353  d->object->uninstall_method_handler<mpris::Player::Next>();
354  d->object->uninstall_method_handler<mpris::Player::Previous>();
355  d->object->uninstall_method_handler<mpris::Player::Pause>();
356  d->object->uninstall_method_handler<mpris::Player::Stop>();
357  d->object->uninstall_method_handler<mpris::Player::Play>();
358  d->object->uninstall_method_handler<mpris::Player::PlayPause>();
359  d->object->uninstall_method_handler<mpris::Player::Seek>();
360  d->object->uninstall_method_handler<mpris::Player::SetPosition>();
361  d->object->uninstall_method_handler<mpris::Player::OpenUri>();
362  d->object->uninstall_method_handler<mpris::Player::CreateVideoSink>();
363  d->object->uninstall_method_handler<mpris::Player::Key>();
364 }
365 
366 const core::Property<bool>& media::PlayerSkeleton::can_play() const
367 {
368  return *d->skeleton.properties.can_play;
369 }
370 
371 const core::Property<bool>& media::PlayerSkeleton::can_pause() const
372 {
373  return *d->skeleton.properties.can_pause;
374 }
375 
376 const core::Property<bool>& media::PlayerSkeleton::can_seek() const
377 {
378  return *d->skeleton.properties.can_seek;
379 }
380 
381 const core::Property<bool>& media::PlayerSkeleton::can_go_previous() const
382 {
383  return *d->skeleton.properties.can_go_previous;
384 }
385 
386 const core::Property<bool>& media::PlayerSkeleton::can_go_next() const
387 {
388  return *d->skeleton.properties.can_go_next;
389 }
390 
391 const core::Property<bool>& media::PlayerSkeleton::is_video_source() const
392 {
393  return *d->skeleton.properties.is_video_source;
394 }
395 
396 const core::Property<bool>& media::PlayerSkeleton::is_audio_source() const
397 {
398  return *d->skeleton.properties.is_audio_source;
399 }
400 
401 const core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status() const
402 {
403  return *d->skeleton.properties.typed_playback_status;
404 }
405 
406 const core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status() const
407 {
408  return *d->skeleton.properties.typed_loop_status;
409 }
410 
411 const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate() const
412 {
413  return *d->skeleton.properties.playback_rate;
414 }
415 
416 const core::Property<bool>& media::PlayerSkeleton::is_shuffle() const
417 {
418  return *d->skeleton.properties.is_shuffle;
419 }
420 
421 const core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track() const
422 {
423  return *d->skeleton.properties.typed_meta_data_for_current_track;
424 }
425 
426 const core::Property<media::Player::Volume>& media::PlayerSkeleton::volume() const
427 {
428  return *d->skeleton.properties.volume;
429 }
430 
431 const core::Property<int64_t>& media::PlayerSkeleton::position() const
432 {
433  return *d->skeleton.properties.position;
434 }
435 
436 const core::Property<int64_t>& media::PlayerSkeleton::duration() const
437 {
438  return *d->skeleton.properties.duration;
439 }
440 
441 const core::Property<media::Player::AudioStreamRole>& media::PlayerSkeleton::audio_stream_role() const
442 {
443  return *d->skeleton.properties.audio_stream_role;
444 }
445 
446 const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate() const
447 {
448  return *d->skeleton.properties.minimum_playback_rate;
449 }
450 
451 const core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate() const
452 {
453  return *d->skeleton.properties.maximum_playback_rate;
454 }
455 
456 core::Property<media::Player::LoopStatus>& media::PlayerSkeleton::loop_status()
457 {
458  return *d->skeleton.properties.typed_loop_status;
459 }
460 
461 core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::playback_rate()
462 {
463  return *d->skeleton.properties.playback_rate;
464 }
465 
466 core::Property<bool>& media::PlayerSkeleton::is_shuffle()
467 {
468  return *d->skeleton.properties.is_shuffle;
469 }
470 
471 core::Property<media::Player::Volume>& media::PlayerSkeleton::volume()
472 {
473  return *d->skeleton.properties.volume;
474 }
475 
476 core::Property<int64_t>& media::PlayerSkeleton::position()
477 {
478  return *d->skeleton.properties.position;
479 }
480 
481 core::Property<int64_t>& media::PlayerSkeleton::duration()
482 {
483  return *d->skeleton.properties.duration;
484 }
485 
486 core::Property<media::Player::AudioStreamRole>& media::PlayerSkeleton::audio_stream_role()
487 {
488  return *d->skeleton.properties.audio_stream_role;
489 }
490 
491 core::Property<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status()
492 {
493  return *d->skeleton.properties.typed_playback_status;
494 }
495 
496 core::Property<bool>& media::PlayerSkeleton::can_play()
497 {
498  return *d->skeleton.properties.can_play;
499 }
500 
501 core::Property<bool>& media::PlayerSkeleton::can_pause()
502 {
503  return *d->skeleton.properties.can_pause;
504 }
505 
506 core::Property<bool>& media::PlayerSkeleton::can_seek()
507 {
508  return *d->skeleton.properties.can_seek;
509 }
510 
511 core::Property<bool>& media::PlayerSkeleton::can_go_previous()
512 {
513  return *d->skeleton.properties.can_go_previous;
514 }
515 
516 core::Property<bool>& media::PlayerSkeleton::can_go_next()
517 {
518  return *d->skeleton.properties.can_go_next;
519 }
520 
521 core::Property<bool>& media::PlayerSkeleton::is_video_source()
522 {
523  return *d->skeleton.properties.is_video_source;
524 }
525 
526 core::Property<bool>& media::PlayerSkeleton::is_audio_source()
527 {
528  return *d->skeleton.properties.is_audio_source;
529 }
530 
531 
532 core::Property<media::Track::MetaData>& media::PlayerSkeleton::meta_data_for_current_track()
533 {
534  return *d->skeleton.properties.typed_meta_data_for_current_track;
535 }
536 
537 core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::minimum_playback_rate()
538 {
539  return *d->skeleton.properties.minimum_playback_rate;
540 }
541 
542 core::Property<media::Player::PlaybackRate>& media::PlayerSkeleton::maximum_playback_rate()
543 {
544  return *d->skeleton.properties.maximum_playback_rate;
545 }
546 
547 const core::Signal<int64_t>& media::PlayerSkeleton::seeked_to() const
548 {
549  return d->signals.seeked_to;
550 }
551 
552 core::Signal<int64_t>& media::PlayerSkeleton::seeked_to()
553 {
554  return d->signals.seeked_to;
555 }
556 
557 const core::Signal<void>& media::PlayerSkeleton::end_of_stream() const
558 {
559  return d->signals.end_of_stream;
560 }
561 
562 core::Signal<void>& media::PlayerSkeleton::end_of_stream()
563 {
564  return d->signals.end_of_stream;
565 }
566 
567 core::Signal<media::Player::PlaybackStatus>& media::PlayerSkeleton::playback_status_changed()
568 {
569  return d->signals.playback_status_changed;
570 }
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