Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling registration ID changes in Google Cloud Messaging on Android

In the docs on Google Cloud Messaging, it states:

The Android application should store this ID for later use (for instance, to check on onCreate() if it is already registered). Note that Google may periodically refresh the registration ID, so you should design your Android application with the understanding that the com.google.android.c2dm.intent.REGISTRATION intent may be called multiple times. Your Android application needs to be able to respond accordingly.

I register my device using the following code:

GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(context); String regID = gcm.register(senderID); 

The GoogleCloudMessaging class encapsulates the registration process. So how am I suppose to handle com.google.android.c2dm.intent.REGISTRATION since handling that is done internally by the GoogleCloudMessaging class?

like image 470
Johann Avatar asked May 30 '13 14:05

Johann


People also ask

How does FCM work on Android?

The Android push notifications through FCM actually treats the Data Messages as notification messages itself. As the interactions in the data messages are handled by the app itself, FCM's work is just to deliver a notification and the message content.


2 Answers

That's an interesting question.

Google encourage you to switch to the new registration process :

An Android application running on a mobile device registers to receive messages by calling the GoogleCloudMessaging method register(senderID...). This method registers the application for GCM and returns the registration ID. This streamlined approach replaces the previous GCM registration process.

The note that says Google may periodically refresh the registration ID only appears on the page that still shows the old registration process, so it's possible that this note is no longer relevant.

If you want to be safe, you can still use the old registration process. Or you can use the new process, but have in addition the code that handles the com.google.android.c2dm.intent.REGISTRATION intent, in order to make sure you are covered if Google do decide to refresh the registration ID.

That said, I never experienced such a refresh, and even when I did experience a change in the registration ID (usually as a result of sending a notification after un-installing the app and then re-installing it), the old registration ID still worked (resulting in a canonical registration ID sent in the response from Google), so no harm was done.

EDIT (06.06.2013) :

Google changed their Demo App to use the new interface. They refresh the registration ID by setting an expiration date on the value persisted locally by the app. When the app starts, they load their locally stored registration id. If it is "expired" (which in the demo means it was received from GCM over 7 days ago), they call gcm.register(senderID) again.

This doesn't handle the hypothetical scenario in which a registration ID is refreshed by Google for an app that hasn't been launched for a long time. In that case, the app won't be aware of the change, and neither will the 3rd party server.

public void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);      setContentView(R.layout.main);     mDisplay = (TextView) findViewById(R.id.display);      context = getApplicationContext();     regid = getRegistrationId(context);      if (regid.length() == 0) {         registerBackground();     }     gcm = GoogleCloudMessaging.getInstance(this); }  /**  * Gets the current registration id for application on GCM service.  * <p>  * If result is empty, the registration has failed.  *  * @return registration id, or empty string if the registration is not  *         complete.  */ private String getRegistrationId(Context context) {     final SharedPreferences prefs = getGCMPreferences(context);     String registrationId = prefs.getString(PROPERTY_REG_ID, "");     if (registrationId.length() == 0) {         Log.v(TAG, "Registration not found.");         return "";     }     // check if app was updated; if so, it must clear registration id to     // avoid a race condition if GCM sends a message     int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);     int currentVersion = getAppVersion(context);     if (registeredVersion != currentVersion || isRegistrationExpired()) {         Log.v(TAG, "App version changed or registration expired.");         return "";     }     return registrationId; }  /**  * Checks if the registration has expired.  *  * <p>To avoid the scenario where the device sends the registration to the  * server but the server loses it, the app developer may choose to re-register  * after REGISTRATION_EXPIRY_TIME_MS.  *  * @return true if the registration has expired.  */ private boolean isRegistrationExpired() {     final SharedPreferences prefs = getGCMPreferences(context);     // checks if the information is not stale     long expirationTime =             prefs.getLong(PROPERTY_ON_SERVER_EXPIRATION_TIME, -1);     return System.currentTimeMillis() > expirationTime; } 

EDIT (08.14.2013) :

Google changed their Demo App again (two days ago). This time they removed the logic that considers the Registration ID to be expired after 7 days. Now they only refresh the Registration ID when a new version of the app it installed.

EDIT (04.24.2014) :

For the sake of completeness, here are the words of Costin Manolache (taken from here), a Google developer involved in the development of GCM, on the matter :

The 'periodical' refresh never happened, and the registration refresh is not included in the new GCM library.

The only known cause for registration ID change is the old bug of apps getting unregistered automatically if they receive a message while getting upgraded. Until this bug is fixed apps still need to call register() after upgrade, and so far the registration ID may change in this case. Calling unregister() explicitly usually changes the registration ID too.

The suggestion/workaround is to generate your own random identifier, saved as a shared preference for example. On each app upgrade you can upload the identifier and the potentially new registration ID. This may also help tracking and debugging the upgrade and registration changes on server side.

This explains the current implementation of the official GCM Demo application. com.google.android.c2dm.intent.REGISTRATION should never be handled when using the GoogleCloudMessaging class to register.

like image 146
Eran Avatar answered Oct 20 '22 08:10

Eran


Reading the new InstanceID API, I found more info on when the token might change:

Your app can request tokens from the Instance ID service as needed using the getToken() method, and like InstanceID, your app can also store tokens on your own server. All tokens issued to your app belong to the app's InstanceID.

Tokens are unique and secure, but your app or the Instance ID service may need to refresh tokens in the event of a security issue or when a user uninstalls and reinstalls your app during device restoration. Your app must implement a listener to respond to token refresh requests from the Instance ID service.

More details:

The Instance ID service initiates callbacks periodically (for example, every 6 months), requesting that your app refreshes its tokens. It may also initiate callbacks when:

  • There are security issues; for example, SSL or platform issues.
  • Device information is no longer valid; for example, backup and restore.
  • The Instance ID service is otherwise affected.

Sources:

https://developers.google.com/instance-id/

https://developers.google.com/instance-id/guides/android-implementation

like image 43
marius bardan Avatar answered Oct 20 '22 09:10

marius bardan