title: App sometimes crashes with "Background start not allowed: service Intent"
body: <p>I only do Android development part time (which seems challenging considering the many subtleties and low-levelness of the APIs) and have a problem with receiving notifications while my app is in the background. I am targeting Android 8.1. My app happens to use Azure and Xamarin, but I do not know if those details are inherent to the problem. </p>
  
  <p>If my app is in the background sometimes notifications can be displayed, other times my app crashes with this error:</p>
  
  <pre><code>2019-03-06 09:12:24.486 9495-9702/? D/tyresense.azurelistenerservice: OnMessage() done
  2019-03-06 09:12:28.278 1548-3232/? W/ActivityManager: Background start not allowed: service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) } to com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService from pid=9495 uid=10173 pkg=com.rimex.tyresense
  2019-03-06 09:12:28.341 9495-9495/? I/MonoDroid: Java.Lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) }: app is in background uid UidRecord{d582ba5 u0a173 RCVR bg:+1m0s848ms idle procs:1 seq(0,0,0)}
  2019-03-06 09:12:28.342 9495-9495/? I/MonoDroid: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) }: app is in background uid UidRecord{d582ba5 u0a173 RCVR bg:+1m0s848ms idle procs:1 seq(0,0,0)}
  2019-03-06 09:12:28.347 9495-9495/? E/AndroidRuntime: FATAL EXCEPTION: main
      Process: com.rimex.tyresense, PID: 9495
      java.lang.RuntimeException: Unable to start receiver md5bf34da605c12d097dc5a495af95e9376.TyreSenseBroadcastReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) }: app is in background uid UidRecord{d582ba5 u0a173 RCVR bg:+1m0s848ms idle procs:1 seq(0,0,0)}
          at android.app.ActivityThread.handleReceiver(ActivityThread.java:3285)
          at android.app.ActivityThread.-wrap17(Unknown Source:0)
          at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1713)
          at android.os.Handler.dispatchMessage(Handler.java:106)
          at android.os.Looper.loop(Looper.java:164)
          at android.app.ActivityThread.main(ActivityThread.java:6626)
          at java.lang.reflect.Method.invoke(Native Method)
          at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811)
       Caused by: java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.rimex.tyresense cmp=com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) }: app is in background uid UidRecord{d582ba5 u0a173 RCVR bg:+1m0s848ms idle procs:1 seq(0,0,0)}
          at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1536)
          at android.app.ContextImpl.startService(ContextImpl.java:1492)
          at android.content.ContextWrapper.startService(ContextWrapper.java:650)
          at android.content.ContextWrapper.startService(ContextWrapper.java:650)
          at md513d040a829b3f298fbeeee5a6e2c042a.GcmBroadcastReceiverBase_1.n_onReceive(Native Method)
          at md513d040a829b3f298fbeeee5a6e2c042a.GcmBroadcastReceiverBase_1.onReceive(GcmBroadcastReceiverBase_1.java:29)
          at android.app.ActivityThread.handleReceiver(ActivityThread.java:3278)
          at android.app.ActivityThread.-wrap17(Unknown Source:0) 
          at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1713) 
          at android.os.Handler.dispatchMessage(Handler.java:106) 
          at android.os.Looper.loop(Looper.java:164) 
          at android.app.ActivityThread.main(ActivityThread.java:6626) 
          at java.lang.reflect.Method.invoke(Native Method) 
          at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) 
          at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811) 
  2019-03-06 09:12:28.348 2761-2761/? W/GCM: broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg=com.rimex.tyresense (has extras) }
  2019-03-06 09:12:28.353 9495-9495/? E/tyresense.splashscreen: System.UnhandledExceptionEventArgs
  2019-03-06 09:12:28.357 1548-7265/? W/ActivityManager:   Force finishing activity com.rimex.tyresense/md5bf34da605c12d097dc5a495af95e9376.MainActivity
  2019-03-06 09:12:28.363 1548-1632/? I/ActivityManager: Showing crash dialog for package com.rimex.tyresense u0
  </code></pre>
  
  <p>I see articles like this one: <a href="https://blog.xamarin.com/replacing-services-jobs-android-oreo-8-0/" rel="nofollow noreferrer">https://blog.xamarin.com/replacing-services-jobs-android-oreo-8-0/</a></p>
  
  <p>But it is not clear to me what exactly is the problem, so I do not know if that will solve it. </p>
  
  <p>Maybe here?</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using Android.App;
  using Android.Content;
  using Android.Util;
  using Gcm.Client;
  using WindowsAzure.Messaging;
  using SharedMobile; // keep it simple, the app might not be running
  using MyApp.Android; // we need to reference ourselves...
  using Android.Support.V4.App;
  using Android.Content.PM;
  using System.Threading.Tasks;
  
  [assembly: Permission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
  [assembly: UsesPermission(Name = "@PACKAGE_NAME@.permission.C2D_MESSAGE")]
  [assembly: UsesPermission(Name = "com.google.android.c2dm.permission.RECEIVE")]
  [assembly: Xamarin.Forms.Dependency(typeof(DependencyListener))]
  namespace MyApp.Android
  {
      [BroadcastReceiver(Permission = Constants.PERMISSION_GCM_INTENTS)]
      [IntentFilter(new string[] { Constants.INTENT_FROM_GCM_MESSAGE }, Categories = new string[] { "@PACKAGE_NAME@" })]
      [IntentFilter(new string[] { Constants.INTENT_FROM_GCM_REGISTRATION_CALLBACK }, Categories = new string[] { "@PACKAGE_NAME@" })]
      [IntentFilter(new string[] { Constants.INTENT_FROM_GCM_LIBRARY_RETRY }, Categories = new string[] { "@PACKAGE_NAME@" })]
      public class MyAppBroadcastReceiver : GcmBroadcastReceiverBase&lt;AzureListenerService&gt;
      {
          // this declaration somehow enables messages
      }
  
      [Service]
      public class AzureListenerService : GcmServiceBase // this service might be started at system boot, when the app is otherwise not active
      {
          const string TAG = "MyApp.azurelistenerservice";
          const string HUBNAME = "myhubname";
          const string CONNECTION =
                 "Endpoint=sb://MyApp.servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=secretstuff...";
  
          public const string GcmProjectNumber = "1234567890";
          private static NotificationHub noteHub = null;
  
          public AzureListenerService() : base(GcmProjectNumber)
          {
          }
  
          // need client and area ids before this is called. Application.Context is not available if called from system boot
          public static void InitializeAzure(Context context = null)
          {
              try
              {
                  if (context == null)
                      context = Application.Context;
                  var enabled = NotificationManagerCompat.From(context).AreNotificationsEnabled();
                  SharedMobile.API.ApiService.Instance.NotificationsEnabled(enabled);
                  if (enabled)
                  {
                      GcmClient.CheckDevice(context);
                      GcmClient.CheckManifest(context);
                      GcmClient.Register(context, GcmProjectNumber);
                  }
              }
              catch (Exception ex)
              {
                  Log.Error(TAG, ex.Message);
                  Messenger.SendException(ex.Message);
              }
          }
  
          // incoming GCM registration
          protected override void OnRegistered(Context context, string token)
          {
              try
              {
                  noteHub = new NotificationHub(HUBNAME, CONNECTION, context);
                  var customerId = Int32.Parse(PropertyHelper.ClientId);
                  var tags = BuildTagList(customerId, PropertyHelper.AreaIds);
                  noteHub.Register(token, tags.ToArray());
                  Messenger.RegisterDevice(token);
              }
              catch (Exception ex)
              {
                  Log.Error(TAG, "OnRegistered() " + ex.Message); // ignore register failure when using the Android emulator
              }
          }
  
          protected override void OnMessage(Context context, Intent intent)
          {
              try
              {
                  var areaIds = PropertyHelper.AreaIds;
                  if (string.IsNullOrWhiteSpace(areaIds)) // in case the user logged out
                      return;
                  new Notifications().CreateEventNotification(intent);
              }
              catch (Exception ex)
              {
                  Log.Error(TAG, "OnMessage() " + ex.Message);
              }
          }
  
          protected List&lt;string&gt; BuildTagList(int clientId, string areaIds)
          {
              var list = new List&lt;string&gt;();
              list.Add($"client_{clientId}");
              foreach (var id in areaIds.Split(','))
                  list.Add($"area_{id}");
              return list;
          }
  
          protected override void OnUnRegistered(Context context, string registrationId)
          {
              Log.Error(TAG, "OnUnRegistered(): " + registrationId);
          }
  
          protected override bool OnRecoverableError(Context context, string errorId)
          {
              Log.Error(TAG, "OnRecoverableError(): " + errorId);
              return base.OnRecoverableError(context, errorId);
          }
  
          protected override void OnError(Context context, string errorId)
          {
              Log.Error(TAG, "OnError(): " + errorId);
          }
      }
  }
  
  
  using System;
  using Android.App;
  using Android.Content;
  using Android.OS;
  using Android.Support.V4.App;
  using Android.Support.V4.Content;
  using Android.Util;
  using Exception = System.Exception;
  
  namespace My.Android
  {
      internal class Notifications
      {
          const string TAG = "My.notifications";
          const string TITLE = "Application is running";
          const string DESC = "Tap to view";
          const int MainProgramNotificationId = 775566;
  
          // unique ID for our notification: 
          static readonly string CHANNEL_ID = "My_channel_23";
          static readonly string CHANNEL_NAME = "[My_channel]";
  
          internal void CreateProgramIcon_on_hold(Intent intent)
          {
              var extras = new Bundle();
              extras.PutString("scopeDescription", TITLE);
              extras.PutString("stateDescription", DESC);
              intent.PutExtras(extras);
  
              ShowNotification(intent.Extras, false);
          }
  
          internal void CreateEventNotification(Intent extra)
          {
              if (extra.Extras != null &amp;&amp; !extra.Extras.IsEmpty)
                  ShowNotification(extra.Extras, true);
  
              var notificationId = extra?.Extras?.GetString("notification-id", null);
              if (notificationId != null &amp;&amp; notificationId.Length &gt; 0)
                  SharedMobile.API.ApiService.NotificationConfirmation(notificationId);
          }
  
          private void ShowNotification(Bundle extras, bool autoCancel)
          {
              try
              {
                  string title = TITLE; 
                  string description = DESC;
                  if (extras != null &amp;&amp; !extras.IsEmpty)
                  {
                      title = extras.GetString("scopeDescription", null);
                      description = extras.GetString("stateDescription", "");
                  }
  
                  Intent intent = new Intent(Application.Context, typeof(SplashScreen));
                  BuildNotification(intent, extras, title, description, autoCancel);
  
              }
              catch (Exception ex)
              {
                  Log.Debug(TAG, ex.Message);
              }
          }
  
          private void BuildNotification(Intent intent, Bundle extras, string title, string description, bool autoCancel)
          {
              bool onGoing = !autoCancel;
              int icon = IconId(null, null); // code not shown
              intent.AddFlags(ActivityFlags.ReorderToFront);
              var pendingIntent = PendingIntent.GetActivity(Application.Context, 0, intent, PendingIntentFlags.UpdateCurrent);
  
              CreateNotificationChannel(autoCancel);
  
              var notificationManager = Application.Context.GetSystemService(Context.NotificationService) as NotificationManager;
              NotificationCompat.Builder builder = new NotificationCompat.Builder(Application.Context, CHANNEL_ID);
  
              builder.SetSmallIcon(icon)
                  .SetContentTitle(title)
                  .SetContentText(description)
                  .SetSmallIcon(Resource.Drawable.notification_icon)
                  .SetAutoCancel(autoCancel)
                  .SetOngoing(onGoing)
                  .SetContentIntent(pendingIntent);
  
              if (Build.VERSION.SdkInt &gt;= BuildVersionCodes.Lollipop)
                  builder.SetColor(ContextCompat.GetColor(Application.Context, Resource.Color.exBackgroundColor));
  
              string utcTicks = null;
              if (extras != null)
                  utcTicks = extras.GetString("utcTicks", null);
              if (utcTicks != null)
              {
                  var ticks = Int64.Parse(utcTicks);
                  DateTimeOffset dto = new DateTimeOffset(ticks, TimeSpan.Zero);
                  builder.SetWhen(dto.ToUnixTimeMilliseconds());
              }
              else
                  builder.SetShowWhen(true);
  
              notificationManager?.Notify(MainProgramNotificationId, builder.Build());
          }
  
          private void CreateNotificationChannel(bool evnt)
          {
              if (Build.VERSION.SdkInt &lt; BuildVersionCodes.O)
              {
                  // There is no need to create a notification channel on older versions of Android.
                  return;
              }
              NotificationImportance ni;
              if (evnt)
                  ni = NotificationImportance.Default;
              else
                  ni = NotificationImportance.Low;
              var channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, ni)
              {
                  Description = "My App",
                  LockscreenVisibility = NotificationVisibility.Private,
              };
              var notificationManager = Application.Context.GetSystemService(Context.NotificationService) as NotificationManager;
              notificationManager.CreateNotificationChannel(channel);
          }
  
          string FormatTimeStamp(string utcTicks)
          {
              try
              {
                  var ticks = Int64.Parse(utcTicks);
                  DateTimeOffset dto = new DateTimeOffset(ticks, TimeSpan.Zero);
                  return dto.ToLocalTime().ToString("MM/dd HH:mm:ss");
              }
              catch (Exception)
              {
                  return string.Empty;
              }
          }
  
          bool IgnoreActionProcess(string actionProcessId, int ackedProcessId)
          {
              try
              {
                  if (int.Parse(actionProcessId) == ackedProcessId)
                      return true;
              }
              catch (Exception ex)
              {
                  Log.Debug(TAG, $"int parse: {actionProcessId} {ex.Message}");
              }
              return false;
          }
  
      }
  }
  </code></pre>
  
  <p><strong>Update</strong>:
  I change the OnMessage in public class AzureListenerService : GcmServiceBase to do nothing and it still crashes:</p>
  
  <p>Unhandled Exception:
  Java.Lang.IllegalStateException: Not allowed to start service Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x1000010 pkg=com.mycompany.myapp cmp=com.mycompany.myapp/md5bf34da605c12d097dc5a495af95e9376.AzureListenerService (has extras) }: app is in background uid UidRecord{d45626f u0a173 RCVR bg:+1m29s589ms idle change:uncached procs:1 seq(0,0,0)} occurred</p>
  
  <p><strong>Update</strong>
  I implemented the JobScheduler, that made no difference. Also, the app has to be in the background for "some time" and the problem will then always occur. I also changed my app to do nothing at all (but log) when a notification arrives, and it still crashes.</p>
  
  <p><strong>Update</strong>
  I got this working: <a href="https://developer.xamarin.com/samples/monodroid/Firebase/FCMNotifications/" rel="nofollow noreferrer">https://developer.xamarin.com/samples/monodroid/Firebase/FCMNotifications/</a>
  By adding it "as is" to my firebase account, downloading the google-services.json and testing with <a href="http://pushtry.com/" rel="nofollow noreferrer">http://pushtry.com/</a>. No luck with Azure portal test send yet. Hope to merge code from FCMNotifications into my app, if I can get Azure working...</p>