[GH-ISSUE #97] Android background location limits prevent location from being retrieved #65

Closed
opened 2026-02-26 21:31:41 +03:00 by kerem · 6 comments
Owner

Originally created by @lbdroid on GitHub (Jan 10, 2023).
Original GitHub issue: https://github.com/seemoo-lab/AirGuard/issues/97

Since AOSP 8, there are limitations placed on background location access for the supposed purpose of reducing battery consumption.

https://developer.android.com/about/versions/oreo/background-location-limits

In practice, background location access is very severely reduced to about 2-3 times per hour.

The easiest way to work around this limitation is to create a foreground service (see link above).

Power consumption on location access can be reduced by only requesting location after a tracking device is already detected. The current process in ScanBluetoothWorker is that first it begins a location request, THEN it begins scanning for trackers, and then it waits for the location request to complete and puts them together. I would propose that it first scans for trackers, then ONLY IF a tracker is detected, begin and wait for a location.

Originally created by @lbdroid on GitHub (Jan 10, 2023). Original GitHub issue: https://github.com/seemoo-lab/AirGuard/issues/97 Since AOSP 8, there are limitations placed on background location access for the supposed purpose of reducing battery consumption. https://developer.android.com/about/versions/oreo/background-location-limits In practice, background location access is very severely reduced to about 2-3 times per hour. The easiest way to work around this limitation is to create a *foreground service* (see link above). Power consumption on location access can be reduced by only requesting location after a tracking device is already detected. The current process in ScanBluetoothWorker is that first it begins a location request, THEN it begins scanning for trackers, and then it waits for the location request to complete and puts them together. I would propose that it first scans for trackers, then ONLY IF a tracker is detected, begin and wait for a location.
kerem closed this issue 2026-02-26 21:31:42 +03:00
Author
Owner

@lbdroid commented on GitHub (Jan 10, 2023):

Create basic foreground service like this (also adding the service to AndroidManifest.xml);

public class ForegroundService extends Service {
    private static final String LOG_TAG = "Tracker Service";
    private final String serviceChannelId = "tracker_detector_s";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        // runs first
        super.onCreate();

        Log.d(LOG_TAG, "Running onCreate()");

        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationChannel mChannel = new NotificationChannel(serviceChannelId, "Nextcloud Service", NotificationManager.IMPORTANCE_NONE);
            mChannel.setDescription("Tracker Detector Service");
            notificationManager.createNotificationChannel(mChannel);
        }

        Notification newNotification =
                new NotificationCompat.Builder(ForegroundService.this, serviceChannelId)
                        .setSmallIcon(android.R.drawable.ic_input_add)
                        .setContentTitle("Tracker-Service")
                        .setContentText("Keep gps working")
                        .build();

        startForeground(88811, newNotification);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // runs second
        return START_STICKY;
    }
}

Add this near the end of TrackingDetectorWorker.doWork():

        val service = Intent(getApplicationContext(), ForegroundService::class.java)
        getApplicationContext()!!.startForegroundService(service)

And rejigging ScanBluetoothWorker.doWork():

        Timber.d("Scanning for bluetooth le devices stopped!. Discovered ${scanResultDictionary.size} devices")

        if (SharedPrefs.useLocationInTrackingDetection && scanResultDictionary.size > 0) {
            val lastLocation = locationProvider.getLastLocation()

            if (lastLocation != null) {
                // Location matches the requirement. No need to request a new one
                location = lastLocation
                Timber.d("Using last location")
            } else {
                //Getting the most accurate location here
                locationProvider.getCurrentLocation { loc ->
                    this.location = loc
                    Timber.d("Updated to current location")
                }

                val fetchedLocation = this.waitForRequestedLocation()
                Timber.d("Fetched location? ${fetchedLocation}")
            }
        }

The above makes location acquisition work infinitely better.
The service doesn't have to do anything at all besides just sit there in the foreground in order to escape from the background location rate restrictions. The rejigging of ScanBluetoothWorker just avoids wasting power on finding a location if no bluetooth device was detected.

<!-- gh-comment-id:1377844628 --> @lbdroid commented on GitHub (Jan 10, 2023): Create basic foreground service like this (also adding the service to AndroidManifest.xml); ``` public class ForegroundService extends Service { private static final String LOG_TAG = "Tracker Service"; private final String serviceChannelId = "tracker_detector_s"; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { // runs first super.onCreate(); Log.d(LOG_TAG, "Running onCreate()"); NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { NotificationChannel mChannel = new NotificationChannel(serviceChannelId, "Nextcloud Service", NotificationManager.IMPORTANCE_NONE); mChannel.setDescription("Tracker Detector Service"); notificationManager.createNotificationChannel(mChannel); } Notification newNotification = new NotificationCompat.Builder(ForegroundService.this, serviceChannelId) .setSmallIcon(android.R.drawable.ic_input_add) .setContentTitle("Tracker-Service") .setContentText("Keep gps working") .build(); startForeground(88811, newNotification); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // runs second return START_STICKY; } } ``` Add this near the end of TrackingDetectorWorker.doWork(): ``` val service = Intent(getApplicationContext(), ForegroundService::class.java) getApplicationContext()!!.startForegroundService(service) ``` And rejigging ScanBluetoothWorker.doWork(): ``` Timber.d("Scanning for bluetooth le devices stopped!. Discovered ${scanResultDictionary.size} devices") if (SharedPrefs.useLocationInTrackingDetection && scanResultDictionary.size > 0) { val lastLocation = locationProvider.getLastLocation() if (lastLocation != null) { // Location matches the requirement. No need to request a new one location = lastLocation Timber.d("Using last location") } else { //Getting the most accurate location here locationProvider.getCurrentLocation { loc -> this.location = loc Timber.d("Updated to current location") } val fetchedLocation = this.waitForRequestedLocation() Timber.d("Fetched location? ${fetchedLocation}") } } ``` The above makes location acquisition work infinitely better. The service doesn't have to do anything at all besides just sit there in the foreground in order to escape from the background location rate restrictions. The rejigging of ScanBluetoothWorker just avoids wasting power on finding a location if no bluetooth device was detected.
Author
Owner

@Sn0wfreezeDev commented on GitHub (Jan 23, 2023):

Hi,
Yes we fixed this issue in the next version. Stay tuned for the update 😊

<!-- gh-comment-id:1400062860 --> @Sn0wfreezeDev commented on GitHub (Jan 23, 2023): Hi, Yes we fixed this issue in the next version. Stay tuned for the update 😊
Author
Owner

@jayb-g commented on GitHub (Oct 14, 2024):

Also, support for opencellid key can be added to have more frequent location data but not GPS based, so to save battery

<!-- gh-comment-id:2410288708 --> @jayb-g commented on GitHub (Oct 14, 2024): Also, support for opencellid key can be added to have more frequent location data but not GPS based, so to save battery
Author
Owner

@Sn0wfreezeDev commented on GitHub (Oct 14, 2024):

How would that be implemented? Is that part of Android's location access API?

<!-- gh-comment-id:2410555372 --> @Sn0wfreezeDev commented on GitHub (Oct 14, 2024): How would that be implemented? Is that part of Android's location access API?
Author
Owner

@jayb-g commented on GitHub (Oct 14, 2024):

No, I meant instead of the app getting location data only from GPS, it could also additionally/alternatively get location from opencellid api(if user has set an api key for it in the settings). Similar to how Find My Device(FMD) does it. Check screenshots.

<!-- gh-comment-id:2410773057 --> @jayb-g commented on GitHub (Oct 14, 2024): No, I meant instead of the app getting location data only from GPS, it could also additionally/alternatively get location from opencellid api(if user has set an api key for it in the settings). Similar to how [Find My Device(FMD)](https://f-droid.org/packages/de.nulide.findmydevice/) does it. Check screenshots.
Author
Owner

@jayb-g commented on GitHub (Nov 25, 2024):

@Sn0wfreezeDev
I meant yes, it is part of Android's location access API but cell location would work only if user has GMS. For non-GMS users, having an option to use a third party like opencellid would be ideal.

<!-- gh-comment-id:2497196741 --> @jayb-g commented on GitHub (Nov 25, 2024): @Sn0wfreezeDev I meant yes, it is part of Android's location access API but cell location would work only if user has GMS. For non-GMS users, having an option to use a third party like opencellid would be ideal.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/AirGuard#65
No description provided.