~ubuntu-branches/ubuntu/vivid/media-hub/vivid-updates

« back to all changes in this revision

Viewing changes to src/core/media/power/state_controller.cpp

  • Committer: Package Import Robot
  • Author(s): CI Train Bot, Thomas Voss, CI Train Bot, Jim Hodapp, Ricardo Mendoza, Ricardo Salveti de Araujo, thomas-voss
  • Date: 2015-03-20 13:16:48 UTC
  • mfrom: (1.1.34)
  • Revision ID: package-import@ubuntu.com-20150320131648-dv8aynyyjib9v2sr
Tags: 3.0.0+15.04.20150320-0ubuntu1
[ Thomas Voss ]
* Refactor client-facing interfaces to pull out explicit dependency on hybris-based media layer.

[ CI Train Bot ]
* New rebuild forced.

[ Jim Hodapp ]
* Disconnect playback_status_changed_signal in ~Private() to avoid a
  deadlock.
* 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.

[ Ricardo Mendoza ]
* Add an interface apparmor::ubuntu::RequestAuthenticator that is used
  to authenticate incoming open uri requests. Add an interface
  apparmor::ubuntu::RequestContextResolver that is used to resolve a
  dbus name to an apparmor::ubuntu::Context. Provide an implementation
  apparmor::ubuntu::ExistingAuthenticator that takes the impl. from
  player_skeleton.cpp and uses it to implement the interface. Provide
  an implementation apparmor::ubuntu::RequestContextResolver that
  reaches out to the dbus daemon to resolve a given bus name to an
  apparmor profile. Remove obsolete query for the apparmor profile in
  ServiceSkeleton. Adjust the implementation in media::PlayerSkeleton
  to rely on Request{Authenticator, ContextResolver}. removed:
  src/core/media/apparmor.h added: src/core/media/apparmor/
  src/core/media/apparmor/context.cpp
  src/core/media/apparmor/context.h src/core/media/apparmor/dbus.h
  src/core/media/apparmor/ubuntu.cpp src/core/media/apparmor/ubuntu.h
* Introduce an interface media::audio::OutputObserver that allows the
  core classes to observer the state of external audio outputs
  (headphones/headsets). Provide an implementation of
  media::audio::OutputObserver that relies on Pulseaudio to monitor
  the availability of ports on the default sink. Adjust
  media::ServiceImplementation to use media::audio::OutputObserver,
  defaulting to media::audio::PulseAudioOutputObserver.
* Migrate all Player DBus methods to use transact_method() instead of
  invoke_method_synchronously(), to prevent bus executor deadlocks.

[ Ricardo Salveti de Araujo ]
* Add an interface apparmor::ubuntu::RequestAuthenticator that is used
  to authenticate incoming open uri requests. Add an interface
  apparmor::ubuntu::RequestContextResolver that is used to resolve a
  dbus name to an apparmor::ubuntu::Context. Provide an implementation
  apparmor::ubuntu::ExistingAuthenticator that takes the impl. from
  player_skeleton.cpp and uses it to implement the interface. Provide
  an implementation apparmor::ubuntu::RequestContextResolver that
  reaches out to the dbus daemon to resolve a given bus name to an
  apparmor profile. Remove obsolete query for the apparmor profile in
  ServiceSkeleton. Adjust the implementation in media::PlayerSkeleton
  to rely on Request{Authenticator, ContextResolver}. removed:
  src/core/media/apparmor.h added: src/core/media/apparmor/
  src/core/media/apparmor/context.cpp
  src/core/media/apparmor/context.h src/core/media/apparmor/dbus.h
  src/core/media/apparmor/ubuntu.cpp src/core/media/apparmor/ubuntu.h
* Move src/core/media/call-monitor to src/core/media/telephony.
  Introduce a proper interface media::telephony::CallMonitor. Slightly
  adjust existing implementation based on Qt. Adjust
  media::ServiceImplementation to account for changes in
  media::telephony::CallMonitor.

[ thomas-voss ]
* Add an interface apparmor::ubuntu::RequestAuthenticator that is used
  to authenticate incoming open uri requests. Add an interface
  apparmor::ubuntu::RequestContextResolver that is used to resolve a
  dbus name to an apparmor::ubuntu::Context. Provide an implementation
  apparmor::ubuntu::ExistingAuthenticator that takes the impl. from
  player_skeleton.cpp and uses it to implement the interface. Provide
  an implementation apparmor::ubuntu::RequestContextResolver that
  reaches out to the dbus daemon to resolve a given bus name to an
  apparmor profile. Remove obsolete query for the apparmor profile in
  ServiceSkeleton. Adjust the implementation in media::PlayerSkeleton
  to rely on Request{Authenticator, ContextResolver}. removed:
  src/core/media/apparmor.h added: src/core/media/apparmor/
  src/core/media/apparmor/context.cpp
  src/core/media/apparmor/context.h src/core/media/apparmor/dbus.h
  src/core/media/apparmor/ubuntu.cpp src/core/media/apparmor/ubuntu.h
* Add an interface media::ClientDeathObserver that abstracts away
  receiving key-based death notifications for clients associated to
  media::Player instances server-side Provide an implementation
  media::HybrisClientDeathObserver that relies on hybris and
  ultimately on Android's onBinderDied to receive death notifications.
  Adjust media::PlayerStub and media::PlayerImplementation to account
  for the new interface. Adjust the CMake setup for tests to link
  media-hub-service instead of recompiling large parts of the
  implementation classes.
* Decouple PlayerSkeleton and PlayerImplementation by making
  PlayerImplementation being able to inherit from arbitrary base
  classes, as long as they provide the set of properties and signals
  defined by media::Player.
* Decouple the ServiceSkeleton from the ServiceImplementation by
  introducing a common interface media::KeyedPlayerStore for storing
  running Player sessions indexed by the Player::Key. Provide a
  default implementation HashedKeyedPlayerStore relying on a hash map
  for keeping track of player instances. Adjust implementation to
  account for ServiceImplementation no longer inheriting from
  ServiceSkeleton.
* Introduce a common class media::helper::ExternalHelpers that
  provides a convenient way to:
* Introduce an interface media::RecorderObserver that allows the core
  classes to monitor the overall state of the system. Provide an
  implementation of media::RecorderObserver relying on Hybris to
  interface with the Android side. Adjust the ServiceImplementation to
  connect to the platform-default media::RecorderObserver.
* Introduce an interface media::audio::OutputObserver that allows the
  core classes to observer the state of external audio outputs
  (headphones/headsets). Provide an implementation of
  media::audio::OutputObserver that relies on Pulseaudio to monitor
  the availability of ports on the default sink. Adjust
  media::ServiceImplementation to use media::audio::OutputObserver,
  defaulting to media::audio::PulseAudioOutputObserver.
* Introduce an interface media::power::BatteryObserver to monitor the
  current battery level of the system. The core reacts to low/very low
  battery levels by pausing all multimedia playback sessions and
  resumes them whenever the user has been notified of the critical
  battery level. Provide an implementation of
  media::power::BatteryObserver using
  com.canonical.indicator.power.Battery. Adjust
  media::ServiceImplementation to use media::power::BatteryObserver.
* Move gstreamer::Playbin implementation to its own cpp file, thus
  internalizing the Hybris setup portions. Make sure that media-hub-
  service knows about
  media::Player::Error::OutOfProcessBufferStreamingNotSupported by
  linking with media-hub-client.
* Move src/core/media/call-monitor to src/core/media/telephony.
  Introduce a proper interface media::telephony::CallMonitor. Slightly
  adjust existing implementation based on Qt. Adjust
  media::ServiceImplementation to account for changes in
  media::telephony::CallMonitor.
* Replace home-grown mask type for the video size with a std::tuple,
  i.e., media::video::Dimensions. Introduce a simple TaggedInteger
  class to distinguish between Width, Height and other dimensions.
  Adjust interfaces of media::Player to rely on the new type. Adjust
  implementation classes to account for interface changes. Adjust
  Codec implementation for sending the tagged integer via the bus.
  Adjust gstreamer::Engine and gstreamer::Playbin to hand out the
  correct types.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2014 Canonical Ltd.
 
3
 *
 
4
 * This program is free software: you can redistribute it and/or modify it
 
5
 * under the terms of the GNU Lesser General Public License version 3,
 
6
 * as published by the Free Software Foundation.
 
7
 *
 
8
 * This program is distributed in the hope that it will be useful,
 
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
 * GNU Lesser General Public License for more details.
 
12
 *
 
13
 * You should have received a copy of the GNU Lesser General Public License
 
14
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
15
 *
 
16
 * Authored by: Thomas Voß <thomas.voss@canonical.com>
 
17
 */
 
18
 
 
19
#include <core/media/power/state_controller.h>
 
20
 
 
21
#include <core/dbus/macros.h>
 
22
#include <core/dbus/object.h>
 
23
 
 
24
#include <iostream>
 
25
 
 
26
namespace media = core::ubuntu::media;
 
27
 
 
28
namespace com { namespace canonical {
 
29
struct Unity
 
30
{
 
31
    struct Screen
 
32
    {
 
33
        static const std::string& name()
 
34
        {
 
35
            static std::string s = "com.canonical.Unity.Screen";
 
36
            return s;
 
37
        }
 
38
 
 
39
        static const core::dbus::types::ObjectPath& path()
 
40
        {
 
41
            static core::dbus::types::ObjectPath p{"/com/canonical/Unity/Screen"};
 
42
            return p;
 
43
        }
 
44
 
 
45
        DBUS_CPP_METHOD_DEF(keepDisplayOn, Screen)
 
46
        DBUS_CPP_METHOD_DEF(removeDisplayOnRequest, Screen)
 
47
    };
 
48
};
 
49
namespace powerd {
 
50
struct Interface
 
51
{
 
52
    static std::string& name()
 
53
    {
 
54
        static std::string s = "com.canonical.powerd";
 
55
        return s;
 
56
    }
 
57
 
 
58
    static const core::dbus::types::ObjectPath& path()
 
59
    {
 
60
        static core::dbus::types::ObjectPath p{"/com/canonical/powerd"};
 
61
        return p;
 
62
    }
 
63
 
 
64
    DBUS_CPP_METHOD_DEF(requestSysState, com::canonical::powerd::Interface)
 
65
    DBUS_CPP_METHOD_DEF(clearSysState, com::canonical::powerd::Interface)
 
66
};
 
67
}}}
 
68
 
 
69
namespace
 
70
{
 
71
namespace impl
 
72
{
 
73
struct DisplayStateLock : public media::power::StateController::Lock<media::power::DisplayState>,
 
74
                          public std::enable_shared_from_this<DisplayStateLock>
 
75
{
 
76
    // To safe us some typing
 
77
    typedef std::shared_ptr<DisplayStateLock> Ptr;
 
78
 
 
79
    // We postpone releasing the display for this amount of time.
 
80
    static boost::posix_time::seconds timeout_for_release()
 
81
    {
 
82
        return boost::posix_time::seconds{4};
 
83
    }
 
84
 
 
85
    // The invalid cookie marker.
 
86
    static constexpr const std::int32_t the_invalid_cookie{-1};
 
87
 
 
88
    DisplayStateLock(const media::power::StateController::Ptr& parent,
 
89
                     boost::asio::io_service& io_service,
 
90
                     const core::dbus::Object::Ptr& object)
 
91
        : parent{parent},
 
92
          timeout{io_service},
 
93
          object{object},
 
94
          cookie{the_invalid_cookie}
 
95
    {
 
96
    }
 
97
 
 
98
    // From core::ubuntu::media::power::StateController::Lock<DisplayState>
 
99
    void request_acquire(media::power::DisplayState state) override
 
100
    {
 
101
        if (state == media::power::DisplayState::off)
 
102
            return;
 
103
 
 
104
        std::weak_ptr<DisplayStateLock> wp{shared_from_this()};
 
105
 
 
106
        object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::keepDisplayOn, std::int32_t>(
 
107
                    [wp, state](const core::dbus::Result<std::int32_t>& result)
 
108
                    {
 
109
                        if (result.is_error())
 
110
                        {
 
111
                            std::cerr << result.error().print() << std::endl;
 
112
                            return;
 
113
                        }
 
114
 
 
115
                        if (auto sp = wp.lock())
 
116
                        {
 
117
                            sp->cookie = result.value();
 
118
                            sp->signals.acquired(state);
 
119
                        }
 
120
                    });
 
121
    }
 
122
 
 
123
    void request_release(media::power::DisplayState state) override
 
124
    {
 
125
        if (state == media::power::DisplayState::off)
 
126
            return;
 
127
 
 
128
        if (cookie == the_invalid_cookie)
 
129
            return;
 
130
 
 
131
        // We make sure that we keep ourselves alive to make sure
 
132
        // that release requests are always correctly issued.
 
133
        auto sp = shared_from_this();
 
134
 
 
135
        auto current_cookie(cookie);
 
136
 
 
137
        timeout.expires_from_now(timeout_for_release());
 
138
        timeout.async_wait([sp, state, current_cookie](const boost::system::error_code& ec)
 
139
        {
 
140
            // We only return early from the timeout handler if the operation has been
 
141
            // explicitly aborted before.
 
142
            if (ec == boost::asio::error::operation_aborted)
 
143
                return;
 
144
 
 
145
            sp->object->invoke_method_asynchronously_with_callback<com::canonical::Unity::Screen::removeDisplayOnRequest, void>(
 
146
                        [sp, state, current_cookie](const core::dbus::Result<void>& result)
 
147
                        {
 
148
                            if (result.is_error())
 
149
                            {
 
150
                                std::cerr << result.error().print() << std::endl;
 
151
                                return;
 
152
                            }
 
153
 
 
154
                            sp->signals.released(state);
 
155
 
 
156
                            // We might have issued a different request before and
 
157
                            // only call the display state done if the original cookie
 
158
                            // corresponds to the one we just gave up.
 
159
                            if (sp->cookie == current_cookie)
 
160
                                sp->cookie = the_invalid_cookie;
 
161
 
 
162
                        }, current_cookie);
 
163
        });
 
164
    }
 
165
 
 
166
    // Emitted whenever the acquire request completes.
 
167
    const core::Signal<media::power::DisplayState>& acquired() const override
 
168
    {
 
169
        return signals.acquired;
 
170
    }
 
171
 
 
172
    // Emitted whenever the release request completes.
 
173
    const core::Signal<media::power::DisplayState>& released() const override
 
174
    {
 
175
        return signals.released;
 
176
    }
 
177
 
 
178
    media::power::StateController::Ptr parent;
 
179
    boost::asio::deadline_timer timeout;
 
180
    core::dbus::Object::Ptr object;
 
181
    std::int32_t cookie;
 
182
 
 
183
    struct
 
184
    {
 
185
        core::Signal<media::power::DisplayState> acquired;
 
186
        core::Signal<media::power::DisplayState> released;
 
187
    } signals;
 
188
};
 
189
 
 
190
struct SystemStateLock : public media::power::StateController::Lock<media::power::SystemState>,
 
191
                         public std::enable_shared_from_this<SystemStateLock>
 
192
{
 
193
    static constexpr const char* wake_lock_name
 
194
    {
 
195
        "media-hub-playback_lock"
 
196
    };
 
197
 
 
198
    SystemStateLock(const media::power::StateController::Ptr& parent, const core::dbus::Object::Ptr& object)
 
199
        : parent{parent},
 
200
          object{object}
 
201
    {
 
202
    }
 
203
 
 
204
    // Informs the system that the caller would like
 
205
    // the system to stay active.
 
206
    void request_acquire(media::power::SystemState state) override
 
207
    {
 
208
        if (state == media::power::SystemState::suspend)
 
209
            return;
 
210
 
 
211
        // Keep scope of this lock tight as to avoid
 
212
        // deadlocks on PlayerImplementation destruction
 
213
        {
 
214
            std::lock_guard<std::mutex> lg{system_state_cookie_store_guard};
 
215
            if (system_state_cookie_store.count(state) > 0)
 
216
                return;
 
217
        }
 
218
 
 
219
        std::weak_ptr<SystemStateLock> wp{shared_from_this()};
 
220
 
 
221
        object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::requestSysState, std::string>([wp, state](const core::dbus::Result<std::string>& result)
 
222
        {
 
223
            if (result.is_error()) // TODO(tvoss): We should log the error condition here.
 
224
                return;
 
225
 
 
226
            if (auto sp = wp.lock())
 
227
            {
 
228
                std::lock_guard<std::mutex> lg{sp->system_state_cookie_store_guard};
 
229
 
 
230
                sp->system_state_cookie_store[state] = result.value();
 
231
                sp->signals.acquired(state);
 
232
            }
 
233
        }, std::string{wake_lock_name}, static_cast<std::int32_t>(state));
 
234
    }
 
235
 
 
236
    // Informs the system that the caller does not
 
237
    // require the system to stay active anymore.
 
238
    void request_release(media::power::SystemState state) override
 
239
    {
 
240
        if (state == media::power::SystemState::suspend)
 
241
            return;
 
242
 
 
243
        std::lock_guard<std::mutex> lg{system_state_cookie_store_guard};
 
244
 
 
245
        if (system_state_cookie_store.count(state) == 0)
 
246
            return;
 
247
 
 
248
        std::weak_ptr<SystemStateLock> wp{shared_from_this()};
 
249
 
 
250
        object->invoke_method_asynchronously_with_callback<com::canonical::powerd::Interface::clearSysState, void>([wp, state](const core::dbus::Result<void>& result)
 
251
        {
 
252
            if (result.is_error())
 
253
                std::cerr << result.error().print() << std::endl;
 
254
 
 
255
            if (auto sp = wp.lock())
 
256
            {
 
257
                std::lock_guard<std::mutex> lg{sp->system_state_cookie_store_guard};
 
258
 
 
259
                sp->system_state_cookie_store.erase(state);
 
260
                sp->signals.released(state);
 
261
            }
 
262
        }, system_state_cookie_store.at(state));
 
263
    }
 
264
 
 
265
    // Emitted whenever the acquire request completes.
 
266
    const core::Signal<media::power::SystemState>& acquired() const override
 
267
    {
 
268
        return signals.acquired;
 
269
    }
 
270
 
 
271
    // Emitted whenever the release request completes.
 
272
    const core::Signal<media::power::SystemState>& released() const override
 
273
    {
 
274
        return signals.released;
 
275
    }
 
276
 
 
277
    // Guards concurrent accesses to the cookie store.
 
278
    std::mutex system_state_cookie_store_guard;
 
279
    // Maps previously requested system states to the cookies returned
 
280
    // by the remote end. Used for keeping track of acquired states and
 
281
    // associated cookies to be able to release previously granted acquisitions.
 
282
    std::map<media::power::SystemState, std::string> system_state_cookie_store;
 
283
    media::power::StateController::Ptr parent;
 
284
    core::dbus::Object::Ptr object;
 
285
    struct
 
286
    {
 
287
        core::Signal<media::power::SystemState> acquired;
 
288
        core::Signal<media::power::SystemState> released;
 
289
    } signals;
 
290
};
 
291
 
 
292
struct StateController : public media::power::StateController,
 
293
                         public std::enable_shared_from_this<impl::StateController>
 
294
{
 
295
    StateController(media::helper::ExternalServices& es)
 
296
        : external_services{es},
 
297
          powerd
 
298
          {
 
299
              core::dbus::Service::use_service<com::canonical::powerd::Interface>(external_services.system)
 
300
                  ->object_for_path(com::canonical::powerd::Interface::path())
 
301
          },
 
302
          unity_screen
 
303
          {
 
304
              core::dbus::Service::use_service<com::canonical::Unity::Screen>(external_services.system)
 
305
                  ->object_for_path(com::canonical::Unity::Screen::path())
 
306
          }
 
307
    {
 
308
    }
 
309
 
 
310
    media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock() override
 
311
    {
 
312
        return std::make_shared<impl::SystemStateLock>(shared_from_this(), powerd);
 
313
    }
 
314
 
 
315
    media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock() override
 
316
    {
 
317
        return std::make_shared<impl::DisplayStateLock>(shared_from_this(), external_services.io_service, unity_screen);
 
318
    }
 
319
 
 
320
    media::helper::ExternalServices& external_services;
 
321
    core::dbus::Object::Ptr powerd;
 
322
    core::dbus::Object::Ptr unity_screen;
 
323
};
 
324
}
 
325
}
 
326
 
 
327
media::power::StateController::Ptr media::power::make_platform_default_state_controller(core::ubuntu::media::helper::ExternalServices& external_services)
 
328
{
 
329
    return std::make_shared<impl::StateController>(external_services);
 
330
}
 
331
 
 
332
// operator<< pretty prints the given display state to the given output stream.
 
333
std::ostream& media::power::operator<<(std::ostream& out, media::power::DisplayState state)
 
334
{
 
335
    switch (state)
 
336
    {
 
337
    case media::power::DisplayState::off:
 
338
        return out << "DisplayState::off";
 
339
    case media::power::DisplayState::on:
 
340
        return out << "DisplayState::on";
 
341
    }
 
342
 
 
343
    return out;
 
344
}
 
345
 
 
346
// operator<< pretty prints the given system state to the given output stream.
 
347
std::ostream& media::power::operator<<(std::ostream& out, media::power::SystemState state)
 
348
{
 
349
    switch (state)
 
350
    {
 
351
    case media::power::SystemState::active:
 
352
        return out << "SystemState::active";
 
353
    case media::power::SystemState::blank_on_proximity:
 
354
        return out << "SystemState::blank_on_proximity";
 
355
    case media::power::SystemState::suspend:
 
356
        return out << "SystemState::suspend";
 
357
    }
 
358
 
 
359
    return out;
 
360
}