Get Different GCM registrationId for Different servers - android

I am using GCM for triggering notification to my android app. notification are triggered from my server. for example suppose the user registers an account in app then server will be sending notifications for that I am passing GCM registration id in my registration request. till now it works perfectly. now I have one more API that is another server. and I am sending the same GCM Registration Id in that request, but I am not getting any notification from that server, so from some reading I have found that each registration_id is associated with a particular app and its corresponding server API key i.e. a single device has different registration_ids for different apps Thus, we got a MismatchSenderId error while trying to send push notifications using the registration_id. how do I resolve this ? I have googled and didn't find any proper solution.
I am generating GCM registration in this way :
String token = instanceID.getToken(defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

Since you have 2 different servers, I believe you have two different sender ID. So in your code, you must have 2 token, one for each server.
Sample:
Token for server 1:
String token = instanceID.getToken(<sender_id_1>),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Token for server 2:
String token2 = instanceID.getToken(<sender_id_2>),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);

There can be an issue in case when a token is updated:
in InstanceIDListenerService I see no way to identify token was updated - for sender_1 or for sender_2.
So it seems when onTokenRefresh is called we have to get new tokens for both senders.

Related

GCM notification results=[error=NotRegistered]

I am using Node GCM.
The notifications are working fine in certain devices of my customer. Some phones have this issue where they do not get the notifications after a certain time period.
After checking the logs I saw the below error in some calls.
{"multicast_id":7040074623564131000,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"NotRegistered"}]
I do not get canonical_ids as 1 or above, for this response or on any responses before it.
I followed the steps mentioned in Canonical Registration ID and message ID format and I did not get any canonical ids in the response.
In some phones, the GCM works just fine so I'm not sure how to proceed from here and where to debug.
NotRegistered - is a message, which say, that there is no any GCM device registered with passed id. Just check id.
You must send push notifications only on devices, which are registered in GCM and in your app (via developer console).
You can test it with different Rest clients like postman. Just send your data on special url using json.
This generally happen if your device token not registered properly may be due to storage length ..so always use text instead of varchar and if use varchar then give the size above 200 .. ..
Thanks
I spended some days try to figure out how to resolve a similar problem and when I tried something I thought stupid it worked. In Android, when generating the token, I changed the scope from "GCM" to "" and after that i didn't receive NotRegistered anymore.
Change:
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
To:
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
"", null);
I don't know why but it works to me.

Custom Google cloud Messaging token

I would like to create a Google Cloud Messaging app but the problem is I would like to create Registration Token per user not per application (so that different user on same devices receives specific message not app wide). I have google cloud messaging working but I cant seem to figure out how to generate Registration ID so i can send a message to specific user
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId ),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
If I am correct it generates here but how would I for example put user ID here (or where). Or did I misunderstood this entirely.
You can use the topic messaging functionality from GCM.
Your server can send a GCM message to /topics/{userId} and your app should subscribe to the users that are using that device. Your app will then receive the GCM message with the from field set to the topic and your app can take it from there.
You do something like this in your GCM registration service:
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId ),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
GcmPubSub pubSub = GcmPubSub.getInstance(this);
pubSub.subscribe(token, "/topics/user1", null);
pubSub.subscribe(token, "/topics/user2", null);
Note that you don't even need to send the GCM token to your backend service because it is never going to send GCM messages to particular devices, only to topics. Because your app subscribed to the corresponding topics it will receive those messages.
In your GCM onMessageReceived you just get the private message and the userId and store it somewhere.
public void onMessageReceived(String from, Bundle data) {
final int topicsLength = "/topics/".length();
String userId = from.substring(topicsLength);
String privateMessage = data.getString("privateMessage");
saveMessageSomewhere(userId, privateMessage);
}
Then when a user logs on your app they have their messages waiting for them.
Note that it is up to you to generate unique user IDs. You can have your app request one from your backend service, or you could just rely on UUID.randomUUID().
Update
Note that the GCM topic must conform to the specification: /topics/[a-zA-Z0-9-_.~%]+. I had mistakenly assumed path separators are allowed, but if you use something like /topics/users/user1 you will run into:
java.lang.IllegalArgumentException: Invalid topic name: /topics/users/user1
This is because of the path separator character in users/user1. GCM does not allow a hierarchy of topics such as you might have in a REST API.
I have updated the above sample code to avoid this mistake.
You cannot create gcm token yourself. It is generated per application and generated outside your app.
What you can do is to maintain intallation --> user & user-installation table in your database and send notification to all installed instances belonging to a particular user.

Correct use and update of GCM-Ids

I have a question about the correct use of GCM-IDs.
At the moment I have a InstanceIDListenerService a GcmListenerService and a RegestrationIntentService.
The RegeistrationIntentServie get started in the MainActivity every time someone opens the app.
I think this is a correct implementation of the Google guidelines.
But what is the best way to handle the GCM-ID so that I will not have incorrect GCM-IDs on my server after the refresh in the InstandeIDListenerService. Because at the moment the refresh only registers a new GCM-ID on my server.
Would it be an idea to generate a random Device ID so that I can update the old GCM-ID?
How do you handle the IDs?
Because at the moment I ask the server every start of the app if he knows a GCM-ID in combination with a (randomly generated) Device-ID and update one of both if the other one is incorrect or does not exist.
And I think that produces a lot of network traffic for nothing.
Just to be sure, you are not calling InstanceIDListenerService.onTokenRefresh() yourself right? This will be called by the system if necessary.
To answer your question, you should use the GCM functionality called "canonical IDs":
Canonical IDs
If a bug in the client app triggers multiple registrations for the
same device, it can be hard to reconcile state and the client app
might end up with duplicate messages.
Implementing canonical IDs can help you more easily recover from these
situations. A canonical registration ID is the registration token of
the last registration requested by the client app. This is the ID
that the server should use when sending messages to the device.
If you try to send a message using an old registration token, GCM will
process the request as usual, but it will include the canonical ID in
the registration_id field of the response. Make sure to replace the
registration token stored in your server with this canonical ID, as
eventually the old registration token will stop working.
To be more precise, if there are two registrations for the same device and you send a notification using the older registration ID, you will get the canonical ID (the registration ID of the newest registration for this device). If this ID is already stored on your server, delete the old registration. If the canonical ID is not stored on your server for any reason, replace the registration ID you used to send the notification with the canonical ID.
#leet GCM ID token initiates callback periodically when your token needs to be refresh, or sometime when:
- Security issues; like ssl or platform issues
- Device info is no longer valid; for example backup and restore.
- Instance ID service is otherwise affected.
public class MyInstanceIDService extends InstanceIDListenerService {
public void onTokenRefresh() {
refreshAllTokens();
}
private void refreshAllTokens() {
// assuming you have defined TokenList as
// some generalized store for your tokens
ArrayList<TokenList> tokenList = TokensList.get();
InstanceID iid = InstanceID.getInstance(this);
for(tokenItem : tokenList) {
tokenItem.token =
iid.getToken(tokenItem.authorizedEntity,tokenItem.scope,tokenItem.options);
// send this tokenItem.token to your server
}
}
};
Keeping the Registration State in
Sync
To protect the client app and app server from potential malicious
re-use of registration tokens, you should periodically initiate token
refresh from the server. When GCM registration token refresh is
initiated from server side, the client app must handle
a tokenRefreshed message with the GCM registration client/server
handshake. This link may help you verify on how
to handle Refresh Token.
As Baris have metioned, your server must use canonical IDs when you are sending message to the device. The tokenRefresh would remove the idea of generating a random device ID and Canonical IDs would solve on how you verify if the ID is correct or not.

How do I manage adding the Registration id in Multicast messaging service at server side of GCM?

I am started to exploring on GCM. Please clarify the following things.
By Registering with GCM server using the SenderID (Project ID get it from google API console), the Different device will giving unique registration id. I have used the following code to send message from server to all the devices by adding registration id as deviceid in the devicelist . I got the registration id of device by logging in the logcat.
Sender sender = new Sender("MY_API_KEY");
Message message = new Message.Builder().collapseKey("1")
.timeToLive(3)
.delayWhileIdle(true)
.addData("message",
"this text will be seen in notification bar!!")
.build();
MulticastResult result;
ArrayList<String> devicesList = new ArrayList<String>();
devicesList.add(deviceid1);
devicesList.add(deviceid2);
result = sender.send(message, devicesList, 1);
Is it right way to send message like above mentioned?
If that's the case how can design the code it will work after release the particular application? how can i get the registration id of all the devices after releasing?
Your server needs to keep track of all the registration ids from the devices. Therefore, you need a database table to store those registration ids. Then you need to expose a web service, or a HTTP POST entry point for your clients to upload their registration ids.
After a device successfully register with Google GCM server, you will receive the registration id in the onRegistered() callback method in GCMBaseIntentService. This is where you want to upload the registration id to your server.

Registration confusion Android GCM

I am trying to migrate to GCM in Android, C2DM now being deprecated. The registration process described here is different from registration described here. Are both registration same? Can we see code for GCMRegistrar to know for sure?
I've successfully migrated my C2DM project to GCM. Tested, it works fine. The only changes were:
in the Android app - change the value of sender upon registration
on the server side - change the auth header and the URL
That was it, as far as the interaction with Google goes. There were more some changes dictated by the app's logic:
in the Android app, the registration ID was cached in its preferences. Upon upgrade, I remove reg ID from the preferences to force re-registration, this time with GCM.
the logic of passing the reg ID to the server got an extra boolean parameter - if this is a C2DM or GCM reg ID
the logic of sending messages became conditional upon the said parameter.
Throwing out the C2DM logic completely out of the server would be unwise - not everyone upgrades their Android apps. The old, C2DM-enabled versions will be out in the wild for some time. And Google pledged to keep C2DM running in the short term. So message sending is conditional - depending on reg ID type, it sends either to GCM or to C2DM.
EDIT re: conditional logic:
if($RegID_Is_GCM)
{
$Auth = GCM_Auth();
$URL = $GCM_URL;
}
else
{
$Auth = C2DM_AUTH();
$URL = $C2DM_URL;
}
They are actually the same thing. The second one encapsulates the first one in a static method and registers a broadcast receiver. You can attach the source to the gcm.jar and see for yourself. You can find source code in ~/android-sdks/extras/google/gcm/gcm-client/gcm-src.jar
The Thing I like most in GCM is the RegID we will get from GCM server,it is not only an ID its an Address of this application on this Device. So this time you don't need to send a device Id to server along with your Registration Id as per was in C2DM.
In C2DM every time you request a registration id you will get a new ID.
But in GCM RegId generated by using your application package along with some device id so if you will request for Registration Id again and again you will receive the same RegId.
And if you uninstall an application and will install it again still GCM server will give you the same Registration Id.
So one Registraion Id will do no need of any Device Id to send to server.
I have been successful at migrating from C2DM to GCM. I have also documented how to implement GCM at
http://android.amolgupta.in/2012/07/google-cloud-messaging-gcm-tutorial.html
GCMRegistrar is just a helper that does the leg work described in the first page.
You can see the class here. android-sdk\extras\google\gcm\gcmclient\src\com\google\android\gcm.

Categories

Resources