Merge lp:~thomas-voss/media-hub/add-display-controller-interface into lp:media-hub

Proposed by Thomas Voß
Status: Work in progress
Proposed branch: lp:~thomas-voss/media-hub/add-display-controller-interface
Merge into: lp:media-hub
Prerequisite: lp:~thomas-voss/media-hub/refactor-video-sink-interface-to-not-rely-on-hybris-types
Diff against target: 2840 lines (+1594/-805)
24 files modified
include/core/media/service.h (+1/-1)
src/core/media/CMakeLists.txt (+9/-1)
src/core/media/audio/output_observer.cpp (+43/-0)
src/core/media/audio/output_observer.h (+74/-0)
src/core/media/audio/pulse_audio_output_observer.cpp (+241/-0)
src/core/media/audio/pulse_audio_output_observer.h (+60/-0)
src/core/media/dispatcher.h (+38/-0)
src/core/media/external_services.h (+83/-0)
src/core/media/indicator_power_service.h (+0/-74)
src/core/media/player_implementation.cpp (+25/-74)
src/core/media/player_implementation.h (+18/-6)
src/core/media/power/battery_observer.cpp (+127/-0)
src/core/media/power/battery_observer.h (+69/-0)
src/core/media/power/state_controller.cpp (+302/-0)
src/core/media/power/state_controller.h (+102/-0)
src/core/media/powerd_service.h (+0/-73)
src/core/media/server/server.cpp (+98/-2)
src/core/media/service_implementation.cpp (+111/-446)
src/core/media/service_implementation.h (+26/-3)
src/core/media/service_stub.h (+3/-1)
src/core/media/telephony/call_monitor.cpp (+34/-29)
src/core/media/telephony/call_monitor.h (+36/-18)
src/core/media/unity_screen_service.h (+0/-72)
tests/acceptance-tests/service.cpp (+94/-5)
To merge this branch: bzr merge lp:~thomas-voss/media-hub/add-display-controller-interface
Reviewer Review Type Date Requested Status
Jim Hodapp (community) code Needs Fixing
Review via email: mp+241570@code.launchpad.net

Commit message

Factor out functional dependencies into interfaces and implementations.

Description of the change

Factor out functional dependencies into interfaces and implementations.

To post a comment you must log in.
96. By Thomas Voß

Move around implementation.
Remove no longer needed interfaces and implementations.

97. By Thomas Voß

Put the CallMonitor interface into its own namespace.

98. By Thomas Voß

Remerge prerequisite branch.

99. By Thomas Voß

Make media::RecorderObserver an external dependency.

Revision history for this message
Jim Hodapp (jhodapp) wrote :

A lot of code but in general looking good. See my comments inline below. Also, I'd like to see unit tests for all of the new classes that you introduced such as BatteryObserver, StateController, etc.

review: Needs Fixing (code)
100. By Thomas Voß

Re-merge prerequisite branch.

101. By Thomas Voß

Adjust setup of service instance in acceptance tests.

102. By Thomas Voß

Make ClientDeathObserver instance a dependency passed down through ctors.

103. By Thomas Voß

* Factor out audio output observation into media::audio::OutputObserver.

[ Jim Hodapp ]
* Pause playback when a headphone is unplugged or an A2DP device is
  unpaired (LP: #1368300)
[ Ricardo Mendoza ]
* Pause playback when a headphone is unplugged or an A2DP device is
  unpaired (LP: #1368300)

104. By Thomas Voß

Merge prerequisite branch.

Unmerged revisions

104. By Thomas Voß

Merge prerequisite branch.

103. By Thomas Voß

* Factor out audio output observation into media::audio::OutputObserver.

[ Jim Hodapp ]
* Pause playback when a headphone is unplugged or an A2DP device is
  unpaired (LP: #1368300)
[ Ricardo Mendoza ]
* Pause playback when a headphone is unplugged or an A2DP device is
  unpaired (LP: #1368300)

102. By Thomas Voß

Make ClientDeathObserver instance a dependency passed down through ctors.

101. By Thomas Voß

Adjust setup of service instance in acceptance tests.

100. By Thomas Voß

Re-merge prerequisite branch.

99. By Thomas Voß

Make media::RecorderObserver an external dependency.

98. By Thomas Voß

Remerge prerequisite branch.

97. By Thomas Voß

Put the CallMonitor interface into its own namespace.

96. By Thomas Voß

Move around implementation.
Remove no longer needed interfaces and implementations.

95. By Thomas Voß

* Refactor client-facing interfaces to pull out explicit dependency on hybris-based media layer.
[ Ubuntu daily release ]
* New rebuild forced
[ Justin McPherson ]
* #1239432 Music fails to pause on incoming/outgoing calls (LP:
  #1239432)
[ thomas-voss ]
* Bump build dependency on dbus-cpp to pull in exception safe dtor.
  (LP: #1390618)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/core/media/service.h'
2--- include/core/media/service.h 2014-04-23 19:00:55 +0000
3+++ include/core/media/service.h 2014-11-25 11:08:14 +0000
4@@ -28,7 +28,7 @@
5 {
6 namespace media
7 {
8-class Service : public std::enable_shared_from_this<Service>
9+class Service
10 {
11 public:
12 struct Client
13
14=== modified file 'src/core/media/CMakeLists.txt'
15--- src/core/media/CMakeLists.txt 2014-11-25 11:08:14 +0000
16+++ src/core/media/CMakeLists.txt 2014-11-25 11:08:14 +0000
17@@ -11,7 +11,7 @@
18
19 message(STATUS ${MEDIA_HUB_HEADERS})
20
21-add_subdirectory(call-monitor)
22+add_subdirectory(telephony)
23
24 add_library(
25 media-hub-common SHARED
26@@ -90,8 +90,16 @@
27 hybris_client_death_observer.cpp
28 cover_art_resolver.cpp
29 engine.cpp
30+
31+ audio/pulse_audio_output_observer.cpp
32+ audio/output_observer.cpp
33+
34+ power/battery_observer.cpp
35+ power/state_controller.cpp
36+
37 recorder_observer.cpp
38 hybris_recorder_observer.cpp
39+
40 gstreamer/engine.cpp
41 gstreamer/playbin.cpp
42
43
44=== added directory 'src/core/media/audio'
45=== added file 'src/core/media/audio/output_observer.cpp'
46--- src/core/media/audio/output_observer.cpp 1970-01-01 00:00:00 +0000
47+++ src/core/media/audio/output_observer.cpp 2014-11-25 11:08:14 +0000
48@@ -0,0 +1,43 @@
49+/*
50+ * Copyright © 2014 Canonical Ltd.
51+ *
52+ * This program is free software: you can redistribute it and/or modify it
53+ * under the terms of the GNU Lesser General Public License version 3,
54+ * as published by the Free Software Foundation.
55+ *
56+ * This program is distributed in the hope that it will be useful,
57+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
58+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
59+ * GNU Lesser General Public License for more details.
60+ *
61+ * You should have received a copy of the GNU Lesser General Public License
62+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
63+ *
64+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
65+ */
66+
67+#include <core/media/audio/output_observer.h>
68+
69+#include <core/media/audio/pulse_audio_output_observer.h>
70+
71+#include <iostream>
72+
73+namespace audio = core::ubuntu::media::audio;
74+
75+std::ostream& audio::operator<<(std::ostream& out, audio::OutputState state)
76+{
77+ switch (state)
78+ {
79+ case audio::OutputState::connected:
80+ return out << "OutputState::connected";
81+ case audio::OutputState::disconnected:
82+ return out << "OutputState::disconnected";
83+ }
84+
85+ return out;
86+}
87+
88+audio::OutputObserver::Ptr audio::make_platform_default_output_observer()
89+{
90+ return std::make_shared<audio::PulseAudioOutputObserver>();
91+}
92
93=== added file 'src/core/media/audio/output_observer.h'
94--- src/core/media/audio/output_observer.h 1970-01-01 00:00:00 +0000
95+++ src/core/media/audio/output_observer.h 2014-11-25 11:08:14 +0000
96@@ -0,0 +1,74 @@
97+/*
98+ * Copyright © 2014 Canonical Ltd.
99+ *
100+ * This program is free software: you can redistribute it and/or modify it
101+ * under the terms of the GNU Lesser General Public License version 3,
102+ * as published by the Free Software Foundation.
103+ *
104+ * This program is distributed in the hope that it will be useful,
105+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
106+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
107+ * GNU Lesser General Public License for more details.
108+ *
109+ * You should have received a copy of the GNU Lesser General Public License
110+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
111+ *
112+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
113+ */
114+#ifndef CORE_UBUNTU_MEDIA_AUDIO_OUTPUT_OBSERVER_H_
115+#define CORE_UBUNTU_MEDIA_AUDIO_OUTPUT_OBSERVER_H_
116+
117+#include <core/property.h>
118+
119+#include <iosfwd>
120+#include <memory>
121+
122+namespace core
123+{
124+namespace ubuntu
125+{
126+namespace media
127+{
128+namespace audio
129+{
130+// All known states of an audio output.
131+enum class OutputState
132+{
133+ // The output is connected.
134+ connected,
135+ // The output is disconnected.
136+ disconnected,
137+};
138+
139+// Models observation of audio outputs of a device.
140+// Right now, we are only interested in monitoring the
141+// state of external outputs to react accordingly if
142+// wired or bluetooth outputs are connected/disconnected.
143+class OutputObserver
144+{
145+public:
146+ // Save us some typing.
147+ typedef std::shared_ptr<OutputObserver> Ptr;
148+
149+ virtual ~OutputObserver() = default;
150+
151+ // Getable/observable property holding the state of external outputs.
152+ virtual const core::Property<OutputState>& external_output_state() const = 0;
153+
154+protected:
155+ OutputObserver() = default;
156+ OutputObserver(const OutputObserver&) = delete;
157+ OutputObserver& operator=(const OutputObserver&) = delete;
158+};
159+
160+// Pretty prints the given state to the given output stream.
161+std::ostream& operator<<(std::ostream&, OutputState);
162+
163+// Creats a platform default instance for observing audio outputs.
164+OutputObserver::Ptr make_platform_default_output_observer();
165+}
166+}
167+}
168+}
169+
170+#endif // CORE_UBUNTU_MEDIA_AUDIO_OUTPUT_OBSERVER_H_
171
172=== added file 'src/core/media/audio/pulse_audio_output_observer.cpp'
173--- src/core/media/audio/pulse_audio_output_observer.cpp 1970-01-01 00:00:00 +0000
174+++ src/core/media/audio/pulse_audio_output_observer.cpp 2014-11-25 11:08:14 +0000
175@@ -0,0 +1,241 @@
176+/*
177+ * Copyright © 2014 Canonical Ltd.
178+ *
179+ * This program is free software: you can redistribute it and/or modify it
180+ * under the terms of the GNU Lesser General Public License version 3,
181+ * as published by the Free Software Foundation.
182+ *
183+ * This program is distributed in the hope that it will be useful,
184+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
185+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
186+ * GNU Lesser General Public License for more details.
187+ *
188+ * You should have received a copy of the GNU Lesser General Public License
189+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
190+ *
191+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
192+ * Ricardo Mendoza <ricardo.mendoza@canonical.com>
193+ */
194+
195+#include <core/media/audio/pulse_audio_output_observer.h>
196+
197+#include <pulse/pulseaudio.h>
198+
199+#include <cstdint>
200+
201+#include <string>
202+
203+namespace audio = core::ubuntu::media::audio;
204+
205+namespace
206+{
207+namespace pa
208+{
209+static constexpr const char* a2dp_output_prefix{"output-a2dp"};
210+static constexpr const char* wired_output_prefix{"output-wired"};
211+
212+// Wraps a pulseaudio sink
213+struct Sink
214+{
215+ static constexpr const std::int32_t the_invalid_index{-1};
216+ static constexpr const std::int32_t the_invalid_port{-1};
217+
218+ std::int32_t index{the_invalid_index};
219+ std::string name{};
220+};
221+
222+typedef std::shared_ptr<pa_threaded_mainloop> ThreadedMainLoopPtr;
223+ThreadedMainLoopPtr make_threaded_main_loop()
224+{
225+ return ThreadedMainLoopPtr
226+ {
227+ pa_threaded_mainloop_new(),
228+ [](pa_threaded_mainloop* ml)
229+ {
230+ pa_threaded_mainloop_stop(ml);
231+ pa_threaded_mainloop_free(ml);
232+ }
233+ };
234+}
235+
236+typedef std::shared_ptr<pa_context> ContextPtr;
237+ContextPtr make_context(ThreadedMainLoopPtr main_loop)
238+{
239+ return ContextPtr
240+ {
241+ pa_context_new(pa_threaded_mainloop_get_api(main_loop.get()), "MediaHubPulseContext"),
242+ pa_context_unref
243+ };
244+}
245+
246+void start_main_loop(ThreadedMainLoopPtr ml)
247+{
248+ pa_threaded_mainloop_start(ml.get());
249+}
250+
251+void get_index_of_primary_sink_async(ContextPtr ctxt, pa_sink_info_cb_t cb, void* cookie)
252+{
253+ pa_context_get_sink_info_by_name(ctxt.get(), "sink.primary", cb, cookie);
254+}
255+
256+void get_sink_info_by_index_async(ContextPtr ctxt, std::uint32_t index, pa_sink_info_cb_t cb, void* cookie)
257+{
258+ pa_context_get_sink_info_by_index(ctxt.get(), index, cb, cookie);
259+}
260+
261+void connect_async(ContextPtr ctxt)
262+{
263+ pa_context_connect(ctxt.get(), nullptr, static_cast<pa_context_flags_t>(PA_CONTEXT_NOAUTOSPAWN | PA_CONTEXT_NOFAIL), nullptr);
264+}
265+
266+bool is_port_available_on_sink(const pa_sink_info* info, const std::string& port)
267+{
268+ if (not info)
269+ return false;
270+
271+ for (std::uint32_t i = 0; i < info->n_ports; i++)
272+ {
273+ if (info->ports[i]->available == PA_PORT_AVAILABLE_NO)
274+ continue;
275+
276+ if (std::string{info->ports[i]->name}.find(port) == 0)
277+ return true;
278+ }
279+
280+ return false;
281+}
282+}
283+}
284+
285+struct audio::PulseAudioOutputObserver::Private
286+{
287+ static void context_notification_cb(pa_context* ctxt, void* cookie)
288+ {
289+ if (auto thiz = static_cast<Private*>(cookie))
290+ {
291+ // Better safe than sorry: Check if we got signaled for the
292+ // context we are actually interested in.
293+ if (thiz->context.get() != ctxt)
294+ return;
295+
296+ switch (pa_context_get_state(ctxt))
297+ {
298+ case PA_CONTEXT_READY:
299+ thiz->on_context_ready();
300+ break;
301+ case PA_CONTEXT_FAILED:
302+ thiz->on_context_failed();
303+ break;
304+ default:
305+ break;
306+ }
307+ }
308+ }
309+
310+ static void context_subscription_cb(pa_context* ctxt, pa_subscription_event_type_t ev, uint32_t idx, void* cookie)
311+ {
312+ (void) idx;
313+
314+ if (auto thiz = static_cast<Private*>(cookie))
315+ {
316+ // Better safe than sorry: Check if we got signaled for the
317+ // context we are actually interested in.
318+ if (thiz->context.get() != ctxt)
319+ return;
320+
321+ if ((ev & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)
322+ thiz->on_sink_event_with_index(idx);
323+ }
324+ }
325+
326+ static void query_for_primary_sink_finished(pa_context* ctxt, const pa_sink_info* si, int eol, void* cookie)
327+ {
328+ if (eol)
329+ return;
330+
331+ if (auto thiz = static_cast<Private*>(cookie))
332+ {
333+ // Better safe than sorry: Check if we got signaled for the
334+ // context we are actually interested in.
335+ if (thiz->context.get() != ctxt)
336+ return;
337+
338+ thiz->on_query_for_primary_sink_finished(si);
339+ }
340+ }
341+
342+ Private()
343+ : external_output_state{audio::OutputState::disconnected},
344+ main_loop{pa::make_threaded_main_loop()},
345+ context{pa::make_context(main_loop)}
346+ {
347+ pa_context_set_state_callback(context.get(), Private::context_notification_cb, this);
348+ pa_context_set_subscribe_callback(context.get(), Private::context_subscription_cb, this);
349+
350+ output.a2dp | external_output_state;
351+ output.wired | external_output_state;
352+
353+ pa::connect_async(context);
354+
355+ }
356+
357+ // The connection attempt has been successful and we are connected
358+ // to pulseaudio now.
359+ void on_context_ready()
360+ {
361+ pa::get_index_of_primary_sink_async(context, Private::query_for_primary_sink_finished, this);
362+ }
363+
364+ // Either a connection attempt failed, or an existing connection
365+ // was unexpectedly terminated.
366+ void on_context_failed()
367+ {
368+ pa::connect_async(context);
369+ }
370+
371+ // Something changed on the sink with index idx.
372+ void on_sink_event_with_index(std::int32_t index)
373+ {
374+ if (index != primary_sink_index)
375+ return;
376+
377+ pa::get_sink_info_by_index_async(context, index, Private::query_for_primary_sink_finished, this);
378+ }
379+
380+ // Query for primary sink finished.
381+ void on_query_for_primary_sink_finished(const pa_sink_info* info)
382+ {
383+ output.wired = pa::is_port_available_on_sink(info, pa::wired_output_prefix)
384+ ? media::audio::OutputState::connected
385+ : media::audio::OutputState::disconnected;
386+
387+ output.a2dp = pa::is_port_available_on_sink(info, pa::a2dp_output_prefix)
388+ ? media::audio::OutputState::connected
389+ : media::audio::OutputState::disconnected;
390+
391+ primary_sink_index = info->index;
392+ }
393+
394+ core::Property<audio::OutputState> external_output_state;
395+
396+ pa::ThreadedMainLoopPtr main_loop;
397+ pa::ContextPtr context;
398+ std::uint32_t primary_sink_index;
399+ struct
400+ {
401+ core::Property<media::audio::OutputState> wired;
402+ core::Property<media::audio::OutputState> a2dp;
403+ } output;
404+};
405+
406+// Constructs a new instance, or throws std::runtime_error
407+// if connection to pulseaudio fails.
408+audio::PulseAudioOutputObserver::PulseAudioOutputObserver() : d{new Private{}}
409+{
410+}
411+
412+// Getable/observable property holding the state of external outputs.
413+const core::Property<audio::OutputState>& audio::PulseAudioOutputObserver::external_output_state() const
414+{
415+ return d->external_output_state;
416+}
417
418=== added file 'src/core/media/audio/pulse_audio_output_observer.h'
419--- src/core/media/audio/pulse_audio_output_observer.h 1970-01-01 00:00:00 +0000
420+++ src/core/media/audio/pulse_audio_output_observer.h 2014-11-25 11:08:14 +0000
421@@ -0,0 +1,60 @@
422+/*
423+ * Copyright © 2014 Canonical Ltd.
424+ *
425+ * This program is free software: you can redistribute it and/or modify it
426+ * under the terms of the GNU Lesser General Public License version 3,
427+ * as published by the Free Software Foundation.
428+ *
429+ * This program is distributed in the hope that it will be useful,
430+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
431+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
432+ * GNU Lesser General Public License for more details.
433+ *
434+ * You should have received a copy of the GNU Lesser General Public License
435+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
436+ *
437+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
438+ * Ricardo Mendoza <ricardo.mendoza@canonical.com>
439+ */
440+#ifndef CORE_UBUNTU_MEDIA_AUDIO_PULSE_AUDIO_OUTPUT_OBSERVER_H_
441+#define CORE_UBUNTU_MEDIA_AUDIO_PULSE_AUDIO_OUTPUT_OBSERVER_H_
442+
443+#include <core/media/audio/output_observer.h>
444+
445+#include <iosfwd>
446+#include <memory>
447+
448+namespace core
449+{
450+namespace ubuntu
451+{
452+namespace media
453+{
454+namespace audio
455+{
456+// Implements the audio::OutputObserver interface
457+// relying on pulse to query the connected ports
458+// of the primary card of the system.
459+class PulseAudioOutputObserver : public OutputObserver
460+{
461+public:
462+ // Save us some typing.
463+ typedef std::shared_ptr<PulseAudioOutputObserver> Ptr;
464+
465+ // Constructs a new instance, or throws std::runtime_error
466+ // if connection to pulseaudio fails.
467+ PulseAudioOutputObserver();
468+
469+ // Getable/observable property holding the state of external outputs.
470+ const core::Property<OutputState>& external_output_state() const override;
471+
472+private:
473+ struct Private;
474+ std::shared_ptr<Private> d;
475+};
476+}
477+}
478+}
479+}
480+
481+#endif // CORE_UBUNTU_MEDIA_AUDIO_PULSE_AUDIO_OUTPUT_OBSERVER_H_
482
483=== added file 'src/core/media/dispatcher.h'
484--- src/core/media/dispatcher.h 1970-01-01 00:00:00 +0000
485+++ src/core/media/dispatcher.h 2014-11-25 11:08:14 +0000
486@@ -0,0 +1,38 @@
487+/*
488+ * Copyright © 2014 Canonical Ltd.
489+ *
490+ * This program is free software: you can redistribute it and/or modify it
491+ * under the terms of the GNU Lesser General Public License version 3,
492+ * as published by the Free Software Foundation.
493+ *
494+ * This program is distributed in the hope that it will be useful,
495+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
496+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
497+ * GNU Lesser General Public License for more details.
498+ *
499+ * You should have received a copy of the GNU Lesser General Public License
500+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
501+ *
502+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
503+ */
504+#ifndef CORE_UBUNTU_MEDIA_DISPATCHER_H_
505+#define CORE_UBUNTU_MEDIA_DISPATCHER_H_
506+
507+#include <functional>
508+
509+namespace core
510+{
511+namespace ubuntu
512+{
513+namespace media
514+{
515+// Functional describing a task to be executed later.
516+typedef std::function<void()> Task;
517+// Functional describing a Dispatcher that takes tasks
518+// and executes them. The implementation is expected to
519+// execute the task later, on a different thread.
520+typedef std::function<void(Task)> Dispatcher;
521+}
522+}
523+}
524+#endif // CORE_UBUNTU_MEDIA_DISPATCHER_H_
525
526=== added file 'src/core/media/external_services.h'
527--- src/core/media/external_services.h 1970-01-01 00:00:00 +0000
528+++ src/core/media/external_services.h 2014-11-25 11:08:14 +0000
529@@ -0,0 +1,83 @@
530+/*
531+ * Copyright © 2014 Canonical Ltd.
532+ *
533+ * This program is free software: you can redistribute it and/or modify it
534+ * under the terms of the GNU Lesser General Public License version 3,
535+ * as published by the Free Software Foundation.
536+ *
537+ * This program is distributed in the hope that it will be useful,
538+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
539+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
540+ * GNU Lesser General Public License for more details.
541+ *
542+ * You should have received a copy of the GNU Lesser General Public License
543+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
544+ *
545+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
546+ */
547+#ifndef CORE_UBUNTU_MEDIA_EXTERNAL_SERVICES_H_
548+#define CORE_UBUNTU_MEDIA_EXTERNAL_SERVICES_H_
549+
550+#include <core/dbus/bus.h>
551+
552+#include <core/dbus/asio/executor.h>
553+
554+#include <boost/asio.hpp>
555+
556+namespace core
557+{
558+namespace ubuntu
559+{
560+namespace media
561+{
562+namespace helper
563+{
564+// A helper struct that bundles:
565+// * a dispatcher, i.e., the io_service
566+// * access to the system and session bus
567+//
568+// In addtion, it allows us to mock out services and
569+// for acceptance testing purposes.
570+struct ExternalServices
571+{
572+ ExternalServices(const core::dbus::Bus::Ptr& session, const core::dbus::Bus::Ptr& system)
573+ : keep_alive{io_service},
574+ session{session},
575+ system{system}
576+ {
577+ }
578+
579+ ExternalServices()
580+ : ExternalServices
581+ {
582+ core::dbus::Bus::Ptr{new core::dbus::Bus{core::dbus::WellKnownBus::session}},
583+ core::dbus::Bus::Ptr{new core::dbus::Bus{core::dbus::WellKnownBus::system}}
584+ }
585+ {
586+ session->install_executor(core::dbus::asio::make_executor(session, io_service));
587+ system->install_executor(core::dbus::asio::make_executor(session, io_service));
588+ }
589+
590+
591+ void run()
592+ {
593+ io_service.run();
594+ }
595+
596+ void stop()
597+ {
598+ io_service.stop();
599+ }
600+
601+ boost::asio::io_service io_service;
602+ boost::asio::io_service::work keep_alive;
603+
604+ core::dbus::Bus::Ptr session;
605+ core::dbus::Bus::Ptr system;
606+};
607+}
608+}
609+}
610+}
611+
612+#endif // CORE_UBUNTU_MEDIA_EXTERNAL_SERVICES_H_
613
614=== removed file 'src/core/media/indicator_power_service.h'
615--- src/core/media/indicator_power_service.h 2014-09-05 18:59:48 +0000
616+++ src/core/media/indicator_power_service.h 1970-01-01 00:00:00 +0000
617@@ -1,74 +0,0 @@
618-/*
619- * Copyright (C) 2014 Canonical Ltd
620- *
621- * This program is free software: you can redistribute it and/or modify
622- * it under the terms of the GNU Lesser General Public License version 3 as
623- * published by the Free Software Foundation.
624- *
625- * This program is distributed in the hope that it will be useful,
626- * but WITHOUT ANY WARRANTY; without even the implied warranty of
627- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
628- * GNU Lesser General Public License for more details.
629- *
630- * You should have received a copy of the GNU Lesser General Public License
631- * along with this program. If not, see <http://www.gnu.org/licenses/>.
632- *
633- * Author: Jim Hodapp <jim.hodapp@canonical.com>
634- */
635-
636-#include <core/dbus/dbus.h>
637-#include <core/dbus/fixture.h>
638-#include <core/dbus/object.h>
639-#include <core/dbus/property.h>
640-#include <core/dbus/service.h>
641-#include <core/dbus/interfaces/properties.h>
642-#include <core/dbus/types/stl/tuple.h>
643-#include <core/dbus/types/stl/vector.h>
644-
645-#include <core/dbus/asio/executor.h>
646-
647-#include <string>
648-
649-namespace core
650-{
651-
652-struct IndicatorPower
653-{
654- static std::string& name()
655- {
656- static std::string s = "com.canonical.indicator.power.Battery";
657- return s;
658- }
659-
660- struct PowerLevel
661- {
662- static std::string name()
663- {
664- static std::string s = "PowerLevel";
665- return s;
666- }
667-
668- typedef IndicatorPower Interface;
669- // Possible values: "ok", "low", "very_low", "critical"
670- typedef std::string ValueType;
671- static const bool readable = true;
672- static const bool writable = false;
673- };
674-
675- struct IsWarning
676- {
677- static std::string name()
678- {
679- static std::string s = "IsWarning";
680- return s;
681- }
682-
683- typedef IndicatorPower Interface;
684- typedef bool ValueType;
685- static const bool readable = true;
686- static const bool writable = false;
687- };
688-
689-}; // IndicatorPower
690-
691-}
692
693=== modified file 'src/core/media/player_implementation.cpp'
694--- src/core/media/player_implementation.cpp 2014-11-25 11:08:14 +0000
695+++ src/core/media/player_implementation.cpp 2014-11-25 11:08:14 +0000
696@@ -25,8 +25,6 @@
697 #include "engine.h"
698 #include "track_list_implementation.h"
699
700-#include "powerd_service.h"
701-#include "unity_screen_service.h"
702 #include "gstreamer/engine.h"
703
704 #include <memory>
705@@ -52,41 +50,25 @@
706 WAKELOCK_CLEAR_INVALID
707 };
708
709- Private(PlayerImplementation* parent,
710- const dbus::types::ObjectPath& session_path,
711- const std::shared_ptr<media::Service>& service,
712- PlayerImplementation::PlayerKey key)
713+ Private(PlayerImplementation* parent, const media::PlayerImplementation::Configuration& config)
714 : parent(parent),
715- service(service),
716+ config(config),
717+ display_state_lock(config.power_state_controller->display_state_lock()),
718+ system_state_lock(config.power_state_controller->system_state_lock()),
719 engine(std::make_shared<gstreamer::Engine>()),
720- session_path(session_path),
721 track_list(
722 new media::TrackListImplementation(
723- session_path.as_string() + "/TrackList",
724+ config.session->path().as_string() + "/TrackList",
725 engine->meta_data_extractor())),
726- sys_lock_name("media-hub-music-playback"),
727- disp_cookie(-1),
728 system_wakelock_count(0),
729 display_wakelock_count(0),
730 previous_state(Engine::State::stopped),
731- key(key),
732 engine_state_change_connection(engine->state().changed().connect(make_state_change_handler()))
733 {
734- auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::system));
735- bus->install_executor(dbus::asio::make_executor(bus));
736-
737- auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::Powerd>::interface_name());
738- powerd_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/powerd"));
739-
740- auto uscreen_stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::UScreen>::interface_name());
741- uscreen_session = uscreen_stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/Unity/Screen"));
742-
743- auto client_death_observer = media::platform_default_client_death_observer();
744-
745- client_death_observer->register_for_death_notifications_with_key(key);
746- client_death_observer->on_client_with_key_died().connect([this](const media::Player::PlayerKey& died)
747+ config.client_death_observer->register_for_death_notifications_with_key(config.key);
748+ config.client_death_observer->on_client_with_key_died().connect([this](const media::Player::PlayerKey& died)
749 {
750- if (died != this->key)
751+ if (died != this->config.key)
752 return;
753
754 on_client_died();
755@@ -169,24 +151,12 @@
756 if (parent->is_video_source())
757 {
758 if (++display_wakelock_count == 1)
759- {
760- auto result = uscreen_session->invoke_method_synchronously<core::UScreen::keepDisplayOn, int>();
761- if (result.is_error())
762- throw std::runtime_error(result.error().print());
763- disp_cookie = result.value();
764- cout << "Requested new display wakelock" << endl;
765- }
766+ display_state_lock->request_acquire(media::power::DisplayState::on);
767 }
768 else
769 {
770 if (++system_wakelock_count == 1)
771- {
772- auto result = powerd_session->invoke_method_synchronously<core::Powerd::requestSysState, std::string>(sys_lock_name, static_cast<int>(1));
773- if (result.is_error())
774- throw std::runtime_error(result.error().print());
775- sys_cookie = result.value();
776- cout << "Requested new system wakelock" << endl;
777- }
778+ system_state_lock->request_acquire(media::power::SystemState::active);
779 }
780 }
781 catch(const std::exception& e)
782@@ -208,20 +178,12 @@
783 case wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM:
784 // Only actually clear the system wakelock once the count reaches zero
785 if (--system_wakelock_count == 0)
786- {
787- cout << "Clearing system wakelock" << endl;
788- powerd_session->invoke_method_synchronously<core::Powerd::clearSysState, void>(sys_cookie);
789- sys_cookie.clear();
790- }
791+ system_state_lock->request_release(media::power::SystemState::active);
792 break;
793 case wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY:
794 // Only actually clear the display wakelock once the count reaches zero
795 if (--display_wakelock_count == 0)
796- {
797- cout << "Clearing display wakelock" << endl;
798- uscreen_session->invoke_method_synchronously<core::UScreen::removeDisplayOnRequest, void>(disp_cookie);
799- disp_cookie = -1;
800- }
801+ display_state_lock->request_release(media::power::DisplayState::on);
802 break;
803 case wakelock_clear_t::WAKELOCK_CLEAR_INVALID:
804 default:
805@@ -275,44 +237,33 @@
806 engine->reset();
807 }
808
809- PlayerImplementation* parent;
810- std::shared_ptr<Service> service;
811+ // Our link back to our parent.
812+ media::PlayerImplementation* parent;
813+ // We just store the parameters passed on construction.
814+ media::PlayerImplementation::Configuration config;
815+ media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock;
816+ media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock;
817+
818 std::shared_ptr<Engine> engine;
819- dbus::types::ObjectPath session_path;
820 std::shared_ptr<TrackListImplementation> track_list;
821- std::shared_ptr<dbus::Object> powerd_session;
822- std::shared_ptr<dbus::Object> uscreen_session;
823- std::string sys_lock_name;
824- int disp_cookie;
825- std::string sys_cookie;
826 std::atomic<int> system_wakelock_count;
827 std::atomic<int> display_wakelock_count;
828 Engine::State previous_state;
829- PlayerImplementation::PlayerKey key;
830 core::Signal<> on_client_disconnected;
831 core::Connection engine_state_change_connection;
832 };
833
834-media::PlayerImplementation::PlayerImplementation(
835- const std::string& identity,
836- const std::shared_ptr<core::dbus::Bus>& bus,
837- const std::shared_ptr<core::dbus::Object>& session,
838- const std::shared_ptr<Service>& service,
839- PlayerKey key)
840+media::PlayerImplementation::PlayerImplementation(const media::PlayerImplementation::Configuration& config)
841 : media::PlayerSkeleton
842 {
843 media::PlayerSkeleton::Configuration
844 {
845- bus,
846- session,
847- identity
848+ config.bus,
849+ config.session,
850+ config.identity
851 }
852 },
853- d(make_shared<Private>(
854- this,
855- session->path(),
856- service,
857- key))
858+ d{std::make_shared<Private>(this, config)}
859 {
860 // Initialize default values for Player interface properties
861 can_play().set(true);
862@@ -452,7 +403,7 @@
863 // TODO: Convert this to be a property instead of sync call
864 media::Player::PlayerKey media::PlayerImplementation::key() const
865 {
866- return d->key;
867+ return d->config.key;
868 }
869
870 media::video::Sink::Ptr media::PlayerImplementation::create_gl_texture_video_sink(std::uint32_t texture_id)
871
872=== modified file 'src/core/media/player_implementation.h'
873--- src/core/media/player_implementation.h 2014-11-25 11:08:14 +0000
874+++ src/core/media/player_implementation.h 2014-11-25 11:08:14 +0000
875@@ -21,6 +21,9 @@
876
877 #include "player_skeleton.h"
878
879+#include "client_death_observer.h"
880+#include "power/state_controller.h"
881+
882 #include <memory>
883
884 namespace core
885@@ -35,12 +38,21 @@
886 class PlayerImplementation : public PlayerSkeleton
887 {
888 public:
889- PlayerImplementation(
890- const std::string& identity,
891- const std::shared_ptr<core::dbus::Bus>& bus,
892- const std::shared_ptr<core::dbus::Object>& session,
893- const std::shared_ptr<Service>& service,
894- PlayerKey key);
895+ // All creation time arguments go here
896+ struct Configuration
897+ {
898+ std::string identity;
899+ std::shared_ptr<core::dbus::Bus> bus;
900+ std::shared_ptr<core::dbus::Object> session;
901+ std::shared_ptr<Service> service;
902+ PlayerKey key;
903+
904+ // Functional dependencies
905+ ClientDeathObserver::Ptr client_death_observer;
906+ power::StateController::Ptr power_state_controller;
907+ };
908+
909+ PlayerImplementation(const Configuration& configuration);
910 ~PlayerImplementation();
911
912 virtual std::shared_ptr<TrackList> track_list();
913
914=== added directory 'src/core/media/power'
915=== added file 'src/core/media/power/battery_observer.cpp'
916--- src/core/media/power/battery_observer.cpp 1970-01-01 00:00:00 +0000
917+++ src/core/media/power/battery_observer.cpp 2014-11-25 11:08:14 +0000
918@@ -0,0 +1,127 @@
919+/*
920+ * Copyright © 2014 Canonical Ltd.
921+ *
922+ * This program is free software: you can redistribute it and/or modify it
923+ * under the terms of the GNU Lesser General Public License version 3,
924+ * as published by the Free Software Foundation.
925+ *
926+ * This program is distributed in the hope that it will be useful,
927+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
928+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
929+ * GNU Lesser General Public License for more details.
930+ *
931+ * You should have received a copy of the GNU Lesser General Public License
932+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
933+ *
934+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
935+ */
936+
937+#include <core/media/power/battery_observer.h>
938+
939+#include <core/dbus/macros.h>
940+#include <core/dbus/object.h>
941+#include <core/dbus/property.h>
942+
943+namespace media = core::ubuntu::media;
944+
945+namespace com { namespace canonical { namespace indicator { namespace power {
946+struct Battery
947+{
948+ static std::string& name()
949+ {
950+ static std::string s = "com.canonical.indicator.power.Battery";
951+ return s;
952+ }
953+
954+ static const core::dbus::types::ObjectPath& path()
955+ {
956+ static core::dbus::types::ObjectPath p{"/com/canonical/indicator/power/Battery"};
957+ return p;
958+ }
959+
960+ // Possible values: "ok", "low", "very_low", "critical"
961+ DBUS_CPP_READABLE_PROPERTY_DEF(PowerLevel, Battery, std::string)
962+ DBUS_CPP_READABLE_PROPERTY_DEF(IsWarning, Battery, bool)
963+}; // IndicatorPower
964+}}}}
965+
966+namespace
967+{
968+namespace impl
969+{
970+struct BatteryObserver : public media::power::BatteryObserver
971+{
972+ static core::ubuntu::media::power::Level power_level_from_string(const std::string& s)
973+ {
974+ static const std::map<std::string, core::ubuntu::media::power::Level> lut =
975+ {
976+ {"ok", core::ubuntu::media::power::Level::ok},
977+ {"low", core::ubuntu::media::power::Level::low},
978+ {"very_low", core::ubuntu::media::power::Level::very_low},
979+ {"critical", core::ubuntu::media::power::Level::critical}
980+ };
981+
982+ if (lut.count(s) == 0)
983+ return core::ubuntu::media::power::Level::unknown;
984+
985+ return lut.at(s);
986+ }
987+
988+ BatteryObserver(const core::dbus::Object::Ptr& object)
989+ : object{object},
990+ properties
991+ {
992+ core::Property<core::ubuntu::media::power::Level>{core::ubuntu::media::power::Level::unknown},
993+ object->get_property<com::canonical::indicator::power::Battery::PowerLevel>(),
994+ object->get_property<com::canonical::indicator::power::Battery::IsWarning>(),
995+ },
996+ connections
997+ {
998+ properties.power_level->changed().connect([this](const std::string& value)
999+ {
1000+ properties.typed_power_level = BatteryObserver::power_level_from_string(value);
1001+ })
1002+ }
1003+ {
1004+ }
1005+
1006+ const core::Property<core::ubuntu::media::power::Level>& level() const override
1007+ {
1008+ return properties.typed_power_level;
1009+ }
1010+
1011+ const core::Property<bool>& is_warning_active() const override
1012+ {
1013+ return *properties.is_warning;
1014+ }
1015+
1016+ // The object representing the remote indicator instance.
1017+ core::dbus::Object::Ptr object;
1018+ // All properties go here.
1019+ struct
1020+ {
1021+ // We have to translate from the raw strings coming in via the bus to
1022+ // the strongly typed enumeration exposed via the interface.
1023+ core::Property<core::ubuntu::media::power::Level> typed_power_level;
1024+ std::shared_ptr<core::dbus::Property<com::canonical::indicator::power::Battery::PowerLevel>> power_level;
1025+
1026+ std::shared_ptr<core::dbus::Property<com::canonical::indicator::power::Battery::IsWarning>> is_warning;
1027+ } properties;
1028+ // Our event connections
1029+ struct
1030+ {
1031+ core::ScopedConnection power_level;
1032+ } connections;
1033+
1034+};
1035+}
1036+}
1037+
1038+media::power::BatteryObserver::Ptr media::power::make_platform_default_battery_observer(media::helper::ExternalServices& es)
1039+{
1040+ auto service = core::dbus::Service::use_service<com::canonical::indicator::power::Battery>(es.session);
1041+ auto object = service->object_for_path(com::canonical::indicator::power::Battery::path());
1042+
1043+ return std::make_shared<impl::BatteryObserver>(object);
1044+}
1045+
1046
1047=== added file 'src/core/media/power/battery_observer.h'
1048--- src/core/media/power/battery_observer.h 1970-01-01 00:00:00 +0000
1049+++ src/core/media/power/battery_observer.h 2014-11-25 11:08:14 +0000
1050@@ -0,0 +1,69 @@
1051+/*
1052+ * Copyright © 2014 Canonical Ltd.
1053+ *
1054+ * This program is free software: you can redistribute it and/or modify it
1055+ * under the terms of the GNU Lesser General Public License version 3,
1056+ * as published by the Free Software Foundation.
1057+ *
1058+ * This program is distributed in the hope that it will be useful,
1059+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1060+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1061+ * GNU Lesser General Public License for more details.
1062+ *
1063+ * You should have received a copy of the GNU Lesser General Public License
1064+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1065+ *
1066+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1067+ */
1068+#ifndef CORE_UBUNTU_MEDIA_POWER_STATE_OBSERVER_H_
1069+#define CORE_UBUNTU_MEDIA_POWER_STATE_OBSERVER_H_
1070+
1071+#include <core/media/external_services.h>
1072+
1073+#include <core/property.h>
1074+
1075+#include <memory>
1076+
1077+namespace core
1078+{
1079+namespace ubuntu
1080+{
1081+namespace media
1082+{
1083+namespace power
1084+{
1085+// Enumerates known power levels.
1086+enum class Level
1087+{
1088+ unknown,
1089+ ok,
1090+ low,
1091+ very_low,
1092+ critical
1093+};
1094+
1095+// Interface that enables observation of the system power state.
1096+struct BatteryObserver
1097+{
1098+ // To safe us some typing.
1099+ typedef std::shared_ptr<BatteryObserver> Ptr;
1100+
1101+ BatteryObserver() = default;
1102+ virtual ~BatteryObserver() = default;
1103+
1104+ // A getable/observable property reporting the current power-level
1105+ // of the system.
1106+ virtual const core::Property<Level>& level() const = 0;
1107+ // A getable/observable property indicating whether a power-level
1108+ // warning is currently presented to the user.
1109+ virtual const core::Property<bool>& is_warning_active() const = 0;
1110+};
1111+
1112+// Creates a BatteryObserver instance that connects to the platform default
1113+// services to observe battery levels.
1114+core::ubuntu::media::power::BatteryObserver::Ptr make_platform_default_battery_observer(core::ubuntu::media::helper::ExternalServices&);
1115+}
1116+}
1117+}
1118+}
1119+#endif // CORE_UBUNTU_MEDIA_POWER_STATE_OBSERVER_H_
1120
1121=== added file 'src/core/media/power/state_controller.cpp'
1122--- src/core/media/power/state_controller.cpp 1970-01-01 00:00:00 +0000
1123+++ src/core/media/power/state_controller.cpp 2014-11-25 11:08:14 +0000
1124@@ -0,0 +1,302 @@
1125+/*
1126+ * Copyright © 2014 Canonical Ltd.
1127+ *
1128+ * This program is free software: you can redistribute it and/or modify it
1129+ * under the terms of the GNU Lesser General Public License version 3,
1130+ * as published by the Free Software Foundation.
1131+ *
1132+ * This program is distributed in the hope that it will be useful,
1133+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1134+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1135+ * GNU Lesser General Public License for more details.
1136+ *
1137+ * You should have received a copy of the GNU Lesser General Public License
1138+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1139+ *
1140+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1141+ */
1142+
1143+#include <core/media/power/state_controller.h>
1144+
1145+#include <core/dbus/macros.h>
1146+#include <core/dbus/object.h>
1147+
1148+namespace media = core::ubuntu::media;
1149+
1150+namespace com { namespace canonical {
1151+struct Unity
1152+{
1153+ struct Screen
1154+ {
1155+ static const std::string& name()
1156+ {
1157+ static std::string s = "com.canonical.Unity.Screen";
1158+ return s;
1159+ }
1160+
1161+ static const core::dbus::types::ObjectPath& path()
1162+ {
1163+ static core::dbus::types::ObjectPath p{"/com/canonical/Unity/Screen"};
1164+ return p;
1165+ }
1166+
1167+ DBUS_CPP_METHOD_DEF(keepDisplayOn, Screen)
1168+ DBUS_CPP_METHOD_DEF(removeDisplayOnRequest, Screen)
1169+ };
1170+};
1171+namespace powerd {
1172+struct Interface
1173+{
1174+ static std::string& name()
1175+ {
1176+ static std::string s = "com.canonical.powerd";
1177+ return s;
1178+ }
1179+
1180+ static const core::dbus::types::ObjectPath& path()
1181+ {
1182+ static core::dbus::types::ObjectPath p{"/com/canonical/powerd"};
1183+ return p;
1184+ }
1185+
1186+ DBUS_CPP_METHOD_DEF(requestSysState, com::canonical::powerd::Interface)
1187+ DBUS_CPP_METHOD_DEF(clearSysState, com::canonical::powerd::Interface)
1188+};
1189+}}}
1190+
1191+namespace
1192+{
1193+namespace impl
1194+{
1195+struct DisplayStateLock : public media::power::StateController::Lock<media::power::DisplayState>,
1196+ public std::enable_shared_from_this<DisplayStateLock>
1197+{
1198+ // To safe us some typing
1199+ typedef std::shared_ptr<DisplayStateLock> Ptr;
1200+
1201+ // We postpone releasing the display for this amount of time.
1202+ static boost::posix_time::seconds timeout_for_release()
1203+ {
1204+ return boost::posix_time::seconds{4};
1205+ }
1206+
1207+ // The invalid cookie marker.
1208+ static constexpr const std::int32_t the_invalid_cookie{-1};
1209+
1210+ DisplayStateLock(const media::power::StateController::Ptr& parent,
1211+ boost::asio::io_service& io_service,
1212+ const core::dbus::Object::Ptr& object)
1213+ : parent{parent},
1214+ timeout{io_service},
1215+ object{object},
1216+ cookie{the_invalid_cookie}
1217+ {
1218+ }
1219+
1220+ // From core::ubuntu::media::power::StateController::Lock<DisplayState>
1221+ void request_acquire(media::power::DisplayState state) override
1222+ {
1223+ if (state == media::power::DisplayState::off)
1224+ return;
1225+
1226+ std::weak_ptr<DisplayStateLock> wp{shared_from_this()};
1227+
1228+ object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::keepDisplayOn, std::int32_t>([wp, state](const core::dbus::Result<std::int32_t>& result)
1229+ {
1230+ if (result.is_error()) // We should at least log the error case here.
1231+ return;
1232+
1233+ if (auto sp = wp.lock())
1234+ {
1235+ sp->cookie = result.value();
1236+ sp->signals.acquired(state);
1237+ }
1238+ });
1239+ }
1240+
1241+ void request_release(media::power::DisplayState state) override
1242+ {
1243+ if (state == media::power::DisplayState::off)
1244+ return;
1245+
1246+ if (cookie == the_invalid_cookie)
1247+ return;
1248+
1249+ std::weak_ptr<DisplayStateLock> wp{shared_from_this()};
1250+
1251+ auto current_cookie(cookie);
1252+
1253+ timeout.expires_from_now(timeout_for_release());
1254+ timeout.async_wait([wp, state, current_cookie](const boost::system::error_code& ec)
1255+ {
1256+ if (not ec)
1257+ {
1258+ if (auto sp = wp.lock())
1259+ {
1260+ sp->object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::removeDisplayOnRequest, void>([wp, state, current_cookie](const core::dbus::Result<void>& result)
1261+ {
1262+ if (result.is_error()) // We should at least log the error case here.
1263+ return;
1264+
1265+ if (auto sp = wp.lock())
1266+ {
1267+ sp->signals.released(state);
1268+
1269+ // We might have issued a different request before and
1270+ // only call the display state done if the original cookie
1271+ // corresponds to the one we just gave up.
1272+ if (sp->cookie == current_cookie)
1273+ sp->cookie = the_invalid_cookie;
1274+ }
1275+
1276+ }, current_cookie);
1277+ }
1278+ }
1279+ });
1280+ }
1281+
1282+ // Emitted whenever the acquire request completes.
1283+ const core::Signal<media::power::DisplayState>& acquired() const override
1284+ {
1285+ return signals.acquired;
1286+ }
1287+
1288+ // Emitted whenever the release request completes.
1289+ const core::Signal<media::power::DisplayState>& released() const override
1290+ {
1291+ return signals.released;
1292+ }
1293+
1294+ media::power::StateController::Ptr parent;
1295+ boost::asio::deadline_timer timeout;
1296+ core::dbus::Object::Ptr object;
1297+ std::int32_t cookie;
1298+
1299+ struct
1300+ {
1301+ core::Signal<media::power::DisplayState> acquired;
1302+ core::Signal<media::power::DisplayState> released;
1303+ } signals;
1304+};
1305+
1306+struct SystemStateLock : public media::power::StateController::Lock<media::power::SystemState>
1307+{
1308+ static constexpr const char* wake_lock_name
1309+ {
1310+ "media-hub-playback_lock"
1311+ };
1312+
1313+ SystemStateLock(const media::power::StateController::Ptr& parent, const core::dbus::Object::Ptr& object)
1314+ : parent{parent},
1315+ object{object}
1316+ {
1317+ }
1318+
1319+ // Informs the system that the caller would like
1320+ // the system to stay active.
1321+ void request_acquire(media::power::SystemState state) override
1322+ {
1323+ if (state == media::power::SystemState::suspend)
1324+ return;
1325+
1326+ std::lock_guard<std::mutex> lg{cookie_store_guard};
1327+ if (cookie_store.count(state) > 0)
1328+ return;
1329+
1330+ object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::requestSysState, std::string>([this, state](const core::dbus::Result<std::string>& result)
1331+ {
1332+ if (result.is_error()) // TODO(tvoss): We should log the error condition here.
1333+ return;
1334+
1335+ std::lock_guard<std::mutex> lg{cookie_store_guard};
1336+
1337+ cookie_store[state] = result.value();
1338+ signals.acquired(state);
1339+ }, std::string{wake_lock_name}, static_cast<std::int32_t>(state));
1340+ }
1341+
1342+ // Informs the system that the caller does not
1343+ // require the system to stay active anymore.
1344+ void request_release(media::power::SystemState state) override
1345+ {
1346+ if (state == media::power::SystemState::suspend)
1347+ return;
1348+
1349+ std::lock_guard<std::mutex> lg{cookie_store_guard};
1350+
1351+ if (cookie_store.count(state) == 0)
1352+ return;
1353+
1354+ object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::clearSysState, void>([this, state](const core::dbus::Result<void>& result)
1355+ {
1356+ if (result.is_error()) {/*TODO(tvoss): We should log the error condition here.*/}
1357+
1358+ std::lock_guard<std::mutex> lg{cookie_store_guard};
1359+
1360+ cookie_store.erase(state);
1361+ signals.released(state);
1362+ }, cookie_store.at(state));
1363+ }
1364+
1365+ // Emitted whenever the acquire request completes.
1366+ const core::Signal<media::power::SystemState>& acquired() const override
1367+ {
1368+ return signals.acquired;
1369+ }
1370+
1371+ // Emitted whenever the release request completes.
1372+ const core::Signal<media::power::SystemState>& released() const override
1373+ {
1374+ return signals.released;
1375+ }
1376+
1377+ std::mutex cookie_store_guard;
1378+ std::map<media::power::SystemState, std::string> cookie_store;
1379+ media::power::StateController::Ptr parent;
1380+ core::dbus::Object::Ptr object;
1381+ struct
1382+ {
1383+ core::Signal<media::power::SystemState> acquired;
1384+ core::Signal<media::power::SystemState> released;
1385+ } signals;
1386+};
1387+
1388+struct StateController : public media::power::StateController,
1389+ public std::enable_shared_from_this<impl::StateController>
1390+{
1391+ StateController(media::helper::ExternalServices& es)
1392+ : external_services{es},
1393+ powerd
1394+ {
1395+ core::dbus::Service::use_service<com::canonical::powerd::Interface>(external_services.system)
1396+ ->object_for_path(com::canonical::powerd::Interface::path())
1397+ },
1398+ unity_screen
1399+ {
1400+ core::dbus::Service::use_service<com::canonical::Unity::Screen>(external_services.system)
1401+ ->object_for_path(com::canonical::Unity::Screen::path())
1402+ }
1403+ {
1404+ }
1405+
1406+ media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock() override
1407+ {
1408+ return std::make_shared<impl::SystemStateLock>(shared_from_this(), powerd);
1409+ }
1410+
1411+ media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock() override
1412+ {
1413+ return std::make_shared<impl::DisplayStateLock>(shared_from_this(), external_services.io_service, unity_screen);
1414+ }
1415+
1416+ media::helper::ExternalServices& external_services;
1417+ core::dbus::Object::Ptr powerd;
1418+ core::dbus::Object::Ptr unity_screen;
1419+};
1420+}
1421+}
1422+
1423+media::power::StateController::Ptr media::power::make_platform_default_state_controller(core::ubuntu::media::helper::ExternalServices& external_services)
1424+{
1425+ return std::make_shared<impl::StateController>(external_services);
1426+}
1427
1428=== added file 'src/core/media/power/state_controller.h'
1429--- src/core/media/power/state_controller.h 1970-01-01 00:00:00 +0000
1430+++ src/core/media/power/state_controller.h 2014-11-25 11:08:14 +0000
1431@@ -0,0 +1,102 @@
1432+/*
1433+ * Copyright © 2014 Canonical Ltd.
1434+ *
1435+ * This program is free software: you can redistribute it and/or modify it
1436+ * under the terms of the GNU Lesser General Public License version 3,
1437+ * as published by the Free Software Foundation.
1438+ *
1439+ * This program is distributed in the hope that it will be useful,
1440+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1441+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1442+ * GNU Lesser General Public License for more details.
1443+ *
1444+ * You should have received a copy of the GNU Lesser General Public License
1445+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1446+ *
1447+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1448+ */
1449+#ifndef CORE_UBUNTU_MEDIA_POWER_STATE_CONTROLLER_H_
1450+#define CORE_UBUNTU_MEDIA_POWER_STATE_CONTROLLER_H_
1451+
1452+#include <core/media/external_services.h>
1453+
1454+#include <core/property.h>
1455+
1456+#include <memory>
1457+
1458+namespace core
1459+{
1460+namespace ubuntu
1461+{
1462+namespace media
1463+{
1464+namespace power
1465+{
1466+// Enumerates all known power states of a display.
1467+enum class DisplayState
1468+{
1469+ // The display is off.
1470+ off = 0,
1471+ // The display is on.
1472+ on = 1
1473+};
1474+
1475+// Enumerates known power states of the system.
1476+enum class SystemState
1477+{
1478+ // Note that callers will be notified of suspend state changes
1479+ // but may not request this state.
1480+ suspend = 0,
1481+ // The Active state will prevent system suspend
1482+ active = 1,
1483+ // Substate of Active with disabled proximity based blanking
1484+ blank_on_proximity = 2
1485+};
1486+
1487+// Interface that enables observation of the system power state.
1488+struct StateController
1489+{
1490+ // To safe us some typing.
1491+ typedef std::shared_ptr<StateController> Ptr;
1492+
1493+ // When acquired, ensures that the system stays active,
1494+ // and decreases the reference count when released.
1495+ template<typename State>
1496+ struct Lock
1497+ {
1498+ // To safe us some typing.
1499+ typedef std::shared_ptr<Lock> Ptr;
1500+
1501+ Lock() = default;
1502+ virtual ~Lock() = default;
1503+
1504+ // Informs the system that the caller would like
1505+ // the system to stay active.
1506+ virtual void request_acquire(State state) = 0;
1507+ // Informs the system that the caller does not
1508+ // require the system to stay active anymore.
1509+ virtual void request_release(State state) = 0;
1510+
1511+ // Emitted whenever the acquire request completes.
1512+ virtual const core::Signal<State>& acquired() const = 0;
1513+ // Emitted whenever the release request completes.
1514+ virtual const core::Signal<State>& released() const = 0;
1515+ };
1516+
1517+ StateController() = default;
1518+ virtual ~StateController() = default;
1519+
1520+ // Returns a power::StateController::Lock<DisplayState> instance.
1521+ virtual Lock<DisplayState>::Ptr display_state_lock() = 0;
1522+ // Returns a power::StateController::Lock<SystemState> instance.
1523+ virtual Lock<SystemState>::Ptr system_state_lock() = 0;
1524+};
1525+
1526+// Creates a StateController instance that connects to the platform default
1527+// services to control system and display power states.
1528+StateController::Ptr make_platform_default_state_controller(core::ubuntu::media::helper::ExternalServices&);
1529+}
1530+}
1531+}
1532+}
1533+#endif // CORE_UBUNTU_MEDIA_POWER_STATE_CONTROLLER_H_
1534
1535=== removed file 'src/core/media/powerd_service.h'
1536--- src/core/media/powerd_service.h 2014-08-08 14:36:29 +0000
1537+++ src/core/media/powerd_service.h 1970-01-01 00:00:00 +0000
1538@@ -1,73 +0,0 @@
1539-/*
1540- * Copyright (C) 2014 Canonical Ltd
1541- *
1542- * This program is free software: you can redistribute it and/or modify
1543- * it under the terms of the GNU Lesser General Public License version 3 as
1544- * published by the Free Software Foundation.
1545- *
1546- * This program is distributed in the hope that it will be useful,
1547- * but WITHOUT ANY WARRANTY; without even the implied warranty of
1548- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1549- * GNU Lesser General Public License for more details.
1550- *
1551- * You should have received a copy of the GNU Lesser General Public License
1552- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1553- *
1554- * Author: Ricardo Mendoza <ricardo.mendoza@canonical.com>
1555- */
1556-
1557-#include <core/dbus/dbus.h>
1558-#include <core/dbus/fixture.h>
1559-#include <core/dbus/object.h>
1560-#include <core/dbus/property.h>
1561-#include <core/dbus/service.h>
1562-#include <core/dbus/interfaces/properties.h>
1563-#include <core/dbus/types/stl/tuple.h>
1564-#include <core/dbus/types/stl/vector.h>
1565-
1566-#include <core/dbus/asio/executor.h>
1567-
1568-#include <string>
1569-#include <vector>
1570-#include <chrono>
1571-
1572-namespace core
1573-{
1574-
1575-struct Powerd
1576-{
1577- static std::string& name()
1578- {
1579- static std::string s = "com.canonical.powerd";
1580- return s;
1581- }
1582-
1583- struct requestSysState
1584- {
1585- static std::string name()
1586- {
1587- static std::string s = "requestSysState";
1588- return s;
1589- }
1590-
1591- static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
1592-
1593- typedef Powerd Interface;
1594- };
1595-
1596- struct clearSysState
1597- {
1598- static std::string name()
1599- {
1600- static std::string s = "clearSysState";
1601- return s;
1602- }
1603-
1604- static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
1605-
1606- typedef Powerd Interface;
1607- };
1608-
1609-};
1610-
1611-}
1612
1613=== modified file 'src/core/media/server/server.cpp'
1614--- src/core/media/server/server.cpp 2014-11-25 11:08:14 +0000
1615+++ src/core/media/server/server.cpp 2014-11-25 11:08:14 +0000
1616@@ -20,8 +20,12 @@
1617 #include <core/media/player.h>
1618 #include <core/media/track_list.h>
1619
1620+#include <core/posix/signal.h>
1621+
1622 #include "core/media/service_implementation.h"
1623
1624+#include "core/media/audio/pulse_audio_output_observer.h"
1625+
1626 #include <iostream>
1627
1628 namespace media = core::ubuntu::media;
1629@@ -52,12 +56,104 @@
1630
1631 int main()
1632 {
1633+ auto trap = core::posix::trap_signals_for_all_subsequent_threads(
1634+ {
1635+ core::posix::Signal::sig_int,
1636+ core::posix::Signal::sig_term
1637+ });
1638+
1639+ trap->signal_raised().connect([trap](core::posix::Signal)
1640+ {
1641+ trap->stop();
1642+ });
1643+
1644+ // Init platform-specific functionality.
1645 platform_init();
1646
1647 cout << "Starting DecodingService..." << endl;
1648
1649- auto service = std::make_shared<media::ServiceImplementation>();
1650- service->run();
1651+ // We keep track of our state.
1652+ bool shutdown_requested{false};
1653+
1654+ // Our helper for connecting to external services.
1655+ core::ubuntu::media::helper::ExternalServices external_services;
1656+
1657+ std::thread external_services_worker
1658+ {
1659+ [&shutdown_requested, &external_services]()
1660+ {
1661+ while (not shutdown_requested)
1662+ {
1663+ try
1664+ {
1665+ external_services.io_service.run();
1666+ }
1667+ catch (const std::exception& e)
1668+ {
1669+ std::cerr << "Error while executing the underlying io_service: " << e.what() << std::endl;
1670+ }
1671+ catch (...)
1672+ {
1673+ std::cerr << "Error while executing the underlying io_service." << std::endl;
1674+ }
1675+ }
1676+ }
1677+ };
1678+
1679+ // We assemble the configuration for executing the service now.
1680+ media::ServiceImplementation::Configuration service_config
1681+ {
1682+ [&external_services](core::ubuntu::media::Task task)
1683+ {
1684+ external_services.io_service.post(task);
1685+ },
1686+ media::platform_default_client_death_observer(),
1687+ media::make_platform_default_recorder_observer(),
1688+ media::audio::make_platform_default_output_observer(),
1689+ media::power::make_platform_default_state_controller(external_services),
1690+ media::power::make_platform_default_battery_observer(external_services),
1691+ media::telephony::make_platform_default_call_monitor()
1692+ };
1693+
1694+ auto service = std::make_shared<media::ServiceImplementation>(service_config);
1695+
1696+ std::thread service_worker
1697+ {
1698+ [&shutdown_requested, service]()
1699+ {
1700+ while (not shutdown_requested)
1701+ {
1702+ try
1703+ {
1704+ service->run();
1705+ }
1706+ catch (const std::exception& e)
1707+ {
1708+ std::cerr << "Recoverable error while executing the service: " << e.what() << std::endl;
1709+ }
1710+ catch (...)
1711+ {
1712+ std::cerr << "Recoverable error while executing the service." << std::endl;
1713+ }
1714+ }
1715+ }
1716+ };
1717+
1718+ // We block on waiting for signals telling us to gracefully shutdown.
1719+ trap->run();
1720+
1721+ // Inform our workers that we should shutdown gracefully
1722+ shutdown_requested = true;
1723+
1724+ // And stop execution of helper and actual service.
1725+ external_services.stop();
1726+ service->stop();
1727+
1728+ if (external_services_worker.joinable())
1729+ external_services_worker.join();
1730+
1731+ if (service_worker.joinable())
1732+ service_worker.join();
1733
1734 return 0;
1735 }
1736
1737=== modified file 'src/core/media/service_implementation.cpp'
1738--- src/core/media/service_implementation.cpp 2014-11-25 11:08:14 +0000
1739+++ src/core/media/service_implementation.cpp 2014-11-25 11:08:14 +0000
1740@@ -22,11 +22,8 @@
1741
1742 #include "service_implementation.h"
1743
1744-#include "indicator_power_service.h"
1745-#include "call-monitor/call_monitor.h"
1746 #include "player_configuration.h"
1747 #include "player_implementation.h"
1748-#include "recorder_observer.h"
1749
1750 #include <boost/asio.hpp>
1751
1752@@ -40,7 +37,6 @@
1753 #include <pulse/pulseaudio.h>
1754
1755 #include "util/timeout.h"
1756-#include "unity_screen_service.h"
1757
1758 namespace media = core::ubuntu::media;
1759
1760@@ -48,462 +44,117 @@
1761
1762 struct media::ServiceImplementation::Private
1763 {
1764- Private()
1765- : resume_key(std::numeric_limits<std::uint32_t>::max()),
1766- keep_alive(io_service),
1767- disp_cookie(0),
1768- pulse_mainloop_api(nullptr),
1769- pulse_context(nullptr),
1770- headphones_connected(false),
1771- a2dp_connected(false),
1772- primary_idx(-1),
1773- call_monitor(new CallMonitor)
1774- {
1775- bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::session));
1776- bus->install_executor(dbus::asio::make_executor(bus, io_service));
1777- worker = std::move(std::thread([this]()
1778- {
1779- bus->run();
1780- }));
1781-
1782- // Spawn pulse watchdog
1783- pulse_mainloop = nullptr;
1784- pulse_worker = std::move(std::thread([this]()
1785- {
1786- std::unique_lock<std::mutex> lk(pulse_mutex);
1787- pcv.wait(lk,
1788- [this]{
1789- if (pulse_mainloop != nullptr || pulse_context != nullptr)
1790- {
1791- // We come from instance death, destroy and create.
1792- if (pulse_context != nullptr)
1793- {
1794- pa_threaded_mainloop_lock(pulse_mainloop);
1795- pa_operation *o;
1796- o = pa_context_drain(pulse_context,
1797- [](pa_context *context, void *userdata)
1798- {
1799- (void) context;
1800-
1801- Private *p = reinterpret_cast<Private*>(userdata);
1802- pa_threaded_mainloop_signal(p->mainloop(), 0);
1803- }, this);
1804-
1805- if (o)
1806- {
1807- while (pa_operation_get_state(o) == PA_OPERATION_RUNNING)
1808- pa_threaded_mainloop_wait(pulse_mainloop);
1809-
1810- pa_operation_unref(o);
1811- }
1812-
1813- pa_context_set_state_callback(pulse_context, NULL, NULL);
1814- pa_context_set_subscribe_callback(pulse_context, NULL, NULL);
1815- pa_context_disconnect(pulse_context);
1816- pa_context_unref(pulse_context);
1817- pulse_context = nullptr;
1818- pa_threaded_mainloop_unlock(pulse_mainloop);
1819- }
1820- }
1821-
1822- if (pulse_mainloop == nullptr)
1823- {
1824- pulse_mainloop = pa_threaded_mainloop_new();
1825-
1826- if (pa_threaded_mainloop_start(pulse_mainloop) != 0)
1827- {
1828- std::cerr << "Unable to start pulseaudio mainloop, audio output detection will not function" << std::endl;
1829- pa_threaded_mainloop_free(pulse_mainloop);
1830- pulse_mainloop = nullptr;
1831- }
1832- }
1833-
1834- do {
1835- create_pulse_context();
1836- } while (pulse_context == nullptr);
1837-
1838- // Wait for next instance death.
1839- return false;
1840- });
1841- }));
1842-
1843- // Connect the property change signal that will allow media-hub to take appropriate action
1844- // when the battery level reaches critical
1845- auto stub_service = dbus::Service::use_service(bus, "com.canonical.indicator.power");
1846- indicator_power_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/indicator/power/Battery"));
1847- power_level = indicator_power_session->get_property<core::IndicatorPower::PowerLevel>();
1848- is_warning = indicator_power_session->get_property<core::IndicatorPower::IsWarning>();
1849-
1850- // Obtain session with Unity.Screen so that we request state when doing recording
1851- auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::system));
1852- bus->install_executor(dbus::asio::make_executor(bus));
1853-
1854- auto uscreen_stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::UScreen>::interface_name());
1855- uscreen_session = uscreen_stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/Unity/Screen"));
1856-
1857- recorder_observer = media::make_platform_default_recorder_observer();
1858- recorder_observer->recording_state().changed().connect([this](media::RecordingState state)
1859- {
1860- media_recording_state_changed(state);
1861- });
1862+ Private(ServiceImplementation& parent, const media::ServiceImplementation::Configuration& config)
1863+ : resume_key{std::numeric_limits<std::uint32_t>::max()},
1864+ parent{parent},
1865+ dispatcher{config.dispatcher},
1866+ display_state_lock{config.power_state_controller->display_state_lock()},
1867+ power_state_controller{config.power_state_controller},
1868+ power_state_observer{config.power_state_observer},
1869+ audio_output_observer{config.audio_output_observer},
1870+ call_monitor(config.call_monitor),
1871+ client_death_observer(config.client_death_observer),
1872+ recorder_observer(config.recorder_observer),
1873+ connections
1874+ {
1875+ power_state_observer->level().changed().connect([this](const core::ubuntu::media::power::Level& level)
1876+ {
1877+ // When the battery level hits 10% or 5%, pause all multimedia sessions.
1878+ // Playback will resume when the user clears the presented notification.
1879+ switch (level)
1880+ {
1881+ case core::ubuntu::media::power::Level::low:
1882+ case core::ubuntu::media::power::Level::very_low:
1883+ this->parent.pause_all_multimedia_sessions();
1884+ break;
1885+ default:
1886+ break;
1887+ }
1888+ }),
1889+ power_state_observer->is_warning_active().changed().connect([this](bool notifying)
1890+ {
1891+ // If the low battery level notification is no longer being displayed,
1892+ // resume what the user was previously playing
1893+ if (!notifying)
1894+ this->parent.resume_multimedia_session();
1895+ }),
1896+ call_monitor->on_call_state_changed().connect([this](media::telephony::CallMonitor::State state) {
1897+ switch (state) {
1898+ case media::telephony::CallMonitor::OffHook:
1899+ this->parent.pause_all_multimedia_sessions();
1900+ break;
1901+ case media::telephony::CallMonitor::OnHook:
1902+ this->parent.resume_paused_multimedia_sessions();
1903+ break;
1904+ }
1905+ }),
1906+ recorder_observer->recording_state().changed().connect([this](media::RecordingState state)
1907+ {
1908+ media_recording_state_changed(state);
1909+ }),
1910+ audio_output_observer->external_output_state().changed().connect([this](media::audio::OutputState state)
1911+ {
1912+ switch (state)
1913+ {
1914+ case media::audio::OutputState::disconnected:
1915+ this->parent.pause_all_multimedia_sessions();
1916+ break;
1917+ case media::audio::OutputState::connected:
1918+ break;
1919+ }
1920+ })
1921+ }
1922+ {
1923 }
1924
1925 ~Private()
1926- {
1927- release_pulse_context();
1928-
1929- if (pulse_mainloop != nullptr)
1930- {
1931- pa_threaded_mainloop_stop(pulse_mainloop);
1932- pa_threaded_mainloop_free(pulse_mainloop);
1933- pulse_mainloop = nullptr;
1934- }
1935-
1936- bus->stop();
1937-
1938- if (worker.joinable())
1939- worker.join();
1940-
1941- if (pulse_worker.joinable())
1942- pulse_worker.join();
1943+ {
1944 }
1945
1946 void media_recording_state_changed(media::RecordingState state)
1947 {
1948- if (uscreen_session == nullptr)
1949- return;
1950-
1951- if (state == media::RecordingState::started)
1952- {
1953- if (disp_cookie > 0)
1954- return;
1955-
1956- auto result = uscreen_session->invoke_method_synchronously<core::UScreen::keepDisplayOn, int>();
1957- if (result.is_error())
1958- throw std::runtime_error(result.error().print());
1959- disp_cookie = result.value();
1960- }
1961- else if (state == media::RecordingState::stopped)
1962- {
1963- if (disp_cookie != -1)
1964- {
1965- timeout(4000, true, [this](){
1966- this->uscreen_session->invoke_method_synchronously<core::UScreen::removeDisplayOnRequest, void>(this->disp_cookie);
1967- this->disp_cookie = -1;
1968- });
1969- }
1970- }
1971- }
1972-
1973- pa_threaded_mainloop *mainloop()
1974- {
1975- return pulse_mainloop;
1976- }
1977-
1978- bool is_port_available(pa_card_port_info **ports, uint32_t n_ports, const char *name)
1979- {
1980- bool ret = false;
1981-
1982- if (ports != nullptr && n_ports > 0 && name != nullptr)
1983- {
1984- for (uint32_t i=0; i<n_ports; i++)
1985- {
1986- if (strstr(ports[i]->name, name) != nullptr && ports[i]->available != PA_PORT_AVAILABLE_NO)
1987- {
1988- ret = true;
1989- break;
1990- }
1991- }
1992- }
1993-
1994- return ret;
1995- }
1996-
1997- void update_wired_output()
1998- {
1999- const pa_operation *o = pa_context_get_card_info_by_index(pulse_context, primary_idx,
2000- [](pa_context *context, const pa_card_info *info, int eol, void *userdata)
2001- {
2002- (void) context;
2003- (void) eol;
2004-
2005- if (info == nullptr || userdata == nullptr)
2006- return;
2007-
2008- Private *p = reinterpret_cast<Private*>(userdata);
2009- if (p->is_port_available(info->ports, info->n_ports, "output-wired"))
2010- {
2011- if (!p->headphones_connected)
2012- std::cout << "Wired headphones connected" << std::endl;
2013- p->headphones_connected = true;
2014- }
2015- else if (p->headphones_connected == true)
2016- {
2017- std::cout << "Wired headphones disconnected" << std::endl;
2018- p->headphones_connected = false;
2019- p->pause_playback_if_necessary(std::get<0>(p->active_sink));
2020- }
2021- }, this);
2022- (void) o;
2023- }
2024-
2025- void pause_playback_if_necessary(int index)
2026- {
2027- // Catch uninitialized case (active index == -1)
2028- if (std::get<0>(active_sink) == -1)
2029- return;
2030-
2031- if (headphones_connected)
2032- return;
2033-
2034- // No headphones/fallback on primary sink? Pause.
2035- if (index == primary_idx)
2036- pause_playback();
2037- }
2038-
2039- void set_active_sink(const char *name)
2040- {
2041- const pa_operation *o = pa_context_get_sink_info_by_name(pulse_context, name,
2042- [](pa_context *context, const pa_sink_info *i, int eol, void *userdata)
2043- {
2044- (void) context;
2045-
2046- if (eol)
2047- return;
2048-
2049- Private *p = reinterpret_cast<Private*>(userdata);
2050- std::tuple<uint32_t, uint32_t, std::string> new_sink(std::make_tuple(i->index, i->card, i->name));
2051-
2052- printf("pulsesink: active_sink=('%s',%d,%d) -> ('%s',%d,%d)\n",
2053- std::get<2>(p->active_sink).c_str(), std::get<0>(p->active_sink),
2054- std::get<1>(p->active_sink), i->name, i->index, i->card);
2055-
2056- p->pause_playback_if_necessary(i->index);
2057- p->active_sink = new_sink;
2058- }, this);
2059-
2060- (void) o;
2061- }
2062-
2063- void update_active_sink()
2064- {
2065- const pa_operation *o = pa_context_get_server_info(pulse_context,
2066- [](pa_context *context, const pa_server_info *i, void *userdata)
2067- {
2068- (void) context;
2069-
2070- Private *p = reinterpret_cast<Private*>(userdata);
2071- if (i->default_sink_name != std::get<2>(p->active_sink))
2072- p->set_active_sink(i->default_sink_name);
2073- p->update_wired_output();
2074- }, this);
2075-
2076- (void) o;
2077- }
2078-
2079- void create_pulse_context()
2080- {
2081- if (pulse_context != nullptr)
2082- return;
2083-
2084- active_sink = std::make_tuple(-1, -1, "");
2085-
2086- bool keep_going = true, ok = true;
2087-
2088- pulse_mainloop_api = pa_threaded_mainloop_get_api(pulse_mainloop);
2089- pa_threaded_mainloop_lock(pulse_mainloop);
2090-
2091- pulse_context = pa_context_new(pulse_mainloop_api, "MediaHubPulseContext");
2092- pa_context_set_state_callback(pulse_context,
2093- [](pa_context *context, void *userdata)
2094- {
2095- (void) context;
2096- Private *p = reinterpret_cast<Private*>(userdata);
2097- // Signals the pa_threaded_mainloop_wait below to proceed
2098- pa_threaded_mainloop_signal(p->mainloop(), 0);
2099- }, this);
2100-
2101- if (pulse_context == nullptr)
2102- {
2103- std::cerr << "Unable to create new pulseaudio context" << std::endl;
2104- pa_threaded_mainloop_unlock(pulse_mainloop);
2105- return;
2106- }
2107-
2108- pa_context_connect(pulse_context, nullptr, pa_context_flags_t((int) PA_CONTEXT_NOAUTOSPAWN | (int) PA_CONTEXT_NOFAIL), nullptr);
2109- pa_threaded_mainloop_wait(pulse_mainloop);
2110-
2111- while (keep_going)
2112- {
2113- switch (pa_context_get_state(pulse_context))
2114- {
2115- case PA_CONTEXT_CONNECTING: // Wait for service to be available
2116- case PA_CONTEXT_AUTHORIZING:
2117- case PA_CONTEXT_SETTING_NAME:
2118- break;
2119-
2120- case PA_CONTEXT_READY:
2121- std::cout << "Pulseaudio connection established." << std::endl;
2122- keep_going = false;
2123- break;
2124-
2125- case PA_CONTEXT_FAILED:
2126- case PA_CONTEXT_TERMINATED:
2127- keep_going = false;
2128- ok = false;
2129- break;
2130-
2131- default:
2132- std::cerr << "Pulseaudio connection failure: " << pa_strerror(pa_context_errno(pulse_context));
2133- keep_going = false;
2134- ok = false;
2135- }
2136-
2137- if (keep_going)
2138- pa_threaded_mainloop_wait(pulse_mainloop);
2139- }
2140-
2141- if (ok)
2142- {
2143- pa_context_set_state_callback(pulse_context,
2144- [](pa_context *context, void *userdata)
2145- {
2146- (void) context;
2147- (void) userdata;
2148- Private *p = reinterpret_cast<Private*>(userdata);
2149- std::unique_lock<std::mutex> lk(p->pulse_mutex);
2150- switch (pa_context_get_state(context))
2151- {
2152- case PA_CONTEXT_FAILED:
2153- case PA_CONTEXT_TERMINATED:
2154- p->pcv.notify_all();
2155- break;
2156- default:
2157- break;
2158- }
2159- }, this);
2160-
2161- //FIXME: Get index for "sink.primary", the default onboard card on Touch.
2162- pa_context_get_sink_info_by_name(pulse_context, "sink.primary",
2163- [](pa_context *context, const pa_sink_info *i, int eol, void *userdata)
2164- {
2165- (void) context;
2166-
2167- if (eol)
2168- return;
2169-
2170- Private *p = reinterpret_cast<Private*>(userdata);
2171- p->primary_idx = i->index;
2172- p->update_wired_output();
2173- }, this);
2174-
2175- update_active_sink();
2176-
2177- pa_context_set_subscribe_callback(pulse_context,
2178- [](pa_context *context, pa_subscription_event_type_t t, uint32_t idx, void *userdata)
2179- {
2180- (void) context;
2181- (void) idx;
2182-
2183- if (userdata == nullptr)
2184- return;
2185-
2186- Private *p = reinterpret_cast<Private*>(userdata);
2187- if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK)
2188- {
2189- p->update_active_sink();
2190- }
2191- }, this);
2192- pa_context_subscribe(pulse_context, PA_SUBSCRIPTION_MASK_SINK, nullptr, this);
2193- }
2194- else
2195- {
2196- std::cerr << "Connection to pulseaudio failed or was dropped." << std::endl;
2197- pa_context_unref(pulse_context);
2198- pulse_context = nullptr;
2199- }
2200-
2201- pa_threaded_mainloop_unlock(pulse_mainloop);
2202- }
2203-
2204- void release_pulse_context()
2205- {
2206- if (pulse_context != nullptr)
2207- {
2208- pa_threaded_mainloop_lock(pulse_mainloop);
2209- pa_context_disconnect(pulse_context);
2210- pa_context_unref(pulse_context);
2211- pa_threaded_mainloop_unlock(pulse_mainloop);
2212- pulse_context = nullptr;
2213+ switch (state)
2214+ {
2215+ case media::RecordingState::started:
2216+ display_state_lock->request_acquire(media::power::DisplayState::on);
2217+ break;
2218+ case media::RecordingState::stopped:
2219+ display_state_lock->request_release(media::power::DisplayState::on);
2220+ break;
2221 }
2222 }
2223
2224 // This holds the key of the multimedia role Player instance that was paused
2225 // when the battery level reached 10% or 5%
2226 media::Player::PlayerKey resume_key;
2227- std::thread worker;
2228- dbus::Bus::Ptr bus;
2229- boost::asio::io_service io_service;
2230- boost::asio::io_service::work keep_alive;
2231- std::shared_ptr<dbus::Object> indicator_power_session;
2232- std::shared_ptr<core::dbus::Property<core::IndicatorPower::PowerLevel>> power_level;
2233- std::shared_ptr<core::dbus::Property<core::IndicatorPower::IsWarning>> is_warning;
2234- int disp_cookie;
2235- std::shared_ptr<dbus::Object> uscreen_session;
2236+
2237+ media::ServiceImplementation& parent;
2238+
2239+ media::Dispatcher dispatcher;
2240+
2241+ media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock;
2242+ media::power::StateController::Ptr power_state_controller;
2243+ media::power::BatteryObserver::Ptr power_state_observer;
2244+ media::audio::OutputObserver::Ptr audio_output_observer;
2245+ media::telephony::CallMonitor::Ptr call_monitor;
2246+ media::ClientDeathObserver::Ptr client_death_observer;
2247 media::RecorderObserver::Ptr recorder_observer;
2248- // Pulse-specific
2249- pa_mainloop_api *pulse_mainloop_api;
2250- pa_threaded_mainloop *pulse_mainloop;
2251- pa_context *pulse_context;
2252- std::thread pulse_worker;
2253- std::mutex pulse_mutex;
2254- std::condition_variable pcv;
2255- bool headphones_connected;
2256- bool a2dp_connected;
2257- std::tuple<int, int, std::string> active_sink;
2258- int primary_idx;
2259+ // All event connections go here.
2260+ struct
2261+ {
2262+ core::ScopedConnection power_level;
2263+ core::ScopedConnection is_warning_active;
2264+ core::ScopedConnection call_state;
2265+ core::ScopedConnection recording_state;
2266+ core::ScopedConnection external_audio_output_state;
2267+ } connections;
2268
2269- // Gets signaled when both the headphone jack is removed or an A2DP device is
2270- // disconnected and playback needs pausing
2271- core::Signal<void> pause_playback;
2272- std::unique_ptr<CallMonitor> call_monitor;
2273 std::list<media::Player::PlayerKey> paused_sessions;
2274 };
2275
2276-media::ServiceImplementation::ServiceImplementation() : d(new Private())
2277+media::ServiceImplementation::ServiceImplementation(const media::ServiceImplementation::Configuration& config)
2278+ : d(new Private(*this, config))
2279 {
2280- d->power_level->changed().connect([this](const core::IndicatorPower::PowerLevel::ValueType &level)
2281- {
2282- // When the battery level hits 10% or 5%, pause all multimedia sessions.
2283- // Playback will resume when the user clears the presented notification.
2284- if (level == "low" || level == "very_low")
2285- pause_all_multimedia_sessions();
2286- });
2287-
2288- d->is_warning->changed().connect([this](const core::IndicatorPower::IsWarning::ValueType &notifying)
2289- {
2290- // If the low battery level notification is no longer being displayed,
2291- // resume what the user was previously playing
2292- if (!notifying)
2293- resume_multimedia_session();
2294- });
2295-
2296- d->pause_playback.connect([this]()
2297- {
2298- std::cout << "Got pause_playback signal, pausing all multimedia sessions" << std::endl;
2299- pause_all_multimedia_sessions();
2300- });
2301-
2302- d->call_monitor->on_change([this](CallMonitor::State state) {
2303- switch (state) {
2304- case CallMonitor::OffHook:
2305- pause_all_multimedia_sessions();
2306- break;
2307- case CallMonitor::OnHook:
2308- resume_paused_multimedia_sessions();
2309- break;
2310- }
2311- });
2312 }
2313
2314 media::ServiceImplementation::~ServiceImplementation()
2315@@ -513,20 +164,34 @@
2316 std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
2317 const media::Player::Configuration& conf)
2318 {
2319- auto player = std::make_shared<media::PlayerImplementation>(
2320- conf.identity, conf.bus, conf.session, shared_from_this(), conf.key);
2321+ media::PlayerImplementation::Configuration player_config
2322+ {
2323+ conf.identity,
2324+ conf.bus,
2325+ conf.session,
2326+ ServiceImplementation::shared_from_this(),
2327+ conf.key,
2328+ d->client_death_observer,
2329+ d->power_state_controller
2330+ };
2331+
2332+ auto player = std::make_shared<media::PlayerImplementation>(player_config);
2333
2334 auto key = conf.key;
2335- player->on_client_disconnected().connect([this, key]()
2336+
2337+ // We hold a reference to ourselves.
2338+ auto sp = ServiceImplementation::shared_from_this();
2339+
2340+ player->on_client_disconnected().connect([sp, key]()
2341 {
2342 // Call remove_player_for_key asynchronously otherwise deadlock can occur
2343 // if called within this dispatcher context.
2344 // remove_player_for_key can destroy the player instance which in turn
2345 // destroys the "on_client_disconnected" signal whose destructor will wait
2346 // until all dispatches are done
2347- d->io_service.post([this, key]()
2348+ sp->d->dispatcher([sp, key]()
2349 {
2350- remove_player_for_key(key);
2351+ sp->remove_player_for_key(key);
2352 });
2353 });
2354
2355
2356=== modified file 'src/core/media/service_implementation.h'
2357--- src/core/media/service_implementation.h 2014-10-31 07:49:33 +0000
2358+++ src/core/media/service_implementation.h 2014-11-25 11:08:14 +0000
2359@@ -19,6 +19,15 @@
2360 #ifndef CORE_UBUNTU_MEDIA_SERVICE_IMPLEMENTATION_H_
2361 #define CORE_UBUNTU_MEDIA_SERVICE_IMPLEMENTATION_H_
2362
2363+#include "dispatcher.h"
2364+
2365+#include "client_death_observer.h"
2366+#include "recorder_observer.h"
2367+#include "audio/output_observer.h"
2368+#include "power/battery_observer.h"
2369+#include "power/state_controller.h"
2370+#include "telephony/call_monitor.h"
2371+
2372 #include "service_skeleton.h"
2373
2374 namespace core
2375@@ -30,10 +39,24 @@
2376
2377 class Player;
2378
2379-class ServiceImplementation : public ServiceSkeleton
2380+class ServiceImplementation
2381+ : public ServiceSkeleton,
2382+ public std::enable_shared_from_this<ServiceImplementation>
2383 {
2384 public:
2385- ServiceImplementation ();
2386+ // All creation time arguments go here.
2387+ struct Configuration
2388+ {
2389+ Dispatcher dispatcher;
2390+ media::ClientDeathObserver::Ptr client_death_observer;
2391+ media::RecorderObserver::Ptr recorder_observer;
2392+ media::audio::OutputObserver::Ptr audio_output_observer;
2393+ power::StateController::Ptr power_state_controller;
2394+ power::BatteryObserver::Ptr power_state_observer;
2395+ telephony::CallMonitor::Ptr call_monitor;
2396+ };
2397+
2398+ ServiceImplementation (const Configuration& configuration);
2399 ~ServiceImplementation ();
2400
2401 std::shared_ptr<Player> create_session(const Player::Configuration&);
2402@@ -46,7 +69,7 @@
2403 void resume_multimedia_session();
2404
2405 struct Private;
2406- std::shared_ptr<Private> d;
2407+ std::unique_ptr<Private> d;
2408 };
2409 }
2410 }
2411
2412=== modified file 'src/core/media/service_stub.h'
2413--- src/core/media/service_stub.h 2014-10-16 15:30:44 +0000
2414+++ src/core/media/service_stub.h 2014-11-25 11:08:14 +0000
2415@@ -33,7 +33,9 @@
2416 {
2417 namespace media
2418 {
2419-class ServiceStub : public core::dbus::Stub<core::ubuntu::media::Service>
2420+class ServiceStub
2421+ : public core::dbus::Stub<core::ubuntu::media::Service>,
2422+ public std::enable_shared_from_this<ServiceStub>
2423 {
2424 public:
2425 ServiceStub();
2426
2427=== renamed directory 'src/core/media/call-monitor' => 'src/core/media/telephony'
2428=== modified file 'src/core/media/telephony/call_monitor.cpp'
2429--- src/core/media/call-monitor/call_monitor.cpp 2014-11-04 23:37:22 +0000
2430+++ src/core/media/telephony/call_monitor.cpp 2014-11-25 11:08:14 +0000
2431@@ -29,8 +29,12 @@
2432 #include <list>
2433 #include <mutex>
2434
2435+namespace media = core::ubuntu::media;
2436
2437-namespace {
2438+namespace
2439+{
2440+namespace impl
2441+{
2442 class TelepathyCallMonitor : public QObject
2443 {
2444 Q_OBJECT
2445@@ -73,7 +77,7 @@
2446 }
2447 }
2448
2449- void on_change(const std::function<void(CallMonitor::State)>& func) {
2450+ void on_change(const std::function<void(media::telephony::CallMonitor::State)>& func) {
2451 std::lock_guard<std::mutex> l(cb_lock);
2452 cb = func;
2453 }
2454@@ -128,19 +132,19 @@
2455 {
2456 std::lock_guard<std::mutex> l(cb_lock);
2457 if (cb)
2458- cb(CallMonitor::OffHook);
2459+ cb(media::telephony::CallMonitor::OffHook);
2460 }
2461
2462 void onHook()
2463 {
2464 std::lock_guard<std::mutex> l(cb_lock);
2465 if (cb)
2466- cb(CallMonitor::OnHook);
2467+ cb(media::telephony::CallMonitor::OnHook);
2468 }
2469
2470 private:
2471 std::mutex cb_lock;
2472- std::function<void (CallMonitor::State)> cb;
2473+ std::function<void (media::telephony::CallMonitor::State)> cb;
2474 Tp::AccountManagerPtr mAccountManager;
2475 std::list<TelepathyCallMonitor*> mCallMonitors;
2476
2477@@ -157,12 +161,11 @@
2478 }
2479 }
2480 };
2481-}
2482
2483-class CallMonitorPrivate
2484+struct CallMonitor : public media::telephony::CallMonitor
2485 {
2486-public:
2487- CallMonitorPrivate() {
2488+ CallMonitor()
2489+ {
2490 try {
2491 std::thread([this]() {
2492 qt::core::world::build_and_run(0, nullptr, [this]() {
2493@@ -176,30 +179,32 @@
2494 } catch(...) {
2495 std::cerr << "exception(...) in CallMonitor thread start" << std::endl;
2496 }
2497- }
2498-
2499- ~CallMonitorPrivate() {
2500- qt::core::world::destroy();
2501+
2502+ mBridge->on_change([this](CallMonitor::State state)
2503+ {
2504+ call_state_changed(state);
2505+ });
2506+ }
2507+
2508+ ~CallMonitor()
2509+ {
2510+ delete mBridge;
2511+ }
2512+
2513+ const core::Signal<media::telephony::CallMonitor::State>& on_call_state_changed() const
2514+ {
2515+ return call_state_changed;
2516 }
2517
2518 TelepathyBridge *mBridge;
2519+ core::Signal<media::telephony::CallMonitor::State> call_state_changed;
2520 };
2521-
2522-
2523-CallMonitor::CallMonitor():
2524- d(new CallMonitorPrivate)
2525-{
2526-}
2527-
2528-CallMonitor::~CallMonitor()
2529-{
2530- delete d->mBridge;
2531- delete d;
2532-}
2533-
2534-void CallMonitor::on_change(const std::function<void(CallMonitor::State)>& func)
2535-{
2536- d->mBridge->on_change(func);
2537+}
2538+}
2539+
2540+media::telephony::CallMonitor::Ptr media::telephony::make_platform_default_call_monitor()
2541+{
2542+ return std::make_shared<impl::CallMonitor>();
2543 }
2544
2545 #include "call_monitor.moc"
2546
2547=== modified file 'src/core/media/telephony/call_monitor.h'
2548--- src/core/media/call-monitor/call_monitor.h 2014-10-31 07:49:33 +0000
2549+++ src/core/media/telephony/call_monitor.h 2014-11-25 11:08:14 +0000
2550@@ -17,25 +17,43 @@
2551 */
2552
2553
2554-#ifndef CALLMONITOR_H
2555-#define CALLMONITOR_H
2556+#ifndef CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_
2557+#define CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_
2558+
2559+#include <core/signal.h>
2560
2561 #include <functional>
2562-
2563-class CallMonitorPrivate;
2564-
2565-class CallMonitor
2566-{
2567-public:
2568- enum State { OffHook, OnHook };
2569-
2570- CallMonitor();
2571- ~CallMonitor();
2572-
2573- void on_change(const std::function<void(CallMonitor::State)>& func);
2574-
2575-private:
2576- CallMonitorPrivate *d;
2577+#include <memory>
2578+
2579+namespace core
2580+{
2581+namespace ubuntu
2582+{
2583+namespace media
2584+{
2585+namespace telephony
2586+{
2587+struct CallMonitor
2588+{
2589+ typedef std::shared_ptr<CallMonitor> Ptr;
2590+
2591+ enum State
2592+ {
2593+ OffHook,
2594+ OnHook
2595+ };
2596+
2597+ CallMonitor() = default;
2598+ virtual ~CallMonitor() = default;
2599+
2600+ virtual const core::Signal<State>& on_call_state_changed() const = 0;
2601 };
2602
2603-#endif // CALLMONITOR_H
2604+CallMonitor::Ptr make_platform_default_call_monitor();
2605+}
2606+}
2607+}
2608+}
2609+
2610+
2611+#endif // CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_
2612
2613=== removed file 'src/core/media/unity_screen_service.h'
2614--- src/core/media/unity_screen_service.h 2014-06-16 22:28:53 +0000
2615+++ src/core/media/unity_screen_service.h 1970-01-01 00:00:00 +0000
2616@@ -1,72 +0,0 @@
2617-/*
2618- * Copyright (C) 2014 Canonical Ltd
2619- *
2620- * This program is free software: you can redistribute it and/or modify
2621- * it under the terms of the GNU Lesser General Public License version 3 as
2622- * published by the Free Software Foundation.
2623- *
2624- * This program is distributed in the hope that it will be useful,
2625- * but WITHOUT ANY WARRANTY; without even the implied warranty of
2626- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2627- * GNU Lesser General Public License for more details.
2628- *
2629- * You should have received a copy of the GNU Lesser General Public License
2630- * along with this program. If not, see <http://www.gnu.org/licenses/>.
2631- *
2632- * Author: Alberto Aguirre <alberto.aguirre@canonical.com>
2633- */
2634-
2635-#include <core/dbus/dbus.h>
2636-#include <core/dbus/fixture.h>
2637-#include <core/dbus/object.h>
2638-#include <core/dbus/property.h>
2639-#include <core/dbus/service.h>
2640-#include <core/dbus/interfaces/properties.h>
2641-#include <core/dbus/types/stl/tuple.h>
2642-#include <core/dbus/types/stl/vector.h>
2643-
2644-#include <core/dbus/asio/executor.h>
2645-
2646-#include <string>
2647-#include <vector>
2648-#include <chrono>
2649-
2650-namespace core
2651-{
2652-
2653-struct UScreen
2654-{
2655- static std::string& name()
2656- {
2657- static std::string s = "com.canonical.Unity.Screen";
2658- return s;
2659- }
2660-
2661- struct keepDisplayOn
2662- {
2663- static std::string name()
2664- {
2665- static std::string s = "keepDisplayOn";
2666- return s;
2667- }
2668-
2669- static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
2670-
2671- typedef UScreen Interface;
2672- };
2673-
2674- struct removeDisplayOnRequest
2675- {
2676- static std::string name()
2677- {
2678- static std::string s = "removeDisplayOnRequest";
2679- return s;
2680- }
2681-
2682- static const std::chrono::milliseconds default_timeout() { return std::chrono::seconds{1}; }
2683-
2684- typedef UScreen Interface;
2685- };
2686-};
2687-
2688-}
2689
2690=== modified file 'tests/acceptance-tests/service.cpp'
2691--- tests/acceptance-tests/service.cpp 2014-04-04 14:31:43 +0000
2692+++ tests/acceptance-tests/service.cpp 2014-11-25 11:08:14 +0000
2693@@ -21,6 +21,7 @@
2694 #include <core/media/track_list.h>
2695
2696 #include "core/media/service_implementation.h"
2697+#include "core/media/audio/pulse_audio_output_observer.h"
2698
2699 #include "../waitable_state_transition.h"
2700
2701@@ -68,6 +69,48 @@
2702 timespec ts{ 0, static_cast<long int>(ms.count()) * 1000 * 1000};
2703 ::nanosleep(&ts, nullptr);
2704 }
2705+
2706+std::thread make_external_services_worker(bool shutdown_requested, media::helper::ExternalServices& es)
2707+{
2708+ return std::thread
2709+ {
2710+ [&shutdown_requested, &es]()
2711+ {
2712+ while (not shutdown_requested)
2713+ {
2714+ try
2715+ {
2716+ es.io_service.run();
2717+ }
2718+ catch (const std::exception& e)
2719+ {
2720+ std::cerr << "Error while executing the underlying io_service: " << e.what() << std::endl;
2721+ }
2722+ catch (...)
2723+ {
2724+ std::cerr << "Error while executing the underlying io_service." << std::endl;
2725+ }
2726+ }
2727+ }
2728+ };
2729+}
2730+
2731+media::ServiceImplementation::Configuration make_default_service_configuration(media::helper::ExternalServices& es)
2732+{
2733+ return media::ServiceImplementation::Configuration
2734+ {
2735+ [&es](media::Task task)
2736+ {
2737+ es.io_service.post(task);
2738+ },
2739+ media::platform_default_client_death_observer(),
2740+ media::make_platform_default_recorder_observer(),
2741+ media::audio::make_platform_default_output_observer(),
2742+ media::power::make_platform_default_state_controller(es),
2743+ media::power::make_platform_default_battery_observer(es),
2744+ media::telephony::make_platform_default_call_monitor()
2745+ };
2746+}
2747 }
2748
2749 TEST(MusicService, DISABLED_accessing_and_creating_a_session_works)
2750@@ -78,7 +121,17 @@
2751 {
2752 SigTermCatcher sc;
2753
2754- auto service = std::make_shared<media::ServiceImplementation>();
2755+ // We keep track of our state.
2756+ bool shutdown_requested{false};
2757+
2758+ // Our helper for connecting to external services.
2759+ media::helper::ExternalServices external_services;
2760+ std::thread external_services_worker
2761+ {
2762+ make_external_services_worker(shutdown_requested, external_services)
2763+ };
2764+
2765+ auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services));
2766 std::thread t([&service](){service->run();});
2767
2768 sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2769@@ -120,7 +173,16 @@
2770 {
2771 SigTermCatcher sc;
2772
2773- auto service = std::make_shared<media::ServiceImplementation>();
2774+ // We keep track of our state.
2775+ bool shutdown_requested{false};
2776+
2777+ media::helper::ExternalServices external_services;
2778+ std::thread external_services_worker
2779+ {
2780+ make_external_services_worker(shutdown_requested, external_services)
2781+ };
2782+
2783+ auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services));
2784 std::thread t([&service](){service->run();});
2785
2786 sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2787@@ -173,7 +235,16 @@
2788 {
2789 SigTermCatcher sc;
2790
2791- auto service = std::make_shared<media::ServiceImplementation>();
2792+ // We keep track of our state.
2793+ bool shutdown_requested{false};
2794+
2795+ media::helper::ExternalServices external_services;
2796+ std::thread external_services_worker
2797+ {
2798+ make_external_services_worker(shutdown_requested, external_services)
2799+ };
2800+
2801+ auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services));
2802 std::thread t([&service](){service->run();});
2803
2804 sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2805@@ -252,7 +323,16 @@
2806 {
2807 SigTermCatcher sc;
2808
2809- auto service = std::make_shared<media::ServiceImplementation>();
2810+ // We keep track of our state.
2811+ bool shutdown_requested{false};
2812+
2813+ media::helper::ExternalServices external_services;
2814+ std::thread external_services_worker
2815+ {
2816+ make_external_services_worker(shutdown_requested, external_services)
2817+ };
2818+
2819+ auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services));
2820 std::thread t([&service](){service->run();});
2821
2822 sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});
2823@@ -311,7 +391,16 @@
2824 {
2825 SigTermCatcher sc;
2826
2827- auto service = std::make_shared<media::ServiceImplementation>();
2828+ // We keep track of our state.
2829+ bool shutdown_requested{false};
2830+
2831+ media::helper::ExternalServices external_services;
2832+ std::thread external_services_worker
2833+ {
2834+ make_external_services_worker(shutdown_requested, external_services)
2835+ };
2836+
2837+ auto service = std::make_shared<media::ServiceImplementation>(make_default_service_configuration(external_services));
2838 std::thread t([&service](){service->run();});
2839
2840 sync_service_start.try_signal_ready_for(std::chrono::milliseconds{500});

Subscribers

People subscribed via source and target branches