FrancescoC
FrancescoC

Reputation: 1058

Issues with GCM Push Notifications

for my first question on StackOverflow I'm gonna ask about Google Cloud Messaging service, and in particular Loopback's implementation.

So, I'm developing an app and started to work on a different branch to introduce Loopback's push notification handling and it's various tools for REST Api. Even if this topic is gonna cover strictly Loopback's way to handle GCM, the question is also related to the original way as is described on Google docs.

So, the main idea behind GCM's kick-off is to check whether the device is registered or not.

This is done by a simple check on a SharedPreferences variable, a name used to store our RegistrationID value.

final LocalInstallation installation = new LocalInstallation(context, adapter);

If this is found, the device has to notify the server, communicating the token. Else, a registration to GCM has to be done. Once this is done, the device notifies the server. ( registerInBackground(installation) will eventually call saveInstallation(installation) after retrieving RegistrationId )

    if (installation.getDeviceToken() != null) {
        saveInstallation(installation);
    } else {
        registerInBackground(installation);
    }

If communication is successful, the device saves RegistrationId using SharedPreferences as described above. (NOTE : getDeviceToken() is Loopback's way to handle via API the value in SharedPreferences)

Let's say this "GCM-Check" is being done every time my MainActivity is being created (so, during the onCreate method).

We also know GCM is sometimes messy, and wants to refresh my app's RegistrationId or some other stuff that, to be honest, is not completely clear to me right now. In short terms, GCM invalidates the token of my app. This causes an error-message when the server send a push-notification using the Token bound to my device-app.

An error similar to

{"multicast_id":0123456789012345678,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"NotRegistered"}]}

You can see, "failure":1 and "results":[{"error":"NotRegistered"}]

Loopback reacts just as Google docs say, by having the server remove the record of the device linked to the faulty RegistrationId . Comprehensible.

Back to our device. Launching my app again and loading MainActivity, causes the same "GCM-check" procedure. This time the app can find RegistrationId using SharedPreferences, and can directly notify the server, which creates a record with the given RegistrationId.

No new registration is being handled by the device-app.

You can see the loop in here. Device will have no knowledge of it's token invalidity and will continue to tell the server the same data, which will keep sending information to the wrong registrationId, thus removing it after receiving the related error.

The problem is that the app has to rely on data which is created once and never gets modified. To remove the old data I should send a notification to the device, which is obviously not possible as I can't reach it from GCM. Other solutions possible is notify the user by sending an email or sms, and ask him to click a button for example, but I'd rather have a more "automated" approach to the problem.


A VERY BAD SOLUTION I'VE FOUND

As to my knowledge the only error-info is returned from GCM to the server during a push-notification, I've made a little hack on the device.

The idea is simple: create a POST request to GCM Servers, using the headers my server should use to authenticate. This causes the error to be given to the device itself, which can parse the JSON and notice what happened. From here the app can forge a new registration process, fixing the issue.

What is bad about this? The fact that to authenticate the device as the server, I have to hard-code the ServerKey and distribute it in every app. The ServerKey should be used only on the server, making this solution a very bad idea.


This being said, the idea of simply letting the device know its state using a SharedPreference value is not so great, as it would only tell me if I ever registered at least once, without letting me know my current status.

On other apps I've developed which use GCM just as well, I've solved in different ways, like having a Button to be clicked by the user or reading some specials SMS send by the server, which then enable GoogleCloudMessaging.unregister() at first and eventually GoogleCloudMessaging.register()


So, asking for forgiveness for such a wall-of-text, how have you solved this NotRegistered thing?

Thanks for your effort and time in reading and, hopefully, in answering : )

Upvotes: 3

Views: 1790

Answers (1)

Cruceo
Cruceo

Reputation: 6824

As an addendum to my comments, since it helped and I have more space here:

Instead of just checking whether or not the token exists inside your SharedPreferences, you should also check to see if the version of your app that token is for matches the version that is currently running the check (that was a suggestion from Google's docs).

If the device version do not match, you should request a valid token (which could actually be the same, but is not guaranteed). Now, you may also want to check for the PACKAGE_REPLACED broadcast (in case you're not incrementing the version in your manifest each time you install for tests, which I'm very guilty of) which, if triggered, should also force you to request a new token.

As for why there's sometimes a change in the token and sometimes not: I couldn't agree more that it's totally sporadic and can't help feeling like there's something going on we don't really know about.

Sometimes the new request after PACKAGE_REPLACED returns the same key; sometimes it doesn't. What happens in between those that makes it weird? God, I would LOVE to know, and I wish I had more info on it. But that's part of why I mentioned trying to catch that broadcast: forcing the check when it happens should ensure that you're always getting a new valid one (in the event that the version check passes when it shouldn't) if it's available.

Hopefully this helps~

Upvotes: 1

Related Questions