Question
I have come across some voices stating that FCM data message delivery is less consistent compared to that of notification messages. Does anyone have direct experience or can point me to resources exploring the issue? Or is a notification message just a collapsible, high-priority data message that the Firebase SDK handles automatically?
https://stackoverflow.com/a/49998176
FCM data message not received in Android when the application is in background
About FCM's notification message and data message type, which has better receiving rate
The question does not consider the case of force quitting the app. In this scenario, both types of messages will not be delivered (to my knowledge).
Background
I am writing a new Android SDK for a push service provider (similar to OneSignal). The SDK should handle the display of push notifications by default, optionally the client app can handle incoming pushes itself.
The actual delivery is of course done by Firebase Cloud Messaging (on devices running Play Services). So there are 2 types of messages to choose from on FCM: data vs notification messages.
As data messages are consistently handled by the registered FirebaseMessagingService (provided there is no notification key in the payload), this should be the way to go for the SDK. [See documentation] So far, I have not been able to produce a situation in which a data message was not delivered (foreground or background).
By setting the priority in Message, We can reflect the delivery.
You have two options for assigning delivery priority to downstream messages on Android: normal and high priority. Delivery of normal and high priority messages works like this:
Normal priority. This is the default priority for data messages. Normal priority messages are delivered immediately when the device is not sleeping. When the device is in Doze mode, delivery may be delayed to conserve battery until the device exits doze. For less time-sensitive messages, such as notifications of new email, keeping your UI in sync, or syncing app data in the background, choose normal delivery priority.
When receiving a normal priority message on Android that requests a background data sync for your app, you can schedule a task with WorkManager to handle it when the network is available.
High priority. FCM attempts to deliver high priority messages immediately, allowing FCM to wake a sleeping device when necessary and to run some limited processing (including very limited network access). High priority messages generally should result in user interaction with your app or its notifications.
See Set and manage message priority topic in FireBase Documentation.
For More details you can prefer this link
For Displaying the Notification You Can Prefer this link
Best Of Luck In Advance.
I am sending "Data" messages with Firebase to my app, but when the app is in Doze mode, those messages are not handled by FirebaseMessagingService, even when my app is whitelisted for battery optimisation and my messages marked as priority.
Firebase docs on why I choose "Data" messages instead of notification.
Android mechanism to add app to whitelist and skip battery optimisation.
Firebase priority messages according to documentation.
I follow all this steps, and even after this, messages are not handed to my implementation of FirebaseMessagingService.
Any idea how to force messages be handed by FirebaseMessagingService, even in Doze mode?
Note: I'm working on a VoIp app, if message its not immediately handed, its no longer valid and the purpose of it is lost.
If you are following all the documentation, it may still be the case that FCM has deprioritized your messages, as described in the documentation:
High priority messages generally should result in user interaction
with your app or its notifications. If FCM detects a pattern in which
they don't, your messages may be de-prioritized. Android P introduced
app standby buckets which limit the number of FCM high priority
messages you can send to your app that don't result in the user using
your app or viewing a notification. If, in response to a high priority
message, a notification is displayed in a way that is visible to the
user, then your app standby bucket quota will not be consumed by that
message.
There is no "force" mode that lets you operate above the rules stated in the documentation. If anyone could simply bypass the rules, then everyone would do it, and the rules would become useless to the end user, who is just trying to save their battery from apps that aren't following best practices.
After a while we found out the problem.
In server side instead of implementing the newest API the legacy one was implemented.
They have a different payload that can be seen here: Firebase Payload
basically instead of :
"android":{
"ttl": "0s",
"priority": "high"
}
we had to use:
"priority": 10,
"time_to_live": 0
TLDR: Messages send to Android devices via FCM take between 10 seconds and 5 minutes to get delivered. Probably due to priority. I set it to "high", but it seems like it doesn't stay on that value.
I develop an app both for iOS and Android. My backend runs on Django. For our realtime communication we recently started using Firebase Cloud Messaging (FCM).
I successfully managed to connect the Django server and am able to send messages to both kind of devices.
Here is my python code that constructs the message. Note that the token data gets appended later dynamically and that the messages are silent notifications.
def _build_silent_message(not_id, data):
"""Construct silent notifiation message.
Silent means that this message won't show up in the notifications hub
of the app.
"""
return {
'message': {
'data': {"data": data},
'apns': {
'payload': {
"notId": not_id, # notId HAS TO BE FIRST!!!
'aps': {
'content-available': 1
}
},
'headers': {
'apns-priority': '10'
},
},
'android': {
'priority': "high",
'data': {
"androidData": data,
'content-available': '1'
}
}
}
}
As you can see I set both the Apple and the Android priority to "high" (/ 10). On iOS all the messages get delivered immediately. On Android however this is not the case. Messages take up to 5 minutes until they arrive on the phones. I think this might be a priority issue, as the documentation states:
High priority. FCM attempts to deliver high priority messages immediately, allowing the FCM service to wake a sleeping device when necessary and to run some limited processing (including very limited network access).
So far so good. I set the priority to high. But the documentation further states:
High priority messages generally should result in user interaction with your app. If FCM detects a pattern in which they don't, your messages may be de-prioritized.
All my messages I sent to the devices do require interaction with the phone. But all my messages are silent messages. Maybe FCM thinks my messages aren't requiring user interaction and therefore gives it a lower priority.
Does any one know how to solve this?
EDIT: I tested with Android Samsung Galaxy S7 and Google Pixel 2.
You don't have access to the way FCM delivers the messages to the device. Just because you marked the message as high priority doesn't mean it is a high priority message that should be delivered immediately.
FCM will observe the interactivity of your notifications/FCM messages. If the user doesn't usually follow through with interacting with them FCM will de-prioritize your messages hence a longer delivery time.
FCM is intrusive in regards to UX...FCM was created in a way not to expose the user to irrelevant data. Think of it as an adBlocker that keeps you away from interrupting a user.
Check out the official documentation for FCM on how it works.
https://firebase.google.com/docs/cloud-messaging/concept-options
I have migrated my project from using GCM to use Firebase. Push notification comes through ok when the device is awake or been asleep recently but if I leave the device for say an hour, no push is sent until I wakeup the device.
The Android docs say that if you need to wake a device to deliver a message, then use FireBase with priority set to high. It also says that device Admin apps are not subject to Doze restrictions, my app is a device admin app.
I thought I would mention that when I migrated the project from GCM to FCM, I only specified the package name in the firebase console and not the fingerprint.
What I have tried.
Set priority to high
{
"time_to_live": 300000,
"delay_while_idle": false,
"android": {
"priority": "high"
},
"data": {
"message": "PING_DEVICE",
"time": "21/01/2018 16:20:28",
"pushguid": "10062"
},
"registration_ids": [
"eOMT........"
]
}
Time to live is set so the message will come through eventually. delay_while_idle is set to false, this is ignored by FCM after sept 2016.
Device admin apps are not subject to Doze, mine is a device admin app but I have also explicitly added the app to the Doze whitelist found in Setting -> Battery -> Optimization. This was done manually through the settings app and NOT programmatically in code.
I have left my device to go to sleep for 3 hours and no push come through. I have also used adb to put the device into Doze. When adb puts the device in Doze no push is received, when adb takes the device out of Doze, the push comes through.
further thoughts I haven't tried.
My pushes are data messages. This is because I don't want the push to come to the notification bar on the device and have the user click it to execute the functionality. The user has no interaction with the device admin app. So a data message is handled by
onMessageReceived(RemoteMessage remoteMessage)
I believe notification messages do wake up the device, which is what I need but I want the app to handle the push, not the user. Could I have messages that are both notification and data but have onMessageRecievied handle the functionality?
Has anybody experienced anything similar or have any solutions to this?
[EDIT1]
I have found the following link below that says you can send a message which is both notification and data, but if the app is in the background, the notification is displayed but the data is only executed when the user clicks the notification. This is not what I want as I would like the data to execute in onMessageRecived straight away.
notification with data
[EDIT2]
I have added the following code and permission to the app. The app now asks the user to whitelist the app for Doze, so I clicked yes. I then via adb put the device in Doze and sent a push. Nothing came through until I took the device back out of doze mode. So, unfortunately, this does not work.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Intent intent = new Intent();
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(packageName)) {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
}
}
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
[EDIT3]
I have done further testing to try to isolate the problem and take my web application code out of the equation. I put the device into Doze via adb and the used FireBase console to send the push instead. The push came through correctly This tells me there is a problem with my web application code that sends all the push info to the fcm endpoint. I will get the code tonight and post later.
[EDIT4]
i have just done some more testing. I placed the device into doze then used FireBase console to send a data message with 2 key-value pairs. When the device is in Doze and the app is in the foreground (on the screen), the push comes through and onMessageReceived executes. This is great. However, if the app is in the BG then only a notification is displayed. I understand that from the docs, the data messages are dispatched to the launcher activity via an Intent, but my launcher app does not handle the pushes.The class that handles the pushes is called MyAndroidFirebaseMsgService and extends FirebaseMessagingService.
Do i have to route the intent to this class in case the app is in the BG? Seems a bit starnge to have to do this. It was never the case in GCM.
Also, i do not want the app launching from a push as this is very invasive as the device user could be using a different app. My app is also a device admin app, so 99% of the time there is no user interaction, it is just a client that executes policies on the device.
[edit5]
internal static void SendNotification ( Dictionary<string, string> nameValues , List<string> theregIDs , string sPushName)
{
string stringregIds = string.Join("\",\"", theregIDs) ;
JavaScriptSerializer js = new JavaScriptSerializer();
string keyValueJson = js.Serialize(nameValues);
string TIME_TO_LIVE = "604800";
string DELAY_WHILE_IDLE = "false";
string ENDPOINTADDRESS = #"https://fcm.googleapis.com/fcm/send";
postData = String.Concat("{\"time_to_live\":", TIME_TO_LIVE, ",\"delay_while_idle\": ", DELAY_WHILE_IDLE, ", \"android\":{\"priority\":\"high\" } ,\"data\": { \"message\" : " + "\"" + sPushName + "\",\"time\": " + "\"" + System.DateTime.Now.ToString() + "\""
, keyValueJson
, "},\"registration_ids\":[\"" + stringregIds + "\"]}");
WebRequest myWebRequest = null;
WebResponse myWebResponse = null;
try
{
myWebRequest = WebRequest.Create(ENDPOINTADDRESS);
myWebRequest.Method = "post";
myWebRequest.ContentType = "application/json";
// myWebRequest.ContentType = "application/x-www-form-urlencoded;charset=UTF-8";
myWebRequest.Headers.Add("Authorization: key=" + Our_Api_Key);
myWebRequest.Headers.Add("Sender:id=" + Our_Sender_Id);
Byte[] BA = Encoding.UTF8.GetBytes(postData);
myWebRequest.ContentLength = BA.Length;
using (Stream dataStreamOut = myWebRequest.GetRequestStream())
{
dataStreamOut.Write(BA, 0, BA.Length);
}
using (myWebResponse = myWebRequest.GetResponse())
{
using (Stream dataStream = myWebResponse.GetResponseStream())
{
using (StreamReader tReader = new StreamReader(dataStream))
{
strServerResponse = tReader.ReadToEnd();
}
}
}
}
catch (WebException ex)
{
}
}//
thanks
After struggling with a similar issue I managed to get it to work.
I send following json data through postman:
{
"data": {
"body": "Test body from curl"
},
"registration_ids": ["Token"],
"webpush": {
"headers": {
"Urgency": "high"
}
},
"android": {
"priority": "high"
},
"priority": 10
}
It seems like the last "priority":10 is what's fixing it for me.
I could not find any reference to this in the Firebase documentation, but in the deprecated GCM documentation it's used. https://developers.google.com/cloud-messaging/concept-options
TL;DR - make sure to set the notification priority correctly based on the JSON payload structure for FCM's legacy HTTP protocol vs its HTTP v1 protocol.
There may already be sufficient answers from posts above based on your circumstance or implementation, but I wanted to provide an answer with more context based on the distinction between the legacy HTTP and HTTP v1 protocols that FCM provides in their documentation, but there is a subtle difference between the two protocol APIs when setting notification priority.
Our team experienced the same problem of not receiving push notifications on Android 6+ devices that have Doze enabled even though our server was seemingly setting the priority correctly in the FCM API payload that similar to the payload provided in the original question. We rely on Amazon SNS to forward payloads to FCM, and the payload sent from our server to Amazon SNS would set the priority based on the AndroidConfig JSON object:
{
"android": {
"priority": "high"
}
}
However, this is only correct according to the HTTP v1 protocol. What we didn't realize is that Amazon SNS is likely still using the legacy HTTP protocol where the priority has to be set at the top-level of the JSON payload:
{
"priority": "high", // legacy HTTP protocol (this can also be set to 10)
"android": {
"priority": "high" // HTTP v1 protocol
}
}
Thus, the notification priority would only take effect and enable push notifications to be received while in Doze when the legacy HTTP priority parameter was set to "high" or 10.
For context, these are the API endpoints for each protocol when sending messages to FCM:
Legacy HTTP: https://fcm.googleapis.com/fcm/send
Requires top-level "priority" param (see here)
HTTP v1: https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send
Requires "android" object with "priority" param (see here)
There is nothing you can do.
This is a known issue that is caused by a battery optimization implemented by some OEMs (like Meizu or Asus). When an app is swiped away in the app switcher, the application is treated as if it were Force stopped, which is not the default Android behavoir. The unfortunate side effect of this is that it can cause the FCM service for your app to stop running. Similar effect can be caused on high priority messages in doze mode.
Firebase team is working to improve this behavior from their end but the actual fix has to come from OEM side.
One way to check if your app is affected by any OEM's battery management feature, is as below:
1) Attach the OEM device to adb
2) Run your app on the device
3) Swipe the app away from recent screen on the device
4) Run command: adb shell dumpsys package MY-PACKAGE | grep stopped
If it shows stopped=true, it's safe to assume that the OEM has such a mechanism and that your app is affected by the same.
While working on an application, I am also stuck at this point. Then I found a issue about it on Github, which solved my problem. That is,
On devices running Android 6.0+, Doze mode terminates all background
connections when the phone is idle and not being charged, including
the background connection to Pushy.
As soon as the device is moved or awoken by the user, background
connections are restored and any pending notifications will be
delivered within seconds, providing they have not expired yet.
To send notifications to devices in Doze mode, your app can declare
the REQUEST_IGNORE_BATTERY_OPTIMIZATIONS permission in its
AndroidManifest.xml and display a system dialog that asks users to
whitelist your app from battery optimizations without leaving the app.
This will effectively keep the background connection to Pushy active
and devices will be able to receive notifications even in Doze mode.
You can check this issue here https://github.com/ToothlessGear/node-gcm/issues/231
Hope it helps you!
It looks like it is not possible to make high priority while sending only data field without notification. Here is the quote from documentation:
High priority messages generally should result in user interaction with your app or its notifications. If FCM detects a pattern in which they don't, your messages may be de-prioritized.
Setting a time_to_live of 0 solved the problem for me.
I think it's because a very small time_to_live will tell FCM that this message is only worth delivering right this instant. So in an attempt to deliver it ASAP, it will ignore battery optimizations like the Android P's "app standby buckets".
Be careful though, as setting a small time_to_live might mean not delivering the notification at all in some cases. I don't think you should be applying it to all kinds of push notifications.
For more details about time_to_live: https://firebase.google.com/docs/cloud-messaging/concept-options#setting-the-priority-of-a-message
Instead of using "android":{"priority":"high"}, use like this
{
"time_to_live": 300000,
"delay_while_idle": false,
"data": {
"message": "PING_DEVICE",
"time": "21/01/2018 16:20:28",
"pushguid": "10062"
},
"priority": "high"
}
Thanks for the replies everyone. we finally sorted it.
We logged into the firebase console and realized due to the age of the code we did not use a settings/config file that is generated in the console. sorry i've forgotten the name of it. this file has settings etc which are used when pushes are sent to google. once we used the file in our requests, my application can wake up a phone in doze.
thanks
I implemented push notification.
It works find when the app is in foreground (the notification triggers OnMessageReceived method).
But, when the app is in background, there's no heads up.
And, when the phone is in sleep mode, the notification don't turns on the screen.
How can I fix this
Check that you send data.
Your request body should be constant data field. E.q.:
{
"to": "/topics/fcm_globals",
"data": {
"field1": "value1",
"field2": "value2"
}
}
If you have Xiaomi device. Check that system not blocked you app in background.
With FCM, you can send two types of messages to clients:
Notification messages, sometimes thought of as "display messages."
Data messages, which are handled by the client app.
As per FCM official document,
Use only data messages if you want to receive(In OnMessageReceived method) and process it when your app is in background.