55

I'm getting a weird push message captured by my push service:

Bundle[{CMD=RST_FULL, from=google.com/iid, android.support.content.wakelockid=1}]

Just started to happen yesterday and I can't really spot which code change is to blame for this. Has anyone seen this message before and maybe knows where is it coming from and why?

vkislicins
  • 3,331
  • 3
  • 32
  • 62
  • Can you please elaborate... Is this the log message that you are getting? Are you using some third party API's or library? – AniV May 28 '15 at 01:01
  • The Bundle above is a copy/paste from the debugger - it's an object inside the intent I'm catching with my receiver. I get it with `intent.getExtras()` expecting a type and a message, instead getting the map above. I'm using some third party libs such as Picasso and Okhttp, but nothing too obscure or in this area. Not using any 3rd party APIs apart from Google (analytics, storage, gcm) – vkislicins May 28 '15 at 08:50
  • Also it seems I only get this message on the first run of the app. Force-stopping the app and clearing the data won't make the message reappear – vkislicins May 28 '15 at 08:59
  • 2
    It also seems to happen only on a debug build (as you said, on the first run). I thought that me updating the google play services library might have caused this, but I tried a couple of versions (7327000, 6171000, 5089000, 4323030, 4242000 and 4132500) with/without internet connection and it reproduced every time on the first run, on a debug build... Also, the intent action is 'com.google.android.c2dm.intent.RECEIVE' – Spiri May 28 '15 at 10:01
  • 2
    i'm getting the same message, seems to be a new "feature" – wutzebaer May 28 '15 at 16:04
  • I am observing this on some devices with builds after updating to SDK Tools Revision 24.3? Same code built earlier runs fine. – iceman Jun 01 '15 at 15:07

6 Answers6

20

Your app is getting this message because it has had data restored from a backup. As the backup may have included registration tokens, this broadcast is sent telling your app to get new tokens as the backed up ones will not work.

This is intended for the new GCM APIs, and it will cause your InstanceIdListenerService implementation's onTokenRefresh() method to be called, where your app should obtain all its tokens again.

Unfortunately, if you are writing your own BroadcastReceiver, these messages will be unexpected and may cause your app to crash. The right thing to do is to filter on the "from" field, and if you see one of these messages, to register again with GCM as your tokens may be invalid.

If you are getting these messages outside the situation of a fresh install where your app's data is being restored, please post to the android-gcm mailing list.

morepork
  • 973
  • 6
  • 9
  • The backup part of your answer is not clear. Why such a notification appears even if the application manifest explicity disallow backups by setting allowBackup to false? – Giulio Piancastelli Jul 07 '15 at 14:52
  • @GiulioPiancastelli According to comments in the [GCM Sample Code](https://github.com/googlesamples/google-services/blob/master/android/gcm/app/src/main/java/gcm/play/android/samples/com/gcmquickstart/MyInstanceIDListenerService.java), this token refresh notification is `Called if InstanceID token is updated. This may occur if the security of the previous token had been compromised.` – mpkuth Jul 22 '15 at 14:01
  • @mpkuth sorry, I don't see how compromised security could possibly have anything to do with the restored-data-from-a-backup use case that I wanted to discuss. – Giulio Piancastelli Jul 22 '15 at 14:26
  • 1
    @GiulioPiancastelli In the linked sample code, MyInstanceIDListenerService is what handles notifications from google.com/iid. The comments in that file seem to indicate that the notification in question is sent whenever the token is updated for any reason, not just restoring from a backup. It lists compromised security as a possible reason, but does not seem to exclude any other reason. – mpkuth Jul 22 '15 at 14:44
  • @mpkuth oh, right, I see. Thanks for clarification! – Giulio Piancastelli Jul 22 '15 at 14:46
  • Here is a sample for the new GCM Client https://github.com/googlesamples/google-services and here is an explanation https://developers.google.com/cloud-messaging/android/client – T-D Jul 23 '15 at 19:40
13

See the updated GCM API Docs as @morepork suggests.

For existing apps that extend a WakefulBroadcastReceiver, Google recommends migrating to GCMReceiver and GcmListenerService. To migrate:

In the app manifest, replace your GcmBroadcastReceiver with "com.google.android.gms.gcm.GcmReceiver", and replace the current service declaration that extends IntentService to the new GcmListenerService

Remove the BroadcastReceiver implementation from your client code

Refactor the current IntentService service implementation to use GcmListenerService

For details, see the example manifest and code samples in this page.

From their sample code, it's pretty easy to follow.

AndroidManifest.xml

<receiver
    android:exported="true"
    android:name="com.google.android.gms.gcm.GcmReceiver"
    android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
        <category android:name="com.example.client"/>
    </intent-filter>
</receiver>

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

<service
    android:name=".MyInstanceIdListenerService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.android.gms.iid.InstanceID"/>
    </intent-filter>
</service>

<service
    android:name=".MyGcmRegistrationService"
    android:exported="false">
</service>

MyGcmListenerService.java

public class MyGcmListenerService extends GcmListenerService {
    @Override
    public void onMessageReceived(String from, Bundle data) {
        final String message = data.getString("message");
        makeNotification(message);
    }
}

MyGcmRegistrationService.java

public class MyGcmRegistrationService extends IntentService {
    private static final String TAG = "MyRegistrationService";
    private static final String GCM_SENDER_ID = "XXXXXXXXXXXX";
    private static final String[] TOPICS = {"global"};

    public MyGcmRegistrationService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        try {
            synchronized (TAG) {
                InstanceID instanceID = InstanceID.getInstance(this);
                String token = instanceID.getToken(GCM_SENDER_ID,
                        GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
                sendTokenToServer(token);
                subscribeTopics(token);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void subscribeTopics(String token) throws IOException {
        for (String topic : TOPICS) {
            GcmPubSub pubSub = GcmPubSub.getInstance(this);
            pubSub.subscribe(token, "/topics/" + topic, null);
        }
    }
}

MyInstanceIdListenerService.java

public class MyInstanceIdListenerService extends InstanceIDListenerService {
    public void onTokenRefresh() {
        Intent intent = new Intent(this, MyGcmRegistrationService.class);
        startService(intent);
    }
}

Then you can replace your old registration code with just

Intent intent = new Intent(this, MyGcmRegistrationService.class);
startService(intent);
mpkuth
  • 6,994
  • 2
  • 27
  • 44
  • I'm getting `INSTANCE_ID_SCOPE cannot be resolved or is not a field` and `the method sendTokenToServer(String) is undefined...`. – ban-geoengineering Aug 03 '15 at 12:57
  • 1
    @ban-geoengineering see the whole source code https://github.com/googlesamples/google-services/blob/master/android/gcm/app/src/main/java/gcm/play/android/samples/com/gcmquickstart/RegistrationIntentService.java. This method is optional – Sergii Sep 08 '15 at 09:47
7

I realized the same issue today. First, this message must come from google itself (from=google.com/iid), otherwise the from attribute would be the id of your project in google developer console (i.e. 475832179747). But to be sure, I shutdown our application server, and I still received the message.

I always receive it when I newly register at the Google Cloud Messaging server. It's not a big problem because you can filter the message by the intent-action, but I would really like to know the purpose of it.

rickul
  • 1,054
  • 1
  • 9
  • 15
2

For existing apps that extend a WakefulBroadcastReceiver, Google recommends migrating to GCMReceiver and GcmListenerService. To migrate:

  • In the app manifest, replace your GcmBroadcastReceiver with "com.google.android.gms.gcm.GcmReceiver", and replace the current service declaration that extends IntentService to the new GcmListenerService
  • Remove the BroadcastReceiver implementation from your client code
  • Refactor the current IntentService service implementation to use GcmListenerService For details, see the example manifest.

it seems google split the GCMIntentService which extended IntentService to handle gcms to two services, one extends GcmListenerService that will handle received messages and other that filter iid.InstanceID separately to filter out that notification received for first installation, this is from new gcm android guides

<service
    android:name="com.example.MyGcmListenerService"
    android:exported="false" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    </intent-filter>
</service>
<service
    android:name="com.example.MyInstanceIDListenerService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.android.gms.iid.InstanceID"/>
    </intent-filter>
</service>

https://developers.google.com/cloud-messaging/android/client

nosaiba darwish
  • 1,179
  • 11
  • 13
0

same thing is happening to me on at least an Asus tablet

likely on more devices but i haven't had a chance to take a look

i'm looking for some particular Strings in Intent.getExtras() so the fix was simple, if they aren't present then ignore the whole thing.

what are the chances someone from Google will show up and explain what's going on?

user1126515
  • 1,133
  • 3
  • 17
  • 34
  • 1
    what are the chances someone from Google will show up and explain what's going on? - that would be pretty great... – vkislicins May 29 '15 at 12:44
0

I had this problem during migrating GCM->FCM with receiving only wakelockid element from:

  • firebase console
  • reproduced request by postman with request like this:

{ "to": "<your token from FirebaseInstanceId.getInstance().getToken()>", "notification": { "body": "Hello", "title": "This is test message." } }

Also I copied also all code from google quickstart firebase messaging. Everything should be fine. However, after all tests I decided to double check my gradle libs versions. So I incremented them to the latest numbers. Since then I started receiving messages correctly.

The fastest solution I would recommend to download project from GitHub and try if this is working for You. Next step would be copy this code to Your project. If in one project everything is working fine, You have at least one standing/working point where to start.

There are rumors about android studio is causing this issue - but it is not true. I checked it.

It can be true that You could use the same old token (from gcm) and not receiving messages but If You had the same case like me which is migrating, then token should be refreshed to new one and You should handle it..

deadfish
  • 11,996
  • 12
  • 87
  • 136