The AndroidManifest.xml:
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- Receiver for GCM Messages-->
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<action android:name="com.google.android.c2dm.intent.GCM_RECEIVED_ACTION"/>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="de.comp.module.client" />
</intent-filter>
</receiver>
<service
android:name="de.comp.module.client.gcm.GCMBroadcastIntentService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
The GCMBroadcastIntentService class:
public class GCMBroadcastIntentService extends GcmListenerService {
#Override
public void onMessageReceived(String from, Bundle extras) {
if (extras.containsKey("message")) {}
}
}
The onMessageReceived() method is never called. The server works fine, the messages are send to Google GCM without any errors, but they never arrive at the device. I also use google-services.json which is stored in the folder /app. In my app the user can loggin after a new installation. In this case a new GCM token is requested and send to the server. So it should be correct and up to date.
What is missing? Thanks for your help.
EDIT:
public class GCMRegistrationIntentService extends IntentService {
#Override
protected void onHandleIntent(Intent intent) {
try {
// register server key to GCM
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String regid = gcm.register(getString(R.string.gcm_defaultSenderId));
// Request new token for this server key
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// Subscribe to topic channels
subscribeTopics(token);
...
}
you must register the sender id in the Gcm:
GoogleCloudMessaging gcm=null;
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(getContext());
}
String regid = gcm.register(SENDER_ID);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
note that SENDER_ID is the id of the server that sends the notification
Use FCM instead of GCM.Check this official links. FCM is a new Version of GCM
Try to start from the beginning.
Make sure SENDER_ID matches the project number in console.
Make sure you implement registration IntentService, device registration is successful and you receive push token (GCM registration id).
Make sure you pass it to server.
Not forget to implement InstanceIDListenerService.
Add missing services to manifest.
Start everything from the beginning and pay attention at each step
https://developers.google.com/cloud-messaging/android/client
Set up the GCM client according to https://developers.google.com/cloud-messaging/android/client
If you still not get a call to onMessageReceived(), you can check whether the problem is somwhat similar to GcmListenerService.onMessageReceived() not called
Also GCM requires a Google account for Pre-4.0.4 devices if you are using the deprecated library (GCMRegistrar.register), . If yes you need to make an entry in AndroidManifest as
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
Refer GET_ACCOUNTS permission while using GCM - Why is this needed?
Related
I am trying to setup a basic Xamarin app with push notifications for Android. I followed the demo here and all seemed to be going well. The application log shows the registration token received using the following code:
using System;
using Android.App;
using Android.Content;
using Android.Util;
using Android.Gms.Gcm;
using Android.Gms.Gcm.Iid;
namespace HawkProject.Droid {
[Service(Exported = false)]
class RegistrationIntentService : IntentService
{
static object locker = new object();
public RegistrationIntentService() : base("RegistrationIntentService") { }
protected override void OnHandleIntent (Intent intent)
{
try
{
Log.Info ("RegistrationIntentService", "Calling InstanceID.GetToken");
lock (locker)
{
var instanceID = InstanceID.GetInstance (this);
var token = instanceID.GetToken (
"1015213323461", GoogleCloudMessaging.InstanceIdScope, null);
Log.Info ("RegistrationIntentService", "GCM Registration Token: " + token);
//SendRegistrationToAppServer (token);
//Subscribe (token);
}
}
catch (Exception e)
{
Log.Debug("RegistrationIntentService", "Failed to get a registration token");
Log.Debug("RegistrationIntentService", e.Message);
return;
}
}
void SendRegistrationToAppServer (string token)
{
// Add custom implementation here as needed.
}
void Subscribe (string token)
{
var pubSub = GcmPubSub.GetInstance(this);
pubSub.Subscribe(token, "/topics/hawkglobal", null);
}
}
}
Application Log returns:
[RegistrationIntentService] Calling InstanceID.GetToken
[RegistrationIntentService] GCM Registration Token: dz8yxsF61tc:APA91bFpHBPsJuGUN2Yh5Hv9CyuAgyIUP4qthhNE79rJUOvLH__ildlVOaP9Q-X1lBpDk69BDRbnzDl4kdEdJBjyudwiVfRZnVR4trQwcM8JZYIEqMNFiXAy61RSjD2MWnC2kmNLj1Ev
My manifest is as follows:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto" package="com.gcs.hawkproject" android:versionCode="1" android:versionName="start">
<uses-sdk android:minSdkVersion="15" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.gcs.hawkproject.permission.C2D_MESSAGE" />
<permission android:name="com.gcs.hawkproject.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<application android:label="Hawk" android:icon="#drawable/log"></application>
<application android:label="RemoteNotifications" android:icon="#drawable/Icon">
<receiver android:name="com.google.android.gms.gcm.GcmReceiver"
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="com.gcs.hawkproject" />
</intent-filter>
</receiver>
<service
android:name="com.gcs.hawkproject.MyGcmListenerService"
android:exported="false" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
</intent-filter>
</service>
<service
android:name="com.gcs.hawkproject.MyInstanceIDListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.gms.iid.InstanceID"/>
</intent-filter>
</service>
</application>
</manifest>
All seems to be going well, but when i try to send a message, I get the following results:
POST:
{
"data" : { "message" : "This is a test" },
"to" : "dz8yxsF61tc:APA91bFpHBPsJuGUN2Yh5Hv9CyuAgyIUP4qthhNE79rJUOvLH__ildlVOaP9Q-X1lBpDk69BDRbnzDl4kdEdJBjyudwiVfRZnVR4trQwcM8JZYIEqMNFiXAy61RSjD2MWnC2kmNLj1Ev"
}
RESPONSE
{
"multicast_id":5139152955408512939,
"success":0,"failure":1,"canonical_ids":0,
"results": [{"error":"NotRegistered"}]
}
Any thoughts? I am new to both Xamarin and GCM, so I am fumbling around for hours with no success.
I have other code from the demo as well that i can post if necessary.
Thanks in advance.
Change
using Android.Gms.Gcm.Iid;
to
using Android.Gms.Iid;
I would suggest try the following:
(1) Uninstall and Reinstall the application, get a new notification token from GCM, try push to the new token again.
(2) Double check the Project id "1015213323461", is it possible that the sender_id does not match with the server certificate used to send the notification?
(3) com.gcs.hawkproject is this project id matching the one you registered in Google Developer console?
There is a Xamarin Studio deployment issue that result in those NotRegistered errors, my workaround is detailed here.
Is this is a new GCM project? You should be now using FCM - Firebase Cloud Messaging.
From GCM website:
Firebase Cloud Messaging (FCM) is the new version of GCM. It inherits
the reliable and scalable GCM infrastructure, plus new features! See
the FAQ to learn more. If you are integrating messaging in a new app,
start with FCM. GCM users are strongly recommended to upgrade to FCM,
in order to benefit from new FCM features today and in the future.
I think that your GCM key might not be valid now. You should create your FCM project in here:
https://firebase.google.com/docs/cloud-messaging/
I inherited an application that already implemented GCM services and is working well enough.
I'm saying well enough because half the times, when the app launches, I get the error INVALID_SENDER and the other half I don't get it!
There is no difference between the times I get the error and the times I don't (or maybe I'm missing the difference).
I do get messages from GCM from time to time (when I don't get the INVALID_SENDER after logging in)
This is the registration code in the onCreate() of my main activity
private void registerGCM() throws Exception {
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
registerReceiver(mHandleMessageReceiver, new IntentFilter(
CommonUtilities.DISPLAY_MESSAGE_ACTION));
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
// Automatically registers application on startup.
GCMRegistrar.register(this, CommonUtilities.SENDER_ID);
} else {
// Device is already registered on GCM, check server.
if (GCMRegistrar.isRegisteredOnServer(this)) {
ServerUtilities.register(mContext, regId);
} else {
// Try to register again, but not in the UI thread.
// It's also necessary to cancel the thread onDestroy(),
// hence the use of AsyncTask instead of a raw thread.
final Context context = this;
mRegisterTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
boolean registered = ServerUtilities.register(context, regId);
if (!registered) {
GCMRegistrar.unregister(context);
}
return null;
}
My Manifest file
<receiver
android:name="com.mixpanel.android.mpmetrics.GCMReceiver"
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.says.broadcaster" />
</intent-filter>
</receiver>
<receiver
android:name="com.google.android.gcm.GCMBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<!-- Receives the actual messages. -->
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<!-- Receives the registration id. -->
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.says.broadcaster" />
</intent-filter>
</receiver>
The reason I have two receivers is that I get push notifications from the stats tracking API I'm using too, and they use GCM.
My app has a new version every week or so, and I was reading that I need to register for a new ID after an app update. Could this be the issue?
Related questions:
Getting INVALID_SENDER on one device while its working with another GCM android
The Broadcasts sent by GCM seem to be ordered. This means they are delivered one at a time. It's possible that the receiver of the library sometimes gets called before your receiver, and it might prevent your receiver from being called (if it cancels the broadcast).
There are several ways to handle it. One way is to give your receiver a higher priority than the library's receiver. Just add the android:priority attribute to the intent-filter of both receiver, and give your receiver a higher priority.
Ordered broadcasts (sent with Context.sendOrderedBroadcast) are delivered to one receiver at a time. As each receiver executes in turn, it can propagate a result to the next receiver, or it can completely abort the broadcast so that it won't be passed to other receivers. The order receivers run in can be controlled with the android:priority attribute of the matching intent-filter; receivers with the same priority will be run in an arbitrary order.
When your broadcast receiver gets called, you should check that intent.getExtras ().get("from") contains the sender ID you are using. Only in this case you should handle the broadcast. If it's a different sender ID, it's probably the sender ID used by the library, and you should let the library handle it.
If that doesn't solve the problem, you can consider declaring only your receiver in the manifest, and forwarding the GCM broadcast to the other receiver when necessary.
I have created a sample project in Google Console. Referring the sample from this site, http://code.google.com/p/blog-samples/downloads/detail?name=gcm_sample_update1.rar&can=2&q=#makechanges I got my registration Id. Now I don't know what to do next, how I can implement push notification in my app. I want to use Urban Airship to send push to my app. I have created a app in Urban Airship, by giving the package name, API key obtained through Google console. To my server I need to send the Apid to get the messages. But i don't know how to get the Apid.
It is explained here: http://developer.android.com/guide/google/gcm/gs.html
Writing the Android Application
You have to download and add gcm libraries.
Then make changes in manifest:
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="xx"/>
and
<permission android:name="my_app_package.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="my_app_package.permission.C2D_MESSAGE" />
inserting Your package name in "my_app_package".
Add other permissions
<!-- App receives GCM messages. -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- GCM connects to Google Services. -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- GCM requires a Google account. -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- Keeps the processor from sleeping when a message is received. -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
receiver
<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="my_app_package" />
</intent-filter>
</receiver>
Again write Your package name in "my_app_package".
Then declare your service
<service android:name=".GCMIntentService" />
You have to create a class named GCMIntentService. This class is going to handle the push notifications.
It should look like this:
class GCMIntentService extends com.google.android.gcm.GCMBaseIntentService{
public void onRegistered(Context context, String regId){}
public void onUnregistered(Context context, String regId){}
public void onError(Context context, String errorId){}
public void onMessage(Context context, Intent intent){}
}
In onMessage() you will have to handle the push message. You can use notifications.
Then in Your starting Activity you will have to put this
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
final String regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals("")) {
GCMRegistrar.register(this, SENDER_ID);
} else {
Log.v(TAG, "Already registered");
}
in onCreate().
SENDER_ID is the String containing all numbers that you got from the google console.
Well.. There are 3 steps to get APID
1. Run the application
2. Open logcat
3. Find the APID
Note first time you may get APID(Android Push ID) null so run it again you will get
If you want to use Urbanairship you have to use their SDK for user registration (look here), cause UA create a new id for each device and not using Google registration Id when pushing message using their API.
Not sure why they doing it like that but that it... =\
The basic architecture of the push functionality that I am trying to create is
1) I would get the phone id and store it in my db
2) Only when something happened on the server I would try to create a push message
So I do not need the part where the app tries to call the server to send it the message. I am wondering what should I extend in the Service class and what methods in it do I need to implement.
Also, am I able to get the device id and send that info to my server without using the server?
I am using Google cloud messaging.
Thanks!
Ok, to get the registration ID for you phone you will need to do the following in an activity:
import com.google.android.gcm.GCMRegistrar;
private final static String SENDER_ID = "0001234567" // API Key, see comments;
public class myActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState)
{
GCMRegistrar.checkDevice(this);
GCMRegistrar.checkManifest(this);
regId = GCMRegistrar.getRegistrationId(this);
if (regId.equals(""))
{
GCMRegistrar.register(this, SENDER_ID);
Log.i(TAG,"registered a new ID");
}
else
{
Log.i(TAG,"Already Registered");
}
your google reg will be contained in regId
You will need this in your Manifest:
<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="my.namespace" />
</intent-filter>
</receiver>
where my.namespace will equal whatever your package name is
You can follow this tutorial.. it uses the new GooglecloudMessaging API. And yes, you should extend IntentService cause the other ones deprecated now.
I am attempting to register my device with C2DM and am having major issues. I have followed several tutorials, all of which are very similar. I believe the issue has to do with the registration intent that it sends to the C2DM server. Does anyone have any suggestions. The following is the relevant code:
Manifest: The permissions (outside my application tag):
<!-- Used for C2DM -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="com.companyname.parade.permission.C2D_MESSAGE" />
This is the Intent registration (inside my application tag):
<receiver
android:name=".C2DMReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<!-- Receive the actual message -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.companyname.parade" />
</intent-filter>
<!-- Receive the registration id -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.companyname.parade" />
</intent-filter>
</receiver>
The following is what I call to register my device to the C2DM server (it starts the service that contacts the C2DM servers that is suppose to send back a registration Intent with my registartionID in it). It is located in a file called C2DMessaging:
public static void register(Context context) {
Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
PendingIntent.getBroadcast(context, 0, new Intent(), 0));
registrationIntent.putExtra(EXTRA_SENDER, SENDER_ID);
ComponentName name = context.startService(registrationIntent);
if(name == null){
// FAIL!
Log.d(TAG, "FAIL");
}else{
// SUCCESS
Log.d(TAG, "Success");
}
}
The ComponentName info is the following:
com.google.android.gsf/com.google.android.gsf.gtalkservice.PushMessagingRegistrar
There is no logcat output. My receiver (named C2DMReceiver) is the following:
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (C2DMessaging.INTENT_REGISTRATION_CALLBACK.equals(action)) {
// Registration Intent
Log.w(TAG, "Registration Receiver called");
handleRegistration(context, intent);
} else if (action.equals(C2DMessaging.INTENT_RECEIVED_MESSAGE_CALLBACK)) {
Log.w(TAG, "Message Receiver called");
handleMessage(context, intent);
} else if (action.equals(C2DMessaging.INTENT_C2DM_RETRY)) {
C2DMessaging.register(context);
}
}
This does not get called at all.
Edit: This whole thing was a stupid mistake on my part. I simply forgot a step somehow in the tutorials I read. I need to add this to my permissions:
<permission android:name="com.companyname.parade.permission.C2D_MESSAGE" android:protectionLevel="signature" />
Thanks to MisterSquonk for the response.
From the Google docs for C2DM for Creating the Manifest, the manifest needs a <permission> entry to complement the <uses-permission> entry for C2D_MESSAGE.
Something like this...
<permission android:name="com.companyname.parade.permission.C2D_MESSAGE" android:protectionLevel="signature" />
This tutorial worked for me in getting me up to speed:
http://www.vogella.com/articles/AndroidCloudToDeviceMessaging/article.html
I'm not sure why you have two intent filters. I only needed one - com.google.android.c2dm.intent.REGISTRATION (see tutorial above for a complete example manifest)
I'd check your manifest is correctly referencing the receiver class - perhaps try a fully-qualified reference to the class. I had an issue at one point where I moved receiver class in my project structure and all messages stopped.
I'd also check that the C2DM account is set up right, and with the right package name.
I'd also try it on another device, as in my experience some Android devices fall off C2DM and just don't receive messages for a period of time. I find sometimes flicking to airplane mode and back sorts it out, but I found testing on several devices essential to rule out problems with a specific device.