I have an InstanceID. I get two differentt tokens for a different app server:
token1 = instanceID.getToken()
token2 = instandeID.getToken()
Can I delete a specific token, for example token1?
You could with deleteInstanceId. Although this would resort in the InstanceId service to generate a new one.
It's intended behavior for each server to generate it's own Registration token for each app instance.
Related
My application is receiving the push notification from 2 firebase project. I am getting the tokens for each sender id by calling "getToken(String authorizedEntity, String scope)" separately.
String token1 = FirebaseInstanceId.getInstance().getToken("authorizedEntity1", "FCM");
String token2 = FirebaseInstanceId.getInstance().getToken("authorizedEntity2", "FCM");
As per the onTokenRefresh documentation
Called when the system determines that the tokens need to be refreshed. The application should call getToken() and send the tokens to all application servers.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
As onTokenRefresh has been deprecated, I have checked the onNewToken, As per the documentation
Called when a new token for the default Firebase project is generated.
This is invoked after app install when a token is first generated, and again if the token changes.
Q1. How to know which is the default Firebase project in case of multiple sender id ?
Q2. Suppose if "authorizedEntity1" is associated with the default firebase project then does it mean onNewToken will be invoked only when token1 will be changed ? or it will be also invoked when token2 will be changed? If it doesn't work for token2 then how to know that token2 need to be refreshed?
Q3. With reference of this my understanding is onTokenRefresh will be invoked whenever any of the token needs to be refreshed(not only for default project). Is this understanding correct ?
I want to send the updated token to the server whenever system determines that the token1 or token2 need to be refreshed.
Note: I am initializing the firebase in my application class as I am dealing with multiple sender ids.
After some test, I found out that only default project's token will be delivered to onNewToken. onNewToken will not be called when new token created for other sender ids by calling getToken.
Tokens retrieved by calling getToken API are consist of different string data than default token.
And these other sender id's tokens are not refreshed when default token changes.
It look like they last until you explicitly call deleteToken API.
(Token value didn't changed when I repeatedly call getToken.)
Depending on #sNash comment who contaced Firebase Support, you should manage tokens for all sender ids other than the default sender id.
How?
One simple solution is through storing all sender ids with their tokens in SharedPreferences or in db. When app starts, check if the token changed for each sender by comparing the stored token with the token returned by
FirebaseInstanceId.getInstance().getToken(SENDER_ID, "FCM");
Moreover, do the same check in onNewToken method. There is a chance that tokens other than the default may be changed when the default token is changed.
The default sender is the one related to your Firebase project that the app is connected to and it can be found in google-services.json
I've been reading on canonical IDs in GCM and how they help to rectify sending duplicate push notifications and with security. But now with Firebase Cloud Messaging (FCM), does this issue still exist?
I am the registration part has been taken away from the developer now and we just wait for a token refresh as per below:
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
#Override
public void onTokenRefresh() {
// Get updated registration ID
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Logger.d("Refreshed FCM token: " + refreshedToken);
}
}
Some info on canonical IDs can be found here.
Update:
I recently revisited this topic on Canonical IDs and have concluded the following.
In FCM, it seems the Canonical IDs are no longer used (or at the very least extremely rarely) because of the way the Instance ID service works. To put it simply, the service works that there would only be one valid token per App Instance.
If the older token expires (for whichever reason), FCM triggers a tokenRefresh event where you would get a new registration token, and where you must also handle it accordingly (in onTokenRefresh()).
Short answer, Yes. It's still necessary.
The onTokenRefresh() method is expected to trigger whenever the token is actually refreshed. From there, it's the developer's responsibility to send the registration token towards an App Server.
BUT in an event where you weren't able to get a hold of the new registration token (e.g. forgot to save it, deleted it and only have the previous registration token, etc.), it may result to you (the developer) to send towards a supposed to be no longer valid registration token. That's when Canonical IDs come in.
I guess you can treat Canonical IDs as another safety measure so that developers can still get a hold of the valid registration token. Details about Canonical IDs (how it is handled and stuff) are mentioned in the FCM docs here.
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.
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.
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.