Firebase message with high priority not waking dev

2019-02-01 13:52发布

问题:

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.

  1. 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.

  2. 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

回答1:

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!



回答2:

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.



回答3:

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.



回答4:

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