34 #include <core/dbus/message.h> 35 #include <core/dbus/object.h> 36 #include <core/dbus/types/object_path.h> 38 #include <core/posix/this_process.h> 40 #include <boost/uuid/uuid.hpp> 41 #include <boost/uuid/uuid_generators.hpp> 42 #include <boost/uuid/uuid_io.hpp> 55 core::Signal<void> the_empty_signal;
63 object(impl->access_service()->add_object_for_path(
64 dbus::traits::Service<
media::Service>::object_path())),
65 configuration(config),
66 exported(impl->access_bus(), config.cover_art_resolver, impl, configuration)
68 object->install_method_handler<mpris::Service::CreateSession>(
70 &Private::handle_create_session,
72 std::placeholders::_1));
73 object->install_method_handler<mpris::Service::DetachSession>(
75 &Private::handle_detach_session,
77 std::placeholders::_1));
78 object->install_method_handler<mpris::Service::ReattachSession>(
80 &Private::handle_reattach_session,
82 std::placeholders::_1));
83 object->install_method_handler<mpris::Service::DestroySession>(
85 &Private::handle_destroy_session,
87 std::placeholders::_1));
88 object->install_method_handler<mpris::Service::CreateFixedSession>(
90 &Private::handle_create_fixed_session,
92 std::placeholders::_1));
93 object->install_method_handler<mpris::Service::ResumeSession>(
95 &Private::handle_resume_session,
97 std::placeholders::_1));
98 object->install_method_handler<mpris::Service::PauseOtherSessions>(
100 &Private::handle_pause_other_sessions,
102 std::placeholders::_1));
107 static unsigned int session_counter = 0;
109 const unsigned int current_session = session_counter++;
110 boost::uuids::uuid uuid = gen();
112 std::stringstream ss;
113 ss <<
"/core/ubuntu/media/Service/sessions/" << current_session;
120 auto session_info = create_session_info();
122 dbus::types::ObjectPath op{std::get<0>(session_info)};
124 std::string uuid{std::get<2>(session_info)};
126 media::Player::Configuration config
130 impl->access_service(),
131 impl->access_service()->add_object_for_path(op),
135 MH_DEBUG(
"Session created by request of: %s, key: %d, uuid: %d, path: %s",
136 msg->sender(), key, uuid, op);
140 const std::shared_ptr<media::Player> player {
impl->create_session(config)};
141 configuration.player_store->add_player_for_key(key, player);
142 uuid_player_map.emplace(std::make_pair(uuid, key));
144 request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
145 [
this, key, msg](
const media::apparmor::ubuntu::Context& context)
147 MH_DEBUG(
" -- app_name='%s', attached", context.str());
148 player_owner_map.emplace(std::make_pair(key, std::make_tuple(context.str(),
true, msg->sender())));
151 auto reply = dbus::Message::make_method_return(msg);
152 reply->writer() << std::make_tuple(op, uuid);
154 impl->access_bus()->send(reply);
155 }
catch(
const std::runtime_error& e)
157 auto reply = dbus::Message::make_error(
161 impl->access_bus()->send(reply);
170 msg->reader() >> uuid;
173 if (!uuid_player_map.empty())
175 const auto key = uuid_player_map.at(uuid);
177 if (player_owner_map.count(key) != 0) {
178 auto info = player_owner_map.at(key);
181 if (std::get<1>(info) && (std::get<2>(info) == msg->sender())) {
182 std::get<1>(info) =
false;
183 std::get<2>(info).clear();
185 auto player = configuration.player_store->player_for_key(key);
186 player->lifetime().set(media::Player::Lifetime::resumable);
191 auto reply = dbus::Message::make_method_return(msg);
192 impl->access_bus()->send(reply);
194 }
catch(
const std::runtime_error& e)
196 auto reply = dbus::Message::make_error(
200 impl->access_bus()->send(reply);
209 msg->reader() >> uuid;
211 if (uuid_player_map.count(uuid) != 0)
213 const auto key = uuid_player_map.at(uuid);
214 if (not configuration.player_store->has_player_for_key(key))
216 auto reply = dbus::Message::make_error(
219 "Unable to locate player session");
220 impl->access_bus()->send(reply);
223 std::stringstream ss;
224 ss <<
"/core/ubuntu/media/Service/sessions/" << key;
225 dbus::types::ObjectPath op{ss.str()};
227 request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
228 [
this, msg, key, op](
const media::apparmor::ubuntu::Context& context)
230 auto info = player_owner_map.at(key);
231 MH_DEBUG(
" -- reattach app_name='%s', info='%s', '%s'",
232 context.str(), std::get<0>(info), std::get<2>(info));
233 if (std::get<0>(info) == context.str()) {
234 std::get<1>(info) =
true;
235 std::get<2>(info) = msg->sender();
238 auto player = configuration.player_store->player_for_key(key);
241 auto reply = dbus::Message::make_method_return(msg);
242 reply->writer() << op;
244 impl->access_bus()->send(reply);
247 auto reply = dbus::Message::make_error(
250 "Invalid permissions for the requested session");
251 impl->access_bus()->send(reply);
257 auto reply = dbus::Message::make_error(
261 impl->access_bus()->send(reply);
264 }
catch(
const std::runtime_error& e)
266 auto reply = dbus::Message::make_error(
270 impl->access_bus()->send(reply);
279 msg->reader() >> uuid;
281 if (uuid_player_map.count(uuid) != 0) {
282 const auto key = uuid_player_map.at(uuid);
283 if (not configuration.player_store->has_player_for_key(key)) {
284 auto reply = dbus::Message::make_error(
287 "Unable to locate player session");
288 impl->access_bus()->send(reply);
294 uuid_player_map.erase(uuid);
296 request_context_resolver->resolve_context_for_dbus_name_async(msg->sender(),
297 [
this, msg, key](
const media::apparmor::ubuntu::Context& context)
299 auto info = player_owner_map.at(key);
300 MH_DEBUG(
" -- Destroying app_name='%s', info='%s', '%s'",
301 context.str(), std::get<0>(info), std::get<2>(info));
302 if (std::get<0>(info) == context.str()) {
303 player_owner_map.erase(key);
306 auto player = configuration.player_store->player_for_key(key);
309 player->lifetime().set(media::Player::Lifetime::normal);
312 auto reply = dbus::Message::make_method_return(msg);
313 impl->access_bus()->send(reply);
316 auto reply = dbus::Message::make_error(
319 "Invalid permissions for the requested session");
320 impl->access_bus()->send(reply);
326 auto reply = dbus::Message::make_error(
330 impl->access_bus()->send(reply);
333 }
catch(
const std::runtime_error& e)
335 auto reply = dbus::Message::make_error(
339 impl->access_bus()->send(reply);
348 msg->reader() >> name;
350 if (named_player_map.count(name) == 0) {
352 auto session_info = create_session_info();
354 dbus::types::ObjectPath op{std::get<0>(session_info)};
357 media::Player::Configuration config
361 impl->access_service(),
362 impl->access_service()->add_object_for_path(op),
366 auto session =
impl->create_session(config);
367 session->lifetime().set(media::Player::Lifetime::resumable);
369 configuration.player_store->add_player_for_key(key, session);
371 named_player_map.insert(std::make_pair(name, key));
373 auto reply = dbus::Message::make_method_return(msg);
374 reply->writer() << op;
376 impl->access_bus()->send(reply);
380 const auto key = named_player_map.at(name);
381 if (not configuration.player_store->has_player_for_key(key)) {
382 auto reply = dbus::Message::make_error(
385 "Unable to locate player session");
386 impl->access_bus()->send(reply);
390 std::stringstream ss;
391 ss <<
"/core/ubuntu/media/Service/sessions/" << key;
392 dbus::types::ObjectPath op{ss.str()};
394 auto reply = dbus::Message::make_method_return(msg);
395 reply->writer() << op;
397 impl->access_bus()->send(reply);
399 }
catch(
const std::runtime_error& e)
401 auto reply = dbus::Message::make_error(
405 impl->access_bus()->send(reply);
413 Player::PlayerKey key;
414 msg->reader() >> key;
416 if (not configuration.player_store->has_player_for_key(key)) {
417 auto reply = dbus::Message::make_error(
420 "Unable to locate player session");
421 impl->access_bus()->send(reply);
425 std::stringstream ss;
426 ss <<
"/core/ubuntu/media/Service/sessions/" << key;
427 dbus::types::ObjectPath op{ss.str()};
429 auto reply = dbus::Message::make_method_return(msg);
430 reply->writer() << op;
432 impl->access_bus()->send(reply);
433 }
catch(
const std::runtime_error& e)
435 auto reply = dbus::Message::make_error(
439 impl->access_bus()->send(reply);
445 Player::PlayerKey key;
446 msg->reader() >> key;
448 core::dbus::Message::Ptr reply;
449 if (not configuration.player_store->has_player_for_key(key))
452 reply = dbus::Message::make_error(
455 "Player key not found");
460 impl->set_current_player(key);
461 reply = dbus::Message::make_method_return(msg);
463 catch (
const std::out_of_range &e) {
464 MH_WARNING(
"Failed to look up Player instance for key %d\ 465 , no valid Player instance for that key value and cannot set current player.\ 466 This most likely means that media-hub-server has crashed and restarted.", key);
467 reply = dbus::Message::make_error(
470 "Player key not found");
474 impl->access_bus()->send(reply);
479 Player::PlayerKey key;
480 msg->reader() >> key;
481 core::dbus::Message::Ptr reply;
483 impl->pause_other_sessions(key);
484 reply = dbus::Message::make_method_return(msg);
486 catch (
const std::out_of_range &e) {
487 MH_WARNING(
"Failed to look up Player instance for key %d\ 488 , no valid Player instance for that key value and cannot set current player.\ 489 This most likely means that media-hub-server has crashed and restarted.", key);
490 reply = dbus::Message::make_error(
493 "Player key not found");
496 impl->access_bus()->send(reply);
511 std::map<media::Player::PlayerKey, std::tuple<std::string, bool, std::string>>
player_owner_map;
513 boost::uuids::random_generator
gen;
522 defaults.
identity =
"core::media::Hub";
540 service{dbus::Service::add_service(bus,
"org.mpris.MediaPlayer2.MediaHub")},
541 object{service->add_object_for_path(dbus::types::ObjectPath{
"/org/mpris/MediaPlayer2"})},
545 cover_art_resolver{cover_art_resolver},
547 service_skel_config(config)
549 object->install_method_handler<core::dbus::interfaces::Properties::GetAll>([
this](
const core::dbus::Message::Ptr& msg)
552 std::string interface;
553 msg->reader() >> interface;
554 core::dbus::Message::Ptr reply = core::dbus::Message::make_method_return(msg);
557 reply->writer() << player.get_all_properties();
559 reply->writer() << media_player.get_all_properties();
561 reply->writer() << playlists.get_all_properties();
563 Exported::bus->send(reply);
567 auto next = [
this](
const core::dbus::Message::Ptr& msg)
569 const auto sp = service_skel_config.player_store->current_player().get();
571 if (is_multimedia_role())
574 Exported::bus->send(core::dbus::Message::make_method_return(msg));
576 object->install_method_handler<mpris::Player::Next>(next);
578 auto previous = [
this](
const core::dbus::Message::Ptr& msg)
580 const auto sp = service_skel_config.player_store->current_player().get();
582 if (is_multimedia_role())
585 Exported::bus->send(core::dbus::Message::make_method_return(msg));
587 object->install_method_handler<mpris::Player::Previous>(previous);
589 auto pause = [
this](
const core::dbus::Message::Ptr& msg)
591 const auto sp = service_skel_config.player_store->current_player().get();
593 if (is_multimedia_role() and sp->can_pause())
596 Exported::bus->send(core::dbus::Message::make_method_return(msg));
598 object->install_method_handler<mpris::Player::Pause>(pause);
600 auto stop = [
this](
const core::dbus::Message::Ptr& msg)
602 const auto sp = service_skel_config.player_store->current_player().get();
604 if (is_multimedia_role())
607 Exported::bus->send(core::dbus::Message::make_method_return(msg));
609 object->install_method_handler<mpris::Player::Stop>(stop);
611 auto play = [
this, impl](
const core::dbus::Message::Ptr& msg)
613 const auto sp = service_skel_config.player_store->current_player().get();
615 if (is_multimedia_role() and sp->can_play())
625 Exported::bus->send(core::dbus::Message::make_method_return(msg));
627 object->install_method_handler<mpris::Player::Play>(play);
629 auto play_pause = [
this, impl](
const core::dbus::Message::Ptr& msg)
631 const auto sp = service_skel_config.player_store->current_player().get();
633 if (is_multimedia_role())
635 if (sp->playback_status() == media::Player::PlaybackStatus::playing
638 else if (sp->playback_status() != media::Player::PlaybackStatus::null
650 Exported::bus->send(core::dbus::Message::make_method_return(msg));
652 object->install_method_handler<mpris::Player::PlayPause>(play_pause);
659 const auto sp = service_skel_config.player_store->current_player().get();
660 return (sp ? sp->audio_stream_role() == media::Player::AudioStreamRole::multimedia :
false);
668 service_skel_config.player_store->set_current_player_for_key(key);
669 const auto player_sp = service_skel_config.player_store->current_player().get();
672 player.properties.can_control->set(
true);
675 connections.seeked_to = player_sp->seeked_to().connect([
this](std::uint64_t position)
677 player.signals.seeked_to->emit(position);
680 connections.duration_changed = player_sp->duration().changed().connect([
this](std::uint64_t duration)
682 player.properties.duration->set(duration);
685 connections.position_changed = player_sp->position().changed().connect([
this](std::uint64_t position)
687 player.properties.position->set(position);
690 connections.playback_status_changed = player_sp->playback_status().changed().connect(
693 const auto cp = service_skel_config.player_store->current_player().get();
695 if (cp and key == cp->key())
699 connections.loop_status_changed = player_sp->loop_status().changed().connect(
705 connections.can_play_changed = player_sp->can_play().changed().connect(
706 [
this, key, player_sp](
bool can_play)
708 const auto cp = service_skel_config.player_store->current_player().get();
710 if (cp and key == cp->key())
711 player.properties.can_play->set(can_play);
714 connections.can_pause_changed = player_sp->can_pause().changed().connect(
715 [
this, key, player_sp](
bool can_pause)
717 const auto cp = service_skel_config.player_store->current_player().get();
719 if (cp and key == cp->key())
720 player.properties.can_pause->set(can_pause);
723 connections.can_go_previous_changed = player_sp->can_go_previous().changed().connect(
724 [
this](
bool can_go_previous)
726 player.properties.can_go_previous->set(can_go_previous);
729 connections.can_go_next_changed = player_sp->can_go_next().changed().connect(
730 [
this](
bool can_go_next)
732 player.properties.can_go_next->set(can_go_next);
738 connections.meta_data_changed = player_sp->meta_data_for_current_track().changed().connect(
739 [
this](
const media::Track::MetaData& metadata)
741 player.properties.meta_data_for_current_track->set(metadata);
743 = dbus::types::Variant::encode(metadata);
744 player.signals.properties_changed->emit(std::make_tuple(
745 dbus::traits::Service<mpris::Player>::interface_name(),
756 player.properties.duration->set(player_sp->duration().get());
757 player.properties.position->set(player_sp->position().get());
759 player_sp->playback_status().get()));
761 player_sp->loop_status().get()));
762 player.properties.can_play->set(player_sp->can_play().get());
763 player.properties.can_pause->set(player_sp->can_pause().get());
764 player.properties.can_go_previous->set(player_sp->can_go_previous().get());
765 player.properties.can_go_next->set(player_sp->can_go_next().get());
772 player.properties.can_control->set(
false);
773 player.properties.can_play->set(
false);
774 player.properties.can_pause->set(
false);
775 player.properties.can_go_previous->set(
false);
776 player.properties.can_go_next->set(
false);
779 connections.seeked_to = the_empty_signal.connect([](){});
780 connections.duration_changed = the_empty_signal.connect([](){});
781 connections.position_changed = the_empty_signal.connect([](){});
782 connections.playback_status_changed = the_empty_signal.connect([](){});
783 connections.loop_status_changed = the_empty_signal.connect([](){});
784 connections.can_play_changed = the_empty_signal.connect([](){});
785 connections.can_pause_changed = the_empty_signal.connect([](){});
786 connections.can_go_previous_changed = the_empty_signal.connect([](){});
787 connections.can_go_next_changed = the_empty_signal.connect([](){});
788 connections.meta_data_changed = the_empty_signal.connect([](){});
794 if (not service_skel_config.player_store)
796 if (not service_skel_config.player_store->current_player().get())
799 return key == service_skel_config.player_store->current_player().get()->key();
819 core::Connection seeked_to
821 the_empty_signal.connect([](){})
823 core::Connection duration_changed
825 the_empty_signal.connect([](){})
827 core::Connection position_changed
829 the_empty_signal.connect([](){})
831 core::Connection playback_status_changed
833 the_empty_signal.connect([](){})
835 core::Connection loop_status_changed
837 the_empty_signal.connect([](){})
839 core::Connection can_play_changed
841 the_empty_signal.connect([](){})
843 core::Connection can_pause_changed
845 the_empty_signal.connect([](){})
847 core::Connection can_go_previous_changed
849 the_empty_signal.connect([](){})
851 core::Connection can_go_next_changed
853 the_empty_signal.connect([](){})
855 core::Connection meta_data_changed
857 the_empty_signal.connect([](){})
865 d(
new Private(
this, configuration))
875 return d->configuration.impl->create_session(config);
880 return d->configuration.impl->detach_session(uuid, config);
885 return d->configuration.impl->reattach_session(uuid, config);
890 return d->configuration.impl->destroy_session(uuid, config);
895 return d->configuration.impl->create_fixed_session(name, config);
900 return d->configuration.impl->resume_session(key);
905 const std::shared_ptr<media::Player> player =
906 d->configuration.player_store->player_for_key(key);
908 if (player->audio_stream_role() == media::Player::AudioStreamRole::multimedia)
909 d->exported.set_current_player(key);
914 return d->exported.is_current_player(key);
920 d->exported.reset_current_player();
926 d->configuration.impl->pause_other_sessions(key);
936 access_bus()->stop();
941 throw std::runtime_error(
"This signal is only accessible from the ServiceStub");
942 static const core::Signal<void> s;
948 throw std::runtime_error(
"This signal is only accessible from the ServiceStub");
949 static const core::Signal<void> s;
static const std::string & name()
static const std::string & name()
static const char * from(core::ubuntu::media::Player::PlaybackStatus status)
static const std::string & name()
static const std::vector< std::string > & the_empty_list_of_invalidated_properties()
static const std::string & name()
std::map< std::string, core::dbus::types::Variant > Dictionary
static const std::string & name()
static const char * from(core::ubuntu::media::Player::LoopStatus status)
static const std::string & name()
static const std::string & name()
static const std::string & name()
static const std::string & name()