I’m trying to implement custom call screen when app receive certain push notification.
When I have all the data avalable, I’m trying to raise local notification while app is still in background.
NB: it’s a .NET code for MAUI, but it’s basically a complete copycat of Android API, so should be readable for Java programmers.
void RegisterNotification(string cid, string title){
.......
var clickIntent = new Intent(context, typeof(MainActivity));
clickIntent.AddFlags(ActivityFlags.SingleTop | ActivityFlags.ClearTop);
clickIntent.PutExtra("field1thatneeded", "abcde");
var pendingIntentFlags = (Build.VERSION.SdkInt >= BuildVersionCodes.S) ? PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable : PendingIntentFlags.UpdateCurrent;
var pendingIntent = PendingIntent.GetActivity(context, activityId, clickIntent, pendingIntentFlags);
.......
var notificationBuilder = new Notification.Builder(this, AppConstants.CallChannelID)
.SetAutoCancel(false)
.SetOngoing(true)
.SetContentTitle("Incoming call")
.SetContentText(title)
.SetContentIntent(pendingIntent)
.SetStyle(Notification.CallStyle.ForIncomingCall(incomingCaller, rejectPendingIntent, answerPendingIntent))
.SetCategory(Notification.CategoryCall);
var notification =notificationBuilder.Build();
#if ANDROID29_0_OR_GREATER
StartForeground(notificationID, notification, Android.Content.PM.ForegroundService.TypePhoneCall);
#else
StartForeground(notificationID, notification );
#endif
}
Creating call notification right away not possible before any activity is attached, I cannot get PendingIntent (Attempt to invoke virtual method ‘android.os.UserHandle android.content.Context.getUser()’ on a null object reference) or feed builder with required resources (Attempt to invoke virtual method ‘android.content.res.Resources android.content.Context.getResources()’ on a null object reference / Java.Lang.NullPointerException: Attempt to invoke virtual method ‘android.os.UserHandle android.content.Context.getUser()’ on a null object reference )
The only solution I found is to create a Service:
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypePhoneCall, Exported = false)] //this attribute fills up AndroidManifest
internal class DroidCallService: Service
{
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
if (intent.Action == "START_SERVICE")
{
var callID = intent.GetStringExtra("call_id");
var name = intent.GetStringExtra("call_title");
RegisterNotification(callID, name);
}
else if (intent.Action == "STOP_SERVICE")
{
StopForeground(StopForegroundFlags.Remove);
StopSelfResult(startId);
}
return StartCommandResult.NotSticky;
}
}
When app is unloaded, all this work fine on Androids 11 and older, but since new restrictions of foreground services run, I get error on starting a service:
Intent startService = new Intent(ctx, typeof(DroidCallService));
startService.SetAction("START_SERVICE");
startService.PutExtra("call_id", callID);
startService.PutExtra("call_title", name);
#if ANDROID26_0_OR_GREATER
ctx.StartForegroundService(startService); // ERROR!
#else
ctx.StartService(startService);
#endif
Android.App.ForegroundServiceStartNotAllowedException:
startForegroundService() not allowed due to mAllowStartForeground
false
and
Android.App.BackgroundServiceStartNotAllowedException: Not allowed to
start service Intent { act=START_SERVICE
cmp=com.company.app/crc646332b1bcddc0e22c.DroidCallService
(has extras) }: app is in background
I saw multiple recommendations like asking user to skip battery optimization, using alarm clock or calendar etc, but it’s all like “quirky” solutions.
Task is simple: background instance needs to run a foreground service. What is the proper way to do it and how?
3
Apps that target Android 12 (API level 31) or higher can’t start foreground services while running in the background, except for a few special cases. If an app tries to start a foreground service while the app is running in the background, and the foreground service doesn’t satisfy one of the exceptional cases, the system throws a ForegroundServiceStartNotAllowedException
.
Exemptions from background start restrictions:
- Your app transitions from a user-visible state, such as an activity.
- Your app can start an activity from the background, except for the
case where the app has an activity in the back stack of an existing
task. - Your app receives a high-priority message using Firebase Cloud
Messaging. - The user performs an action on a UI element related to your app. For
example, they might interact with a bubble, notification, widget, or
activity. - Your app invokes an exact alarm to complete an action that the user
requests. - Your app is the device’s current input method.
- Your app receives an event that’s related to geofencing or activity
recognition transition. - After the device reboots and receives the ACTION_BOOT_COMPLETED,
ACTION_LOCKED_BOOT_COMPLETED, or ACTION_MY_PACKAGE_REPLACED intent
action in a broadcast receiver.
For more information, please check: Restrictions on starting a foreground service from the background.