Reputation: 1323
TLDR: If a scheduled alarmClockInfo(PendingIntent) opens a Service instance WHILE the App is on the front with an ACTIVE Service instance via context.bindService()
...
AND the alarm DOES NOT trigger a notification...
an Context.startForegroundService() did not then call Service.startForeground()
error appears.
Details:
My aim is for notifications to be silently queued when the app is on the front... this means that while one notification is being attended (Action that opens an Activity), incoming scheduled alarms fill a queue within the Service instance... and not show any heads-up notifications.
My issue is that... as opposed to most cases here in SO, I cannot control the way in which the Service gets started, in this case via AlarmManager scheduling.
@RequiresApi(api = Build.VERSION_CODES.O)
public static PendingIntent foregroundServicePendingIntent(Context context, int reqCode, Supplier<Intent> intentSupplier) {
assert Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
return PendingIntent.getForegroundService(myService,
reqCode,
intentSupplier.get(),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
}
The intent is the basic stuff... using the context + destination constructor (android.content.Intent#Intent(android.content.Context, java.lang.Class<?>)
), and a bundle with info. with MyService.class as destination.
Please notice the PednignIntent is scheduling a scheduled alarm FROM the MyService TOWARDS the same MyService.
(I've seen the Broadcast mediator option and I'm yet to understand why this indirection is needed if the Broadcaster is still forced to enter the Service sequential event loop)
The scheduling is made as:
assert isAtLeastOreo;
PendingIntent pendingIntent =
PendingIntent.getActivity( // This defines what will get shown when clicking in the clock icon on top of the android status bar.
context,
prefix_code + id,
openAlarmsFragmentIntent,
flag);
manager.setAlarmClock(
new AlarmManager.AlarmClockInfo(timeMillis, pendingIntent),
foregroundServicePendingIntent // variable created via foregroundServicePendingIntent(context, intent)
);
Since "VERSION O (26+)" Trampoline events are no longer allowed, this means that whatever logic the Service executed before resolving the trampoline Action, now needs to be resolved later in the destination once the Activity destination has been created/reached, so a Service instance in the Activity destination must resolve the Intent's bundle either in the onCreate()
or onNewIntent(Intent)
.
This is the thing that may be causing the error...
When the Service handles the heads-up notifications... while the App has not come up to the front, nothing happens, everything is fine. Multiple notifications can handle the same Service instance and no error appears... good.
** CASE A:
If I schedule a SINGLE alarm... no error appears:
When a single notification's Action
opens a "MainActivity"...
This Activity, as required by the notification to fulfill the Intent's bundle resolution MUST grab the Service instance...
I have a singleton that gets assigned when the alarm calls the Service the first time and gets cleared when the Service calls onDestroy()
.
Once the Activity is opened, I can use the Service to perform more alarm scheduling tasks. removals, postpone... etc...
NO error appears.
NOW IF, a second notification arrives AFTER the fact, the error comes up, no matter if this alarm comes up immediately after the Activity has been brought to the front, 5 seconds or 1 minute AFTER.
** CASE B:
This also happens if MainActivity instantiates a Service first... via:
context.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
In this case, the Service becomes active... bound the MainActivity lifecycle, this Service ALSO gets stored on a singleton that gets cleared onDestroy()
.
NO ERROR WHEN:
With Activity and Service in background...
single scheduled alarm opens Service instance (same instance in MainActivity), builds and shows notification.
Notification Action opens Activity instance triggering onNewIntent(Intent)
Activity uses Service instance to continue Intent manipulation + extra stuff...
...finish... no issue.
ERROR WHEN:
AFTER step "3" if a second alarm calls onStartCommand(Intent intent, int flags, int startId)
then the App will go on an ANR error after 5 seconds.
I have NO CONTROL over how the alarms decide to open the Service since this was specified at the moment of AlarmClockInfo creation, so I don't know how am I supposed to solve this issue...
One key fact is that... ONCE an ACTION from a Notifications has been chosen (opening the Activity) every onStartCommand(Intent intent, int flags, int startId)
triggered by scheduled PendingIntents that followed will NOT build and show ANY new heads-up Notifications.
Upvotes: 0
Views: 69
Reputation: 1323
The error disappears when I let the notification appear.
This means that... when a Service gets opened via a PendingIntent triggered by a scheduled AlarmClockInfo... maybe even from some other source...
The Service is obliged to let the notification appear...
This is weird since it becomes at odds with the purpose a Service is supposed to serve according to the docs, which is not necessarily to display notifications, but it may also be used to do other background tasks...
Another culprit may be the way in which the AlarmScheduler triggers the Service activation which dictates that it's only function should be that of displaying notifications.
In conclusion this seems like a security counter measure... which in all honesty seems fine with me.
Upvotes: 0