this is a common situation that lot's of apps facing, but I'm straggling to understand how to implement:
let's say that my application is a social network with a current logged in user.
I'd like to send GCM messages to that user, for all of his devices that currently logged in with my app.
that's mean that my server holds for each user a list of all of his registration ID's - one for each of his devices.
the problem: how can I track uniquely each one of his devices? seems there is no reliable way to get specific device identifier
without storing registration id for each unique device - I don't see how can I manage it.
things get messy when user will uninstall/logout and acquire new registration id afterwards, that suppose to replace one of the existing known id's, but which one of them??
things will get even more problematic if Id like to send message only to a specific device, and not all of them...
please help me understand what I missing, and what is the right way handling multiple devices registration id's for same user.
Google handles pretty much all the hard work for you when working with GCM. And they provided a simple method to always keep the registration id up to date. There is an extra field on each message sent called canonicalRegistrationId. If there is an id in that field, than the registration id has changed and needs to be updated. This field exists on every message and every time you send one, you have to check that field. If there is a new canonicalRegistrationId then you should update the registrationId as soon as possible. The old one may continue to work for some time, but there is no telling when it becomes invalid.
For example in a Google App Engine backend the code which handles the changing registration ids would look something like this:
// We want to send a message to some devices
// Get registered devices and then loop through them
List<RegistrationRecord> records = ofy().load().type(RegistrationRecord.class).limit(10).list();
for(RegistrationRecord record : records) {
// Send the message to one device
Result result = sender.send(msg, record.getRegId(), 5);
// If the messageId is not null, then the message has been sent successfully
if (result.getMessageId() != null) {
log.info("Message sent to " + record.getRegId());
// Check for canonical message id
// If it is not null, then the registrationId has changed
String canonicalRegId = result.getCanonicalRegistrationId();
if (canonicalRegId != null) {
// The registrationId has changed! We need to update it in the database
log.info("Registration Id changed for " + record.getRegId() + " updating to " + canonicalRegId);
record.setRegId(canonicalRegId);
ofy().save().entity(record).now();
}
} else {
... // Irrelevant error handling
}
}
So what you have to do is pretty simple: Every time you send a message, check if there is a canonicalRegistrationId, if yes then update the old registrationId with the canonical one.
Google supports device group messaging. Refer to this link: https://developers.google.com/cloud-messaging/notifications#managing_device_groups
To handle multiple users you can create a group for that user and add the registration id to that group. To create a group send a request like the following to https://android.googleapis.com/gcm/notification:
Content-Type:application/json Authorization:key=API_KEY project_id:SENDER_ID
{ "operation": "create", "notification_key_name": "appUser-Chris", "registration_ids": ["4", "8", "15", "16", "23", "42"] }
This API returns a notification key in response which you can store in your db and later use to send notifications to that group. Subsequently whenever user logs in to some other device you can get the reg_id of that device and add it to the group. To add a device with the registration ID 51 to appUser-Chris, you would send this request:
{ "operation": "add", "notification_key_name": "appUser-Chris", "notification_key": "aUniqueKey", "registration_ids": ["51"] }
And when user logs out of the device you can remove the reg_id from the group.
Sending a notification to a group is same as sending notification to a registration id.
One issue I have observed here is if you fail to store the notification_key to your db while creating a group there is no way you can get it again. And a new group cannot be created for that same notification_key_name again. A workaround to this problem is choosing your notification_key_name wisely. So when you fail to store the notification_key and try again to create a group with the same noitification_key_name, you will get an error. In that case you can simply change the notification_key_name for that user and create a new group. Hope this helps.
i think you have to get Device IMEI no and save it to your server with Registration id and when user ReRegister him self with other device then get Check IMEI Number Replace it with old one. Main thing Rise now in old device check send IMEI no with message and IF IMEI no is Match then no issue otherwise block application.and inform user...
Thats it...
Related
I am trying to implement Firebase cloud messaging in my Android app through a Node.js server and I have got stuck at a usecase.
I saw the Firebase tutorial of creating a device group using registration tokens to send messages/notifications to all devices with the same user logged in, what I don't understand is what happens when one of the registration tokens get refreshed by onTokenRefresh() method.
How will I distinguish which token to change as all will be belonging to the same user?
Update:
Ok, so now I have got stucked on another blocking use case. I am creating a user group identified by the user id from my server. If user uninstalls and reinstalls the app immediately and another user logs in on the device, if I call a gcm message on the previous user group this device still receives it.
Is there any way for the gcm to identify is the device it is sending the notification to is logged in or not and if it is, is it logged in with the same user as for the group?
There is another way to solve this problem using Cloud Firebase Functions.
How will I distinguish which token to change as all will be belonging
to the same user?
Using Firebase Functions, you don't have to. Within onTokenRefresh(), you send the new token to the server.
For Example:
The user has 3 devices, each of which have a token that has been sent to server.
*** deviceTokenA/B/C represent UIDs of the token ... we do not know what they are, or which device they belong to.
UserId:
Device Tokens:
deviceTokenA: true,
deviceTokenB: true,
deviceTokenC: true,
Now, the User is on the device that triggered deviceTokenA. The token is refreshed, and onTokenRefresh() is called, sending the token to that collection.
onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
sendTokenToServer(refreshedToken);
}
sendTokenToServer(String refreshedToken) {
// send to Firebase or Firestore Database, put in the Device_Tokens collection. }
Now, you will have 4 tokens in the collection.
UserId:
Device Tokens:
deviceTokenA: true, // this one has been "unregistered"
deviceTokenB: true,
deviceTokenC: true,
deviceTokenD: true, // this one has been registered.
The deviceTokenA no longer applies, because it was refreshed, and is not attached to an actual device anymore.
When looking at the device Tokens, we still don't know which ones are good, which are bad, and which tokens belong to which device. That's ok!
So, then create a forEach loop, getting each Token, and then send an FCM to each of these Tokens, FCM can let us know which tokens were sent successfully. One of them will return an error. If it returns an error saying the token was bad, we can then catch the error and delete that token, so it will not be called again.
// outside for Each loop
var promiseHolder = [];
// create a forEach loop, iterating through the collection of deviceTokens
// within that loop, put:
let innerPromise = admin.messaging().send(message)
.then(response => {
console.log('notification sent success: ' + response);
})
.catch((error) => {
console.log('Error sending notification: ' + error);
// if the error == bad token message, then Delete the Token.
if (error == 'Error: Requested entity was not found.') {
console.log('you matched the error, token doesn't work, handle here.');
//delete the old token
return admin.firestore()doc(`users/${userID}/device_tokens/${token_id}`).delete();
}
}
// still within forEach loop
promiseHolder.push(innerPromise);
// end the forEach Loop, and outside forEachLoop put:
return Promise.all(promiseHolder);
So I've been thinking about how to go with this scenario. First off, let's put in the instances when onRefreshToken() is called:
This will not be called very frequently, it is needed for key rotation and to handle Instance ID changes due to:
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
Guess with that, you can say that 'onTokenRefresh()` will be called after one the above happens and if the device is online (of course it has to be online on order to get a new token). So I guess here's how I'd go on the scenario:
First off, upon registration, I would save the registration token and pair it along another identifier, let's say a deviceId (since we're in a scenario for a user with multiple devices) in my App Server.
So assume I add in 3 registration tokens, those are also paired with their deviceIds. I add them all to a device group.
Now say one of the devices triggers the onTokenRefresh(), I would immediately send a delete request to my App Server for the registration token that is currently paired to that deviceId (you should also delete it in any device group(s) it's connected to), replacing it with the new one, then re-add it to the corresponding device group(s).
That's the simplest way I can think of. The key here is for you to pair the registration tokens with another identifier and use it to find which registration token you need to replace.
at moment i use this method. in my database i create a node with devices id
deviceId: {
uid1: deviceId,
uid2: deviceId,
uid3: deviceId
}
another node with the users that are subscribed to receive a notifications
newsSubscriber: {
uid1: [array of subscribers],
uid2: [array of subscribers],
uid3: [array of subscribers]
}
when i can send a notification by my nodejs script i get all users that are saved in the newsSubscriber node, and for any user i get his deviceId and i put it into an array of devices to send a notification.
There are only one problem, i read now that in this mode there are a limit of only 20 devices!.
but i think that this is a good easy method to have a corresponding deviceId for any user because when i use the logout or the login in my app i can change the correspondent deviceId for any user, so as to have consistency between user and device.
what happen if to bypass the limit of 20 devices i send the same notification to different groups of 20 devices?
I want to implement GCM in my android app, and i have used this code for my server side implementation.
https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/GcmEndpoints
And the implementation is working fine, and my app is able to receive the messages sent.
I installed my app and got a GCM ID, registered it with my server,and i uninstalled it and again installed it and got a new GCM id this time.
So now i send GCM messages and as a result this code(Canonical IDs), which updates GCM ID in data store if it receives getCanonicalRegistrationId from the result.
Result result = sender.send(msg, record.getRegId(), 5);
if (result.getMessageId() != null) {
log.info("Message sent to " + record.getRegId());
String canonicalRegId = result.getCanonicalRegistrationId();
if (canonicalRegId != null) {
// if the regId changed, we have to update the datastore
log.info("Registration Id changed for " + record.getRegId() + " updating to " + canonicalRegId);
record.setRegId(canonicalRegId);
ofy().save().entity(record).now();
}
But in app-engine data store i am able to see 2 rows with same GCM ID, as after response it updates the record.
This is the image of database before sending any message, and 2 different GCM id are there of same device(as app was installed twice)
This is the image of database after sending messages, and 2 same GCM id are there (GCM ID field has equal values)
So the question is, Is this a problem, when i send messages after i have multiple record with same GCM id, as i am receiving multiple messages in my app.And how should i try to correct it.
And when i uninstall the app, and after that i get these logs in app engine.
That the GCM ID is not present deleting it from data store, and this call is also executed multiple times.
Registration Id APA91bGn_GgXcp4L_bqwOMhmO9BRnXfjCAQgso-EbmC27L1UoNcdcPmtt5rxVMXKtW7P3_fzUNGmyMXtKBjz22iCVLiYyKPwp3uGrd8BxPlXhfQeIqYHKfyimciVXLuXdsGFvzIlIMrf no longer registered with GCM, removing from datastore
So as a result of sending multiple messages to same device, will i be using my free quota of GCM from app engine quickly?
I added androidId field in my table, so while registering i check, if androidId is present in the table i update the gcm id for that row, in this way i have only one row in my table even if app was installed multiple times on same device
I have app that allows user to login with credentials issued by the server, lets say simple login with password and usename.
So user starts the app, enter username and password, server authentificates the user, save this information. (details are really don't play any role in this case).
What I need to implement ?
There some events when my server need to notify user. Lets consider order status changed. If so I need to notify user about this, with notification in the status bar.
I have decided to use GCM for this task.
I read about this, and got some questions.
When user gets registration ID from GCM service in android, is it constant or it can be changed. As I understand there is no guarantee that it be always the same. So how can I handle refreshes of this ID.
Or if I will not call GoogleCloudMessaging.getInstance(applicationContext).registerit will stay the same until I register new or even if I call register method it can return the same ID. May I save this ID in SharedPreferences for example to use it for a long time ? Or it can be become invalid after some period of time ?
The main problem is that I need to handle multiple users on the same device. As far as I know, registration id issued by gcm service it tied to the device and app. So if new user logged out, and logged in with new credentials (new user account) how can I handle this ? I can for example store this ID in my database on the server and use it until user logout from the account inside my app, and after that just remove it from the database, and if user on this device will login with another account registration ID can be used to notify another user ? Will it work ? Or I have missed something.
How can I handle user multiple device. My group notifaction key will be some unique data from user account (email,username..). If I understand correctly, I need to get registration ID on all devices where user logins with its my server account and map this to one notification key. Should I handle notification dismiss by myself and than send upstream message to the GCM server in order to notify other user devices that notification has been dismissed ?
Please help with understanding of this service.
Thanks in advance.
I have implemented this in one of our app. In this we send the notification to users whenever new listing get added.
User can login from multiple devices same time. Device may be android or iOS anything.
Also multiple users can login from same device (After logging out previous users).
Whenever user log in from Android device will get the registration ID from GCM. This GCM id is not constant for one device, it gets changed whenever you again request it from GCM server.
We send the notifications to multiple devices using notification key, it contains GCM register ids of all devices. If you lose a notification key, you can’t get it back. To make it worse, You can’t regenerate a new notification key with a previously-used notification key name.
Never use unique data from user account (email, username..) as notification key name. Always generate some random string as notification key name.
To overcome this follow following steps:
Create one table to store the following details
Columns :
id
userId
deviceGCMId
deviceId
notificationKey
notificationKeyName
whenever user logs in sent the following parameters to the server.
GCMId, deviceId, userId
First check that userId and deviceId exists or not.
If not exists then go to step 5 else go to step 6
From given userId get the notificationKey and notificationKeyName from table. If not found then generate new notificationKeyName and from that key generate new notificationKey. Use current GCMRegId to generate the key. Store the keys in variables.
Add new entry in table with notificationKey and keyname generated in step 4. Also add the GCM id under notification key.
Check the deviceId is same and GCM id is different then add update the GCM id in table and add that GCM id under notification key. If device id and GCM id same then no need to do anything
Sometimes notification key get discarded from GCM server and it shows the key not found error. In this case create the new notificationKey from new key name and add GCM ids against that key.
For more info go through following useful links
https://medium.com/appunite-edu-collection/notifications-d7df385b0ff4
https://medium.com/#Bicx/adventures-in-android-user-notifications-e6568871d9be
Hope this will help
I am seeing this issue on my push notifications server - different Android devices (identified by their IMEI) receive the SAME registration id from Google's GCM service.
Isn't the registration id supposed to be unique? at east for the same application or GCM API Key?
I saw this thread but there doesn't seem to be an answer there:
Can two different devices have same GCM Registration ID?
Any help would be much appreciated
EDIT
here is the relevant code for registration:
Intent intent = new Intent(
"com.google.android.c2dm.intent.REGISTER");
intent.setPackage("com.google.android.gsf");
intent.putExtra("app",
PendingIntent.getBroadcast(context, 0, new Intent(), 0));
intent.putExtra("sender", flatSenderIds);
context.startService(intent);
The only idea that comes to my mind is that this deprecated method will assign the same ID on different devices if the extra value for sender is the same on all of them. That differs from the current version where you don't say who you are, you don't even send an Intent, you just call register() and Google determines who you are and what ID you should be given.
If you finally use this new library, there's an example snippet of how to register (inside an AsyncTask or a Thread):
GoogleCloudMessaging gcm = null;
try {
// Will be for the first time
if (gcm == null)
gcm = GoogleCloudMessaging.getInstance(your_context);
// Registration against the GCM service
String regid = gcm.register(YOUR_SENDER_ID_OBTAINED_FROM_YOUR_PROJECT);
// You'll need to send the registration info to your remote server
registerRemoteServer(regid);
Log.d("MyGCM", "Registered on GCM as " + regid);
}
catch (final IOException ex) { ex.printStackTrace(); }
Sometimes Google changes the registration ID and you'll have multiple IDs associated. The server that sends the notification (your server) has to update the database with the new ID.
For more info check this document:
http://developer.android.com/google/gcm/adv.html
they says:
It's an Canonical IDs
On the server side, as long as the application is behaving well,
everything should work normally. However, if a bug in the application
triggers multiple registrations for the same device, it can be hard to
reconcile state and you might end up with duplicate messages.
GCM provides a facility called "canonical registration IDs" to easily
recover from these situations. A canonical registration ID is defined
to be the ID of the last registration requested by your application.
This is the ID that the server should use when sending messages to the
device.
If later on you try to send a message using a different registration
ID, GCM will process the request as usual, but it will include the
canonical registration ID in the registration_id field of the
response. Make sure to replace the registration ID stored in your
server with this canonical ID, as eventually the ID you're using will
stop working.
Well, after adding your registration code, I can see you are using the old method of registration. That method has been depecated since the middle of last year. It is likely to be less reliable than the new registration method.
You should try registering via the GoogleCloudMessaging.register method of the Google Play Services library. That's the recommended way by Google. See the official demo here.
I have configured GCM in my application. I want to unregister device from GCM whenever user will uninstall application.
I got the code as
Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER");
unregIntent.putExtra("app", PendingIntent.getBroadcast(this, 0, new Intent(), 0));
startService(unregIntent);
but where we have to put this code..?
Thank You.
You cannot call unregister from GCM while uninstalling, because there is no method called while user uninstalls the application.
when you send a push notification, GCM will check if the user has your application, if the user has uninstalled the application GCM will note the same and inform you as part of reply for the push.
You have to check this on your server. You cannot do it from the application code since there is no way of knowing when the user is uninstalling the application.
See: Implement Canonical IDs.
Reference: https://developers.google.com/cloud-messaging/http#request
A canonical registration ID is defined to be the ID of the last registration requested by your application. This is the ID that the server should use when sending messages to the device.
If later on you try to send a message using a different registration ID, GCM will process the request as usual, but it will include the canonical registration ID in the registration_id field of the response. Make sure to replace the registration ID stored in your server with this canonical ID, as eventually the ID you're using will stop working.
Reference: https://stuff.mit.edu/afs/sipb/project/android/docs/google/gcm/adv.html#canonical
If the Canonical ID is not 0, then you have a duplicate registration.
Say for instance, you have 2 registrations in your database:
registration_A
registration_B
When you send a push notification, your server will get respond with something that looks like this:
{"multicast_id":########,"success":1,"failure":0,"canonical_ids":1,"results":
[{"registration_id":"new_id_registration_id","message_id":"0:########"}]}
{"multicast_id":######### ,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:################"}]}
Store this data in an array. Notice that the first one has a "canonical_ids":1. This means there was a duplicate. So to know which record in your database is the old one. Just search for "registration_id" in the above and save the index value. This index value points to the old record in your database.
In the above example, registration_A would be the old registration_id.
Get all the records from your database. Then delete it based on the index value that you retrieved. OR you can update it. That depends on how you set up your database.
Good luck!