Handling registration ID changes in Google Cloud Messaging on Android

56,194

Solution 1

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.

Solution 2

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

Solution 3

After scrubbing through tonnes of misleading answers across the net, including SO, the only place I found a complete answer was as remarked by Eran's answer and here:

While automatic registration refresh might or might never have happened, google describes a simiple algorithm to handle the canocical_ids by parsing successful response:

If the value of failure and canonical_ids is 0, it's not necessary to parse the remainder of the response. Otherwise, we recommend that you iterate through the results field and do the following for each object in that list:

If message_id is set, check for registration_id:
If registration_id is set, replace the original ID with the new value (canonical ID) in your server database. Note that the original ID is not part of the result, so you need to obtain it from the list of code>registration_ids passed in the request (using the same index).
Otherwise, get the value of error:
If it is Unavailable, you could retry to send it in another request.
If it is NotRegistered, you should remove the registration ID from your server database because the application was uninstalled from the device or it does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents.
Otherwise, there is something wrong in the registration ID passed in the request; it is probably a non-recoverable error that will also require removing the registration from the server database. See Interpreting an error response for all possible error values.

From aforementioned link.

Share:
56,194

Related videos on Youtube

Johann
Author by

Johann

Medium Articles Creating responsive layouts with Jetpack Compose https://johannblake.medium.com/creating-responsive-layouts-using-jetpack-compose-7746ba42666c Create Bitmaps From Jetpack Composables https://proandroiddev.com/create-bitmaps-from-jetpack-composables-bdb2c95db51 Navigation with Animated Transitions Using Jetpack Compose https://proandroiddev.com/navigation-with-animated-transitions-using-jetpack-compose-daeb00d4fb45 In-App Testing For Android https://proandroiddev.com/in-app-testing-for-android-6f762bb97387 Github https://github.com/JohannBlake ANDROID DEVELOPMENT 10 years of native Android development with Kotlin, Java &amp; Android Studio Design, develop, test and deploy Android applications Professionally looking UIs with Jetpack Compose &amp; Material Design Develop using continuous integration APIs: Google Cloud Messaging, Google Maps, Google Drive, Gmail, OAuth Frameworks &amp; Patterns: MVVM, RxJava, Dagger, Koin, Retrofit, Sqlite, Room, Realm, Crashlytics Communicate with web services via RESTful APIs Troubleshoot, optimize and performance tune Code versioning using Git and the Gitflow model Project management with Jira Agile development with Scrum WEB DEVELOPMENT 25 years of web development React, Material-UI Javascript, HTML5, CSS3, jQuery, ASP.NET, C#, Java, Servlets Microservices running on the Google Cloud Platform ELECTRON DEVELOPMENT 2 Years of Electron development Javascript, jQuery Node.js HTML5, CSS3 Published Apps Motel One https://play.google.com/store/apps/details?id=com.motelone.m NBC Sports https://play.google.com/store/apps/details?id=air.com.nbcuni.com.nbcsports.liveextra Telemundo Noticias https://play.google.com/store/apps/details?id=com.nbcuni.telemundo.noticiastelemundo TD Mobile Banking https://play.google.com/store/apps/details?id=com.td

Updated on July 05, 2022

Comments

  • Johann
    Johann almost 2 years

    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?

  • NickT
    NickT about 11 years
    I think that you are correct in your answer and that any changes in regid can be handled by the php or jsp responses in the project's app server. It's about time that Google updated their demo client/server code bundled with the SDK though!
  • Johann
    Johann about 11 years
    It appears that Google's docs are out-dated. The GCMBaseIntentService class, when implemented has onUnregistered that gets called. Whether that gets called when the registration ID gets refreshed isn't stated. The "Getting Started" section on GCM doesn't even mention GCMBaseIntentService but it's included in the sample code. Google really needs to clean up their docs.
  • Eran
    Eran about 11 years
    @AndroidDev Yes, their docs are a bit messy at the moment. However, they do state that there are multiple ways to implement GCM. Getting Started shows the new way, but it mentions (in Step 3) that there's another way, and links to it. GCMBaseIntentService is still mentioned in the Using the GCM Helper Libraries page.
  • Johann
    Johann about 11 years
    The Google Play services is suppose to be the new way of doing things, yet the GoogleCloudMessaging class doesn't have the same support as the GCMBaseIntentService class. I'd stay clear of the GoogleCloudMessaging class until Google provides at least the same amount of API support as the GCMBaseIntentService class. It seems that the GoogleCloudMessaging class was a quick way for Google to get users to switch to Google Play services but fails to address the issue I raised of handling expired registration IDs.
  • Eran
    Eran about 11 years
    @AndroidDev Google updated their GCM demo to use the new API. I updated my answer accordingly.
  • Pankaj Kumar
    Pankaj Kumar about 11 years
    @Eran I copied your last edit at stackoverflow.com/questions/17335572/… and I have given your credit there. :)
  • tony9099
    tony9099 almost 11 years
    @Eran in case the scenario of "google has refreshed the registration IDs" happened. How to do we get the new registration ID ? and How do we know it is changed in the first place (that is, we compare the current reg ID that is in the preferences with what) ?
  • Johann
    Johann almost 11 years
    It appears that Google isn't even faithful at keeping a 7 day period for IDs. I've seen them expire within hours. This is probably a result of some system maintenance or some bug on their servers. My advice is don't use any locally cached expiry date. Just use whatever ID you get until it fails and then request a new one. That's the only reliable way of using GCM.
  • Eran
    Eran almost 11 years
    @AndroidDev That's interesting. The only scenario in which I encountered changes in the registration ID so far involved uninstalling and re-installing the app.
  • Harshad Ranganathan
    Harshad Ranganathan about 10 years
    +1 for edit(04.24.2014) the old SO questions and answers misled and wasted my time ... wish everyone posted an edit to their old answers which are no more valid
  • Dzmitry Lazerka
    Dzmitry Lazerka almost 10 years
    I had to implement REGISTRATION receiver for Sony Xperia Ultra. My other phones and tablets work fine without it, but on Xperia gcm.register() always throws SERVICE_NOT_AVAILABLE, on the very same code.
  • Eran
    Eran about 9 years
    @An-droid What do you mean?
  • An-droid
    An-droid about 9 years
    I was saying it for @Dzmitry and his SERVICE_NOT_AVAILABLE
  • Admin
    Admin about 9 years
    Could you please let me know - how to register Android device with GCM? Please help me
  • Kowlown
    Kowlown almost 9 years
    We will need a edit for the GCM 3.0. developers.google.com/cloud-messaging/android/start
  • Eran
    Eran almost 9 years
    @Kowlown Yeah, I read about that. Unfortunately, I currently don't have time to test the new registration code in order to post an updated answer.