My app is using Google's C2DM (push notification) to notify users about new activity from friends. Once they install the app I register the device with C2DM servers and store user's phone number. So I know that the user is using my app and I can send him/her the push notifications. But what happens if users uninstalls my app, is there a way to catch it in my app? Or the only way is to catch an error on my server when I send a C2DM and it's unreachable, then mark a user as inactive?
I would love to notify users when their friends are using an app and when they no longer do.
What's is the best solution for this scenario?
The GCM documentation explains this situation here:
https://developers.google.com/cloud-messaging/registration#how-uninstalled-client-app-unregistration-works
"An application can be automatically unregistered after it is uninstalled from the device. However, this process does not happens right away, as Android does not provide an uninstall callback."
Basically when GCM tries to send the next push notification, the device will tell GCM the receiving application was uninstalled.
As for notifying friends that their friends aren't using the app any more, GCM will send a NotRegistered error to your notification server when this failure occurs; it won't be immediate, but could you use that?
Unfortunately the ACTION_PACKAGE_REMOVED intent will be sent out to all receivers except for your own. This is confirmed here.
Some questions for your C2DM plan, since I'm not very familiar with it. If the user just leaves their device off for a long period of time, will that trigger the error condition you use? How does C2DM actually report an "unreachable" device? Is that a condition that only occurs when it attempts to send the push notification and fails or is it when it somehow determines it reaches the device but fails to be handled properly? Obviously in the second scenario your plan would work, but I can see some "false positives" occurring otherwise.
Older SO question for reference: android not receiving Intent ACTION_PACKAGE_REMOVED in the removed package
Yes, but it is quite hacky.
The method is based on the fact that the first thing android does when uninstalling your app is deleting your data file. So you could use a file watcher to detect the deletion.
Also you need to write this in native code. If you write your code in java, your app will be uninstalled before it could execute any code.
please see this demo : https://github.com/sevenler/Uninstall_Statics
Google C2DM service is working in passive mode when it comes to detecting uninstalled applications.
First push notification after uninstalling your application (without unregistering from C2DM!!!) will NOT return any error in response. However, the second push notification will return an "invalid registration" or "not registered" error codes where you can realize the application was uninstalled.
The reason is that C2DM servers return the response code immediately and only then tries to push the client. When client respond that an application was uninstalled, it is deleted from C2DM servers. Next push attempt will return an error code immediately.
I have some points to tell you ,
Android community recommends you to use GCM instead of C2DM as it's no longer available.
In android there is no way for applications to get itself notified that app is getting uninstalled.
in GCM if you want to stop sending messages to uninstalled apps you can refer this
When you send messages to GCM from your server you will get response string.In that if you are getting error as "NotRegistered, you should remove the registration ID from your server database because the application was uninstalled from the device or it does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents."
I know only one way with server response 200 with "NotRegistered" message in body.
NotRegistered — The registration_id is no longer valid, for example user has uninstalled the application or turned off notifications. Sender should stop sending messages to this device.
Look into this GCM doc:
GCM Unregistration
You should never unregister your app. This is taken care from server side.
To detect app uninstallation using Google Play Services, you can use the App Uninstallation Reporting API. This API allows you to receive notifications when your app is uninstalled by users. #ThanksChatGPT
Related
My app is using Google's C2DM (push notification) to notify users about new activity from friends. Once they install the app I register the device with C2DM servers and store user's phone number. So I know that the user is using my app and I can send him/her the push notifications. But what happens if users uninstalls my app, is there a way to catch it in my app? Or the only way is to catch an error on my server when I send a C2DM and it's unreachable, then mark a user as inactive?
I would love to notify users when their friends are using an app and when they no longer do.
What's is the best solution for this scenario?
The GCM documentation explains this situation here:
https://developers.google.com/cloud-messaging/registration#how-uninstalled-client-app-unregistration-works
"An application can be automatically unregistered after it is uninstalled from the device. However, this process does not happens right away, as Android does not provide an uninstall callback."
Basically when GCM tries to send the next push notification, the device will tell GCM the receiving application was uninstalled.
As for notifying friends that their friends aren't using the app any more, GCM will send a NotRegistered error to your notification server when this failure occurs; it won't be immediate, but could you use that?
Unfortunately the ACTION_PACKAGE_REMOVED intent will be sent out to all receivers except for your own. This is confirmed here.
Some questions for your C2DM plan, since I'm not very familiar with it. If the user just leaves their device off for a long period of time, will that trigger the error condition you use? How does C2DM actually report an "unreachable" device? Is that a condition that only occurs when it attempts to send the push notification and fails or is it when it somehow determines it reaches the device but fails to be handled properly? Obviously in the second scenario your plan would work, but I can see some "false positives" occurring otherwise.
Older SO question for reference: android not receiving Intent ACTION_PACKAGE_REMOVED in the removed package
Yes, but it is quite hacky.
The method is based on the fact that the first thing android does when uninstalling your app is deleting your data file. So you could use a file watcher to detect the deletion.
Also you need to write this in native code. If you write your code in java, your app will be uninstalled before it could execute any code.
please see this demo : https://github.com/sevenler/Uninstall_Statics
Google C2DM service is working in passive mode when it comes to detecting uninstalled applications.
First push notification after uninstalling your application (without unregistering from C2DM!!!) will NOT return any error in response. However, the second push notification will return an "invalid registration" or "not registered" error codes where you can realize the application was uninstalled.
The reason is that C2DM servers return the response code immediately and only then tries to push the client. When client respond that an application was uninstalled, it is deleted from C2DM servers. Next push attempt will return an error code immediately.
I have some points to tell you ,
Android community recommends you to use GCM instead of C2DM as it's no longer available.
In android there is no way for applications to get itself notified that app is getting uninstalled.
in GCM if you want to stop sending messages to uninstalled apps you can refer this
When you send messages to GCM from your server you will get response string.In that if you are getting error as "NotRegistered, you should remove the registration ID from your server database because the application was uninstalled from the device or it does not have a broadcast receiver configured to receive com.google.android.c2dm.intent.RECEIVE intents."
I know only one way with server response 200 with "NotRegistered" message in body.
NotRegistered — The registration_id is no longer valid, for example user has uninstalled the application or turned off notifications. Sender should stop sending messages to this device.
Look into this GCM doc:
GCM Unregistration
You should never unregister your app. This is taken care from server side.
To detect app uninstallation using Google Play Services, you can use the App Uninstallation Reporting API. This API allows you to receive notifications when your app is uninstalled by users. #ThanksChatGPT
Scenario: I have a 3rd-party app server, a gcm http connection server, and an android app setup for push notifications. When the user uninstalls the app, I can delete them from my 3rd-party server by sending their (no-longer-valid) registration id a push notification and handling the "NotRegistered" error returned from the gcm connection server.
However, this similar approach does not seem to work when you send a push notification to users subscribed to a "Topic", as here the gcm connection server only returns a json object with the "message_id".
The notable columns in my 3rd-party server database table are as follows:
| gcmRegistrationID (string) | subscribedToNotificationTopic (boolean) |
Does anyone know how to identify a user that has previously set true in the subscribedToNotificationTopic column, then uninstalled the app?
I have an idea on how to resolve this, but it seems somewhat messy.
Every so often, instead of only sending push notifications to a topic e.g.
{
"to" : "/topics/global",
...
}
send a push notification to the registration ids of the users subscribed to the topic e.g.
{
"registration_ids" : {list of registration ids},
...
}
such that I can retrieve and handle any "NotRegistered" errors.
The problem with this however, is that multicast messaging has a limit of 1000 users so I'd have to send a message for each thousand.
This seems like bad practice (especially if there is more than one topic involved), but maybe it's the only way?
(Extra tags: topic messaging, pubsub, unregister, google cloud messaging)
First off, I am not sure it is correct to assume that an "NotRegistered" error can only be the result of the user uninstalling your app. It seems like the device can become unregistered if the device receives a message but that message can not be delivered. This can happen if the user stops the app (which is different from uninstalling it, or even if the app was killed and remains stopped due to a power saving mode.
https://developers.google.com/cloud-messaging/ccs#response
If it is NotRegistered, you should remove the registration ID from
your server database because the application was uninstalled from the
device, or the client app isn't configured to receive messages.
What you can do is from the device side, periodically register the device, and make sure that the token you get back is the same as the one you stored, if not update your server with the new token.
As far as detecting if a user has uninstalled your app via a response to a topic message, I agree with you it does not seem to be directly possible. As an alternative to your proposed solution, you might, every now and then include a flag in the topic message that would ask the device to validate itself with your server, if a device does not validate itself after some number of attempts or some period of time you might conclude the user is not longer listening.
The other question is why do you really need to know. Is there any harm in sending some extra regid's to google? If a user quits your web service you can remove them from the list, but do you really need to take action if they just un-install your app from there device? - Just some thing to consider. Hope this helps.
When the user uninstalls the app from his android device, it means that the registration_id for C2DM is no more valid. Now, how does the server which sends the push notifications know this. With Apple, there is something called Apple-feedback which lists out all the device-tokens (Android folks, read as registration-id) that are invalid.
Please, help me out with this.
I'm also looking for a sollution to this, so far I've only found one useful sollution which is mentioned here: http://groups.google.com/group/android-c2dm/browse_thread/thread/8e58ed95a0818716
In short: When you SEND the message to the device, send an UID with it (eg. generated on first install). When a message is received in the application, check if the UID is the same, if it is, do you thing (eg create notification), otherwise ignore it, and send a msg to your C2DM server that this Google C2DM registration ID is not valid anymore.
I think that is the reason C2DM are refreshing the Registration ID after random time. If user uninstall the App from his device, he is not not going to update his/her Registration ID either. Then he will not getting any Push Notifications anymore.
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.
I'm wondering if anyone has faced this issue with Google C2DM? This is the scenario I am faced with:
User installs the app and registers
with C2DM server for a registration
key.
User uninstalls the app.
User reinstalls the app (and
registers with C2DM server for new
registration key).
Now I send message from my server to the user's phone and they get a duplicate message.
Could anyone shed any insight into wether this is expected behaviour or how I can fix it?
Thanks,
Not sure if this is the best approach, but there's a relevant thread over at the android-c2dm group, where the poster offers one technique:
I am sending registration id in the message, so I can check it against the stored registration id on the device.
If it's not the same, discard it and notify the service that registration Id is no longer in use
Downside is sending registration Id takes up some space in already
limited message size. But works perfectly in my case since my
original message is no more than a few chars long.
This should only happen for the first push notification after re-installing your application.
Google C2DM service is working in passive mode when it comes to detecting uninstalled applications.
First push notification after uninstalling your application (without unregistering from C2DM!!!) will NOT return any error in response. However, the second push notification will return an "invalid registration" or "not registered" error codes where you can realize the application was uninstalled.
The reason is that C2DM servers return the response code immediately and only then tries to push the client. When client respond that an application was uninstalled, it is deleted from C2DM servers. Next push attempt will return an error code immediately.
Another solution could be to provide your server with a unique identifier for the device. In that case you can just update the registrationID for that UUID when the device tries to register after re-installation.
Yup, I've run into the same issue and in my opinion it's a big oversight in the Android C2DM implementation. iOS handles this much better in that an app can only ever receive notifications for one and only one device token (equivalent of the c2dm registration id)
The workaround I use is to send the last 10 characters of the registration id as part of the c2dm payload and then in my onMessage method I do the following check:
if (!regId.endsWith(bundle.getString("regsuffix"))) return null;
Both #Zamel and #johan answers are good and need to be combined. If you combine both solutions than you will minimize your server's database.
So the best solution will be to:
Send device id when sending the push token to the server
Update push token when is sent for existing device id
Invalidate push token in the server's database, if push notification returns an "invalid registration" or "not registered" error codes to the server
When push token is recognized as "invalid registration" or "not registered" you can invalidate it(mark it as null), delete the row in the database or implement expiration functionality. It depends on your needs