Azure / azure-notificationhubs-android

Android SDK and Samples for Azure Notification Hubs
Apache License 2.0
33 stars 62 forks source link

[BUG] Hub.Register() Errors in Release, works fine in Debug #171

Open JoeTomkinson opened 3 years ago

JoeTomkinson commented 3 years ago

Describe the bug In a Xamarin Forms project implementing the Azure Notification Hub throws an exception in release mode that it does not exhibit during debugging that causes remote PNS registrations to fail.

Exception or Stack Trace

{
      "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.MYAPPNAME.mobile.FirebaseService.n_onNewToken(Native Method)\n\tat 
      com.MYAPPNAME.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
}

To Reproduce

Simply run the example project for Xamarin Forms focusing on the android platform, switch to release mode and look out for the errors in the logcat.

Code Snippet

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>

Expected behavior

I would expect this to register the device with the notification hub and be visible as a registration in Service Bus Explorer.

Setup (please complete the following information):

marstr commented 3 years ago

Howdy - I've seen reports of similar issues. In those cases, the Xamarin.Android Release profile was using a code shrinker that was more (read: too) aggressive than the Debug profile. To see if this is your issue, you'll need to ensure that the proguard configuration file is setup to not strip out anything in the com.microsoft.windowsazure.messaging.notificationhubs namespace. Something like:

-keep class com.microsoft.windowsazure.messaging.notificationhubs.** { <init>(***); }
JoeTomkinson commented 3 years ago

Howdy - I've seen reports of similar issues. In those cases, the Xamarin.Android Release profile was using a code shrinker that was more (read: too) aggressive than the Debug profile. To see if this is your issue, you'll need to ensure that the proguard configuration file is setup to not strip out anything in the com.microsoft.windowsazure.messaging.notificationhubs namespace. Something like:

-keep class com.microsoft.windowsazure.messaging.notificationhubs.** { <init>(***); }

Apologies for a slow reply. I'll look to try this today; as far as I was aware however I don't believe I had a code shrinker set on the release build. But it wouldn't surprise me if there was something strange going on under the hood of the release build so I'll investigate further and report back.

JoeTomkinson commented 3 years ago

So I actually set the R8 shrinker to run as it was set to blank, that's produced 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": 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
}

I can see that there's a reference to a status code this time and a 400 error. Any thoughts on what could be causing that because as far as I know this is all set up exactly as it should be based on the Microsoft documentation.

JoeTomkinson commented 3 years ago

Also not sure if it's relevant but while looking at logcat I get the following warning just after the above error:

A connection to https://[Our Azure Notification Hub name].servicebus.windows.net/ was leaked. Did you forget to close a response body?