Skip to content

Instantly share code, notes, and snippets.

@sjones94549
Last active November 13, 2017 01:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sjones94549/dd192e6671e9c8759f34 to your computer and use it in GitHub Desktop.
Save sjones94549/dd192e6671e9c8759f34 to your computer and use it in GitHub Desktop.
How to handle multiple GCM senders in the same app

Multiple GCM Senders in the Same App

Google does support multiple GCM senders (Layer, etc.) for a single app, but you have to account for it. If not, one or more GCM sender will fail to send GCM intents, and GCM intents that do come in may randomly fail to reach GCM receivers in your manifest.

GCM Registration

GCM registration happens in two phases. First, the device supplies a Sender ID to Google, and Google gives the device back a Registration ID. The Registration ID is what the device sends to Layer so we can send GCM to that device. The trick is, each app can only have one current Registration ID assigned by Google at a time; previously assigned Registration IDs will no longer work. And, the Registration ID is based on the Sender ID supplied at registration time. So, if multiple senders (e.g. Layer and another service) register for GCM, one will register last and invalidate the other.

Option 1: Handling GCM Registration Yourself

The first method is documented:

	To make this possible, all you need to do is have each sender
	generate its own project number. Then include those IDs in
	the sender field, separated by commas, when requesting a
	registration.  Finally, share the registration ID with your
	partners, and they'll be able to send messages to your
	application using their own authentication keys.

	Note that there is limit of 100 multiple senders.

This approach requires your app to handle its own GCM registration, providing all GCM Sender IDs during that process, like so:

	Intent intent = new Intent(GCMConstants.INTENT_TO_GCM_REGISTRATION);
	intent.setPackage(GSF_PACKAGE);
	intent.putExtra(GCMConstants.EXTRA_APPLICATION_PENDING_INTENT,
	        PendingIntent.getBroadcast(context, 0, new Intent(), 0));
	String senderIds = "968350041068,652183961211";
	intent.putExtra(GCMConstants.EXTRA_SENDER, senderIds);
	context.startService(intent);

And then hand the resulting Registration ID to all senders (e.g. Layer and the other service). However, all sender libraries must accept a Registration ID (rather than a Sender ID) to support this method. Though Layer supports it through the LayerClient.setGcmRegistrationId() method, most libraries do not. So we've found an undocumented work-around.

Option 2: Supply Sender ID List to Libraries

The second (though undocumented) method is to supply a comma-separated list of Sender IDs to each library as their Sender ID, and allow each library to handle their own GCM registration as usual. So, if Layer has Sender ID "123456", and the other has "654321", you would supply both libraries with "123456,654321" as the Sender ID. It's important to supply the same concatenation to all libraries so they don't invalidate the others' Registration ID. This method works because Google hands back the same Registration ID for the same Sender ID, so although multiple libraries register GCM, they each supply the same Sender ID concatenation, and receive the same Registration ID back from Google.

GCM Receiver Priority

Once registration is sorted out, you may need to take one more step to ensure all GCM receivers play nicely together. GCM intents are "ordered" -- that is, they get handled by receivers one at a time, and each receiver can either consume the intent or pass it on to the next receiver. Layer does a good job handling its GCM intents and passing non-Layer intents down the chain, but not all libraries are good citizens in this way, and simply consume all intents. The workaround is to add a priority to each GCM receiver, and prioritize Layer above the offending receivers. For example:

    <receiver
        android:name="com.layer.sdk.services.GcmBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND">
        <intent-filter android:priority="470">
            <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
            <action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
            <action android:name="com.google.android.c2dm.intent.REGISTER"/>
            <category android:name="com.my.app"/>
        </intent-filter>
    </receiver>

    <receiver
        android:name="com.another.service.GcmBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND">
        <intent-filter android:priority="1">
            <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
            <action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
            <action android:name="com.google.android.c2dm.intent.REGISTER"/>
            <category android:name="com.my.app"/>
        </intent-filter>
    </receiver>

Note the android:priority="470" attribute in the Layer GCM receiver -- higher priorities get the GCM intents first. This will allow Layer to receive GCM intents, consume them if they are Layer intents, and pass them on down the priority chain if they are not Layer intents.

@mnr-moustafa
Copy link

Hello,

How can we achieve same behavior now with FCM?

Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment