Search code examples
javaandroidservicenotificationsbackground

getting location updates in foreground service isn't working when app is moved to background


i'm using the below app code to recieve locations updates from the GPS every 5 seconds and print an output to the log. this works fine for me as long as the app is in foreground, but the updates stop when it is moved to background until it is being brought back to foreground. i'm using galaxy s20 (android 10) to run and debug, and the application is set to never be put to sleep (android setting). here is the full process description:

i have these permissions at the manifest:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.android.hardware.location.gps"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

this is the main activity that enables the GPS and creates the service:

public class MainActivity extends AppCompatActivity {

    private LocationManager _locManager;
    private Intent _scanServiceIntent;

    public void onStart()
    {
        super.onStart();
        boolean gps_enabled = _locManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        if (gps_enabled)
        {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.INTERNET}, 1);
            _scanServiceIntent = new Intent(this, ScanService.class);
            startService(_scanServiceIntent);
        }       
    }
}

this is the service class - i'm creating a notification in order to make the serice run all the time - even when the app goes to background:

public class ScanService extends Service {

    private LocationListener _locListener;
    private LocationManager _locManager;


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        String NOTIFICATION_CHANNEL_ID = "com.tandenkore.mychannel";

        PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);

        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        Notification notification = notificationBuilder.setOngoing(true)
                .setContentTitle("App is running in background")
                .setPriority(NotificationManager.IMPORTANCE_MAX)
                .setCategory(Notification.CATEGORY_SERVICE)
                .setContentIntent(pendingIntent)
                .build();
        startForeground(300, notification);
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        _locListener = new MyLocationListener();
        _locManager = (LocationManager)getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
        _locManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 0, _locListener);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopForeground(true);
        _locManager.removeUpdates(_locListener);
    }

    class MyLocationListener implements LocationListener {
        @Override
        public void onLocationChanged(Location location)
        {
            Log.v(TAG, "location changes");
        }
    }
}

when i debug it on the device and the application is in the foreground, i am getting an output in logcat of "location changes" line every 5 seconds. this is the behavior i am looking for. the problem happens when the app is going to background, then i receive the following in logcat:

2021-04-11 00:00:26.648 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:31.657 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:36.656 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:41.656 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:46.653 26160-26160/com.tandenkore.application V/application: location changes
2021-04-11 00:00:50.170 7241-7307/? E/RequestManager_FLP: [LocationManager] Paused by AppOps 7e14624(Listener) gps interval=5000 from com.tandenkore.application (10367)
2021-04-11 00:00:50.173 7241-7307/? I/RequestManager_FLP: onOpChanged, op=0 / packageName=com.tandenkore.application
2021-04-11 00:00:50.174 7241-7307/? I/PackageInfoManager_FLP: checkLocationAccess for com.tandenkore.application[gps,5000,100], result is false

but after this last message - no more updates until i bring the app back to the foreground. i want it to keep it running ALL the time - what else needs to be done for this?


Solution

  • According to the official documentation :

    When a feature in your app requests background location on a device that runs Android 10 (API level 29), the system permissions dialog includes an option named Allow all the time. If the user selects this option, the feature in your app gains background location access.

    On Android 11 (API level 30) and higher, however, the system dialog doesn't include the Allow all the time option. Instead, users must enable background location on a settings page, as shown in figure 3.

    So you need to see this option named Allow all the time in the Setting page of your application

    enter image description here

    for that you must have this permission in your manifest :

    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
    

    On API 30 devices, and below so that the Allow All the time option will be visible in the Dialog you must Add the "Manifest.permission.ACCESS_BACKGROUND_LOCATION" when you ask the user to grant them permission. as below

    ActivityCompat.requestPermissions(MainActivity.this,
                    new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION ,Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION},
                    1);
    

    as you see the dialog has the Allow all the time

    enter image description here

    But on the Android Api 30 and higher you must invite the user to check this option manually against the API < 30

    So if you want to load the Setings page you can use this method :

       // load the permission setting screen
        private void loadPermissionPage(Activity context) {
            Intent intent = new Intent();
            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
            Uri uri = Uri.fromParts("package", context.getPackageName(), null);
            intent.setData(uri);
            context.startActivityForResult(intent, 0);
        }
    

    And finally if you check the Allow all the time option your application will be able to listen to the location changes in the background

    UPDATE :

    For Android 30 and higher you can ask the user to garant the Background Location by opening the location permission pages directly using this line of code :

    requestPermissions(new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, REQUEST_CODE);
    

    For more information see the Request background location