Firebase cloud messaging data messages stop being received after some period - android

I want to use my Samsung Android 12 device to track if there is an electricity supply (always connected to AC). In order to achieve this I came up with the idea that the device can send data to Firebase Firestore every minute.
At first, I implemented foreground service, displayed a notification, and configured settings for the app so that it is not optimized by device care. But after one day my service got killed and the device stopped updating the status of the electricity supply.
Then I found that I can leverage Firebase functions pubsub to send recurrent data messages to my device. I went through this article in order to build proper data messages and configure receiving of them: Receive messages in an Android app. But after 10-12 hours the device stopped responding to the messages (firebase logs say it was sent).
Here is how I send messages in my cloud function:
const payload = {
data: {},
android: {
'direct_boot_ok': true,
"priority": "high",
"ttl": 60000
},
topic: 'get-el'
};
const response = await admin
.messaging()
.send(payload);
My FirebaseMessagingService configuration is the following:
<service
android:name=".MyFirebaseMessagingService"
android:exported="false"
android:directBootAware="true">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Here is how I subscribe to the topic in the Application.onCreate method:
FirebaseMessaging.getInstance().subscribeToTopic("get-el").addOnSuccessListener {
Toast.makeText(
applicationContext,
"Subscribed to the topic",
Toast.LENGTH_LONG
).show()
}
I also installed the dependency in order to support direct boot:
implementation 'com.google.firebase:firebase-messaging-directboot:20.2.0'
I would appreciate any ideas on how I can troubleshoot it or how to achieve my goal another way.

Related

Nodejs Firebase cloud messaging delayed delivery on android

I'm trying to push some high-priority data messages from a nodejs backend to the android client. Here is my code,
var payload = {
data
};
var options = {
priority: "high",
timeToLive: 0,
ttl: 0,
android: {
priority: "high",
timeToLive: 0,
ttl: 0,
},
};
admin.messaging()
.sendToDevice(deviceFcmId, payload, options)
.then(function (response) {})
.catch(function (response) {
console.error("error sendNotification", response);
});
As you can see, I'm using priority "high" and timeToLive: 0. But this does not wake up the device if it is in Doze mode, or sometimes even if the device is in an unlocked state, the message is delayed up to a few minutes. This is a voice-calling app, so the delivery delay should be minimal. I've searched similar posts on StackOverflow but nothing works for me. What am I missing here? Thanks in advance.
UPDATE:
In my app, there are two types of notifications, Chat notifications and Call notifications. When the user receives a chat notification, the app will write a Realm database entry and fetch the sender's profile picture from API, and shows the notification. I know it's a network call, but the thing is, the image fetching is managed by Glide so 90% of the time it will be a disk cache read rather than a network call (even the network call has a 1-second timeout, after this timeout, it will return a local image).
When the user receives a call notification, the app will start a service and ping the caller using socket.io. If the ping is a success it will show the call screen using a fullscreen intent by calling startForeground from the service. If the ping fails it will show a miscall notification and stops the service.
So, no matter whichever the case is there will always be a user-facing notification.
From the official docs
High priority messages on Android are meant for time sensitive, user visible content, and should result in user-facing notifications. If FCM detects a pattern in which messages do not result in user-facing notifications, your messages may be deprioritized to normal priority. FCM uses 7 days of message behavior when determining whether to deprioritize messages; it makes this determination independently for every instance of your application. If, in response to high priority messages, notifications are displayed in a way that is visible to the user, then your future high-priority messages will not be deprioritized.
This means your android app should pretty much just show a notification (make sure the app has notifications enabled). If you abuse this and instead try to directly do other things when receiving the push (such as open connections to the backend or do any other computationally-expensive task) then your messages might be downgraded to normal priority.
On a single device, you can verify if a message has been deprioritized by checking if getPriority() returns a lower value than getOriginalPriority().
And for all messages, you can query the Data API.

With react-native-firebase v6, app crashed when receive notification while in foreground

I try to get react-native-firebase v6 to work in my app. I use React Native 0.59.10.
I have installed react-native-firebase v6 according to the documentation. It didn't specify about adding service MyFirebaseMessagingService into the AndroidManifest.xml unlike in v5 so I didn't do it. Afterwards, the app didn't receive any notification while in foreground but did receive them while in background.
I tried to add MyFirebaseMessagingService into AndroidManifest.xml like so:
<service
android:name=".java.MyFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
There was some sort of a progress. The app crashed immediately after I sent a notification from Firebase console. Hence, I knew the app was aware of incoming notification but somehow crashed.
Below is my code to import and initialize a listener.
import messaging from '#react-native-firebase/messaging';
import { Alert } from 'react-native';
// Initialize notifications
const init = () => {
try {
messaging().onMessage((message) => {
Alert.alert('Received', JSON.stringify(message));
});
} catch (err) {
Alert.alert('Error', err.message);
}
};
In summary, I expect to receive a notification while the app is in foreground but nothing happens if I don't add MyFirebaseMessagingService to AndroidManifest.xml. If I add it, the app will crash on receiving notification.
Okay. After further hair pulling and research, I found out that this was en expected behaviour rather than a bug.
Adding MyFirebaseMessagingService or any other service to AndroidManifest.xml to install react-native-firebase v6 is unneccessary.
Afterwards, the app didn't receive any notification while in foreground but did receive them while in background.
Actually, it does. After peeking through Android log with adb logcat -s RNFirebaseMsgService:V, I found out that react-native-firebase v6 doesn't support notification as of now. This is their code which made me think my app didn't receive any notification while in foreground.
if (remoteMessage.getNotification() != null && remoteMessage.getData().size() == 0) {
// TODO broadcast intent when notifications module ready
return;
}
It's still a to-do item for them lmao!!
It turned out I need to send a notification with custom data because it already is supported. Hence, I need to use it every time so my app could display anything upon receiving such a notification.

Firebase message with high priority not waking device from Doze android 6+

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 want to receive GCM notification in all android devices even if that app is closed?

I have implemented GCM push notification :
Case 1 : When app is in foreground receive notification on all devices - OK
Case 2 : when app is in background receive notification on all devices - OK
Case 3 : when app is closed from task manager or recently app list won't receive notification on some devices and receive on some devices.
so can anyone help me how can we achieve to receive notification on all devices when app is closed
this my code snippet for GCM :
How to open android app on all devices when receive push notification (GCM) while app is closed?
GCM? Not FCM? Google recommends using FCM - so this answer might not be applicable to all aspects but I want to share what I know anyway.
What happens on a device depends on the content of the notification.
Basically you have two types of Notifications:
If the json of the message does NOT contain a "notification" object (which describes the visible part of the notification in the top toolbar of the device), your App will always receive the notification (wake up), no matter whether it is running, in foreground or background or charging or screen on or off or any other state you can think of. As long as it is connected to the internet of course.
Important is here, that you have registered your service correctly in the manifest and with FCM.
(You need a Service derived from FirebaseMessagingService that overrides the method onMessageReceived). I can provide a sample if needed.
If the json does contain a "notification" object, there can happen two different things:
First, if your app is not running, it will not be started. Instead, just a notification is displayed on the top bar of your device and only when the user clicks it, your app starts.
Second, if your app is running, the Activity that contains the matching intent-filter for the notification will be launched/called.
Here is an example of the json of such a message - notice the tag - This will group similar messages, and notice the click_action this is the Action of the intent for the intent filter that will trigger.
{
"to":"<<FCMToken>>",
"priority":"high",
"notification":{
"title":"sender name",
"body":"chat message",
"icon":"icon_nav_main_chat",
"tag":"XMPP_MESSAGE",
"click_action":"XMPP_MESSAGE",
"sound":"default"
},
"data":{
"body":"stanza-content"
}
}
So basically, you decide what will happen by sending the appropriate message.
If you want to receive it ALWAYS (app running or not), you could omit the notification part, then the message will always be delivered to your service.
The drawback of this is, you have to launch any visible notification locally and put your entire data you need in the payload of the data object. You have about 4000 Bytes here if I remember right.
But there are use cases where this makes sense. Whatsapp does it that way as far as i know, as they use the way more powerful local notifications with direct reply and that kind of things.
Hope this helps,
cheers, Gris
First have a look at WakefulBroadcastReceiver. After that you need to change your code to:
AndroidManifest.xml
// add permission for wake lock
<uses-permission android:name="android.permission.WAKE_LOCK" />
<receiver
android:name=".GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="YOUR_APPLICATION_PACKAGE_NAME" />
</intent-filter>
</receiver>
<service android:name=".YourGcmIntentService" />
Java Code:
public class MyWakefulBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ComponentName comp = new ComponentName(context.getPackageName(),
YourGcmIntentService.class.getName());
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
Note:
GCM is deprecated you should consider FCM.

Android Firebase MyFirebaseMessagingService not called when app is swiped from recent list

Edit 24/11/2016:
I've just received word from Firebase Support. They suggest this is an Asus issue. The device I'm using is an Asus ZenFone 2 Laser. Here's their response:
The issue here is that Google does not control how manufacturers
customize their version of android. If Asus is following the Android
guidelines, then the notification should work (like on the other
devices).
One thing that we found is that some phones put the apps in force-stop
state, which prevents the app from receiving the notification.
From your test result this doesn't seems to be the case. BUT there are
many other ways in which Asus can stop notifications from working, and
unfortunately we cannot check all of them.
It would be best for you to contact Asus support and let us know if
there are any news.
As this is a low priority issue for my client, I'm not going to pursue it further at the moment. I unfortunately don't have the time to test the same issue on other devices. If this changes in the future and I'll update this issue accirdingly. Until now, I will consider this unsolved. I would be interested to hear if others are having the same issues on other brand phones.
As for Asus users, there's one thing to consider: There are power saving settings that disable notificaitons.
Edit 30/09/2016:
I've contacted Firebase support about this issue. They seem to agree with what Arthur Thompson is saying:
Stopped apps should not receive messages.
Swiping an app from the recent list should not stop an app.
The interesting part is that my app doesn't seem to be stopped. When running adb shell dumpsys package | grep stopped my package returns:
User 0: installed=true hidden=false stopped=false notLaunched=false enabled=0
This indicates it's not stopped and should still receive messages (according to the very helpful Firebase Support engineer). However the app is not receiving the messages.
Edit 27/09/2016:
I've omitted the notification object in the payload and moved that information do the data object instead. This doesn't solve the issue. I think I haven't been clear enough on what the issue is. The issue is that when the app is killed (swiped out of recent) the service doesn't get called or receive notifications anymore.
Original post
There's similar posts out there, but none that address this issue or give a solution.
I'm implementing FCM to get notifications. (I'm fairly new at FCM.) The message payload is a combo of both notification and data. When the app is in the foreground the service fires the onMessageReceived(). When the app is in the background (i.e. I push the home button), onMessageReceived() doesn't fire (as expected) but simply shows a notification. I can still see the log statements on MyFirebaseMessagingService.onCreate() and onDestroy(), which is great.
Now when I close the app by pushing the 'recent apps' button, and swiping the app out of the list, I no longer receive any notifications. onCreate() and onDestroy() are no longer called. No notification appears. I would like a notification to appear in this scenario too.
Example payload:
{
"to":"token",
"priority":"high",
"notification": {
"body":"hello",
"title":"Message from Mom",
"click_action":"ACTIVITY_MAIN"
},
"data":{
"open":"chat"
}
}
FCM Service:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onCreate() {
super.onCreate();
Log.v(DmcApplication.TAG, "Service Created");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.v(DmcApplication.TAG, "Service Destroyed...");
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Log.v(DmcApplication.TAG, "Message received!");
EventBus.getDefault().post(new MessageEvent(remoteMessage.getNotification().getBody()));
}
}
Bit of the manifest (in case relevant)
<service
android:name="com.example.package.myFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Logs:
//Log when app is in foreground and message comes in:
V/coo: Service Created
V/coo: Message received!
V/coo: Service Destroyed...
//Log when app is in background and message comes in:
V/coo: Service Created
V/coo: Service Destroyed...
//Log when app is killed:
<silence...>
Any help would be greatly appreciated as I've been stuck with this for days now.
I ran into the same issue, looking at your "priority:high" string it looks like you are sending to Android and iOS. I ended up getting rid of the notification portion of the message and just putting the information that I needed into the data message. The data payload part triggers your service even when it is backgrounded or removed from recent apps.
On my server I check to see if the user registered their token from an iOS device or an Android device and send out the payload accordingly with the notification payload for iOS devices and the data payload for Android devices.

Categories

Resources