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.
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
Hi Eric,
With a stock standard Android OS you do not have much of a choice. The only other way is to have the device plug-in to a charger or a powerbank. When it is charging the all the power saving features are switched off.
With the battery optimization and data access exclusion, you can try the AlarmManager SetAndAllowWhileIdle to wake up the device from Doze and perform some tasks but the caveat here is that the shortest duration is around 10 minutes. Meaning setting a wake up shorter than 10 minutes has no effect, it would still wake up after 10 minutes. FYI, the rest of the Setxxx wake up method does not work when Doze kicks in.
Hope this helps.
Hi,
I have a sample code here. It is to start a background service and wake up every 10mins or so.
Answers
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
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
Hi Eric,
With a stock standard Android OS you do not have much of a choice. The only other way is to have the device plug-in to a charger or a powerbank. When it is charging the all the power saving features are switched off.
With the battery optimization and data access exclusion, you can try the AlarmManager SetAndAllowWhileIdle to wake up the device from Doze and perform some tasks but the caveat here is that the shortest duration is around 10 minutes. Meaning setting a wake up shorter than 10 minutes has no effect, it would still wake up after 10 minutes. FYI, the rest of the Setxxx wake up method does not work when Doze kicks in.
Hope this helps.
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:
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
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
@AlbertK Thank you very much !!!