Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unregistering and re-registering for GCM messages causes two regId's to be valid. Is this as intended?

I have noticed some strange behaviour when doing registering/unregistering for GCM messages on an Android devices. Observe the following use case from the perspective of the client device:

  1. Register for GCM -- ID A assigned
  2. Unregister
  3. Register for GCM -- ID B assigned

If, after step 2, the server attempts to send a message to ID A, it will receive a NotRegistered error, as documented and expected.

But now the strange part: After step 3, both ID A and B are valid IDs! Both IDs will trigger the Intent receiver on the device, resulting in two messages to the app.

Is this behaviour as expeced or am I doing something wrong?

This is my code to register and unregister, triggered from onCreate() on the first activity launching on my app:

 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     unregister(getApplicationContext());
     register(getApplicationContext());
}


/** Registers this device for GCM messages */
public static void register(Context context) {
    GCMRegistrar.checkDevice(context);
    GCMRegistrar.checkManifest(context);
    String regId = GCMRegistrar.getRegistrationId(context);
    if (regId.equals("")) {
        GCMRegistrar.register(context, SENDER_ID);
    } else {
        storeRegId(regId); // Also notifies back-end
    }
}

public void unregister(Context context) {
    GCMRegistrar.unregister(context);
}

Note 1: I've only included the unregister()-call for debugging purposes. My app usually stays registered for "for life" (I also want to receive GCM messages while suspended and terminated), but I still want to figure out the cause of this behaviour as I'm not sure if unregistering is the only case where the IDs are regenerated. What if the user uninstalls and reinstalls the app? I want a bullet proof system - my users shall never receive the same GCM message twice.

Note 2: The problem is pretty similar to this, except that I am indeed registering with getApplicationContext() as the answer suggests.

like image 513
Nilzor Avatar asked May 01 '13 14:05

Nilzor


2 Answers

  1. When you un-register, you should send the old registration ID to your server and remove it from your DB.

  2. Assuming you failed to do step 1, if after registering and getting the new registration ID you send a message with the old registration ID, the response from Google will contain a Canonical Registration ID (which is the new registration ID). This response indicates that your server should delete the old registration ID and use only the new one.

like image 184
Eran Avatar answered Oct 01 '22 04:10

Eran


I had a similar problem, and believe I have pieced together what is happening and have worked out a hybrid solution. Unfortunately, it's still not bulletproof, though it is working for my case (more about that later).

Here's what I believe you are seeing:

  • Case 1: Your 3rd-party server posts a GCM push message after your step 2 but before step 3 (i.e. the case where your server has sent a message for device ID-A and received NotRegistered). According to Google's documentation, your GCM device client has reported that there is no broadcast receiver configured for the message (since it's currently uninstalled). This has the effect of unregistering ID-A from the GCM service, as you expected.

    Here's a SO post that describes the GCM unregistration process for this case.

  • Case 2: Your app has been uninstalled then reinstalled on the device without a GCM push message in the interim. In this case, a new GCM ID is assigned, but the GCM service is never alerted to the lack of a broadcast receiver, so both IDs remain valid, and they are both deliverable to the device. This is reported as a duplicate registration_id by the GCM service.

In both cases, the GCM service is reporting the problem, and cleanup can be handled after-the-fact.

See this SO post for a very good explanation of the GCM service feedback.

Unfortunately, after-the-fact could be too late if you wish to ensure exactly one delivery of your message. After some searching, I think I have to conclude that one cannot completely prevent this with certainty, but you can do a bit better. I suggest you also manage the unregistration on the 3rd-party server rather than expecting the GCM service to exclusively handle it. I do this by ensuring that each new device sends/saves a unique device id along with the ID assigned by GCM (i.e. the pair ). Then, if the same device is ever assigned a new ID for any reason (i.e. ), the 3rd-party server can recognize the case and stop using ID-A -- rather than waiting for the GCM service to alert you to the duplication after a duplicate delivery.

Now, why don't I particularly like that solution? Several reasons:

  1. For instances that are already out in the wild, there is no migration path -- we have to assume those ids may remain valid until we can discover otherwise. I cannot force them to re-register their GCM ids with my 3rd-party server except through an app update (which I cannot force).
  2. For those devices that do go through an app update, they can transmit their deviceId and current GCM ID to my 3rd-party server, but they don't know any ids that might have been lost thru uninstall/re-install cycles. It's exactly those "lost" ids that may cause trouble in the future -- but only once if you use the service feedback described in that SO link above.
  3. Determining a unique deviceId is, itself, problematic. Just google for StackOverflow discussions about unique Android device ids to see...
like image 43
JCicchi Avatar answered Oct 01 '22 03:10

JCicchi