Service started from widget doesn't work in Oreo - android

When I implemented my widget a couple years ago, I used this advice from the App Widgets page on the development site (still there): "If your App Widget setup process can take several seconds (perhaps while performing web requests) and you require that your process continues, consider starting a Service in the onUpdate() method." I did this and it worked like a charm when built for SDKs up through 25.
It also worked on Android 8.0, but as soon as I targeted 26, the app started crashing in the onUpdate method of the widget provider when issuing the startService call. The error message had to do with trying to start a background service from another background service, and although I tried a few things in the service definition in the manifest I couldn't get it working.
I ended up doing a workaround when I decided that I didn't really need to update the widget from a service, so now just do the update directly in onUpdate. Services and background/foreground issues are something I've not messed with much, but I'd still like to know if there is a way to have kept using a service to update the widget.

in Android O, we have a new background limitations. When you're trying to startService(), you will get IlleagalStateException, so now you should use startForegroundService(), but if you start service by this new method, you will get RemoteServiceException. To avoid this exception you have 5 seconds to make startForeground() after startForegroundService(), to notify user, that you're working in background.
So, where is only one way in Android O:
context.startForegroundService(intent)
And in service onCreate() make something like that:
startForeground(1, new Notification());
UPDATE
So as i assume, some manufacturers have backport on Android N these new background limitations, that's why you can get same exception in Android N.

Without a Minimal, Complete, and Verifiable example, it is difficult to help you. Depending on what you had, you could have:
Switched to JobIntentService, particularly if your old service was an IntentService and you did not mind the possibility of a several-second delay in the work being started
Called startForegroundService() on Context (instead of startService()) to start your service, and in your service call startForeground(), to make your service be a foreground service
Called getForegroundService() on PendingIntent (instead of getService()), and in your service call startForeground(), to make your service be a foreground service

Related

Android 12 - Foreground service launch restrictions

I'm developing an SDK that needs to startForeground service from the background. Because it uses background location and Bluetooth-related works. If the application is killed, the monitoring is performing in the background. That's why I'm using the foreground service. There is a condition that starts the foreground service from the background.
Currently, my SDK using Service to handle this job. But Android 12 on-words it doesn't support to start service from the background.
I'm trying to start the service from the background the below exception throws.
ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false
How can I use WorkManager to fix this issue, all my handling is done by the Service class and how can I pass the Service object to Worker class and start this job inside the Worker class.
Actually, my project is based on beacon technology.
and the beacon signals are used to show different recommendations to the user.
In my current implementation, if the application is killed by the user,
and also accepts the foreground service, the SDK will be run in the background.
and detect the beacon and provide appropriate actions.
My implementation is that, if the application initializes my SDK with the foreground service "OFF"
Then sometime later, when the application is in the background and trying to start the foreground service from the background this exception throws.
The foreground service-related decisions are held by the server-side API. I'm periodically checking whether the server-side value is changed or not, and if the value is changed the changed action is reflected in the SDK.
There is no one in the world that can give you an answer. The idea of all these restrictions is that we as developers need to optimize our applications. So if this is not possible for you it means most likely that you need to optimize the way you do your work. For this to happen you need to provide more info of what exactly events you are receiving, what is exactly your use case, etc.
https://developer.android.com/about/versions/12/foreground-services#cases-fgs-background-starts-allowed
As you can see there is info about exceptions for:
Your app receives a Bluetooth broadcast that requires the BLUETOOTH_CONNECT or BLUETOOTH_SCAN permissions.
But there is nothing in your question saying that your use case might relate to this.
Also, I don't understand how the app might be killed, but you keep working in the background.
Also if you want to constantly do something - why there is an event when you are in the background. Just when the user opens the app - start the service and keep it going.
You can also just "hack" it and ask the user to remove you from battery optimization.
https://developer.android.com/training/monitoring-device-state/doze-standby#support_for_other_use_cases
Earlier we were using Service to run background tasks. But, due to Android 12 - Foreground service launch restrictions, we will not be able to invoke Service for performing background tasks for Android 12+.
So from now on, from targetSdk 31, Service can be invoked only when the application is in the foreground. When the application is closed or when the application went to the background, invoking Service using startForegroundService will cause ForegroundServiceStartNotAllowedException.
So to perform background tasks, we need to use Worker instead of Service. Please refer to this answer to get an idea of how it is implemented. Hope it helps. Also, refer to the below links to get a high-level overview of what changes needs to be done.
Android 12 Behavior Changes
Work Requests
According to the official docs, if your app does one of the following, it should be able to start an FGS:
Your app receives a Bluetooth broadcast that requires the BLUETOOTH_CONNECT or BLUETOOTH_SCAN permissions.
or
Your app receives an event that's related to geofencing or activity recognition transition.
Those two seem like pretty good candidates for your use-case, at least how I understood it.

Having an Android service as both background and foreground service

I've read that I should call
startForegroundService(intent)
if I want to properly start a service that can run as a foreground service. But how should I think if I want it to start without it being a foreground service but potentially becoming one later on? Some info on how we have it right now:
We have an App which uses a service for many tasks. It performs all of these task when the user interacts with the app in the foreground but depending on if the user has enabled a certain feature we want to keep performing these tasks in the background. So when the app is launched (and obviously is in foreground) we start the service using
startService(intent);
When the app transitions into background we check whether the feature is enabled, and if so, run startForeground(id, notificiation); which effectively adds the non-removable notification out in the OS and the service keeps on running. When the app goes back into foreground we call stopForeground(true). If the feature isn't enabled the service will never be set as a foreground service and we won't try to perform these tasks in the background.
So my question is: Is this sufficient to get the "full foreground service performance"? Or am I losing something by not calling startForegroundService(intent)? My impression is that we actually do have a fully working foreground service but I'm getting confused when I read statements that foreground services must be started using startForegroundService(intent) which we're not doing. If we really need to use startForegroundService(intent), the only solution I can think of would be to initially start a normal instance of the service and when the app enters background we start a new one using startForegroundService(intent). But do we need to do this or is running startForegund(id, notification) on a service started using startService(intent) sufficient to achieve a foreground service?
NOTE: One difference I find is that if the application itself is in background when start the service I then need startForegroundService(intent) for API >= 26. In our case we always start the service when the app is in foreground. I do however recall some instances where I've seen the exception thrown when we try to start the service using startService(intent), somehow the app (very very rarely) still think it's in background (perhaps something from the OS comes up just when app is launched). So if I catch when that happens I could run startForegroundService(intent) to start the service. But is it possible to run stopForeground(true) on that service when the app comes back into foreground? Or is it bound to be a foreground service forever since we started it that way?
Seems like you don't have to call startForegroundService and startService is sufficient:
If your service is started (running through Context#startService(Intent)), then also make this service run in the foreground, supplying the ongoing notification to be shown to the user while in this state. By default started services are background, meaning that their process won't be given foreground CPU scheduling (unless something else in that process is foreground) and, if the system needs to kill them to reclaim more memory (such as to display a large page in a web browser), they can be killed without too much harm. You use startForeground(int, Notification) if killing your service would be disruptive to the user, such as if your service is performing background music playback, so the user would notice if their music stopped playing.
From Documentation
I read your question multiple time and I think (hopefully) the answer to your question is :
According to the official document of Android 8.0 Background Execution Limits:
Android 8.0 introduces the new method startForegroundService() to
start a new service in the foreground. After the system has created
the service, the app has five seconds to call the service's
startForeground() method to show the new service's user-visible
notification. If the app does not call startForeground() within the
time limit, the system stops the service and declares the app to be
ANR.
So, make sure you have started ongoing notification by calling startForeground (int id, Notification notification) in the onCreate() method of your service.
Note: Apps targeting API Build.VERSION_CODES.P or later must request the permission Manifest.permission.FOREGROUND_SERVICE in order to use this API.
And there is great medium article posted by Maciej Witowski that help you understand how really new services API works :
https://proandroiddev.com/pitfalls-of-a-foreground-service-lifecycle-59f014c6a125

Service not re-started

We have stumbled upon an issue in Android 8.1 and above that, we haven't seen before, and that, unfortunately, makes our app less convenient for our users. Background sticky services in Android 8.1 and above isn't re-started when user swipe kills the app.
In Android 8.0 and below, there are high promises for services to be always available in the background, especially for services that are implemented as "sticky services".
To sum it up, the non-sticky services is not re-launched during a swipe kill in the recent app list. For sticky services (application host together with the service), the app is re-launched shortly after the kill. And foreground services apps are actually never killed, even during a swipe kill.
This works as intended and just fine in Android 8.0 and before. So, after a kill swipe, the app process is re-created within a short time, and then onCreate and onStartCommand is called, starting up the service completely again.
But from Android 8.1, the onStartCommand call is omitted. So instead of getting onCreate and OnStartCommand, we get onCreate and OnDestroy. This effectively does not make the service active again. Needless to say, we cannot manually start our own service in onCreate either.
I think the following article shows the differences quite clear (and with functional code) between non-sticky services, sticky services, and foreground services.
In Android 8.1 and above, the onStartCommand call is omitted (and therefore the service is not restarted). So instead of getting onCreate and OnStartCommand, we get onCreate and OnDestroy during app re-creation.
Also, we would not prefer to have a visible app icon at the top persistent all the time (as when using setForeground service), and rather just let it work in the background without bothering the user too much.
Don't Optimise Battery may work your service to run properly
you can also use AlarmManager to restart service after a fixed time
it could help click
from android 8 shouldn't be started service in background, you must start service as foreground.
my suggestion using android workManager supported by Google link
workManager is a stable and reliable library for working with service and schedulers task

Remote service still running after unbinding, service stopping even with bound applications

I have an Android Library with a service, which I implemented using AIDL. I want a single instance, cross application to be used with other apps. So I have a base app with the service and I managed to make the library for other apps to use the same instance.
My problem comes when I close all apps using the service, because for every app, the on destroy unbinds from the service, but the service is still running.
Also, I'm only using the service by binding, not by startService().
I checked through android studio that the service is running after closing the apps, and the counter I have on the service for each bind/unbind call is 0!. I increment the counter when there is a call to bind, and decrease for calls to unbind. My only way to make the service stop is by opening the base app, which has the service defined and closing it.
Edit: Also noticed that onBind is being called only once, even for other apps that are binding, but the reference for all those apps is still the same service, they share the same information and only one Service is shown in the android studio.
Edit: I've also observed that if one of the apps using the service is the base app, if I close it, the service dies and another one is started, the other apps don't notice the change, they keep using the service as if nothing happened, which is understandable because it's a remote service.
What is happening and what can I do about it?
For the two points being questioned:
I was able to verify what CommonsWare said that the process in which the service is run is still up, but the service itself is not. At first I had a thread running in the service after every app unbound, but after making sure it wasn't up in the end made the service be destroyed.
The second issue, regarding the service being destroyed while still bound with other activities was solved by seeing this link which describes a bug in android that kills services when it shouldn't. My case was simply solved by making my service run in the foreground, which I didn't know was possible.
After some tests I verified the service is still intact as long as any app is bound with it, and that the service indeed is destroyed correctly after no more app is bound.

Long running background task, Android

I am parsing all text messages from the device and extracting words from them. For doing this I first used Service, but the issue with it was that it made application slower or sometimes I got notification that Application is taking longer to run.
Alternative to this I used IntentService. But problem with intent service is that whenever I stopped the application, I couldn't see my service running anymore. Alongside I also have to use Alarm Manager to schedule the things.
I am planning to use SyncAdapter for doing both of the things, but I don't think it would be a good option to use it. It would be really helpful if there is a better possible for doing this.
Background task might take upto 5-10 minutes for completion and I am planning to run it in every 12 hours. Though I won't be parsing old messages again. So it won't take longer after first time. The task should not end even when application is closed.
Basically IntentService is apt for background tasks which are not tied to the application lifecycle.
But problem with intent service is that whenever I stopped the
application, I couldn't see my service running anymore.
You can send updates to UI from intent service by using:
LocalBroadcastManager: how to use LocalBroadcastManager?
Handler: How to Collect info from IntentService and Update Android UI
Also you might want to see this video: The Zen of IntentService. (Android Performance Patterns)
EDIT:
Forget about using IntentService, it stops as the app stops because it runs on the same process as the app.
Since you want your service to work as a job every 12 hours, you could use a 'Scheduled Service'.
You can use JobScheduler or Firebase JobDispatcher API

Categories

Resources