Merge lp:~jhodapp/media-hub/fix-zero-position-while-seeking into lp:media-hub

Proposed by Jim Hodapp
Status: Merged
Approved by: Ricardo Mendoza
Approved revision: 124
Merged at revision: 126
Proposed branch: lp:~jhodapp/media-hub/fix-zero-position-while-seeking
Merge into: lp:media-hub
Prerequisite: lp:~thomas-voss/media-hub/decouple-player-skeleton-and-implementation
Diff against target: 3163 lines (+1445/-633)
34 files modified
debian/changelog (+6/-9)
debian/control (+0/-6)
include/core/media/player.h (+1/-0)
src/core/media/CMakeLists.txt (+6/-2)
src/core/media/apparmor.h (+0/-93)
src/core/media/apparmor/context.cpp (+34/-0)
src/core/media/apparmor/context.h (+51/-0)
src/core/media/apparmor/dbus.h (+94/-0)
src/core/media/apparmor/ubuntu.cpp (+198/-0)
src/core/media/apparmor/ubuntu.h (+167/-0)
src/core/media/gstreamer/engine.cpp (+3/-3)
src/core/media/gstreamer/playbin.cpp (+23/-6)
src/core/media/gstreamer/playbin.h (+2/-0)
src/core/media/hashed_keyed_player_store.cpp (+80/-0)
src/core/media/hashed_keyed_player_store.h (+76/-0)
src/core/media/keyed_player_store.h (+83/-0)
src/core/media/mpris/player.h (+3/-0)
src/core/media/null_track_list.h (+114/-0)
src/core/media/player.cpp (+0/-1)
src/core/media/player_configuration.h (+0/-3)
src/core/media/player_implementation.cpp (+104/-81)
src/core/media/player_implementation.h (+9/-9)
src/core/media/player_skeleton.cpp (+35/-93)
src/core/media/player_skeleton.h (+22/-16)
src/core/media/player_stub.cpp (+18/-0)
src/core/media/player_stub.h (+1/-0)
src/core/media/power/state_controller.cpp (+21/-25)
src/core/media/server/server.cpp (+19/-4)
src/core/media/service_implementation.cpp (+35/-27)
src/core/media/service_implementation.h (+2/-1)
src/core/media/service_skeleton.cpp (+125/-174)
src/core/media/service_skeleton.h (+14/-25)
src/core/media/telephony/call_monitor.cpp (+56/-37)
src/core/media/telephony/call_monitor.h (+43/-18)
To merge this branch: bzr merge lp:~jhodapp/media-hub/fix-zero-position-while-seeking
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Ubuntu Phablet Team Pending
Review via email: mp+252605@code.launchpad.net

Commit message

* Prevent a 0 position from being reported to the app which happens while seeking. Covers bad behavior that happens from GStreamer. Also expose the about_to_finish signal to the client.
* Enable playback again after manually seeking all the way to EOS. Also only send VideoDimensionsChanged signal only when necessary.

Description of the change

* Prevent a 0 position from being reported to the app which happens while seeking. Covers bad behavior that happens from GStreamer. Also expose the about_to_finish signal to the client.
* Enable playback again after manually seeking all the way to EOS. Also only send VideoDimensionsChanged signal only when necessary.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
122. By Jim Hodapp

Merged prereqs.

123. By Jim Hodapp

Merge prereqs.

124. By Jim Hodapp

Fix the last merge.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2015-03-16 15:38:27 +0000
3+++ debian/changelog 2015-03-16 15:38:27 +0000
4@@ -1,4 +1,9 @@
5-<<<<<<< TREE
6+media-hub (3.0.0) UNRELEASED; urgency=medium
7+
8+ * Refactor client-facing interfaces to pull out explicit dependency on hybris-based media layer.
9+
10+ -- Thomas Voss <thomas.voss@canonical.com> Tue, 11 Nov 2014 17:15:52 +0100
11+
12 media-hub (2.0.0+15.04.20150303-0ubuntu2) vivid; urgency=medium
13
14 * debian/control:
15@@ -17,14 +22,6 @@
16
17 -- CI Train Bot <ci-train-bot@canonical.com> Tue, 03 Mar 2015 22:56:52 +0000
18
19-=======
20-media-hub (3.0.0) UNRELEASED; urgency=medium
21-
22- * Refactor client-facing interfaces to pull out explicit dependency on hybris-based media layer.
23-
24- -- Thomas Voss <thomas.voss@canonical.com> Tue, 11 Nov 2014 17:15:52 +0100
25-
26->>>>>>> MERGE-SOURCE
27 media-hub (2.0.0+15.04.20150120-0ubuntu1) vivid; urgency=low
28
29 [ Jim Hodapp ]
30
31=== modified file 'debian/control'
32--- debian/control 2015-03-16 15:38:27 +0000
33+++ debian/control 2015-03-16 15:38:27 +0000
34@@ -39,14 +39,8 @@
35 Section: libdevel
36 Architecture: any
37 Multi-Arch: same
38-<<<<<<< TREE
39-Depends: libmedia-hub-common2 (= ${binary:Version}),
40- libmedia-hub-client2 (= ${binary:Version}),
41-=======
42-Pre-Depends: dpkg (>= 1.15.6~)
43 Depends: libmedia-hub-common3 (= ${binary:Version}),
44 libmedia-hub-client3 (= ${binary:Version}),
45->>>>>>> MERGE-SOURCE
46 ${misc:Depends},
47 libproperties-cpp-dev,
48 Suggests: libmedia-hub-doc
49
50=== modified file 'include/core/media/player.h'
51--- include/core/media/player.h 2015-03-16 15:38:27 +0000
52+++ include/core/media/player.h 2015-03-16 15:38:27 +0000
53@@ -168,6 +168,7 @@
54 virtual core::Property<Lifetime>& lifetime() = 0;
55
56 virtual const core::Signal<int64_t>& seeked_to() const = 0;
57+ virtual const core::Signal<void>& about_to_finish() const = 0;
58 virtual const core::Signal<void>& end_of_stream() const = 0;
59 virtual core::Signal<PlaybackStatus>& playback_status_changed() = 0;
60 virtual const core::Signal<video::Dimensions>& video_dimension_changed() const = 0;
61
62=== modified file 'src/core/media/CMakeLists.txt'
63--- src/core/media/CMakeLists.txt 2015-03-16 15:38:27 +0000
64+++ src/core/media/CMakeLists.txt 2015-03-16 15:38:27 +0000
65@@ -11,7 +11,7 @@
66
67 message(STATUS ${MEDIA_HUB_HEADERS})
68
69-add_subdirectory(call-monitor)
70+add_subdirectory(telephony)
71
72 add_library(
73 media-hub-common SHARED
74@@ -87,10 +87,14 @@
75 ${MPRIS_HEADERS}
76
77 client_death_observer.cpp
78- hybris_client_death_observer.cpp
79+ hashed_keyed_player_store.cpp
80+ hybris_client_death_observer.cpp
81 cover_art_resolver.cpp
82 engine.cpp
83
84+ apparmor/context.cpp
85+ apparmor/ubuntu.cpp
86+
87 audio/pulse_audio_output_observer.cpp
88 audio/ostream_reporter.cpp
89 audio/output_observer.cpp
90
91=== added directory 'src/core/media/apparmor'
92=== removed file 'src/core/media/apparmor.h'
93--- src/core/media/apparmor.h 2014-09-01 07:49:45 +0000
94+++ src/core/media/apparmor.h 1970-01-01 00:00:00 +0000
95@@ -1,93 +0,0 @@
96-/*
97- * Copyright (C) 2013-2014 Canonical Ltd
98- *
99- * This program is free software: you can redistribute it and/or modify
100- * it under the terms of the GNU Lesser General Public License version 3 as
101- * published by the Free Software Foundation.
102- *
103- * This program is distributed in the hope that it will be useful,
104- * but WITHOUT ANY WARRANTY; without even the implied warranty of
105- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
106- * GNU Lesser General Public License for more details.
107- *
108- * You should have received a copy of the GNU Lesser General Public License
109- * along with this program. If not, see <http://www.gnu.org/licenses/>.
110- *
111- * Author: Jim Hodapp <jim.hodapp@canonical.com>
112- */
113-
114-#ifndef APPARMOR_DBUS_H_
115-#define APPARMOR_DBUS_H_
116-
117-#include <core/dbus/macros.h>
118-#include <core/dbus/object.h>
119-#include <core/dbus/service.h>
120-
121-#include <string>
122-#include <chrono>
123-
124-// TODO(tvoss): This really should live in trust-store, providing a straightforward
125-// way for parties involved in managing trust relationships to query peers' apparmor
126-// profiles. Please see https://bugs.launchpad.net/trust-store/+bug/1350736 for the
127-// related bug
128-namespace org
129-{
130-namespace freedesktop
131-{
132-namespace dbus
133-{
134-struct DBus
135-{
136- static const std::string& name()
137- {
138- static const std::string s = "org.freedesktop.DBus";
139- return s;
140- }
141-
142- // Gets the AppArmor confinement string associated with the unique connection name. If
143- // D-Bus is not performing AppArmor mediation, the
144- // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
145- DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, DBus)
146-
147- struct Stub
148- {
149- // Creates a new stub instance for the given object to access
150- // DBus functionality.
151- Stub(const core::dbus::Object::Ptr& object) : object{object}
152- {
153- }
154-
155- // Creates a new stub instance for the given bus connection
156- Stub(const core::dbus::Bus::Ptr& bus)
157- : object
158- {
159- core::dbus::Service::use_service<org::freedesktop::dbus::DBus>(bus)
160- ->object_for_path(core::dbus::types::ObjectPath{"/org/freedesktop/DBus"})
161- }
162- {
163- }
164-
165- // Gets the AppArmor confinement string associated with the unique connection name. If
166- // D-Bus is not performing AppArmor mediation, the
167- // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
168- //
169- // Invokes the given handler on completion.
170- void get_connection_app_armor_security_async(
171- const std::string& name,
172- std::function<void(const std::string&)> handler)
173- {
174- object->invoke_method_asynchronously_with_callback<GetConnectionAppArmorSecurityContext, std::string>(
175- [handler](const core::dbus::Result<std::string>& result)
176- {
177- if (not result.is_error()) handler(result.value());
178- }, name);
179- }
180-
181- core::dbus::Object::Ptr object;
182- };
183-};
184-}
185-}
186-}
187-
188-#endif // APPARMOR_DBUS_H_
189
190=== added file 'src/core/media/apparmor/context.cpp'
191--- src/core/media/apparmor/context.cpp 1970-01-01 00:00:00 +0000
192+++ src/core/media/apparmor/context.cpp 2015-03-16 15:38:27 +0000
193@@ -0,0 +1,34 @@
194+/*
195+ * Copyright © 2014 Canonical Ltd.
196+ *
197+ * This program is free software: you can redistribute it and/or modify it
198+ * under the terms of the GNU Lesser General Public License version 3,
199+ * as published by the Free Software Foundation.
200+ *
201+ * This program is distributed in the hope that it will be useful,
202+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
203+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
204+ * GNU Lesser General Public License for more details.
205+ *
206+ * You should have received a copy of the GNU Lesser General Public License
207+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
208+ *
209+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
210+ */
211+
212+#include <core/media/apparmor/context.h>
213+
214+namespace apparmor = core::ubuntu::media::apparmor;
215+
216+apparmor::Context::Context(const std::string& name) : name{name}
217+{
218+ if (name.empty()) throw std::runtime_error
219+ {
220+ "apparmor::Context cannot be created for empty name."
221+ };
222+}
223+
224+const std::string& apparmor::Context::str() const
225+{
226+ return name;
227+}
228
229=== added file 'src/core/media/apparmor/context.h'
230--- src/core/media/apparmor/context.h 1970-01-01 00:00:00 +0000
231+++ src/core/media/apparmor/context.h 2015-03-16 15:38:27 +0000
232@@ -0,0 +1,51 @@
233+/*
234+ * Copyright © 2014 Canonical Ltd.
235+ *
236+ * This program is free software: you can redistribute it and/or modify it
237+ * under the terms of the GNU Lesser General Public License version 3,
238+ * as published by the Free Software Foundation.
239+ *
240+ * This program is distributed in the hope that it will be useful,
241+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
242+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
243+ * GNU Lesser General Public License for more details.
244+ *
245+ * You should have received a copy of the GNU Lesser General Public License
246+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
247+ *
248+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
249+ */
250+#ifndef CORE_UBUNTU_MEDIA_APPARMOR_AUTHENTICATOR_H_
251+#define CORE_UBUNTU_MEDIA_APPARMOR_AUTHENTICATOR_H_
252+
253+#include <memory>
254+#include <string>
255+
256+namespace core
257+{
258+namespace ubuntu
259+{
260+namespace media
261+{
262+namespace apparmor
263+{
264+// Models an apparmor context name, and provides convenience functionality
265+// on top of it.
266+class Context
267+{
268+public:
269+ // Constructs a new Context instance for the given raw name.
270+ // Throws std::logic_error for empty names.
271+ explicit Context(const std::string& name);
272+ virtual ~Context() = default;
273+ // Returns the raw string describing the context.
274+ const std::string& str() const;
275+
276+private:
277+ const std::string name;
278+};
279+}
280+}
281+}
282+}
283+#endif // CORE_UBUNTU_MEDIA_APPARMOR_AUTHENTICATOR_H_
284
285=== added file 'src/core/media/apparmor/dbus.h'
286--- src/core/media/apparmor/dbus.h 1970-01-01 00:00:00 +0000
287+++ src/core/media/apparmor/dbus.h 2015-03-16 15:38:27 +0000
288@@ -0,0 +1,94 @@
289+/*
290+ * Copyright (C) 2013-2014 Canonical Ltd
291+ *
292+ * This program is free software: you can redistribute it and/or modify
293+ * it under the terms of the GNU Lesser General Public License version 3 as
294+ * published by the Free Software Foundation.
295+ *
296+ * This program is distributed in the hope that it will be useful,
297+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
298+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
299+ * GNU Lesser General Public License for more details.
300+ *
301+ * You should have received a copy of the GNU Lesser General Public License
302+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
303+ *
304+ * Author: Jim Hodapp <jim.hodapp@canonical.com>
305+ */
306+
307+#ifndef CORE_UBUNTU_MEDIA_APPARMOR_DBUS_H_
308+#define CORE_UBUNTU_MEDIA_APPARMOR_DBUS_H_
309+
310+#include <core/dbus/bus.h>
311+#include <core/dbus/macros.h>
312+#include <core/dbus/object.h>
313+#include <core/dbus/service.h>
314+
315+#include <string>
316+#include <chrono>
317+
318+// TODO(tvoss): This really should live in trust-store, providing a straightforward
319+// way for parties involved in managing trust relationships to query peers' apparmor
320+// profiles. Please see https://bugs.launchpad.net/trust-store/+bug/1350736 for the
321+// related bug
322+namespace org
323+{
324+namespace freedesktop
325+{
326+namespace dbus
327+{
328+struct DBus
329+{
330+ static const std::string& name()
331+ {
332+ static const std::string s = "org.freedesktop.DBus";
333+ return s;
334+ }
335+
336+ // Gets the AppArmor confinement string associated with the unique connection name. If
337+ // D-Bus is not performing AppArmor mediation, the
338+ // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
339+ DBUS_CPP_METHOD_DEF(GetConnectionAppArmorSecurityContext, DBus)
340+
341+ struct Stub
342+ {
343+ // Creates a new stub instance for the given object to access
344+ // DBus functionality.
345+ Stub(const core::dbus::Object::Ptr& object) : object{object}
346+ {
347+ }
348+
349+ // Creates a new stub instance for the given bus connection
350+ Stub(const core::dbus::Bus::Ptr& bus)
351+ : object
352+ {
353+ core::dbus::Service::use_service<org::freedesktop::dbus::DBus>(bus)
354+ ->object_for_path(core::dbus::types::ObjectPath{"/org/freedesktop/DBus"})
355+ }
356+ {
357+ }
358+
359+ // Gets the AppArmor confinement string associated with the unique connection name. If
360+ // D-Bus is not performing AppArmor mediation, the
361+ // org.freedesktop.DBus.Error.AppArmorSecurityContextUnknown error is returned.
362+ //
363+ // Invokes the given handler on completion.
364+ void get_connection_app_armor_security_async(
365+ const std::string& name,
366+ std::function<void(const std::string&)> handler)
367+ {
368+ object->invoke_method_asynchronously_with_callback<GetConnectionAppArmorSecurityContext, std::string>(
369+ [handler](const core::dbus::Result<std::string>& result)
370+ {
371+ if (not result.is_error()) handler(result.value());
372+ }, name);
373+ }
374+
375+ core::dbus::Object::Ptr object;
376+ };
377+};
378+}
379+}
380+}
381+
382+#endif // CORE_UBUNTU_MEDIA_APPARMOR_DBUS_H_
383
384=== added file 'src/core/media/apparmor/ubuntu.cpp'
385--- src/core/media/apparmor/ubuntu.cpp 1970-01-01 00:00:00 +0000
386+++ src/core/media/apparmor/ubuntu.cpp 2015-03-16 15:38:27 +0000
387@@ -0,0 +1,198 @@
388+/*
389+ * Copyright © 2014 Canonical Ltd.
390+ *
391+ * This program is free software: you can redistribute it and/or modify it
392+ * under the terms of the GNU Lesser General Public License version 3,
393+ * as published by the Free Software Foundation.
394+ *
395+ * This program is distributed in the hope that it will be useful,
396+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
397+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
398+ * GNU Lesser General Public License for more details.
399+ *
400+ * You should have received a copy of the GNU Lesser General Public License
401+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
402+ *
403+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
404+ */
405+
406+#include <core/media/apparmor/ubuntu.h>
407+
408+#include <core/media/external_services.h>
409+
410+#include <iostream>
411+#include <regex>
412+
413+namespace apparmor = core::ubuntu::media::apparmor;
414+namespace media = core::ubuntu::media;
415+namespace ubuntu = apparmor::ubuntu;
416+
417+namespace
418+{
419+struct Uri
420+{
421+ std::string scheme;
422+ std::string authority;
423+ std::string path;
424+ std::string query;
425+ std::string fragment;
426+};
427+
428+// Poor mans version of a uri parser.
429+// See https://tools.ietf.org/html/rfc3986#appendix-B
430+Uri parse_uri(const std::string& s)
431+{
432+ // Indices into the regex match go here.
433+ struct Index
434+ {
435+ const std::size_t scheme{2};
436+ const std::size_t authority{4};
437+ const std::size_t path{5};
438+ const std::size_t query{7};
439+ const std::size_t fragment{9};
440+ } static index;
441+
442+ static const std::regex regex{R"delim(^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)delim"};
443+ std::smatch match;
444+
445+ if (not std::regex_match(s, match, regex)) throw std::runtime_error
446+ {
447+ "Not a valid URI: " + s
448+ };
449+
450+ return Uri
451+ {
452+ match.str(index.scheme),
453+ match.str(index.authority),
454+ match.str(index.path),
455+ match.str(index.query),
456+ match.str(index.fragment)
457+ };
458+}
459+
460+static constexpr std::size_t index_package{1};
461+static constexpr std::size_t index_app{2};
462+
463+// Returns true if the context name is a valid Ubuntu app id.
464+// If it is, out is populated with the package and app name.
465+bool process_context_name(const std::string& s, std::smatch& out)
466+{
467+ // See https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId.
468+ static const std::regex short_re{"(.*)_(.*)"};
469+ static const std::regex full_re{"(.*)_(.*)_(.*)"};
470+
471+ if (std::regex_match(s, out, full_re))
472+ return true;
473+
474+ if (std::regex_match(s, out, short_re))
475+ return true;
476+
477+ return false;
478+}
479+}
480+
481+apparmor::ubuntu::Context::Context(const std::string& name)
482+ : apparmor::Context{name},
483+ unconfined_{str() == ubuntu::unconfined},
484+ has_package_name_{process_context_name(str(), match_)}
485+{
486+ if (not is_unconfined() && not has_package_name()) throw std::logic_error
487+ {
488+ "apparmor::ubuntu::Context: Invalid profile name " + str()
489+ };
490+}
491+
492+bool apparmor::ubuntu::Context::is_unconfined() const
493+{
494+ return unconfined_;
495+}
496+
497+bool apparmor::ubuntu::Context::has_package_name() const
498+{
499+ return has_package_name_;
500+}
501+
502+
503+std::string apparmor::ubuntu::Context::package_name() const
504+{
505+ return std::string{match_[index_package]};
506+}
507+
508+apparmor::ubuntu::DBusDaemonRequestContextResolver::DBusDaemonRequestContextResolver(const core::dbus::Bus::Ptr& bus) : dbus_daemon{bus}
509+{
510+}
511+
512+void apparmor::ubuntu::DBusDaemonRequestContextResolver::resolve_context_for_dbus_name_async(
513+ const std::string& name,
514+ apparmor::ubuntu::RequestContextResolver::ResolveCallback cb)
515+{
516+ dbus_daemon.get_connection_app_armor_security_async(name, [cb](const std::string& context_name)
517+ {
518+ cb(apparmor::ubuntu::Context{context_name});
519+ });
520+}
521+
522+apparmor::ubuntu::RequestAuthenticator::Result apparmor::ubuntu::ExistingAuthenticator::authenticate_open_uri_request(const apparmor::ubuntu::Context& context, const std::string& uri)
523+{
524+ if (context.is_unconfined())
525+ return Result{true, "Client allowed access since it's unconfined"};
526+
527+ Uri parsed_uri = parse_uri(uri);
528+
529+ // All confined apps can access their own files
530+ if (parsed_uri.path.find(std::string(".local/share/" + context.package_name() + "/")) != std::string::npos ||
531+ parsed_uri.path.find(std::string(".cache/" + context.package_name() + "/")) != std::string::npos)
532+ {
533+ return Result
534+ {
535+ true,
536+ "Client can access content in ~/.local/share/" + context.package_name() + " or ~/.cache/" + context.package_name()
537+ };
538+ }
539+ else if (parsed_uri.path.find(std::string("opt/click.ubuntu.com/")) != std::string::npos &&
540+ parsed_uri.path.find(context.package_name()) != std::string::npos)
541+ {
542+ return Result{true, "Client can access content in own opt directory"};
543+ }
544+ else if ((parsed_uri.path.find(std::string("/system/media/audio/ui/")) != std::string::npos ||
545+ parsed_uri.path.find(std::string("/android/system/media/audio/ui/")) != std::string::npos) &&
546+ context.package_name() == "com.ubuntu.camera")
547+ {
548+ return Result{true, "Camera app can access ui sounds"};
549+ }
550+
551+ // TODO: Check if the trust store previously allowed direct access to uri
552+
553+ // Check in ~/Music and ~/Videos
554+ // TODO: when the trust store lands, check it to see if this app can access the dirs and
555+ // then remove the explicit whitelist of the music-app, and gallery-app
556+ else if ((context.package_name() == "com.ubuntu.music" || context.package_name() == "com.ubuntu.gallery") &&
557+ (parsed_uri.path.find(std::string("Music/")) != std::string::npos ||
558+ parsed_uri.path.find(std::string("Videos/")) != std::string::npos ||
559+ parsed_uri.path.find(std::string("/media")) != std::string::npos))
560+ {
561+ return Result{true, "Client can access content in ~/Music or ~/Videos"};
562+ }
563+ else if (parsed_uri.path.find(std::string("/usr/share/sounds")) != std::string::npos)
564+ {
565+ return Result{true, "Client can access content in /usr/share/sounds"};
566+ }
567+ else if (parsed_uri.scheme == "http" || parsed_uri.scheme == "rtsp")
568+ {
569+ return Result{true, "Client can access streaming content"};
570+ }
571+
572+ return Result{false, "Client is not allowed to access: " + uri};
573+}
574+
575+// Returns the platform-default implementation of RequestContextResolver.
576+apparmor::ubuntu::RequestContextResolver::Ptr apparmor::ubuntu::make_platform_default_request_context_resolver(media::helper::ExternalServices& es)
577+{
578+ return std::make_shared<apparmor::ubuntu::DBusDaemonRequestContextResolver>(es.session);
579+}
580+
581+// Returns the platform-default implementation of RequestAuthenticator.
582+apparmor::ubuntu::RequestAuthenticator::Ptr apparmor::ubuntu::make_platform_default_request_authenticator()
583+{
584+ return std::make_shared<apparmor::ubuntu::ExistingAuthenticator>();
585+}
586
587=== added file 'src/core/media/apparmor/ubuntu.h'
588--- src/core/media/apparmor/ubuntu.h 1970-01-01 00:00:00 +0000
589+++ src/core/media/apparmor/ubuntu.h 2015-03-16 15:38:27 +0000
590@@ -0,0 +1,167 @@
591+/*
592+ * Copyright © 2014 Canonical Ltd.
593+ *
594+ * This program is free software: you can redistribute it and/or modify it
595+ * under the terms of the GNU Lesser General Public License version 3,
596+ * as published by the Free Software Foundation.
597+ *
598+ * This program is distributed in the hope that it will be useful,
599+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
600+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
601+ * GNU Lesser General Public License for more details.
602+ *
603+ * You should have received a copy of the GNU Lesser General Public License
604+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
605+ *
606+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
607+ */
608+#ifndef CORE_UBUNTU_MEDIA_APPARMOR_UBUNTU_H_
609+#define CORE_UBUNTU_MEDIA_APPARMOR_UBUNTU_H_
610+
611+#include <core/media/apparmor/context.h>
612+#include <core/media/apparmor/dbus.h>
613+
614+#include <functional>
615+#include <memory>
616+#include <regex>
617+#include <string>
618+#include <vector>
619+
620+namespace core
621+{
622+namespace dbus
623+{
624+class Bus;
625+}
626+
627+namespace ubuntu
628+{
629+namespace media
630+{
631+namespace helper
632+{
633+struct ExternalServices;
634+}
635+namespace apparmor
636+{
637+// Collects Ubuntu-specific apparmor conventions, e.g., format
638+// of short and full package names as well as convenience functionality
639+// to inspect apparmor::Context instances.
640+namespace ubuntu
641+{
642+// The unconfined profile, unconditionally trusted
643+// by the system.
644+static constexpr const char* unconfined
645+{
646+ "unconfined"
647+};
648+
649+class Context : public apparmor::Context
650+{
651+public:
652+ // Constructs a new Context instance for the given raw name.
653+ // Throws std::logic_error for empty names or for names not
654+ // complying to Ubuntu conventions.
655+ Context(const std::string& name);
656+
657+ // Returns true iff the context is unconfined.
658+ virtual bool is_unconfined() const;
659+
660+ // Returns true iff the context contains a package name.
661+ virtual bool has_package_name() const;
662+
663+ // Returns the package name or throws if no package name can be found.
664+ virtual std::string package_name() const;
665+
666+private:
667+ std::smatch match_;
668+ const bool unconfined_;
669+ const bool has_package_name_;
670+};
671+
672+// Abstracts query for the apparmor context of an incoming request
673+class RequestContextResolver
674+{
675+public:
676+ // To save us some typing.
677+ typedef std::shared_ptr<RequestContextResolver> Ptr;
678+
679+ // Callback for resolve context operations.
680+ typedef std::function<void(const Context&)> ResolveCallback;
681+
682+ // Resolves the given name (of a dbus participant) to its apparmor context,
683+ // invoking the callback whenever a result is available.
684+ virtual void resolve_context_for_dbus_name_async(const std::string& name, ResolveCallback cb) = 0;
685+
686+protected:
687+ RequestContextResolver() = default;
688+ RequestContextResolver(const RequestContextResolver&) = delete;
689+ virtual ~RequestContextResolver() = default;
690+ RequestContextResolver& operator=(const RequestContextResolver&) = delete;
691+};
692+
693+// An implementation of RequestContextResolver that queries the dbus
694+// daemon to resolve the apparmor context.
695+class DBusDaemonRequestContextResolver : public RequestContextResolver
696+{
697+public:
698+ // To save us some typing.
699+ typedef std::shared_ptr<DBusDaemonRequestContextResolver> Ptr;
700+
701+ // Constructs a new instance for the given bus connection.
702+ DBusDaemonRequestContextResolver(const core::dbus::Bus::Ptr &);
703+
704+ // From RequestContextResolver
705+ void resolve_context_for_dbus_name_async(const std::string& name, ResolveCallback) override;
706+
707+private:
708+ org::freedesktop::dbus::DBus::Stub dbus_daemon;
709+};
710+
711+// Abstracts an apparmor-based authentication of
712+// incoming requests from clients.
713+class RequestAuthenticator
714+{
715+public:
716+ // To save us some typing.
717+ typedef std::shared_ptr<RequestAuthenticator> Ptr;
718+
719+ // Return type of an authentication call.
720+ typedef std::tuple
721+ <
722+ bool, // True if authenticated, false if not.
723+ std::string // Reason for the result.
724+ > Result;
725+
726+ virtual ~RequestAuthenticator() = default;
727+
728+ // Returns true iff the client identified by the given apparmor::Context is allowed
729+ // to access the given uri, false otherwise.
730+ virtual Result authenticate_open_uri_request(const Context&, const std::string& uri) = 0;
731+
732+protected:
733+ RequestAuthenticator() = default;
734+ RequestAuthenticator(const RequestAuthenticator&) = default;
735+ RequestAuthenticator& operator=(const RequestAuthenticator&) = default;
736+};
737+
738+// Takes the existing logic and exposes it as an implementation
739+// of the RequestAuthenticator interface.
740+struct ExistingAuthenticator : public RequestAuthenticator
741+{
742+ ExistingAuthenticator() = default;
743+ // From RequestAuthenticator
744+ Result authenticate_open_uri_request(const Context&, const std::string& uri) override;
745+};
746+
747+// Returns the platform-default implementation of RequestContextResolver.
748+RequestContextResolver::Ptr make_platform_default_request_context_resolver(helper::ExternalServices& es);
749+// Returns the platform-default implementation of RequestAuthenticator.
750+RequestAuthenticator::Ptr make_platform_default_request_authenticator();
751+}
752+}
753+}
754+}
755+}
756+
757+#endif // CORE_UBUNTU_MEDIA_APPARMOR_UBUNTU_H_
758
759=== modified file 'src/core/media/gstreamer/engine.cpp'
760--- src/core/media/gstreamer/engine.cpp 2015-03-16 15:38:27 +0000
761+++ src/core/media/gstreamer/engine.cpp 2015-03-16 15:38:27 +0000
762@@ -355,7 +355,7 @@
763 if (result)
764 {
765 d->state = media::Engine::State::playing;
766- cout << "play" << endl;
767+ cout << __PRETTY_FUNCTION__ << endl;
768 d->playback_status_changed(media::Player::PlaybackStatus::playing);
769 }
770
771@@ -373,7 +373,7 @@
772 if (result)
773 {
774 d->state = media::Engine::State::stopped;
775- cout << "stop" << endl;
776+ cout << __PRETTY_FUNCTION__ << endl;
777 d->playback_status_changed(media::Player::PlaybackStatus::stopped);
778 }
779
780@@ -387,7 +387,7 @@
781 if (result)
782 {
783 d->state = media::Engine::State::paused;
784- cout << "pause" << endl;
785+ cout << __PRETTY_FUNCTION__ << endl;
786 d->playback_status_changed(media::Player::PlaybackStatus::paused);
787 }
788
789
790=== modified file 'src/core/media/gstreamer/playbin.cpp'
791--- src/core/media/gstreamer/playbin.cpp 2015-03-16 15:38:27 +0000
792+++ src/core/media/gstreamer/playbin.cpp 2015-03-16 15:38:27 +0000
793@@ -97,11 +97,15 @@
794 this,
795 std::placeholders::_1))),
796 is_seeking(false),
797- player_lifetime(media::Player::Lifetime::normal)
798+ previous_position(0),
799+ player_lifetime(media::Player::Lifetime::normal),
800+ is_eos(false)
801 {
802 if (!pipeline)
803 throw std::runtime_error("Could not create pipeline for playbin.");
804
805+ is_eos = false;
806+
807 // Add audio and/or video sink elements depending on environment variables
808 // being set or not set
809 setup_pipeline_for_audio_video();
810@@ -199,6 +203,7 @@
811 }
812 break;
813 case GST_MESSAGE_EOS:
814+ is_eos = true;
815 signals.on_end_of_stream();
816 default:
817 break;
818@@ -334,6 +339,17 @@
819 int64_t pos = 0;
820 gst_element_query_position (pipeline, GST_FORMAT_TIME, &pos);
821
822+ // This prevents a 0 position from being reported to the app which happens while seeking.
823+ // This is covering over a GStreamer issue
824+ if ((static_cast<uint64_t>(pos) < duration()) && is_seeking && pos == 0)
825+ {
826+ return previous_position;
827+ }
828+
829+ // Save the current position to use just in case it's needed the next time position is
830+ // requested
831+ previous_position = static_cast<uint64_t>(pos);
832+
833 // FIXME: this should be int64_t, but dbus-cpp doesn't seem to handle it correctly
834 return static_cast<uint64_t>(pos);
835 }
836@@ -358,7 +374,7 @@
837 file_type = MEDIA_FILE_TYPE_VIDEO;
838 else if (is_audio_file(uri))
839 file_type = MEDIA_FILE_TYPE_AUDIO;
840-
841+
842 request_headers = headers;
843 }
844
845@@ -366,7 +382,7 @@
846 {
847 if (source == NULL || request_headers.empty())
848 return;
849-
850+
851 if (request_headers.find("Cookie") != request_headers.end()) {
852 if (g_object_class_find_property(G_OBJECT_GET_CLASS(source),
853 "cookies") != NULL) {
854@@ -375,7 +391,7 @@
855 g_strfreev(cookies);
856 }
857 }
858-
859+
860 if (request_headers.find("User-Agent") != request_headers.end()) {
861 if (g_object_class_find_property(G_OBJECT_GET_CLASS(source),
862 "user-agent") != NULL) {
863@@ -427,8 +443,9 @@
864 }
865
866 // We only should query the pipeline if we actually succeeded in
867- // setting the requested state.
868- if (result && new_state == GST_STATE_PLAYING)
869+ // setting the requested state. Also don't send on_video_dimensions_changed
870+ // signal during EOS.
871+ if (result && new_state == GST_STATE_PLAYING && !is_eos)
872 {
873 // Get the video height/width from the video sink
874 try
875
876=== modified file 'src/core/media/gstreamer/playbin.h'
877--- src/core/media/gstreamer/playbin.h 2015-03-16 15:38:27 +0000
878+++ src/core/media/gstreamer/playbin.h 2015-03-16 15:38:27 +0000
879@@ -112,8 +112,10 @@
880 GstElement* video_sink;
881 core::Connection on_new_message_connection;
882 bool is_seeking;
883+ mutable uint64_t previous_position;
884 core::ubuntu::media::Player::HeadersType request_headers;
885 core::ubuntu::media::Player::Lifetime player_lifetime;
886+ bool is_eos;
887 struct
888 {
889 core::Signal<void> about_to_finish;
890
891=== added file 'src/core/media/hashed_keyed_player_store.cpp'
892--- src/core/media/hashed_keyed_player_store.cpp 1970-01-01 00:00:00 +0000
893+++ src/core/media/hashed_keyed_player_store.cpp 2015-03-16 15:38:27 +0000
894@@ -0,0 +1,80 @@
895+/*
896+ * Copyright © 2014 Canonical Ltd.
897+ *
898+ * This program is free software: you can redistribute it and/or modify it
899+ * under the terms of the GNU Lesser General Public License version 3,
900+ * as published by the Free Software Foundation.
901+ *
902+ * This program is distributed in the hope that it will be useful,
903+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
904+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
905+ * GNU Lesser General Public License for more details.
906+ *
907+ * You should have received a copy of the GNU Lesser General Public License
908+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
909+ *
910+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
911+ */
912+
913+#include <core/media/hashed_keyed_player_store.h>
914+
915+namespace media = core::ubuntu::media;
916+
917+media::HashedKeyedPlayerStore::HashedKeyedPlayerStore()
918+{
919+}
920+
921+const core::Property<std::shared_ptr<media::Player>>& media::HashedKeyedPlayerStore::current_player() const
922+{
923+ return prop_current_player;
924+}
925+
926+bool media::HashedKeyedPlayerStore::has_player_for_key(const media::Player::PlayerKey& key) const
927+{
928+ std::lock_guard<std::mutex> lg{guard};
929+ return map.count(key) > 0;
930+}
931+
932+std::shared_ptr<media::Player> media::HashedKeyedPlayerStore::player_for_key(const media::Player::PlayerKey& key) const
933+{
934+ std::lock_guard<std::mutex> lg{guard};
935+ auto it = map.find(key);
936+
937+ if (it == map.end()) throw std::out_of_range
938+ {
939+ "HashedKeyedPlayerStore::player_for_key: No player known for " + std::to_string(key)
940+ };
941+
942+ return it->second;
943+}
944+
945+void media::HashedKeyedPlayerStore::enumerate_players(const media::KeyedPlayerStore::PlayerEnumerator& enumerator) const
946+{
947+ std::lock_guard<std::mutex> lg{guard};
948+ for (const auto& pair : map)
949+ enumerator(pair.first, pair.second);
950+}
951+
952+void media::HashedKeyedPlayerStore::add_player_for_key(const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
953+{
954+ std::lock_guard<std::mutex> lg{guard};
955+ map[key] = player;
956+}
957+
958+void media::HashedKeyedPlayerStore::remove_player_for_key(const media::Player::PlayerKey& key)
959+{
960+ std::lock_guard<std::mutex> lg{guard};
961+ auto it = map.find(key);
962+ if (it != map.end())
963+ {
964+ if (prop_current_player == it->second)
965+ prop_current_player = nullptr;
966+
967+ map.erase(it);
968+ }
969+}
970+
971+void media::HashedKeyedPlayerStore::set_current_player_for_key(const media::Player::PlayerKey& key)
972+{
973+ prop_current_player = player_for_key(key);
974+}
975
976=== added file 'src/core/media/hashed_keyed_player_store.h'
977--- src/core/media/hashed_keyed_player_store.h 1970-01-01 00:00:00 +0000
978+++ src/core/media/hashed_keyed_player_store.h 2015-03-16 15:38:27 +0000
979@@ -0,0 +1,76 @@
980+/*
981+ * Copyright © 2014 Canonical Ltd.
982+ *
983+ * This program is free software: you can redistribute it and/or modify it
984+ * under the terms of the GNU Lesser General Public License version 3,
985+ * as published by the Free Software Foundation.
986+ *
987+ * This program is distributed in the hope that it will be useful,
988+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
989+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
990+ * GNU Lesser General Public License for more details.
991+ *
992+ * You should have received a copy of the GNU Lesser General Public License
993+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
994+ *
995+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
996+ */
997+
998+#ifndef CORE_UBUNTU_MEDIA_HASHED_KEYED_PLAYER_STORE_H_
999+#define CORE_UBUNTU_MEDIA_HASHED_KEYED_PLAYER_STORE_H_
1000+
1001+#include <core/media/keyed_player_store.h>
1002+
1003+#include <mutex>
1004+#include <unordered_map>
1005+
1006+namespace core
1007+{
1008+namespace ubuntu
1009+{
1010+namespace media
1011+{
1012+// Implements KeyedPlayerStore using a std::unordered_map.
1013+class HashedKeyedPlayerStore : public KeyedPlayerStore
1014+{
1015+public:
1016+ HashedKeyedPlayerStore();
1017+ // We keep track of the "current" player, that is, the one
1018+ // that has been created most recently, or has been explicitly foregrounded, or has been enabled for
1019+ // background playback. We provide a getable/observable access to that designated instance.
1020+ const core::Property<std::shared_ptr<media::Player>>& current_player() const override;
1021+
1022+ // We keep track of all known player sessions here and render them accessible via
1023+ // the key. All of these functions are thread-safe but not reentrant.
1024+ // Returns true iff a player is known for the given key.
1025+ bool has_player_for_key(const Player::PlayerKey& key) const override;
1026+
1027+ // Returns the player for the given key or throws std::out_of_range if no player is known
1028+ // for the given key.
1029+ // Throws std::out_of_range if no player is known for the key.
1030+ std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const override;
1031+
1032+ // Enumerates all known players and invokes the given enumerator for each
1033+ // (key, player) pair.
1034+ void enumerate_players(const PlayerEnumerator& enumerator) const override;
1035+
1036+ // Adds the given player with the given key.
1037+ void add_player_for_key(const Player::PlayerKey& key, const std::shared_ptr<Player>& player) override;
1038+
1039+ // Removes the player for the given key, and unsets it if it is the current one.
1040+ void remove_player_for_key(const Player::PlayerKey& key) override;
1041+
1042+ // Makes the player known under the given key current.
1043+ // Throws std::out_of_range if no player is known for the key.
1044+ void set_current_player_for_key(const Player::PlayerKey& key) override;
1045+
1046+private:
1047+ core::Property<std::shared_ptr<Player>> prop_current_player;
1048+ mutable std::mutex guard;
1049+ std::unordered_map<Player::PlayerKey, std::shared_ptr<Player>> map;
1050+};
1051+}
1052+}
1053+}
1054+
1055+#endif // CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_
1056
1057=== added file 'src/core/media/keyed_player_store.cpp'
1058=== added file 'src/core/media/keyed_player_store.h'
1059--- src/core/media/keyed_player_store.h 1970-01-01 00:00:00 +0000
1060+++ src/core/media/keyed_player_store.h 2015-03-16 15:38:27 +0000
1061@@ -0,0 +1,83 @@
1062+/*
1063+ * Copyright © 2014 Canonical Ltd.
1064+ *
1065+ * This program is free software: you can redistribute it and/or modify it
1066+ * under the terms of the GNU Lesser General Public License version 3,
1067+ * as published by the Free Software Foundation.
1068+ *
1069+ * This program is distributed in the hope that it will be useful,
1070+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1071+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1072+ * GNU Lesser General Public License for more details.
1073+ *
1074+ * You should have received a copy of the GNU Lesser General Public License
1075+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1076+ *
1077+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1078+ */
1079+
1080+#ifndef CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_
1081+#define CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_
1082+
1083+#include <core/media/player.h>
1084+
1085+#include <core/property.h>
1086+
1087+#include <functional>
1088+#include <memory>
1089+
1090+namespace core
1091+{
1092+namespace ubuntu
1093+{
1094+namespace media
1095+{
1096+// An interface abstracting keyed lookups of known Player instances.
1097+class KeyedPlayerStore
1098+{
1099+public:
1100+ // Save us some typing.
1101+ typedef std::shared_ptr<KeyedPlayerStore> Ptr;
1102+ // Functor for enumerating all known (key, player) pairs.
1103+ typedef std::function
1104+ <
1105+ void(
1106+ // The key of the player.
1107+ const Player::PlayerKey&,
1108+ // The actual player instance.
1109+ const std::shared_ptr<Player>&
1110+ )
1111+ > PlayerEnumerator;
1112+ // We keep track of the "current" player, that is, the one
1113+ // that has been created most recently and provide a getable/observable
1114+ // access to that designated instance.
1115+ virtual const core::Property<std::shared_ptr<Player>>& current_player() const = 0;
1116+
1117+ // We keep track of all known player sessions here and render them accessible via
1118+ // the key. All of these functions are thread-safe but not reentrant.
1119+ // Returns true iff a player is known for the given key.
1120+ virtual bool has_player_for_key(const Player::PlayerKey& key) const = 0;
1121+ // Returns the player for the given key or throws std::out_of_range if no player is known
1122+ // for the given key.
1123+ virtual std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const = 0;
1124+ // Enumerates all known players and invokes the given enumerator for each
1125+ // (key, player) pair.
1126+ virtual void enumerate_players(const PlayerEnumerator& enumerator) const = 0;
1127+ // Adds the given player with the given key.
1128+ virtual void add_player_for_key(const Player::PlayerKey& key, const std::shared_ptr<Player>& player) = 0;
1129+ // Removes the player for the given key, and unsets it if it is the current one.
1130+ virtual void remove_player_for_key(const Player::PlayerKey& key) = 0;
1131+ // Makes the player known under the given key current.
1132+ virtual void set_current_player_for_key(const Player::PlayerKey& key) = 0;
1133+
1134+protected:
1135+ KeyedPlayerStore() = default;
1136+ KeyedPlayerStore(const KeyedPlayerStore&) = delete;
1137+ virtual ~KeyedPlayerStore() = default;
1138+ KeyedPlayerStore& operator=(const KeyedPlayerStore&) = delete;
1139+};
1140+}
1141+}
1142+}
1143+
1144+#endif // CORE_UBUNTU_MEDIA_KEYED_PLAYER_STORE_H_
1145
1146=== modified file 'src/core/media/mpris/player.h'
1147--- src/core/media/mpris/player.h 2015-03-16 15:38:27 +0000
1148+++ src/core/media/mpris/player.h 2015-03-16 15:38:27 +0000
1149@@ -136,6 +136,7 @@
1150 struct Signals
1151 {
1152 DBUS_CPP_SIGNAL_DEF(Seeked, Player, std::int64_t)
1153+ DBUS_CPP_SIGNAL_DEF(AboutToFinish, Player, void)
1154 DBUS_CPP_SIGNAL_DEF(EndOfStream, Player, void)
1155 DBUS_CPP_SIGNAL_DEF(PlaybackStatusChanged, Player, core::ubuntu::media::Player::PlaybackStatus)
1156 DBUS_CPP_SIGNAL_DEF(VideoDimensionChanged, Player, core::ubuntu::media::video::Dimensions)
1157@@ -246,6 +247,7 @@
1158 signals
1159 {
1160 configuration.object->template get_signal<Signals::Seeked>(),
1161+ configuration.object->template get_signal<Signals::AboutToFinish>(),
1162 configuration.object->template get_signal<Signals::EndOfStream>(),
1163 configuration.object->template get_signal<Signals::PlaybackStatusChanged>(),
1164 configuration.object->template get_signal<Signals::VideoDimensionChanged>(),
1165@@ -373,6 +375,7 @@
1166 struct
1167 {
1168 typename core::dbus::Signal<Signals::Seeked, Signals::Seeked::ArgumentType>::Ptr seeked_to;
1169+ typename core::dbus::Signal<Signals::AboutToFinish, Signals::AboutToFinish::ArgumentType>::Ptr about_to_finish;
1170 typename core::dbus::Signal<Signals::EndOfStream, Signals::EndOfStream::ArgumentType>::Ptr end_of_stream;
1171 typename core::dbus::Signal<Signals::PlaybackStatusChanged, Signals::PlaybackStatusChanged::ArgumentType>::Ptr playback_status_changed;
1172 typename core::dbus::Signal<Signals::VideoDimensionChanged, Signals::VideoDimensionChanged::ArgumentType>::Ptr video_dimension_changed;
1173
1174=== added file 'src/core/media/null_track_list.h'
1175--- src/core/media/null_track_list.h 1970-01-01 00:00:00 +0000
1176+++ src/core/media/null_track_list.h 2015-03-16 15:38:27 +0000
1177@@ -0,0 +1,114 @@
1178+/*
1179+ *
1180+ * This program is free software: you can redistribute it and/or modify it
1181+ * under the terms of the GNU Lesser General Public License version 3,
1182+ * as published by the Free Software Foundation.
1183+ *
1184+ * This program is distributed in the hope that it will be useful,
1185+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1186+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1187+ * GNU Lesser General Public License for more details.
1188+ *
1189+ * You should have received a copy of the GNU Lesser General Public License
1190+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1191+ *
1192+ * Authored by: Thomas Voß <thomas.voss@canonical.com>
1193+ */
1194+
1195+#ifndef CORE_MEDIA_NULL_TRACK_LIST_H_
1196+#define CORE_MEDIA_NULL_TRACK_LIST_H_
1197+
1198+#include <core/media/track.h>
1199+#include <core/media/track_list.h>
1200+
1201+namespace core
1202+{
1203+namespace ubuntu
1204+{
1205+namespace media
1206+{
1207+// A helper type to replace the playlist implementation below.
1208+// Please note that this type is only a temporary manner. Ideally,
1209+// the actual implementation should be injected as a dependency from the
1210+// outside.
1211+struct NullTrackList : public media::TrackList
1212+{
1213+ NullTrackList() = default;
1214+
1215+ bool has_next()
1216+ {
1217+ return false;
1218+ }
1219+
1220+ media::Track::Id next()
1221+ {
1222+ return media::Track::Id{};
1223+ }
1224+
1225+ media::Track::UriType query_uri_for_track(const media::Track::Id&)
1226+ {
1227+ return media::Track::UriType{};
1228+ }
1229+
1230+ const core::Property<bool>& can_edit_tracks() const override
1231+ {
1232+ return props_and_sigs.can_edit_tracks;
1233+ }
1234+
1235+ const core::Property<Container>& tracks() const override
1236+ {
1237+ return props_and_sigs.tracks;
1238+ }
1239+
1240+ virtual media::Track::MetaData query_meta_data_for_track(const media::Track::Id&) override
1241+ {
1242+ return media::Track::MetaData{};
1243+ }
1244+
1245+ void add_track_with_uri_at(const media::Track::UriType&, const media::Track::Id&, bool) override
1246+ {
1247+ }
1248+
1249+ void remove_track(const media::Track::Id&) override
1250+ {
1251+ }
1252+
1253+ void go_to(const media::Track::Id&) override
1254+ {
1255+ }
1256+
1257+ const core::Signal<void>& on_track_list_replaced() const override
1258+ {
1259+ return props_and_sigs.on_track_list_replaced;
1260+ }
1261+
1262+ const core::Signal<media::Track::Id>& on_track_added() const override
1263+ {
1264+ return props_and_sigs.on_track_added;
1265+ }
1266+
1267+ const core::Signal<media::Track::Id>& on_track_removed() const override
1268+ {
1269+ return props_and_sigs.on_track_removed;
1270+ }
1271+
1272+ const core::Signal<media::Track::Id>& on_track_changed() const override
1273+ {
1274+ return props_and_sigs.on_track_changed;
1275+ }
1276+
1277+ struct
1278+ {
1279+ core::Property<bool> can_edit_tracks;
1280+ core::Property<TrackList::Container> tracks;
1281+ core::Signal<void> on_track_list_replaced;
1282+ core::Signal<media::Track::Id> on_track_added;
1283+ core::Signal<media::Track::Id> on_track_removed;
1284+ core::Signal<media::Track::Id> on_track_changed;
1285+ } props_and_sigs;
1286+};
1287+}
1288+}
1289+}
1290+
1291+#endif // CORE_MEDIA_NULL_TRACK_LIST_H_
1292
1293=== modified file 'src/core/media/player.cpp'
1294--- src/core/media/player.cpp 2015-03-16 15:38:27 +0000
1295+++ src/core/media/player.cpp 2015-03-16 15:38:27 +0000
1296@@ -31,7 +31,6 @@
1297 {
1298 static const media::Player::Configuration config
1299 {
1300- std::string{""},
1301 0,
1302 nullptr,
1303 nullptr
1304
1305=== modified file 'src/core/media/player_configuration.h'
1306--- src/core/media/player_configuration.h 2014-09-09 10:28:32 +0000
1307+++ src/core/media/player_configuration.h 2015-03-16 15:38:27 +0000
1308@@ -27,9 +27,6 @@
1309 // to the implementation in a way that is opaque to the client.
1310 struct core::ubuntu::media::Player::Configuration
1311 {
1312- // An identifier that is helpful in referencing the player instance
1313- // across multiple services.
1314- std::string identity;
1315 // Unique key for identifying the session.
1316 core::ubuntu::media::Player::PlayerKey key;
1317 // The bus connection to expose objects on.
1318
1319=== modified file 'src/core/media/player_implementation.cpp'
1320--- src/core/media/player_implementation.cpp 2015-03-16 15:38:27 +0000
1321+++ src/core/media/player_implementation.cpp 2015-03-16 15:38:27 +0000
1322@@ -23,6 +23,7 @@
1323
1324 #include "client_death_observer.h"
1325 #include "engine.h"
1326+#include "null_track_list.h"
1327 #include "track_list_implementation.h"
1328
1329 #include "gstreamer/engine.h"
1330@@ -39,7 +40,8 @@
1331
1332 using namespace std;
1333
1334-struct media::PlayerImplementation::Private :
1335+template<typename Parent>
1336+struct media::PlayerImplementation<Parent>::Private :
1337 public std::enable_shared_from_this<Private>
1338 {
1339 enum class wakelock_clear_t
1340@@ -50,30 +52,18 @@
1341 WAKELOCK_CLEAR_INVALID
1342 };
1343
1344- Private(PlayerImplementation* parent, const media::PlayerImplementation::Configuration& config)
1345+ Private(PlayerImplementation* parent, const media::PlayerImplementation<Parent>::Configuration& config)
1346 : parent(parent),
1347 config(config),
1348 display_state_lock(config.power_state_controller->display_state_lock()),
1349 system_state_lock(config.power_state_controller->system_state_lock()),
1350 engine(std::make_shared<gstreamer::Engine>()),
1351- track_list(
1352- new media::TrackListImplementation(
1353- config.session->path().as_string() + "/TrackList",
1354- engine->meta_data_extractor())),
1355+ track_list(std::make_shared<NullTrackList>()),
1356 system_wakelock_count(0),
1357 display_wakelock_count(0),
1358 previous_state(Engine::State::stopped),
1359 engine_state_change_connection(engine->state().changed().connect(make_state_change_handler()))
1360 {
1361- config.client_death_observer->register_for_death_notifications_with_key(config.key);
1362- config.client_death_observer->on_client_with_key_died().connect([this](const media::Player::PlayerKey& died)
1363- {
1364- if (died != this->config.key)
1365- return;
1366-
1367- on_client_died();
1368- });
1369-
1370 // Poor man's logging of release/acquire events.
1371 display_state_lock->acquired().connect([](media::power::DisplayState state)
1372 {
1373@@ -207,7 +197,7 @@
1374 if (--system_wakelock_count == 0)
1375 {
1376 std::cout << "Clearing system wakelock." << std::endl;
1377- system_state_lock->request_release(media::power::SystemState::active);
1378+ system_state_lock->request_release(media::power::SystemState::active);
1379 }
1380 break;
1381 case wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY:
1382@@ -257,7 +247,7 @@
1383 // the execution of the functor may surpass the lifetime of this Private
1384 // object instance. By keeping a weak_ptr to the private object instance
1385 // we can check if the object is dead before calling methods on it
1386- std::weak_ptr<Private> weak_self{shared_from_this()};
1387+ std::weak_ptr<Private> weak_self{this->shared_from_this()};
1388 auto wakelock_type = current_wakelock_type();
1389 return [weak_self, wakelock_type] {
1390 if (auto self = weak_self.lock())
1391@@ -271,14 +261,14 @@
1392 }
1393
1394 // Our link back to our parent.
1395- media::PlayerImplementation* parent;
1396+ media::PlayerImplementation<Parent>* parent;
1397 // We just store the parameters passed on construction.
1398- media::PlayerImplementation::Configuration config;
1399+ media::PlayerImplementation<Parent>::Configuration config;
1400 media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock;
1401 media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock;
1402
1403 std::shared_ptr<Engine> engine;
1404- std::shared_ptr<TrackListImplementation> track_list;
1405+ std::shared_ptr<media::NullTrackList> track_list;
1406 std::atomic<int> system_wakelock_count;
1407 std::atomic<int> display_wakelock_count;
1408 Engine::State previous_state;
1409@@ -286,36 +276,29 @@
1410 core::Connection engine_state_change_connection;
1411 };
1412
1413-media::PlayerImplementation::PlayerImplementation(const media::PlayerImplementation::Configuration& config)
1414- : media::PlayerSkeleton
1415- {
1416- media::PlayerSkeleton::Configuration
1417- {
1418- config.bus,
1419- config.session,
1420- config.identity
1421- }
1422- },
1423+template<typename Parent>
1424+media::PlayerImplementation<Parent>::PlayerImplementation(const media::PlayerImplementation<Parent>::Configuration& config)
1425+ : Parent{config.parent},
1426 d{std::make_shared<Private>(this, config)}
1427 {
1428 // Initialize default values for Player interface properties
1429- can_play().set(true);
1430- can_pause().set(true);
1431- can_seek().set(true);
1432- can_go_previous().set(true);
1433- can_go_next().set(true);
1434- is_video_source().set(false);
1435- is_audio_source().set(false);
1436- is_shuffle().set(true);
1437- playback_rate().set(1.f);
1438- playback_status().set(Player::PlaybackStatus::null);
1439- loop_status().set(Player::LoopStatus::none);
1440- position().set(0);
1441- duration().set(0);
1442- audio_stream_role().set(Player::AudioStreamRole::multimedia);
1443+ Parent::can_play().set(true);
1444+ Parent::can_pause().set(true);
1445+ Parent::can_seek().set(true);
1446+ Parent::can_go_previous().set(true);
1447+ Parent::can_go_next().set(true);
1448+ Parent::is_video_source().set(false);
1449+ Parent::is_audio_source().set(false);
1450+ Parent::is_shuffle().set(true);
1451+ Parent::playback_rate().set(1.f);
1452+ Parent::playback_status().set(Player::PlaybackStatus::null);
1453+ Parent::loop_status().set(Player::LoopStatus::none);
1454+ Parent::position().set(0);
1455+ Parent::duration().set(0);
1456+ Parent::audio_stream_role().set(Player::AudioStreamRole::multimedia);
1457 d->engine->audio_stream_role().set(Player::AudioStreamRole::multimedia);
1458- orientation().set(Player::Orientation::rotate0);
1459- lifetime().set(Player::Lifetime::normal);
1460+ Parent::orientation().set(Player::Orientation::rotate0);
1461+ Parent::lifetime().set(Player::Lifetime::normal);
1462 d->engine->lifetime().set(Player::Lifetime::normal);
1463
1464 // Make sure that the Position property gets updated from the Engine
1465@@ -324,7 +307,7 @@
1466 {
1467 return d->engine->position().get();
1468 };
1469- position().install(position_getter);
1470+ Parent::position().install(position_getter);
1471
1472 // Make sure that the Duration property gets updated from the Engine
1473 // every time the client requests duration
1474@@ -332,23 +315,23 @@
1475 {
1476 return d->engine->duration().get();
1477 };
1478- duration().install(duration_getter);
1479+ Parent::duration().install(duration_getter);
1480
1481 std::function<bool()> video_type_getter = [this]()
1482 {
1483 return d->engine->is_video_source().get();
1484 };
1485- is_video_source().install(video_type_getter);
1486+ Parent::is_video_source().install(video_type_getter);
1487
1488 std::function<bool()> audio_type_getter = [this]()
1489 {
1490 return d->engine->is_audio_source().get();
1491 };
1492- is_audio_source().install(audio_type_getter);
1493+ Parent::is_audio_source().install(audio_type_getter);
1494
1495 // Make sure that the audio_stream_role property gets updated on the Engine side
1496 // whenever the client side sets the role
1497- audio_stream_role().changed().connect([this](media::Player::AudioStreamRole new_role)
1498+ Parent::audio_stream_role().changed().connect([this](media::Player::AudioStreamRole new_role)
1499 {
1500 d->engine->audio_stream_role().set(new_role);
1501 });
1502@@ -357,16 +340,18 @@
1503 // update the Player's cached value
1504 d->engine->orientation().changed().connect([this](const Player::Orientation& o)
1505 {
1506- orientation().set(o);
1507+ Parent::orientation().set(o);
1508 });
1509
1510- lifetime().changed().connect([this](media::Player::Lifetime lifetime)
1511+ Parent::lifetime().changed().connect([this](media::Player::Lifetime lifetime)
1512 {
1513 d->engine->lifetime().set(lifetime);
1514 });
1515
1516 d->engine->about_to_finish_signal().connect([this]()
1517 {
1518+ Parent::about_to_finish()();
1519+
1520 if (d->track_list->has_next())
1521 {
1522 Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
1523@@ -386,31 +371,52 @@
1524
1525 d->engine->seeked_to_signal().connect([this](uint64_t value)
1526 {
1527- seeked_to()(value);
1528+ Parent::seeked_to()(value);
1529 });
1530
1531 d->engine->end_of_stream_signal().connect([this]()
1532 {
1533- end_of_stream()();
1534+ Parent::end_of_stream()();
1535 });
1536
1537 d->engine->playback_status_changed_signal().connect([this](const Player::PlaybackStatus& status)
1538 {
1539- playback_status_changed()(status);
1540+ Parent::playback_status_changed()(status);
1541 });
1542
1543 d->engine->video_dimension_changed_signal().connect([this](const media::video::Dimensions& dimensions)
1544 {
1545- video_dimension_changed()(dimensions);
1546+ Parent::video_dimension_changed()(dimensions);
1547 });
1548
1549 d->engine->error_signal().connect([this](const Player::Error& e)
1550 {
1551- error()(e);
1552+ Parent::error()(e);
1553+ });
1554+
1555+ // Everything is setup, we now subscribe to death notifications.
1556+ std::weak_ptr<Private> wp{d};
1557+
1558+ d->config.client_death_observer->register_for_death_notifications_with_key(config.key);
1559+ d->config.client_death_observer->on_client_with_key_died().connect([wp](const media::Player::PlayerKey& died)
1560+ {
1561+ if (auto sp = wp.lock())
1562+ {
1563+ if (died != sp->config.key)
1564+ return;
1565+
1566+ static const std::chrono::milliseconds timeout{1000};
1567+ media::timeout(timeout.count(), true, [wp]()
1568+ {
1569+ if (auto sp = wp.lock())
1570+ sp->on_client_died();
1571+ });
1572+ }
1573 });
1574 }
1575
1576-media::PlayerImplementation::~PlayerImplementation()
1577+template<typename Parent>
1578+media::PlayerImplementation<Parent>::~PlayerImplementation()
1579 {
1580 // Install null getters as these properties may be destroyed
1581 // after the engine has been destroyed since they are owned by the
1582@@ -419,84 +425,101 @@
1583 {
1584 return static_cast<uint64_t>(0);
1585 };
1586- position().install(position_getter);
1587+ Parent::position().install(position_getter);
1588
1589 std::function<uint64_t()> duration_getter = [this]()
1590 {
1591 return static_cast<uint64_t>(0);
1592 };
1593- duration().install(duration_getter);
1594+ Parent::duration().install(duration_getter);
1595
1596 std::function<bool()> video_type_getter = [this]()
1597 {
1598 return false;
1599 };
1600- is_video_source().install(video_type_getter);
1601+ Parent::is_video_source().install(video_type_getter);
1602
1603 std::function<bool()> audio_type_getter = [this]()
1604 {
1605 return false;
1606 };
1607- is_audio_source().install(audio_type_getter);
1608+ Parent::is_audio_source().install(audio_type_getter);
1609 }
1610
1611-std::shared_ptr<media::TrackList> media::PlayerImplementation::track_list()
1612+template<typename Parent>
1613+std::shared_ptr<media::TrackList> media::PlayerImplementation<Parent>::track_list()
1614 {
1615 return d->track_list;
1616 }
1617
1618 // TODO: Convert this to be a property instead of sync call
1619-media::Player::PlayerKey media::PlayerImplementation::key() const
1620+template<typename Parent>
1621+media::Player::PlayerKey media::PlayerImplementation<Parent>::key() const
1622 {
1623 return d->config.key;
1624 }
1625
1626-media::video::Sink::Ptr media::PlayerImplementation::create_gl_texture_video_sink(std::uint32_t texture_id)
1627+template<typename Parent>
1628+media::video::Sink::Ptr media::PlayerImplementation<Parent>::create_gl_texture_video_sink(std::uint32_t texture_id)
1629 {
1630 d->engine->create_video_sink(texture_id);
1631 return media::video::Sink::Ptr{};
1632 }
1633
1634-bool media::PlayerImplementation::open_uri(const Track::UriType& uri)
1635+template<typename Parent>
1636+bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri)
1637 {
1638 return d->engine->open_resource_for_uri(uri);
1639 }
1640
1641-bool media::PlayerImplementation::open_uri(const Track::UriType& uri, const Player::HeadersType& headers)
1642+template<typename Parent>
1643+bool media::PlayerImplementation<Parent>::open_uri(const Track::UriType& uri, const Player::HeadersType& headers)
1644 {
1645 return d->engine->open_resource_for_uri(uri, headers);
1646 }
1647
1648-void media::PlayerImplementation::next()
1649-{
1650-}
1651-
1652-void media::PlayerImplementation::previous()
1653-{
1654-}
1655-
1656-void media::PlayerImplementation::play()
1657+template<typename Parent>
1658+void media::PlayerImplementation<Parent>::next()
1659+{
1660+}
1661+
1662+template<typename Parent>
1663+void media::PlayerImplementation<Parent>::previous()
1664+{
1665+}
1666+
1667+template<typename Parent>
1668+void media::PlayerImplementation<Parent>::play()
1669 {
1670 d->engine->play();
1671 }
1672
1673-void media::PlayerImplementation::pause()
1674+template<typename Parent>
1675+void media::PlayerImplementation<Parent>::pause()
1676 {
1677 d->engine->pause();
1678 }
1679
1680-void media::PlayerImplementation::stop()
1681+template<typename Parent>
1682+void media::PlayerImplementation<Parent>::stop()
1683 {
1684 std::cout << __PRETTY_FUNCTION__ << std::endl;
1685 d->engine->stop();
1686 }
1687
1688-void media::PlayerImplementation::seek_to(const std::chrono::microseconds& ms)
1689+template<typename Parent>
1690+void media::PlayerImplementation<Parent>::seek_to(const std::chrono::microseconds& ms)
1691 {
1692 d->engine->seek_to(ms);
1693 }
1694
1695-const core::Signal<>& media::PlayerImplementation::on_client_disconnected() const
1696+template<typename Parent>
1697+const core::Signal<>& media::PlayerImplementation<Parent>::on_client_disconnected() const
1698 {
1699 return d->on_client_disconnected;
1700 }
1701+
1702+#include <core/media/player_skeleton.h>
1703+
1704+// For linking purposes, we have to make sure that we have all symbols included within the dso.
1705+template class media::PlayerImplementation<media::PlayerSkeleton>;
1706
1707=== modified file 'src/core/media/player_implementation.h'
1708--- src/core/media/player_implementation.h 2015-03-16 15:38:27 +0000
1709+++ src/core/media/player_implementation.h 2015-03-16 15:38:27 +0000
1710@@ -19,8 +19,9 @@
1711 #ifndef CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_
1712 #define CORE_UBUNTU_MEDIA_PLAYER_IMPLEMENTATION_H_
1713
1714-#include "player_skeleton.h"
1715+#include <core/media/player.h>
1716
1717+#include "apparmor/ubuntu.h"
1718 #include "client_death_observer.h"
1719 #include "power/state_controller.h"
1720
1721@@ -35,18 +36,17 @@
1722 class Engine;
1723 class Service;
1724
1725-class PlayerImplementation : public PlayerSkeleton
1726+template<typename Parent>
1727+class PlayerImplementation : public Parent
1728 {
1729 public:
1730 // All creation time arguments go here
1731 struct Configuration
1732 {
1733- std::string identity;
1734- std::shared_ptr<core::dbus::Bus> bus;
1735- std::shared_ptr<core::dbus::Object> session;
1736- std::shared_ptr<Service> service;
1737- PlayerKey key;
1738-
1739+ // All creation time configuration options of the Parent class.
1740+ typename Parent::Configuration parent;
1741+ // The unique key identifying the player instance.
1742+ Player::PlayerKey key;
1743 // Functional dependencies
1744 ClientDeathObserver::Ptr client_death_observer;
1745 power::StateController::Ptr power_state_controller;
1746@@ -56,7 +56,7 @@
1747 ~PlayerImplementation();
1748
1749 virtual std::shared_ptr<TrackList> track_list();
1750- virtual PlayerKey key() const;
1751+ virtual Player::PlayerKey key() const;
1752
1753 virtual video::Sink::Ptr create_gl_texture_video_sink(std::uint32_t texture_id);
1754
1755
1756=== modified file 'src/core/media/player_skeleton.cpp'
1757--- src/core/media/player_skeleton.cpp 2015-03-16 15:38:27 +0000
1758+++ src/core/media/player_skeleton.cpp 2015-03-16 15:38:27 +0000
1759@@ -17,15 +17,16 @@
1760 * Jim Hodapp <jim.hodapp@canonical.com>
1761 */
1762
1763-#include "apparmor.h"
1764 #include "codec.h"
1765 #include "engine.h"
1766+#include "external_services.h"
1767 #include "player_skeleton.h"
1768 #include "player_traits.h"
1769 #include "property_stub.h"
1770 #include "the_session_bus.h"
1771 #include "xesam.h"
1772
1773+#include "apparmor/ubuntu.h"
1774 #include "mpris/media_player2.h"
1775 #include "mpris/metadata.h"
1776 #include "mpris/player.h"
1777@@ -44,19 +45,20 @@
1778 struct media::PlayerSkeleton::Private
1779 {
1780 Private(media::PlayerSkeleton* player,
1781- const std::string& identity,
1782 const std::shared_ptr<core::dbus::Bus>& bus,
1783- const std::shared_ptr<core::dbus::Object>& session)
1784+ const std::shared_ptr<core::dbus::Object>& session,
1785+ const apparmor::ubuntu::RequestContextResolver::Ptr& request_context_resolver,
1786+ const apparmor::ubuntu::RequestAuthenticator::Ptr& request_authenticator)
1787 : impl(player),
1788- identity(identity),
1789 bus(bus),
1790 object(session),
1791- apparmor_session(nullptr),
1792- dbus_stub{bus},
1793+ request_context_resolver{request_context_resolver},
1794+ request_authenticator{request_authenticator},
1795 skeleton{mpris::Player::Skeleton::Configuration{bus, session, mpris::Player::Skeleton::Configuration::Defaults{}}},
1796 signals
1797 {
1798 skeleton.signals.seeked_to,
1799+ skeleton.signals.about_to_finish,
1800 skeleton.signals.end_of_stream,
1801 skeleton.signals.playback_status_changed,
1802 skeleton.signals.video_dimension_changed,
1803@@ -163,82 +165,6 @@
1804 bus->send(reply);
1805 }
1806
1807- bool does_client_have_access(const std::string& context, const std::string& uri)
1808- {
1809- if (context.empty() || uri.empty())
1810- {
1811- std::cout << "Client denied access since context or uri are empty" << std::endl;
1812- return false;
1813- }
1814-
1815- if (context == "unconfined")
1816- {
1817- std::cout << "Client allowed access since it's unconfined" << std::endl;
1818- return true;
1819- }
1820-
1821- size_t pos = context.find_first_of('_');
1822- if (pos == std::string::npos)
1823- {
1824- std::cout << "Client denied access since it's an invalid apparmor security context" << std::endl;
1825- return false;
1826- }
1827-
1828- const std::string pkgname = context.substr(0, pos);
1829- std::cout << "client pkgname: " << pkgname << std::endl;
1830- std::cout << "uri: " << uri << std::endl;
1831-
1832- // All confined apps can access their own files
1833- if (uri.find(std::string(".local/share/" + pkgname + "/")) != std::string::npos
1834- || uri.find(std::string(".cache/" + pkgname + "/")) != std::string::npos)
1835- {
1836- std::cout << "Client can access content in ~/.local/share/" << pkgname << " or ~/.cache/" << pkgname << std::endl;
1837- return true;
1838- }
1839- else if (uri.find(std::string("opt/click.ubuntu.com/")) != std::string::npos
1840- && uri.find(pkgname) != std::string::npos)
1841- {
1842- std::cout << "Client can access content in own opt directory" << std::endl;
1843- return true;
1844- }
1845- else if ((uri.find(std::string("/system/media/audio/ui/")) != std::string::npos
1846- || uri.find(std::string("/android/system/media/audio/ui/")) != std::string::npos)
1847- && pkgname == "com.ubuntu.camera")
1848- {
1849- std::cout << "Camera app can access ui sounds" << std::endl;
1850- return true;
1851- }
1852- // TODO: Check if the trust store previously allowed direct access to uri
1853-
1854- // Check in ~/Music and ~/Videos
1855- // TODO: when the trust store lands, check it to see if this app can access the dirs and
1856- // then remove the explicit whitelist of the music-app, and gallery-app
1857- else if ((pkgname == "com.ubuntu.music" || pkgname == "com.ubuntu.gallery") &&
1858- (uri.find(std::string("Music/")) != std::string::npos
1859- || uri.find(std::string("Videos/")) != std::string::npos
1860- || uri.find(std::string("/media")) != std::string::npos))
1861- {
1862- std::cout << "Client can access content in ~/Music or ~/Videos" << std::endl;
1863- return true;
1864- }
1865- else if (uri.find(std::string("/usr/share/sounds")) != std::string::npos)
1866- {
1867- std::cout << "Client can access content in /usr/share/sounds" << std::endl;
1868- return true;
1869- }
1870- else if (uri.find(std::string("http://")) != std::string::npos
1871- || uri.find(std::string("rtsp://")) != std::string::npos)
1872- {
1873- std::cout << "Client can access streaming content" << std::endl;
1874- return true;
1875- }
1876- else
1877- {
1878- std::cout << "Client denied access to open_uri()" << std::endl;
1879- return false;
1880- }
1881- }
1882-
1883 void handle_key(const core::dbus::Message::Ptr& in)
1884 {
1885 auto reply = dbus::Message::make_method_return(in);
1886@@ -248,15 +174,15 @@
1887
1888 void handle_open_uri(const core::dbus::Message::Ptr& in)
1889 {
1890- dbus_stub.get_connection_app_armor_security_async(in->sender(), [this, in](const std::string& profile)
1891+ request_context_resolver->resolve_context_for_dbus_name_async(in->sender(), [this, in](const media::apparmor::ubuntu::Context& context)
1892 {
1893 Track::UriType uri;
1894 in->reader() >> uri;
1895
1896- bool have_access = does_client_have_access(profile, uri);
1897+ auto result = request_authenticator->authenticate_open_uri_request(context, uri);
1898
1899 auto reply = dbus::Message::make_method_return(in);
1900- reply->writer() << (have_access ? impl->open_uri(uri) : false);
1901+ reply->writer() << (std::get<0>(result) ? impl->open_uri(uri) : false);
1902
1903 bus->send(reply);
1904 });
1905@@ -264,16 +190,16 @@
1906
1907 void handle_open_uri_extended(const core::dbus::Message::Ptr& in)
1908 {
1909- dbus_stub.get_connection_app_armor_security_async(in->sender(), [this, in](const std::string& profile)
1910+ request_context_resolver->resolve_context_for_dbus_name_async(in->sender(), [this, in](const media::apparmor::ubuntu::Context& context)
1911 {
1912 Track::UriType uri;
1913 Player::HeadersType headers;
1914
1915 in->reader() >> uri >> headers;
1916
1917- bool have_access = does_client_have_access(profile, uri);
1918+ auto result = request_authenticator->authenticate_open_uri_request(context, uri);
1919 auto reply = dbus::Message::make_method_return(in);
1920- reply->writer() << (have_access ? impl->open_uri(uri, headers) : false);
1921+ reply->writer() << (std::get<0>(result) ? impl->open_uri(uri, headers) : false);
1922
1923 bus->send(reply);
1924 });
1925@@ -301,12 +227,10 @@
1926 }
1927
1928 media::PlayerSkeleton* impl;
1929- std::string identity;
1930 dbus::Bus::Ptr bus;
1931 dbus::Object::Ptr object;
1932- dbus::Object::Ptr apparmor_session;
1933-
1934- org::freedesktop::dbus::DBus::Stub dbus_stub;
1935+ media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
1936+ media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
1937
1938 mpris::Player::Skeleton skeleton;
1939
1940@@ -314,11 +238,13 @@
1941 {
1942 typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;
1943 typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
1944+ typedef core::dbus::Signal<mpris::Player::Signals::AboutToFinish, mpris::Player::Signals::AboutToFinish::ArgumentType> DBusAboutToFinishSignal;
1945 typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
1946 typedef core::dbus::Signal<mpris::Player::Signals::VideoDimensionChanged, mpris::Player::Signals::VideoDimensionChanged::ArgumentType> DBusVideoDimensionChangedSignal;
1947 typedef core::dbus::Signal<mpris::Player::Signals::Error, mpris::Player::Signals::Error::ArgumentType> DBusErrorSignal;
1948
1949 Signals(const std::shared_ptr<DBusSeekedToSignal>& remote_seeked,
1950+ const std::shared_ptr<DBusAboutToFinishSignal>& remote_atf,
1951 const std::shared_ptr<DBusEndOfStreamSignal>& remote_eos,
1952 const std::shared_ptr<DBusPlaybackStatusChangedSignal>& remote_playback_status_changed,
1953 const std::shared_ptr<DBusVideoDimensionChangedSignal>& remote_video_dimension_changed,
1954@@ -329,6 +255,11 @@
1955 remote_seeked->emit(value);
1956 });
1957
1958+ about_to_finish.connect([remote_atf]()
1959+ {
1960+ remote_atf->emit();
1961+ });
1962+
1963 end_of_stream.connect([remote_eos]()
1964 {
1965 remote_eos->emit();
1966@@ -351,6 +282,7 @@
1967 }
1968
1969 core::Signal<int64_t> seeked_to;
1970+ core::Signal<void> about_to_finish;
1971 core::Signal<void> end_of_stream;
1972 core::Signal<media::Player::PlaybackStatus> playback_status_changed;
1973 core::Signal<media::video::Dimensions> video_dimension_changed;
1974@@ -360,7 +292,7 @@
1975 };
1976
1977 media::PlayerSkeleton::PlayerSkeleton(const media::PlayerSkeleton::Configuration& config)
1978- : d(new Private{this, config.identity, config.bus, config.session})
1979+ : d(new Private{this, config.bus, config.session, config.request_context_resolver, config.request_authenticator})
1980 {
1981 // Setup method handlers for mpris::Player methods.
1982 auto next = std::bind(&Private::handle_next, d, std::placeholders::_1);
1983@@ -635,6 +567,16 @@
1984 return d->signals.seeked_to;
1985 }
1986
1987+const core::Signal<void>& media::PlayerSkeleton::about_to_finish() const
1988+{
1989+ return d->signals.about_to_finish;
1990+}
1991+
1992+core::Signal<void>& media::PlayerSkeleton::about_to_finish()
1993+{
1994+ return d->signals.about_to_finish;
1995+}
1996+
1997 const core::Signal<void>& media::PlayerSkeleton::end_of_stream() const
1998 {
1999 return d->signals.end_of_stream;
2000
2001=== modified file 'src/core/media/player_skeleton.h'
2002--- src/core/media/player_skeleton.h 2015-03-16 15:38:27 +0000
2003+++ src/core/media/player_skeleton.h 2015-03-16 15:38:27 +0000
2004@@ -24,6 +24,7 @@
2005
2006 #include "player_traits.h"
2007
2008+#include "apparmor/ubuntu.h"
2009 #include "mpris/player.h"
2010
2011 #include <core/dbus/skeleton.h>
2012@@ -37,11 +38,29 @@
2013 {
2014 namespace media
2015 {
2016+namespace helper
2017+{
2018+struct ExternalServices;
2019+}
2020+
2021 class Service;
2022
2023 class PlayerSkeleton : public core::ubuntu::media::Player
2024 {
2025 public:
2026+ // All creation time arguments go here.
2027+ struct Configuration
2028+ {
2029+ // The bus connection we are associated with.
2030+ std::shared_ptr<core::dbus::Bus> bus;
2031+ // The session object that we want to expose the skeleton upon.
2032+ std::shared_ptr<core::dbus::Object> session;
2033+ // Our functional dependencies.
2034+ apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
2035+ apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
2036+ };
2037+
2038+ PlayerSkeleton(const Configuration& configuration);
2039 ~PlayerSkeleton();
2040
2041 virtual const core::Property<bool>& can_play() const;
2042@@ -73,26 +92,11 @@
2043 virtual core::Property<Lifetime>& lifetime();
2044
2045 virtual const core::Signal<int64_t>& seeked_to() const;
2046+ virtual const core::Signal<void>& about_to_finish() const;
2047 virtual const core::Signal<void>& end_of_stream() const;
2048- virtual core::Signal<PlaybackStatus>& playback_status_changed();
2049 virtual const core::Signal<video::Dimensions>& video_dimension_changed() const;
2050 virtual const core::Signal<Error>& error() const;
2051
2052-protected:
2053- // All creation time arguments go here.
2054- struct Configuration
2055- {
2056- // The bus connection we are associated with.
2057- std::shared_ptr<core::dbus::Bus> bus;
2058- // The session object that we want to expose the skeleton upon.
2059- std::shared_ptr<core::dbus::Object> session;
2060- // Our identity, an identifier we pass out to other parts of the system.
2061- // Defaults to the short app id (${PKG_NAME}_${APP}).
2062- std::string identity;
2063- };
2064-
2065- PlayerSkeleton(const Configuration& configuration);
2066-
2067 // These properties are not exposed to the client, but still need to be
2068 // able to be settable from within the Player:
2069 virtual core::Property<PlaybackStatus>& playback_status();
2070@@ -111,7 +115,9 @@
2071 virtual core::Property<Orientation>& orientation();
2072
2073 virtual core::Signal<int64_t>& seeked_to();
2074+ virtual core::Signal<void>& about_to_finish();
2075 virtual core::Signal<void>& end_of_stream();
2076+ virtual core::Signal<PlaybackStatus>& playback_status_changed();
2077 virtual core::Signal<video::Dimensions>& video_dimension_changed();
2078 virtual core::Signal<Error>& error();
2079
2080
2081=== modified file 'src/core/media/player_stub.cpp'
2082--- src/core/media/player_stub.cpp 2015-03-16 15:38:27 +0000
2083+++ src/core/media/player_stub.cpp 2015-03-16 15:38:27 +0000
2084@@ -76,6 +76,7 @@
2085 signals
2086 {
2087 object->get_signal<mpris::Player::Signals::Seeked>(),
2088+ object->get_signal<mpris::Player::Signals::AboutToFinish>(),
2089 object->get_signal<mpris::Player::Signals::EndOfStream>(),
2090 object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>(),
2091 object->get_signal<mpris::Player::Signals::VideoDimensionChanged>(),
2092@@ -122,17 +123,20 @@
2093 struct Signals
2094 {
2095 typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;
2096+ typedef core::dbus::Signal<mpris::Player::Signals::AboutToFinish, mpris::Player::Signals::AboutToFinish::ArgumentType> DBusAboutToFinishSignal;
2097 typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
2098 typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
2099 typedef core::dbus::Signal<mpris::Player::Signals::VideoDimensionChanged, mpris::Player::Signals::VideoDimensionChanged::ArgumentType> DBusVideoDimensionChangedSignal;
2100 typedef core::dbus::Signal<mpris::Player::Signals::Error, mpris::Player::Signals::Error::ArgumentType> DBusErrorSignal;
2101
2102 Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked,
2103+ const std::shared_ptr<DBusAboutToFinishSignal>& atf,
2104 const std::shared_ptr<DBusEndOfStreamSignal>& eos,
2105 const std::shared_ptr<DBusPlaybackStatusChangedSignal>& status,
2106 const std::shared_ptr<DBusVideoDimensionChangedSignal>& d,
2107 const std::shared_ptr<DBusErrorSignal>& e)
2108 : seeked_to(),
2109+ about_to_finish(),
2110 end_of_stream(),
2111 playback_status_changed(),
2112 video_dimension_changed(),
2113@@ -140,6 +144,7 @@
2114 dbus
2115 {
2116 seeked,
2117+ atf,
2118 eos,
2119 status,
2120 d,
2121@@ -152,6 +157,12 @@
2122 seeked_to(value);
2123 });
2124
2125+ dbus.about_to_finish->connect([this]()
2126+ {
2127+ std::cout << "AboutToFinish signal arrived via the bus." << std::endl;
2128+ about_to_finish();
2129+ });
2130+
2131 dbus.end_of_stream->connect([this]()
2132 {
2133 std::cout << "EndOfStream signal arrived via the bus." << std::endl;
2134@@ -178,6 +189,7 @@
2135 }
2136
2137 core::Signal<int64_t> seeked_to;
2138+ core::Signal<void> about_to_finish;
2139 core::Signal<void> end_of_stream;
2140 core::Signal<media::Player::PlaybackStatus> playback_status_changed;
2141 core::Signal<media::video::Dimensions> video_dimension_changed;
2142@@ -186,6 +198,7 @@
2143 struct DBus
2144 {
2145 std::shared_ptr<DBusSeekedToSignal> seeked_to;
2146+ std::shared_ptr<DBusAboutToFinishSignal> about_to_finish;
2147 std::shared_ptr<DBusEndOfStreamSignal> end_of_stream;
2148 std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed;
2149 std::shared_ptr<DBusVideoDimensionChangedSignal> video_dimension_changed;
2150@@ -434,6 +447,11 @@
2151 return d->signals.seeked_to;
2152 }
2153
2154+const core::Signal<void>& media::PlayerStub::about_to_finish() const
2155+{
2156+ return d->signals.about_to_finish;
2157+}
2158+
2159 const core::Signal<void>& media::PlayerStub::end_of_stream() const
2160 {
2161 return d->signals.end_of_stream;
2162
2163=== modified file 'src/core/media/player_stub.h'
2164--- src/core/media/player_stub.h 2015-03-16 15:38:27 +0000
2165+++ src/core/media/player_stub.h 2015-03-16 15:38:27 +0000
2166@@ -86,6 +86,7 @@
2167 virtual core::Property<Lifetime>& lifetime();
2168
2169 virtual const core::Signal<int64_t>& seeked_to() const;
2170+ virtual const core::Signal<void>& about_to_finish() const;
2171 virtual const core::Signal<void>& end_of_stream() const;
2172 virtual core::Signal<PlaybackStatus>& playback_status_changed();
2173 virtual const core::Signal<video::Dimensions>& video_dimension_changed() const;
2174
2175=== modified file 'src/core/media/power/state_controller.cpp'
2176--- src/core/media/power/state_controller.cpp 2015-03-16 15:38:27 +0000
2177+++ src/core/media/power/state_controller.cpp 2015-03-16 15:38:27 +0000
2178@@ -128,42 +128,38 @@
2179 if (cookie == the_invalid_cookie)
2180 return;
2181
2182- std::weak_ptr<DisplayStateLock> wp{shared_from_this()};
2183+ // We make sure that we keep ourselves alive to make sure
2184+ // that release requests are always correctly issued.
2185+ auto sp = shared_from_this();
2186
2187 auto current_cookie(cookie);
2188
2189 timeout.expires_from_now(timeout_for_release());
2190- timeout.async_wait([wp, state, current_cookie](const boost::system::error_code& ec)
2191+ timeout.async_wait([sp, state, current_cookie](const boost::system::error_code& ec)
2192 {
2193 // We only return early from the timeout handler if the operation has been
2194 // explicitly aborted before.
2195 if (ec == boost::asio::error::operation_aborted)
2196 return;
2197
2198- if (auto sp = wp.lock())
2199- {
2200- sp->object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::removeDisplayOnRequest, void>(
2201- [wp, state, current_cookie](const core::dbus::Result<void>& result)
2202+ sp->object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::removeDisplayOnRequest, void>(
2203+ [sp, state, current_cookie](const core::dbus::Result<void>& result)
2204+ {
2205+ if (result.is_error())
2206 {
2207- if (result.is_error())
2208- {
2209- std::cerr << result.error().print() << std::endl;
2210- return;
2211- }
2212-
2213- if (auto sp = wp.lock())
2214- {
2215- sp->signals.released(state);
2216-
2217- // We might have issued a different request before and
2218- // only call the display state done if the original cookie
2219- // corresponds to the one we just gave up.
2220- if (sp->cookie == current_cookie)
2221- sp->cookie = the_invalid_cookie;
2222- }
2223-
2224- }, current_cookie);
2225- }
2226+ std::cerr << result.error().print() << std::endl;
2227+ return;
2228+ }
2229+
2230+ sp->signals.released(state);
2231+
2232+ // We might have issued a different request before and
2233+ // only call the display state done if the original cookie
2234+ // corresponds to the one we just gave up.
2235+ if (sp->cookie == current_cookie)
2236+ sp->cookie = the_invalid_cookie;
2237+
2238+ }, current_cookie);
2239 });
2240 }
2241
2242
2243=== modified file 'src/core/media/server/server.cpp'
2244--- src/core/media/server/server.cpp 2015-03-16 15:38:27 +0000
2245+++ src/core/media/server/server.cpp 2015-03-16 15:38:27 +0000
2246@@ -20,6 +20,7 @@
2247 #include <core/media/player.h>
2248 #include <core/media/track_list.h>
2249
2250+#include "core/media/hashed_keyed_player_store.h"
2251 #include "core/media/service_implementation.h"
2252
2253 #include <core/posix/signal.h>
2254@@ -103,23 +104,37 @@
2255 }
2256 };
2257
2258+ // Our common player store instance for tracking player instances.
2259+ auto player_store = std::make_shared<media::HashedKeyedPlayerStore>();
2260 // We assemble the configuration for executing the service now.
2261 media::ServiceImplementation::Configuration service_config
2262 {
2263+ std::make_shared<media::HashedKeyedPlayerStore>(),
2264 external_services
2265 };
2266
2267- auto service = std::make_shared<media::ServiceImplementation>(service_config);
2268+ auto impl = std::make_shared<media::ServiceImplementation>(media::ServiceImplementation::Configuration
2269+ {
2270+ player_store,
2271+ external_services
2272+ });
2273+
2274+ auto skeleton = std::make_shared<media::ServiceSkeleton>(media::ServiceSkeleton::Configuration
2275+ {
2276+ impl,
2277+ player_store,
2278+
2279+ });
2280
2281 std::thread service_worker
2282 {
2283- [&shutdown_requested, service]()
2284+ [&shutdown_requested, skeleton]()
2285 {
2286 while (not shutdown_requested)
2287 {
2288 try
2289 {
2290- service->run();
2291+ skeleton->run();
2292 }
2293 catch (const std::exception& e)
2294 {
2295@@ -142,7 +157,7 @@
2296 shutdown_requested = true;
2297
2298 // And stop execution of helper and actual service.
2299- service->stop();
2300+ skeleton->stop();
2301
2302 if (service_worker.joinable())
2303 service_worker.join();
2304
2305=== modified file 'src/core/media/service_implementation.cpp'
2306--- src/core/media/service_implementation.cpp 2015-03-16 15:38:27 +0000
2307+++ src/core/media/service_implementation.cpp 2015-03-16 15:38:27 +0000
2308@@ -22,14 +22,16 @@
2309
2310 #include "service_implementation.h"
2311
2312+#include "apparmor/ubuntu.h"
2313 #include "audio/output_observer.h"
2314 #include "client_death_observer.h"
2315-#include "call-monitor/call_monitor.h"
2316 #include "player_configuration.h"
2317+#include "player_skeleton.h"
2318 #include "player_implementation.h"
2319 #include "power/battery_observer.h"
2320 #include "power/state_controller.h"
2321 #include "recorder_observer.h"
2322+#include "telephony/call_monitor.h"
2323
2324 #include <boost/asio.hpp>
2325
2326@@ -59,8 +61,10 @@
2327 client_death_observer(media::platform_default_client_death_observer()),
2328 recorder_observer(media::make_platform_default_recorder_observer()),
2329 audio_output_observer(media::audio::make_platform_default_output_observer()),
2330+ request_context_resolver(media::apparmor::ubuntu::make_platform_default_request_context_resolver(configuration.external_services)),
2331+ request_authenticator(media::apparmor::ubuntu::make_platform_default_request_authenticator()),
2332 audio_output_state(media::audio::OutputState::Speaker),
2333- call_monitor(new CallMonitor)
2334+ call_monitor(media::telephony::make_platform_default_call_monitor())
2335 {
2336 }
2337
2338@@ -74,13 +78,16 @@
2339 media::ClientDeathObserver::Ptr client_death_observer;
2340 media::RecorderObserver::Ptr recorder_observer;
2341 media::audio::OutputObserver::Ptr audio_output_observer;
2342- media::audio::OutputObserver audio_output_state;
2343+ media::apparmor::ubuntu::RequestContextResolver::Ptr request_context_resolver;
2344+ media::apparmor::ubuntu::RequestAuthenticator::Ptr request_authenticator;
2345+ media::audio::OutputState audio_output_state;
2346
2347- std::unique_ptr<CallMonitor> call_monitor;
2348+ media::telephony::CallMonitor::Ptr call_monitor;
2349 std::list<media::Player::PlayerKey> paused_sessions;
2350 };
2351
2352-media::ServiceImplementation::ServiceImplementation(const Configuration& configuration) : d(new Private(configuration))
2353+media::ServiceImplementation::ServiceImplementation(const Configuration& configuration)
2354+ : d(new Private(configuration))
2355 {
2356 d->battery_observer->level().changed().connect([this](const media::power::Level& level)
2357 {
2358@@ -118,22 +125,20 @@
2359 break;
2360 case audio::OutputState::External:
2361 std::cout << "AudioOutputObserver reports that output is now External." << std::endl;
2362- if (d->audio_output_state == audio::OutputState::Earpiece)
2363- pause_all_multimedia_sessions();
2364 break;
2365 }
2366 d->audio_output_state = state;
2367 });
2368
2369- d->call_monitor->on_change([this](CallMonitor::State state) {
2370+ d->call_monitor->on_call_state_changed().connect([this](media::telephony::CallMonitor::State state)
2371+ {
2372 switch (state) {
2373- case CallMonitor::OffHook:
2374+ case media::telephony::CallMonitor::State::OffHook:
2375 std::cout << "Got call started signal, pausing all multimedia sessions" << std::endl;
2376 pause_all_multimedia_sessions();
2377 break;
2378- case CallMonitor::OnHook:
2379+ case media::telephony::CallMonitor::State::OnHook:
2380 std::cout << "Got call ended signal, resuming paused multimedia sessions" << std::endl;
2381- // Don't auto-resume any paused video playback sessions
2382 resume_paused_multimedia_sessions(false);
2383 break;
2384 }
2385@@ -160,12 +165,15 @@
2386 std::shared_ptr<media::Player> media::ServiceImplementation::create_session(
2387 const media::Player::Configuration& conf)
2388 {
2389- auto player = std::make_shared<media::PlayerImplementation>(media::PlayerImplementation::Configuration
2390+ auto player = std::make_shared<media::PlayerImplementation<media::PlayerSkeleton>>(media::PlayerImplementation<media::PlayerSkeleton>::Configuration
2391 {
2392- conf.identity,
2393- conf.bus,
2394- conf.session,
2395- shared_from_this(),
2396+ media::PlayerSkeleton::Configuration
2397+ {
2398+ conf.bus,
2399+ conf.session,
2400+ d->request_context_resolver,
2401+ d->request_authenticator
2402+ },
2403 conf.key,
2404 d->client_death_observer,
2405 d->power_state_controller
2406@@ -181,11 +189,11 @@
2407 // until all dispatches are done
2408 d->configuration.external_services.io_service.post([this, key]()
2409 {
2410- if (!has_player_for_key(key))
2411+ if (!d->configuration.player_store->has_player_for_key(key))
2412 return;
2413
2414- if (player_for_key(key)->lifetime() == Player::Lifetime::normal)
2415- remove_player_for_key(key);
2416+ if (d->configuration.player_store->player_for_key(key)->lifetime() == Player::Lifetime::normal)
2417+ d->configuration.player_store->remove_player_for_key(key);
2418 });
2419 });
2420
2421@@ -206,19 +214,19 @@
2422
2423 void media::ServiceImplementation::pause_other_sessions(media::Player::PlayerKey key)
2424 {
2425- if (not has_player_for_key(key))
2426+ if (not d->configuration.player_store->has_player_for_key(key))
2427 {
2428 cerr << "Could not find Player by key: " << key << endl;
2429 return;
2430 }
2431
2432- auto current_player = player_for_key(key);
2433+ auto current_player = d->configuration.player_store->player_for_key(key);
2434
2435 // We immediately make the player known as new current player.
2436 if (current_player->audio_stream_role() == media::Player::multimedia)
2437- set_current_player_for_key(key);
2438+ d->configuration.player_store->set_current_player_for_key(key);
2439
2440- enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player)
2441+ d->configuration.player_store->enumerate_players([current_player, key](const media::Player::PlayerKey& other_key, const std::shared_ptr<media::Player>& other_player)
2442 {
2443 // Only pause a Player if all of the following criteria are met:
2444 // 1) currently playing
2445@@ -238,7 +246,7 @@
2446
2447 void media::ServiceImplementation::pause_all_multimedia_sessions()
2448 {
2449- enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
2450+ d->configuration.player_store->enumerate_players([this](const media::Player::PlayerKey& key, const std::shared_ptr<media::Player>& player)
2451 {
2452 if (player->playback_status() == Player::playing
2453 && player->audio_stream_role() == media::Player::multimedia)
2454@@ -253,7 +261,7 @@
2455 void media::ServiceImplementation::resume_paused_multimedia_sessions(bool resume_video_sessions)
2456 {
2457 std::for_each(d->paused_sessions.begin(), d->paused_sessions.end(), [this, resume_video_sessions](const media::Player::PlayerKey& key) {
2458- auto player = player_for_key(key);
2459+ auto player = d->configuration.player_store->player_for_key(key);
2460 // Only resume video playback if explicitly desired
2461 if (resume_video_sessions || player->is_audio_source())
2462 player->play();
2463@@ -266,10 +274,10 @@
2464
2465 void media::ServiceImplementation::resume_multimedia_session()
2466 {
2467- if (not has_player_for_key(d->resume_key))
2468+ if (not d->configuration.player_store->has_player_for_key(d->resume_key))
2469 return;
2470
2471- auto player = player_for_key(d->resume_key);
2472+ auto player = d->configuration.player_store->player_for_key(d->resume_key);
2473
2474 if (player->playback_status() == Player::paused)
2475 {
2476
2477=== modified file 'src/core/media/service_implementation.h'
2478--- src/core/media/service_implementation.h 2015-03-16 15:38:27 +0000
2479+++ src/core/media/service_implementation.h 2015-03-16 15:38:27 +0000
2480@@ -30,12 +30,13 @@
2481 {
2482 class Player;
2483
2484-class ServiceImplementation : public ServiceSkeleton
2485+class ServiceImplementation : public Service
2486 {
2487 public:
2488 // All creation time arguments go here.
2489 struct Configuration
2490 {
2491+ KeyedPlayerStore::Ptr player_store;
2492 helper::ExternalServices& external_services;
2493 };
2494
2495
2496=== modified file 'src/core/media/service_skeleton.cpp'
2497--- src/core/media/service_skeleton.cpp 2014-11-18 20:29:26 +0000
2498+++ src/core/media/service_skeleton.cpp 2015-03-16 15:38:27 +0000
2499@@ -19,8 +19,6 @@
2500
2501 #include "service_skeleton.h"
2502
2503-#include "apparmor.h"
2504-
2505 #include "mpris/media_player2.h"
2506 #include "mpris/metadata.h"
2507 #include "mpris/player.h"
2508@@ -51,12 +49,12 @@
2509
2510 struct media::ServiceSkeleton::Private
2511 {
2512- Private(media::ServiceSkeleton* impl, const media::CoverArtResolver& resolver)
2513+ Private(media::ServiceSkeleton* impl, const ServiceSkeleton::Configuration& config)
2514 : impl(impl),
2515 object(impl->access_service()->add_object_for_path(
2516- dbus::traits::Service<media::Service>::object_path())),
2517- dbus_stub(impl->access_bus()),
2518- exported(impl->access_bus(), resolver)
2519+ dbus::traits::Service<media::Service>::object_path())),
2520+ exported(impl->access_bus(), config.cover_art_resolver),
2521+ configuration(config)
2522 {
2523 object->install_method_handler<mpris::Service::CreateSession>(
2524 std::bind(
2525@@ -99,129 +97,71 @@
2526 dbus::types::ObjectPath op{session_info.first};
2527 media::Player::PlayerKey key{session_info.second};
2528
2529- dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg, op, key](const std::string& profile)
2530- {
2531- media::Player::Configuration config
2532- {
2533- profile,
2534- key,
2535- impl->access_bus(),
2536- impl->access_service()->add_object_for_path(op)
2537- };
2538-
2539- try
2540- {
2541+ media::Player::Configuration config
2542+ {
2543+ key,
2544+ impl->access_bus(),
2545+ impl->access_service()->add_object_for_path(op)
2546+ };
2547+
2548+ try
2549+ {
2550+ configuration.player_store->add_player_for_key(key, impl->create_session(config));
2551+ auto reply = dbus::Message::make_method_return(msg);
2552+ reply->writer() << op;
2553+
2554+ impl->access_bus()->send(reply);
2555+ } catch(const std::runtime_error& e)
2556+ {
2557+ auto reply = dbus::Message::make_error(
2558+ msg,
2559+ mpris::Service::Errors::CreatingSession::name(),
2560+ e.what());
2561+ impl->access_bus()->send(reply);
2562+ }
2563+ }
2564+
2565+ void handle_create_fixed_session(const core::dbus::Message::Ptr& msg)
2566+ {
2567+ try
2568+ {
2569+ std::string name;
2570+ msg->reader() >> name;
2571+
2572+ if (named_player_map.count(name) == 0) {
2573+ // Create new session
2574+ auto session_info = create_session_info();
2575+
2576+ dbus::types::ObjectPath op{session_info.first};
2577+ media::Player::PlayerKey key{session_info.second};
2578+
2579+ media::Player::Configuration config
2580+ {
2581+ key,
2582+ impl->access_bus(),
2583+ impl->access_service()->add_object_for_path(op)
2584+ };
2585+
2586 auto session = impl->create_session(config);
2587-
2588- bool inserted = false;
2589- std::tie(std::ignore, inserted)
2590- = session_store.insert(std::make_pair(key, session));
2591-
2592- if (!inserted)
2593- throw std::runtime_error("Problem persisting session in session store.");
2594-
2595+ session->lifetime().set(media::Player::Lifetime::resumable);
2596+
2597+ configuration.player_store->add_player_for_key(key, session);
2598+
2599+
2600+ named_player_map.insert(std::make_pair(name, key));
2601
2602 auto reply = dbus::Message::make_method_return(msg);
2603 reply->writer() << op;
2604
2605 impl->access_bus()->send(reply);
2606- } catch(const std::runtime_error& e)
2607- {
2608- auto reply = dbus::Message::make_error(
2609- msg,
2610- mpris::Service::Errors::CreatingSession::name(),
2611- e.what());
2612- impl->access_bus()->send(reply);
2613- }
2614- });
2615- }
2616-
2617- void handle_create_fixed_session(const core::dbus::Message::Ptr& msg)
2618- {
2619- dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg](const std::string& profile)
2620- {
2621- try
2622- {
2623- std::string name;
2624- msg->reader() >> name;
2625-
2626- if (fixed_session_store.count(name) == 0) {
2627- // Create new session
2628- auto session_info = create_session_info();
2629-
2630- dbus::types::ObjectPath op{session_info.first};
2631- media::Player::PlayerKey key{session_info.second};
2632-
2633- media::Player::Configuration config
2634- {
2635- profile,
2636- key,
2637- impl->access_bus(),
2638- impl->access_service()->add_object_for_path(op)
2639- };
2640-
2641- auto session = impl->create_session(config);
2642- session->lifetime().set(media::Player::Lifetime::resumable);
2643-
2644- bool inserted = false;
2645- std::tie(std::ignore, inserted)
2646- = session_store.insert(std::make_pair(key, session));
2647-
2648- if (!inserted)
2649- throw std::runtime_error("Problem persisting session in session store.");
2650-
2651- fixed_session_store.insert(std::make_pair(name, key));
2652-
2653- auto reply = dbus::Message::make_method_return(msg);
2654- reply->writer() << op;
2655-
2656- impl->access_bus()->send(reply);
2657- }
2658- else {
2659- // Resume previous session
2660- auto key = fixed_session_store[name];
2661- if (session_store.count(key) == 0) {
2662- auto reply = dbus::Message::make_error(
2663- msg,
2664- mpris::Service::Errors::CreatingFixedSession::name(),
2665- "Unable to locate player session");
2666- impl->access_bus()->send(reply);
2667- return;
2668- }
2669-
2670- std::stringstream ss;
2671- ss << "/core/ubuntu/media/Service/sessions/" << key;
2672- dbus::types::ObjectPath op{ss.str()};
2673-
2674- auto reply = dbus::Message::make_method_return(msg);
2675- reply->writer() << op;
2676-
2677- impl->access_bus()->send(reply);
2678- }
2679- } catch(const std::runtime_error& e)
2680- {
2681- auto reply = dbus::Message::make_error(
2682- msg,
2683- mpris::Service::Errors::CreatingSession::name(),
2684- e.what());
2685- impl->access_bus()->send(reply);
2686- }
2687- });
2688- }
2689-
2690- void handle_resume_session(const core::dbus::Message::Ptr& msg)
2691- {
2692- dbus_stub.get_connection_app_armor_security_async(msg->sender(), [this, msg](const std::string&)
2693- {
2694- try
2695- {
2696- Player::PlayerKey key;
2697- msg->reader() >> key;
2698-
2699- if (session_store.count(key) == 0) {
2700+ }
2701+ else {
2702+ // Resume previous session
2703+ auto key = named_player_map.at(name);
2704+ if (not configuration.player_store->has_player_for_key(key)) {
2705 auto reply = dbus::Message::make_error(
2706 msg,
2707- mpris::Service::Errors::ResumingSession::name(),
2708+ mpris::Service::Errors::CreatingFixedSession::name(),
2709 "Unable to locate player session");
2710 impl->access_bus()->send(reply);
2711 return;
2712@@ -235,15 +175,49 @@
2713 reply->writer() << op;
2714
2715 impl->access_bus()->send(reply);
2716- } catch(const std::runtime_error& e)
2717- {
2718+ }
2719+ } catch(const std::runtime_error& e)
2720+ {
2721+ auto reply = dbus::Message::make_error(
2722+ msg,
2723+ mpris::Service::Errors::CreatingSession::name(),
2724+ e.what());
2725+ impl->access_bus()->send(reply);
2726+ }
2727+ }
2728+
2729+ void handle_resume_session(const core::dbus::Message::Ptr& msg)
2730+ {
2731+ try
2732+ {
2733+ Player::PlayerKey key;
2734+ msg->reader() >> key;
2735+
2736+ if (not configuration.player_store->has_player_for_key(key)) {
2737 auto reply = dbus::Message::make_error(
2738 msg,
2739- mpris::Service::Errors::CreatingSession::name(),
2740- e.what());
2741+ mpris::Service::Errors::ResumingSession::name(),
2742+ "Unable to locate player session");
2743 impl->access_bus()->send(reply);
2744+ return;
2745 }
2746- });
2747+
2748+ std::stringstream ss;
2749+ ss << "/core/ubuntu/media/Service/sessions/" << key;
2750+ dbus::types::ObjectPath op{ss.str()};
2751+
2752+ auto reply = dbus::Message::make_method_return(msg);
2753+ reply->writer() << op;
2754+
2755+ impl->access_bus()->send(reply);
2756+ } catch(const std::runtime_error& e)
2757+ {
2758+ auto reply = dbus::Message::make_error(
2759+ msg,
2760+ mpris::Service::Errors::CreatingSession::name(),
2761+ e.what());
2762+ impl->access_bus()->send(reply);
2763+ }
2764 }
2765
2766 void handle_pause_other_sessions(const core::dbus::Message::Ptr& msg)
2767@@ -260,11 +234,10 @@
2768 media::ServiceSkeleton* impl;
2769 dbus::Object::Ptr object;
2770
2771- // We query the apparmor profile to obtain an identity for players.
2772- org::freedesktop::dbus::DBus::Stub dbus_stub;
2773- // We track all running player instances.
2774- std::map<media::Player::PlayerKey, std::shared_ptr<media::Player>> session_store;
2775- std::map<std::string, media::Player::PlayerKey> fixed_session_store;
2776+ // We remember all our creation time arguments.
2777+ ServiceSkeleton::Configuration configuration;
2778+ // We map named/fixed player instances to their respective keys.
2779+ std::map<std::string, media::Player::PlayerKey> named_player_map;
2780 // We expose the entire service as an MPRIS player.
2781 struct Exported
2782 {
2783@@ -498,7 +471,7 @@
2784 mpris::Player::Skeleton player;
2785 mpris::Playlists::Skeleton playlists;
2786
2787- // Helper to resolve (title, artist, album) tuples to cover art.
2788+ // The CoverArtResolver used by the exported player.
2789 media::CoverArtResolver cover_art_resolver;
2790 // The actual player instance.
2791 std::weak_ptr<media::Player> current_player;
2792@@ -533,9 +506,9 @@
2793 } exported;
2794 };
2795
2796-media::ServiceSkeleton::ServiceSkeleton(const media::CoverArtResolver& resolver)
2797+media::ServiceSkeleton::ServiceSkeleton(const Configuration& configuration)
2798 : dbus::Skeleton<media::Service>(the_session_bus()),
2799- d(new Private(this, resolver))
2800+ d(new Private(this, configuration))
2801 {
2802 }
2803
2804@@ -543,46 +516,24 @@
2805 {
2806 }
2807
2808-bool media::ServiceSkeleton::has_player_for_key(const media::Player::PlayerKey& key) const
2809-{
2810- return d->session_store.count(key) > 0;
2811-}
2812-
2813-std::shared_ptr<media::Player> media::ServiceSkeleton::player_for_key(const media::Player::PlayerKey& key) const
2814-{
2815- return d->session_store.at(key);
2816-}
2817-
2818-void media::ServiceSkeleton::enumerate_players(const media::ServiceSkeleton::PlayerEnumerator& enumerator) const
2819-{
2820- for (const auto& pair : d->session_store)
2821- enumerator(pair.first, pair.second);
2822-}
2823-
2824-void media::ServiceSkeleton::set_current_player_for_key(const media::Player::PlayerKey& key)
2825-{
2826- if (not has_player_for_key(key))
2827- return;
2828-
2829- d->exported.set_current_player(player_for_key(key));
2830-}
2831-
2832-void media::ServiceSkeleton::remove_player_for_key(const media::Player::PlayerKey& key)
2833-{
2834- if (not has_player_for_key(key))
2835- return;
2836-
2837- auto player = player_for_key(key);
2838-
2839- d->session_store.erase(key);
2840- d->exported.unset_if_current(player);
2841- // All non-durable fixed sessions are also removed
2842- for (auto it: d->fixed_session_store) {
2843- if (it.second == key) {
2844- d->fixed_session_store.erase(it.first);
2845- break;
2846- }
2847- }
2848+std::shared_ptr<media::Player> media::ServiceSkeleton::create_session(const media::Player::Configuration& config)
2849+{
2850+ return d->configuration.impl->create_session(config);
2851+}
2852+
2853+std::shared_ptr<media::Player> media::ServiceSkeleton::create_fixed_session(const std::string& name, const media::Player::Configuration&config)
2854+{
2855+ return d->configuration.impl->create_fixed_session(name, config);
2856+}
2857+
2858+std::shared_ptr<media::Player> media::ServiceSkeleton::resume_session(media::Player::PlayerKey key)
2859+{
2860+ return d->configuration.impl->resume_session(key);
2861+}
2862+
2863+void media::ServiceSkeleton::pause_other_sessions(media::Player::PlayerKey key)
2864+{
2865+ d->configuration.impl->pause_other_sessions(key);
2866 }
2867
2868 void media::ServiceSkeleton::run()
2869
2870=== modified file 'src/core/media/service_skeleton.h'
2871--- src/core/media/service_skeleton.h 2014-09-09 21:27:29 +0000
2872+++ src/core/media/service_skeleton.h 2015-03-16 15:38:27 +0000
2873@@ -22,6 +22,7 @@
2874 #include <core/media/service.h>
2875
2876 #include "cover_art_resolver.h"
2877+#include "keyed_player_store.h"
2878 #include "service_traits.h"
2879
2880 #include <core/dbus/skeleton.h>
2881@@ -37,34 +38,22 @@
2882 class ServiceSkeleton : public core::dbus::Skeleton<core::ubuntu::media::Service>
2883 {
2884 public:
2885- // Functor for enumerating all known (key, player) pairs.
2886- typedef std::function
2887- <
2888- void(
2889- // The key of the player.
2890- const core::ubuntu::media::Player::PlayerKey&,
2891- // The actual player instance.
2892- const std::shared_ptr<core::ubuntu::media::Player>&
2893- )
2894- > PlayerEnumerator;
2895+ // Creation time arguments go here.
2896+ struct Configuration
2897+ {
2898+ std::shared_ptr<Service> impl;
2899+ KeyedPlayerStore::Ptr player_store;
2900+ CoverArtResolver cover_art_resolver;
2901+ };
2902
2903- ServiceSkeleton(const CoverArtResolver& cover_art_resolver = always_missing_cover_art_resolver());
2904+ ServiceSkeleton(const Configuration& configuration);
2905 ~ServiceSkeleton();
2906
2907- // We keep track of all known player sessions here and render them accessible via
2908- // the key. All of these functions are thread-safe but not reentrant.
2909- // Returns true iff a player is known for the given key.
2910- bool has_player_for_key(const Player::PlayerKey& key) const;
2911- // Returns the player for the given key or throws std::out_of_range if no player is known
2912- // for the given key.
2913- std::shared_ptr<Player> player_for_key(const Player::PlayerKey& key) const;
2914- // Enumerates all known players and invokes the given enumerator for each
2915- // (key, player) pair.
2916- void enumerate_players(const PlayerEnumerator& enumerator) const;
2917- // Removes the player for the given key, and unsets it if it is the current one.
2918- void remove_player_for_key(const Player::PlayerKey& key);
2919- // Makes the player known under the given key current.
2920- void set_current_player_for_key(const Player::PlayerKey& key);
2921+ // From media::Service
2922+ std::shared_ptr<Player> create_session(const Player::Configuration&);
2923+ std::shared_ptr<Player> create_fixed_session(const std::string& name, const Player::Configuration&);
2924+ std::shared_ptr<Player> resume_session(Player::PlayerKey);
2925+ void pause_other_sessions(Player::PlayerKey key);
2926
2927 void run();
2928 void stop();
2929
2930=== renamed directory 'src/core/media/call-monitor' => 'src/core/media/telephony'
2931=== modified file 'src/core/media/telephony/call_monitor.cpp'
2932--- src/core/media/call-monitor/call_monitor.cpp 2015-01-12 21:35:36 +0000
2933+++ src/core/media/telephony/call_monitor.cpp 2015-03-16 15:38:27 +0000
2934@@ -29,8 +29,12 @@
2935 #include <list>
2936 #include <mutex>
2937
2938+namespace media = core::ubuntu::media;
2939
2940-namespace {
2941+namespace
2942+{
2943+namespace impl
2944+{
2945 class TelepathyCallMonitor : public QObject
2946 {
2947 Q_OBJECT
2948@@ -73,7 +77,7 @@
2949 }
2950 }
2951
2952- void on_change(const std::function<void(CallMonitor::State)>& func) {
2953+ void on_change(const std::function<void(media::telephony::CallMonitor::State)>& func) {
2954 std::lock_guard<std::mutex> l(cb_lock);
2955 cb = func;
2956 }
2957@@ -131,19 +135,19 @@
2958 {
2959 std::lock_guard<std::mutex> l(cb_lock);
2960 if (cb)
2961- cb(CallMonitor::OffHook);
2962+ cb(media::telephony::CallMonitor::State::OffHook);
2963 }
2964
2965 void onHook()
2966 {
2967 std::lock_guard<std::mutex> l(cb_lock);
2968 if (cb)
2969- cb(CallMonitor::OnHook);
2970+ cb(media::telephony::CallMonitor::State::OnHook);
2971 }
2972
2973 private:
2974 std::mutex cb_lock;
2975- std::function<void (CallMonitor::State)> cb;
2976+ std::function<void (media::telephony::CallMonitor::State)> cb;
2977 Tp::AccountManagerPtr mAccountManager;
2978 std::list<TelepathyCallMonitor*> mCallMonitors;
2979
2980@@ -159,63 +163,78 @@
2981 mCallMonitors.push_back(tcm);
2982 }
2983 };
2984-}
2985
2986-class CallMonitorPrivate
2987+struct CallMonitor : public media::telephony::CallMonitor
2988 {
2989-public:
2990- CallMonitorPrivate() {
2991- mBridge = nullptr;
2992- try {
2993- std::thread([this]() {
2994- qt::core::world::build_and_run(0, nullptr, [this]() {
2995- qt::core::world::enter_with_task([this]() {
2996+ CallMonitor() : mBridge{nullptr}
2997+ {
2998+ try
2999+ {
3000+ qt_world = std::move(std::thread([this]()
3001+ {
3002+ qt::core::world::build_and_run(0, nullptr, [this]()
3003+ {
3004+ qt::core::world::enter_with_task([this]()
3005+ {
3006 std::cout << "CallMonitor: Creating TelepathyBridge" << std::endl;
3007 mBridge = new TelepathyBridge();
3008 cv.notify_all();
3009 });
3010 });
3011- }).detach();
3012+ }));
3013 } catch(const std::system_error& error) {
3014 std::cerr << "exception(std::system_error) in CallMonitor thread start" << error.what() << std::endl;
3015 } catch(...) {
3016 std::cerr << "exception(...) in CallMonitor thread start" << std::endl;
3017 }
3018+
3019 // Wait until telepathy bridge is set, so we can hook up the change signals
3020 std::unique_lock<std::mutex> lck(mtx);
3021 cv.wait_for(lck, std::chrono::seconds(3));
3022+
3023+ if (mBridge)
3024+ {
3025+ mBridge->on_change([this](CallMonitor::State state)
3026+ {
3027+ call_state_changed(state);
3028+ });
3029+ }
3030 }
3031
3032- ~CallMonitorPrivate() {
3033+ ~CallMonitor()
3034+ {
3035+ // We first clean up the bridge instance.
3036+ qt::core::world::enter_with_task([this]()
3037+ {
3038+ delete mBridge;
3039+ }).get();
3040+
3041+ // We then request destruction of the qt world.
3042 qt::core::world::destroy();
3043+
3044+ // Before we finally join the worker.
3045+ if (qt_world.joinable())
3046+ qt_world.join();
3047+ }
3048+
3049+ const core::Signal<media::telephony::CallMonitor::State>& on_call_state_changed() const
3050+ {
3051+ return call_state_changed;
3052 }
3053
3054 TelepathyBridge *mBridge;
3055+ core::Signal<media::telephony::CallMonitor::State> call_state_changed;
3056
3057-private:
3058+ std::thread qt_world;
3059 std::mutex mtx;
3060 std::condition_variable cv;
3061 };
3062-
3063-
3064-CallMonitor::CallMonitor():
3065- d(new CallMonitorPrivate)
3066-{
3067-}
3068-
3069-CallMonitor::~CallMonitor()
3070-{
3071- delete d->mBridge;
3072- delete d;
3073-}
3074-
3075-void CallMonitor::on_change(const std::function<void(CallMonitor::State)>& func)
3076-{
3077- if (d->mBridge != nullptr) {
3078- std::cout << "CallMonitor: Setting up callback for TelepathyBridge on_change" << std::endl;
3079- d->mBridge->on_change(func);
3080- } else
3081- std::cerr << "TelepathyBridge: Failed to hook on_change signal, bridge not yet set" << std::endl;
3082+}
3083+}
3084+
3085+media::telephony::CallMonitor::Ptr media::telephony::make_platform_default_call_monitor()
3086+{
3087+ return std::make_shared<impl::CallMonitor>();
3088 }
3089
3090 #include "call_monitor.moc"
3091
3092=== modified file 'src/core/media/telephony/call_monitor.h'
3093--- src/core/media/call-monitor/call_monitor.h 2014-10-31 07:49:33 +0000
3094+++ src/core/media/telephony/call_monitor.h 2015-03-16 15:38:27 +0000
3095@@ -17,25 +17,50 @@
3096 */
3097
3098
3099-#ifndef CALLMONITOR_H
3100-#define CALLMONITOR_H
3101+#ifndef CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_
3102+#define CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_
3103+
3104+#include <core/signal.h>
3105
3106 #include <functional>
3107-
3108-class CallMonitorPrivate;
3109-
3110-class CallMonitor
3111-{
3112-public:
3113- enum State { OffHook, OnHook };
3114-
3115- CallMonitor();
3116- ~CallMonitor();
3117-
3118- void on_change(const std::function<void(CallMonitor::State)>& func);
3119-
3120-private:
3121- CallMonitorPrivate *d;
3122+#include <memory>
3123+
3124+namespace core
3125+{
3126+namespace ubuntu
3127+{
3128+namespace media
3129+{
3130+namespace telephony
3131+{
3132+// CallMonitor models the ability to observe and react
3133+// to changes of the overall call state of the system.
3134+struct CallMonitor
3135+{
3136+ // Save us some typing.
3137+ typedef std::shared_ptr<CallMonitor> Ptr;
3138+
3139+ // All known call states
3140+ enum class State
3141+ {
3142+ // No current call.
3143+ OffHook,
3144+ // Call in progress.
3145+ OnHook
3146+ };
3147+
3148+ CallMonitor() = default;
3149+ virtual ~CallMonitor() = default;
3150+
3151+ // Emitted whenever the current call state of the system changes.
3152+ virtual const core::Signal<State>& on_call_state_changed() const = 0;
3153 };
3154
3155-#endif // CALLMONITOR_H
3156+// Returns a platform default implementation of CallMonitor.
3157+CallMonitor::Ptr make_platform_default_call_monitor();
3158+}
3159+}
3160+}
3161+}
3162+
3163+#endif // CORE_UBUNTU_MEDIA_TELEPHONY_CALL_MONITOR_H_

Subscribers

People subscribed via source and target branches