I am trying to set up a Google App engine server endpoint that sends Google Cloud Messages to android devices. So far, the device registers using GoogleCloudMessaging.register() and sends the generated regID to the server. The server then can send a message to the regIDs it has saved. Through the log messages I can see that the regIDs are all arriving correctly, and the server is sending messages correctly. The devices though aren't getting any messages.
This is the receiver in the manifest:
<receiver
android:name="---.app.MessageReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.permission.RECEIVE" />
<category android:name="---.app" />
</intent-filter>
</receiver>
This is the code for the receiver:
public class MessageReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("---", "RECIVED A MESSAGE!!!!!!");
// Explicitly specify that GcmIntentService will handle the intent.
ComponentName comp = new ComponentName(context.getPackageName(),
GCMReceiverService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
The service never gets started, and the log message here is never shown.
Is there something I'm missing?
EDIT - these are the permissions:
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="---.app.permission.C2D_MESSAGE" />
<permission
android:name="---.app.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
There is a working solution on SO Android GCM basic implementation
The author edited his post, so you should just take it as an example. Basically, without correct permissions set AND correct Broadcast's constructor used, android won't give a damn about messages sent to an application.
Please set your permissions and other things accordingly and try again.
There is really nice post later down the comments, way under accepted one.
Really stupid mistake. Instead of .intent.RECIEVE in the intent filter it was .permission.RECEIVE.
Related
I have been reading various tutorials, other SO threads, as well as the official Android Developer and Firebase documentation to no avail. I've tried nearly everything and I'm running out of steam as well as time as I'm in the process of repairing a notification system that previously worked but no longer works.
I am using Azure Notification Hubs to distribute notifications to FCM among other Push Notification platforms. My FCM project targets only Android. My app is built in Xamarin.Forms using the latest NuGet package versions of all dependencies (Xamarin.Forms 5.x.x.x, Firebase.Messaging 122.0, etc.).
Currently, remote messages received while the app is running or backgrounded work flawlessly via a custom Service inheriting and implementing FirebaseMessagingService. Once the app is killed (task switcher -> swipe app away), upon sending further messages I start seeing Logcat messages with the following:
broadcast intent callback: result=CANCELLED forIntent { act=com.google.android.c2dm.intent.RECEIVE pkg= (has extras) }
I understand Google changed the way implicit receivers work in API 26 and above and my understanding is that this action (com.google.android.c2dm.intent.RECEIVE) is not included in the exception list, so I am lost as to how the messages can be listened for and handled in the background. I have read in other threads as recently as July 2019 that FCM messages received while an app is killed are supposed to be sent directly to the notification tray. This cannot be a widespread problem as many applications send notifications while killed, so I'm hoping to get some current information to direct me to a solution.
Is this intent broadcast being cancelled because of the implicit receiver changes, or am I doing something else wrong?
I am testing on a OnePlus 7 Pro with Android 10, so I am wondering if maybe it's a battery optimization issue that others have mentioned on devices by OEMs such as Huawei and Xiaomi.
My app is targeting Android API level 29 with min API 21
I have enabled Direct Boot Awareness for my main activity as well as the receiver to ensure that the receiver intercepts intents for my app upon boot and before the user has opened the app:
<receiver android:directBootAware="true" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND" android:name=".NotificationReceiver">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="<package>" />
</intent-filter>
</receiver>
My main activity includes intent filters for being the launch activity:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name=".MainActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
I request the following permissions:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
I have defined the following meta-data tags in my manifest <application> tag:
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/..."/>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#drawable/..." />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#color/..." />
These values work perfectly for received notifications while in background, so I know they are correctly configured.
Edit 1:
I have done more digging and debugging since posting and I do see that, without my custom BroadcastReceiver also listening for the c2dm.intent.RECEIVE action that my app:
is in fact getting remote messages I send while the app is killed via the Google-provided internal FirebaseInstanceIdReceiver that is part of the documented FCM setup (I removed the NotificationReceiver service mentioned prior)
is starting my custom implementation of the FirebaseMessagingService which (see below screenshot)
is not triggering OnMessageReceived overload in my FirebaseMessagingService (see below screenshot)
logcat upon receiving FCM remote message while app is killed
*******FirebaseService partial code:
[Service(DirectBootAware = true, Exported = true, Enabled = true)]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class MyFirebaseService : FirebaseMessagingService
{
public MyFirebaseService()
{
}
public override ComponentName StartService(Intent service)
{
Log.Info("GCM", $"MyFirebaseService started from intent {service}");
return base.StartService(service);
}
public override void OnMessageReceived(RemoteMessage message)
{
var notification = message.GetNotification();
Log.Info("GCM", $"Received remote message from FCM. Has notification: {notification != null}, has data: {message.Data != null}");
if (notification != null)
{
message.Data.TryGetValue("route", out string route);
SendNotification(notification.Title, notification.Body, route);
}
else
{
ParseDataNotification(message);
}
}
...
I was able to resolve the issue. My FirebaseMessagingService implementation had a Dependency Injection call in the constructor which failed when the service was started in the background by the FirebaseIidInstanceReceiver. This caused the service to fail to start and did not generate Android notifications while the app was killed.
Since I've done a lot of digging and information on this topic is so fragmented and out of date, I'll try to compile what I know results in a working solution here:
Follow the steps here, notably setting up your FCM project and downloading the google-services.json file.
Ensure your manifest declares the following permissions:
<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" />
Add the following within your AndroidManifest <application> tag to listen for message receives:
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" android:exported="false" />
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver" 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="${applicationId}" />
</intent-filter>
</receiver>
Optionally define defaults for notification channel, notification icon (must be white color only, allowing transparency), and notification icon color when the notification tray is expanded, also within the <application> manifest tag:
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="#string/..."/>
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="#drawable/..." />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="#color/..." />
Create a custom class inheriting from FirebaseMessagingService. In Xamarin.Forms, you will need the Xamarin.Firebase.Messaging NuGet package for this class. Within your implementation, you should override OnMessageReceived(RemoteMessage) and add your application logic which will handle messages containing the notification property in the foreground and messages with only the data property in both the foreground and background. Your class should be decorated with the following attributes (note that DirectBootAware is optional; see below):
[Service(DirectBootAware = true, Exported = true, Enabled = true)]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
If you wish to ensure that notifications can be received after a device reboot and before the device is unlocked, you may consider making your application and your FirebaseMessagingService implementation Direct Boot Aware (more here)
In your MainActivity, ensure a Notification Channel is created for devices running Android O or higher, and this method invoked at some point during OnCreate:
private void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
var channelId = GetString(Resource.String./*res id here*/);
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
// Don't re-create the notification channel if we already created it
if (notificationManager.GetNotificationChannel(channelId) == null)
{
var channel = new NotificationChannel(channelId,
"<display name>",
NotificationImportance.Default);
notificationManager.CreateNotificationChannel(channel);
}
}
Add a ProGuard config file ("proguard.cfg") to your Android project to prevent the SDK linker from killing Google Play and Firebase libraries. Edit the Properties of this file in Visual Studio and set the Build Action to ProguardConfiguration. Even if the option is missing from the dropdown list, Xamarin will recognize it. If you are using d8 and r8 instead of dx and ProGuard in your build, Xamarin will still use this config file and conform to the rules you define within.
# Keep commands are required to prevent the linker from killing dependencies not directly referenced in code
# See: https://forums.xamarin.com/discussion/95107/firebaseinstanceidreceiver-classnotfoundexception-when-receiving-notifications
-dontwarn com.google.android.gms.**
-keep class com.google.android.gms.** { *; }
-keep class com.google.firebase.** { *; }
Hope this helps and if I've missed anything I will update with further details.
The #AndrewH solution worked for me. With one missing detail. The Firebase messaging service will get called also when the app is killed. When the app is killed, only the constructor of the service will get called because internally firebase knows your app is killed. So, before you initialize any code that will handle notifications on foreground or any code that interacts with Xamarin forms, you should check if Xamarin forms has been initialized. For example:
if (Xamarin.Forms.Forms.IsInitialized)
{
// Do stuffs.
}
Otherwise your app will crash when it receives a push notification from killed state.
Also you should know that if the app is killed or Xamarin.Forms.Forms.IsInitialized == false, you should not try to execute any code. Just leave it. Firebase will just show the notification for you. You will just handle the notification when the user click on the notification from the system tray in your MainActivity.OnCreate().
I have a requirement of reading incoming SMS from a few of the e-commerce apps. For that, I added BroadcastReceiver for receiving SMS and reading that. Also added runtime permission of READ_SMS for that, done setting a priority of 1000 for that receiver. I tested it for a few days sending a few dummy messages, along with the eCommerce app messages similar to -
Delivered: Your package with Macbook Air
... has been successfully delivered. More info
at http://amzn.in/bAieP6f
Your SnapDeal order AWB:12791911327207 is delivered on 19-02-2020 at
16:20 by Xpressbees received by Username. You may contact us on
020-49116100.
Delivered: Gillette Sensitive Ski... from flipkart.com was delivered.
Click here to give feedback: http://fkrt.it/u33XFQHHHH
And so on.
But after testing for a few days, around 3-4 days, the app suddenly stopped working to read those and any other messages.
Note: The device I am using is - MI A1, with the Android 9 (Pie) version.
The code for the same, I used is as follows -
SmsListener.java (Broadcast Receiver class)
public class SmsListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.d("TAG","msg receiver entered");
if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
String messageBody = "";
String msg_from = "";
for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
msg_from = smsMessage.getServiceCenterAddress();
Log.d("TAG","msg_from = "+msg_from);
Log.d("TAG","msgBody = "+messageBody);
}
}
}
}
AndroidManifest.xml
a) necessary permissions
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
b) receiver entry
<receiver android:name=".receiver.SmsListener"
>
<intent-filter android:priority="1000">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
I also tried to modify the priority to 999, as suggested in a few other StackOverflow answers to a similar query, but no luck.
Though, the same code is still working in the demo app, but unluckily not in my app.
I also tried using EventBus referring here. That too worked for some time, unless I again tried testing using
Your SnapDeal order AWB:12791911327207 is delivered on 19-02-2020 at
16:20 by Xpressbees received by Username. You may contact us on
020-49116100.
Don't know what's wrong, as the code looks fine, and was working fine in the same app, also the same code working fine in another demo app.
I also found a suggestion to whitelist the App in this answer. Though, don't know how to do that or whether its the perfect solution.
Please suggest how to achieve reading incoming SMS, or what I am missing or going wrong. Thanks.
Finally, made it working in higher versions too, just by adding
android:permission="android.permission.BROADCAST_SMS"
in the receiver tag in AndroidManifest.xml
and made it something like -
<receiver android:name=".receiver.SmsListener"
android:permission="android.permission.BROADCAST_SMS"
>
<intent-filter android:priority="999">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
Is there anyways in sending a location based push notification for android devices with out using a third party push notification service such as Parse? I would like to send push notification to my users without the annoyance of getting a notification that doesn't relate to that specific user because they are not in a certain area. Also, I could get the users location based on a time interval but I would rather do it a different way then that, if possible.
Yes, this is entirely possible, as long as I'm correctly interpreting what you are asking.
To accomplish this, you would send the GCM push notification to all of your users (unless you had a way, server-side of filtering some of them out). Then in your application, instead of just creating a Notification and passing it to the notification manager, you would first use the LocationManager (or the newer LocationServices API) to determine if the user is within the proper location first, and then just discard the GCM notification if they're not.
You'll need to take care of several things in order to do this:
Your AndroidManifest.xml will require several permission changes, both for the GCM changes, and for the Location access:
<!-- Needed for processing notifications -->
<permission android:name="com.myappname.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.myappname.permission.C2D_MESSAGE" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<!-- Needed for Location -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
You'll also need to set up a Notification Receiver in the <application> section of the manifest:
<receiver android:name="com.myappname.NotificationReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.myappname" />
</intent-filter>
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.myappname" />
</intent-filter>
</receiver>
In addition, you'll need write your NotificationReceiver java class, and override the onReceive function:
public class NotificationReceiver extends BroadcastReceiver {
public void onReceive(final Context context, final Intent intent) {
if ("com.google.android.c2dm.intent.REGISTRATION".equals(intent.getAction())) {
handleRegistration(context, intent); // you'll have to write this function
} else if ("com.google.android.c2dm.intent.RECEIVE".equals(intent.getAction())) {
// the handle message function will need to check the user's current location using the location API you choose, and then create the proper Notification if necessary.
handleMessage(context, intent);
}
}
I have a broadcast receiver registered in Manifest:
<application ...>
<receiver android:name="com.some.pkg.NewAppReceiver" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
</intent-filter>
</receiver>
</appcication>
And the receiver:
public class NewAppReceiver extends BroadcastReceiver {
private static final String TAG = "NewAppReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Intent: " + intent.getAction());
}
}
And nothing is received when I install APK manually or from the Android Market. Why?
Did you run the app that contains this broadcastReceiver before installing the other apps?
Starting at some API version, broadcastReceivers will not work till you execute the app. Put an activity and execute it.
Also , don't forget to add the following into the broadcastReceiver:
<data android:scheme="package" />
EDIT: On Android 8 and above, if your app targets API 27 or more, it will work partially, so you have to register to those events in code and not in manifest. Here's a list of intents that are still safe to use in manifest: https://developer.android.com/guide/components/broadcast-exceptions.html .
The rest should be used in code. More info here
Since android.intent.action.PACKAGE_ADDED is a System Intent (note that your own app will not receive it at its installation), your BroadcastReceiver will receive messages from sources outside your app. Thus, check you did NOT put: android:exported="false"
You also may need to add:
<data android:scheme="package" />
So, your BroadcastReceiver in your AndroidManifest.xml should look like this:
<application ...>
<receiver android:name=".NewAppReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
</appcication>
If it still doesn't work, you may try to put an higher priority, such as: android:priority="1000"
Take a look at: http://developer.android.com/guide/topics/manifest/receiver-element.html
Registering receiver from manifest would not work from API 26(android 8). Because it had performance impact on older versions.
But we can register receiver from java code and receive updates of removed and added applications.
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED)
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED)
intentFilter.addDataScheme("package")
registerReceiver(YourBroadcastReceiver(), intentFilter)
Are you trying to receive the intent in the application you are installing? The documentation for ACTION_PACKAGE_ADDED says:
Note that the newly installed package does not receive this broadcast.
Another possibility is that this intent might not be delivered to components registered via the manifest but only manually (as described in an answer by Mark Murphy to Stack Overflow question Can't receive broadcasts for PACKAGE intents).
If you try to receive some other package it must be worked.
(As #Savvas noted) If you try to receive your own package's addition you can't receive it. Even if your broadcast receiver has action.PACKAGE_ADDED, receiver's onReceive method isn't triggered.
In this case your best bet is saving this data. By using sharedPreferences, add a key something like "appIsWorkedBefore", and on your launcher Activity's onCreate method set this variable as "true". And you can make your works with respect to this Boolean.
This intent action is no longer available for applications.
This is a protected intent that can only be sent by the system.
https://developer.android.com/reference/android/content/Intent#ACTION_PACKAGE_ADDED
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.