5

Background: We have an app that does time registration for a group of people. In this example we use a Zebra TC26 (model: TC26BK-11A222-A6) and we clock in multiple people by scanning. Next the app tracks the GPS position of the phone to locate in which field the activity is performed. The farmer can then calculated its costs per field more accuratly.

This al worked fine till android 10. Now I have on my device that I don't get a new location after like 1h or 1h30. On another device I sometimes got it longer but than it also stopped. Since Android 10 I am having issues and I found a lot of information online, but it doesn't seem to fix the issue. A normal workday:

  • Start around 8 AM. and then device is in the pocket till lunch
  • Lunch between 1:00 PM and 1:30 PM and again device goes in the pocket.
  • End around 5PM.

So during the work they don't use the phone.

Extra:

  • In the past I tried with a wakelock on the screen. This worked for android 8/9 to activate the GPS again. but this doesn't work on android 10/11
  • I also added the background location access while not required but to see if I can enable "location always" if this had in affect.
  • The antiDoze service was also something from few years ago to keep the CPU awake. note that the channelID for the notification is the same. I don't know if this has an influence.
  • I also see in my app that most of the times (like 99%) the server still receives data from the device but the location is empty. In the 1% case, the device is not sending data.
  • I don't use the fuse location provider as not all devices have the GMS service on them.
  • It is possible that the answer is that I need to rewrite some things. For me this is not a problem as my end goal is to have a working product. If this is the case, then please provide me with either a detailed plan and where to put what or to provide a link to a tutorial.

Manifest: (I left out some permissions and an antiDozeService as I think they are not relevant for the GPS part)

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>

 <activity android:name=".SplashActivity"
           android:theme="@style/SplashTheme">
               <intent-filter>
                  <action android:name="android.intent.action.MAIN" />
                  <category android:name="android.intent.category.LAUNCHER" />
               </intent-filter>
</activity>
<activity android:name=".MainActivity" 
          android:launchMode="singleTop" 
          android:windowSoftInputMode="stateAlwaysHidden">
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
</activity>

<service
   android:name=".Services.LocationTracker"
   android:enabled="true"
   android:foregroundServiceType="location">
</service>

Main acitivity (note the splash activity is the entry point but was added later so this is still called the main activity)

protected void onCreate(Bundle savedInstanceState)
{
 ...

  Intent service = new Intent(this, xxx.LocationTracker.class);
  service.setAction("STARTFOREGROUND_ACTION");
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
         startForegroundService(service);
   }else{
        startService(service);
   }
}

LocationTracker: (onStartCommand)

public int onStartCommand(Intent intent, int flags, int startId) {
        //Show notification
        Intent notificationIntent = new Intent(mContext, MainActivity.class);
        notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);

        //Create notification channel
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = getString(R.string.service_channel_name);
            String description = getString(R.string.service_channel_description);
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            NotificationChannel channel = new NotificationChannel("12345678", name, importance);
            channel.setDescription(description);
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            NotificationManager notificationManager = getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(channel);
        }

        Notification notification = new NotificationCompat.Builder(this, "12345678")
                .setContentTitle("My app title")
                .setTicker("My app title")
                .setContentText("Application is running")
                .setSmallIcon(R.drawable.xxxx)
                .setContentIntent(pendingIntent)
                .build();

        // starts this service as foreground
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
        {
            startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
            //=> My latest test I added here the "FOREGROUND_SERVICE_TYPE_LOCATION"
        }
        else{
            startForeground("12345678", notification);
        }

        return START_STICKY;
}

LocationTracker: (onCreate)

@Override
    public void onCreate() {
        //LogUtils.debug("LocationTracker service started");
        mContext = getApplicationContext();

       //Location listener
        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            public void run() {
                try{
                    if(StaticVariables.LocationAllowed)
                    {
                        if(StaticVariables.LocationTrackingActive)
                        {
                            //Check if we found a point.
                            Location location = getLatestPoint();
                            writeLocationToServer(location);

                            startLocationTracking();
                        }
                        else
                        {
                            //LogUtils.debug("Location 1: Location tracking is not active");
                            stopLocationListener();
                        }
                    }
                    handler.postDelayed(this, HANDLER_DELAY);
                }
                catch (Exception ex)
                {
                    LogUtils.debug("Location failed to run: " + ex.getMessage());
                }
            }
        }, 1000);

LocationTracker: (getLatestPoint)

private Location getLatestPoint()
    {
        Location gpsPoint = null;
        Location networkPoint = null;

        LogUtils.debug("Getting latest point");

        if(gpsListener != null)
        {
            gpsPoint = gpsListener.getLastLocation();
        }
        if(networkListener != null)
        {
            networkPoint = networkListener.getLastLocation();
        }

        if(gpsPoint != null && networkPoint != null)
        {
            if(gpsPoint.getAccuracy() < networkPoint.getAccuracy())
            {
                return gpsPoint;
            }
            else
            {
                return networkPoint;
            }
        }
        else if(gpsPoint != null)
        {
            return gpsPoint;
        }
        else if(networkPoint != null)
        {
            return networkPoint;
        }
        else
        {
            return null;
        }
    }

Location listener:

public CustomerLocationListener(LocationManager locationManager, String provider) {
        this.locationManager = locationManager;
        this.provider = provider;
}

@Override
    public void onLocationChanged(Location location) {
        this.location = location;
        stopLocationListener();
}

@SuppressLint("MissingPermission") //=> Not an issue as this is checked before starting
    public void startLocationListener()
    {
        if(StaticVariables.LocationAllowed)
        {
            if(locationManager.isProviderEnabled(provider))
            {
                locationManager.requestLocationUpdates(provider, GPS_TIME_INTERVAL, GPS_DISTANCE,this);
            }
            else
            {
                //LogUtils.debug("Provider: " + provider + " is not available");
            }
        }
    }

public void stopLocationListener()
{
        if(locationManager != null)
        {
            locationManager.removeUpdates(this); // remove this listener
        }
}

build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 30
    buildToolsVersion '30.0.3'
    defaultConfig {
        applicationId "xxxx"
        minSdkVersion 21
        targetSdkVersion 30
        versionCode 28
        versionName "VERSIONNAME"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        multiDexEnabled true //For zxing
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    apply plugin: 'com.android.application'
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}

...

The interval was: "1 request / 20 seconds" but I have already changed this to "1 request/min"

I hope that someone can help me find a solution as this is a popular product. Thank you for your time.

Boeykes
  • 137
  • 2
  • 9
  • Did you ever come up with a solution? I am facing the same problem using fused location. 90% of runs everything works as expected but with the remaining 10% GPS updates stop after some time (30-60 minutes). The only solution to get updates again is by restarting the app. The code is almost 1-1 copied from https://github.com/android/location-samples/blob/master/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java. – Hyndrix Dec 24 '21 at 13:39
  • Hey, yes, the problem was that the gps stops working if the device is left without movement for a while (30 to 60 minutes). Once the device was moved, then the gps reactivated. (This was all while the device was outside) – Boeykes Dec 25 '21 at 14:09
  • Hi @Boeykes, the problem didn't appear again even if the device is moving? Have you tested it on Android 13? I'm facing the same problem, GPS stops to return fresh positions and always returns the same last valid position during 1-2h, on a 6-8h tracking route. I've tried everything, request to avoid battery optimizations, using & refreshing wakelock PARTIAL_WAKE_LOCK, restart GPS provider subscription every 10 minutes... but something blocks the app from getting fresh positions for a while... we are testing on a Samsung A23 device. Thanks a lot for your feedback! – Pelanes Jul 21 '23 at 07:56
  • 1
    Hi @Pelanes, I did not update the app for android 13 yet. I will keep you posted if I have the problem also on android 13. – Boeykes Jul 22 '23 at 10:23

1 Answers1

1

If you want to keep your service running in Android 10 & above devices, then you must use startForeground(id, notification) otherwise, it's going to get killed by the system after some time.

You have to make the user aware that your app is constantly running in background as mentioned in the new API changes. So try with just a simple notification & send only current location on some time interval and check if it's still running or getting killed.

You also have to turn off the battery optimisation for your app otherwise, the same problem will occur.

You can find more information about startForeground as well as how to ignore battery optimisation programmatically on Google.

So Just give it a try. :)

Mars
  • 4,197
  • 11
  • 39
  • 63
Dev4Life
  • 2,328
  • 8
  • 22