Monday 4 January 2021

Xamarin Forms - Android - Remote Push Notifications - hub.Register(); exception in release build fine in debug

So working on this reminds me of why I absolutely hate remote push notifications. Xamarin Forms solution, specifically trying to get remote push notifications to work in an ad-hoc distributed .apk file.

Here's the relevant code:

FireBaseService in the Android Native project:

using Android.App;
using Android.Content;
using Android.Util;
using AndroidX.Core.App;
using Firebase.Messaging;
using Newtonsoft.Json;
using Mobile.Client.Configuration;
using System;
using System.Linq;
using WindowsAzure.Messaging;
using Xamarin.Forms;

namespace Mobile.Client.Droid.Notifications
{
    [Service]
    [IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
    [IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
    public class FirebaseService : FirebaseMessagingService
    {
        const string TAG = "FirebaseService";

        public override void OnMessageReceived(RemoteMessage message)
        {
            Log.Debug(TAG, "From: " + message.From);
            if (message.GetNotification() != null)
            {
                //These is how most messages will be received
                Log.Debug(TAG, "Notification Message Body: " + message.GetNotification().Body);
                SendNotification(message.GetNotification().Body);
            }
            else
            {
                //Only used for debugging payloads sent from the Azure portal
                SendNotification(message.Data.Values.First());
            }
        }

        public override async void OnNewToken(string token)
        {
            Log.Info(TAG, $"Registration Token: {token}");
            App.Current.Properties["NotificationToken"] = token;
            await App.Current.SavePropertiesAsync();
            SendRegistrationToServer(token);
        }

        private void SendNotification(string messageBody)
        {
            var intent = new Intent(this, typeof(MainActivity));
            intent.AddFlags(ActivityFlags.ClearTop);
            var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);

            var notificationChannelName = AppSettingsManager.Settings["NotificationChannelName"];

            var notificationBuilder = new NotificationCompat.Builder(this, notificationChannelName);

            notificationBuilder.SetContentTitle("Notification")
                        .SetSmallIcon(Resource.Drawable.nndc_logo)
                        .SetContentText(messageBody)
                        .SetAutoCancel(true)
                        .SetAllowSystemGeneratedContextualActions(true)
                        .SetShowWhen(false)
                        .SetContentIntent(pendingIntent);

            var notificationManager = NotificationManager.FromContext(this);

            notificationManager.Notify(0, notificationBuilder.Build());

            MessagingCenter.Send(App.Current, "NotificationRecieved");
        }

        private void SendRegistrationToServer(string token)
        {
            try
            {
                var notificationHubName = AppSettingsManager.Settings["NotificationHubName"];
                var listenConnectionString = AppSettingsManager.Settings["ListenConnectionString"];
                var subscriptionTags = AppSettingsManager.Settings["SubscriptionTags"]?.Split(",");
                var fCMTemplateBody = AppSettingsManager.Settings["FCMTemplateBody"];

                NotificationHub hub = new NotificationHub(notificationHubName, listenConnectionString, this);
                Log.Info(TAG, $"Created hub object: {hub.NotificationHubPath}");

                // register device with Azure Notification Hub using the token from FCM
                Registration registration = hub.Register(token, subscriptionTags);
                Log.Info(TAG, $"Registered token and tags: {registration.PNSHandle}");

                // subscribe to the SubscriptionTags list with a simple template.
                string pnsHandle = registration.PNSHandle;
                Log.Info(TAG, $"PNS Handle: {pnsHandle}");

                TemplateRegistration templateReg = hub.RegisterTemplate(pnsHandle, "defaultTemplate", fCMTemplateBody, subscriptionTags);

                Log.Info(TAG, $"Registered template: {templateReg.NotificationHubPath}");
            }
            catch (Exception e)
            {
                Log.Info(TAG, $"PNS REGISTRATION EXCEPTION: {JsonConvert.SerializeObject(e)}");
                //Log.Error("DEBUG", $"Error registering device: {e.Message}");
            }
        }
    }
}

Android Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.android.mobile" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="28" />
    <!--android:extractNativeLibs="true" had to be added below due to a VS/Xamarin Bug: https://github.com/xamarin/xamarin-android/issues/4990-->
    <application android:label="APPNAME" android:icon="@mipmap/icon" android:extractNativeLibs="true" android:roundIcon="@mipmap/icon_round">
        <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>
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
  <uses-permission android:name="android.permission.WAKE_LOCK" />
  <uses-permission android:name="android.permission.GET_ACCOUNTS"/>

  <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
  <uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" />

  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
    <uses-permission android:name="com.samsung.android.providers.context.permission.WRITE_USE_APP_FEATURE_SURVEY" />
    <uses-permission android:name="android.permission.USE_BIOMETRIC" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
</manifest>

Anyway as the title says, this works fine in Debug, it registers with no issue and sends push notifications from azure absolutely fine. Now the FCM key is the same key for sandbox or production as far as I can tell, so I don't think it's that, but feel free to correct me if I'm wrong.

If I run this in release, or install it ad-hoc as an apk file installation I get the following error:

{
  "JniPeerMembers": {
    "ManagedPeerType": "WindowsAzure.Messaging.NotificationHubException, Xamarin.Azure.NotificationHubs.Android, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
    "JniPeerTypeName": "com/microsoft/windowsazure/messaging/NotificationHubException",
    "JniPeerType": {
      "PeerReference": {
        "Handle": {
          "value": 15194
        },
        "Type": 2,
        "IsValid": true
      },
      "Name": "com/microsoft/windowsazure/messaging/NotificationHubException"
    },
    "InstanceMethods": {},
    "InstanceFields": {},
    "StaticMethods": {},
    "StaticFields": {}
  },
  "StatusCode": 400,
  "StackTrace": "  
  at Java.Interop.JniEnvironment+InstanceMethods.CallObjectMethod (Java.Interop.JniObjectReference instance, 
  Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0006e] in <3f19c9fc57a34ac9a473579164f8755e>:0 \n  
  at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualObjectMethod (System.String encodedMember, 
  Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0002a] in <3f19c9fc57a34ac9a473579164f8755e>:0 \n  
  at WindowsAzure.Messaging.NotificationHub.Register (System.String pnsHandle, System.String[] tags) [0x00043] in <15ee46979411457bb0abfed951cc2b1e>:0 \n  
  at NorthNorfolk.Mobile.Client.Droid.Notifications.FirebaseService.SendRegistrationToServer (System.String token) [0x00078] in <f0d16bc144b04854b79558630f62c467>:0 \n  
  --- End of managed WindowsAzure.Messaging.NotificationHubException stack trace 
  
  ---\ncom.microsoft.windowsazure.messaging.NotificationHubException\n\tat 
  com.microsoft.windowsazure.messaging.Connection.executeRequest(Connection.java:254)\n\tat 
  com.microsoft.windowsazure.messaging.Connection.executeRequest(Connection.java:170)\n\tat 
  com.microsoft.windowsazure.messaging.Connection.executeRequest(Connection.java:130)\n\tat 
  com.microsoft.windowsazure.messaging.NotificationHub.upsertRegistrationInternal(NotificationHub.java:446)\n\tat 
  com.microsoft.windowsazure.messaging.NotificationHub.registerInternal(NotificationHub.java:410)\n\tat 
  com.microsoft.windowsazure.messaging.NotificationHub.register(NotificationHub.java:148)\n\tat 
  com.northnorfolk.mobile.FirebaseService.n_onNewToken(Native Method)\n\tat 
  com.northnorfolk.mobile.FirebaseService.onNewToken(FirebaseService.java:38)\n\tat 
  com.google.firebase.messaging.FirebaseMessagingService.zzd(Unknown Source:86)\n\tat 
  com.google.firebase.iid.zzg.run(Unknown Source:4)\n\tat 
  java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n\tat 
  java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n\tat 
  com.google.android.gms.common.util.concurrent.zza.run(Unknown Source:6)\n\tat 
  java.lang.Thread.run(Thread.java:919)\n",
  "JniIdentityHashCode": 19017287,
  "PeerReference": {
    "Handle": {
      "value": 19298
    },
    "Type": 2,
    "IsValid": true
  },
  "Handle": {
    "value": 19298
  },
  "Message": null,
  "Data": {},
  "InnerException": null,
  "Source": "mscorlib",
  "HResult": -2146233088
}

This is thrown from the FirebaseService class from this method:

// register device with Azure Notification Hub using the token from FCM
Registration registration = hub.Register(token, subscriptionTags);

---Update---

I've raised this against the repo on github too at this link. They suggested messaging around the proguard or r8. I actually didn't have these set but because I know Xamarin and VS sometimes do their own thing I setup the r8 shrinker and now when I run this I get the following error which looks a little more descriptive as it shows a 400 error potentially.

{
  "JniPeerMembers": {
    "ManagedPeerType": "WindowsAzure.Messaging.NotificationHubException, Xamarin.Azure.NotificationHubs.Android, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
    "JniPeerTypeName": "com/microsoft/windowsazure/messaging/NotificationHubException",
    "JniPeerType": {
      "PeerReference": {
        "Handle": {
          "value": 17866
        },
        "Type": 2,
        "IsValid": true
      },
      "Name": "com/microsoft/windowsazure/messaging/NotificationHubException"
    },
    "InstanceMethods": {},
    "InstanceFields": {},
    "StaticMethods": {},
    "StaticFields": {}
  },
  "StatusCode": 400,
  "StackTrace": "  at Java.Interop.JniEnvironment+InstanceMethods.CallObjectMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0006e] in <3f19c9fc57a34ac9a473579164f8755e>:0 \n  at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0002a] in <3f19c9fc57a34ac9a473579164f8755e>:0 \n  at WindowsAzure.Messaging.NotificationHub.Register (System.String pnsHandle, System.String[] tags) [0x00043] in <254d369ec5544bb1b606035994077f91>:0 \n  at NorthNorfolk.Mobile.Client.Droid.Notifications.FirebaseService.SendRegistrationToServer (System.String token) [0x00078] in <4b8b745c15df475a92a1855a0ba61d95>:0 \n  --- End of managed WindowsAzure.Messaging.NotificationHubException stack trace ---\ncom.microsoft.windowsazure.messaging.NotificationHubException\n\tat com.microsoft.windowsazure.messaging.Connection.executeRequest(Unknown Source:146)\n\tat com.microsoft.windowsazure.messaging.Connection.executeRequest(Unknown Source:134)\n\tat com.microsoft.windowsazure.messaging.Connection.executeRequest(Unknown Source:7)\n\tat com.microsoft.windowsazure.messaging.NotificationHub.upsertRegistrationInternal(Unknown Source:22)\n\tat com.microsoft.windowsazure.messaging.NotificationHub.registerInternal(Unknown Source:48)\n\tat com.microsoft.windowsazure.messaging.NotificationHub.register(Unknown Source:27)\n\tat crc64d75b20eac0c397e9.FirebaseService.n_onNewToken(Native Method)\n\tat crc64d75b20eac0c397e9.FirebaseService.onNewToken(Unknown Source:0)\n\tat com.google.firebase.messaging.FirebaseMessagingService.handleIntent(Unknown Source:53)\n\tat com.google.firebase.messaging.EnhancedIntentService.lambda$processIntent$0$EnhancedIntentService(Unknown Source:1)\n\tat com.google.firebase.messaging.EnhancedIntentService$$Lambda$0.run(Unknown Source:6)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)\n\tat com.google.android.gms.common.util.concurrent.zza.run(Unknown Source:7)\n\tat java.lang.Thread.run(Thread.java:764)\n",
  "JniIdentityHashCode": 196544507,
  "PeerReference": {
    "Handle": {
      "value": 18122
    },
    "Type": 2,
    "IsValid": true
  },
  "Handle": {
    "value": 18122
  },
  "Cause": null,
  "Message": null,
  "Data": {},
  "InnerException": null,
  "Source": "mscorlib",
  "HResult": -2146233088
}

Anyone got any ideas on the above, this is driving me a bit nuts.



from Xamarin Forms - Android - Remote Push Notifications - hub.Register(); exception in release build fine in debug

No comments:

Post a Comment