I am using FirebaseInstanceId.getInstance().getToken() method to generate device token. I know this method is deprecated, but I am keen to find the exact RCA for the issue.
When I am using this method in emulator, it is working fine and returning token with all emulator variation tried with multiple emulators.
I tried debugging with android phone it is returning device token. But Our application runs on Zebra TC57 device and there it is not working and method is returning null.
This works in some device and not works in some device. I have one device where it's not working to reproduce the issue
Android version : 8.1.0
I tried checking Google play Service version with
PackageInfoCompat.getLongVersionCode(getPackageManager().getPackageInfo(GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE, 0 )); this is returning 17785019
I checked for google play store package availability
PackageInfoCompat.getLongVersionCode(getPackageManager().getPackageInfo(GoogleApiAvailability.GOOGLE_PLAY_SERVICES_PACKAGE, 0 )); this is giving package name `com.android.vending` translating to new GooglePlayStorePackageName.
Tried multiple other steps but not able to find exact root cause
That method is deprecated for a reason. Just use the new recommended method.
FCM does not guarantee the generation of these tokens. But when it does generate the token, you are guaranteed to get the new token in the onNewToken method in your FCM service.
class NotificationService: FirebaseMessagingService() {
override fun onNewToken(token: String) {
super.onNewToken(token)
}
}
excerpt from the documentation
/**
There are two scenarios when onNewToken is called:
When a new token is generated on initial app startup
Whenever an existing token is changed
Under #2, there are three scenarios when the existing token is changed:
A) App is restored to a new device
B) User uninstalls/reinstalls the app
C) User clears app data
*/
You can get the generated token on demand anywhere in your app using the new method. This method will only return the token if the initial generation was successful.
fun tryGetToken() {
FirebaseMessaging.getInstance().getToken().addOnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w(FCM_TAG, "Fetching FCM registration token failed", task.exception)
return#addOnCompleteListener
}
fcmToken = task.result
Log.d(FCM_TAG, "FCM fetch success $fcmToken")
}
Related
I am using following code snippet, however I still can't get the pushToken.
private void obtainToken() {
// get token
new Thread() {
#Override
public void run() {
try {
String appId = AGConnectServicesConfig.fromContext(MainActivity.this).getString("client/app_id");
pushtoken = HmsInstanceId.getInstance(MainActivity.this).getToken(appId, "HCM");
if(!TextUtils.isEmpty(pushtoken)) {
Log.i(TAG, "get token:" + pushtoken);
}
} catch (Exception e) {
Log.i(TAG,"getToken failed, " + e);
}
}
}.start();
}
Having a log would be perfect but if everything fine in the logs, no exception and result code from HCM is success, then verify the EMUI version of your device.
If your device's EMUI version is earlier than 10.0, the code you have used will return empty push token. In such case, it is necessary to implement a custom service extending HmsMessageService.
In your AndroidManifest.xml add;
<service
android:name=".CustomPushService"
android:exported="false">
<intent-filter>
<action android:name="com.huawei.push.action.MESSAGING_EVENT" />
</intent-filter>
</service>
Then create following class;
public class CustomPushService extends HmsMessageService {
private static final String TAG = "PushTokenLog";
#Override
public void onNewToken(String token) {
super.onNewToken(token);
Log.i(TAG, "receive token:" + token);
}
}
Last but not least, make sure your device is Huawei :) Most of the features of HMS Core relies on EMUI. Without EMUI, functionality of the functions is not guaranteed for now.
Below is a nice reference to see HMS Core - EMUI relation.
https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/emui_version_dependent_features
Update as per the comment of question owner
The return code 907135000 means that your SDK configurations are not correct. Take your time to check following points;
Check whether the app_id and package_name parameters in the agconnect-services.json file are correct. The app_id and package name should match the app created on AGC. Also, consider re-downloading corresponding agconnect-service.json
Check whether the certificate signature is configured in the build.gradle file.
The fault may be caused by the cache of HMS Core (APK). Uninstall and then reinstall HMS Core (APK), disconnect and reconnect the phone with the Internet, and start the app again.
You can get a pushToken on the premise that the preparation work has been done, especially enabling the push service and setting up the fingerprint. For details, refer to the following link: App development
You can view the logcat with filter "hmssdk" after checking. If there is an exception, you can see the error code, and then you can refer to the document for how to solve the problem. Here is the link:
Show the log if possible so that we can solve the problem together.
For more details, you can refer to the document about how to get pushToken.
If the EMUI version is 10.0 or later on a Huawei device, a token will be returned through the getToken method. If the getToken method fails to be called, HUAWEI Push Kit automatically caches the token request and calls the method again. A token will then be returned through the onNewToken method.
If the EMUI version on a Huawei device is earlier than 10.0 and no token is returned using the getToken method, a token will be returned using the onNewToken method.
Similar issue, getting an error:
GET token failed, com.huawei.hms.common.ApiException: 907122036: no right
Solution:
AppGallery Connect
Project
Push Kit
Enable
Done
I got the same error. I fixed the problem. If you work in more than one medium, check the flavor files. Make sure it's correct.
Multiple Flavors Document :
https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-config-flavor
Maybe a little bit late, but still.
getToken fails because AGConnectServicesConfig.getString("client/app_id"); returns null, which you pass into getToken method.
In the recent Huawei Services version, to get appId you should ask for /client/app_id, not client/app_id string like this:
String appId = AGConnectServicesConfig.fromContext(MainActivity.this).getString("/client/app_id");
Everything else stays the same. But I would recommend writing if statement to check if appId is null and track that. That will save you time if Huawei decides to change that value again.
How to integrate Push Notification to an already existing android app?
My android app is already available on playstore and now I wants to integrate Push Notification in next release. I have implemented it using FCM and AWS SNS.
Problem is : onNewToken method of FirebaseMessagingService will get called only when we installed the app freshly. But when we update it onNewToken method never gets call. So we cannot register the token on AWS portal while updating the app. Experts please advise how to implement this in existing app?
// Use this in your splashscreen or dashboard view.
FirebaseInstanceId.getInstance().instanceId.addOnCompleteListener { task ->
if (!task.isSuccessful)
return#addOnCompleteListener
if(prefs.pushNotificationToken == "") {
//log the token
prefs.pushNotificationToken = task.result?.token?.trim() ?: ""
//send user push notification token to the server(use Patch instead of Post)
}
}
Doing this, both old and new user will have "pushNotificationToken" in prefs to be empty. Thus, we can fetch the token any time from firebase and send it to the backend.Or, also first we can check for token in our prefs and then only ask to firebase for token.
Problem is : onNewToken method of FirebaseMessagingService will get
called only when we installed the app freshly. But when we update it
onNewToken method never gets call. So we cannot register the token on
AWS portal while updating the app. Experts please advise how to
implement this in existing app?
You can call
FirebaseInstanceId.getInstance().getToken(senderId,"FCM");
At anytime to get an instance id to push to your server, this is a blocking call so make sure to do it on a background thread
In the example OutlookQuickStart for Android works fine in the first request after logon().Now I want to keep connect my app to that user and continue checking for new emails.. How can I re use the access token and build the request to check for new emails? Do I have to save the access token, refresh token ?
How I can refresh the token in Android if it is expired.
According to the documentation for the auth library at https://github.com/AzureAD/azure-activedirectory-library-for-android, the library caches the token and refresh token for you. So you would just use acquireTokenSilentSync to get the token each time you need it. That function would return the current token from the cache if it is still valid, and would refresh it if it is expired.
UPDATE: I've taken a closer look at the sample you're using and the Outlook SDK that it uses. The key thing here is the DependencyResolver object. You pass that object to the OutlookClient constructor. Then anytime you make an API call with that OutlookClient, it just calls the getCredentials override that you supply when you create the DependencyResolver.
So as the sample stands, you should be able to make multiple calls through that OutlookClient without having to change it at all. However, after an hour, when the access token expires, calls will start to fail. The fix for that would be to change the getCredentials override to always call acquireTokenSilentSync. Something like:
#Override
public Credentials getCredentials() {
logger.debug("getCredentials in resolver called");
AuthenticationResult result = mAuthContext.acquireTokenSilentSync(
scopes,
getResources().getString(R.string.AADClientId),
UserIdentifier.getAnyUser());
logger.debug("AcquireTokenSilentSync SUCCESS");
logger.debug("Token expires: ", result.getExpiresOn());
logger.debug("Token: ", result.getAccessToken());
return new OAuthCredentials(result.getAccessToken());
}
Caveat: I'm unable to run this code to validate it due to problems getting the Android emulator running on my dev machine :(.
I am creating an application that uses goolg's firebase as its push notification service. I am just using the push notifications server and DON'T WANT TO use its other services (such as database, authentication and etc).
In my application, a user that signs up can have multiple devices and thus multiple firebase tokens. The problem occurs when onRefreshToken is called. I should know which device the user is using and update that specific device's token in my database. So I should know a unique identifier for each device that does not change in device's lifetime.
I was wondering if there are any better ways to tackle this problem and if not, I am so confused about an android unique ID. The two options are Settings.Secure.ANDROID_ID which may have some issues (so I am not sure if I should use it) and TelephonyManger.getDeviceId() which return null sometimes and for devices that have no sim slots (such as tablets).
So what do you suggest me to do?
This answer has 2 solutions:
1) Using ANDROID_ID as a Device Identifier.
2) Using cloud functions and FCM to determine a bad token, after 1st message sent to it.
1)
Try Settings.Secure.ANDROID_ID to get the Device ID.
String deviceAppUID = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
if (deviceAppUID!=null) {
setFirebaseTokenInServer(deviceAppUID, FirebaseDeviceToken);}
Android is changing ANDROID_ID in 8.0 (API 26), so that it will apply only to the combination of specific app, user, and deviceId. When that happens, you aren't really getting a Device ID that is ALWAYS true for every app, but rather, you are getting a Device ID for your specific App, with its specific signing key, on your specific devise. You can uninstall the app, and reinstall the app, and the ANDROID_ID will be the same as it was before.
That makes it safer for the user, but would still allow you to persist the Firebase Device Tokens in case the registration token changes for these following reasons:
App deletes Instance ID
App is restored on a new device User
uninstalls/reinstall the app
User clears app data
Then in Firebase, you can save these as:
DeviceTokens:
deviceID: firebaseDeviceToken
deviceID: firebaseDeviceToken
2) Using Firebase Cloud Messaging and Firebase Google Cloud Functions to determine a bad token.
If you also use Cloud Functions to send the FCM to each listed deviceToken (as is necessary if multiple devices), you can check for a response after the first notification is sent. If there is an error, and the error returns a message stating the token is bad, you can handle the error and then delete it from the Server.
No device ID is necessary, much safer for user.
Sends 1 FCM to a bad token, but not again after that
If Firebase Team changes its errors or error messages, you will have to change the function or this will no longer work.
See this answer for more details:
Handling refresh tokens for FCM device groups
In class you are extending "FirebaseInstanceIdService" in method "onTokenRefresh" you can get it like :
FirebaseInstanceId.getInstance().getId()
You can get the device token by extending your custom class with FirebaseInstanceIdService Class
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseInsIDService";
#Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Device Token: " + refreshedToken);
}
}
Add service in your AndroidManifest.xml file
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
Now, you can use the device token to send notifications on the user mobile.
I strongly recommend retrieving the latest updated registration token.
The registration token may change when:
The app deletes Instance ID
The app is restored on a new device
The user uninstalls/reinstall the app The user clears app data.
When you need to retrieve the current token, call FirebaseInstanceId.getInstance().getToken(). This method returns null if the token has not yet been generated.
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