question

ManikishoreA-8992 avatar image
1 Vote"
ManikishoreA-8992 asked MaticDiBatista-6817 answered

Xamarin forms Foreground service is stopping automatically

Hi Everyone,

I'm pretty new to Xamarin Development.
And my work is to track the user's location for every X minutes between User Sign in and Sign out. So, I opted for the Foreground service. And in few of mobiles, it is running fine. But, In few mobiles the Foreground service is getting stopped automatically.

And have selected the Battery optimization as NO RESTRICTIONS. Still it is getting stopped automatically.

Any other conditions I can for this reason?


Here is my Foreground Service Code

 [Service]
 public class AndroidLocationService : Service
 {
     CancellationTokenSource _cts;
     public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
     System.Timers.Timer aTimer;
     public static event EventHandler<string> onNotificationRaised;

     public override IBinder OnBind(Intent intent)
     {
         return null;
     }

     public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
     {
         _cts = new CancellationTokenSource();

         Notification notif = DependencyService.Get<INotification>().ReturnNotif();
         StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notif);

         LocationTrackBL trackBL = new LocationTrackBL();
         var freq = trackBL.GetTrackFrequency();

         Utilities.Log("Foreground Created: " + freq);

         aTimer = new System.Timers.Timer();
         aTimer.Elapsed += new ElapsedEventHandler(GetLocation);
         aTimer.Interval = freq * 60000;
         aTimer.Enabled = true;
         aTimer.Start();

         Utilities.Log("Timer Started");

         return StartCommandResult.Sticky;
     }

     public async void GetLocation(object source, ElapsedEventArgs e)
     {
         Utilities.Log("Location Fired");
         LocationTrackBL trackBL = new LocationTrackBL();
         try
         {
             bool signout = trackBL.IsManualSignOutHappened_Valid();
             if (signout)
             {
                 aTimer.Enabled = false;
                 aTimer.Stop();
                 return;
             }

             bool signin = trackBL.IsManualSignInHappened_Valid();

             var freq = trackBL.GetTrackFrequency();
             DateTime tomorrow = DateTime.Now.Date.AddDays(1);
             TimeSpan difference = tomorrow - DateTime.Now;
             Utilities.Log("Time Difference: " + difference.TotalMinutes);

             if (difference.TotalMinutes < freq || !signin)
             {
                 Utilities.Log("Timer stopped!!" + freq + " ||" + signin.ToString());
                 aTimer.Enabled = false;
                 aTimer.Stop();

                 //stop the Foreground service
                 var actname = CrossCurrentActivity.Current.Activity.LocalClassName;
                 if (actname.Contains("SignInActivity"))
                 {
                     (CrossCurrentActivity.Current.Activity as SignInActivity).StopService_ForFG();
                 }
                 return;
             }

             bool gpsStatus = DependencyService.Get<ILocSettings>().isGpsAvailable();
             if (!gpsStatus)
             {
                 LocationTrack log = new LocationTrack();
                 log.TrackDate = DateTime.Now;
                 log.IsManual = false;
                 log.Latitude = "0.0";
                 log.Longitude = "0.0";
                 log.IsSync = false;
                 log.DayInOut = false;
                 log.Image = "";
                 log.InOut = 0;
                 log.LocationName = "";
                 log.LocationCode = "";
                 log.LocationName = "";
                 log.LocationType = 0;
                 log.OffsiteReason = "GPS is not Enabled.";
                 log.SignType = 0;
                 trackBL.SaveLocationTrack(log);

                 Utilities.Log("GPS not available");
                 onNotificationRaised?.Invoke(null, "Enable GPS to track the Location%SignIn");
                 return;
             }

             var request = new GeolocationRequest(GeolocationAccuracy.High, TimeSpan.FromSeconds(1));
             var cts = new CancellationTokenSource();
             var location = await Geolocation.GetLocationAsync(request, cts.Token);

             if (location == null)
             {
                 var locator = CrossGeolocator.Current;
                 var position = await locator.GetPositionAsync(TimeSpan.FromSeconds(5), null, true);

                 if (position != null)
                 {
                     location = new Xamarin.Essentials.Location();
                     location.Accuracy = position.Accuracy;
                     location.Latitude = position.Latitude;
                     location.Longitude = position.Longitude;
                     location.Altitude = position.Altitude;
                 }
             }

             if (location != null)
             {
                 LocationTrack log = new LocationTrack();
                 log.TrackDate = DateTime.Now;
                 log.IsManual = false;
                 log.Latitude = location.Latitude.ToString();
                 log.Longitude = location.Longitude.ToString();
                 log.IsSync = false;
                 log.DayInOut = false;
                 log.Image = "";
                 log.InOut = 0;
                 log.LocationName = "";

                 var currentloc = trackBL.SaveBackgroundLocationTrack(log, location.Accuracy ?? 0);
                 trackBL.SyncPendingTracks();

                 Device.BeginInvokeOnMainThread(() =>
                 {
                     MessagingCenter.Send<object, string>(this, "CurrentLocation", currentloc + "$^$" + location.Latitude + ", " + location.Longitude);
                 });

                 Console.WriteLine($"Latitude: {location.Latitude}, Longitude: {location.Longitude}, Altitude: {location.Altitude}");

                 Utilities.Log("CurrentLocation" + currentloc);
             }
             else
             {
                 LocationTrack log = new LocationTrack();
                 log.TrackDate = DateTime.Now;
                 log.IsManual = false;
                 log.Latitude = "0.0";
                 log.Longitude = "0.0";
                 log.IsSync = false;
                 log.DayInOut = false;
                 log.Image = "";
                 log.InOut = 0;
                 log.LocationName = "";
                 log.LocationCode = "";
                 log.LocationName = "";
                 log.LocationType = 0;
                 log.OffsiteReason = "GPS is not Enabled for Always.";
                 log.SignType = 0;
                 trackBL.SaveLocationTrack(log);

                 Utilities.Log("Location Null");
             }
         }
         catch (FeatureNotSupportedException fnsEx)
         {
             Utilities.Log("FG Destroyed 1");
             // Handle not supported on device exception
         }
         catch (FeatureNotEnabledException fneEx)
         {
             Utilities.Log("FG Destroyed2");
             // Handle not enabled on device exception
         }
         catch (PermissionException pEx)
         {
             Utilities.Log("FG Destroyed3");
             // Handle permission exception
         }
         catch (Exception ex)
         {
             LocationTrack log = new LocationTrack();
             log.TrackDate = DateTime.Now;
             log.IsManual = false;
             log.Latitude = "0.0";
             log.Longitude = "0.0";
             log.IsSync = false;
             log.DayInOut = false;
             log.Image = "";
             log.InOut = 0;
             log.LocationName = "";
             log.LocationCode = "";
             log.LocationName = "";
             log.LocationType = 0;
             log.OffsiteReason = "GPS is not Enabled for Always.";
             log.SignType = 0;
             trackBL.SaveLocationTrack(log);

             Utilities.Log("Location Null");

             Utilities.Log("FG Destroyed4");
             Utilities.Log(ex.ToString());
             // Unable to get location
         }
     }

     public override void OnDestroy()
     {
         Utilities.Log("FG Destroyed");
         if (_cts != null)
         {
             _cts.Token.ThrowIfCancellationRequested();
             _cts.Cancel();
         }
         base.OnDestroy();
     }
 }


Is there any way how to check whether the Service is running or not?


Thanks in Advance.

dotnet-xamarin
· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

And in few of mobiles, it is running fine. But, In few mobiles the Foreground service is getting stopped automatically.

Hi, ManikishoreA-8992. What do these devices have in common? These device systems may constomize the different settings, some may require you to change some settings.

0 Votes 0 ·
JarvanZhang-MSFT avatar image
0 Votes"
JarvanZhang-MSFT answered JarvanZhang-MSFT commented

Hello,​

Welcome to our Microsoft Q&A platform!

Try using the Handler with an Action to keep doing the work in a foreground service. Here is the sample code, you could refer to:

[Service(Enabled = true)]
public class TestService : Service
{
    private Handler handler;
    private Action runnable;

    private bool IsStarted;
    private int DELAY_BETWEEN_LOG_MESSAGES = 5000;
    private int NOTIFICATION_SERVICE_ID = 1001;
    private int NOTIFICATION_AlARM_ID = 1002;
    private string NOTIFICATION_CHANNEL_ID = "1003";
    private string NOTIFICATION_CHANNEL_NAME = "MyChannel";

    public override void OnCreate()
    {
        base.OnCreate();
        handler = new Handler(Application.MainLooper);
        runnable = new Action(() =>
        {
            if (IsStarted)
            {
                functionMethod();
                handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
            }
        });
    }

    private void functionMethod()
    {
        //
    }

    public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
    {

        if (IsStarted)
        {
            //service is already started
        }
        else
        {
            CreateNotificationChannel();
            DispatchNotification_RegisterService();//register a service as a foreground service
            handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES);
            IsStarted = true;
        }
        return StartCommandResult.Sticky;
    }

    public override void OnDestroy()
    {
        // Stop the handler.
        handler.RemoveCallbacks(runnable);

        // Remove the notification from the status bar.
        var notificationManager = (NotificationManager)GetSystemService(NotificationService);
        notificationManager.Cancel(NOTIFICATION_SERVICE_ID);

        IsStarted = false;
        base.OnDestroy();
    }

    public override IBinder OnBind(Intent intent)
    {
        return null;
    }

    private void DispatchNotification_RegisterService()
    {
        var notificationManager = (NotificationManager)GetSystemService(NotificationService);

        if (Build.VERSION.SdkInt > BuildVersionCodes.O)
        {
            var chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, GetString(Resource.String.abc_action_bar_home_description), NotificationImportance.Default);
            chan.LightColor = Android.Graphics.Color.Green;
            chan.LockscreenVisibility = NotificationVisibility.Private;

            notificationManager.CreateNotificationChannel(chan);

            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
               .SetAutoCancel(false)
               .SetSmallIcon(Resource.Drawable.abc_btn_radio_material)
               .SetContentTitle("Mobile")
               .SetContentText("My service started");

            StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
        }
        else
        {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
               .SetDefaults((int)NotificationDefaults.All)
               .SetSmallIcon(Resource.Drawable.abc_btn_radio_material)
               .SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 })
               .SetSound(null)
               .SetPriority(NotificationCompat.PriorityDefault)
               .SetAutoCancel(false)
               .SetContentTitle("Mobile")
               .SetContentText("My service started")
               .SetOngoing(true);

            StartForeground(NOTIFICATION_SERVICE_ID, builder.Build());
        }
    }


    private void CreateNotificationChannel()
    {
        //Notification Channel
        NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Max);
        notificationChannel.EnableLights(true);
        notificationChannel.EnableVibration(true);
        notificationChannel.SetVibrationPattern(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 });

        NotificationManager notificationManager = (NotificationManager)this.GetSystemService(Context.NotificationService);
        notificationManager.CreateNotificationChannel(notificationChannel);
    }
}

Sample code: https://github.com/xamarin/monodroid-samples/blob/master/ApplicationFundamentals/ServiceSamples/ForegroundServiceDemo/TimestampService.cs

Best Regards,

Jarvan Zhang


If the response is helpful, please click "Accept Answer" and upvote it.

Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


· 6
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

@JarvanZhang-MSFT

Thanks for help.

In my code, I've started a timer in foreground service.
My thought was, once the timer started, the foreground service will running continuously.

Anyway, I'll have the above sample code implement and test.

0 Votes 0 ·

Waiting for your udpate. And please make sure you've registered the serive as a foreground service. The normal service will be terminated in a certain amount of time after the app goes to background. Check the code:
https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/services/foreground-services#registering-as-a-foreground-service

0 Votes 0 ·

@JarvanZhang-MSFT -
The above code looks working in my testing through Emulator. But, When I shared the apk for real time testing to some of people, Working in few mobiles and not working in few mobiles.

Also, added the logs for the testing purpose. A log will be created on each time runnable action is fired. These logs were not created in few mobiles.

Also, I had registered the service as Foreground.
Any best way that we can find whether the Foreground service is running or not? or Any extra steps that we need to implement to find the actual reason why Foreground is not working?

0 Votes 0 ·
Show more comments
MaticDiBatista-6817 avatar image
0 Votes"
MaticDiBatista-6817 answered

Hi,
I have a similar issue. I prepared an example project: https://github.com/MaticDiba/XFBackgroundLocationSampleExtemded

I tried deploying this project to my phone (Samsung Galaxy S20) via VisualStudio 2022. When I test this application with the screen off, it stops recording location after 2-3 minutes.

When I run the application on my phone in debug mode it actually works. But if I disconnect my phone and then run the application, this issue happens: when putting the application to the background and then turning off the screen, it's logging locations for 2-3 minutes then it stops. When I resume the application it continues.

I have other tracking applications like LocusMap, and there it works. So I guess the phone is not a problem.

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.