how to fix hostname verifier interface while uploading app to google play console - android-security

I updated the version code and version name of app but i got warning message from google play
Your app(s) are using an unsafe implementation of the HostnameVerifier interface. You can find more information about how resolve the issue in this Google Help Center article.
thanks in advance

Had plenty of versions we assumed were right in this Flutter project and had been rejected over and over again but finally, we figured out. 
Pubspec.yaml  - previous version.
flutter_html: ^0.11.1
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
http: ^0.12.0+2
provider: ^4.1.3
logger: ^0.7.0+2
shared_preferences: ^0.5.4+6
json_annotation: ^3.0.0
flutter_dotenv: ^2.1.0
flutter_swiper: ^1.1.6
package_info: ^0.4.0+3
get_version: ^0.2.0+1
uuid: ^2.0.4
flappy_translator: ^1.2.2
flutter_circular_chart: ^0.1.0
percent_indicator: "^2.1.1"
intl: ^0.16.0
bezier_chart: ^1.0.15
charts_flutter: ^0.8.1
fl_chart: ^0.6.0
flutter_native_timezone: ^1.0.4
url_launcher: ^5.7.8
permission_handler: ^5.0.1+1
onesignal_flutter: 2.6.1
flutter_braintree: 1.1.0
after_layout: ^1.0.7+2
flutter_svg: ^0.19.0
custom_switch_button: 0.5.0
wc_flutter_share: ^0.2.2
esys_flutter_share: ^1.0.2
just_audio: ^0.4.4
cached_network_image: 2.2.0+1
sqflite: ^1.3.1
cupertino_icons: ^0.1.2
in_app_purchase: 0.3.4+5
Hostname verification
HttpsURLConnection.setDefaultHostnameVerifier { hostname, arg1 ->      
val herokuPattern = “PROJECTNAME-(dev|stg|prd)\\.herokuapp.com”.toRegex()      
val awsPattern = “PROJECTNAME-(dev|stg|prd)\\.s3\\..*\\.amazonaws.com”.toRegex()
      herokuPattern.containsMatchIn(hostname)
||      awsPattern.containsMatchIn(hostname)
||        hostname.equals(“onesignal.com”, ignoreCase = true)
||        hostname.equals(“api.braintreegateway.com”, ignoreCase = true)
||        hostname.equals(“payments.braintree-api.com”, ignoreCase = true)
||        hostname.equals(“api.sandbox.braintreegateway.com”, ignoreCase = true)
||        hostname.equals(“payments.sandbox.braintree-api.com”, ignoreCase = true)    }
After the first try we got this message as you do:
HostnameVerifierYour app(s) are using an unsafe implementation of
the HostnameVerifier interface. You can find more information about
how resolve the issue in this Google Help Center article.
Then we turned to the ‘Google Development/Developer support’ with the question what should we do as there was lack of information about the problem. After a week we received a message and had a better understanding not how can we find the solution but at least where to find it.
Vulnerable implementation of HostnameVerifier:
Lf/a/a/a/a/l/e$a;
Lf/a/a/a/a/l/f$a;
To properly handle hostname verification, you'll need to change the verify method in your custom HostnameVerifier interface to return false whenever the hostname of the server does not meet your expectations. You may refer to the Alerts page of your Play Console for additional guidance.
Exposed Google Cloud Platform (GCP) API key(s). 
com.onesignal.h2->dLocations of exposed GCP API keys in your app can be found in the Play Console notification for your app. You may refer to this Help Center page to fix the leaked credentials vulnerability issue.
OneSignal related info was pretty clear, after a short search we found a similar comment that recommends setting the version number (from 2.6.1) to onesignal_flutter: 2.6.2. OneSignal problem solved.
Genuinely, there were two weeks desperate times when we could not find anything for the  Vulnerable implementation problem, nor with the ‘Developer Support’ advice:
“Although I’m happy to answer any questions about managing your apps
on the Google Play Store, our team isn’t trained to provide technical
support for app development questions. For help developing Android
apps, I recommend using our Android Developers site. The site has
technical documentation, the Android SDK, and tips for distributing
your apps.” - GooglePlay Developer Support.
Eventually, we had to look after vulnerability issues regarding the plugins we used and found a Braintree issue that suggests setting the version number to flutter_braintree: 1.1.0+1. 
After these two versions-number upgrade (Onesignal, Braintree) got no more message about the HostameVerifier issue, all seem well. 
Pubspec.yaml
flutter_html: ^0.11.1
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
http: ^0.12.0+2
provider: ^4.1.3
logger: ^0.7.0+2
shared_preferences: ^0.5.4+6
json_annotation: ^3.0.0
flutter_dotenv: ^2.1.0
flutter_swiper: ^1.1.6
package_info: ^0.4.0+3
path_provider: 1.6.24
get_version: ^0.2.0+1
uuid: ^2.0.4
flappy_translator: ^1.2.2
flutter_circular_chart: ^0.1.0
percent_indicator: "^2.1.1"
intl: ^0.16.0
bezier_chart: ^1.0.15
charts_flutter: ^0.8.1
fl_chart: ^0.6.0
flutter_native_timezone: ^1.0.4
url_launcher: ^5.7.8
permission_handler: ^5.0.1+1
onesignal_flutter: 2.6.2
flutter_braintree: 1.1.0+1
after_layout: ^1.0.7+2
flutter_svg: ^0.19.0
custom_switch_button: 0.5.0
wc_flutter_share: ^0.2.2
esys_flutter_share: ^1.0.2
just_audio: ^0.5.7
cached_network_image: 2.2.0+1
sqflite: ^1.3.1
cupertino_icons: ^0.1.2
in_app_purchase: 0.3.4+5

Surely you have a code similar to this
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
return true;
}
};
Or:
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
public boolean verify(String hostname, SSLSession session) {
return true;
}});
Replace by:
HostnameVerifier hostnameVerifier = new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
if(myHostNameToVerify==hostname || myOtherHostNameToVerify == hostname) {
return true;
} else {
return false;
}
}
};
Or:
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
public boolean verify(String hostname, SSLSession session) {
if(myHostNameToVerify==hostname ||
myOtherHostNameToVerify == hostname) {
return true;
} else {
return false;
}
}});
If you use a SSL verifier, I recommend to add code similar to this:
#Override
public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
// the main thing is to show dialog informing user
// that SSL cert is invalid and prompt him to continue without
// protection: handler.proceed();
// or cancel: handler.cancel();
String message;
switch(error.getPrimaryError()) {
case SslError.SSL_DATE_INVALID:
message = ResHelper.getString(R.string.ssl_cert_error_date_invalid);
break;
case SslError.SSL_EXPIRED:
message = ResHelper.getString(R.string.ssl_cert_error_expired);
break;
case SslError.SSL_IDMISMATCH:
message = ResHelper.getString(R.string.ssl_cert_error_idmismatch);
break;
case SslError.SSL_INVALID:
message = ResHelper.getString(R.string.ssl_cert_error_invalid);
break;
case SslError.SSL_NOTYETVALID:
message = ResHelper.getString(R.string.ssl_cert_error_not_yet_valid);
break;
case SslError.SSL_UNTRUSTED:
message = ResHelper.getString(R.string.ssl_cert_error_untrusted);
break;
default:
message = ResHelper.getString(R.string.ssl_cert_error_cert_invalid);
}
mSSLConnectionDialog = new MaterialDialog.Builder(getParentActivity())
.title(R.string.ssl_cert_error_title)
.content(message)
.positiveText(R.string.continue_button)
.negativeText(R.string.cancel_button)
.titleColorRes(R.color.black)
.positiveColorRes(R.color.main_red)
.contentColorRes(R.color.comment_grey)
.backgroundColorRes(R.color.sides_menu_gray)
.onPositive(new MaterialDialog.SingleButtonCallback() {
#Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
mSSLConnectionDialog.dismiss();
handler.proceed();
}
})
.onNegative(new MaterialDialog.SingleButtonCallback() {
#Override
public void onClick(MaterialDialog materialDialog, DialogAction dialogAction) {
handler.cancel();
}
})
.build();
mSSLConnectionDialog.show();
Google from mid-2016 and early 2017 require a more secure code.

Related

Firebase.Functions.FunctionsException: UNAUTHENTICATED, Can't Call cloud function even after user is authenticated

Unity editor version: 2021.3.9f
Firebase Unity SDK version: 9.6.0
Source you installed the SDK: FirebaseFunctions(.unitypackage or Unity Package Manager)
Problematic Firebase Component: FirebaseFunctions
Other Firebase Components in use: FirebaseFunctions ,Auth
Additional SDKs you are using:
Platform you are using the Unity editor on: Windows
Platform you are targeting:Android
Scripting Runtime: IL2CPP
[REQUIRED] Please describe the issue here:
Firebase.Functions.FunctionsException: UNAUTHENTICATED
at Firebase.Functions.HttpsCallableReference.b__9_0 (System.Threading.Tasks.Task`1[TResult] task) [0x00000] in <00000000000000000000000000000000>:0
I m trying to user firebase functions with unity. I have added firebase auth that is working perfectly. After auth I am calling my firebase function using this code below
`void FirebaseFucntionTest()
{
var data = new Dictionary<string, object>();
Debug.Log("Calling");
FirebaseFunctions functions = FirebaseFunctions.DefaultInstance;
Debug.Log(FirebaseAuth.DefaultInstance.CurrentUser.DisplayName);
var function = functions.GetHttpsCallable("testunity");
function.CallAsync(data).ContinueWith((task) =>
{
if (task.IsFaulted)
{
foreach (var inner in task.Exception.InnerExceptions)
{
if (inner is FunctionsException)
{
var e = (FunctionsException)inner;
var code = e.ErrorCode;
var message = e.ToString();
Debug.Log(message);
Debug.Log(code);
}
}
}
else
if (task.IsCompleted)
{
Debug.Log((string)task.Result.Data);
}
});
}`
I am holding a reference to auth using: auth = FirebaseAuth.DefaultInstance; and after this I am calling my function that runs the cloud function, as you can see in the debug statement at the start of the method I getting the currentusername successfully, but after code moves to cloud function call it is giving me exception of unauthenticated:

iOS Push Notifications with Azure Notification Hub

I am having absolutely no luck getting push notifications to work in iOS in a Xamarin Forms project.
In AppDelegate.cs, I am calling the following in the FinishedLaunching override:
MSNotificationHub.Start("Endpoint=sb://[redacted].servicebus.windows.net/;SharedAccessKeyName=DefaultListenSharedAccessSignature;SharedAccessKey=[redacted]",
"[redacted]");
After the user logs in further in the app lifecycle, I also register the user with their user tag as follows:
public async Task UpdateTags(string token)
{
await Task.Run(() =>
{
try
{
// No point registering tags until the user has signed in and we have a device token
if (CurrentAccount == null)
{
Console.WriteLine($"UpdateTags cancelled: Account is null");
return;
}
var tag = $"user:{CurrentAccount.UserName}";
Console.WriteLine($"Registering tag: {tag}");
MSNotificationHub.AddTag(tag);
}
catch (Exception e)
{
Console.WriteLine($"Error registering tag: {e.ToString()}");
}
});
}
I have properly configured the Apple (APNS) settings in the notification hub, using the Token authentication mode (verified the four fields several times). The certificate (signing identity) is "iOS Distribution", the identifier bundle matches exactly what I have in the configuration (not using wildcard), the key has Apple Push Notifications service (APNs) enabled, and the provisioning profile has Platform: iOS and Type: App Store.
I pushed the application to TestFlight, as I don't have access to a physical Mac (we use a Cloud mac for development). When I view the device logs from my personal iPhone with the app installed, I see the following when I run it:
<Notice>: Registered for push notifications with token: [redacted]
<Notice>: Registering tag: user:[redacted]
There are no instances of "Error registering tag" or "UpdateTags cancelled" in the logs at all, which tells me that the method calls are succeeding without an exception. However, when I attempt to send a test notification to either a blank/empty tag, or the specific tag for my test user, no notifications are received and the messaging simply shows "Message was successfully sent, but there were no matching targets."
Also, when I pull all of the registrations with var registrations = await hub.GetAllRegistrationsAsync(0);, I only see the FCM (Firebase/Android) registrations from my successful testing on the Android side of things.
I am at a complete loss and have hit a wall, as there are no exceptions being thrown, and seemingly no way to troubleshoot what is going on behind the scenes.
This is also my 2nd attempt - I was using a more complex SBNotificationHub implementation and had the same results - no exceptions and everything looked fine at face value.
Thanks to a comment pointing to another question, I have determined that all I needed to do was to ensure that my tag registration ran on the main UI thread. My updated code below is working:
public async Task UpdateTags(string token)
{
await Task.Run(() =>
{
Device.BeginInvokeOnMainThread(() =>
{
try
{
// No point registering tags until the user has signed in and we have a device token
if (CurrentAccount == null)
{
Console.WriteLine($"UpdateTags cancelled: Account: {Trico.OrbitalApp.App.CurrentAccount};");
return;
}
var tag = $"user:{CurrentAccount.UserName}";
Console.WriteLine($"Registering tag: {tag}");
MSNotificationHub.AddTag(tag);
}
catch (Exception e)
{
Console.WriteLine($"Error registering device: {e.ToString()}");
}
});
});
}
You can try implementing the MSInstallationLifecycleDelegate interface which will allow you to check and see if the installation is being saved on the back end with either success or failure.
// Set a listener for lifecycle management
MSNotificationHub.SetLifecycleDelegate(new InstallationLifecycleDelegate());
// Implementation of the lifecycle listener.
public class InstallationLifecycleDelegate : MSInstallationLifecycleDelegate
{
public InstallationLifecycleDelegate()
{
}
public override void DidFailToSaveInstallation(MSNotificationHub notificationHub, MSInstallation installation, NSError error)
{
Console.WriteLine($"Save installation failed with exception: {error.LocalizedDescription}");
}
public override void DidSaveInstallation(MSNotificationHub notificationHub, MSInstallation installation)
{
Console.WriteLine($"Installation successfully saved with Installation ID: {installation.InstallationId}");
}
}

Getting no Firebase Cloud Messaging Token in Xamarin iOS when started from Windows PC on an iPhone test device. [Updated]

Summary
I try to use the Firebase Cloud Messaging to send Notification to a xamarin app for Android and iOS. Android already works.
I followed mainly the guide in this answer (and several others):
https://stackoverflow.com/a/60001529/8542816
I get this error message:
An error occurred: 'Object reference not set to an instance of an object.'. Callstack: ' at Productname.iOS.AppDelegate.FinishedLaunching (UIKit.UIApplication app, Foundation.NSDictionary options) [0x0008c] in C:\Path\repos\Productname\Productname.iOS\AppDelegate.cs:56
Setting:
I develope on a Windows PC and debug on a physical IPhone with iOS 14.4.2
Xamarin.Firebase.iOS.CloudMessaging 4.7.1
Xamarin.Firebase.iOS.Core 6.10.4
Xamarin.Firebase.iOS.Installations 1.7.0
Xamarin.Firebase.iOS.InstanceID 4.8.0
What I've tried
While debugging i realized that Messaging.SharedInstance is null. When i searched for it i came across 2 possible solutions:
Downgrading to an older FCM Framework (I tried Xamarin.Firebase.iOS.CloudMessaging 3.1.2 and corresponding downgrades of other Framework dependincies)
Problems with GoogleService-Info.plist (Triple checked the values, redownloaded)
Code Snippets
GoogleService-Info.plist (is set as BundleResource):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CLIENT_ID</key>
<string>123456789012-replaced123456789012345678901234.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.123456789012-replaced123456789012345678901234</string>
<key>API_KEY</key>
<string>HereIsThe_KeyWhichIReplaced123456789012</string>
<key>GCM_SENDER_ID</key>
<string>123456789012</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>de.companyname.productname</string>
<key>PROJECT_ID</key>
<string>productname-a43fc</string>
<key>STORAGE_BUCKET</key>
<string>productname-a43fc.appspot.com</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:123456789012:ios:0123456789abcdef012345</string>
<key>DATABASE_URL</key>
<string>https://productname-a43fc.firebaseio.com</string>
</dict>
</plist>
AppDelegate.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using Firebase.InstanceID;
using Firebase.Core;
using Firebase.CloudMessaging;
using Foundation;
using Productname.Models.Global;
using UIKit;
using UserNotifications;
namespace Productname.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IMessagingDelegate, IUNUserNotificationCenterDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
// Register your app for remote notifications.
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.Current.Delegate = (IUNUserNotificationCenterDelegate)this;
var authOptions = UNAuthorizationOptions.Alert | UNAuthorizationOptions.Badge | UNAuthorizationOptions.Sound;
UNUserNotificationCenter.Current.RequestAuthorization(authOptions, (granted, error) => {
Console.WriteLine(granted);
});
}
else
{
// iOS 9 or before
var allNotificationTypes = UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound;
var settings = UIUserNotificationSettings.GetSettingsForTypes(allNotificationTypes, null);
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
UIApplication.SharedApplication.RegisterForRemoteNotifications();
Firebase.Core.App.Configure();
Messaging.SharedInstance.Delegate = this; // <-- The Execution crashes here
InstanceId.SharedInstance.GetInstanceId(InstanceIdResultHandler);
return base.FinishedLaunching(app, options);
}
[Export("messaging:didReceiveRegistrationToken:")]
public void DidReceiveRegistrationToken(Messaging messaging, string fcmToken)
{
//Never Fired
FirebaseToken.Singleton.Token = fcmToken;
Console.WriteLine($"Firebase registration token: {fcmToken}");
LogInformation(nameof(DidReceiveRegistrationToken), $"Firebase registration token: {fcmToken}");
}
void InstanceIdResultHandler(InstanceIdResult result, NSError error)
{
if (error != null)
{
LogInformation(nameof(InstanceIdResultHandler), $"Error: {error.LocalizedDescription}");
return;
}
LogInformation(nameof(InstanceIdResultHandler), $"Remote Instance Id token: {result.Token}");
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
HandleMessage(userInfo);
// Print full message.
LogInformation(nameof(DidReceiveRemoteNotification), userInfo);
completionHandler(UIBackgroundFetchResult.NewData);
}
[Export("messaging:didReceiveMessage:")]
public void DidReceiveMessage(Messaging messaging, RemoteMessage remoteMessage)
{
// Handle Data messages for iOS 10 and above.
HandleMessage(remoteMessage.AppData);
LogInformation(nameof(DidReceiveMessage), remoteMessage.AppData);
}
void HandleMessage(NSDictionary message)
{
}
public static void ShowMessage(string title, string message, UIViewController fromViewController, Action actionForOk = null)
{
var alert = UIAlertController.Create(title, message, UIAlertControllerStyle.Alert);
alert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Default, (obj) => actionForOk?.Invoke()));
fromViewController.PresentViewController(alert, true, null);
}
void LogInformation(string methodName, object information) => Console.WriteLine($"\nMethod name: {methodName}\nInformation: {information}");
}
}
Remote Notification in Info.plist is activated.
When i comment out the line Messaging.SharedInstance.Delegate = this; the app will start and work perfectly besides missing notification functionality.
Any hints or further debug approaches much appreciated.
Update
I managed to get my hands on a MacBook. I started the App from the MacBook via Visual Studio on the very same testing iPhone Device and it worked instantly. I would like to keep the developing and testing process on my windows PC though. Is it even possible?

Unable to recieve notifications from Firebase. Unregistered registration token

I have setup Firebase in an Xamarin.Android application, following this guide:
https://developer.xamarin.com/guides/android/application_fundamentals/notifications/remote-notifications-with-fcm/
It seemed to work at first, with receiving notifications without problems. Then I tried to update the code and redeploy to Device and afterwards nothing happened. Since I have been unable to receive any notifications from the Firebase Console.
I have tried
Uninstall application and deploy again
Download new google-service.json from Firebase Console
Delete and setup new Firebase Project, and updating google-service.json for this
When the application is newly deployed, and I try to send a notification, nothing happens. After an update and redeploy I get the error "Unregistered registration token" in the console.
Before setting up a new Firebase Project I would get the following error when trying to subscribe to a topic
"INVALID_PARAMETERS", but this problem has seen to disappear after creating a new project. Now then subscribing to Topic, it appears in the Console after some time. But trying to send notifications to the topic, gives no results.
Edit - Added code
MainActivity.cs
[Activity(Label = "<<App Label>>", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
private TextView msgText;
private const string Tag = "MainActivity";
private FirebaseAnalytics _analytics;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
_analytics = FirebaseAnalytics.GetInstance(this);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
msgText = FindViewById<TextView>(Resource.Id.msgText);
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
var value = Intent.Extras.GetString(key);
Log.Debug(Tag, "Key: {0} Value: {1}", key, value);
}
}
IsPlayServicesAvailable();
var logTokenButton = FindViewById<Button>(Resource.Id.logTokenButton);
logTokenButton.Click += delegate {
Log.Debug(Tag, "InstanceID token: " + FirebaseInstanceId.Instance.Token);
};
var subButton = FindViewById<Button>(Resource.Id.subscribeButton);
subButton.Click += SubButton_Click;
}
private void SubButton_Click(object sender, System.EventArgs e)
{
FirebaseMessaging.Instance.SubscribeToTopic("A");
Log.Debug(Tag, "Subscribed to A");
}
public bool IsPlayServicesAvailable()
{
int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.Success)
{
if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
msgText.Text = GoogleApiAvailability.Instance.GetErrorString(resultCode);
else
{
msgText.Text = "This device is not supported";
Finish();
}
return false;
}
else
{
msgText.Text = "Google Play Services is available.";
return true;
}
}
}
StwFirebaseInstanceIdService.cs
[Service]
[IntentFilter(new []{ "com.google.firebase.INSTANCE_ID_EVENT" })]
public class StwFirebaseInstanceIdService : FirebaseInstanceIdService
{
private const string Tag = "StwFirebaseInstanceIdService";
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(Tag, "Refreshed Token: " + refreshedToken);
//SendRegistrationToServer(refreshedToken);
}
private void SendRegistrationToServer(string token)
{
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="<<Package Name>>" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk android:minSdkVersion="16" />
<application android:label="<<App Label>>">
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
</application>
I bumped into the same issue. Try to uninstall the app one more time and before you get the token (old one), call the refresh token to get the refreshed token:
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(TAG, "Refreshed token: " + refreshedToken);
SendRegistrationToServer(refreshedToken);
}
void SendRegistrationToServer(string token)
{ // Your custom implementation depends on your server }
for more information visit Here
The mentioned tutorial is quite good but misses a few points. I couldn't get it to work until I added my app's SHA1 key to the registration. You can get this by the following command:
"C:\Program Files\Java\jdk1.8.0_161\bin\keytool.exe" -list -v -keystore "%USERPROFILE%\AppData\Local\Xamarin\Mono for Android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
You might have to edit the command a bit to match your Java version.
The second thing is that that there seems to be some issues with DEBUG builds. I got this method from somewhere (don't know where now).
private void ConfigureFireBase()
{
#if DEBUG
Task.Run(() =>
{
var instanceId = FirebaseInstanceId.Instance;
instanceId.DeleteInstanceId();
Android.Util.Log.Debug("TAG", "{0} {1}", instanceId?.Token?.ToString(), instanceId.GetToken(GetString(Resource.String.gcm_defaultSenderId), Firebase.Messaging.FirebaseMessaging.InstanceIdScope));
});
// For debug mode only - will accept the HTTPS certificate of Test/Dev server, as the HTTPS certificate is invalid /not trusted
ServicePointManager.ServerCertificateValidationCallback += (o, certificate, chain, errors) => true;
#endif
}
Call it from OnCreate, just after OnCreate like so:
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
ConfigureFireBase();
I'm using Xamarin.Firebase.Messaging (71.1740.0) on Visual Studio for Mac (8.3.11). Testing on older (API 21) and newer (API 28) physical android devices.
First deploy works, new tokens on both devices. A custom REST API call to FCM successful:
{"multicast_id":4160565375891761047,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1576702929647400%320d8b4c320d8b4c"}]}
Changing code, and redeploying in Debug mode to the same devices works fine. Sending from Firebase console works fine, notifications to both devices in same project app in FCM.
After some hours, the custom REST API returns for the older device token (no code change):
{"multicast_id":565695677811030352,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"NotRegistered"}]}
Then after some more hours, the new device token also returns "NotRegistered" from the custom REST API (no code change). Also, the Firebase console no longer sends notifications to devices.
The only way I got the custom REST API and Firebase console to correctly send notifications once this happens, is to use Visual Studio to manually remove the app from the device:
Visual Studio Menu > Build > Remove from Device
Deploying again will install the app as though it were new to the device, and request a fresh token.
Note: Already unchecked "Preserve data/cache between application deploys" in the Visual Studio preferences, with no significant change. Clearing the app's data/cache doesn't work either.
Used latest Microsoft code from GitHub (Dec 2019): https://github.com/xamarin/GooglePlayServicesComponents/tree/master/samples/com.google.firebase/firebase-messaging

SharedPreferences empty after restarting service / device

As I am working with Google Firebase for Push Notifications, I want to save the Instance Token to the SharedPreferences. Unfortunately, whenever the token gets refreshed and want to check the previous one from SharedPreferences, they are empty...
Is it because I am using a Service here?
public class MyFirebaseIIDService : FirebaseInstanceIdService
{
public override void OnTokenRefresh()
{
var sharedPreferences = PreferenceManager.GetDefaultSharedPreferences(this);
var sharedPreferencesEditor = sharedPreferences.Edit();
// Get Firebase Instance Token
var refreshedToken = FirebaseInstanceId.Instance.Token;
// Check if a Firebase Instance Token has been registered before and unregister it
var oldToken = sharedPreferences.GetString("FirebaseInstanceToken", null);
if (oldToken != null) // is ALWAYS null :(
{
// Unregister old token...
}
// Save the Firebase Instance Token locally
sharedPreferencesEditor.PutString("FirebaseInstanceToken", refreshedToken);
sharedPreferencesEditor.Apply();
// At this point, the SharedPreferences have to token saved.
// Next time, the app reaches this point, it is gone...
}
}
Sorry for the syntax confusion, I use Xamarin, so this is C# but it should not make any difference.
I don't know how it work in xamarin but in native android getSharedPreferences from service context maybe wrong. You should use only applicationContext or MODE_MULTI_PROCESS when open shared preferences.
You can see similar question here.
Xamarin and C# also have this mode when your open some file, so i think exactly the same with preferences. Try some like this instead of using GetDefaultSharedPreferences:
ISharedPreferences prefs = Application.Context.GetSharedPreferences ("PREF_NAME", FileCreationMode.MultiProcess);
Did you uninstall your app to refresh you token? If yes I think you can not get the old token because the SharedPreferences is cleared when you uninstall your app.
I have tried java code to save the token :
public void onTokenRefresh() {
// Get updated InstanceID token.
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d("Mike", "Refreshed token: " + refreshedToken);
SharedPreferences mSharedPreferences = getSharedPreferences("hello",0);
String oldToken = mSharedPreferences.getString("Token",null);
if(oldToken == null)
{
Log.d("Mike", "oldToken: " + null);
}
SharedPreferences.Editor mEditor = mSharedPreferences.edit();
mEditor.putString("Token", refreshedToken);
mEditor.commit();
// TODO: Implement this method to send any registration to your app's servers.
//sendRegistrationToServer(refreshedToken);
}
At first time you install your app you will get the token and save it in the SharedPreferences, next time you open your app and show the old token in the textview then you can find your token has been saved , And do not uninstall your app:
TextView tv1 = (TextView)findViewById(R.id.tv1);
SharedPreferences mSharedPreferences = getSharedPreferences("hello",0);
String oldToken = mSharedPreferences.getString("Token",null);
tv1.setText(oldToken);
It works. But when you uninstall your app the textview shows null.
I solved the problem and it turned out, that my code was working correctly but behaved strange on my test device.
After re-building the application, SharedPreferences have been cleared, although I checked the Preserve application data/cache on device between deploys option in Visual Studio. That was because my physical testing device was rooted and did not accept this option.
When trying on an unrooted device, everything worked as expected.

Resources