I have multiple different deviceids in my database table pointing to same a device because google will send me different device id when a device reinstall/install. Due to which devices getting multiple notifications which is hurting me and my users very much. Is there is a way to tell google that only send one notification to single device? or is there a way to check the id is new or old before sending request? Anyone experience with this weird issue? BTW, I am using PushSharp/ASP.NET in backend.
Update: I am now relying on native device id. So, I will remove/replace the old registration ids from my database table where native device id is same.
I think you need to remove the device id from your database once you get unregistered error code.
Unregistered Device
An existing registration ID may cease to be valid in a number of scenarios, including:
If the application manually unregisters by issuing a com.google.android.c2dm.intent.UNREGISTER intent.
If the application is automatically unregistered, which can happen (but is not guaranteed) if the user uninstalls the application.
If the registration ID expires. Google might decide to refresh registration IDs.
If the application is updated but the new version does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents.
For all these cases, you should remove this registration ID from the 3rd-party server and stop using it to send messages.
Happens when error code is NotRegistered.
I recommend you to handle error codes when you send a message to a device.
http://developer.android.com/google/gcm/gcm.html
Please read role of 3rd party server. You would get more details.
Related
I'm a bit confused about how to manage registration ids for all app installations.
In the app server database I have a User table and a UserDevice table. Every User may have many UserDevices. Each UserDevice have a registration id. So when a user should be notified I send the message to all the registration id's for all the devices of the user.
But when the application is reinstalled, Android is updated or something else happens that causes the registration id to change, the app will send a new registration id to our app server. But I have no way to know if this is a new device or if an existing UserDevice should be updated with the new registration id.
How is this supposed to be handled? My first thought is to send a persistent hardware ID as well as the registration ID, but perhaps there is a better way?
You don't have to worry about this. If the user does reinstall the app and the token is refreshed just add it to the users list of tokens (keeping the old one for now).
next time you send a notification to this user it will fail on the token that is no longer used. Now all you have to do when you get the fail reply from google is remove that token from your devices table.
Remember if you fail to send to a token for reasons such as invalidRegID or NotRegistered then the GCM will/has unregistered that token so it is no good so it needs to be removed from your list otherwise you will get in googles bad books for repeatedly sending to the same failed token. So remove ones that fail.
when my apps start i always check to see if the device is registered if it is I send the same token to my app server just incase it somehow got deleted.
Hope this helps
You could take a look at GCM's Device Group feature, which is explicitly intended for the "one user, many devices" problem:
https://developers.google.com/cloud-messaging/notifications
I use PushSharp and there I can use the different event handlers that are defined to handle subscription changes or expirations. The source code is available on github https://github.com/Redth/PushSharp/blob/master/PushSharp.Android/Gcm/GcmPushChannel.cs
You can probably apply the same logic to your application.
Thinking of a problem that I already have 20 app instances of the app installed on different devices and I have decided that I want to install another one of this app on my newly bought device. This would really be a big problem in terms of obtaining a notification key right? because the max registration id that is allowed for notificatin_key members is 20, so I should limit my app instances.
One of the solution is to uninstall a single app instance from a device and tell my online database server to delete the saved registration id similar to the registration id of the uninstalled app. In this way, I have a permission to install the app on my newly bought device by checking my registration id field in my database if its less than or not equal to maximum number registration id which 20. Let's assume that I don't have an internet connection when I am uninstalling the app on the device. How can I update my online database server and provide another space for the newly bought device.
For those who are experienced developer of this kind of android app. Please share your thoughts on how can I properly handle this kind of situations. thanks!
You need to read Interpreting an error response in details. Your server will get Unregistered Device error [error code is NotRegistered] if application has been uninstalled from device.
So if you get this error, you can remove entry for that particular device from server's database.
From documentation
Unregistered Device
An existing registration ID may cease to be valid in a number of scenarios, including:
If the application manually unregisters by issuing a com.google.android.c2dm.intent.UNREGISTER intent.
If the application is automatically unregistered, which can happen (but is not guaranteed) if the user uninstalls the application.
If the registration ID expires. Google might decide to refresh registration IDs.
If the application is updated but the new version does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents.
For all these cases, you should remove this registration ID from the 3rd-party server and stop using it to send messages.
Happens when error code is NotRegistered.
I am doing an android application which uses gcm.I came to know that when the application is uninstalled and re installed there is no guarantee to receive the same registration id again.So what I did is while logging in I take one unique Id from application server and whenever new registration id is made I am able to replace that in application server.So what my question is, I am not using the canonical id for replacing the latest registration id in gcm server.Will it cause any problem? Please help me.
Your solution is basically good, though I'm not sure whether that unique Id your server generates is associated with the device or the logged in user. If it's the former, make sure you persist it on the device's external storage, so that it doesn't get lost when the app is uninstalled. If it's the latter, I'm not sure how you are handling the case of the same user logging in on multiple devices.
While your strategy is good for maintaining the current registration ID on both the device and your server, you should still handle canonical registration ID responses from Google just to be safe.
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.
Source : http://developer.android.com/google/gcm/adv.html#canonical
I have developed android where I am using GCM to send notification.
I am facing one problem that i will explain by taking an example.
Let's say user A logging in my app. So I generate token for that user let's say that is "AA1" using GCM and storing it our database. But then if user uninstall app and install it again. Then I am generating token again (Lets say AA2) and storing in our server.
If both tokens are same then it will not cause any issue as I am comparing before inserting a token. But as GCM tokens for same device can differ. Right ?
Later, When I want to send a notification to that user. That user will get notification twice as his previous token is still working. I tried to search but found nothing related to this. It may that I am missing something or none have thought like this.
Doesn't matter which but I think there must a way to solve this issue.
Question
Is there a way to check both the notification tokens belongs to same device or not?
Is there a way to remove notification token from our database when user uninstall apps ?
Please guide me.
What i think is this problem can be solved this way.
When you are storing Token value to application server, you should map it with users device id. therefore when this token is re-generated (with different value), your application server can update the token for that user as in both cases device id was same.
Is there a way to check both the notification tokens belongs to same device or not?
Not in a unique way. You could make an IP comparison when the user registers and store both the GCM id and their IP address, this way when they connect again you can check whether you already have a registration with that IP address and delete the previous. However, this is not a good 100% solution as there are countries that assign the same IP address to several devices. I think the best approach here is implementing a timeout mechanism. In my apps, when the user registers, they have to send a "keepalive" HTTP POST signal every 30 seconds to say "hey, I'm still here". If that keepalive doesn't arrive in a reasonable amount of time, I consider it a timeout and remove it from the database.
This is, however (now I mean unistallation), one of the cases where you should call the GoogleCloudMessaging.unregister() method and send it also to the third-party server.
Is there a way to remove notification token from our database when user uninstall apps ?
Seems that there's not (Perform a task on uninstall in android). But however, with the above approach, you wouldn't have any further issues as this user would stop sending the "keepalive" notifications and he would be timed-out.
Agree with you. But still there is a solution. what you can do is, you can use device IMEI number as device id and use it. This IMEI number will never change even if on factory reset.
How to get unique device hardware id in Android?
TelephonyManager telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.getDeviceId();
Also add the below manifest permission.
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
What we do is generate a unique ID (in our server) for each application instance. The application gets that ID by making an API call after first being installed on the device. We store that ID in external storage (that doesn't get wiped when the app is uninstalled). The registration ID of the device is associated in our DB with our unique ID.
This way, when the app is installed again on the same device, we get that identifier from the external storage (if available) and re-use it, and even if the registration ID is changed, we know it belongs to the same device.
In addition to this strategy, your server should handle the following scenarios :
If you get a canonical registration ID in the response from GCM, delete the old registration ID (if the canonical ID already exists in your DB) or change it to the new canonical ID. This would prevent duplicate messages from being sent again to this device.
If you get NotRegistered error in the response from GCM, delete the registration ID you attempted to send from the DB. This means the application was uninstalled. There is no other way to detect that the user uninstalled the app.
One Solution I think would be to send a pushnotification key with every notification. Handle this on your client side by keeping a last pushnotification key used in shared prefs. If you get the same notification key again drop it.
I just added C2DM capability to my Android App.
At the moment the following happens if C2DM is started in my App.
My App sends the registration Intent
The answer broadcast is received by my app
The device token is retrieved from the intent and sent to my server
From that moment everything is working fine. The client receives the push notifications etc.
A problem occurs if the following hapens:
The user uninstalls the application without disabling push. (Completely deleting it not only updating)
The user reinstalls the application
If after step 5 a push notification is sent my app still receives this notification.
It seems that the token which was retrieved from the previous install is still active and is reconnected to the new instance of my application.
This leeds to the following problem:
A user who reinstalls my app but has no intention of receiving push notifications has no possibility to remove himself from the service because the new instance of the app has no way to unregister the old token from my server.
Is this a bug in the C2DM system or is something wrong in my setup?
Update
I followed Berdons advice and did the following:
For testing purposes only start an unregister Intent every time my app starts up.
After I send the unregister intent no push notification from my server is sent to my application. That seems to do the trick, but if I now go the C2DM Settings Screen and turn on push notifications for my app all the old tokens get active again and I receive information that I did not register for in the current installation of my app.
Next Update
It seems I'm not the only one with this problem:
Android C2DM : Duplicate message to the same device and App
I hoped that Google would manage those tokens in a way that old tokens from the same device get disabled after a new one was issued. I also expect that after I sent an unregister Intent all tokens for that application and that device are marked as invalid or deleted from the Google Server for ever. If this is somehow a design decision by Google for special use cases I don't see please enlighten me.
We now found a solution that should work in most of the cases.
The server adds the C2DM registration id as a data field with every C2D Message.
The device now only shows a notification if the token in the message and the token that is stored in a pref file match. That way we guarantee that we don't show messages for device tokens that were obtained by previous installs.
If the tokens do not match we found an old token that should not be active anymore. We now mark the token on our own web server as inactive to prevent the server from sending more unnecessary C2D Messages
This solution enables us to show only relevant data without the need to store a unique user id.
In my C2DM implementation, each user's device token is saved in a database against their UDID and the package name of the app (among other things). The UDID and package name form the primary key, meaning that the table can list multiple apps from the same device (UDID). When a user runs a particular app, the device token is recorded, and if they uninstall and re-run the app, the new device token would be recorded over the old one. We also have columns to record whether push is active for that particular app/device combo, and which types of push messages the user has enabled/disabled.
When the time comes to send a push for a particular app, no UDID will be registered more than once (since these two fields form the primary key), and therefore only the latest device token will be used. Furthermore, our query only returns the rows that have push messages enabled.
This solution should solve your problem because it prevents you from sending the push to both device tokens. I hope this helps!
More like an unexpected "feature". You might consider issuing an unregister request on the "first run" (first run ever) of your application to prevent this from occuring.
Update
You could could do the work of differentiating between different C2DM messages by using the collapse_key (or anything of your own creation) as an identifier. Update it between registrations and pass it to the device following registrations, unregistrations and messages.