3

I went through all the "Not Registered" error posts and could not solve the problem. I also contacted firebase support, unfortunately I only received URLs to the official documentation from them.

Our app has been in the store for a while and was using GCM successfully for notifications when the app is in the background. Due to deprecation of GCM we are currently trying to migrate to FCM.

Summarised: We went through the official migration steps, changed the code in the android client, updated firebase console, downloaded/added google-services.json and also adapted the endpoint URL on the app server.

Now the problem we are facing is as follows: The app requests a token using FirebaseInstanceId singleton and receives it successfully. This token we then forward to our app server that saves it in a DB. Then, the server wants to send the android device a message using the Sender.sender(...) method but receives the infamous 'NotRegistered' error. All of this happens within about 10 seconds.

We tried our luck on these devices:

  • Emulator device Nexus 5X API 27 x86
  • Nexus 5X API 23
  • Pixel 2 API 28

Looking at the list of reasons the NotRegistered error is returned, I can comment as follows:

  1. Client app unregisters with FCM -> since we are in control of the phone I can exclude this point
  2. Uninstallation of app -> same here
  3. Registration token expires -> possible, but within 10 seconds?
  4. "If the client app is updated but the new version is not configured to receive messages" -> what does that exactly mean? Since the app was able to receive messages before, I guess it is configured to receive them.

Android Client

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.acme.app">

    <!-- permissions omitted for brevity -->

    <application
        android:name=".App"
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:largeHeap="true"
        android:theme="@style/AppTheme"
        tools:node="replace">

        <!-- Register AnalyticsReceiver and AnalyticsService to support background dispatching for Google Analytics on non-Google Play devices. -->
        <receiver
            android:name="com.google.android.gms.analytics.AnalyticsReceiver"
            android:enabled="true">
            <intent-filter>
                <action android:name="com.google.android.gms.analytics.ANALYTICS_DISPATCH" />
            </intent-filter>
        </receiver>

        <service
            android:name="com.google.android.gms.analytics.AnalyticsService"
            android:enabled="true"
            android:exported="false" />

        <service
            android:name=".gcm.MyGcmListenerService"
            android:exported="true"
            android:enabled="true">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />                
            </intent-filter>
        </service>
        <service android:name=".gcm.MyInstanceIDListenerService">
            <intent-filter>
                <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
            </intent-filter>
        </service>
        <!-- this is need for the firebase messaging service. It should be included automatically, but somehow gets deleted by the AndroidManifest file merging. So we manually have to add it -->
        <service android:name="com.google.firebase.components.ComponentDiscoveryService">
            <meta-data
                android:name="com.google.firebase.components:com.google.firebase.iid.Registrar"
                android:value="com.google.firebase.components.ComponentRegistrar" />
        </service>

        <!-- omitted for brevity -->

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />        
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_icon"
            android:resource="@drawable/notification_icon" />        
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_color"
            android:resource="@color/base_color" />
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="@string/default_notification_channel_id" />
    </application>

</manifest>

Code in Activity (shortened)

String token = FirebaseInstanceId.getInstance().getToken();
sendTokenToServer(token);
FirebaseMessaging.getInstance().subscribeToTopic("news");

Services

public class MyGcmListenerService extends FirebaseMessagingService {

    @Override
    public void onMessageReceived(RemoteMessage message) {
        ...// never called
    }
}

I know that FirebaseInstanceIdService is deprecated, but I tried it also with the new version which also doesn't work.

public class MyInstanceIDListenerService extends FirebaseInstanceIdService {
    @Override
    public void onTokenRefresh() {
        // never called...
        String token = FirebaseInstanceId.getInstance().getToken();
        sendTokenToServer(token);
    }
}

The android client uses the following dependencies:

    implementation "com.google.firebase:firebase-core:16.0.7"
    implementation "com.google.firebase:firebase-messaging:17.4.0"

App Server

On the server side we are using the following library. According to the documentation and this post we should be able to use the same library just with the adapted endpoint URL (https://fcm.googleapis.com/fcm/send) without any further changes:

com.google.gcm:gcm-server:1.0.0 

Notification is built as follows:

private Message buildMessage(String title, String body, Map<String, String> attributes) {
    final Notification.Builder notificationBuilder = new Notification.Builder(null)
            .body(body)
            .title(StringUtils.trimToNull(title));
    final Message.Builder messageBuilder = new Message.Builder()
            .addData(KEY_BODY, body)
            .notification(notificationBuilder.build());

    if (!Strings.isNullOrEmpty(title)) {
        messageBuilder.addData(KEY_TITLE, title);
    }

    for (Map.Entry<String, String> attr : attributes.entrySet()) {
        messageBuilder.addData(attr.getKey(), attr.getValue());
    }
    return messageBuilder.build();
}

and sent using this call:

Result result = sender.send(message, deviceToken, retryCount);

if (result.getMessageId() == null) {
    if (ERROR_CODE_NOT_REGISTERED.equals(result.getErrorCodeName())) { // <-- returns NotRegistered
        // omitted
    } 
}

Firebase console

I uploaded the debug and release SHA1s of the app. I also tried sending a message from the firebase console (Grow > Cloud messaging > Notifications), still no messages on Android.

Interestingly, we do not have any problems with the iOS version of the app, which also uses FCM. Messages from the app server and from firebase console arrive successfully. However, the iOS app never used GCM.

Any clues? What did we forget? And thanks for reading up to here!

SOERGI
  • 193
  • 2
  • 13
  • Is your APK working when you are not installing from play store or installing manually release APK? – Dhaval Solanki Mar 15 '19 at 12:59
  • @DhavalSolanki So the APK with the FCM code is currently not in the play store only the GCM version, but neither the release nor the debug version installed through the Android Studio work properly, i.e. I can install them, but the FCM does not work. – SOERGI Mar 15 '19 at 14:03

1 Answers1

1

So this one wasn't obvious... I've been banging my head against the wall for 3 days, and this post took me onto the right path. The following line in the manifest was the problematic piece:

tools:node="replace"

It causes attributes in the manifest not to be merged but replaced which resulted in important automatically generated tags by the firebase library not to be included in the final manifest file. I simply removed the line (and fixed some minor problems that depended on it being replace) to fix the issue.

A symptom of the replace is the following error message

Make sure to call FirebaseApp.initializeApp(Context) first

a commonly proposed solution for it (e.g. here) is setting that initializeApp line somewhere early in the code. That leads to a runtime NPE when fetching the FCM token. Which can be fixed with yet another hack and you end up with an app that compiles and does not throw any errors, but also does not receive any notifications (as described in the question).

So bottom line: be wary with blindly using accepted answers on SO and try to understand the underlying problem. Of course, it would be helpful if the firebase migration guide provided a way to verify if the right pieces of code were generated correctly.

SOERGI
  • 193
  • 2
  • 13