~thomas-voss/location-service/fix-1347887

« back to all changes in this revision

Viewing changes to src/location_service/com/ubuntu/location/service/skeleton.cpp

This MP consolidates multiple related changes together, with the goal of:

(1.) Make the service instance accessible via a cli. Useful for testing scenarios.
(2.) To cut down time-to-first-fix (ttff) by:
  (2.1) Leveraging SUPL and other supplementary data downloaded over ordinary data connections.
  (2.2) Enabling network-based positioning providers to acquire fast position estimates.

In more detail:

* Added tests for daemon and cli.
* Unified daemon and cli header and implementation files.
* Add a command-line interface to the service.
* Split up provider selection policy to rely on an interface ProviderEnumerator to ease in testing.
* Trimmed down on types.
* Removed connectivity API draft to prepare for simpler approach.
* Refactored includes.
* Added a configuration option to handle cell and wifi ID reporting.
* Add a mock for a connectivity API exposed to providers and reporters.
* Add units for connectivity api.
* Refactor cell class into namespace radio. Fixes: 1226204, 1248973, 1281817

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
 *
16
16
 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17
17
 */
18
 
#include "com/ubuntu/location/service/skeleton.h"
19
 
 
20
 
#include "com/ubuntu/location/logging.h"
21
 
 
22
 
#include <core/dbus/dbus.h>
23
 
#include <core/dbus/object.h>
24
 
#include <core/dbus/skeleton.h>
 
18
#include <com/ubuntu/location/service/skeleton.h>
 
19
#include <com/ubuntu/location/service/session/skeleton.h>
 
20
 
 
21
#include <com/ubuntu/location/logging.h>
 
22
 
25
23
#include <core/dbus/types/object_path.h>
26
24
 
27
25
namespace cul = com::ubuntu::location;
32
30
 
33
31
namespace
34
32
{
35
 
 
36
 
template<typename SessionType>
37
 
struct SessionStore
38
 
{
39
 
    typedef std::shared_ptr<SessionStore> Ptr;
40
 
 
41
 
    SessionStore() = default;
42
 
    SessionStore(const SessionStore&) = delete;
43
 
    SessionStore& operator=(const SessionStore&) = delete;
44
 
    virtual ~SessionStore() = default;
45
 
 
46
 
    virtual void remove_session(const std::shared_ptr<SessionType>& session) = 0;
47
 
};
48
 
 
49
 
struct SessionWrapper : public std::enable_shared_from_this<SessionWrapper>
50
 
{
51
 
    typedef std::shared_ptr<SessionWrapper> Ptr;
52
 
 
53
 
    SessionWrapper(const SessionStore<SessionWrapper>::Ptr& session_store,
54
 
                   const culss::Interface::Ptr& session,
55
 
                   core::dbus::Service::Ptr service,
56
 
                   core::dbus::Object::Ptr object)
57
 
            : session_store(session_store),
58
 
              session{session},
59
 
              remote(service, object)
60
 
    {
61
 
        session->install_position_updates_handler(std::bind(&SessionWrapper::on_position_update, this, std::placeholders::_1));
62
 
        session->install_velocity_updates_handler(std::bind(&SessionWrapper::on_velocity_update, this, std::placeholders::_1));
63
 
        session->install_heading_updates_handler(std::bind(&SessionWrapper::on_heading_update, this, std::placeholders::_1));
64
 
    }
65
 
 
66
 
    const core::dbus::types::ObjectPath& path() const
67
 
    {
68
 
        return session->path();
69
 
    }
70
 
 
71
 
    void on_session_died() noexcept
72
 
    {
73
 
        std::cout << __PRETTY_FUNCTION__ << std::endl;
74
 
        VLOG(1) << "Session died, removing from store and stopping all updates.";
75
 
 
76
 
        auto thiz = shared_from_this();
77
 
        try
78
 
        {
79
 
            session_store->remove_session(thiz);
80
 
            session->stop_position_updates();
81
 
            session->stop_heading_updates();
82
 
            session->stop_velocity_updates();
83
 
        } catch(const std::runtime_error& e)
84
 
        {
85
 
            LOG(ERROR) << "Error while stopping updates for died session: " << e.what();
86
 
        }
87
 
    }
88
 
 
89
 
    void on_position_update(const cul::Update<cul::Position>& position)
90
 
    {
91
 
        try
92
 
        {
93
 
        auto result = remote.session->transact_method<culs::session::Interface::UpdatePosition, void>(position);
94
 
        if (result.is_error())
95
 
        {
96
 
            LOG(ERROR) << result.error().print();
97
 
            on_session_died();
98
 
        }
99
 
        } catch(const std::runtime_error& e)
100
 
        {
101
 
            // We consider the session to be dead once we hit an exception here.
102
 
            // We thus remove it from the central and end its lifetime.
103
 
        on_session_died();
104
 
        }
105
 
    }
106
 
 
107
 
    void on_velocity_update(const cul::Update<cul::Velocity>& velocity)
108
 
    {
109
 
        try
110
 
        {
111
 
        auto result = remote.session->transact_method<culs::session::Interface::UpdateVelocity, void>(velocity);
112
 
        if (result.is_error())
113
 
        {
114
 
            LOG(ERROR) << result.error().print();
115
 
            on_session_died();
116
 
        }
117
 
        } catch(const std::runtime_error& e)
118
 
        {
119
 
            // We consider the session to be dead once we hit an exception here.
120
 
            // We thus remove it from the central and end its lifetime.
121
 
        on_session_died();
122
 
        }
123
 
    }
124
 
 
125
 
    void on_heading_update(const cul::Update<cul::Heading>& heading)
126
 
    {
127
 
        try
128
 
        {
129
 
        auto result = remote.session->transact_method<culs::session::Interface::UpdateHeading, void>(heading);
130
 
        if (result.is_error())
131
 
        {
132
 
            LOG(ERROR) << result.error().print();
133
 
            on_session_died();
134
 
        }
135
 
        } catch(const std::runtime_error& e)
136
 
        {
137
 
            // We consider the session to be dead once we hit an exception here.
138
 
            // We thus remove it from the central and end its lifetime.
139
 
        on_session_died();
140
 
        }
141
 
    }
142
 
 
143
 
    SessionStore<SessionWrapper>::Ptr session_store;
144
 
    culs::session::Interface::Ptr session;
145
 
    struct Remote
146
 
    {
147
 
        explicit Remote(const dbus::Service::Ptr& service,
148
 
                        const dbus::Object::Ptr& session)
149
 
                : service(service),
150
 
                  session(session)
151
 
        {
152
 
        }
153
 
        
154
 
        dbus::Service::Ptr service;
155
 
        dbus::Object::Ptr session;
156
 
    } remote;
157
 
};
158
 
}
159
 
 
160
 
struct culs::Skeleton::Private : public SessionStore<SessionWrapper>, std::enable_shared_from_this<culs::Skeleton::Private>
161
 
{
162
 
    Private(Skeleton* parent, const dbus::Bus::Ptr& connection, const culs::PermissionManager::Ptr& permission_manager)
163
 
        : parent(parent),
164
 
          permission_manager(permission_manager),
165
 
          daemon(connection),
166
 
          object(parent->access_service()->add_object_for_path(culs::Interface::path()))
167
 
    {
168
 
        object->install_method_handler<culs::Interface::CreateSessionForCriteria>(
169
 
            std::bind(&culs::Skeleton::Private::handle_create_session_for_criteria, this, std::placeholders::_1));
170
 
    }
171
 
 
172
 
    ~Private() noexcept {}
173
 
 
174
 
    void handle_create_session_for_criteria(const core::dbus::Message::Ptr& in);
175
 
    void remove_session(const std::shared_ptr<SessionWrapper>& session);
176
 
 
177
 
    Skeleton* parent;
178
 
    PermissionManager::Ptr permission_manager;
179
 
    dbus::DBus daemon;
180
 
    dbus::Object::Ptr object;
181
 
    std::mutex guard;
182
 
    std::map<dbus::types::ObjectPath, std::shared_ptr<SessionWrapper>> session_store;
183
 
};
184
 
 
185
 
culs::Skeleton::Skeleton(const dbus::Bus::Ptr& connection, const culs::PermissionManager::Ptr& permission_manager)
186
 
        : dbus::Skeleton<culs::Interface>(connection), d{new Private{this, connection, permission_manager}}
187
 
{
 
33
dbus::Message::Ptr the_empty_reply()
 
34
{
 
35
    return dbus::Message::Ptr{};
 
36
}
 
37
}
 
38
 
 
39
culs::Skeleton::DBusDaemonCredentialsResolver::DBusDaemonCredentialsResolver(const dbus::Bus::Ptr& bus)
 
40
    : daemon(bus)
 
41
{
 
42
}
 
43
 
 
44
culs::Credentials
 
45
culs::Skeleton::DBusDaemonCredentialsResolver::resolve_credentials_for_incoming_message(const dbus::Message::Ptr& msg)
 
46
{
 
47
    return culs::Credentials
 
48
    {
 
49
        daemon.get_connection_unix_process_id(msg->sender()),
 
50
        daemon.get_connection_unix_user(msg->sender())
 
51
    };
 
52
}
 
53
 
 
54
core::dbus::types::ObjectPath culs::Skeleton::ObjectPathGenerator::object_path_for_caller_credentials(const culs::Credentials&)
 
55
{
 
56
    static std::uint32_t index{0};
 
57
    std::stringstream ss; ss << "/sessions/" << index++;
 
58
 
 
59
    return core::dbus::types::ObjectPath{ss.str()};
 
60
}
 
61
 
 
62
 
 
63
culs::Skeleton::Skeleton(const culs::Skeleton::Configuration& configuration)
 
64
    : dbus::Skeleton<culs::Interface>(configuration.incoming),
 
65
      configuration(configuration),
 
66
      object(access_service()->add_object_for_path(culs::Interface::path())),
 
67
      properties
 
68
      {
 
69
          object->get_property<culs::Interface::Properties::DoesSatelliteBasedPositioning>(),
 
70
          object->get_property<culs::Interface::Properties::DoesReportCellAndWifiIds>(),
 
71
          object->get_property<culs::Interface::Properties::IsOnline>(),
 
72
          object->get_property<culs::Interface::Properties::VisibleSpaceVehicles>()
 
73
      }
 
74
{
 
75
    object->install_method_handler<culs::Interface::CreateSessionForCriteria>([this](const dbus::Message::Ptr& msg)
 
76
    {
 
77
        handle_create_session_for_criteria(msg);
 
78
    });
188
79
}
189
80
 
190
81
culs::Skeleton::~Skeleton() noexcept
191
82
{
 
83
    object->uninstall_method_handler<culs::Interface::CreateSessionForCriteria>();
192
84
}
193
85
 
194
 
void culs::Skeleton::Private::handle_create_session_for_criteria(const core::dbus::Message::Ptr& in)
 
86
void culs::Skeleton::handle_create_session_for_criteria(const dbus::Message::Ptr& in)
195
87
{
 
88
    VLOG(1) << __PRETTY_FUNCTION__;
 
89
 
196
90
    auto sender = in->sender();
 
91
    auto reply = the_empty_reply();
197
92
 
198
93
    try
199
94
    {
200
 
        Criteria criteria;
201
 
        in->reader() >> criteria;
202
 
 
203
 
        Credentials credentials
204
 
        {
205
 
            static_cast<pid_t>(daemon.get_connection_unix_process_id(sender)),
206
 
            static_cast<uid_t>(daemon.get_connection_unix_user(sender))
207
 
        };
208
 
 
209
 
        if (PermissionManager::Result::rejected == permission_manager->check_permission_for_credentials(criteria, credentials))
210
 
            throw std::runtime_error("Client lacks permissions to access the service with the given criteria");
211
 
 
212
 
        auto session = parent->create_session_for_criteria(criteria);
213
 
 
214
 
    auto service = dbus::Service::use_service(parent->access_bus(), in->sender());
215
 
    auto object = service->object_for_path(session->path());
216
 
 
 
95
        Criteria criteria;
 
96
        in->reader() >> criteria;
 
97
 
 
98
        auto credentials =
 
99
            configuration.credentials_resolver->resolve_credentials_for_incoming_message(in);
 
100
 
 
101
        auto result =
 
102
            configuration.permission_manager->check_permission_for_credentials(criteria, credentials);
 
103
 
 
104
        if (PermissionManager::Result::rejected == result) throw std::runtime_error
 
105
        {
 
106
            "Client lacks permissions to access the service with the given criteria"
 
107
        };
 
108
 
 
109
        auto path =
 
110
            configuration.object_path_generator->object_path_for_caller_credentials(credentials);
 
111
 
 
112
        auto stub =
 
113
            dbus::Service::use_service(configuration.outgoing, sender);
 
114
 
 
115
        culss::Skeleton::Configuration config
 
116
        {
 
117
            path,
 
118
            culss::Skeleton::Local
 
119
            {
 
120
                create_session_for_criteria(criteria),
 
121
                configuration.incoming
 
122
            },
 
123
            culss::Skeleton::Remote
 
124
            {
 
125
                stub->object_for_path(path)
 
126
            }
 
127
        };
 
128
 
 
129
        culss::Interface::Ptr session
 
130
        {
 
131
            new culss::Skeleton{config}
 
132
        };
 
133
 
 
134
        if (not add_to_session_store_for_path(path, session))
 
135
        {
 
136
            reply = dbus::Message::make_error(
 
137
                        in,
 
138
                        culs::Interface::Errors::CreatingSession::name(),
 
139
                        "Refused to create second session for same process");
 
140
        } else
 
141
        {
 
142
            reply = dbus::Message::make_method_return(in);
 
143
            reply->writer() << path;
 
144
        }
 
145
 
 
146
    } catch(const std::exception& e)
217
147
    {
218
 
        std::lock_guard<std::mutex> lg(guard);
219
 
 
220
 
        auto wrapper = SessionWrapper::Ptr{new SessionWrapper{shared_from_this(), session, service, object}};
221
 
 
222
 
        bool inserted = false;
223
 
        std::tie(std::ignore, inserted) = session_store.insert(std::make_pair(session->path(), wrapper));
224
 
 
225
 
        auto reply = dbus::Message::make_method_return(in);
226
 
        reply->writer() << session->path();
227
 
        parent->access_bus()->send(reply);
228
 
 
229
 
        if (!inserted)
230
 
            throw std::runtime_error("Could not insert duplicate session into store.");
 
148
        // We only send a very generic error message to the client to avoid
 
149
        // leaking any sort of internal error handling details to untrusted
 
150
        // apps.
 
151
        reply = dbus::Message::make_error(
 
152
                    in,
 
153
                    culs::Interface::Errors::CreatingSession::name(),
 
154
                    "Error creating session");
 
155
        // We log the error for debugging purposes.
 
156
        LOG(ERROR) << "Error creating session: " << e.what();
231
157
    }
232
158
 
233
 
    } catch(const std::runtime_error& e)
234
 
    {
235
 
    parent->access_bus()->send(
236
 
        dbus::Message::make_error(
237
 
            in,
238
 
            culs::Interface::Errors::CreatingSession::name(),
239
 
            e.what()));
240
 
 
241
 
    LOG(ERROR) << "Error creating session: " << e.what();
 
159
    // We are done processing the request and try to send out the result to the client.
 
160
    try
 
161
    {
 
162
        configuration.incoming->send(reply);
 
163
    } catch(const std::exception& e)
 
164
    {
 
165
        // We log the error for debugging purposes.
 
166
        LOG(ERROR) << "Error sending reply to session creation request: " << e.what();
242
167
    }
243
168
}
244
169
 
245
 
void culs::Skeleton::Private::remove_session(const SessionWrapper::Ptr& session)
 
170
bool culs::Skeleton::add_to_session_store_for_path(
 
171
        const core::dbus::types::ObjectPath& path,
 
172
        const culss::Interface::Ptr& session)
246
173
{
247
174
    std::lock_guard<std::mutex> lg(guard);
248
 
    session_store.erase(session->path());
249
 
 
250
 
    VLOG(1) << "# of session in session store: " << session_store.size() << std::endl;
 
175
    bool inserted = false;
 
176
    std::tie(std::ignore, inserted) = session_store.insert(std::make_pair(path, session));
 
177
    return inserted;
 
178
}
 
179
 
 
180
core::Property<bool>& culs::Skeleton::does_satellite_based_positioning()
 
181
{
 
182
    return *properties.does_satellite_based_positioning;
 
183
}
 
184
 
 
185
core::Property<bool>& culs::Skeleton::does_report_cell_and_wifi_ids()
 
186
{
 
187
    return *properties.does_report_cell_and_wifi_ids;
 
188
}
 
189
 
 
190
core::Property<bool>& culs::Skeleton::is_online()
 
191
{
 
192
    return *properties.is_online;
 
193
}
 
194
 
 
195
core::Property<std::map<cul::SpaceVehicle::Key, cul::SpaceVehicle>>& culs::Skeleton::visible_space_vehicles()
 
196
{
 
197
    return *properties.visible_space_vehicles;
251
198
}