Josh Russo
Josh Russo

Reputation: 3241

Google Cloud Messaging connection problems

I'm attempting to setup a Xamarin Forms application to use Google Cloud Messaging (GCM) and I've encountering a very strange behavior. I'm currently using Xamarin Studio on Windows and following their remote notification walkthrough .

For some reason the GCMPubSub.Subscribe() works only over a cellular connection and not wifi. I've tried different wifi networks with the same result. Is it possible that the development scenario uses some different ports or network behavior that a production setup does not? There's never been a problem with my Android phone on these different networks receiving push notifications.

Any thoughts?

Edit

The error that I'm currently receiving is an IOException with the message of "Invalid Parameters" when Subscribe() is called in GcmRegistrationService, but only when on a wifi network. I tried comparing what I've done to the GCM Android examples and they behave similarly

The MainActivity:

[Activity(Label = "MyApp", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        global::Xamarin.Forms.Forms.Init(this, bundle);
        LoadApplication(new App(DeviceType.Android));

        if (IsPlayServicesAvailable())
        {
            var intent = GcmRegistrationService.GetIntent(this, "MyTopic");
            StartService(intent);
        }
    }

    public bool IsPlayServicesAvailable()
    {
        var resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
        if (resultCode != ConnectionResult.Success)
        {
            if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
                ToastHelper.ShowStatus("Google Play Services error: " + GoogleApiAvailability.Instance.GetErrorString(resultCode));
            else
            {
                ToastHelper.ShowStatus("Sorry, this device is not supported");
                Finish();
            }
            return false;
        }
        else
        {
            ToastHelper.ShowStatus("Google Play Services is available.");
            return true;
        }
    }

}

The GCM registration Intent:

/// <summary>
/// The background process that handles retrieving GCM token
/// </summary>
[Service(Exported = false)]
public class GcmRegistrationService : IntentService
{
    private static readonly object Locker = new object();

    public GcmRegistrationService() : base("GcmRegistrationService") { }

    public static Intent GetIntent(Context context, string topic)
    {
        var valuesForActivity = new Bundle();
        valuesForActivity.PutString("topic", topic);

        var intent = new Intent(context, typeof(GcmRegistrationService));

        intent.PutExtras(valuesForActivity);

        return intent;
    }

    protected override void OnHandleIntent(Intent intent)
    {
        try
        {
            // Get the count value passed to us from MainActivity:
            var topic = intent.Extras.GetString("topic", "");

            if (string.IsNullOrWhiteSpace(topic))
                throw new Java.Lang.Exception("Missing topic value");

            Log.Info("RegistrationIntentService", "Calling InstanceID.GetToken");
            lock (Locker)
            {
                var instanceId = InstanceID.GetInstance(this);
                var projectNumber = Resources.GetString(Resource.String.ProjectNumber);
                var token = instanceId.GetToken(projectNumber, GoogleCloudMessaging.InstanceIdScope, null);

                Log.Info("RegistrationIntentService", "GCM Registration Token: " + token);

                var applicationState = DataCacheService.GetApplicationState ();

                // Save the token to the server if the user is logged in
                if(applicationState.IsAuthenticated)
                    SendRegistrationToAppServer(applicationState.DeviceId, token);

                Subscribe(token, topic);
            }
        }
        catch (SecurityException e)
        {
            Log.Debug("RegistrationIntentService", "Failed to get a registration token because of a security exception");
            Log.Debug ("RegistrationIntentService", "Exception message: " + e.Message);
            ToastHelper.ShowStatus("Google Cloud Messaging Security Error");
            return;
        }
        catch (Java.Lang.Exception e)
        {
            Log.Debug("RegistrationIntentService", "Failed to get a registration token");
            Log.Debug ("RegistrationIntentService", "Exception message: " + e.Message);
            ToastHelper.ShowStatus("Google Cloud Messaging Error");
            return;
        }
    }

    void SendRegistrationToAppServer(Guid deviceId, string token)
    {
        // Save the Auth Token on the server so messages can be pushed to the device
        DeviceService.UpdateCloudMessageToken (deviceId, token);

    }

    void Subscribe(string token, string topic)
    {
        var pubSub = GcmPubSub.GetInstance(this);

        pubSub.Subscribe(token, "/topics/" + topic, new Bundle());
        Log.Debug("RegistrationIntentService", "Successfully subscribed to /topics/" +topic);
        DataCacheService.SaveCloudMessageToken(token, topic);
    }

}

The AndroidManifest.xml:

<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    android:installLocation="auto" 
    package="com.myapp" 
    android:versionCode="1" 
    android:versionName="1.0">

    <uses-sdk android:minSdkVersion="19" />

    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <permission 
        android:name="com.myapp.permission.C2D_MESSAGE" 
        android:protectionLevel="signature" />

    <uses-permission 
        android:name="com.myapp.permission.C2D_MESSAGE" />

    <application 
        android:label="My App" 
        android:icon="@drawable/icon">

        <receiver 
            android:name="com.google.android.gms.gcm.GcmReceiver" 
            android:permission="com.google.android.c2dm.permission.SEND"
            android:exported="true">

            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
                <category android:name="com.myapp" />
            </intent-filter>

        </receiver>

        <service 
            android:name="com.myapp.XamarinMobile.Droid.Services.MyGcmListenerService"
            android:exported="false">
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            </intent-filter>
        </service>

    </application>
</manifest>

Upvotes: 0

Views: 620

Answers (1)

Rendy Del Rosario
Rendy Del Rosario

Reputation: 1297

I haven't experienced that issue in my case when using GCM. Have been comparing my implementation with your current code to see if find something relevant. Will suggest try using the application context to get the instances to assure all instances are in the same context.

For InstanceID :

var instanceId = InstanceID.GetInstance(Android.App.Application.Context));

For GcmPubSub :

GcmPubSub pubSub = GcmPubSub.GetInstance(Android.App.Application.Context);

For GcmRegistrationService:

GcmRegistrationService.GetIntent(Android.App.Application.Context, "MyTopic");

Let me know if helps.

Upvotes: 1

Related Questions