seemoo-lab / AirGuard

Protect yourself from being tracked 🌍 by AirTags 🏷 and Find My accessories 📍
Apache License 2.0
1.96k stars 110 forks source link

Android background location limits prevent location from being retrieved #97

Closed lbdroid closed 1 year ago

lbdroid commented 1 year ago

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.

lbdroid commented 1 year ago

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.

Sn0wfreezeDev commented 1 year ago

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

jayb-g commented 1 month ago

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

Sn0wfreezeDev commented 1 month ago

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

jayb-g commented 1 month ago

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.

jayb-g commented 4 days ago

@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.