I have implemented GCM in my project using guidelines from https://developers.google.com.
Steps that I have take are:
For receiving device Token:
-> Class GCMRegistrationIntentService which extends IntentService and it is started from my mainActivity.
-> In this service I have used:
InstanceID instanceID = InstanceID.getInstance(getApplicationContext());
token = instanceID.getToken(AppConstants.SENDER_ID, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
I have received the token successfully.
LocalBroadcastListener in my main activity to get the token generated in step 1 and store it in shared preference (Also send it to my message Server).
Class GCMTokenRefreshListenerService which extends InstanceIDListenerService to get the token in case old one expired.
-> In this I made a call to GCMRegistrationIntentService in onTokenRefresh()
Now my questions are:
In which case GCMTokenRefreshListenerService be called?
What is instanceID? What is lifecycle of instanceID?
I ran the service from terminal using:
./adb shell am startservice -a com.google.android.gms.iid.InstanceID --es "CMD" "RST" -n package.name/service.name.
Which gave me new token every time. How do I save this newly generated token in my shared preference?
When I receive new token from this service, I don’t receive notification as my token is changed. I will have to open my app in order to update the token. How to update this token?
Do I have to call GCM every time when my app opens to get the token?
You don't have to call your GCMTokenRefreshListenerService it is called automatically by android system whenever your token is refreshed.
Instance ID provides a unique ID per instance of your apps. You can find more about it here.
First you don't have to run your service, as i said earlier it will be called automatically every time your token is refreshed. To send the token to your server you should call a separate service say RegistrationService. To save the token in your SharedPreference you can do that from your RegistrationService once you have received your token.
You won't receive notification in your GcmListenerService this service is called only if your server has send some data to you via the push notification. Whenever your token is changed/refreshed you get a call in your onTokenRefresh() method. You should than make your server aware of this new token to receive notifications properly using the RegistrationService. And you don't have to open your app to get the refreshed token.
No you don't have to call or start any of the service required for push notification you just have to specify the service properly in your manifest rest all things will be handled by android system
Related
I understand that the FCM token renews itself if one of the following happens.
-The app deletes Instance ID
-The app is restored on a new device
-The user uninstalls/reinstall the app
-The user clears app data.
The following can be used at the App side to monitor Token renewal.
Monitor token generation
The onTokenRefreshcallback fires whenever a new token is generated, so
calling getToken in its context ensures that you are accessing a
current, available registration token. Make sure you have added the
service to your manifest, then call getToken in the context of
onTokenRefresh, and log the value as shown:
#Override
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Refreshed token: " + refreshedToken);
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// Instance ID token to your app server.
sendRegistrationToServer(refreshedToken);
}
My question is, when the app is terminated, the token expires and there is no way for the FCM server to know what the new token is for the device (if exist). So when I send a notification/data message to this device, the server fails to send it to the device as it doesn't know where to send it to (as there is no valid token). How do I make sure that in such situations I can notify the device ? I dont do a customer token generation. So the it seems to refresh the token now and then. How do I increase the validity of my token ?
You will need to check for an error when sending the message, and pay attention to the error codes, as listed in the documentation. You should stop using the token if you get the error messaging/registration-token-not-registered.
I am working on IBM Mobilefirst native android app. I have written code to enable push notification. I am getting notification but I have an issue here.
On Launch of the app I am calling following code.
final WLClient client = WLClient.getInstance();
push = client.getPush();
ResponseListener listener = new ResponseListener(ResponseListener.AUTHENTICITY_CONNECT);
client.getPush().setOnReadyToSubscribeListener(listener);
challengeHandler = new AndroidChallengeHandler(realm);
client.registerChallengeHandler(challengeHandler);
WLRequestOptions options=new WLRequestOptions();
options.setAppUserId("sample");
client.connect(listener,options);
When I launch the app for the first time all the above code gets executed and the listener calls the below method
#Override
public void onReadyToSubscribe() {
WLClient.getInstance().getPush().registerEventSourceCallback(pushAliasName, "PushAdapter","PushEventSource", this);
}
After this listener is getting executed i am calling subscribe method. I get success for the subscription to push.
On the server side I call the procedure to send the push notification and it reaches the phone.
Now when my app goes to background and I get a notification . I click on the notification and app restarts and never call onRecieve method of the registered interface.
On click of the notification it relaunches the app and call the onReadyToSubscribe() again and it never calls the onRecieve method. what should I do to get the onReceive() method to be called and app shouldn't get relaunched(if app is already in background) on click of notification?
My server side security test is as below
<customSecurityTest name="AuthSecurityTest">
<test realm="wl_antiXSRFRealm" step="1"/>
<test realm="wl_authenticityRealm" step="1"/>
<test realm="wl_remoteDisableRealm" step="1"/>
<test realm="wl_anonymousUserRealm" isInternalUserID="true" step="1"/>
<test realm="wl_deviceAutoProvisioningRealm" step="2" isInternalDeviceID="true"/>
</customSecurityTest>
The useridentity is not binded to security test but i am putting it in the apps applicationdescriptor. So it never call for credentials required of challenge handler on connect.
Here I think this could be the issue in the sample code that is provided by MFP in 7.1 version security test has useridentity realm but in my case I am not using the custom useridentity realm but I am using the default wl_anonymousUserRealm. This is the issue because when i tried working with the sample code it works completely fine with all the scenarios. But with the wl_anonymousUserRealm I have this issue.
onReadyToSubscribe() method getting called everytime the application connects to the server ( even if already subscribed for push) is expected and by design.
onReadyToSubscribe() is a call back that fires at the client denoting the token exchange between client and server is complete. This is required because tokens issued to an application by mediator can change. This makes it imperative that the server always stays updated with the latest token. If not , push notification will fail with invalid token error.
When the client connects to the server , they compare tokens - client presents the token it has now and server presents what it had stored. Three cases happen:
1) In a new registration , server does not have a token. Here client passes on what it got from the mediator, server persists it and issues a success. Client fires the onReadyToSubscribe() callback to indicate push handshake is complete and client can now subscribe to aliases or tags.
2) Client has received a new token from the mediator. This does not match the one server already has. Client passes on the new token to server, server updates its records with the new token and lets the client know. onReadyToSubscribe() call back fires at client indicating successful handshake. Earlier subscriptions records stay.
3)If the tokens match, then onReadyToSubscribe() fires denoting that push handshake is complete (no changes required)
Thanks to Vivin for the suggestions.
Here was the issue in the sample code that is provided by MFP in 7.1 version security test has useridentity realm but in my case I am not using the custom useridentity realm but I am using the default wl_anonymousUserRealm.
The actual issue was wl_anonymousUserRealm generates a new random userid everytime. So when ever my app is relaunched my userid gets changed and I wont get push in onrecieve because it thinks a different user is logged into the app..
Now because I cant change the wl_anonymousUserRealm I am sending push using deviceid. Please check the below link(Adapter code as below).
https://www.ibm.com/support/knowledgecenter/SSHS8R_7.1.0/com.ibm.worklight.apiref.doc/html/refjavascript-server/html/WL.Server.html#sendMessage
check sample client code from below link.
https://mobilefirstplatform.ibmcloud.com/tutorials/en/foundation/7.1/notifications/push-notifications-overview/push-notifications-native-android-applications/tag-based-notifications-in-native-android-applications/
I am new to firebase I am learning it like a toddler learning to walk. So far I have managed to send a message to my own phone using a token generated for my phone by firebase framework. Now here's where it gets tricky (in my opinion). There is a method called onTokenRefresh() in the FirebaseInstanceIdService extended service. Since it is called refresh, Then I am assuming that it will change. I want to know when this token is created and when will it be changed?
And if it changes, suppose I send a message to a device with token 'A' which is offline for now, so it will be queued. Now when the device gets online, it will "refresh" the token to 'B'. Now as the message was supposed to be delivered to token 'A', the message will never be delivered. How can I manage this situation?
The token is generated, after the app is first launched, as soon as the phone can connect to the Google servers. Due to the required connectivity this might not happen immediately, but in most of the cases it will happen in few seconds after the user open the app.
As soon as the token is generated the method onTokenRefresh() is called.
As you pointed out the token can change, in which case the onTokenRefresh() method will be called again.
The refresh event is somehow rare, don't expect to see it often at all.
When the refresh token happens, all the messages that have been "successfully" sent (the API returned you a message-id) to the old token will be delivered.
Finally, even after the refresh happened the old token will still be working for a short period, to allow the app to communicate the new token to its back-end.
On initial startup of your app, the sdk of FCM generates the registration token for the client app instance. As above said, It is a rare event. To be specific,The registration token may change when:
The app deletes Instance ID.
The app is restored on a new device
The user uninstall/reinstall the app
The user clears app data.
Instance ID provides a unique ID per instance of your apps.Instance ID provides a simple API to generate security tokens that authorize third parties to access your app's server side managed resources.The Instance ID server can even tell you when the device on which your app is installed was last used.We can use this to decide whether to keep data from the app or send a push message to re-engage with the users.
Every time the device token is changed, It is reflected in onTokenRefresh() method.For getting the device token when it is changed, we can call this method to get the refreshed token.
and to get the device token at any time we can use FirebaseInstanceId.getInstance().getToken() method to get the current device token.It takes a bit of time to get the device token.
Click here to read more about accessing device registration token.
onTokenRefresh() and FirebaseInstanceIdService are deprecated.
This call is also deprecated FirebaseInstanceId.getInstance().getToken()
Instead, You should override onNewToken(String token) in FirebaseMessagingService. This method triggered when the token is changed. Once you override this method, you can safely remove FirebaseInstanceIdService whcih contains onTokenRefresh().
When token can change?
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
How to retrieve the current token:
by calling FirebaseInstanceId.getInstance().getInstanceId():
FirebaseInstanceId.getInstance().getInstanceId()
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() {
#Override
public void onComplete(#NonNull Task<InstanceIdResult> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
String token = task.getResult().getToken();
// Log and toast
String msg = getString(R.string.msg_token_fmt, token);
Log.d(TAG, msg);
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
For more info:
https://firebase.google.com/docs/cloud-messaging/android/client
For Managing tokens for specific sender id (other than the default sender id),
check here
I'm using GCM on Android.
I use InstanceID.getInstance(...).getToken(...) to receive a push token, but in some cases (after application updates or re-install) I receive an invalid token.
When the server returns a NotRegistered error, I've connected with a debugger and called InstanceID.getInstance(...).getToken(...). But this token is not valid (I've tried to send push via curl -s "https://android.googleapis.com/gcm/send" ... using this token), I receive NotRegistered error.
Why instanceID could return invalid token?
It shouldn't give invalid token.
Have you applied all procedures?
InstanceIDListenerService: When the token changes via the app
updates, etc.
RegistrationIntentService: When the token changes, you receive it via InstanceIDListenerService and call this intent to get a new
token.
Finally, I found a solution.
I worked with instanceID from two different threads. I called getToken(...) two times simultaneously. If instance doesn't have a cache, it get token from network. I think, it sends two requests in my case and there is no guarantee of it's order. So instanceID cached one token, but google cloud another one.
I have tried to register my device by calling the push notification service which stores tokens inside push_notification_token table.
Registering the token is no problem but in the same app i wanted to delete the token from the table in some situation so i was trying to see if same service is helpful or not.
I tried calling the service by using URL http://mysite/endpoint/push_notifications to register the token where i will pass parameters as token is token generated from GCM service and type is android. This is working fine.
So to delete the token what is the procedure.
I solved the issue by following these steps.
1) Used DELETE method instead of normal POST method
2) Sent the tokens in the URL => http://example.com/endpoint_name/push_notifications/{token}
Like http://example.com/endpoint_name/push_notifications/abcgr123 whole token value in the end.
This will delete that token from the database.