25

I'm trying to simulate as if the device is receiving a GCM push message by using adb and the command line. I've tried this command to broadcast a GCM intent:

adb shell am broadcast -c com.myapp -a com.google.android.c2dm.intent.RECEIVE -e data "SomeData"

This triggers a "Permission denial" log line though:

09-19 12:23:34.820      725-787/? W/BroadcastQueue﹕ Permission Denial: broadcasting Intent { act=com.google.android.c2dm.intent.RECEIVE cat=[com.myapp] flg=0x10 (has extras) } from null (pid=21244, uid=2000) requires com.google.android.c2dm.permission.SEND due to receiver com.myapp/com.google.android.gcm.GCMBroadcastReceiver

Relevant parts of my manifest:

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<application>
<receiver
    android:name="com.google.android.gcm.GCMBroadcastReceiver"
    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="com.myapp" />
    </intent-filter>
</receiver>
</application>

Any ideas?

Edit / clarification: Push / GCM receive works in production. I'm looking for an easier way to test changes.

Nilzor
  • 18,082
  • 22
  • 100
  • 167

4 Answers4

20

You need to remove the permission property in your receiver's definition like this:

<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<application>
<receiver
    android:name="com.google.android.gcm.GCMBroadcastReceiver" >
    <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>
</application>
guy.gc
  • 3,359
  • 2
  • 24
  • 39
  • 4
    Works! I misunderstood that permission. Having that paramtere there, states we only want messages coming from activities having the SEND permission - which the command line tool doesn't have. Unsure if I want this in production though, but maybe I can tailor a manifest for Debug builds only – Nilzor Oct 08 '14 at 12:29
10

Better than temporarily editing AndroidManifest.xml to remove the permission check, and remembering to restore it later, you can automatically disable the permission check in debug builds.

To do this, set the attribute via a Manifest Merger placeholder:

<receiver
  android:name="com.google.android.gcm.GCMBroadcastReceiver"
  android:permission="${gcmPermissionRequired}">

then set the placeholder in build.gradle:

buildTypes {
    debug {
        ...
        manifestPlaceholders = [gcmPermissionRequired: ""] // "" => let the GCM BroadcastReceiver accept Intents from 'adb shell am broadcast'
    }
    release {
        ...
        manifestPlaceholders = [gcmPermissionRequired: "com.google.android.c2dm.permission.SEND"]
    }
}

(Note: Previously I used debug & release string resources. It turns out that the Play store rejects the app if it defines an intent filter permission using a string resource.)

Jerry101
  • 12,157
  • 5
  • 44
  • 63
  • If the payload data is in JSON format, it's a difficult puzzle to quote that properly in the `adb shell am broadcast` command. See http://stackoverflow.com/a/29428061/1682419 for a solution. – Jerry101 Apr 03 '15 at 07:35
  • Since the Play store won't accept the string resource technique, I changed this to use a Manifest Merger placeholder. – Jerry101 Apr 15 '15 at 07:10
6

I suggest using command-line curl, as sending GCM pushes is as easy as calling some REST API. See sample shell script below:

#!/bin/bash

REGISTRATION_ID=YOUR_GCM_REGISTRATION_ID

SERVER_KEY=YOUR_SERVER_KEY_FROM_GOOGLE_API_CONSOLE

curl --header "Authorization: key=$SERVER_KEY" --header  Content-Type:"application/json"  https://android.googleapis.com/gcm/send  -d  "{ \"data\" : {\"foo\": \"bar\"}, \"registration_ids\":[\"$REGISTRATION_ID\"]  }"
mlepicki
  • 391
  • 2
  • 11
  • GREAT tip! a small addition, you get the YOUR_GCM_REGISTRATION_ID in the application from "GoogleCloudMessaging.getInstance(context).register(...)" – janCoffee Sep 29 '14 at 18:38
  • I've decided to write few more words abot that, for both iOS and Android: http://codingfingers.com/testing-push-notifications-on-ios-and-android/ – mlepicki Oct 01 '14 at 13:36
  • It's a good solution, but not optimal. What if you're offline? Or what if you want this to run as an integration test on a build server and don't want the server key stored there? – Nilzor Oct 02 '14 at 10:30
  • I see your point, but I don't see better solution - however, you can try to add second receiver (maybe exposed just in some gradle build variant) & use it to deliver messages to GCMIntentService. Still - not perfect, but would just allow you to test how pushes are handled. – mlepicki Oct 03 '14 at 06:32
0
  1. Install adb first.

sudo apt-get install adb

  1. Remove the SEND permission from manifest.xml.

android:permission="com.google.android.c2dm.permission.SEND" >

  1. Command to push notifications from shell

adb shell am broadcast -c com.myapp -a com.google.android.c2dm.intent.RECEIVE -e data "SomeData"

  1. Change the package name (com.myapp) and "SomeData" as you want.
Parinda Rajapaksha
  • 2,963
  • 1
  • 36
  • 40