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.
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’m implementing Firebase Cloud Messaging (FCM) and am experiencing a problem that I’m unable to solve. I have implemented FirebaseMessagingService and FirebaseInstanceIdService according to the guide(s). When I go to Firebase Console for my app, and use the Notification function, I can successfully send a message to ALL my app instances (using the package name).
Now, in the code I have fetched the Firebase Instance Id (token) by use of the following code:
String token = FirebaseInstanceId.getInstance().getToken();
SendFirebaseTokenToServer(token);
(note that currently I’m using HTTP protocol, as my server does not yet have a cert). Anyway using the token I get from the call above, I go back to the Firebase Console and try to send a message to one (1) installed instance of my app. I grab the token from our server DB where it is stored as "varchar(max)". When I do that I get the following error message:
Invalid registration token. Check the token format.
I have googled that and found only one hit (having to do with Firebase and iOS):
http://stackoverflow.com/questions/41343520/ios-invalid-registration-token-check-the-token-format
That issue indicates that a cert was required (I think I’m reading it correctly). I’m not sure what I’m doing wrong. I need to get this to work using the Firebase Console first, then my server guy can start on his end knowing that it should work.
Turns out i was programatically encoding all POST or PUT parameters prior to sending to our server. the FCM token had a semicolon in it, which got encoded to a "%3A", seemingly causing the problem.
do NOT encode the FCM token.
I´m trying to use FCM to send Firebase Notifications, but I have a Application using one Firebase Project and a library using another Firebase Project. I want to receive the token from library Firebase Project to receive Firebase Notifications.
When I try directly receive the token using:
FirebaseInstanceId.getInstance().getToken();
I received a valid token, but from Application Firebase Project. If i try to force in getInstance() the library Firebase Project, using:
FirebaseInstanceId.getInstance(FirebaseApp.getInstance("ABC")).getToken();
I receive null. If I try to use Application or Library Firebase Project passing parameters in getToken() as:
FirebaseInstanceId.getInstance().getToken(getApplication().getResources().getString(R.string.gcm_defaultSenderId), FirebaseMessaging.INSTANCE_ID_SCOPE);
or
FirebaseInstanceId.getInstance(FirebaseApp.getInstance("ABC")).getToken(getApplication().getResources().getString(R.string.gcm_defaultSenderId), FirebaseMessaging.INSTANCE_ID_SCOPE);
I received the same valid token from Application Firebase Project.
the R.string.gcm_defaultSenderId is from library project resources
There is another way to receive Library Firebase Project token or I'm doing something wrong?
This is my understanding of what you are observing based on my experiments and the documentation.
When you first call getToken() using a FirebaseApp other then the default, there is no token and communication with the server is required to produce one. A null value is returned and the process to fetch a token is initiated. The documentation for getToken() says it returns "the master token or null if the token is not yet available". After a few seconds the token is received. You can detect that event using the onTokenRefresh() method of FirebaseInstanceIdService, if you have implemented that. On subsequent calls to getToken() for the non-default app, the token will be present and returned immediately by getToken().
I think the calls to get a scoped token always return a token because they are blocking (see docs), and wait for the interaction with the server to complete before returning a result.
I made a mistake in FirebaseApp.initializeApp(), because I forgot to set:
.setGcmSenderId(getApplication().getResources().getString(R.string.gcm_defaultSenderId))
in FirebaseOptions.Builder().
After that change, I receive a valid Library Firebase Token in getToken() and in onTokenRefresh()
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'm developing a GCM Application.
My issue is that when first install(launch) the app, it takes some time to get GCM token.
But My app just 'POST' to server to register the user.
So at first launch , because of registering when GCM token is null, my app always register GCM token null at first time.
How could I solve this problem?
Actually I tried to solve this by using Progressbar but Progressbar doesn't show like the sample project of GCM provided by google.
How do you guys solve this problem?
You should only send the token to your application server after it has been returned by your InstanceID.getToken call which should not happen on the main thread.
You should use an IntentService that will get the token AND send it to your application server, then if necessary, you can use a BroadcastReceiver/LocalBroadcastManager to let your UI know when the the token has been successfully sent to the application server.
Consider the GCM quickstart sample. In particular look at the RegistrationIntentService that gets the token and sends it to the server.
// [START get_token]
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// [END get_token]
Log.i(TAG, "GCM Registration Token: " + token);
// TODO: Implement this method to send any registration to your app's servers.
sendRegistrationToServer(token);
This should be quite an easy fix. if you followed googles example then the registering to the GCM should be done using an async task class called RegisterApp.
The async task has 3 main methods OnPreExecute, DoInBackground and OnPostExecute. DoInBackground is the method that runs on a seperate thread and that will be why it is failing currently. It hasn't finished running that thread and you have already sent an empty regID to your backend. This wont be failing next time you login to your app because more than likely you are saving the regId to a file and reading it if your app is already registered to the GCM
You can send the regId to your backend in either the DoInBackground, which is where you should be registering to the GCM, or in the OnPostExecute.
I do it in the DoInBackground method straight after it registers as this is the only part that runs on a separate thread and if for some reason it fails to send to my backend it is handled on a seperate thread to the main UIThread.
Dont forget you will also need your current method of sending it because the RegisterApp wont run if your app is already registered.
Hope this helps.
This is the sample i followed. Here
Currently it works for me as the activity that handles the registering is locked to portrait and not much else is going on, but each to their own.
Arthur makes some valid points