Android Question foreground service exception - update to "short-term" service or receiver?

Dave O

Well-Known Member
Licensed User
Longtime User
My checklist app uses a service to upload a backup file to Google Drive when the list changes or the app exits. I first save the file locally (instant), but because it takes a few seconds to upload to the net, I thought a service would be the right way to do this (so that the service gets the ack message and shows a success toast a few seconds after the app has closed).

This has worked well until recently, but now I'm getting crash reports for a ForegroundServiceStartNotAllowedException on Android 12, 13, and 14. I suspect that this is because of the recent restrictions placed on services.

My question is, should I add a "short-run" type to the service, as described in the Android 14 docs, or should I switch to a receiver (which seems to be the general solution suggested by Erel and others)?

For background, I call the service from a (foreground) activity:
B4X:
Sub saveIfNeeded
    If listHasChanged Then
        saveCurrentShoplist
        CallSubDelayed2(backupService, "backupCurrentListIfEnabled_", currentShopList)
    end if
End Sub

...then that calls the "GoogleDrive via API V3" class to connect to Drive and upload the file, with several async operations that usually last 1-10 seconds depending on the connection speed.

Here's a typical stack trace on Android 14:
B4X:
Exception java.lang.RuntimeException:
  at android.app.ActivityThread.handleServiceArgs (ActivityThread.java:4962)
  at android.app.ActivityThread.-$$Nest$mhandleServiceArgs
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2393)
  at android.os.Handler.dispatchMessage (Handler.java:111)
  at android.os.Looper.loopOnce (Looper.java:242)
  at android.os.Looper.loop (Looper.java:362)
  at android.app.ActivityThread.main (ActivityThread.java:8393)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:552)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:992)
Caused by android.app.ForegroundServiceStartNotAllowedException:
  at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel (ForegroundServiceStartNotAllowedException.java:54)
  at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel (ForegroundServiceStartNotAllowedException.java:50)
  at android.os.Parcel.readParcelableInternal (Parcel.java:4870)
  at android.os.Parcel.readParcelable (Parcel.java:4852)
  at android.os.Parcel.createExceptionOrNull (Parcel.java:3052)
  at android.os.Parcel.createException (Parcel.java:3041)
  at android.os.Parcel.readException (Parcel.java:3024)
  at android.os.Parcel.readException (Parcel.java:2966)
  at android.app.IActivityManager$Stub$Proxy.startService (IActivityManager.java:6377)
  at android.app.ContextImpl.startServiceCommon (ContextImpl.java:1951)
  at android.app.ContextImpl.startForegroundService (ContextImpl.java:1926)
  at android.content.ContextWrapper.startForegroundService (ContextWrapper.java:830)
  at anywheresoftware.b4a.keywords.Common.StartServiceImpl (Common.java:924)
  at anywheresoftware.b4a.keywords.Common.StartService (Common.java:911)
  at anywheresoftware.b4a.objects.ServiceHelper$StarterHelper.onStartCommand (ServiceHelper.java:229)
  at name.obrien.dave.lister.backupservice.onStartCommand (backupservice.java:72)
  at android.app.ActivityThread.handleServiceArgs (ActivityThread.java:4944)

Any tips appreciated!
 

josejad

Expert
Licensed User
Longtime User
Hi:

Maybe this read can help you

 
Upvote 0

Dave O

Well-Known Member
Licensed User
Longtime User
...and more specifically, this page about Data transfer background task options.

Looks like the "datasync" type has been quasi-deprecated, and "shortService" seems to fit the bill better based on their examples.

But will adding the service type solve this problem across all Android versions? Or should I just switch to a receiver (will have to look up how, hopefully easy)?
 
Upvote 0

Dave O

Well-Known Member
Licensed User
Longtime User
OK, looks like the most practical way to fix my problem is to assign the shortService type to my backup service. Thanks.

Do I need to update B4A to v13.0 for this? (I'll have to do an update this month anyway for targeting SDK 34.).

And does the service type get quoted? e.g.
B4X:
SetServiceAttribute(backupService, android:foregroundServiceType, shortService)
vs.
SetServiceAttribute(backupService, android:foregroundServiceType, "shortService")

Thanks again!
 
Last edited:
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Do I need to update B4A to v13.0 for this?
Yes.

And does the service type get quoted?
It doesn't matter.

And you must add this sub to the service:
B4X:
Private Sub Service_Timeout(Params As Map)
    Service.StopForeground(NotificationId)
End Sub
 
Upvote 0

Dave O

Well-Known Member
Licensed User
Longtime User
I don't use notifications for the service, so I don't have a variable called NotificationId to pass to StopForeground.

The Android docs show this as an enumeration, so I'll assume I can just pass a "1" (the value of STOP_FOREGROUND_REMOVE):
B4X:
Service.StopForeground(1)        '1 = STOP_FOREGROUND_REMOVE
 
Upvote 0

Alessandro71

Well-Known Member
Licensed User
Longtime User
I don't use notifications for the service, so I don't have a variable called NotificationId to pass to StopForeground.

The Android docs show this as an enumeration, so I'll assume I can just pass a "1" (the value of STOP_FOREGROUND_REMOVE):
B4X:
Service.StopForeground(1)        '1 = STOP_FOREGROUND_REMOVE
I always assumed that the notification id was an integer that should match the one you used in Service.StartForeground(NotificationId, n)
 
Upvote 1

Dave O

Well-Known Member
Licensed User
Longtime User
As shown in my original post, I'm implicitly starting the service by using CallSubDelayed2:

B4X:
CallSubDelayed2(backupService, "backupCurrentListIfEnabled_", currentShopList)

...so I'm not associating it with a notification. Should I?
 
Upvote 0
Top