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

« back to all changes in this revision

Viewing changes to src/location_service/com/ubuntu/location/connectivity/ofono_nm_connectivity_manager.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:
 
1
/*
 
2
 * Copyright © 2012-2013 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 "ofono_nm_connectivity_manager.h"
 
20
 
 
21
namespace connectivity = com::ubuntu::location::connectivity;
 
22
namespace dbus = core::dbus;
 
23
namespace xdg = org::freedesktop;
 
24
namespace
 
25
{
 
26
connectivity::State from_nm_property(std::uint32_t value)
 
27
{
 
28
    connectivity::State result{connectivity::State::unknown};
 
29
 
 
30
    switch (value)
 
31
    {
 
32
    case xdg::NetworkManager::Properties::Connectivity::Values::unknown:
 
33
        result = connectivity::State::unknown;
 
34
        break;
 
35
    case xdg::NetworkManager::Properties::Connectivity::Values::none:
 
36
        result = connectivity::State::none;
 
37
        break;
 
38
    case xdg::NetworkManager::Properties::Connectivity::Values::portal:
 
39
        result = connectivity::State::portal;
 
40
        break;
 
41
    case xdg::NetworkManager::Properties::Connectivity::Values::limited:
 
42
        result = connectivity::State::limited;
 
43
        break;
 
44
    case xdg::NetworkManager::Properties::Connectivity::Values::full:
 
45
        result = connectivity::State::full;
 
46
        break;
 
47
    }
 
48
 
 
49
    return result;
 
50
}
 
51
}
 
52
 
 
53
const core::Property<connectivity::State>& impl::OfonoNmConnectivityManager::state() const
 
54
{
 
55
    return d.state;
 
56
}
 
57
 
 
58
void impl::OfonoNmConnectivityManager::request_scan_for_wireless_networks()
 
59
{
 
60
    std::lock_guard<std::mutex> lg(d.cached.guard);
 
61
 
 
62
    for (const auto& pair : d.cached.wireless_devices)
 
63
        pair.second.request_scan();
 
64
}
 
65
 
 
66
const core::Signal<>& impl::OfonoNmConnectivityManager::wireless_network_scan_finished() const
 
67
{
 
68
    return d.signals.wireless_network_scan_finished;
 
69
}
 
70
 
 
71
const core::Signal<connectivity::WirelessNetwork::Ptr>& impl::OfonoNmConnectivityManager::wireless_network_added() const
 
72
{
 
73
    return d.signals.wireless_network_added;
 
74
}
 
75
 
 
76
const core::Signal<connectivity::WirelessNetwork::Ptr>& impl::OfonoNmConnectivityManager::wireless_network_removed() const
 
77
{
 
78
    return d.signals.wireless_network_removed;
 
79
}
 
80
 
 
81
void impl::OfonoNmConnectivityManager::enumerate_visible_wireless_networks(const std::function<void(const connectivity::WirelessNetwork::Ptr&)>& f) const
 
82
{
 
83
    std::lock_guard<std::mutex> lg(d.cached.guard);
 
84
    for (const auto& wifi : d.cached.wifis)
 
85
        f(wifi.second);
 
86
}
 
87
 
 
88
const core::Signal<connectivity::RadioCell::Ptr>& impl::OfonoNmConnectivityManager::connected_cell_added() const
 
89
{
 
90
    return d.signals.connected_cell_added;
 
91
}
 
92
 
 
93
const core::Signal<connectivity::RadioCell::Ptr>& impl::OfonoNmConnectivityManager::connected_cell_removed() const
 
94
{
 
95
    return d.signals.connected_cell_removed;
 
96
}
 
97
 
 
98
void impl::OfonoNmConnectivityManager::enumerate_connected_radio_cells(const std::function<void(const connectivity::RadioCell::Ptr&)>& f) const
 
99
{
 
100
    std::lock_guard<std::mutex> lg(d.cached.guard);
 
101
    for (const auto& cell : d.cached.cells)
 
102
        f(cell.second);
 
103
}
 
104
 
 
105
impl::OfonoNmConnectivityManager::Private::Private()
 
106
{
 
107
    try
 
108
    {
 
109
        system_bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::system);
 
110
        executor = dbus::asio::make_executor(system_bus);
 
111
        system_bus->install_executor(executor);
 
112
 
 
113
        worker = std::move(std::thread
 
114
        {
 
115
           [this]()
 
116
           {
 
117
               system_bus->run();
 
118
           }
 
119
        });
 
120
 
 
121
        setup_network_stack_access();
 
122
        setup_radio_stack_access();
 
123
    }
 
124
    catch (const std::exception& e)
 
125
    {
 
126
        LOG(ERROR) << "Error while setting up access to radio and network stack: " << e.what();
 
127
    }
 
128
}
 
129
 
 
130
impl::OfonoNmConnectivityManager::Private::~Private()
 
131
{
 
132
    if (system_bus)
 
133
        system_bus->stop();
 
134
 
 
135
    if (worker.joinable())
 
136
        worker.join();
 
137
}
 
138
 
 
139
void impl::OfonoNmConnectivityManager::Private::setup_radio_stack_access()
 
140
{
 
141
    modem_manager.reset(new org::Ofono::Manager(system_bus));
 
142
 
 
143
    modem_manager->for_each_modem([this](const core::dbus::types::ObjectPath& path)
 
144
    {
 
145
        try
 
146
        {
 
147
            on_modem_added(path);
 
148
        }
 
149
        catch(const std::runtime_error& e)
 
150
        {
 
151
            LOG(WARNING) << "Exception while creating connected radio cell: " << e.what();
 
152
        }
 
153
    });
 
154
 
 
155
    modem_manager->signals.modem_added->connect([this](const org::Ofono::Manager::ModemAdded::ArgumentType& arg)
 
156
    {
 
157
        try
 
158
        {
 
159
            on_modem_added(std::get<0>(arg));
 
160
        }
 
161
        catch(const std::exception& e)
 
162
        {
 
163
            LOG(WARNING) << "Exception while adding modem: " << e.what();
 
164
        }
 
165
    });
 
166
 
 
167
    modem_manager->signals.modem_removed->connect([this](const core::dbus::types::ObjectPath& path)
 
168
    {
 
169
        try
 
170
        {
 
171
            on_modem_removed(path);
 
172
        }
 
173
        catch(const std::exception& e)
 
174
        {
 
175
            LOG(WARNING) << "Exception while removing modem: " << e.what();
 
176
        }
 
177
    });
 
178
}
 
179
 
 
180
void impl::OfonoNmConnectivityManager::Private::on_modem_added(const core::dbus::types::ObjectPath& path)
 
181
{
 
182
    auto modem = modem_manager->modem_for_path(path);
 
183
 
 
184
    // We first wire up to property changes here.
 
185
    modem.signals.property_changed->connect([this, path](const std::tuple<std::string, core::dbus::types::Variant>& tuple)
 
186
    {
 
187
        const auto& key = std::get<0>(tuple); VLOG(10) << "Property changed for modem: " << key;
 
188
 
 
189
        if (org::Ofono::Manager::Modem::Properties::Interfaces::name() == key)
 
190
        {
 
191
            auto interfaces = std::get<1>(tuple).as<std::vector<std::string> >();
 
192
            if (VLOG_IS_ON(10)) for(const auto& interface : interfaces) VLOG(10) << interface;
 
193
            on_modem_interfaces_changed(path, interfaces);
 
194
        }
 
195
 
 
196
    });
 
197
 
 
198
    // And update our cache of modems and registered cells.
 
199
    auto cell = std::make_shared<CachedRadioCell>(modem);
 
200
    {
 
201
        std::lock_guard<std::mutex> lg(cached.guard);
 
202
        cached.modems.insert(std::make_pair(modem.object->path(), modem));
 
203
        cached.cells.insert(std::make_pair(modem.object->path(), cell));
 
204
    }
 
205
    // Announce the newly added cell to API customers, without the lock
 
206
    // on the cache being held.
 
207
    signals.connected_cell_added(cell);
 
208
}
 
209
 
 
210
void impl::OfonoNmConnectivityManager::Private::on_modem_removed(const core::dbus::types::ObjectPath& path)
 
211
{
 
212
    CachedRadioCell::Ptr cell;
 
213
    {
 
214
        std::lock_guard<std::mutex> lg(cached.guard);
 
215
 
 
216
        // Update modem and cell cache.
 
217
        auto itc = cached.cells.find(path);
 
218
        auto itm = cached.modems.find(path);
 
219
 
 
220
        if (itc != cached.cells.end())
 
221
        {
 
222
            cell = itc->second;
 
223
            cached.cells.erase(itc);
 
224
        }
 
225
 
 
226
        if (itm != cached.modems.end())
 
227
        {
 
228
            cached.modems.erase(path);
 
229
        }
 
230
    }
 
231
    // Inform customers of the registered cell being removed, without
 
232
    // the lock on the cache being held.
 
233
    if (cell) signals.connected_cell_removed(cell);
 
234
}
 
235
 
 
236
void impl::OfonoNmConnectivityManager::Private::on_modem_interfaces_changed(
 
237
        const core::dbus::types::ObjectPath& path,
 
238
        const std::vector<std::string>& interfaces)
 
239
{
 
240
    std::unique_lock<std::mutex> ul(cached.guard);
 
241
 
 
242
    auto itm = cached.modems.find(path);
 
243
    if (itm == cached.modems.end())
 
244
    {
 
245
        LOG(WARNING) << "Could not find a modem for path " << path.as_string();
 
246
        return;
 
247
    }
 
248
 
 
249
    auto itt = cached.cells.find(path);
 
250
    const bool has_cell_for_modem = itt != cached.cells.end();
 
251
 
 
252
    auto it = std::find(
 
253
                interfaces.begin(),
 
254
                interfaces.end(),
 
255
                std::string{org::Ofono::Manager::Modem::Properties::Interfaces::network_registration});
 
256
    const bool modem_has_network_registration = it != interfaces.end();
 
257
 
 
258
    if (has_cell_for_modem and not modem_has_network_registration)
 
259
    {
 
260
        // A network registration was lost and we remove the corresponding
 
261
        // cell instance from the cache.
 
262
        auto cell = itt->second;
 
263
        cached.cells.erase(itt);
 
264
 
 
265
        // Cache is up to date now and we announce the removal of the cell
 
266
        // to API customers, with the lock on the cache not being held.
 
267
        ul.unlock(); signals.connected_cell_removed(cell);
 
268
    } else if (not has_cell_for_modem and modem_has_network_registration)
 
269
    {
 
270
        // A new network registration is coming in and we have to create
 
271
        // a corresponding cell instance.
 
272
        auto cell = std::make_shared<CachedRadioCell>(itm->second);
 
273
        cached.cells.insert(std::make_pair(path,cell));
 
274
        // Cache is up to date now and we announce the new cell to
 
275
        // API customers, with the lock on the cache not being held.
 
276
        ul.unlock(); signals.connected_cell_added(cell);
 
277
    }
 
278
}
 
279
 
 
280
void impl::OfonoNmConnectivityManager::Private::setup_network_stack_access()
 
281
{
 
282
    network_manager.reset(new xdg::NetworkManager(system_bus));
 
283
 
 
284
    network_manager->for_each_device([this](const core::dbus::types::ObjectPath& device_path)
 
285
    {
 
286
        auto device = network_manager->device_for_path(device_path);
 
287
 
 
288
        if (device.type() == xdg::NetworkManager::Device::Type::wifi)
 
289
        {
 
290
            // Make the device known to the cache.
 
291
            cached.wireless_devices.insert(std::make_pair(device_path, device));
 
292
 
 
293
            // Iterate over all currently known wifis
 
294
            device.for_each_access_point([this, device_path](const core::dbus::types::ObjectPath& path)
 
295
            {
 
296
                try
 
297
                {
 
298
                    on_access_point_added(path, device_path);
 
299
                }
 
300
                catch (const std::exception& e)
 
301
                {
 
302
                    LOG(ERROR) << "Error while creating ap/wifi: " << e.what();
 
303
                }
 
304
            });
 
305
 
 
306
            device.signals.scan_done->connect([this]()
 
307
            {
 
308
                signals.wireless_network_scan_finished();
 
309
            });
 
310
 
 
311
            device.signals.ap_added->connect([this, device_path](const core::dbus::types::ObjectPath& path)
 
312
            {
 
313
                try
 
314
                {
 
315
                    on_access_point_added(path, device_path);
 
316
                }
 
317
                catch (const std::exception& e)
 
318
                {
 
319
                    LOG(ERROR) << "Error while creating ap/wifi: " << e.what();
 
320
                }
 
321
            });
 
322
 
 
323
            device.signals.ap_removed->connect([this](const core::dbus::types::ObjectPath& path)
 
324
            {
 
325
                try
 
326
                {
 
327
                    on_access_point_removed(path);
 
328
                }
 
329
                catch (const std::exception& e)
 
330
                {
 
331
                    LOG(ERROR) << "Error while removing ap/wifi: " << e.what();
 
332
                }
 
333
            });
 
334
        }
 
335
    });
 
336
 
 
337
    // Query the initial connectivity state
 
338
    state.set(from_nm_property(network_manager->properties.connectivity->get()));
 
339
 
 
340
    // And we wire up to property changes here
 
341
    network_manager->signals.properties_changed->connect([this](const std::map<std::string, core::dbus::types::Variant>& dict)
 
342
    {
 
343
        for (const auto& pair : dict)
 
344
        {
 
345
            VLOG(1) << "Property has changed: " << std::endl
 
346
                    << "  " << pair.first;
 
347
 
 
348
            if (xdg::NetworkManager::Properties::Connectivity::name() == pair.first)
 
349
            {
 
350
                state.set(from_nm_property(pair.second.as<xdg::NetworkManager::Properties::Connectivity::ValueType>()));
 
351
            }
 
352
        }
 
353
    });
 
354
}
 
355
 
 
356
void impl::OfonoNmConnectivityManager::Private::on_access_point_added(
 
357
        const core::dbus::types::ObjectPath& ap_path,
 
358
        const core::dbus::types::ObjectPath& device_path)
 
359
{
 
360
    std::unique_lock<std::mutex> ul(cached.guard);
 
361
 
 
362
    // Let's see if we have a device known for the path. We return early
 
363
    // if we do not know about the device.
 
364
    auto itd = cached.wireless_devices.find(device_path);
 
365
    if (itd == cached.wireless_devices.end())
 
366
        return;
 
367
 
 
368
    xdg::NetworkManager::AccessPoint ap
 
369
    {
 
370
        network_manager->service->add_object_for_path(ap_path)
 
371
    };
 
372
 
 
373
    auto wifi = std::make_shared<CachedWirelessNetwork>(itd->second, ap);
 
374
    cached.wifis[ap_path] = wifi;
 
375
 
 
376
    // Let API consumers know that an AP appeared. The lock on the cache is
 
377
    // not held to prevent from deadlocks.
 
378
    ul.unlock(); signals.wireless_network_added(wifi);
 
379
}
 
380
 
 
381
void impl::OfonoNmConnectivityManager::Private::on_access_point_removed(const core::dbus::types::ObjectPath& ap_path)
 
382
{
 
383
    std::unique_lock<std::mutex> ul(cached.guard);
 
384
 
 
385
    // Let's see if we know about the wifi. We return early if not.
 
386
    auto itw = cached.wifis.find(ap_path);
 
387
    if (itw == cached.wifis.end())
 
388
        return;
 
389
 
 
390
    // Update the cache and keep the wifi object alive until API consumers
 
391
    // have been informed of the wifi going away.
 
392
    connectivity::WirelessNetwork::Ptr wifi = itw->second;
 
393
    cached.wifis.erase(itw);
 
394
 
 
395
    // Let API consumers know that an AP disappeared. The lock on the cache is
 
396
    // not held to prevent from deadlocks.
 
397
    ul.unlock(); signals.wireless_network_removed(wifi);
 
398
}
 
399
 
 
400
const std::shared_ptr<connectivity::Manager>& connectivity::platform_default_manager()
 
401
{
 
402
    static const std::shared_ptr<connectivity::Manager> instance{new impl::OfonoNmConnectivityManager{}};
 
403
    return instance;
 
404
}