phonegap / phonegap-plugin-push

Register and receive push notifications
MIT License
1.94k stars 1.91k forks source link

Invalid token even though registration was successful #927

Open rdesimone opened 8 years ago

rdesimone commented 8 years ago

GCM notifications seem not to work anymore on Android 6.0.1 (Nexus 6p). Also the upgrade to version 1.6.4 of the plugin-push did not help.

The app registers successfully and receives the registrationId -> no error.

When sending the message to GCM, the backend returns an error: "NotRegistered"

With the same APK, Nexus 4 (Android 4.4.4) and Nexus 7 (Android 5.1.1) devices receive push notifications.

As there was this Google Play Service update which created other issues, I compared versions on the three different devices - they are different:

Nexus 6p: 9.0.83 (440-1219111109) Nexus 7: 9.0.83 (236-1219111109) Nexus 4: 9.0.82 (036-121907432)

But: the same Nexus 6p receives GCM notifications with an older app version that uses the old phonegap-build push plugin. The server backend is the same. Also from other apps push notifications can be received.

Thanks for your help!

Environment:

Mac OSX Android Studio 2.1.1 Android Support Repository 32.0.0

Cordova CLI 6.0.0 cordova-android@5.1.1 phonegap-plugin-push@1.6.4 cordova-plugin-crosswalk-webview@1.6.1

rdesimone commented 8 years ago

@macdonst we retested several times before submitting this issue. Also we have an utility where we can enter the registrationId and send messages to GCM. We are sure, we send the registrationId to GCM. Nexus 4 and Nexus 7 receive the notification. The Nexus 6p doesn't receive the notification. Did you test with an Android 6 device?

macdonst commented 8 years ago

@rdesimone Yes, in fact I tested with a Nexus 6P running Android 6 and Google Play Services 9.0.83 (440-1219111109). So essentially the same phone as you are using. I'd be interested to see what if any logs are shown when you register/receive a push when running adb logcat | grep PushPlugin

rdesimone commented 8 years ago

@macdonst what do you want to know - I cannot send you the log with senderID. But I don't see problems. There is onRegistered with a registrationId and the id itself.

rdesimone commented 8 years ago

@macdonst OK, removed the ids... LOL. Do you need something more?

05-25 16:13:36.685 21860 21957 V PushPlugin: execute: action=init
05-25 16:13:36.686 21860 21958 V PushPlugin: execute: data=[{"android":{"senderID":"xxxxxxxxxxx","icon":"ic_notify"},"ios":{"alert":"true","badge":"false","sound":"true"},"windows":{}}]
05-25 16:13:36.687 21860 21958 V PushPlugin: execute: jo={"senderID":"xxxxxxxxxxxxxxx","icon":"ic_notify"}
05-25 16:13:36.687 21860 21958 V PushPlugin: execute: senderID=xxxxxxxxx
05-25 16:13:38.215 21860 21958 V PushPlugin: onRegistered: {"registrationId":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
05-25 16:13:38.215 21860 21958 D PushPlugin: no iconColor option
macdonst commented 8 years ago

@rdesimone yeah, you are never getting the push data to the device so the log is useless.

Are you sure the sender ID you are using on the Nexus 6P corresponds to the API key you are using on your backend? If they are mis-matched you would get this issue. I honestly can't reproduce this and I've tried on multiple phones.

ngt14 commented 8 years ago

this may be due to auto-start which blocking your applications , and you can't receive notification. i've got the same problem the last time

rdesimone commented 8 years ago

@macdonst yes, of course - otherwise Nexus 4 and Nexus 7 would not receive push, they are using the same APK. The server gives an error (NotRegistered) for this registrationId even though registration was successful. @jedofus can you explain more.

macdonst commented 8 years ago

@rdesimone well I have to ask because it is a mistake I've made before :) Actually, your 6P can get a successful registration even when you have the wrong sender ID so if you are saying your are building and running the code from the same location for the Nexus 4, 7 and 6P I have to trust you.

ngt14 commented 8 years ago

i have a Asus Zenfone, and i have application that you can manage your app , and you can enable or desabled app on start of phone . https://www.asus.com/support/faq/1011266/

rdesimone commented 8 years ago

@macdonst LOL - you are right to ask - the most stupid things happen sometimes!

rdesimone commented 8 years ago

@macdonst I start to receive notifications again - I did not change anything - but the registrationId (token) changed. GCM can revoke a token for several reasons - https://developers.google.com/cloud-messaging/registration#automatic-retry-using-exponential-back-off . The docs recommend to handle the tokenRefresh message the client app must handle a tokenRefreshed message. For about 24h no messages were delivered to the device and the token did not update even though I started the app multiple times and each time a successful registration happened (with obviously an invalid token). At the moment, I have no opinion about this situation and if the app/plugin should or could handle this situation. The question is: why did the token not update earlier in the app?

rdesimone commented 8 years ago

@macdonst the title of this issue should be: "invalid token even though registration was successful"

rdesimone commented 8 years ago

@macdonst some thoughts: https://developers.google.com/cloud-messaging/registration#automatic-retry-using-exponential-back-off

  1. we learned a token can be revoked and a new token will be issued. This should be handled with the tokenRefreshedmessage. This seems to be a standard procedure. Does the plugin support this?
  2. the registration returns success but the delivered token is invalid (the case of this issue). IMO this should never happen. I would expect that the registration returns an error or at least an empty token. Now, who is to blame for this? Does the plugin catch all messages correctly? Is this a GCM bug? Do we miss something?
macdonst commented 8 years ago

@rdesimone Yeah, the plugin registers a service for token refresh messages. I'm going to see if I can find a way to properly simulate the refresh message.

rdesimone commented 8 years ago

@macdonst my Java/Android skills are nearly inexistent but I think your code is wrong. As far as I can see, you are using a saved token - you take only a token if their is not a saved token. You don't take under consideration that the token can refresh. This would explain the issue.

// first time run get new token
if ("".equals(savedRegID)) {
    token = InstanceID.getInstance(getApplicationContext()).getToken(senderID, GCM);
}
 // new sender ID, re-register
else if (!savedSenderID.equals(senderID)) {
    token = InstanceID.getInstance(getApplicationContext()).getToken(senderID, GCM);
}
// use the saved one
else {
    token = sharedPref.getString(REGISTRATION_ID, "");
}
macdonst commented 8 years ago

@rdesimone yes, but you are totally ignoring PushInstanceIDListenerService.java which listens for Google invalidating your sender ID and RegistrationIntentService.java which handles the registration for you.

rdesimone commented 8 years ago

@macdonst yes, but this seems to be for onTokenRefresh. IMO the problem is .init()when called by JS - the app registers, gets a token and the token will be stored. At the next .init()the stored token will be delivered instead of asking again for the token. At the moment the code assumes that onTokenRefresh will update the stored token. There are several insecurities: when does this event really fire? What happens when the app is closed while GCM sends the event? Does the event fire in the current version? To handle this case also a JS event for onTokenRefresh is needed in order that the web app can handle the change and update the token on the server. But why - as an additional security mechanism - not to ask on every .init()for a token to be sure to get a valid token? Let me know if I am wrong.

macdonst commented 8 years ago

@rdesimone onTokenRefresh should be called when Google invalidates the token. It shouldn't matter if the app is running or not as it will come in via the GCM sub-system and then find the service that is responsible for receiving the broadcast and start it. Like I said earlier I need to find a way to simulate this occurance.

rdesimone commented 8 years ago

@macdonst OK, I let you know my opinion - you have more experience with this topic. Thanks for your time!

fredgalvao commented 8 years ago

Actually, the only scenario where a delay would exist between a new token being generated and it being delivered to your application (js code) would be the following:

At this point, caused by the absence of a register event being triggered exactly when the token was refreshed, the whole code base (app and backend/service) will be definitely outdated. So @rdesimone is right: there are situations that we can have inconsistency.

Possible solutions

  1. Create a new event, called tokenRefresh, and trigger it from PushInstanceIDListenerService everytime {the token is refreshed by GCM/FCM} AND {we have a webview alive}. A good choice here would be to decouple the token refresh from the registration event, so that registration and tokenRefresh are independent and consistent without being partial dupes of each other.
  2. Make the registration event trigger again everytime {the first run of the event during startup has already triggered} AND {the token is refreshed by GCM/FCM} AND {we have a webview alive}.
dankingston commented 8 years ago

The app registers successfully and receives the registrationId -> no error. When sending the message to GCM, the backend returns an error: "NotRegistered"

I also have this error since upgrading. The reason for my error is different though:

How do I send push notifications to this new style registration id?

macdonst commented 8 years ago

@dankingston that registration ID is in the Google Instance ID format which is what you should expect. Send a message to it in the regular way.

dankingston commented 8 years ago

Thanks @macdonst

Unfortunately sending a push notification using the Instance Id format gives me a NotRegistered error.

Solved: I was using the http API to send, with registration_ids=[...]. This does not work for instance ids. to=... should be used instead of regstration_ids=[...]. This means no multicasting.

https://developers.google.com/cloud-messaging/http-server-ref#send-downstream

macdonst commented 8 years ago

@dankingston no, that's not true at all. The registration tokens returned by this plugin can be used in multicast sending of push messages. For instance here is a small snippet of node code that works:

var gcm = require('node-gcm');
// Replace these with your own values.
var apiKey = "replace with API key";
var deviceIDs = [ id1, id2, id3 ];
// end replace
var service = new gcm.Sender(apiKey);
var message = new gcm.Message();
message.addData('title', 'Title');
message.addData('message', 'Message.');
service.send(message, { registrationTokens: deviceIDs }, function (err, response) {
    if(err) console.error(err);
    else    console.log(response);
});
josschne commented 8 years ago

I am seeing this as well. Nexus 6, running Android 6.0.1, stock firmware. Google Play Service 9.0.83 (430-121911109).

rdesimone commented 8 years ago

When this happens, only removing and reinstalling the app solves the issue. I could reproduce it on all our devices. Reading more I come to the conclusion that it is related to the intend. See also the second answer of this SO question: http://stackoverflow.com/questions/26718115/gcm-error-not-registered The whole thing is a little bit scary. A token refresh does not happen often but it can happen. Somehow this issue should be investigated further. If this happena also with a published app, iit is a serious problem.

samikha commented 8 years ago

I'm getting similar issues with a Nexus 6 as well. I'm using Crosswalk webview, and uninstall and reinstall doesn't help - you need the manually clear the data of the app before receiving push notifications again.

albertleao commented 8 years ago

Same issue here. All my tokens on Android are invalid. Error happening with crosswalk enabled and disabled

rdesimone commented 8 years ago

@macdonst we had again invalid token - "not registered "server response - on two Nexus 6p. It was also not possible to receive push notifications after deinstalling and reinstalling the APK. Finally I came to this thread https://github.com/phonegap/phonegap-plugin-push/issues/805.

Now in code I do

var push = PushNotification.init({...}) ;

and afterwards

push.unregister(function() {
    console.log('success unregister');
    -> call again init()
    }, function() {
    console.log('error unregister');
});

On success, .init() will be called again. Now the device receives push notifications.

Looking at the UNREGISTER code in PushPlugin.java, I noticed this line:

InstanceID.getInstance(getApplicationContext()).deleteInstanceID();

See also again this SO thread http://stackoverflow.com/questions/26718115/gcm-error-not-registered (second answer)

My impression is that this line is necessary to clean the environment. Maybe something similar should also be implemented in INIT. You have much more experience with push notification to judge.

In any case this issue has to be finally carefully investigated and closed.

rdesimone commented 8 years ago

Calling push.unregister() seems to make push work again after manual deinstallation and installation of an APK. But for production it cannot be used like mentioned above because already requested push notifications get lost after a new app start. So modifying INIT with InstanceID.getInstance(getApplicationContext()).deleteInstanceID(); is actually not a good idea. We need something different to be able to copy manually a release build for testing.

kukikiloke commented 8 years ago

Here is what is happening on my samsung devices (samsung s6 & s3): 1) Got the registrationId A, but got NotRegistered error when app server tried to push to device. 2) Clear the app data and relaunch the app. Got new token registrationId B. Push works. 3) Build the app again with cordova client, strangely the push plugin used the old token registrationId A.

Tried a couple of times, but after rebuild with cordova the registration ID recovered from SharedPreferences is always the old invalid registrationId A.

kukikiloke commented 8 years ago

Update:

For those you're still experiencing this problem when installing the app through Cordova CLI, check your cordova-android version.

I was using cordova-android 5.0.0 when I was encountering this issue, upgrading it to 5.1.1 solved the problem.

Fix for CB-9557 in 5.0.0 introduced this CB-10157 which will uninstall the app first before reinstalling the app. Version 5.1.1 fixed that.

BUT, the problem (mentioned in my previous comment above) is still there when manually uninstall the app and reinstall it through Cordova CLI.

DanielFreiburger commented 8 years ago

Same here. Got invalidated id for the last few hours. After clearing app data I received a new one and now everything works like a charm again.

ElNinjaGaiden commented 8 years ago

I'm having the same issue with an app that I released 3 months ago.

@rdesimone what do you mean with this??

already requested push notifications get lost after a new app start

I've not tried your solution yet but I guess if you force the app to generate a new token with that line, you will get a new (valid) token and if you store that new token and use it for future push notifications, it should works.

Of course, this is a workaround because the app won't be requesting the new token until the user starts it. That means if the user doesn't start the app lets say in a week or two, he won't get any push notifications during that period of time.

Actually I think that situation applies for all apparent solutions here. @DanielFreiburger did you try clearing the app data but NOT starting the app again?

I don't know if that's possible but the best scenario will be to find a solution not depending on starting the app. My users (as simple users) tell me "I don't open the app because I'm not getting any notification that tells me I need to open it". It's kind of silly to tell them "well... you need to open it in order to start getting notifications", specially if that "solution" doesn't work 100%.

rdesimone commented 8 years ago

@ElNinjaGaiden yes, when unregistering, a new token will be issued and the database (backend) must be updated with the new token. Anyhow I am not sure if the whole issue happens also in production. From the experiences I made it seems related only to development when copying an APK. To solve this issue we have a hidden unregister functionality. Tapping 4 times on a certain label allows to unregister. This functionality we use in development after copying an APK. Still I believe something is wrong - for sure the push plugin misses the JS event that a token change happened. This at some point must be implemented. I am also not sure if by default on app start an unregister should happen. But this would mean as written above that on every app start the token has to be checked and replaced. @macdonst has to investigate this profoundly.

elettrico commented 7 years ago

I'm experiencing the same issue with android 6 on motorola phone. Is there a way to be sure to have always the latest token?

shogunfighter commented 7 years ago

I encountered this issue. I tested on different android devices and found out that new phones (new android os) tend to save and cache the registrationid or token even if you uninstall-then-reinstall the app. If you uninstall-then-reinstall, then run your app, when registering for push notification, you will still receive the same cached registrationid or token wherein you should have gotten a new registrationid or token.

So the solution is simple, clear all the DATA and CACHE for that app.

How?

Here's what i did on my S7 Edge.

Settings > Applications > Your App > Storage > Clear Data and Clear Cache

Now run your app and check if you receive a new registrationid or token. Cheers!

photofroggy commented 6 years ago

Hi, is this issue being worked on? Seems I have this problem in a cordova App. Currently on version 1.8.0 of this plugin so could probably do with an update anyway, but it would be really nice if this were fixed when updating.

Otherwise, could you advise on what changes could be made manually in the meantime?