Xamarin.Android: Service stopps working when screen off

Hi,

I have to implement a service were I transmit GPS (Lat/Lon) every 120 sec. to my REST backend.

I have the problem that the service seems to stop working when the screen is turned off.

  1. Attempt with a StartedService and a BroadcastReceiver
  2. Attempt with an IntentService and a WakefulBroadcastReceiver

The 1. Attempt works for example on a SAMSUNG Galaxy X COVER but not on a HUAWEI P9 Lite. Dispite the fact that I turned Power Saving mode (Power Apps) in HUAWEI phone off. So my app keeps running when screen is turned off. After e.g 10 Minutes I turned screen on on the HUAWEI phone and saw in runing services that the service is running (elapsed seconds) so I could verify that the service is not shut down.

The 2. Attempt works on no phone when screen is turned off.

Both attempts are working properly when screen is turned on (no sleep).

Note: For brevity I stripped out the code of not relevant methods (fetch/transfer gps data).

Here is the code for 1. Attempt:

Service

[Service]
public class GpsTrackerService : Service
{
    private static readonly string LogTag = "X:TruckerApp-" + typeof(GpsTrackerService).Name;
    private static readonly int NOTIFICATION_ID = 10000;
    private MobileServiceClient _mobileServiceClient;

    public TelephonyManager GetTelefonyManager()
    {
        return (Android.Telephony.TelephonyManager) GetSystemService(TelephonyService);
    }

    public bool IsRunningAsService { get; set; }


    public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
    {
        Log.Debug(LogTag, "GpsTrackerService.OnStartCommand :: Service started. Timer instantiated.");
        GpsTrackerServiceUtils.SendGenericMessageToApplicationInsights("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnStartCommand :: Service started.Timer instantiated.");

        Task.Run(async () =>
        {
            try
            {
                await DoWork();
                await Task.Delay(TimeSpan.FromMinutes(2));
            }
            catch (Exception e)
            {
                Log.Debug(LogTag, $"GpsTrackerService.HandleTimerCallback :: (OUTER try-catch) Exception:{e.Message} Type:{e.GetType().Name}, Stacktrack:{e.StackTrace}");
                if (e.InnerException != null)
                {
                    Log.Debug(LogTag, $"GpsTrackerService.HandleTimerCallback :: (OUTER try-catch) Exception:{e.InnerException.Message} Type:{e.InnerException.GetType().Name}, Stacktrack:{e.InnerException.StackTrace}");
                }
                GpsTrackerServiceUtils.SendExceptionToApplicationInsights(e, LogTag, _mobileServiceClient, this);
            }
            finally
            {
                // Restart Service
                Intent broadcastIntent = new Intent(action: GpsConstants.GpsRestart);
                SendBroadcast(broadcastIntent);
            }

        });

        return StartCommandResult.Sticky;
    }

      public override IBinder OnBind(Intent intent)
    {
        // This is a started service, not a bound service, so we just return null.
        return null;
    }
}

BroadcastReceiver

[BroadcastReceiver(Enabled = true, Exported = true, Name = GpsConstants.GpsBroadcastReceiver, Label = "RestartServiceWhenStopped")]
[IntentFilter(new[] {GpsConstants.GpsRestart})]
public class GpsTrackerServiceBroadcastReceiver : BroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        try
        {
            MetricsManagerHelper.Instance.SendGenericMessageToApplicationInsights("Gps-Info", "OnReceive :: Gps Service broadcast message received. Restarting GPS Service...");
            context.StartService(new Intent(context, typeof(GpsTrackerService)));
         }
        catch (Exception e)
        {
            MetricsManagerHelper.Instance.SendExceptionToApplicationInsights(e);
        }
    }
}

Register Broadcastreceiver in MainActivity.OnCreate

 GpsTrackerServiceBroadcastReceiver = new GpsTrackerServiceBroadcastReceiver();
 RegisterReceiver(GpsTrackerServiceBroadcastReceiver, new IntentFilter(GpsConstants.GpsBroadcastReceiver));

2.Attempt

Service

[Service]
public class GpsTrackerIntentService : IntentService
{
    private static readonly string LogTag = "X:TruckerApp-" + typeof(GpsTrackerIntentService).Name;
    private static readonly int NOTIFICATION_ID = 10000;
    private MobileServiceClient _mobileServiceClient;

    public TelephonyManager GetTelefonyManager()
    {
        return (TelephonyManager) GetSystemService(TelephonyService);
    }

    protected override async void OnHandleIntent(Intent intent)
    {
        try
        {
            // perform task
            await Bootstrap();

            GpsTrackerServiceUtils.SendGenericMessageToApplicationInsightsWakeLock("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.OnHandleIntent :: Invoked.");

            await DoWork();
            await Task.Delay(TimeSpan.FromMinutes(2));
        }
        catch (Exception e)
        {
            Log.Debug(LogTag, $"GpsTrackerService.OnCreate :: Exception:{e.Message} Type:{e.GetType().Name}");
            GpsTrackerServiceUtils.SendExceptionToApplicationInsightsWakeLock(e, LogTag, _mobileServiceClient, this);
        }
        finally
        {
            WakefulBroadcastReceiver.CompleteWakefulIntent(intent);
            SendBroadcast(new Intent(this, typeof(GpsTrackerServceWakefulReceiver)));
            //var wakefulReceiverIntent = new Intent(this, typeof(GpsTrackerServceWakefulReceiver));
            //var pending = PendingIntent.GetBroadcast(this, 0, wakefulReceiverIntent, PendingIntentFlags.UpdateCurrent);
            //AlarmManager manager = (AlarmManager)GetSystemService(AlarmService);
            //manager.SetRepeating(AlarmType.RtcWakeup, SystemClock.ElapsedRealtime(), 120 * 1000, pending);
        }
    }


    public async Task DoWork()
    {
        // long running code ...
        Log.Debug(LogTag, "GpsTrackerService.HandleTimerCallback :: Invoked.");
        GpsTrackerServiceUtils.SendGenericMessageToApplicationInsightsWakeLock("Gps-Info", LogTag, _mobileServiceClient, this, "GpsTrackerService.HandleTimerCallback :: Invoked.");


    }
}

WakefulReceiver

[BroadcastReceiver]
public class GpsTrackerServceWakefulReceiver : WakefulBroadcastReceiver
{
    public override void OnReceive(Context context, Intent intent)
    {
        var serviceIntent = new Intent(context, typeof(GpsTrackerIntentService));
        StartWakefulService(context, serviceIntent);
    }
}

AndroidManifest.xml

added WAKE_LOCK permission

  <uses-permission android:name="android.permission.WAKE_LOCK"/>
  <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>

I got really desperate now. Hope someone can help me.
Thanks

Eric

Tagged:

Best Answers

  • AlbertKAlbertK MY ✭✭✭
    Accepted Answer

    Hi,

    I have a sample code here. It is to start a background service and wake up every 10mins or so.

    https://forums.xamarin.com/discussion/83601/android-service

  • EricBrunnerEricBrunner US ✭✭
    Accepted Answer

    @AlbertK Thank you very much !!!

Answers

  • AlbertKAlbertK MYMember ✭✭✭

    Hi,

    Samsung devices Android OS has a lot of the standard Android power saving features and permissions removed and this is the reason that it worked. In other devices they tend to follow what is dished out by Google and some even implement their own restricted version of power saving.

    By default on Android Marshmallow (Api 23, version 6) onward there is a more restrictive power saving called Doze that will pause process, network access, etc to conserve power.

    https://developer.android.com/training/monitoring-device-state/doze-standby.html

  • EricBrunnerEricBrunner USMember ✭✭
    edited June 2017
    Hi Albert,

    First of all thank you for the quick reply. Wow what a nightmare with that doze... As far as I could gain information from the doze article I have no way to establish HTTP connection to my remote Azure REST backend ?! FCM is mentioned but that does not seem to be a practical way . I perstist the gps data in a sql database and FCM is not suited for such needs. Or am I wrong?
    The HUAWEI P9 has Android 7.0 / EMUI 5.0.
    I tried to set my app to allow battery optimization and data access exclusion. But I guess that would not work either.

    best
    Eric
  • EricBrunnerEricBrunner USMember ✭✭
    edited June 2017

    Hi Albert,

    I posted my implementation of the service on gist GpsTrackerService
    and my BroadcastReceiver.

    Because of your valueable information from yesterday I now understand a bit better what is going on in Doze mode. As an improvement I did 2 things:

    1. I start my service now as a Foreground Service
    2. In OnCreate I instantiate a WakeLock Instance
    3. In OnStartCommand I aquire a WakeLock before I do my GPS stuff and finally release the WakeLock

    I tried the app with new code and could collect data to my backend yesterday for about 5 hours while my phone (HUAWEI P9 Lite) was stationary (no movement, screen off) on my desk. At midnight I didn't get any data. Obviously the phone got into Deep Doze mode.

    Today I turned screen on and could see in task manager that my service was not running anymore. My app which started the service initially was running. Seems like a kind of hibernation. When I clicked my app and brought it to foreground the service restarted.

    Today I try to disable Battery Optimization and allow the app unrestricted data access. Hope it works ;-)

    Could you give me a hint where I would have to add the AlarmManager SetAndAllowWhileIdle call in my service and if I have to remove the WakeLock calls ? The 10 minutes or 15 min. would be better than getting no data.

    thanks a lot.
    best

    Eric

  • AlbertKAlbertK MYMember ✭✭✭
    Accepted Answer

    Hi,

    I have a sample code here. It is to start a background service and wake up every 10mins or so.

    https://forums.xamarin.com/discussion/83601/android-service

  • EricBrunnerEricBrunner USMember ✭✭
    Accepted Answer

    @AlbertK Thank you very much !!!

Sign In or Register to comment.