In my last post on Android platform integration I had suggested increasing the focus on Linux on mobile phones, due to Google’s ongoing attempts to close down Android for us. I have to follow that myself then of course, starting with looking at the situation around location/positioning.

What we have

The positioning stack looks roughly as follows as far as KDE applications are concerned:

  • Geoclue collects positioning data from various sources (GNSS receiver, cell modem, online service, etc) and provides a D-Bus API for it.
  • XDG Desktop Portal has a Location API for exposing this to sandboxed applications, including permission handling. XDG Desktop Portal itself uses Geoclue as its source.
  • Qt Positioning provides the application-facing API for retrieving position data, and QLocationPermission provides the API for requesting permissions when running sandboxed.

There’s two main gaps here though:

  • Qt Positioning only has a Geoclue backend, not one for the XDG Desktop Portal Location API. This means we wont have access to positioning information in a sandbox.
  • Qt’s entire permission API has only a dummy implementation on Linux, meaning it will always claim all permissions have been granted, which isn’t true in a sandbox.

What I couldn’t look into is whether Geoclue is able to actually retrieve GNSS data on “real” hardware, lacking access to devices to test this on.

Developer Tooling

Testing GNSS code with real hardware is rather inconvenient anyway, you have to move around for this, and quite a bit even when you also want to test various edge cases. Much better would be a way to inject arbitrary GNSS data low enough in the stack.

Many years ago I had built something like this for GammaRay, but that works on the level of Qt Positioning, which is above the parts we want to test here. Fortunately Geoclue offers us a way to do this as well. It’s looking for _nmea-0183._tcp mDNS services that provide a NMEA 0183 feed, and will use that as a source for GNSS data.

NMEA 0183 is a decades-old serial port protocol for GNSS equipment, easy enough to implement. As there are more usecases for this below, there’s now a small library setting up such a services, announcing it via mDNS and sending NMEA 0183 messages. NMEA 0183 defines two dozen or so different message types, but since Geoclue only looks at GGA and RMC ones those are all we need.

Screenshot of a map view showing a GPX track replay on the left and simulated and received position data in text form on the right.
XDG portal location spoofing tool.

Then all we need is a little GUI on top of this to select inputs on a map and have that fed into Geoclue. As a bonus we also have GPX track replay, so you can get a continuous feed of position updates automatically. The code is here.

XDG Portal plugin for Qt Positioning

Being able to “move” around the world from the convenience of my desk/couch made it then easy to implement the main missing piece here, connecting Qt Positioning to the XDG Desktop Portal Location API. The code is here, it’s just a couple of D-Bus calls and a bit of boilerplate needed for positioning plugins.

It’s registered with a higher priority than the Geoclue plugin and will be skipped if the portal API is not available.

QPermission API

That still leaves the permission handling. Qt’s API for that lives in Qt Core, so we cannot just implement support for XDG protal permissions there, as that needs dependencies from higher up in the stack, such as D-Bus and window ids.

There’s an easy way out though, by generalizing an already existing permission plugin system that so far is only provided on Apple platforms. A patch to Qt enabling this on Unix systems as well is in review currently. With that applied implementing support for requesting location permissions is then very similar to what the positioning plugin already does, you’ll find the code here.

A very convenient side-effect of having permission plugins is that we can now also build a “simulator” that allows testing permission flows in applications while running in an unrestricted development environment and without having to mess with system permission settings each time.

So I did that here, usage is pretty simple:

$ qpermission-simulator [--ask|--grant|--deny] <application-to-test>

Note that this also needs the above mentioned Qt patch to actually do anything, and it currently requires the tested application to be a QApplication in order to show its UI. It does work for all permission types supported by Qt though, not just the location one.

Other positioning sources

One unique feature that microG offers on Google-free Android is using onboard APIs of planes/trains/buses as a location source. That’s useful as the GNSS antennas of those vehicles tend to give you much better results than the one on your phone inside the metal casing of the vehicle.

Of course we have to have that as well, and it’s easy enough to build that since we have an existing library for dealing with onboard APIs already as part of KPublicTransport.

Putting that together with the NMEA 0183 server we get a small KDED module that watches for changes to the Wi-Fi network you are connected to, and if it’s one of a known onboard API it’ll offer its service to Geoclue. As Geoclue will only connect to it on demand (ie. when an application actually asks for a location) it will only poll the onboard API when necessary, so we also get practically no overhead in the idle state here.

Other sources are possible in a similar way as well, e.g. having KDE Connect provide location data from your phone to your desktop computer.

Feedback

This is mostly the result of about two weeks of prototyping, and I’d very much appreciate review and feedback on this. Does the general approach make sense? Is there something else missing around the the location/position topic? Where should the various components live and be distributed eventually (some of this only really relevant inside a Flatpak sandbox fox example)?